-
Notifications
You must be signed in to change notification settings - Fork 128
Add verbosity support to getblockheader RPC
#1006
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
Changes from all commits
94530f8
9b27552
44fffb5
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 |
|---|---|---|
|
|
@@ -15,6 +15,7 @@ use bitcoin::ScriptBuf; | |
| use bitcoin::Txid; | ||
| use bitcoin::VarInt; | ||
| use corepc_types::v29::GetTxOut; | ||
| use corepc_types::v30::GetBlockHeaderVerbose; | ||
| use corepc_types::v30::GetBlockVerboseOne; | ||
| use corepc_types::ScriptPubkey; | ||
| use floresta_chain::extensions::HeaderExt; | ||
|
|
@@ -24,6 +25,7 @@ use serde_json::json; | |
| use serde_json::Value; | ||
| use tracing::debug; | ||
|
|
||
| use super::res::GetBlockHeaderRes; | ||
| use super::res::GetBlockchainInfoRes; | ||
| use super::res::GetTxOutProof; | ||
| use super::res::JsonRpcError; | ||
|
|
@@ -106,7 +108,7 @@ impl<Blockchain: RpcChain> RpcImpl<Blockchain> { | |
| at: u32, | ||
| ) -> Result<u32, JsonRpcError> { | ||
| let hash = provider.get_block_hash(at)?; | ||
| let block = provider.get_block_header(hash)?; | ||
| let block = provider.get_block_header_inner(hash)?; | ||
| Ok(block.time) | ||
| } | ||
|
|
||
|
|
@@ -182,31 +184,15 @@ impl<Blockchain: RpcChain> RpcImpl<Blockchain> { | |
| return Ok(GetBlockRes::Zero(hex)); | ||
| } | ||
| if verbosity == 1 { | ||
| let header = &block.header; | ||
| let height = header.get_height(&self.chain)?; | ||
| let median_time = header.calculate_median_time_past(&self.chain)?; | ||
| let chain_work = header.calculate_chain_work(&self.chain)?.to_string_hex(); | ||
| let confirmations = header.get_confirmations(&self.chain)? as i64; | ||
| let version_hex = header.get_version_hex(); | ||
|
|
||
| let next_block_hash = header | ||
| .get_next_block_hash(&self.chain)? | ||
| .map(|h| h.to_string()); | ||
|
|
||
| let bits = header.get_bits_hex(); | ||
| let difficulty = header.get_difficulty(); | ||
| let target = header.get_target_hex(); | ||
| let header_fields = self.get_block_header_verbose_inner(&block)?; | ||
|
|
||
| // Stripped size is the size of the block without witness data | ||
| // Header + VarInt for number of transactions + sum of base sizes of each transaction | ||
| let tx_count_varint_size = VarInt::from(block.txdata.len()).size(); | ||
| let total_tx_base_size: usize = block.txdata.iter().map(|tx| tx.base_size()).sum(); | ||
| let stripped_size_bytes = Header::SIZE + tx_count_varint_size + total_tx_base_size; | ||
|
|
||
| let stripped_size = Some(stripped_size_bytes as i64); | ||
|
|
||
| let previous_block_hash = (header.prev_blockhash != BlockHash::all_zeros()) | ||
| .then_some(header.prev_blockhash.to_string()); | ||
| let stripped_size = Some(stripped_size_bytes.try_into()?); | ||
|
|
||
| let tx = block | ||
| .txdata | ||
|
|
@@ -215,26 +201,26 @@ impl<Blockchain: RpcChain> RpcImpl<Blockchain> { | |
| .collect(); | ||
|
|
||
| let block = GetBlockVerboseOne { | ||
| bits, | ||
| chain_work, | ||
| confirmations, | ||
| difficulty, | ||
| hash: header.block_hash().to_string(), | ||
| height: height as i64, | ||
| merkle_root: header.merkle_root.to_string(), | ||
| nonce: header.nonce as i64, | ||
| previous_block_hash, | ||
| size: block.total_size() as i64, | ||
| time: header.time as i64, | ||
| bits: header_fields.bits, | ||
| chain_work: header_fields.chain_work, | ||
| confirmations: header_fields.confirmations, | ||
| difficulty: header_fields.difficulty, | ||
| hash: header_fields.hash, | ||
| height: header_fields.height, | ||
| merkle_root: header_fields.merkle_root, | ||
| nonce: header_fields.nonce, | ||
| previous_block_hash: header_fields.previous_block_hash, | ||
| size: block.total_size().try_into()?, | ||
| time: header_fields.time, | ||
| tx, | ||
| version: header.version.to_consensus(), | ||
| version_hex, | ||
| version: header_fields.version, | ||
| version_hex: header_fields.version_hex, | ||
| weight: block.weight().to_wu(), | ||
| median_time: Some(median_time as i64), | ||
| n_tx: block.txdata.len() as i64, | ||
| next_block_hash, | ||
| median_time: Some(header_fields.median_time), | ||
| n_tx: header_fields.n_tx.into(), | ||
| next_block_hash: header_fields.next_block_hash, | ||
| stripped_size, | ||
| target, | ||
| target: header_fields.target, | ||
| }; | ||
|
|
||
| return Ok(GetBlockRes::One(Box::new(block))); | ||
|
|
@@ -302,10 +288,23 @@ impl<Blockchain: RpcChain> RpcImpl<Blockchain> { | |
| } | ||
|
|
||
| // getblockheader | ||
| pub(super) fn get_block_header(&self, hash: BlockHash) -> Result<Header, JsonRpcError> { | ||
| self.chain | ||
| .get_block_header(&hash) | ||
| .map_err(|_| JsonRpcError::BlockNotFound) | ||
| pub(super) async fn get_block_header( | ||
| &self, | ||
| hash: BlockHash, | ||
| verbosity: bool, | ||
| ) -> Result<GetBlockHeaderRes, JsonRpcError> { | ||
| let header = self.get_block_header_inner(hash)?; | ||
|
|
||
| if !verbosity { | ||
| let hex = serialize_hex(&header); | ||
| return Ok(GetBlockHeaderRes::Raw(hex)); | ||
| } | ||
|
|
||
| let block = self.get_block_inner(hash).await?; | ||
|
|
||
| let get_block_header = self.get_block_header_verbose_inner(&block)?; | ||
|
|
||
| Ok(GetBlockHeaderRes::Verbose(Box::new(get_block_header))) | ||
| } | ||
|
|
||
| // getblockstats | ||
|
|
@@ -320,6 +319,55 @@ impl<Blockchain: RpcChain> RpcImpl<Blockchain> { | |
| // getmempoolinfo | ||
| // getrawmempool | ||
|
|
||
| /// Same as `get_block_header_inner` but verbose. | ||
| fn get_block_header_verbose_inner( | ||
| &self, | ||
| block: &Block, | ||
| ) -> Result<GetBlockHeaderVerbose, JsonRpcError> { | ||
| let header = &block.header; | ||
| let height = header.get_height(&self.chain)?; | ||
| let median_time = header.calculate_median_time_past(&self.chain)?; | ||
| let chain_work = header.calculate_chain_work(&self.chain)?.to_string_hex(); | ||
| let confirmations = header.get_confirmations(&self.chain)?; | ||
| let version_hex = header.get_version_hex(); | ||
|
|
||
| let next_block_hash = header | ||
| .get_next_block_hash(&self.chain)? | ||
| .map(|h| h.to_string()); | ||
|
|
||
| let bits = header.get_bits_hex(); | ||
| let difficulty = header.get_difficulty(); | ||
| let target = header.get_target_hex(); | ||
|
Comment on lines
+328
to
+340
Member
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. The extensions are shining
Collaborator
Author
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. Yes, |
||
| let previous_block_hash = (header.prev_blockhash != BlockHash::all_zeros()) | ||
| .then_some(header.prev_blockhash.to_string()); | ||
|
|
||
| Ok(GetBlockHeaderVerbose { | ||
| bits, | ||
| chain_work, | ||
| confirmations: confirmations.into(), | ||
| difficulty, | ||
| hash: header.block_hash().to_string(), | ||
| height: height.into(), | ||
| median_time: median_time.into(), | ||
| next_block_hash, | ||
| version: header.version.to_consensus(), | ||
| version_hex, | ||
| previous_block_hash, | ||
| merkle_root: header.merkle_root.to_string(), | ||
| time: header.time.into(), | ||
| target, | ||
| nonce: header.nonce.into(), | ||
| n_tx: block.txdata.len().try_into()?, | ||
| }) | ||
| } | ||
|
|
||
| /// Helper method to get a block header by its hash, used by multiple rpcs. | ||
| fn get_block_header_inner(&self, hash: BlockHash) -> Result<Header, JsonRpcError> { | ||
| self.chain | ||
| .get_block_header(&hash) | ||
| .map_err(|_| JsonRpcError::BlockNotFound) | ||
| } | ||
|
|
||
| /// Check if the script is anchor type | ||
| fn is_anchor_type(script: &Script) -> bool { | ||
| script.as_bytes().starts_with(&[0x51, 0x02, 0x4e, 0x73]) | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -4,8 +4,11 @@ use core::fmt; | |||||||||||||||||||
| use core::fmt::Debug; | ||||||||||||||||||||
| use core::fmt::Display; | ||||||||||||||||||||
| use core::fmt::Formatter; | ||||||||||||||||||||
| use core::num::TryFromIntError; | ||||||||||||||||||||
| use std::convert::Infallible; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| use axum::response::IntoResponse; | ||||||||||||||||||||
| use corepc_types::v30::GetBlockHeaderVerbose; | ||||||||||||||||||||
| use corepc_types::v30::GetBlockVerboseOne; | ||||||||||||||||||||
| use floresta_chain::extensions::HeaderExtError; | ||||||||||||||||||||
| use floresta_common::impl_error_from; | ||||||||||||||||||||
|
|
@@ -131,6 +134,18 @@ pub enum GetBlockRes { | |||||||||||||||||||
| One(Box<GetBlockVerboseOne>), | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| #[derive(Debug, Deserialize, Serialize)] | ||||||||||||||||||||
| #[serde(untagged)] | ||||||||||||||||||||
| /// The response for `getblockheader`, which can be either a raw hex-encoded block header or a verbose | ||||||||||||||||||||
| /// one with all the fields parsed and decoded. | ||||||||||||||||||||
| pub enum GetBlockHeaderRes { | ||||||||||||||||||||
|
Member
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. docstrings for this one ?
Collaborator
Author
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. Done |
||||||||||||||||||||
| /// The raw hex-encoded block header, as returned by `getblockheader` with verbosity false | ||||||||||||||||||||
| Raw(String), | ||||||||||||||||||||
|
|
||||||||||||||||||||
| /// A verbose block header, as returned by `getblockheader` with verbosity true | ||||||||||||||||||||
| Verbose(Box<GetBlockHeaderVerbose>), | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
Comment on lines
+143
to
+147
Member
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.
Suggested change
nit
Member
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 had a pending review with this, forgot to publish. Agree 👍
Collaborator
Author
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. Done |
||||||||||||||||||||
|
|
||||||||||||||||||||
| #[derive(Debug, Deserialize, Serialize)] | ||||||||||||||||||||
| pub struct RpcError { | ||||||||||||||||||||
| pub code: i32, | ||||||||||||||||||||
|
|
@@ -226,6 +241,9 @@ pub enum JsonRpcError { | |||||||||||||||||||
|
|
||||||||||||||||||||
| /// Something went wrong when attempting to publish a transaction to mempool | ||||||||||||||||||||
| MempoolAccept(MempoolError), | ||||||||||||||||||||
|
|
||||||||||||||||||||
| /// A numeric conversion overflows, e.g., u64 to u32 | ||||||||||||||||||||
| ConversionOverflow(String), | ||||||||||||||||||||
|
Member
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. Wait, it feels really off to have this kind of error here, surely theres an existing alternative for this
Collaborator
Author
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 don’t think there are alternatives, because there are some situations where we’re working with |
||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| impl_error_from!(JsonRpcError, MempoolError, MempoolAccept); | ||||||||||||||||||||
|
|
@@ -260,6 +278,7 @@ impl Display for JsonRpcError { | |||||||||||||||||||
| JsonRpcError::InvalidDisconnectNodeCommand => write!(f, "Invalid disconnectnode command"), | ||||||||||||||||||||
| JsonRpcError::PeerNotFound => write!(f, "Peer not found in the peer list"), | ||||||||||||||||||||
| JsonRpcError::MempoolAccept(e) => write!(f, "Could not send transaction to mempool due to {e}"), | ||||||||||||||||||||
| JsonRpcError::ConversionOverflow(e) => write!(f, "Numeric conversion overflow: {e}"), | ||||||||||||||||||||
| } | ||||||||||||||||||||
| } | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
@@ -289,6 +308,18 @@ impl From<HeaderExtError> for JsonRpcError { | |||||||||||||||||||
| } | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| impl From<TryFromIntError> for JsonRpcError { | ||||||||||||||||||||
| fn from(e: TryFromIntError) -> Self { | ||||||||||||||||||||
| JsonRpcError::ConversionOverflow(e.to_string()) | ||||||||||||||||||||
| } | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| impl From<Infallible> for JsonRpcError { | ||||||||||||||||||||
| fn from(e: Infallible) -> Self { | ||||||||||||||||||||
| JsonRpcError::ConversionOverflow(e.to_string()) | ||||||||||||||||||||
| } | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| impl_error_from!(JsonRpcError, DescriptorError, InvalidDescriptor); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| impl<T: Debug> From<floresta_watch_only::WatchOnlyError<T>> for JsonRpcError { | ||||||||||||||||||||
|
|
||||||||||||||||||||
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.
65acc75 and 481e59c
can be squashed, they pretty much jut add verbosity support for getblockheader
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.
Done