From 8436b96a71acafd7d6c0cf93f489cb377892e3d9 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Wed, 18 Sep 2024 08:05:25 -0300 Subject: [PATCH] feat(rpc): Add more fields to `getmininginfo` call (#8860) * add additional fields to getmininginfo * update openapi spec * fix zebra-state standalone build * make sure fields are not present when tip is 0 --- openapi.yaml | 58 +++++++++---------- zebra-chain/src/chain_tip/mock.rs | 2 +- .../src/methods/get_block_template_rpcs.rs | 30 ++++++++++ .../types/get_mining_info.rs | 23 +++++++- .../snapshots/get_mining_info@mainnet_10.snap | 2 + .../snapshots/get_mining_info@testnet_10.snap | 2 + zebra-state/src/request.rs | 7 +++ zebra-state/src/response.rs | 6 +- zebra-state/src/service.rs | 43 ++++++++++++++ 9 files changed, 141 insertions(+), 32 deletions(-) diff --git a/openapi.yaml b/openapi.yaml index abc70299814..58a754c9731 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -28,7 +28,7 @@ paths: default: getinfo id: type: string - default: dX2SRjFwfc + default: uf2E54tQkk params: type: array items: {} @@ -61,7 +61,7 @@ paths: default: getblockchaininfo id: type: string - default: LoRrjyRM4l + default: Sbre3vivr8 params: type: array items: {} @@ -99,7 +99,7 @@ paths: default: getaddressbalance id: type: string - default: WWIvpPiJo0 + default: f5qarOBgzK params: type: array items: {} @@ -147,7 +147,7 @@ paths: default: sendrawtransaction id: type: string - default: '5tVg2R9ZeI' + default: IlNHvAcSMS params: type: array items: {} @@ -196,7 +196,7 @@ paths: default: getblock id: type: string - default: vZ5KPOdiue + default: s9678BM3Lc params: type: array items: {} @@ -239,7 +239,7 @@ paths: default: getbestblockhash id: type: string - default: IifeYgN2ZK + default: FGQPJY8Tp8 params: type: array items: {} @@ -272,7 +272,7 @@ paths: default: getbestblockheightandhash id: type: string - default: tNLKsWqtNW + default: c2MfkL7xP9 params: type: array items: {} @@ -305,7 +305,7 @@ paths: default: getrawmempool id: type: string - default: IZ6todle9t + default: BugnNFhJpA params: type: array items: {} @@ -343,7 +343,7 @@ paths: default: z_gettreestate id: type: string - default: SSZAwyUO6t + default: fCUQvR1BVa params: type: array items: {} @@ -393,7 +393,7 @@ paths: default: z_getsubtreesbyindex id: type: string - default: '3fJMQ0Hfxt' + default: TtPnptV6EU params: type: array items: {} @@ -432,7 +432,7 @@ paths: default: getrawtransaction id: type: string - default: RTdE1YnNxy + default: QqYeOGSzje params: type: array items: {} @@ -480,7 +480,7 @@ paths: default: getaddresstxids id: type: string - default: ifahwzVoYe + default: AsWWVyqp8x params: type: array items: {} @@ -528,7 +528,7 @@ paths: default: getaddressutxos id: type: string - default: PcPdZ7aiKy + default: Qscn5dUFgD params: type: array items: {} @@ -571,7 +571,7 @@ paths: default: stop id: type: string - default: rWlJLGe7VJ + default: WuIaPXV5fO params: type: array items: {} @@ -604,7 +604,7 @@ paths: default: getblockcount id: type: string - default: f4p3Cb4sDu + default: '5F9M7Wp0oI' params: type: array items: {} @@ -642,7 +642,7 @@ paths: default: getblockhash id: type: string - default: '3QXvqbEWqb' + default: f7hdgVjctr params: type: array items: {} @@ -690,7 +690,7 @@ paths: default: getblocktemplate id: type: string - default: GXKjn81k0D + default: pq0uXn3YGs params: type: array items: {} @@ -728,7 +728,7 @@ paths: default: submitblock id: type: string - default: cwGy92Mwn9 + default: bs4v4JmVw3 params: type: array items: {} @@ -761,7 +761,7 @@ paths: default: getmininginfo id: type: string - default: '4ZFY9ljh5I' + default: pp5xV6v3pm params: type: array items: {} @@ -776,7 +776,7 @@ paths: properties: result: type: object - default: '{"networksolps":0,"networkhashps":0,"chain":"","testnet":false}' + default: '{"blocks":0,"networksolps":0,"networkhashps":0,"chain":"","testnet":false}' /getnetworksolps: post: tags: @@ -794,7 +794,7 @@ paths: default: getnetworksolps id: type: string - default: tJlKGzARjU + default: '7bU98TeCV6' params: type: array items: {} @@ -827,7 +827,7 @@ paths: default: getnetworkhashps id: type: string - default: '7pUkOt26PB' + default: fskOJeXqjo params: type: array items: {} @@ -860,7 +860,7 @@ paths: default: getpeerinfo id: type: string - default: JjnSrPKeyS + default: jPV8ufjDdt params: type: array items: {} @@ -898,7 +898,7 @@ paths: default: validateaddress id: type: string - default: pxZQt6VQ9U + default: xOyxICseV9 params: type: array items: {} @@ -936,7 +936,7 @@ paths: default: z_validateaddress id: type: string - default: x2R2oRhdZE + default: xa6PoC4uN6 params: type: array items: {} @@ -974,7 +974,7 @@ paths: default: getblocksubsidy id: type: string - default: vkhYJS3FH8 + default: vYEVtnVK9o params: type: array items: {} @@ -1017,7 +1017,7 @@ paths: default: getdifficulty id: type: string - default: bC6q9c3xYO + default: tVzSTZu2sD params: type: array items: {} @@ -1055,7 +1055,7 @@ paths: default: z_listunifiedreceivers id: type: string - default: EQvPXkcJC2 + default: le2NmJBmPt params: type: array items: {} @@ -1093,7 +1093,7 @@ paths: default: generate id: type: string - default: w41FKROii3 + default: vVVOWxHqlN params: type: array items: {} diff --git a/zebra-chain/src/chain_tip/mock.rs b/zebra-chain/src/chain_tip/mock.rs index 46ca5e89e5e..f1fc8fb6e27 100644 --- a/zebra-chain/src/chain_tip/mock.rs +++ b/zebra-chain/src/chain_tip/mock.rs @@ -106,7 +106,7 @@ impl ChainTip for MockChainTip { } fn best_tip_mined_transaction_ids(&self) -> Arc<[transaction::Hash]> { - unreachable!("Method not used in tests"); + Arc::new([]) } fn estimate_distance_to_network_chain_tip( diff --git a/zebra-rpc/src/methods/get_block_template_rpcs.rs b/zebra-rpc/src/methods/get_block_template_rpcs.rs index 826f8d3e930..2d50552cfec 100644 --- a/zebra-rpc/src/methods/get_block_template_rpcs.rs +++ b/zebra-rpc/src/methods/get_block_template_rpcs.rs @@ -1012,9 +1012,39 @@ where fn get_mining_info(&self) -> BoxFuture> { let network = self.network.clone(); + let mut state = self.state.clone(); + + let chain_tip = self.latest_chain_tip.clone(); + let tip_height = chain_tip.best_tip_height().unwrap_or(Height(0)).0; + + let mut current_block_tx = None; + if tip_height > 0 { + let mined_tx_ids = chain_tip.best_tip_mined_transaction_ids(); + current_block_tx = + (!mined_tx_ids.is_empty()).then(|| mined_tx_ids.len().saturating_sub(1)); + } + let solution_rate_fut = self.get_network_sol_ps(None, None); async move { + // Get the current block size. + let mut current_block_size = None; + if tip_height > 0 { + let request = zebra_state::ReadRequest::TipBlockSize; + let response: zebra_state::ReadResponse = state + .ready() + .and_then(|service| service.call(request)) + .await + .map_server_error()?; + current_block_size = match response { + zebra_state::ReadResponse::TipBlockSize(Some(block_size)) => Some(block_size), + _ => None, + }; + } + Ok(get_mining_info::Response::new( + tip_height, + current_block_size, + current_block_tx, network, solution_rate_fut.await?, )) diff --git a/zebra-rpc/src/methods/get_block_template_rpcs/types/get_mining_info.rs b/zebra-rpc/src/methods/get_block_template_rpcs/types/get_mining_info.rs index a14d4a081e7..21627d509db 100644 --- a/zebra-rpc/src/methods/get_block_template_rpcs/types/get_mining_info.rs +++ b/zebra-rpc/src/methods/get_block_template_rpcs/types/get_mining_info.rs @@ -5,6 +5,18 @@ use zebra_chain::parameters::Network; /// Response to a `getmininginfo` RPC request. #[derive(Debug, Default, PartialEq, Eq, serde::Serialize)] pub struct Response { + /// The current tip height. + #[serde(rename = "blocks")] + tip_height: u32, + + /// The size of the last mined block if any. + #[serde(rename = "currentblocksize", skip_serializing_if = "Option::is_none")] + current_block_size: Option, + + /// The number of transactions in the last mined block if any. + #[serde(rename = "currentblocktx", skip_serializing_if = "Option::is_none")] + current_block_tx: Option, + /// The estimated network solution rate in Sol/s. networksolps: u64, @@ -20,8 +32,17 @@ pub struct Response { impl Response { /// Creates a new `getmininginfo` response - pub fn new(network: Network, networksolps: u64) -> Self { + pub fn new( + tip_height: u32, + current_block_size: Option, + current_block_tx: Option, + network: Network, + networksolps: u64, + ) -> Self { Self { + tip_height, + current_block_size, + current_block_tx, networksolps, networkhashps: networksolps, chain: network.bip70_network_name(), diff --git a/zebra-rpc/src/methods/tests/snapshot/snapshots/get_mining_info@mainnet_10.snap b/zebra-rpc/src/methods/tests/snapshot/snapshots/get_mining_info@mainnet_10.snap index 67ffde393c4..de309513443 100644 --- a/zebra-rpc/src/methods/tests/snapshot/snapshots/get_mining_info@mainnet_10.snap +++ b/zebra-rpc/src/methods/tests/snapshot/snapshots/get_mining_info@mainnet_10.snap @@ -3,6 +3,8 @@ source: zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs expression: get_mining_info --- { + "blocks": 1687104, + "currentblocksize": 1617, "networksolps": 2, "networkhashps": 2, "chain": "main", diff --git a/zebra-rpc/src/methods/tests/snapshot/snapshots/get_mining_info@testnet_10.snap b/zebra-rpc/src/methods/tests/snapshot/snapshots/get_mining_info@testnet_10.snap index fc728a8540f..2051e6913ce 100644 --- a/zebra-rpc/src/methods/tests/snapshot/snapshots/get_mining_info@testnet_10.snap +++ b/zebra-rpc/src/methods/tests/snapshot/snapshots/get_mining_info@testnet_10.snap @@ -3,6 +3,8 @@ source: zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs expression: get_mining_info --- { + "blocks": 1842420, + "currentblocksize": 1618, "networksolps": 0, "networkhashps": 0, "chain": "test", diff --git a/zebra-state/src/request.rs b/zebra-state/src/request.rs index 28740a336bb..1863c56b2ed 100644 --- a/zebra-state/src/request.rs +++ b/zebra-state/src/request.rs @@ -1063,6 +1063,11 @@ pub enum ReadRequest { /// Returns [`ReadResponse::ValidBlockProposal`] when successful, or an error if /// the block fails contextual validation. CheckBlockProposalValidity(SemanticallyVerifiedBlock), + + #[cfg(feature = "getblocktemplate-rpcs")] + /// Returns [`ReadResponse::TipBlockSize(usize)`](ReadResponse::TipBlockSize) + /// with the current best chain tip block size in bytes. + TipBlockSize, } impl ReadRequest { @@ -1098,6 +1103,8 @@ impl ReadRequest { ReadRequest::SolutionRate { .. } => "solution_rate", #[cfg(feature = "getblocktemplate-rpcs")] ReadRequest::CheckBlockProposalValidity(_) => "check_block_proposal_validity", + #[cfg(feature = "getblocktemplate-rpcs")] + ReadRequest::TipBlockSize => "tip_block_size", } } diff --git a/zebra-state/src/response.rs b/zebra-state/src/response.rs index 22e610838de..77c252b0c75 100644 --- a/zebra-state/src/response.rs +++ b/zebra-state/src/response.rs @@ -229,6 +229,10 @@ pub enum ReadResponse { #[cfg(feature = "getblocktemplate-rpcs")] /// Response to [`ReadRequest::CheckBlockProposalValidity`] ValidBlockProposal, + + #[cfg(feature = "getblocktemplate-rpcs")] + /// Response to [`ReadRequest::TipBlockSize`] + TipBlockSize(Option), } /// A structure with the information needed from the state to build a `getblocktemplate` RPC response. @@ -315,7 +319,7 @@ impl TryFrom for Response { ReadResponse::ValidBlockProposal => Ok(Response::ValidBlockProposal), #[cfg(feature = "getblocktemplate-rpcs")] - ReadResponse::ChainInfo(_) | ReadResponse::SolutionRate(_) => { + ReadResponse::ChainInfo(_) | ReadResponse::SolutionRate(_) | ReadResponse::TipBlockSize(_) => { Err("there is no corresponding Response for this ReadResponse") } } diff --git a/zebra-state/src/service.rs b/zebra-state/src/service.rs index 2116ab10470..4f970be89d4 100644 --- a/zebra-state/src/service.rs +++ b/zebra-state/src/service.rs @@ -39,6 +39,9 @@ use zebra_chain::{ subtree::NoteCommitmentSubtreeIndex, }; +#[cfg(feature = "getblocktemplate-rpcs")] +use zebra_chain::{block::Height, serialization::ZcashSerialize}; + use crate::{ constants::{ MAX_FIND_BLOCK_HASHES_RESULTS, MAX_FIND_BLOCK_HEADERS_RESULTS_FOR_ZEBRA, @@ -1905,6 +1908,46 @@ impl Service for ReadStateService { }) .wait_for_panics() } + + #[cfg(feature = "getblocktemplate-rpcs")] + ReadRequest::TipBlockSize => { + let state = self.clone(); + + tokio::task::spawn_blocking(move || { + span.in_scope(move || { + // Get the best chain tip height. + let tip_height = state + .non_finalized_state_receiver + .with_watch_data(|non_finalized_state| { + read::tip_height(non_finalized_state.best_chain(), &state.db) + }) + .unwrap_or(Height(0)); + + // Get the block at the best chain tip height. + let block = state.non_finalized_state_receiver.with_watch_data( + |non_finalized_state| { + read::block( + non_finalized_state.best_chain(), + &state.db, + tip_height.into(), + ) + }, + ); + + // The work is done in the future. + timer.finish(module_path!(), line!(), "ReadRequest::TipBlockSize"); + + // Respond with the length of the obtained block if any. + match block { + Some(b) => Ok(ReadResponse::TipBlockSize(Some( + b.zcash_serialize_to_vec()?.len(), + ))), + None => Ok(ReadResponse::TipBlockSize(None)), + } + }) + }) + .wait_for_panics() + } } } }