11use std:: collections:: HashMap ;
22use std:: sync:: Arc ;
33
4- use anyhow:: { anyhow, Context , Result } ;
4+ use anyhow:: { anyhow, Result } ;
55use bitcoind_async_client:: corepc_types:: model:: ListUnspentItem ;
6+ use bitcoind_async_client:: error:: ClientError ;
67use bitcoind_async_client:: traits:: { Broadcaster , Reader , Signer , Wallet } ;
78use bitcoind_async_client:: types:: { CreateRawTransactionOutput , WalletCreateFundedPsbtOptions } ;
89use bitcoind_async_client:: { Auth , Client as AsyncBitcoinRpc } ;
@@ -72,13 +73,15 @@ impl BitcoindWallet {
7273 )
7374 . await
7475 } )
75- } ) ?;
76+ } )
77+ . map_err ( |e| rpc_context ( e, "Failed to create funded PSBT" ) ) ?;
7678
7779 let processed = tokio:: task:: block_in_place ( || {
7880 tokio:: runtime:: Handle :: current ( ) . block_on ( async {
7981 self . rpc . wallet_process_psbt ( & result. psbt . to_string ( ) , None , None , None ) . await
8082 } )
81- } ) ?
83+ } )
84+ . map_err ( |e| rpc_context ( e, "Failed to process PSBT" ) ) ?
8285 . psbt ;
8386
8487 Ok ( processed)
@@ -93,15 +96,17 @@ impl BitcoindWallet {
9396 tokio:: runtime:: Handle :: current ( ) . block_on ( async {
9497 self . rpc . wallet_process_psbt ( & psbt_str, Some ( true ) , None , None ) . await
9598 } )
96- } ) ?;
99+ } )
100+ . map_err ( |e| rpc_context ( e, "Failed to process PSBT" ) ) ?;
97101 Ok ( processed. psbt )
98102 }
99103
100104 pub fn can_broadcast ( & self , tx : & Transaction ) -> Result < bool > {
101105 let mempool_results = tokio:: task:: block_in_place ( || {
102106 tokio:: runtime:: Handle :: current ( )
103107 . block_on ( async { self . rpc . test_mempool_accept ( tx) . await } )
104- } ) ?;
108+ } )
109+ . map_err ( |e| rpc_context ( e, "Failed to test mempool accept" ) ) ?;
105110
106111 mempool_results
107112 . results
@@ -116,7 +121,7 @@ impl BitcoindWallet {
116121 tokio:: runtime:: Handle :: current ( )
117122 . block_on ( async { self . rpc . send_raw_transaction ( tx) . await } )
118123 } )
119- . context ( "Failed to broadcast transaction" )
124+ . map_err ( |e| rpc_context ( e , "Failed to broadcast transaction" ) )
120125 }
121126
122127 /// Check if a script belongs to this wallet
@@ -126,7 +131,7 @@ impl BitcoindWallet {
126131 tokio:: runtime:: Handle :: current ( )
127132 . block_on ( async { self . rpc . get_address_info ( & address) . await } )
128133 } )
129- . context ( "Failed to get address info" ) ?;
134+ . map_err ( |e| rpc_context ( e , "Failed to get address info" ) ) ?;
130135 Ok ( info. is_mine )
131136 } else {
132137 Ok ( false )
@@ -150,7 +155,8 @@ impl BitcoindWallet {
150155 } ,
151156 }
152157 } )
153- } ) ?;
158+ } )
159+ . map_err ( |e| rpc_context ( e, "Failed to get transaction" ) ) ?;
154160 Ok ( raw_tx)
155161 }
156162
@@ -159,7 +165,7 @@ impl BitcoindWallet {
159165 let addr = tokio:: task:: block_in_place ( || {
160166 tokio:: runtime:: Handle :: current ( ) . block_on ( async { self . rpc . get_new_address ( ) . await } )
161167 } )
162- . context ( "Failed to get new address" ) ?;
168+ . map_err ( |e| rpc_context ( e , "Failed to get new address" ) ) ?;
163169 Ok ( addr)
164170 }
165171
@@ -169,7 +175,7 @@ impl BitcoindWallet {
169175 tokio:: runtime:: Handle :: current ( )
170176 . block_on ( async { self . rpc . list_unspent ( None , None , None , None , None ) . await } )
171177 } )
172- . context ( "Failed to list unspent" ) ?;
178+ . map_err ( |e| rpc_context ( e , "Failed to list unspent" ) ) ?;
173179 Ok ( unspent. 0 . into_iter ( ) . map ( input_pair_from_corepc) . collect ( ) )
174180 }
175181
@@ -184,10 +190,26 @@ impl BitcoindWallet {
184190 tokio:: task:: block_in_place ( || {
185191 tokio:: runtime:: Handle :: current ( ) . block_on ( async { self . rpc . network ( ) . await } )
186192 } )
187- . map_err ( |_| anyhow ! ( "Failed to get blockchain info" ) )
193+ . map_err ( |e| rpc_context ( e , "Failed to get blockchain info" ) )
188194 }
189195}
190196
197+ /// Wrap an RPC client error with a user-friendly context message.
198+ ///
199+ /// `bitcoind-async-client` discards JSON-RPC error details from HTTP 500
200+ /// response bodies (see issue #1258), so we add hints about common causes.
201+ fn rpc_context ( err : ClientError , operation : & str ) -> anyhow:: Error {
202+ let hint = match & err {
203+ ClientError :: Status ( 500 , _) =>
204+ ". bitcoind returned HTTP 500; check that the wallet is \
205+ loaded and bitcoind is fully synced (see debug.log for \
206+ the full RPC error)",
207+ ClientError :: Connection ( _) => ". Is bitcoind running?" ,
208+ _ => "" ,
209+ } ;
210+ anyhow:: Error :: new ( err) . context ( format ! ( "{operation}{hint}" ) )
211+ }
212+
191213pub fn input_pair_from_corepc ( utxo : ListUnspentItem ) -> InputPair {
192214 let psbtin = Input {
193215 // NOTE: non_witness_utxo is not necessary because bitcoin-cli always supplies
0 commit comments