draft
This commit is contained in:
parent
2fcf6fe309
commit
270d67e995
|
@ -638,7 +638,10 @@ where
|
||||||
//
|
//
|
||||||
// Optional TODO:
|
// Optional TODO:
|
||||||
// - add `async changed()` method to ChainSyncStatus (like `ChainTip`)
|
// - add `async changed()` method to ChainSyncStatus (like `ChainTip`)
|
||||||
check_synced_to_tip(&network, latest_chain_tip.clone(), sync_status.clone())?;
|
// TODO: Add a `disable_peers` field to `Network` to check instead of `disable_pow()` (#8361)
|
||||||
|
if !network.disable_pow() {
|
||||||
|
check_synced_to_tip(&network, latest_chain_tip.clone(), sync_status.clone())?;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: return an error if we have no peers, like `zcashd` does,
|
// TODO: return an error if we have no peers, like `zcashd` does,
|
||||||
// and add a developer config that mines regardless of how many peers we have.
|
// and add a developer config that mines regardless of how many peers we have.
|
||||||
|
|
|
@ -395,7 +395,7 @@ impl StateService {
|
||||||
- HeightDiff::try_from(checkpoint_verify_concurrency_limit)
|
- HeightDiff::try_from(checkpoint_verify_concurrency_limit)
|
||||||
.expect("fits in HeightDiff");
|
.expect("fits in HeightDiff");
|
||||||
let full_verifier_utxo_lookahead =
|
let full_verifier_utxo_lookahead =
|
||||||
full_verifier_utxo_lookahead.expect("unexpected negative height");
|
full_verifier_utxo_lookahead.unwrap_or(block::Height::MIN);
|
||||||
|
|
||||||
let non_finalized_state_queued_blocks = QueuedBlocks::default();
|
let non_finalized_state_queued_blocks = QueuedBlocks::default();
|
||||||
let pending_utxos = PendingUtxos::default();
|
let pending_utxos = PendingUtxos::default();
|
||||||
|
|
|
@ -607,6 +607,8 @@ fn best_relevant_chain(
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.take(POW_MEDIAN_BLOCK_SPAN)
|
.take(POW_MEDIAN_BLOCK_SPAN)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
// TODO: Find out what zcashd does here.
|
||||||
let best_relevant_chain = best_relevant_chain.try_into().map_err(|_error| {
|
let best_relevant_chain = best_relevant_chain.try_into().map_err(|_error| {
|
||||||
"Zebra's state only has a few blocks, wait until it syncs to the chain tip"
|
"Zebra's state only has a few blocks, wait until it syncs to the chain tip"
|
||||||
})?;
|
})?;
|
||||||
|
|
|
@ -78,14 +78,17 @@
|
||||||
//!
|
//!
|
||||||
//! Some of the diagnostic features are optional, and need to be enabled at compile-time.
|
//! Some of the diagnostic features are optional, and need to be enabled at compile-time.
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use abscissa_core::{config, Command, FrameworkError};
|
use abscissa_core::{config, Command, FrameworkError};
|
||||||
use color_eyre::eyre::{eyre, Report};
|
use color_eyre::eyre::{eyre, Report};
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
use tokio::{pin, select, sync::oneshot};
|
use tokio::{pin, select, sync::oneshot};
|
||||||
use tower::{builder::ServiceBuilder, util::BoxService};
|
use tower::{builder::ServiceBuilder, util::BoxService, ServiceExt};
|
||||||
use tracing_futures::Instrument;
|
use tracing_futures::Instrument;
|
||||||
|
|
||||||
use zebra_consensus::router::BackgroundTaskHandles;
|
use zebra_chain::block::genesis::regtest_genesis_block;
|
||||||
|
use zebra_consensus::{router::BackgroundTaskHandles, ParameterCheckpoint};
|
||||||
use zebra_rpc::server::RpcServer;
|
use zebra_rpc::server::RpcServer;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -110,9 +113,8 @@ pub struct StartCmd {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StartCmd {
|
impl StartCmd {
|
||||||
async fn start(&self) -> Result<(), Report> {
|
/// Starts `zebrad` with the provided config.
|
||||||
let config = APPLICATION.config();
|
pub async fn start(&self, config: Arc<ZebradConfig>) -> Result<(), Report> {
|
||||||
|
|
||||||
info!("initializing node state");
|
info!("initializing node state");
|
||||||
let (_, max_checkpoint_height) = zebra_consensus::router::init_checkpoint_list(
|
let (_, max_checkpoint_height) = zebra_consensus::router::init_checkpoint_list(
|
||||||
config.consensus.clone(),
|
config.consensus.clone(),
|
||||||
|
@ -135,7 +137,7 @@ impl StartCmd {
|
||||||
read_only_state_service.log_db_metrics();
|
read_only_state_service.log_db_metrics();
|
||||||
|
|
||||||
let state = ServiceBuilder::new()
|
let state = ServiceBuilder::new()
|
||||||
.buffer(Self::state_buffer_bound())
|
.buffer(Self::state_buffer_bound(config.clone()))
|
||||||
.service(state_service);
|
.service(state_service);
|
||||||
|
|
||||||
info!("initializing network");
|
info!("initializing network");
|
||||||
|
@ -177,7 +179,7 @@ impl StartCmd {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
info!("initializing syncer");
|
info!("initializing syncer");
|
||||||
let (syncer, sync_status) = ChainSync::new(
|
let (mut syncer, sync_status) = ChainSync::new(
|
||||||
&config,
|
&config,
|
||||||
max_checkpoint_height,
|
max_checkpoint_height,
|
||||||
peer_set.clone(),
|
peer_set.clone(),
|
||||||
|
@ -300,7 +302,28 @@ impl StartCmd {
|
||||||
);
|
);
|
||||||
|
|
||||||
info!("spawning syncer task");
|
info!("spawning syncer task");
|
||||||
let syncer_task_handle = tokio::spawn(syncer.sync().in_current_span());
|
let syncer_task_handle = if config.network.network.is_regtest() {
|
||||||
|
if !syncer
|
||||||
|
.state_contains(config.network.network.genesis_hash())
|
||||||
|
.await?
|
||||||
|
{
|
||||||
|
let genesis_hash = block_verifier_router
|
||||||
|
.clone()
|
||||||
|
.oneshot(zebra_consensus::Request::Commit(regtest_genesis_block()))
|
||||||
|
.await
|
||||||
|
.expect("should validate Regtest genesis block");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
genesis_hash,
|
||||||
|
config.network.network.genesis_hash(),
|
||||||
|
"validated block hash should match network genesis hash"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
tokio::spawn(std::future::pending().in_current_span())
|
||||||
|
} else {
|
||||||
|
tokio::spawn(syncer.sync().in_current_span())
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(feature = "shielded-scan")]
|
#[cfg(feature = "shielded-scan")]
|
||||||
// Spawn never ending scan task only if we have keys to scan for.
|
// Spawn never ending scan task only if we have keys to scan for.
|
||||||
|
@ -505,9 +528,7 @@ impl StartCmd {
|
||||||
|
|
||||||
/// Returns the bound for the state service buffer,
|
/// Returns the bound for the state service buffer,
|
||||||
/// based on the configurations of the services that use the state concurrently.
|
/// based on the configurations of the services that use the state concurrently.
|
||||||
fn state_buffer_bound() -> usize {
|
fn state_buffer_bound(config: Arc<ZebradConfig>) -> usize {
|
||||||
let config = APPLICATION.config();
|
|
||||||
|
|
||||||
// Ignore the checkpoint verify limit, because it is very large.
|
// Ignore the checkpoint verify limit, because it is very large.
|
||||||
//
|
//
|
||||||
// TODO: do we also need to account for concurrent use across services?
|
// TODO: do we also need to account for concurrent use across services?
|
||||||
|
@ -536,8 +557,10 @@ impl Runnable for StartCmd {
|
||||||
.rt
|
.rt
|
||||||
.take();
|
.take();
|
||||||
|
|
||||||
|
let config = APPLICATION.config();
|
||||||
|
|
||||||
rt.expect("runtime should not already be taken")
|
rt.expect("runtime should not already be taken")
|
||||||
.run(self.start());
|
.run(self.start(config));
|
||||||
|
|
||||||
info!("stopping zebrad");
|
info!("stopping zebrad");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1136,7 +1136,7 @@ where
|
||||||
|
|
||||||
/// Returns `true` if the hash is present in the state, and `false`
|
/// Returns `true` if the hash is present in the state, and `false`
|
||||||
/// if the hash is not present in the state.
|
/// if the hash is not present in the state.
|
||||||
async fn state_contains(&mut self, hash: block::Hash) -> Result<bool, Report> {
|
pub(crate) async fn state_contains(&mut self, hash: block::Hash) -> Result<bool, Report> {
|
||||||
match self
|
match self
|
||||||
.state
|
.state
|
||||||
.ready()
|
.ready()
|
||||||
|
|
|
@ -3126,3 +3126,15 @@ async fn validate_regtest_genesis_block() {
|
||||||
"validated block hash should match network genesis hash"
|
"validated block hash should match network genesis hash"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Test successful `getblocktemplate` and `submitblock` RPC calls on Regtest on Canopy and NU5.
|
||||||
|
///
|
||||||
|
/// See [`common::regtest::submit_blocks`] for more information.
|
||||||
|
#[tokio::test]
|
||||||
|
#[cfg(feature = "getblocktemplate-rpcs")]
|
||||||
|
async fn regtest_submit_block() -> Result<()> {
|
||||||
|
for nu5_activation_height in [None, Some(1), Some(1_000)] {
|
||||||
|
common::regtest::submit_blocks::run(nu5_activation_height).await?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
|
@ -24,5 +24,8 @@ pub mod checkpoints;
|
||||||
#[cfg(feature = "getblocktemplate-rpcs")]
|
#[cfg(feature = "getblocktemplate-rpcs")]
|
||||||
pub mod get_block_template_rpcs;
|
pub mod get_block_template_rpcs;
|
||||||
|
|
||||||
|
#[cfg(feature = "getblocktemplate-rpcs")]
|
||||||
|
pub mod regtest;
|
||||||
|
|
||||||
#[cfg(feature = "shielded-scan")]
|
#[cfg(feature = "shielded-scan")]
|
||||||
pub mod shielded_scan;
|
pub mod shielded_scan;
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
//! Acceptance tests for Regtest in Zebra.
|
||||||
|
|
||||||
|
pub(crate) mod submit_blocks;
|
|
@ -0,0 +1,88 @@
|
||||||
|
//! Test submitblock RPC method on Regtest.
|
||||||
|
//!
|
||||||
|
//! This test will get block templates via the `getblocktemplate` RPC method and submit them as new blocks
|
||||||
|
//! via the `submitblock` RPC method on Regtest.
|
||||||
|
|
||||||
|
use std::{net::SocketAddr, sync::Arc, time::Duration};
|
||||||
|
|
||||||
|
use color_eyre::eyre::Result;
|
||||||
|
use tokio::task::JoinHandle;
|
||||||
|
use tracing::*;
|
||||||
|
|
||||||
|
use zebra_chain::{parameters::Network, 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 crate::common::config::random_known_rpc_port_config;
|
||||||
|
use zebrad::commands::StartCmd;
|
||||||
|
|
||||||
|
/// Number of blocks that should be submitted before the test is considered successful.
|
||||||
|
const NUM_BLOCKS_TO_SUBMIT: usize = 2_000;
|
||||||
|
|
||||||
|
/// - Starts Zebrad
|
||||||
|
/// - Calls `getblocktemplate` RPC method and submits blocks with empty equihash solutions
|
||||||
|
/// - Checks that blocks were validated and committed successfully
|
||||||
|
// TODO: Implement custom serialization for `zebra_network::Config` and spawn a zebrad child process instead?
|
||||||
|
pub(crate) async fn run(nu5_activation_height: Option<u32>) -> Result<()> {
|
||||||
|
let _init_guard = zebra_test::init();
|
||||||
|
info!("starting regtest submit_blocks test");
|
||||||
|
|
||||||
|
let network = Network::new_regtest(nu5_activation_height);
|
||||||
|
let config = random_known_rpc_port_config(false, &network)?;
|
||||||
|
let rpc_address = config.rpc.listen_addr.unwrap();
|
||||||
|
|
||||||
|
info!("starting zebrad");
|
||||||
|
let _zebrad_start_task: JoinHandle<_> = tokio::task::spawn_blocking(|| {
|
||||||
|
let rt = tokio::runtime::Builder::new_multi_thread()
|
||||||
|
.enable_all()
|
||||||
|
.build()
|
||||||
|
.expect("runtime building should not fail");
|
||||||
|
let start_cmd = StartCmd::default();
|
||||||
|
|
||||||
|
let _ = rt.block_on(start_cmd.start(Arc::new(config)));
|
||||||
|
});
|
||||||
|
|
||||||
|
info!("waiting for zebrad to start");
|
||||||
|
|
||||||
|
tokio::time::sleep(Duration::from_secs(30)).await;
|
||||||
|
|
||||||
|
info!("attempting to submit blocks");
|
||||||
|
submit_blocks(rpc_address).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get block templates and submit blocks
|
||||||
|
async fn submit_blocks(rpc_address: SocketAddr) -> Result<()> {
|
||||||
|
let client = RpcRequestClient::new(rpc_address);
|
||||||
|
|
||||||
|
for _ in 0..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 block_data = hex::encode(
|
||||||
|
proposal_block_from_template(&block_template, TimeSource::default())?
|
||||||
|
.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"#);
|
||||||
|
|
||||||
|
info!(was_submission_successful, "submitted 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}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
Loading…
Reference in New Issue