-
Notifications
You must be signed in to change notification settings - Fork 4
Add Blockchain RPC methods #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -2,12 +2,22 @@ use std::{ | |||||||||||||||||||||||||||||||||||||||
| fs::File, | ||||||||||||||||||||||||||||||||||||||||
| io::{BufRead, BufReader}, | ||||||||||||||||||||||||||||||||||||||||
| path::PathBuf, | ||||||||||||||||||||||||||||||||||||||||
| str::FromStr, | ||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| use crate::error::Error; | ||||||||||||||||||||||||||||||||||||||||
| use crate::jsonrpc::minreq_http::Builder; | ||||||||||||||||||||||||||||||||||||||||
| use corepc_types::bitcoin::BlockHash; | ||||||||||||||||||||||||||||||||||||||||
| use jsonrpc::{serde, serde_json, Transport}; | ||||||||||||||||||||||||||||||||||||||||
| use corepc_types::{ | ||||||||||||||||||||||||||||||||||||||||
| bitcoin::{ | ||||||||||||||||||||||||||||||||||||||||
| block::Header, consensus::deserialize, hex::FromHex, Block, BlockHash, Transaction, Txid, | ||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||
| model::{GetBlockCount, GetBlockFilter, GetBlockVerboseOne, GetRawMempool}, | ||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||
| use jsonrpc::{ | ||||||||||||||||||||||||||||||||||||||||
| serde, | ||||||||||||||||||||||||||||||||||||||||
| serde_json::{self, json}, | ||||||||||||||||||||||||||||||||||||||||
| Transport, | ||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| /// client authentication methods | ||||||||||||||||||||||||||||||||||||||||
| #[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -101,11 +111,94 @@ impl Client { | |||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| // `bitcoind` RPC methods | ||||||||||||||||||||||||||||||||||||||||
| impl Client { | ||||||||||||||||||||||||||||||||||||||||
| /// Get best block hash. | ||||||||||||||||||||||||||||||||||||||||
| /// Get block | ||||||||||||||||||||||||||||||||||||||||
| pub fn get_block(&self, block_hash: &BlockHash) -> Result<Block, Error> { | ||||||||||||||||||||||||||||||||||||||||
| let hex_string: String = self.call("getblock", &[json!(block_hash), json!(0)])?; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| let bytes: Vec<u8> = Vec::<u8>::from_hex(&hex_string).map_err(Error::HexToBytes)?; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| let block: Block = deserialize(&bytes) | ||||||||||||||||||||||||||||||||||||||||
| .map_err(|e| Error::InvalidResponse(format!("failed to deserialize block: {e}")))?; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| Ok(block) | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| /// Get block verboseone | ||||||||||||||||||||||||||||||||||||||||
| pub fn get_block_verbose(&self, block_hash: &BlockHash) -> Result<GetBlockVerboseOne, Error> { | ||||||||||||||||||||||||||||||||||||||||
| let res: GetBlockVerboseOne = self.call("getblock", &[json!(block_hash), json!(1)])?; | ||||||||||||||||||||||||||||||||||||||||
| Ok(res) | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+127
to
+130
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I assume we first want to deserialize the response as a type from |
||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| /// Get best block hash | ||||||||||||||||||||||||||||||||||||||||
| pub fn get_best_block_hash(&self) -> Result<BlockHash, Error> { | ||||||||||||||||||||||||||||||||||||||||
| let res: String = self.call("getbestblockhash", &[])?; | ||||||||||||||||||||||||||||||||||||||||
| Ok(res.parse()?) | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| /// Get block count | ||||||||||||||||||||||||||||||||||||||||
| pub fn get_block_count(&self) -> Result<u64, Error> { | ||||||||||||||||||||||||||||||||||||||||
| let res: GetBlockCount = self.call("getblockcount", &[])?; | ||||||||||||||||||||||||||||||||||||||||
| Ok(res.0) | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| /// Get block hash | ||||||||||||||||||||||||||||||||||||||||
| pub fn get_block_hash(&self, height: u32) -> Result<BlockHash, Error> { | ||||||||||||||||||||||||||||||||||||||||
| let raw: serde_json::Value = self.call("getblockhash", &[json!(height)])?; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| let hash_str = match raw { | ||||||||||||||||||||||||||||||||||||||||
| serde_json::Value::String(s) => s, | ||||||||||||||||||||||||||||||||||||||||
| serde_json::Value::Object(obj) => obj | ||||||||||||||||||||||||||||||||||||||||
| .get("hash") | ||||||||||||||||||||||||||||||||||||||||
| .and_then(|v| v.as_str()) | ||||||||||||||||||||||||||||||||||||||||
| .ok_or_else(|| Error::InvalidResponse("getblockhash: missing 'hash' field".into()))? | ||||||||||||||||||||||||||||||||||||||||
| .to_string(), | ||||||||||||||||||||||||||||||||||||||||
| _ => { | ||||||||||||||||||||||||||||||||||||||||
| return Err(Error::InvalidResponse( | ||||||||||||||||||||||||||||||||||||||||
| "getblockhash: unexpected response type".into(), | ||||||||||||||||||||||||||||||||||||||||
| )); | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| BlockHash::from_str(&hash_str).map_err(Error::HexToArray) | ||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+146
to
+162
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems complicated. Don't we always expect the value to be
Suggested change
|
||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| /// Get block filter | ||||||||||||||||||||||||||||||||||||||||
| pub fn get_block_filter(&self, block_hash: BlockHash) -> Result<GetBlockFilter, Error> { | ||||||||||||||||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For consistency we should decide whether the API can take a block hash by value or by reference.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||
| let res: GetBlockFilter = self.call("getblockfilter", &[json!(block_hash)])?; | ||||||||||||||||||||||||||||||||||||||||
| Ok(res) | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| /// Get block header | ||||||||||||||||||||||||||||||||||||||||
| pub fn get_block_header(&self, block_hash: &BlockHash) -> Result<Header, Error> { | ||||||||||||||||||||||||||||||||||||||||
| let hex_string: String = self.call("getblockheader", &[json!(block_hash), json!(false)])?; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| let bytes = Vec::<u8>::from_hex(&hex_string).map_err(Error::HexToBytes)?; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| let header = deserialize(&bytes).map_err(|e| { | ||||||||||||||||||||||||||||||||||||||||
| Error::InvalidResponse(format!("failed to deserialize block header: {e}")) | ||||||||||||||||||||||||||||||||||||||||
| })?; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| Ok(header) | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| /// Get raw mempool | ||||||||||||||||||||||||||||||||||||||||
| pub fn get_raw_mempool(&self) -> Result<Vec<Txid>, Error> { | ||||||||||||||||||||||||||||||||||||||||
| let res: GetRawMempool = self.call("getrawmempool", &[])?; | ||||||||||||||||||||||||||||||||||||||||
| Ok(res.0) | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| /// Get raw transaction | ||||||||||||||||||||||||||||||||||||||||
| pub fn get_raw_transaction(&self, txid: &Txid) -> Result<Transaction, Error> { | ||||||||||||||||||||||||||||||||||||||||
| let hex_string: String = self.call("getrawtransaction", &[json!(txid)])?; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| let bytes = Vec::<u8>::from_hex(&hex_string).map_err(Error::HexToBytes)?; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| let transaction = deserialize(&bytes).map_err(|e| { | ||||||||||||||||||||||||||||||||||||||||
| Error::InvalidResponse(format!("transaction deserialization failed: {e}")) | ||||||||||||||||||||||||||||||||||||||||
| })?; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| Ok(transaction) | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| #[cfg(test)] | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For things that can be consensus decoded directly from a hex string, like
Block,Header, andTransaction, I think it will be easier to usedeserialize_hexand have a newError::DecodeHexerror that wraps theFromHexError.