1- use std:: process:: Command ;
1+ use std:: {
2+ io:: Read ,
3+ process:: { Command , Stdio } ,
4+ time:: { Duration , Instant } ,
5+ } ;
26
37use bitcoin_primitives:: Transaction as BitcoinTransaction ;
48use bitcoin_primitives:: hex;
@@ -9,6 +13,16 @@ fn bitcoin_bin() -> String {
913 std:: env:: var ( "BITCOIN_BIN" ) . unwrap_or_else ( |_| "bitcoin" . to_owned ( ) )
1014}
1115
16+ fn bitcoin_rpc_timeout ( ) -> Duration {
17+ const DEFAULT_TIMEOUT_SECS : u64 = 15 ;
18+
19+ std:: env:: var ( "BITCOIN_RPC_TIMEOUT_SECS" )
20+ . ok ( )
21+ . and_then ( |secs| secs. parse :: < u64 > ( ) . ok ( ) )
22+ . map ( Duration :: from_secs)
23+ . unwrap_or_else ( || Duration :: from_secs ( DEFAULT_TIMEOUT_SECS ) )
24+ }
25+
1226fn bitcoin_rpc ( wallet : Option < & str > , args : & [ & str ] ) -> Result < String , String > {
1327 let owned_args: Vec < String > = args. iter ( ) . map ( |arg| ( * arg) . to_owned ( ) ) . collect ( ) ;
1428 bitcoin_rpc_owned ( wallet, & owned_args)
@@ -34,19 +48,57 @@ fn bitcoin_rpc_owned(wallet: Option<&str>, args: &[String]) -> Result<String, St
3448 }
3549 command. args ( args) ;
3650
37- let output = command
38- . output ( )
51+ let rendered_args = args. join ( " " ) ;
52+ let timeout = bitcoin_rpc_timeout ( ) ;
53+ command. stdout ( Stdio :: piped ( ) ) . stderr ( Stdio :: piped ( ) ) ;
54+
55+ let mut child = command
56+ . spawn ( )
3957 . map_err ( |e| format ! ( "failed to execute bitcoin rpc command: {e}" ) ) ?;
40- if output. status . success ( ) {
41- Ok ( String :: from_utf8 ( output. stdout )
42- . unwrap_or_else ( |_| String :: new ( ) )
43- . trim ( )
44- . to_owned ( ) )
45- } else {
46- Err ( format ! (
47- "bitcoin rpc command failed: {}" ,
48- String :: from_utf8_lossy( & output. stderr) . trim( )
49- ) )
58+ let start = Instant :: now ( ) ;
59+
60+ loop {
61+ match child. try_wait ( ) {
62+ Ok ( Some ( status) ) => {
63+ let mut stdout = Vec :: new ( ) ;
64+ let mut stderr = Vec :: new ( ) ;
65+ if let Some ( mut pipe) = child. stdout . take ( ) {
66+ pipe. read_to_end ( & mut stdout)
67+ . map_err ( |e| format ! ( "failed to read bitcoin rpc stdout: {e}" ) ) ?;
68+ }
69+ if let Some ( mut pipe) = child. stderr . take ( ) {
70+ pipe. read_to_end ( & mut stderr)
71+ . map_err ( |e| format ! ( "failed to read bitcoin rpc stderr: {e}" ) ) ?;
72+ }
73+
74+ if status. success ( ) {
75+ return Ok ( String :: from_utf8 ( stdout)
76+ . unwrap_or_else ( |_| String :: new ( ) )
77+ . trim ( )
78+ . to_owned ( ) ) ;
79+ }
80+
81+ return Err ( format ! (
82+ "bitcoin rpc command failed ({rendered_args}): {}" ,
83+ String :: from_utf8_lossy( & stderr) . trim( )
84+ ) ) ;
85+ }
86+ Ok ( None ) => {
87+ if start. elapsed ( ) >= timeout {
88+ let _ = child. kill ( ) ;
89+ let _ = child. wait ( ) ;
90+ return Err ( format ! (
91+ "bitcoin rpc command timed out after {timeout:?} ({rendered_args})"
92+ ) ) ;
93+ }
94+ std:: thread:: sleep ( Duration :: from_millis ( 100 ) ) ;
95+ }
96+ Err ( e) => {
97+ let _ = child. kill ( ) ;
98+ let _ = child. wait ( ) ;
99+ return Err ( format ! ( "failed to poll bitcoin rpc command status: {e}" ) ) ;
100+ }
101+ }
50102 }
51103}
52104
0 commit comments