X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning-block-sync%2Fsrc%2Frpc.rs;h=88199688aefd1a48fd128aac5c45f319f947c90e;hb=c05347f48a4ff2789ae261ee8c8f3f4277489420;hp=60ea1a360c06f9038d05ed64712a4816fdfbde3f;hpb=6ca3b8ed496290cd9b6c49525d57a887a735a914;p=rust-lightning diff --git a/lightning-block-sync/src/rpc.rs b/lightning-block-sync/src/rpc.rs index 60ea1a36..88199688 100644 --- a/lightning-block-sync/src/rpc.rs +++ b/lightning-block-sync/src/rpc.rs @@ -1,4 +1,12 @@ -use crate::http::{HttpClient, HttpEndpoint, JsonResponse}; +//! Simple RPC client implementation which implements [`BlockSource`] against a Bitcoin Core RPC +//! endpoint. + +use crate::{BlockHeaderData, BlockSource, AsyncBlockSourceResult}; +use crate::http::{HttpClient, HttpEndpoint, HttpError, JsonResponse}; + +use bitcoin::blockdata::block::Block; +use bitcoin::hash_types::BlockHash; +use bitcoin::hashes::hex::ToHex; use serde_json; @@ -29,7 +37,7 @@ impl RpcClient { } /// Calls a method with the response encoded in JSON format and interpreted as type `T`. - async fn call_method(&mut self, method: &str, params: &[serde_json::Value]) -> std::io::Result + pub async fn call_method(&mut self, method: &str, params: &[serde_json::Value]) -> std::io::Result where JsonResponse: TryFrom, Error = std::io::Error> + TryInto { let host = format!("{}:{}", self.endpoint.host(), self.endpoint.port()); let uri = self.endpoint.path(); @@ -39,8 +47,20 @@ impl RpcClient { "id": &self.id.fetch_add(1, Ordering::AcqRel).to_string() }); - let mut response = self.client.post::(&uri, &host, &self.basic_auth, content) - .await?.0; + let mut response = match self.client.post::(&uri, &host, &self.basic_auth, content).await { + Ok(JsonResponse(response)) => response, + Err(e) if e.kind() == std::io::ErrorKind::Other => { + match e.get_ref().unwrap().downcast_ref::() { + Some(http_error) => match JsonResponse::try_from(http_error.contents.clone()) { + Ok(JsonResponse(response)) => response, + Err(_) => Err(e)?, + }, + None => Err(e)?, + } + }, + Err(e) => Err(e)?, + }; + if !response.is_object() { return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "expected JSON object")); } @@ -61,6 +81,29 @@ impl RpcClient { } } +impl BlockSource for RpcClient { + fn get_header<'a>(&'a mut self, header_hash: &'a BlockHash, _height: Option) -> AsyncBlockSourceResult<'a, BlockHeaderData> { + Box::pin(async move { + let header_hash = serde_json::json!(header_hash.to_hex()); + Ok(self.call_method("getblockheader", &[header_hash]).await?) + }) + } + + fn get_block<'a>(&'a mut self, header_hash: &'a BlockHash) -> AsyncBlockSourceResult<'a, Block> { + Box::pin(async move { + let header_hash = serde_json::json!(header_hash.to_hex()); + let verbosity = serde_json::json!(0); + Ok(self.call_method("getblock", &[header_hash, verbosity]).await?) + }) + } + + fn get_best_block<'a>(&'a mut self) -> AsyncBlockSourceResult<'a, (BlockHash, Option)> { + Box::pin(async move { + Ok(self.call_method("getblockchaininfo", &[]).await?) + }) + } +} + #[cfg(test)] mod tests { use super::*; @@ -87,7 +130,7 @@ mod tests { let mut client = RpcClient::new(CREDENTIALS, server.endpoint()).unwrap(); match client.call_method::("getblockcount", &[]).await { - Err(e) => assert_eq!(e.kind(), std::io::ErrorKind::NotFound), + Err(e) => assert_eq!(e.kind(), std::io::ErrorKind::Other), Ok(_) => panic!("Expected error"), } } @@ -112,7 +155,7 @@ mod tests { let response = serde_json::json!({ "error": { "code": -8, "message": "invalid parameter" }, }); - let server = HttpServer::responding_with_ok(MessageBody::Content(response)); + let server = HttpServer::responding_with_server_error(response); let mut client = RpcClient::new(CREDENTIALS, server.endpoint()).unwrap(); let invalid_block_hash = serde_json::json!("foo");