use std::sync::Arc;
use color_eyre::eyre::{eyre, Context, Result};
use tower::BoxError;
use zebra_chain::{
block::{self, Block, Height},
parameters::{testnet::ConfiguredActivationHeights, Network},
primitives::byte_array::increment_big_endian,
serialization::{ZcashDeserializeInto, ZcashSerialize},
};
use zebra_node_services::rpc_client::RpcRequestClient;
use zebra_rpc::{
client::{
BlockTemplateResponse, BlockTemplateTimeSource, GetBlockchainInfoResponse, HexData,
SubmitBlockResponse,
},
methods::GetBlockHash,
proposal_block_from_template,
server::{self, OPENED_RPC_ENDPOINT_MSG},
};
use zebra_test::args;
use crate::common::{
config::{os_assigned_rpc_port_config, read_listen_addr_from_logs, testdir},
launch::{ZebradTestDirExt, LAUNCH_DELAY},
};
const NUM_BLOCKS_TO_SUBMIT: usize = 200;
pub(crate) async fn submit_blocks_test() -> Result<()> {
let _init_guard = zebra_test::init();
let network = Network::new_regtest(
ConfiguredActivationHeights {
nu5: Some(100),
..Default::default()
}
.into(),
);
let mut config = os_assigned_rpc_port_config(false, &network)?;
config.mempool.debug_enable_at_height = Some(0);
let mut zebrad = testdir()?
.with_config(&mut config)?
.spawn_child(args!["start"])?;
let rpc_address = read_listen_addr_from_logs(&mut zebrad, OPENED_RPC_ENDPOINT_MSG)?;
tokio::time::sleep(LAUNCH_DELAY).await;
let client = RpcRequestClient::new(rpc_address);
for _ in 1..=NUM_BLOCKS_TO_SUBMIT {
let (mut block, height) = client.block_from_template(&network).await?;
while !network.disable_pow()
&& zebra_consensus::difficulty_is_valid(&block.header, &network, &height, &block.hash())
.is_err()
{
increment_big_endian(Arc::make_mut(&mut block.header).nonce.as_mut());
}
client.submit_block(block).await?;
}
zebrad.kill(false)?;
let output = zebrad.wait_with_output()?;
let output = output.assert_failure()?;
output
.assert_was_killed()
.wrap_err("Possible port conflict. Are there other acceptance tests running?")
}
#[allow(dead_code)]
pub trait MiningRpcMethods {
async fn block_from_template(&self, net: &Network) -> Result<(Block, Height)>;
async fn submit_block(&self, block: Block) -> Result<()>;
async fn get_block(&self, height: i32) -> Result<Option<Arc<Block>>, BoxError>;
async fn generate(&self, num_blocks: u32) -> Result<Vec<block::Hash>>;
async fn blockchain_info(&self) -> Result<GetBlockchainInfoResponse>;
}
impl MiningRpcMethods for RpcRequestClient {
async fn block_from_template(&self, net: &Network) -> Result<(Block, Height)> {
let block_template: BlockTemplateResponse = self
.json_result_from_call("getblocktemplate", "[]".to_string())
.await
.expect("response should be success output with a serialized `GetBlockTemplate`");
Ok((
proposal_block_from_template(&block_template, BlockTemplateTimeSource::default(), net)?,
Height(block_template.height()),
))
}
async fn submit_block(&self, block: Block) -> Result<()> {
let block_data = hex::encode(block.zcash_serialize_to_vec()?);
let submit_block_response: SubmitBlockResponse = self
.json_result_from_call("submitblock", format!(r#"["{block_data}"]"#))
.await
.map_err(|err| eyre!(err))?;
match submit_block_response {
SubmitBlockResponse::Accepted => Ok(()),
SubmitBlockResponse::ErrorResponse(err) => {
Err(eyre!("block submission failed: {err:?}"))
}
}
}
async fn get_block(&self, height: i32) -> Result<Option<Arc<Block>>, BoxError> {
match self
.json_result_from_call("getblock", format!(r#"["{height}", 0]"#))
.await
{
Ok(HexData(raw_block)) => {
let block = raw_block.zcash_deserialize_into::<Block>()?;
Ok(Some(Arc::new(block)))
}
Err(err)
if err
.downcast_ref::<jsonrpsee_types::ErrorObject>()
.is_some_and(|err| {
let error: i32 = server::error::LegacyCode::InvalidParameter.into();
err.code() == error
}) =>
{
Ok(None)
}
Err(err) => Err(err),
}
}
async fn generate(&self, num_blocks: u32) -> Result<Vec<block::Hash>> {
self.json_result_from_call("generate", format!("[{num_blocks}]"))
.await
.map(|response: Vec<GetBlockHash>| response.into_iter().map(|rsp| rsp.hash()).collect())
.map_err(|err| eyre!(err))
}
async fn blockchain_info(&self) -> Result<GetBlockchainInfoResponse> {
self.json_result_from_call("getblockchaininfo", "[]")
.await
.map_err(|err| eyre!(err))
}
}