@@ -31,8 +31,8 @@ use bitcoin::{
3131
3232use crate :: api:: AddressStats ;
3333use crate :: {
34- BlockStatus , BlockSummary , Builder , Error , MerkleProof , OutputStatus , Tx , TxStatus ,
35- BASE_BACKOFF_MILLIS , RETRYABLE_ERROR_CODES ,
34+ BlockStatus , BlockSummary , Builder , Error , MerkleProof , OutputStatus , SubmitPackageResult , Tx ,
35+ TxStatus , BASE_BACKOFF_MILLIS , RETRYABLE_ERROR_CODES ,
3636} ;
3737
3838#[ derive( Debug , Clone ) ]
@@ -88,6 +88,24 @@ impl BlockingClient {
8888 Ok ( request)
8989 }
9090
91+ fn post_request < T > ( & self , path : & str , body : T ) -> Result < Request , Error >
92+ where
93+ T : Into < Vec < u8 > > ,
94+ {
95+ let mut request = minreq:: post ( format ! ( "{}/{}" , self . url, path) ) . with_body ( body) ;
96+
97+ if let Some ( proxy) = & self . proxy {
98+ let proxy = Proxy :: new ( proxy. as_str ( ) ) ?;
99+ request = request. with_proxy ( proxy) ;
100+ }
101+
102+ if let Some ( timeout) = & self . timeout {
103+ request = request. with_timeout ( * timeout) ;
104+ }
105+
106+ Ok ( request)
107+ }
108+
91109 fn get_opt_response < T : Decodable > ( & self , path : & str ) -> Result < Option < T > , Error > {
92110 match self . get_with_retry ( path) {
93111 Ok ( resp) if is_status_not_found ( resp. status_code ) => Ok ( None ) ,
@@ -268,20 +286,58 @@ impl BlockingClient {
268286
269287 /// Broadcast a [`Transaction`] to Esplora
270288 pub fn broadcast ( & self , transaction : & Transaction ) -> Result < ( ) , Error > {
271- let mut request = minreq:: post ( format ! ( "{}/tx" , self . url) ) . with_body (
289+ let request = self . post_request (
290+ "tx" ,
272291 serialize ( transaction)
273292 . to_lower_hex_string ( )
274293 . as_bytes ( )
275294 . to_vec ( ) ,
276- ) ;
295+ ) ? ;
277296
278- if let Some ( proxy) = & self . proxy {
279- let proxy = Proxy :: new ( proxy. as_str ( ) ) ?;
280- request = request. with_proxy ( proxy) ;
297+ match request. send ( ) {
298+ Ok ( resp) if !is_status_ok ( resp. status_code ) => {
299+ let status = u16:: try_from ( resp. status_code ) . map_err ( Error :: StatusCode ) ?;
300+ let message = resp. as_str ( ) . unwrap_or_default ( ) . to_string ( ) ;
301+ Err ( Error :: HttpResponse { status, message } )
302+ }
303+ Ok ( _resp) => Ok ( ( ) ) ,
304+ Err ( e) => Err ( Error :: Minreq ( e) ) ,
281305 }
306+ }
282307
283- if let Some ( timeout) = & self . timeout {
284- request = request. with_timeout ( * timeout) ;
308+ /// Broadcast a package of [`Transaction`] to Esplora
309+ ///
310+ /// if `maxfeerate` is provided, any transaction whose
311+ /// fee is higher will be rejected
312+ ///
313+ /// if `maxburnamount` is provided, any transaction
314+ /// with higher provably unspendable outputs amount
315+ /// will be rejected
316+ pub fn broadcast_package (
317+ & self ,
318+ transactions : & [ Transaction ] ,
319+ maxfeerate : Option < f64 > ,
320+ maxburnamount : Option < f64 > ,
321+ ) -> Result < SubmitPackageResult , Error > {
322+ let serialized_txs = transactions
323+ . iter ( )
324+ . map ( |tx| serialize ( & tx) . to_lower_hex_string ( ) )
325+ . collect :: < Vec < _ > > ( ) ;
326+
327+ let mut request = self . post_request (
328+ "txs/package" ,
329+ serde_json:: to_string ( & serialized_txs)
330+ . unwrap ( )
331+ . as_bytes ( )
332+ . to_vec ( ) ,
333+ ) ?;
334+
335+ if let Some ( maxfeerate) = maxfeerate {
336+ request = request. with_param ( "maxfeerate" , maxfeerate. to_string ( ) )
337+ }
338+
339+ if let Some ( maxburnamount) = maxburnamount {
340+ request = request. with_param ( "maxburnamount" , maxburnamount. to_string ( ) )
285341 }
286342
287343 match request. send ( ) {
@@ -290,7 +346,7 @@ impl BlockingClient {
290346 let message = resp. as_str ( ) . unwrap_or_default ( ) . to_string ( ) ;
291347 Err ( Error :: HttpResponse { status, message } )
292348 }
293- Ok ( _resp ) => Ok ( ( ) ) ,
349+ Ok ( resp ) => Ok ( resp . json :: < SubmitPackageResult > ( ) . map_err ( Error :: Minreq ) ? ) ,
294350 Err ( e) => Err ( Error :: Minreq ( e) ) ,
295351 }
296352 }
0 commit comments