change(rpc): Add value pool balances to `getblockchaininfo` RPC method response (#8769)
* Moves `Zec` type out from behind feature flag * Adds 'ValuePoolBalance` type * Updates getblockchaininfo return type to a BoxFuture * minor refactor * Adds service request * Adds real value balances to getblockchaininfo RPC response * Updates snapshots and the suggested command for updating snapshots * Uses generic error constructors wherever possible and removes outdated TODOs * Updates prop tests to handle mock service requests
This commit is contained in:
parent
11b0833374
commit
83c6725e84
|
@ -14,7 +14,7 @@ pub mod mock;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
use network_chain_tip_height_estimator::NetworkChainTipHeightEstimator;
|
pub use network_chain_tip_height_estimator::NetworkChainTipHeightEstimator;
|
||||||
|
|
||||||
/// An interface for querying the chain tip.
|
/// An interface for querying the chain tip.
|
||||||
///
|
///
|
||||||
|
|
|
@ -21,7 +21,7 @@ use tracing::Instrument;
|
||||||
use zcash_primitives::consensus::Parameters;
|
use zcash_primitives::consensus::Parameters;
|
||||||
use zebra_chain::{
|
use zebra_chain::{
|
||||||
block::{self, Height, SerializedBlock},
|
block::{self, Height, SerializedBlock},
|
||||||
chain_tip::ChainTip,
|
chain_tip::{ChainTip, NetworkChainTipHeightEstimator},
|
||||||
parameters::{ConsensusBranchId, Network, NetworkUpgrade},
|
parameters::{ConsensusBranchId, Network, NetworkUpgrade},
|
||||||
serialization::ZcashDeserialize,
|
serialization::ZcashDeserialize,
|
||||||
subtree::NoteCommitmentSubtreeIndex,
|
subtree::NoteCommitmentSubtreeIndex,
|
||||||
|
@ -45,6 +45,8 @@ use errors::{MapServerError, OkOrServerError};
|
||||||
// We don't use a types/ module here, because it is redundant.
|
// We don't use a types/ module here, because it is redundant.
|
||||||
pub mod trees;
|
pub mod trees;
|
||||||
|
|
||||||
|
pub mod types;
|
||||||
|
|
||||||
#[cfg(feature = "getblocktemplate-rpcs")]
|
#[cfg(feature = "getblocktemplate-rpcs")]
|
||||||
pub mod get_block_template_rpcs;
|
pub mod get_block_template_rpcs;
|
||||||
|
|
||||||
|
@ -85,7 +87,7 @@ pub trait Rpc {
|
||||||
/// Some fields from the zcashd reference are missing from Zebra's [`GetBlockChainInfo`]. It only contains the fields
|
/// Some fields from the zcashd reference are missing from Zebra's [`GetBlockChainInfo`]. It only contains the fields
|
||||||
/// [required for lightwalletd support.](https://github.com/zcash/lightwalletd/blob/v0.4.9/common/common.go#L72-L89)
|
/// [required for lightwalletd support.](https://github.com/zcash/lightwalletd/blob/v0.4.9/common/common.go#L72-L89)
|
||||||
#[rpc(name = "getblockchaininfo")]
|
#[rpc(name = "getblockchaininfo")]
|
||||||
fn get_blockchain_info(&self) -> Result<GetBlockChainInfo>;
|
fn get_blockchain_info(&self) -> BoxFuture<Result<GetBlockChainInfo>>;
|
||||||
|
|
||||||
/// Returns the total balance of a provided `addresses` in an [`AddressBalance`] instance.
|
/// Returns the total balance of a provided `addresses` in an [`AddressBalance`] instance.
|
||||||
///
|
///
|
||||||
|
@ -500,98 +502,120 @@ where
|
||||||
Ok(response)
|
Ok(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: use a generic error constructor (#5548)
|
|
||||||
#[allow(clippy::unwrap_in_result)]
|
#[allow(clippy::unwrap_in_result)]
|
||||||
fn get_blockchain_info(&self) -> Result<GetBlockChainInfo> {
|
fn get_blockchain_info(&self) -> BoxFuture<Result<GetBlockChainInfo>> {
|
||||||
let network = &self.network;
|
let network = self.network.clone();
|
||||||
|
let debug_force_finished_sync = self.debug_force_finished_sync;
|
||||||
|
let mut state = self.state.clone();
|
||||||
|
|
||||||
// `chain` field
|
async move {
|
||||||
let chain = self.network.bip70_network_name();
|
// `chain` field
|
||||||
|
let chain = network.bip70_network_name();
|
||||||
|
|
||||||
// `blocks` and `best_block_hash` fields
|
let request = zebra_state::ReadRequest::TipPoolValues;
|
||||||
let (tip_height, tip_hash) = self
|
let response: zebra_state::ReadResponse = state
|
||||||
.latest_chain_tip
|
.ready()
|
||||||
.best_tip_height_and_hash()
|
.and_then(|service| service.call(request))
|
||||||
.ok_or_server_error("No Chain tip available yet")?;
|
.await
|
||||||
|
.map_server_error()?;
|
||||||
|
|
||||||
// `estimated_height` field
|
let zebra_state::ReadResponse::TipPoolValues {
|
||||||
let current_block_time = self
|
tip_height,
|
||||||
.latest_chain_tip
|
tip_hash,
|
||||||
.best_tip_block_time()
|
value_balance,
|
||||||
.ok_or_server_error("No Chain tip available yet")?;
|
} = response
|
||||||
|
else {
|
||||||
|
unreachable!("unmatched response to a TipPoolValues request")
|
||||||
|
};
|
||||||
|
|
||||||
let zebra_estimated_height = self
|
let request = zebra_state::ReadRequest::BlockHeader(tip_hash.into());
|
||||||
.latest_chain_tip
|
let response: zebra_state::ReadResponse = state
|
||||||
.estimate_network_chain_tip_height(network, Utc::now())
|
.ready()
|
||||||
.ok_or_server_error("No Chain tip available yet")?;
|
.and_then(|service| service.call(request))
|
||||||
|
.await
|
||||||
|
.map_server_error()?;
|
||||||
|
|
||||||
let mut estimated_height =
|
let zebra_state::ReadResponse::BlockHeader(block_header) = response else {
|
||||||
if current_block_time > Utc::now() || zebra_estimated_height < tip_height {
|
unreachable!("unmatched response to a BlockHeader request")
|
||||||
|
};
|
||||||
|
|
||||||
|
let tip_block_time = block_header
|
||||||
|
.ok_or_server_error("unexpectedly could not read best chain tip block header")?
|
||||||
|
.time;
|
||||||
|
|
||||||
|
let now = Utc::now();
|
||||||
|
let zebra_estimated_height =
|
||||||
|
NetworkChainTipHeightEstimator::new(tip_block_time, tip_height, &network)
|
||||||
|
.estimate_height_at(now);
|
||||||
|
|
||||||
|
// If we're testing the mempool, force the estimated height to be the actual tip height, otherwise,
|
||||||
|
// check if the estimated height is below Zebra's latest tip height, or if the latest tip's block time is
|
||||||
|
// later than the current time on the local clock.
|
||||||
|
let estimated_height = if tip_block_time > now
|
||||||
|
|| zebra_estimated_height < tip_height
|
||||||
|
|| debug_force_finished_sync
|
||||||
|
{
|
||||||
tip_height
|
tip_height
|
||||||
} else {
|
} else {
|
||||||
zebra_estimated_height
|
zebra_estimated_height
|
||||||
};
|
};
|
||||||
|
|
||||||
// If we're testing the mempool, force the estimated height to be the actual tip height.
|
// `upgrades` object
|
||||||
if self.debug_force_finished_sync {
|
|
||||||
estimated_height = tip_height;
|
|
||||||
}
|
|
||||||
|
|
||||||
// `upgrades` object
|
|
||||||
//
|
|
||||||
// Get the network upgrades in height order, like `zcashd`.
|
|
||||||
let mut upgrades = IndexMap::new();
|
|
||||||
for (activation_height, network_upgrade) in network.full_activation_list() {
|
|
||||||
// Zebra defines network upgrades based on incompatible consensus rule changes,
|
|
||||||
// but zcashd defines them based on ZIPs.
|
|
||||||
//
|
//
|
||||||
// All the network upgrades with a consensus branch ID are the same in Zebra and zcashd.
|
// Get the network upgrades in height order, like `zcashd`.
|
||||||
if let Some(branch_id) = network_upgrade.branch_id() {
|
let mut upgrades = IndexMap::new();
|
||||||
// zcashd's RPC seems to ignore Disabled network upgrades, so Zebra does too.
|
for (activation_height, network_upgrade) in network.full_activation_list() {
|
||||||
let status = if tip_height >= activation_height {
|
// Zebra defines network upgrades based on incompatible consensus rule changes,
|
||||||
NetworkUpgradeStatus::Active
|
// but zcashd defines them based on ZIPs.
|
||||||
} else {
|
//
|
||||||
NetworkUpgradeStatus::Pending
|
// All the network upgrades with a consensus branch ID are the same in Zebra and zcashd.
|
||||||
};
|
if let Some(branch_id) = network_upgrade.branch_id() {
|
||||||
|
// zcashd's RPC seems to ignore Disabled network upgrades, so Zebra does too.
|
||||||
|
let status = if tip_height >= activation_height {
|
||||||
|
NetworkUpgradeStatus::Active
|
||||||
|
} else {
|
||||||
|
NetworkUpgradeStatus::Pending
|
||||||
|
};
|
||||||
|
|
||||||
let upgrade = NetworkUpgradeInfo {
|
let upgrade = NetworkUpgradeInfo {
|
||||||
name: network_upgrade,
|
name: network_upgrade,
|
||||||
activation_height,
|
activation_height,
|
||||||
status,
|
status,
|
||||||
};
|
};
|
||||||
upgrades.insert(ConsensusBranchIdHex(branch_id), upgrade);
|
upgrades.insert(ConsensusBranchIdHex(branch_id), upgrade);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// `consensus` object
|
||||||
|
let next_block_height =
|
||||||
|
(tip_height + 1).expect("valid chain tips are a lot less than Height::MAX");
|
||||||
|
let consensus = TipConsensusBranch {
|
||||||
|
chain_tip: ConsensusBranchIdHex(
|
||||||
|
NetworkUpgrade::current(&network, tip_height)
|
||||||
|
.branch_id()
|
||||||
|
.unwrap_or(ConsensusBranchId::RPC_MISSING_ID),
|
||||||
|
),
|
||||||
|
next_block: ConsensusBranchIdHex(
|
||||||
|
NetworkUpgrade::current(&network, next_block_height)
|
||||||
|
.branch_id()
|
||||||
|
.unwrap_or(ConsensusBranchId::RPC_MISSING_ID),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
let response = GetBlockChainInfo {
|
||||||
|
chain,
|
||||||
|
blocks: tip_height,
|
||||||
|
best_block_hash: tip_hash,
|
||||||
|
estimated_height,
|
||||||
|
value_pools: types::ValuePoolBalance::from_value_balance(value_balance),
|
||||||
|
upgrades,
|
||||||
|
consensus,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(response)
|
||||||
}
|
}
|
||||||
|
.boxed()
|
||||||
// `consensus` object
|
|
||||||
let next_block_height =
|
|
||||||
(tip_height + 1).expect("valid chain tips are a lot less than Height::MAX");
|
|
||||||
let consensus = TipConsensusBranch {
|
|
||||||
chain_tip: ConsensusBranchIdHex(
|
|
||||||
NetworkUpgrade::current(network, tip_height)
|
|
||||||
.branch_id()
|
|
||||||
.unwrap_or(ConsensusBranchId::RPC_MISSING_ID),
|
|
||||||
),
|
|
||||||
next_block: ConsensusBranchIdHex(
|
|
||||||
NetworkUpgrade::current(network, next_block_height)
|
|
||||||
.branch_id()
|
|
||||||
.unwrap_or(ConsensusBranchId::RPC_MISSING_ID),
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
let response = GetBlockChainInfo {
|
|
||||||
chain,
|
|
||||||
blocks: tip_height,
|
|
||||||
best_block_hash: tip_hash,
|
|
||||||
estimated_height,
|
|
||||||
upgrades,
|
|
||||||
consensus,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(response)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: use a generic error constructor (#5548)
|
|
||||||
fn get_address_balance(
|
fn get_address_balance(
|
||||||
&self,
|
&self,
|
||||||
address_strings: AddressStrings,
|
address_strings: AddressStrings,
|
||||||
|
@ -615,7 +639,6 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: use HexData or GetRawTransaction::Bytes to handle the transaction data argument
|
// TODO: use HexData or GetRawTransaction::Bytes to handle the transaction data argument
|
||||||
// use a generic error constructor (#5548)
|
|
||||||
fn send_raw_transaction(
|
fn send_raw_transaction(
|
||||||
&self,
|
&self,
|
||||||
raw_transaction_hex: String,
|
raw_transaction_hex: String,
|
||||||
|
@ -963,7 +986,6 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: use HexData or SentTransactionHash to handle the transaction ID
|
// TODO: use HexData or SentTransactionHash to handle the transaction ID
|
||||||
// use a generic error constructor (#5548)
|
|
||||||
fn get_raw_transaction(
|
fn get_raw_transaction(
|
||||||
&self,
|
&self,
|
||||||
txid_hex: String,
|
txid_hex: String,
|
||||||
|
@ -1197,7 +1219,6 @@ where
|
||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: use a generic error constructor (#5548)
|
|
||||||
fn get_address_tx_ids(
|
fn get_address_tx_ids(
|
||||||
&self,
|
&self,
|
||||||
request: GetAddressTxIdsRequest,
|
request: GetAddressTxIdsRequest,
|
||||||
|
@ -1258,7 +1279,6 @@ where
|
||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: use a generic error constructor (#5548)
|
|
||||||
fn get_address_utxos(
|
fn get_address_utxos(
|
||||||
&self,
|
&self,
|
||||||
address_strings: AddressStrings,
|
address_strings: AddressStrings,
|
||||||
|
@ -1372,6 +1392,10 @@ pub struct GetBlockChainInfo {
|
||||||
#[serde(rename = "estimatedheight")]
|
#[serde(rename = "estimatedheight")]
|
||||||
estimated_height: Height,
|
estimated_height: Height,
|
||||||
|
|
||||||
|
/// Value pool balances
|
||||||
|
#[serde(rename = "valuePools")]
|
||||||
|
value_pools: [types::ValuePoolBalance; 5],
|
||||||
|
|
||||||
/// Status of network upgrades
|
/// Status of network upgrades
|
||||||
upgrades: IndexMap<ConsensusBranchIdHex, NetworkUpgradeInfo>,
|
upgrades: IndexMap<ConsensusBranchIdHex, NetworkUpgradeInfo>,
|
||||||
|
|
||||||
|
@ -1386,6 +1410,7 @@ impl Default for GetBlockChainInfo {
|
||||||
blocks: Height(1),
|
blocks: Height(1),
|
||||||
best_block_hash: block::Hash([0; 32]),
|
best_block_hash: block::Hash([0; 32]),
|
||||||
estimated_height: Height(1),
|
estimated_height: Height(1),
|
||||||
|
value_pools: types::ValuePoolBalance::zero_pools(),
|
||||||
upgrades: IndexMap::new(),
|
upgrades: IndexMap::new(),
|
||||||
consensus: TipConsensusBranch {
|
consensus: TipConsensusBranch {
|
||||||
chain_tip: ConsensusBranchIdHex(ConsensusBranchId::default()),
|
chain_tip: ConsensusBranchIdHex(ConsensusBranchId::default()),
|
||||||
|
|
|
@ -551,7 +551,6 @@ where
|
||||||
best_chain_tip_height(&self.latest_chain_tip).map(|height| height.0)
|
best_chain_tip_height(&self.latest_chain_tip).map(|height| height.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: use a generic error constructor (#5548)
|
|
||||||
fn get_block_hash(&self, index: i32) -> BoxFuture<Result<GetBlockHash>> {
|
fn get_block_hash(&self, index: i32) -> BoxFuture<Result<GetBlockHash>> {
|
||||||
let mut state = self.state.clone();
|
let mut state = self.state.clone();
|
||||||
let latest_chain_tip = self.latest_chain_tip.clone();
|
let latest_chain_tip = self.latest_chain_tip.clone();
|
||||||
|
@ -567,11 +566,7 @@ where
|
||||||
.ready()
|
.ready()
|
||||||
.and_then(|service| service.call(request))
|
.and_then(|service| service.call(request))
|
||||||
.await
|
.await
|
||||||
.map_err(|error| Error {
|
.map_server_error()?;
|
||||||
code: ErrorCode::ServerError(0),
|
|
||||||
message: error.to_string(),
|
|
||||||
data: None,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
match response {
|
match response {
|
||||||
zebra_state::ReadResponse::BlockHash(Some(hash)) => Ok(GetBlockHash(hash)),
|
zebra_state::ReadResponse::BlockHash(Some(hash)) => Ok(GetBlockHash(hash)),
|
||||||
|
@ -586,7 +581,6 @@ where
|
||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: use a generic error constructor (#5548)
|
|
||||||
fn get_block_template(
|
fn get_block_template(
|
||||||
&self,
|
&self,
|
||||||
parameters: Option<get_block_template::JsonParameters>,
|
parameters: Option<get_block_template::JsonParameters>,
|
||||||
|
@ -830,11 +824,7 @@ where
|
||||||
Is Zebra shutting down?"
|
Is Zebra shutting down?"
|
||||||
);
|
);
|
||||||
|
|
||||||
return Err(Error {
|
return Err(recv_error).map_server_error();
|
||||||
code: ErrorCode::ServerError(0),
|
|
||||||
message: recv_error.to_string(),
|
|
||||||
data: None,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,4 +11,3 @@ pub mod transaction;
|
||||||
pub mod unified_address;
|
pub mod unified_address;
|
||||||
pub mod validate_address;
|
pub mod validate_address;
|
||||||
pub mod z_validate_address;
|
pub mod z_validate_address;
|
||||||
pub mod zec;
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ use zebra_chain::{
|
||||||
transparent,
|
transparent,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::methods::get_block_template_rpcs::types::zec::Zec;
|
use crate::methods::types::Zec;
|
||||||
|
|
||||||
/// A response to a `getblocksubsidy` RPC request
|
/// A response to a `getblocksubsidy` RPC request
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! Randomised property tests for RPC methods.
|
//! Randomised property tests for RPC methods.
|
||||||
|
|
||||||
use std::collections::HashSet;
|
use std::{collections::HashSet, sync::Arc};
|
||||||
|
|
||||||
use futures::{join, FutureExt, TryFutureExt};
|
use futures::{join, FutureExt, TryFutureExt};
|
||||||
use hex::ToHex;
|
use hex::ToHex;
|
||||||
|
@ -11,7 +11,7 @@ use tower::buffer::Buffer;
|
||||||
|
|
||||||
use zebra_chain::{
|
use zebra_chain::{
|
||||||
amount::{Amount, NonNegative},
|
amount::{Amount, NonNegative},
|
||||||
block::{Block, Height},
|
block::{self, Block, Height},
|
||||||
chain_tip::{mock::MockChainTip, NoChainTip},
|
chain_tip::{mock::MockChainTip, NoChainTip},
|
||||||
parameters::{
|
parameters::{
|
||||||
Network::{self, *},
|
Network::{self, *},
|
||||||
|
@ -20,6 +20,7 @@ use zebra_chain::{
|
||||||
serialization::{ZcashDeserialize, ZcashSerialize},
|
serialization::{ZcashDeserialize, ZcashSerialize},
|
||||||
transaction::{self, Transaction, UnminedTx, VerifiedUnminedTx},
|
transaction::{self, Transaction, UnminedTx, VerifiedUnminedTx},
|
||||||
transparent,
|
transparent,
|
||||||
|
value_balance::ValueBalance,
|
||||||
};
|
};
|
||||||
use zebra_node_services::mempool;
|
use zebra_node_services::mempool;
|
||||||
use zebra_state::BoxError;
|
use zebra_state::BoxError;
|
||||||
|
@ -553,19 +554,34 @@ proptest! {
|
||||||
NoChainTip,
|
NoChainTip,
|
||||||
);
|
);
|
||||||
|
|
||||||
let response = rpc.get_blockchain_info();
|
|
||||||
prop_assert_eq!(
|
|
||||||
&response.err().unwrap().message,
|
|
||||||
"No Chain tip available yet"
|
|
||||||
);
|
|
||||||
|
|
||||||
// The queue task should continue without errors or panics
|
|
||||||
let rpc_tx_queue_task_result = rpc_tx_queue_task_handle.now_or_never();
|
|
||||||
prop_assert!(rpc_tx_queue_task_result.is_none());
|
|
||||||
|
|
||||||
runtime.block_on(async move {
|
runtime.block_on(async move {
|
||||||
|
let response_fut = rpc.get_blockchain_info();
|
||||||
|
let mock_state_handler = {
|
||||||
|
let mut state = state.clone();
|
||||||
|
async move {
|
||||||
|
state
|
||||||
|
.expect_request(zebra_state::ReadRequest::TipPoolValues)
|
||||||
|
.await
|
||||||
|
.expect("getblockchaininfo should call mock state service with correct request")
|
||||||
|
.respond(Err(BoxError::from("no chain tip available yet")));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let (response, _) = tokio::join!(response_fut, mock_state_handler);
|
||||||
|
|
||||||
|
prop_assert_eq!(
|
||||||
|
&response.err().unwrap().message,
|
||||||
|
"no chain tip available yet"
|
||||||
|
);
|
||||||
|
|
||||||
|
// The queue task should continue without errors or panics
|
||||||
|
let rpc_tx_queue_task_result = rpc_tx_queue_task_handle.now_or_never();
|
||||||
|
prop_assert!(rpc_tx_queue_task_result.is_none());
|
||||||
|
|
||||||
mempool.expect_no_requests().await?;
|
mempool.expect_no_requests().await?;
|
||||||
state.expect_no_requests().await?;
|
state.expect_no_requests().await?;
|
||||||
|
|
||||||
Ok::<_, TestCaseError>(())
|
Ok::<_, TestCaseError>(())
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
@ -581,17 +597,11 @@ proptest! {
|
||||||
let mut mempool = MockService::build().for_prop_tests();
|
let mut mempool = MockService::build().for_prop_tests();
|
||||||
let mut state: MockService<_, _, _, BoxError> = MockService::build().for_prop_tests();
|
let mut state: MockService<_, _, _, BoxError> = MockService::build().for_prop_tests();
|
||||||
|
|
||||||
// get block data
|
// get arbitrary chain tip data
|
||||||
let block_height = block.coinbase_height().unwrap();
|
let block_height = block.coinbase_height().unwrap();
|
||||||
let block_hash = block.hash();
|
let block_hash = block.hash();
|
||||||
let block_time = block.header.time;
|
let block_time = block.header.time;
|
||||||
|
|
||||||
// create a mocked `ChainTip`
|
|
||||||
let (chain_tip, mock_chain_tip_sender) = MockChainTip::new();
|
|
||||||
mock_chain_tip_sender.send_best_tip_height(block_height);
|
|
||||||
mock_chain_tip_sender.send_best_tip_hash(block_hash);
|
|
||||||
mock_chain_tip_sender.send_best_tip_block_time(block_time);
|
|
||||||
|
|
||||||
// Start RPC with the mocked `ChainTip`
|
// Start RPC with the mocked `ChainTip`
|
||||||
let (rpc, rpc_tx_queue_task_handle) = RpcImpl::new(
|
let (rpc, rpc_tx_queue_task_handle) = RpcImpl::new(
|
||||||
"RPC test",
|
"RPC test",
|
||||||
|
@ -601,50 +611,82 @@ proptest! {
|
||||||
true,
|
true,
|
||||||
mempool.clone(),
|
mempool.clone(),
|
||||||
Buffer::new(state.clone(), 1),
|
Buffer::new(state.clone(), 1),
|
||||||
chain_tip,
|
NoChainTip,
|
||||||
);
|
);
|
||||||
let response = rpc.get_blockchain_info();
|
|
||||||
|
|
||||||
// Check response
|
|
||||||
match response {
|
|
||||||
Ok(info) => {
|
|
||||||
prop_assert_eq!(info.chain, network.bip70_network_name());
|
|
||||||
prop_assert_eq!(info.blocks, block_height);
|
|
||||||
prop_assert_eq!(info.best_block_hash, block_hash);
|
|
||||||
prop_assert!(info.estimated_height < Height::MAX);
|
|
||||||
|
|
||||||
prop_assert_eq!(
|
|
||||||
info.consensus.chain_tip.0,
|
|
||||||
NetworkUpgrade::current(&network, block_height)
|
|
||||||
.branch_id()
|
|
||||||
.unwrap()
|
|
||||||
);
|
|
||||||
prop_assert_eq!(
|
|
||||||
info.consensus.next_block.0,
|
|
||||||
NetworkUpgrade::current(&network, (block_height + 1).unwrap())
|
|
||||||
.branch_id()
|
|
||||||
.unwrap()
|
|
||||||
);
|
|
||||||
|
|
||||||
for u in info.upgrades {
|
|
||||||
let mut status = NetworkUpgradeStatus::Active;
|
|
||||||
if block_height < u.1.activation_height {
|
|
||||||
status = NetworkUpgradeStatus::Pending;
|
|
||||||
}
|
|
||||||
prop_assert_eq!(u.1.status, status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
unreachable!("Test should never error with the data we are feeding it")
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// The queue task should continue without errors or panics
|
|
||||||
let rpc_tx_queue_task_result = rpc_tx_queue_task_handle.now_or_never();
|
|
||||||
prop_assert!(rpc_tx_queue_task_result.is_none());
|
|
||||||
|
|
||||||
// check no requests were made during this test
|
// check no requests were made during this test
|
||||||
runtime.block_on(async move {
|
runtime.block_on(async move {
|
||||||
|
let response_fut = rpc.get_blockchain_info();
|
||||||
|
let mock_state_handler = {
|
||||||
|
let mut state = state.clone();
|
||||||
|
async move {
|
||||||
|
state
|
||||||
|
.expect_request(zebra_state::ReadRequest::TipPoolValues)
|
||||||
|
.await
|
||||||
|
.expect("getblockchaininfo should call mock state service with correct request")
|
||||||
|
.respond(zebra_state::ReadResponse::TipPoolValues {
|
||||||
|
tip_height: block_height,
|
||||||
|
tip_hash: block_hash,
|
||||||
|
value_balance: ValueBalance::default(),
|
||||||
|
});
|
||||||
|
|
||||||
|
state
|
||||||
|
.expect_request(zebra_state::ReadRequest::BlockHeader(block_hash.into()))
|
||||||
|
.await
|
||||||
|
.expect("getblockchaininfo should call mock state service with correct request")
|
||||||
|
.respond(zebra_state::ReadResponse::BlockHeader(Some(Arc::new(block::Header {
|
||||||
|
time: block_time,
|
||||||
|
version: Default::default(),
|
||||||
|
previous_block_hash: Default::default(),
|
||||||
|
merkle_root: Default::default(),
|
||||||
|
commitment_bytes: Default::default(),
|
||||||
|
difficulty_threshold: Default::default(),
|
||||||
|
nonce: Default::default(),
|
||||||
|
solution: Default::default()
|
||||||
|
}))));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let (response, _) = tokio::join!(response_fut, mock_state_handler);
|
||||||
|
|
||||||
|
// Check response
|
||||||
|
match response {
|
||||||
|
Ok(info) => {
|
||||||
|
prop_assert_eq!(info.chain, network.bip70_network_name());
|
||||||
|
prop_assert_eq!(info.blocks, block_height);
|
||||||
|
prop_assert_eq!(info.best_block_hash, block_hash);
|
||||||
|
prop_assert!(info.estimated_height < Height::MAX);
|
||||||
|
|
||||||
|
prop_assert_eq!(
|
||||||
|
info.consensus.chain_tip.0,
|
||||||
|
NetworkUpgrade::current(&network, block_height)
|
||||||
|
.branch_id()
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
prop_assert_eq!(
|
||||||
|
info.consensus.next_block.0,
|
||||||
|
NetworkUpgrade::current(&network, (block_height + 1).unwrap())
|
||||||
|
.branch_id()
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
for u in info.upgrades {
|
||||||
|
let mut status = NetworkUpgradeStatus::Active;
|
||||||
|
if block_height < u.1.activation_height {
|
||||||
|
status = NetworkUpgradeStatus::Pending;
|
||||||
|
}
|
||||||
|
prop_assert_eq!(u.1.status, status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
unreachable!("Test should never error with the data we are feeding it")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// The queue task should continue without errors or panics
|
||||||
|
let rpc_tx_queue_task_result = rpc_tx_queue_task_handle.now_or_never();
|
||||||
|
prop_assert!(rpc_tx_queue_task_result.is_none());
|
||||||
|
|
||||||
mempool.expect_no_requests().await?;
|
mempool.expect_no_requests().await?;
|
||||||
state.expect_no_requests().await?;
|
state.expect_no_requests().await?;
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
//!
|
//!
|
||||||
//! To update these snapshots, run:
|
//! To update these snapshots, run:
|
||||||
//! ```sh
|
//! ```sh
|
||||||
//! cargo insta test --review -p zebra-rpc --lib -- test_rpc_response_data
|
//! cargo insta test --review --release -p zebra-rpc --lib -- test_rpc_response_data
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
@ -210,6 +210,7 @@ async fn test_rpc_response_data_for_network(network: &Network) {
|
||||||
if network.is_a_test_network() && !network.is_default_testnet() {
|
if network.is_a_test_network() && !network.is_default_testnet() {
|
||||||
let get_blockchain_info = rpc
|
let get_blockchain_info = rpc
|
||||||
.get_blockchain_info()
|
.get_blockchain_info()
|
||||||
|
.await
|
||||||
.expect("We should have a GetBlockChainInfo struct");
|
.expect("We should have a GetBlockChainInfo struct");
|
||||||
snapshot_rpc_getblockchaininfo("_future_nu6_height", get_blockchain_info, &settings);
|
snapshot_rpc_getblockchaininfo("_future_nu6_height", get_blockchain_info, &settings);
|
||||||
|
|
||||||
|
@ -223,6 +224,7 @@ async fn test_rpc_response_data_for_network(network: &Network) {
|
||||||
// `getblockchaininfo`
|
// `getblockchaininfo`
|
||||||
let get_blockchain_info = rpc
|
let get_blockchain_info = rpc
|
||||||
.get_blockchain_info()
|
.get_blockchain_info()
|
||||||
|
.await
|
||||||
.expect("We should have a GetBlockChainInfo struct");
|
.expect("We should have a GetBlockChainInfo struct");
|
||||||
snapshot_rpc_getblockchaininfo("", get_blockchain_info, &settings);
|
snapshot_rpc_getblockchaininfo("", get_blockchain_info, &settings);
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,33 @@ expression: info
|
||||||
"blocks": 10,
|
"blocks": 10,
|
||||||
"bestblockhash": "00074c46a4aa8172df8ae2ad1848a2e084e1b6989b7d9e6132adc938bf835b36",
|
"bestblockhash": "00074c46a4aa8172df8ae2ad1848a2e084e1b6989b7d9e6132adc938bf835b36",
|
||||||
"estimatedheight": "[Height]",
|
"estimatedheight": "[Height]",
|
||||||
|
"valuePools": [
|
||||||
|
{
|
||||||
|
"id": "transparent",
|
||||||
|
"chainValue": 0.034375,
|
||||||
|
"chainValueZat": 3437500
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "sprout",
|
||||||
|
"chainValue": 0.0,
|
||||||
|
"chainValueZat": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "sapling",
|
||||||
|
"chainValue": 0.0,
|
||||||
|
"chainValueZat": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "orchard",
|
||||||
|
"chainValue": 0.0,
|
||||||
|
"chainValueZat": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "deferred",
|
||||||
|
"chainValue": 0.0,
|
||||||
|
"chainValueZat": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
"upgrades": {
|
"upgrades": {
|
||||||
"5ba81b19": {
|
"5ba81b19": {
|
||||||
"name": "Overwinter",
|
"name": "Overwinter",
|
||||||
|
|
|
@ -7,6 +7,33 @@ expression: info
|
||||||
"blocks": 10,
|
"blocks": 10,
|
||||||
"bestblockhash": "079f4c752729be63e6341ee9bce42fbbe37236aba22e3deb82405f3c2805c112",
|
"bestblockhash": "079f4c752729be63e6341ee9bce42fbbe37236aba22e3deb82405f3c2805c112",
|
||||||
"estimatedheight": "[Height]",
|
"estimatedheight": "[Height]",
|
||||||
|
"valuePools": [
|
||||||
|
{
|
||||||
|
"id": "transparent",
|
||||||
|
"chainValue": 0.034375,
|
||||||
|
"chainValueZat": 3437500
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "sprout",
|
||||||
|
"chainValue": 0.0,
|
||||||
|
"chainValueZat": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "sapling",
|
||||||
|
"chainValue": 0.0,
|
||||||
|
"chainValueZat": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "orchard",
|
||||||
|
"chainValue": 0.0,
|
||||||
|
"chainValueZat": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "deferred",
|
||||||
|
"chainValue": 0.0,
|
||||||
|
"chainValueZat": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
"upgrades": {
|
"upgrades": {
|
||||||
"5ba81b19": {
|
"5ba81b19": {
|
||||||
"name": "Overwinter",
|
"name": "Overwinter",
|
||||||
|
|
|
@ -7,6 +7,33 @@ expression: info
|
||||||
"blocks": 10,
|
"blocks": 10,
|
||||||
"bestblockhash": "079f4c752729be63e6341ee9bce42fbbe37236aba22e3deb82405f3c2805c112",
|
"bestblockhash": "079f4c752729be63e6341ee9bce42fbbe37236aba22e3deb82405f3c2805c112",
|
||||||
"estimatedheight": "[Height]",
|
"estimatedheight": "[Height]",
|
||||||
|
"valuePools": [
|
||||||
|
{
|
||||||
|
"id": "transparent",
|
||||||
|
"chainValue": 0.034375,
|
||||||
|
"chainValueZat": 3437500
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "sprout",
|
||||||
|
"chainValue": 0.0,
|
||||||
|
"chainValueZat": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "sapling",
|
||||||
|
"chainValue": 0.0,
|
||||||
|
"chainValueZat": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "orchard",
|
||||||
|
"chainValue": 0.0,
|
||||||
|
"chainValueZat": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "deferred",
|
||||||
|
"chainValue": 0.0,
|
||||||
|
"chainValueZat": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
"upgrades": {
|
"upgrades": {
|
||||||
"5ba81b19": {
|
"5ba81b19": {
|
||||||
"name": "Overwinter",
|
"name": "Overwinter",
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
//! Types used in RPC methods.
|
||||||
|
|
||||||
|
mod get_blockchain_info;
|
||||||
|
mod zec;
|
||||||
|
|
||||||
|
pub use get_blockchain_info::ValuePoolBalance;
|
||||||
|
pub use zec::Zec;
|
|
@ -0,0 +1,72 @@
|
||||||
|
//! Types used in `getblockchaininfo` RPC method.
|
||||||
|
|
||||||
|
use zebra_chain::{
|
||||||
|
amount::{Amount, NonNegative},
|
||||||
|
value_balance::ValueBalance,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// A value pool's balance in Zec and Zatoshis
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct ValuePoolBalance {
|
||||||
|
/// Name of the pool
|
||||||
|
id: String,
|
||||||
|
/// Total amount in the pool, in ZEC
|
||||||
|
chain_value: Zec<NonNegative>,
|
||||||
|
/// Total amount in the pool, in zatoshis
|
||||||
|
chain_value_zat: Amount<NonNegative>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ValuePoolBalance {
|
||||||
|
/// Returns a list of [`ValuePoolBalance`]s converted from the default [`ValueBalance`].
|
||||||
|
pub fn zero_pools() -> [Self; 5] {
|
||||||
|
Self::from_value_balance(Default::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new [`ValuePoolBalance`] from a pool name and its value balance.
|
||||||
|
pub fn new(id: impl ToString, amount: Amount<NonNegative>) -> Self {
|
||||||
|
Self {
|
||||||
|
id: id.to_string(),
|
||||||
|
chain_value: Zec::from(amount),
|
||||||
|
chain_value_zat: amount,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a [`ValuePoolBalance`] for the transparent pool.
|
||||||
|
pub fn transparent(amount: Amount<NonNegative>) -> Self {
|
||||||
|
Self::new("transparent", amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a [`ValuePoolBalance`] for the Sprout pool.
|
||||||
|
pub fn sprout(amount: Amount<NonNegative>) -> Self {
|
||||||
|
Self::new("sprout", amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a [`ValuePoolBalance`] for the Sapling pool.
|
||||||
|
pub fn sapling(amount: Amount<NonNegative>) -> Self {
|
||||||
|
Self::new("sapling", amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a [`ValuePoolBalance`] for the Orchard pool.
|
||||||
|
pub fn orchard(amount: Amount<NonNegative>) -> Self {
|
||||||
|
Self::new("orchard", amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a [`ValuePoolBalance`] for the Deferred pool.
|
||||||
|
pub fn deferred(amount: Amount<NonNegative>) -> Self {
|
||||||
|
Self::new("deferred", amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts a [`ValueBalance`] to a list of [`ValuePoolBalance`]s.
|
||||||
|
pub fn from_value_balance(value_balance: ValueBalance<NonNegative>) -> [Self; 5] {
|
||||||
|
[
|
||||||
|
Self::transparent(value_balance.transparent_amount()),
|
||||||
|
Self::sprout(value_balance.sprout_amount()),
|
||||||
|
Self::sapling(value_balance.sapling_amount()),
|
||||||
|
Self::orchard(value_balance.orchard_amount()),
|
||||||
|
Self::deferred(value_balance.deferred_amount()),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -815,6 +815,10 @@ pub enum ReadRequest {
|
||||||
/// with the current best chain tip.
|
/// with the current best chain tip.
|
||||||
Tip,
|
Tip,
|
||||||
|
|
||||||
|
/// Returns [`ReadResponse::TipPoolValues(Option<(Height, block::Hash, ValueBalance)>)`](ReadResponse::TipPoolValues)
|
||||||
|
/// with the current best chain tip.
|
||||||
|
TipPoolValues,
|
||||||
|
|
||||||
/// Computes the depth in the current best chain of the block identified by the given hash.
|
/// Computes the depth in the current best chain of the block identified by the given hash.
|
||||||
///
|
///
|
||||||
/// Returns
|
/// Returns
|
||||||
|
@ -1065,6 +1069,7 @@ impl ReadRequest {
|
||||||
fn variant_name(&self) -> &'static str {
|
fn variant_name(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
ReadRequest::Tip => "tip",
|
ReadRequest::Tip => "tip",
|
||||||
|
ReadRequest::TipPoolValues => "tip_pool_values",
|
||||||
ReadRequest::Depth(_) => "depth",
|
ReadRequest::Depth(_) => "depth",
|
||||||
ReadRequest::Block(_) => "block",
|
ReadRequest::Block(_) => "block",
|
||||||
ReadRequest::BlockHeader(_) => "block_header",
|
ReadRequest::BlockHeader(_) => "block_header",
|
||||||
|
|
|
@ -10,6 +10,7 @@ use zebra_chain::{
|
||||||
subtree::{NoteCommitmentSubtreeData, NoteCommitmentSubtreeIndex},
|
subtree::{NoteCommitmentSubtreeData, NoteCommitmentSubtreeIndex},
|
||||||
transaction::{self, Transaction},
|
transaction::{self, Transaction},
|
||||||
transparent,
|
transparent,
|
||||||
|
value_balance::ValueBalance,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "getblocktemplate-rpcs")]
|
#[cfg(feature = "getblocktemplate-rpcs")]
|
||||||
|
@ -128,6 +129,17 @@ pub enum ReadResponse {
|
||||||
/// Response to [`ReadRequest::Tip`] with the current best chain tip.
|
/// Response to [`ReadRequest::Tip`] with the current best chain tip.
|
||||||
Tip(Option<(block::Height, block::Hash)>),
|
Tip(Option<(block::Height, block::Hash)>),
|
||||||
|
|
||||||
|
/// Response to [`ReadRequest::TipPoolValues`] with
|
||||||
|
/// the current best chain tip and its [`ValueBalance`].
|
||||||
|
TipPoolValues {
|
||||||
|
/// The current best chain tip height.
|
||||||
|
tip_height: block::Height,
|
||||||
|
/// The current best chain tip hash.
|
||||||
|
tip_hash: block::Hash,
|
||||||
|
/// The value pool balance at the current best chain tip.
|
||||||
|
value_balance: ValueBalance<NonNegative>,
|
||||||
|
},
|
||||||
|
|
||||||
/// Response to [`ReadRequest::Depth`] with the depth of the specified block.
|
/// Response to [`ReadRequest::Depth`] with the depth of the specified block.
|
||||||
Depth(Option<u32>),
|
Depth(Option<u32>),
|
||||||
|
|
||||||
|
@ -287,7 +299,8 @@ impl TryFrom<ReadResponse> for Response {
|
||||||
|
|
||||||
ReadResponse::ValidBestChainTipNullifiersAndAnchors => Ok(Response::ValidBestChainTipNullifiersAndAnchors),
|
ReadResponse::ValidBestChainTipNullifiersAndAnchors => Ok(Response::ValidBestChainTipNullifiersAndAnchors),
|
||||||
|
|
||||||
ReadResponse::TransactionIdsForBlock(_)
|
ReadResponse::TipPoolValues { .. }
|
||||||
|
| ReadResponse::TransactionIdsForBlock(_)
|
||||||
| ReadResponse::SaplingTree(_)
|
| ReadResponse::SaplingTree(_)
|
||||||
| ReadResponse::OrchardTree(_)
|
| ReadResponse::OrchardTree(_)
|
||||||
| ReadResponse::SaplingSubtrees(_)
|
| ReadResponse::SaplingSubtrees(_)
|
||||||
|
|
|
@ -1192,6 +1192,38 @@ impl Service<ReadRequest> for ReadStateService {
|
||||||
.wait_for_panics()
|
.wait_for_panics()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Used by `getblockchaininfo` RPC method.
|
||||||
|
ReadRequest::TipPoolValues => {
|
||||||
|
let state = self.clone();
|
||||||
|
|
||||||
|
tokio::task::spawn_blocking(move || {
|
||||||
|
span.in_scope(move || {
|
||||||
|
let tip_with_value_balance = state
|
||||||
|
.non_finalized_state_receiver
|
||||||
|
.with_watch_data(|non_finalized_state| {
|
||||||
|
read::tip_with_value_balance(
|
||||||
|
non_finalized_state.best_chain(),
|
||||||
|
&state.db,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
// The work is done in the future.
|
||||||
|
// TODO: Do this in the Drop impl with the variant name?
|
||||||
|
timer.finish(module_path!(), line!(), "ReadRequest::TipPoolValues");
|
||||||
|
|
||||||
|
let (tip_height, tip_hash, value_balance) = tip_with_value_balance?
|
||||||
|
.ok_or(BoxError::from("no chain tip available yet"))?;
|
||||||
|
|
||||||
|
Ok(ReadResponse::TipPoolValues {
|
||||||
|
tip_height,
|
||||||
|
tip_hash,
|
||||||
|
value_balance,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.wait_for_panics()
|
||||||
|
}
|
||||||
|
|
||||||
// Used by the StateService.
|
// Used by the StateService.
|
||||||
ReadRequest::Depth(hash) => {
|
ReadRequest::Depth(hash) => {
|
||||||
let state = self.clone();
|
let state = self.clone();
|
||||||
|
|
|
@ -482,6 +482,17 @@ impl Chain {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the non-finalized tip block height, hash, and total pool value balances.
|
||||||
|
pub fn non_finalized_tip_with_value_balance(
|
||||||
|
&self,
|
||||||
|
) -> (Height, block::Hash, ValueBalance<NonNegative>) {
|
||||||
|
(
|
||||||
|
self.non_finalized_tip_height(),
|
||||||
|
self.non_finalized_tip_hash(),
|
||||||
|
self.chain_value_pools,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the Sprout note commitment tree of the tip of this [`Chain`],
|
/// Returns the Sprout note commitment tree of the tip of this [`Chain`],
|
||||||
/// including all finalized notes, and the non-finalized notes in this chain.
|
/// including all finalized notes, and the non-finalized notes in this chain.
|
||||||
///
|
///
|
||||||
|
|
|
@ -36,7 +36,7 @@ pub use block::{
|
||||||
pub use find::{
|
pub use find::{
|
||||||
best_tip, block_locator, depth, finalized_state_contains_block_hash, find_chain_hashes,
|
best_tip, block_locator, depth, finalized_state_contains_block_hash, find_chain_hashes,
|
||||||
find_chain_headers, hash_by_height, height_by_hash, next_median_time_past,
|
find_chain_headers, hash_by_height, height_by_hash, next_median_time_past,
|
||||||
non_finalized_state_contains_block_hash, tip, tip_height,
|
non_finalized_state_contains_block_hash, tip, tip_height, tip_with_value_balance,
|
||||||
};
|
};
|
||||||
pub use tree::{orchard_subtrees, orchard_tree, sapling_subtrees, sapling_tree};
|
pub use tree::{orchard_subtrees, orchard_tree, sapling_subtrees, sapling_tree};
|
||||||
|
|
||||||
|
|
|
@ -19,8 +19,10 @@ use std::{
|
||||||
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use zebra_chain::{
|
use zebra_chain::{
|
||||||
|
amount::NonNegative,
|
||||||
block::{self, Block, Height},
|
block::{self, Block, Height},
|
||||||
serialization::DateTime32,
|
serialization::DateTime32,
|
||||||
|
value_balance::ValueBalance,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -82,6 +84,40 @@ where
|
||||||
tip(chain, db).map(|(_height, hash)| hash)
|
tip(chain, db).map(|(_height, hash)| hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the tip of `chain` with its [`ValueBalance`].
|
||||||
|
/// If there is no chain, returns the tip of `db`.
|
||||||
|
pub fn tip_with_value_balance<C>(
|
||||||
|
chain: Option<C>,
|
||||||
|
db: &ZebraDb,
|
||||||
|
) -> Result<Option<(Height, block::Hash, ValueBalance<NonNegative>)>, BoxError>
|
||||||
|
where
|
||||||
|
C: AsRef<Chain>,
|
||||||
|
{
|
||||||
|
match chain.map(|chain| chain.as_ref().non_finalized_tip_with_value_balance()) {
|
||||||
|
tip_with_value_balance @ Some(_) => Ok(tip_with_value_balance),
|
||||||
|
None => {
|
||||||
|
// Retry the finalized state query if it was interrupted by a finalizing block.
|
||||||
|
//
|
||||||
|
// TODO: refactor this into a generic retry(finalized_closure, process_and_check_closure) fn
|
||||||
|
for _ in 0..=FINALIZED_STATE_QUERY_RETRIES {
|
||||||
|
let tip @ Some((tip_height, tip_hash)) = db.tip() else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
let value_balance = db.finalized_value_pool();
|
||||||
|
|
||||||
|
if tip == db.tip() {
|
||||||
|
return Ok(Some((tip_height, tip_hash, value_balance)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err("Zebra is committing too many blocks to the state, \
|
||||||
|
wait until it syncs to the chain tip"
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Return the depth of block `hash` from the chain tip.
|
/// Return the depth of block `hash` from the chain tip.
|
||||||
/// Searches `chain` for `hash`, then searches `db`.
|
/// Searches `chain` for `hash`, then searches `db`.
|
||||||
pub fn depth<C>(chain: Option<C>, db: &ZebraDb, hash: block::Hash) -> Option<u32>
|
pub fn depth<C>(chain: Option<C>, db: &ZebraDb, hash: block::Hash) -> Option<u32>
|
||||||
|
|
Loading…
Reference in New Issue