Tests that `ChainTipChange` is updated when the non-finalized best chain grows
This commit is contained in:
parent
0d1f434bdf
commit
dc514b82e2
|
@ -29,7 +29,7 @@ pub struct JsonParameters {
|
|||
/// Response to a `submitblock` RPC request.
|
||||
///
|
||||
/// Zebra never returns "duplicate-invalid", because it does not store invalid blocks.
|
||||
#[derive(Debug, PartialEq, Eq, serde::Serialize)]
|
||||
#[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum ErrorResponse {
|
||||
/// Block was already committed to the non-finalized or finalized state
|
||||
|
@ -45,7 +45,7 @@ pub enum ErrorResponse {
|
|||
/// Response to a `submitblock` RPC request.
|
||||
///
|
||||
/// Zebra never returns "duplicate-invalid", because it does not store invalid blocks.
|
||||
#[derive(Debug, PartialEq, Eq, serde::Serialize)]
|
||||
#[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum Response {
|
||||
/// Block was not successfully submitted, return error
|
||||
|
|
|
@ -3173,15 +3173,18 @@ async fn regtest_submit_blocks() -> Result<()> {
|
|||
|
||||
// TODO:
|
||||
// - Test that chain forks are handled correctly and that `ChainTipChange` sees a `Reset`
|
||||
// - Test that `ChainTipChange` is updated when the non-finalized best chain grows
|
||||
// - Test that the `ChainTipChange` updated by the RPC syncer is updated when the finalized tip changes
|
||||
#[cfg(feature = "rpc-syncer")]
|
||||
#[tokio::test]
|
||||
async fn trusted_chain_sync_handles_forks_correctly() -> Result<()> {
|
||||
use common::regtest::MiningRpcMethods;
|
||||
use tokio::time::timeout;
|
||||
use zebra_chain::parameters::testnet::REGTEST_NU5_ACTIVATION_HEIGHT;
|
||||
|
||||
let _init_guard = zebra_test::init();
|
||||
let mut config = random_known_rpc_port_config(false, &Mainnet)?;
|
||||
let mut config = random_known_rpc_port_config(false, &Network::new_regtest(None))?;
|
||||
config.state.ephemeral = false;
|
||||
config.network.network = Network::new_regtest(None);
|
||||
let rpc_address = config.rpc.listen_addr.unwrap();
|
||||
|
||||
let testdir = testdir()?.with_config(&mut config)?;
|
||||
let mut child = testdir.spawn_child(args!["start"])?;
|
||||
|
@ -3189,17 +3192,38 @@ async fn trusted_chain_sync_handles_forks_correctly() -> Result<()> {
|
|||
std::thread::sleep(LAUNCH_DELAY);
|
||||
|
||||
// Spawn a read state with the RPC syncer to check that it has the same best chain as Zebra
|
||||
let (_read_state, _latest_chain_tip, _chain_tip_change, _sync_task) =
|
||||
let (_read_state, _latest_chain_tip, mut chain_tip_change, _sync_task) =
|
||||
zebra_rpc::sync::init_read_state_with_syncer(
|
||||
config.state,
|
||||
&config.network.network,
|
||||
config.rpc.listen_addr.unwrap(),
|
||||
rpc_address,
|
||||
)
|
||||
.await?
|
||||
.map_err(|err| eyre!(err))?;
|
||||
|
||||
let rpc_client = RpcRequestClient::new(rpc_address);
|
||||
|
||||
for _ in 0..100 {
|
||||
let (block, height) = rpc_client
|
||||
.block_from_template(Height(REGTEST_NU5_ACTIVATION_HEIGHT))
|
||||
.await?;
|
||||
|
||||
rpc_client.submit_block(block).await?;
|
||||
|
||||
let tip_action = timeout(
|
||||
Duration::from_secs(1),
|
||||
chain_tip_change.wait_for_tip_change(),
|
||||
)
|
||||
.await??;
|
||||
|
||||
assert_eq!(
|
||||
tip_action.best_tip_height(),
|
||||
height,
|
||||
"tip action height should match block submission"
|
||||
);
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// - Submit blocks while checking that `ChainTipChange` shows the chain growing before submitting the next block
|
||||
// - Submit several blocks before checking that `ChainTipChange` has the latest block hash and is a `TipAction::Reset`
|
||||
// - Check that `getblock` RPC returns the same block as the read state for every height
|
||||
// - Submit more blocks with an older block template (and a different nonce so the hash is different) to trigger a chain reorg
|
||||
|
|
|
@ -5,17 +5,19 @@
|
|||
|
||||
use std::{net::SocketAddr, sync::Arc, time::Duration};
|
||||
|
||||
use color_eyre::eyre::{Context, Result};
|
||||
use color_eyre::eyre::{eyre, Context, Result};
|
||||
use tracing::*;
|
||||
|
||||
use zebra_chain::{
|
||||
block::{Block, Height},
|
||||
parameters::{testnet::REGTEST_NU5_ACTIVATION_HEIGHT, Network, NetworkUpgrade},
|
||||
primitives::byte_array::increment_big_endian,
|
||||
serialization::ZcashSerialize,
|
||||
};
|
||||
use zebra_node_services::rpc_client::RpcRequestClient;
|
||||
use zebra_rpc::methods::get_block_template_rpcs::get_block_template::{
|
||||
proposal::TimeSource, proposal_block_from_template, GetBlockTemplate,
|
||||
use zebra_rpc::methods::get_block_template_rpcs::{
|
||||
get_block_template::{proposal::TimeSource, proposal_block_from_template, GetBlockTemplate},
|
||||
types::submit_block,
|
||||
};
|
||||
use zebra_test::args;
|
||||
|
||||
|
@ -62,23 +64,10 @@ pub(crate) async fn submit_blocks_test() -> Result<()> {
|
|||
async fn submit_blocks(network: Network, rpc_address: SocketAddr) -> Result<()> {
|
||||
let client = RpcRequestClient::new(rpc_address);
|
||||
|
||||
for height in 1..=NUM_BLOCKS_TO_SUBMIT {
|
||||
let block_template: GetBlockTemplate = client
|
||||
.json_result_from_call("getblocktemplate", "[]".to_string())
|
||||
.await
|
||||
.expect("response should be success output with a serialized `GetBlockTemplate`");
|
||||
|
||||
let network_upgrade = if height < REGTEST_NU5_ACTIVATION_HEIGHT.try_into().unwrap() {
|
||||
NetworkUpgrade::Canopy
|
||||
} else {
|
||||
NetworkUpgrade::Nu5
|
||||
};
|
||||
|
||||
let mut block =
|
||||
proposal_block_from_template(&block_template, TimeSource::default(), network_upgrade)?;
|
||||
let height = block
|
||||
.coinbase_height()
|
||||
.expect("should have a coinbase height");
|
||||
for _ in 1..=NUM_BLOCKS_TO_SUBMIT {
|
||||
let (mut block, height) = client
|
||||
.block_from_template(Height(REGTEST_NU5_ACTIVATION_HEIGHT))
|
||||
.await?;
|
||||
|
||||
while !network.disable_pow()
|
||||
&& zebra_consensus::difficulty_is_valid(&block.header, &network, &height, &block.hash())
|
||||
|
@ -87,29 +76,55 @@ async fn submit_blocks(network: Network, rpc_address: SocketAddr) -> Result<()>
|
|||
increment_big_endian(Arc::make_mut(&mut block.header).nonce.as_mut());
|
||||
}
|
||||
|
||||
let block_data = hex::encode(block.zcash_serialize_to_vec()?);
|
||||
|
||||
let submit_block_response = client
|
||||
.text_from_call("submitblock", format!(r#"["{block_data}"]"#))
|
||||
.await?;
|
||||
|
||||
let was_submission_successful = submit_block_response.contains(r#""result":null"#);
|
||||
|
||||
if height.0 % 40 == 0 {
|
||||
info!(
|
||||
was_submission_successful,
|
||||
?block_template,
|
||||
?network_upgrade,
|
||||
"submitted block"
|
||||
);
|
||||
info!(?block, ?height, "submitting block");
|
||||
}
|
||||
|
||||
// Check that the block was validated and committed.
|
||||
assert!(
|
||||
submit_block_response.contains(r#""result":null"#),
|
||||
"unexpected response from submitblock RPC, should be null, was: {submit_block_response}"
|
||||
);
|
||||
client.submit_block(block).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub trait MiningRpcMethods {
|
||||
async fn block_from_template(&self, nu5_activation_height: Height) -> Result<(Block, Height)>;
|
||||
async fn submit_block(&self, block: Block) -> Result<()>;
|
||||
}
|
||||
|
||||
impl MiningRpcMethods for RpcRequestClient {
|
||||
async fn block_from_template(&self, nu5_activation_height: Height) -> Result<(Block, Height)> {
|
||||
let block_template: GetBlockTemplate = self
|
||||
.json_result_from_call("getblocktemplate", "[]".to_string())
|
||||
.await
|
||||
.expect("response should be success output with a serialized `GetBlockTemplate`");
|
||||
|
||||
let height = Height(block_template.height);
|
||||
|
||||
let network_upgrade = if height < nu5_activation_height {
|
||||
NetworkUpgrade::Canopy
|
||||
} else {
|
||||
NetworkUpgrade::Nu5
|
||||
};
|
||||
|
||||
Ok((
|
||||
proposal_block_from_template(&block_template, TimeSource::default(), network_upgrade)?,
|
||||
height,
|
||||
))
|
||||
}
|
||||
|
||||
async fn submit_block(&self, block: Block) -> Result<()> {
|
||||
let block_data = hex::encode(block.zcash_serialize_to_vec()?);
|
||||
|
||||
let submit_block_response: submit_block::Response = self
|
||||
.json_result_from_call("submitblock", format!(r#"["{block_data}"]"#))
|
||||
.await
|
||||
.map_err(|err| eyre!(err))?;
|
||||
|
||||
match submit_block_response {
|
||||
submit_block::Response::Accepted => Ok(()),
|
||||
submit_block::Response::ErrorResponse(err) => {
|
||||
Err(eyre!("block submission failed: {err:?}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue