1
1
//! Simple RPC client implementation which implements [`BlockSource`] against a Bitcoin Core RPC
2
2
//! endpoint.
3
3
4
- use crate :: { BlockData , BlockHeaderData , BlockSource , AsyncBlockSourceResult } ;
5
- use crate :: http:: { HttpClient , HttpEndpoint , HttpError , JsonResponse } ;
6
4
use crate :: gossip:: UtxoSource ;
5
+ use crate :: http:: { HttpClient , HttpEndpoint , HttpError , JsonResponse } ;
6
+ use crate :: { AsyncBlockSourceResult , BlockData , BlockHeaderData , BlockSource } ;
7
7
8
8
use bitcoin:: hash_types:: BlockHash ;
9
9
use bitcoin:: OutPoint ;
@@ -28,9 +28,9 @@ pub struct RpcError {
28
28
}
29
29
30
30
impl fmt:: Display for RpcError {
31
- fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
32
- write ! ( f, "RPC error {}: {}" , self . code, self . message)
33
- }
31
+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
32
+ write ! ( f, "RPC error {}: {}" , self . code, self . message)
33
+ }
34
34
}
35
35
36
36
impl Error for RpcError { }
@@ -63,8 +63,12 @@ impl RpcClient {
63
63
///
64
64
/// When an `Err` is returned, [`std::io::Error::into_inner`] may contain an [`RpcError`] if
65
65
/// [`std::io::Error::kind`] is [`std::io::ErrorKind::Other`].
66
- pub async fn call_method < T > ( & self , method : & str , params : & [ serde_json:: Value ] ) -> std:: io:: Result < T >
67
- where JsonResponse : TryFrom < Vec < u8 > , Error = std:: io:: Error > + TryInto < T , Error = std:: io:: Error > {
66
+ pub async fn call_method < T > (
67
+ & self , method : & str , params : & [ serde_json:: Value ] ,
68
+ ) -> std:: io:: Result < T >
69
+ where
70
+ JsonResponse : TryFrom < Vec < u8 > , Error = std:: io:: Error > + TryInto < T , Error = std:: io:: Error > ,
71
+ {
68
72
let host = format ! ( "{}:{}" , self . endpoint. host( ) , self . endpoint. port( ) ) ;
69
73
let uri = self . endpoint . path ( ) ;
70
74
let content = serde_json:: json!( {
@@ -73,9 +77,13 @@ impl RpcClient {
73
77
"id" : & self . id. fetch_add( 1 , Ordering :: AcqRel ) . to_string( )
74
78
} ) ;
75
79
76
- let mut client = if let Some ( client) = self . client . lock ( ) . unwrap ( ) . take ( ) { client }
77
- else { HttpClient :: connect ( & self . endpoint ) ? } ;
78
- let http_response = client. post :: < JsonResponse > ( & uri, & host, & self . basic_auth , content) . await ;
80
+ let mut client = if let Some ( client) = self . client . lock ( ) . unwrap ( ) . take ( ) {
81
+ client
82
+ } else {
83
+ HttpClient :: connect ( & self . endpoint ) ?
84
+ } ;
85
+ let http_response =
86
+ client. post :: < JsonResponse > ( & uri, & host, & self . basic_auth , content) . await ;
79
87
* self . client . lock ( ) . unwrap ( ) = Some ( client) ;
80
88
81
89
let mut response = match http_response {
@@ -93,38 +101,49 @@ impl RpcClient {
93
101
} ;
94
102
95
103
if !response. is_object ( ) {
96
- return Err ( std:: io:: Error :: new ( std:: io:: ErrorKind :: InvalidData , "expected JSON object" ) ) ;
104
+ return Err ( std:: io:: Error :: new (
105
+ std:: io:: ErrorKind :: InvalidData ,
106
+ "expected JSON object" ,
107
+ ) ) ;
97
108
}
98
109
99
110
let error = & response[ "error" ] ;
100
111
if !error. is_null ( ) {
101
112
// TODO: Examine error code for a more precise std::io::ErrorKind.
102
- let rpc_error = RpcError {
103
- code : error[ "code" ] . as_i64 ( ) . unwrap_or ( -1 ) ,
104
- message : error[ "message" ] . as_str ( ) . unwrap_or ( "unknown error" ) . to_string ( )
113
+ let rpc_error = RpcError {
114
+ code : error[ "code" ] . as_i64 ( ) . unwrap_or ( -1 ) ,
115
+ message : error[ "message" ] . as_str ( ) . unwrap_or ( "unknown error" ) . to_string ( ) ,
105
116
} ;
106
117
return Err ( std:: io:: Error :: new ( std:: io:: ErrorKind :: Other , rpc_error) ) ;
107
118
}
108
119
109
120
let result = match response. get_mut ( "result" ) {
110
121
Some ( result) => result. take ( ) ,
111
- None =>
112
- return Err ( std:: io:: Error :: new ( std:: io:: ErrorKind :: InvalidData , "expected JSON result" ) ) ,
122
+ None => {
123
+ return Err ( std:: io:: Error :: new (
124
+ std:: io:: ErrorKind :: InvalidData ,
125
+ "expected JSON result" ,
126
+ ) )
127
+ } ,
113
128
} ;
114
129
115
130
JsonResponse ( result) . try_into ( )
116
131
}
117
132
}
118
133
119
134
impl BlockSource for RpcClient {
120
- fn get_header < ' a > ( & ' a self , header_hash : & ' a BlockHash , _height : Option < u32 > ) -> AsyncBlockSourceResult < ' a , BlockHeaderData > {
135
+ fn get_header < ' a > (
136
+ & ' a self , header_hash : & ' a BlockHash , _height : Option < u32 > ,
137
+ ) -> AsyncBlockSourceResult < ' a , BlockHeaderData > {
121
138
Box :: pin ( async move {
122
139
let header_hash = serde_json:: json!( header_hash. to_string( ) ) ;
123
140
Ok ( self . call_method ( "getblockheader" , & [ header_hash] ) . await ?)
124
141
} )
125
142
}
126
143
127
- fn get_block < ' a > ( & ' a self , header_hash : & ' a BlockHash ) -> AsyncBlockSourceResult < ' a , BlockData > {
144
+ fn get_block < ' a > (
145
+ & ' a self , header_hash : & ' a BlockHash ,
146
+ ) -> AsyncBlockSourceResult < ' a , BlockData > {
128
147
Box :: pin ( async move {
129
148
let header_hash = serde_json:: json!( header_hash. to_string( ) ) ;
130
149
let verbosity = serde_json:: json!( 0 ) ;
@@ -133,14 +152,14 @@ impl BlockSource for RpcClient {
133
152
}
134
153
135
154
fn get_best_block < ' a > ( & ' a self ) -> AsyncBlockSourceResult < ' a , ( BlockHash , Option < u32 > ) > {
136
- Box :: pin ( async move {
137
- Ok ( self . call_method ( "getblockchaininfo" , & [ ] ) . await ?)
138
- } )
155
+ Box :: pin ( async move { Ok ( self . call_method ( "getblockchaininfo" , & [ ] ) . await ?) } )
139
156
}
140
157
}
141
158
142
159
impl UtxoSource for RpcClient {
143
- fn get_block_hash_by_height < ' a > ( & ' a self , block_height : u32 ) -> AsyncBlockSourceResult < ' a , BlockHash > {
160
+ fn get_block_hash_by_height < ' a > (
161
+ & ' a self , block_height : u32 ,
162
+ ) -> AsyncBlockSourceResult < ' a , BlockHash > {
144
163
Box :: pin ( async move {
145
164
let height_param = serde_json:: json!( block_height) ;
146
165
Ok ( self . call_method ( "getblockhash" , & [ height_param] ) . await ?)
@@ -152,8 +171,8 @@ impl UtxoSource for RpcClient {
152
171
let txid_param = serde_json:: json!( outpoint. txid. to_string( ) ) ;
153
172
let vout_param = serde_json:: json!( outpoint. vout) ;
154
173
let include_mempool = serde_json:: json!( false ) ;
155
- let utxo_opt: serde_json:: Value = self . call_method (
156
- "gettxout" , & [ txid_param, vout_param, include_mempool] ) . await ?;
174
+ let utxo_opt: serde_json:: Value =
175
+ self . call_method ( "gettxout" , & [ txid_param, vout_param, include_mempool] ) . await ?;
157
176
Ok ( !utxo_opt. is_null ( ) )
158
177
} )
159
178
}
@@ -229,7 +248,7 @@ mod tests {
229
248
230
249
#[ tokio:: test]
231
250
async fn call_method_returning_missing_result ( ) {
232
- let response = serde_json:: json!( { } ) ;
251
+ let response = serde_json:: json!( { } ) ;
233
252
let server = HttpServer :: responding_with_ok ( MessageBody :: Content ( response) ) ;
234
253
let client = RpcClient :: new ( CREDENTIALS , server. endpoint ( ) ) . unwrap ( ) ;
235
254
0 commit comments