add(consensus): Add a `target_difficulty_limit` field on `testnet::Parameters` (#8518)
* Adds a `target_difficulty_limit` field on `testnet::Parameters` * updates test to increment block nonce until finding a block that's below the difficulty threshold * increment the nonce while the difficulty is invalid instead of while the difficulty threshold is invalid * Adds comments
This commit is contained in:
parent
827599e1aa
commit
99b017e2a4
|
@ -10,6 +10,7 @@ use crate::{
|
|||
network_upgrade::TESTNET_ACTIVATION_HEIGHTS,
|
||||
Network, NetworkUpgrade, NETWORK_UPGRADES_IN_ORDER,
|
||||
},
|
||||
work::difficulty::{ExpandedDifficulty, U256},
|
||||
};
|
||||
|
||||
/// The Regtest NU5 activation height in tests
|
||||
|
@ -63,7 +64,7 @@ pub struct ConfiguredActivationHeights {
|
|||
}
|
||||
|
||||
/// Builder for the [`Parameters`] struct.
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub struct ParametersBuilder {
|
||||
/// The name of this network to be used by the `Display` trait impl.
|
||||
network_name: String,
|
||||
|
@ -79,6 +80,8 @@ pub struct ParametersBuilder {
|
|||
hrp_sapling_payment_address: String,
|
||||
/// Slow start interval for this network
|
||||
slow_start_interval: Height,
|
||||
/// Target difficulty limit for this network
|
||||
target_difficulty_limit: ExpandedDifficulty,
|
||||
/// A flag for disabling proof-of-work checks when Zebra is validating blocks
|
||||
disable_pow: bool,
|
||||
}
|
||||
|
@ -102,6 +105,19 @@ impl Default for ParametersBuilder {
|
|||
.parse()
|
||||
.expect("hard-coded hash parses"),
|
||||
slow_start_interval: SLOW_START_INTERVAL,
|
||||
// Testnet PoWLimit is defined as `2^251 - 1` on page 73 of the protocol specification:
|
||||
// <https://zips.z.cash/protocol/protocol.pdf>
|
||||
//
|
||||
// `zcashd` converts the PoWLimit into a compact representation before
|
||||
// using it to perform difficulty filter checks.
|
||||
//
|
||||
// The Zcash specification converts to compact for the default difficulty
|
||||
// filter, but not for testnet minimum difficulty blocks. (ZIP 205 and
|
||||
// ZIP 208 don't specify this conversion either.) See #1277 for details.
|
||||
target_difficulty_limit: ExpandedDifficulty::from((U256::one() << 251) - 1)
|
||||
.to_compact()
|
||||
.to_expanded()
|
||||
.expect("difficulty limits are valid expanded values"),
|
||||
disable_pow: false,
|
||||
}
|
||||
}
|
||||
|
@ -247,6 +263,16 @@ impl ParametersBuilder {
|
|||
self
|
||||
}
|
||||
|
||||
/// Sets the target difficulty limit to be used in the [`Parameters`] being built.
|
||||
// TODO: Accept a hex-encoded String instead?
|
||||
pub fn with_target_difficulty_limit(mut self, target_difficulty_limit: U256) -> Self {
|
||||
self.target_difficulty_limit = ExpandedDifficulty::from(target_difficulty_limit)
|
||||
.to_compact()
|
||||
.to_expanded()
|
||||
.expect("difficulty limits are valid expanded values");
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the `disable_pow` flag to be used in the [`Parameters`] being built.
|
||||
pub fn with_disable_pow(mut self, disable_pow: bool) -> Self {
|
||||
self.disable_pow = disable_pow;
|
||||
|
@ -263,6 +289,7 @@ impl ParametersBuilder {
|
|||
hrp_sapling_extended_full_viewing_key,
|
||||
hrp_sapling_payment_address,
|
||||
slow_start_interval,
|
||||
target_difficulty_limit,
|
||||
disable_pow,
|
||||
} = self;
|
||||
Parameters {
|
||||
|
@ -274,6 +301,7 @@ impl ParametersBuilder {
|
|||
hrp_sapling_payment_address,
|
||||
slow_start_interval,
|
||||
slow_start_shift: Height(slow_start_interval.0 / 2),
|
||||
target_difficulty_limit,
|
||||
disable_pow,
|
||||
}
|
||||
}
|
||||
|
@ -285,7 +313,7 @@ impl ParametersBuilder {
|
|||
}
|
||||
|
||||
/// Network consensus parameters for test networks such as Regtest and the default Testnet.
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub struct Parameters {
|
||||
/// The name of this network to be used by the `Display` trait impl.
|
||||
network_name: String,
|
||||
|
@ -307,6 +335,8 @@ pub struct Parameters {
|
|||
slow_start_interval: Height,
|
||||
/// Slow start shift for this network, always half the slow start interval
|
||||
slow_start_shift: Height,
|
||||
/// Target difficulty limit for this network
|
||||
target_difficulty_limit: ExpandedDifficulty,
|
||||
/// A flag for disabling proof-of-work checks when Zebra is validating blocks
|
||||
disable_pow: bool,
|
||||
}
|
||||
|
@ -338,6 +368,8 @@ impl Parameters {
|
|||
network_name: "Regtest".to_string(),
|
||||
..Self::build()
|
||||
.with_genesis_hash(REGTEST_GENESIS_HASH)
|
||||
// This value is chosen to match zcashd, see: <https://github.com/zcash/zcash/blob/master/src/chainparams.cpp#L654>
|
||||
.with_target_difficulty_limit(U256::from_big_endian(&[0x0f; 32]))
|
||||
.with_disable_pow(true)
|
||||
.with_slow_start_interval(Height::MIN)
|
||||
.with_sapling_hrps(
|
||||
|
@ -373,6 +405,7 @@ impl Parameters {
|
|||
hrp_sapling_payment_address,
|
||||
slow_start_interval,
|
||||
slow_start_shift,
|
||||
target_difficulty_limit,
|
||||
disable_pow,
|
||||
} = Self::new_regtest(None);
|
||||
|
||||
|
@ -383,6 +416,7 @@ impl Parameters {
|
|||
&& self.hrp_sapling_payment_address == hrp_sapling_payment_address
|
||||
&& self.slow_start_interval == slow_start_interval
|
||||
&& self.slow_start_shift == slow_start_shift
|
||||
&& self.target_difficulty_limit == target_difficulty_limit
|
||||
&& self.disable_pow == disable_pow
|
||||
}
|
||||
|
||||
|
@ -426,6 +460,11 @@ impl Parameters {
|
|||
self.slow_start_shift
|
||||
}
|
||||
|
||||
/// Returns the target difficulty limit for this network
|
||||
pub fn target_difficulty_limit(&self) -> ExpandedDifficulty {
|
||||
self.target_difficulty_limit
|
||||
}
|
||||
|
||||
/// Returns true if proof-of-work validation should be disabled for this network
|
||||
pub fn disable_pow(&self) -> bool {
|
||||
self.disable_pow
|
||||
|
|
|
@ -100,7 +100,7 @@ pub const INVALID_COMPACT_DIFFICULTY: CompactDifficulty = CompactDifficulty(u32:
|
|||
/// [section 7.7.2]: https://zips.z.cash/protocol/protocol.pdf#difficulty
|
||||
//
|
||||
// TODO: Use NonZeroU256, when available
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct ExpandedDifficulty(U256);
|
||||
|
||||
/// A 128-bit unsigned "Work" value.
|
||||
|
@ -696,11 +696,11 @@ impl ParameterDifficulty for Network {
|
|||
/// See [`ParameterDifficulty::target_difficulty_limit`]
|
||||
fn target_difficulty_limit(&self) -> ExpandedDifficulty {
|
||||
let limit: U256 = match self {
|
||||
/* 2^243 - 1 */
|
||||
// Mainnet PoWLimit is defined as `2^243 - 1` on page 73 of the protocol specification:
|
||||
// <https://zips.z.cash/protocol/protocol.pdf>
|
||||
Network::Mainnet => (U256::one() << 243) - 1,
|
||||
/* 2^251 - 1 */
|
||||
// TODO: Add a `target_difficulty_limit` field to `testnet::Parameters` to return here. (`U256::from_big_endian(&[0x0f].repeat(8))` for Regtest)
|
||||
Network::Testnet(_params) => (U256::one() << 251) - 1,
|
||||
// 2^251 - 1 for the default testnet, see `testnet::ParametersBuilder::default`()
|
||||
Network::Testnet(params) => return params.target_difficulty_limit(),
|
||||
};
|
||||
|
||||
// `zcashd` converts the PoWLimit into a compact representation before
|
||||
|
|
|
@ -176,18 +176,16 @@ where
|
|||
Err(BlockError::MaxHeight(height, hash, block::Height::MAX))?;
|
||||
}
|
||||
|
||||
if !network.disable_pow() {
|
||||
// > The block data MUST be validated and checked against the server's usual
|
||||
// > acceptance rules (excluding the check for a valid proof-of-work).
|
||||
// <https://en.bitcoin.it/wiki/BIP_0023#Block_Proposal>
|
||||
if request.is_proposal() {
|
||||
check::difficulty_threshold_is_valid(&block.header, &network, &height, &hash)?;
|
||||
} else {
|
||||
// Do the difficulty checks first, to raise the threshold for
|
||||
// attacks that use any other fields.
|
||||
check::difficulty_is_valid(&block.header, &network, &height, &hash)?;
|
||||
check::equihash_solution_is_valid(&block.header)?;
|
||||
}
|
||||
// > The block data MUST be validated and checked against the server's usual
|
||||
// > acceptance rules (excluding the check for a valid proof-of-work).
|
||||
// <https://en.bitcoin.it/wiki/BIP_0023#Block_Proposal>
|
||||
if request.is_proposal() || network.disable_pow() {
|
||||
check::difficulty_threshold_is_valid(&block.header, &network, &height, &hash)?;
|
||||
} else {
|
||||
// Do the difficulty checks first, to raise the threshold for
|
||||
// attacks that use any other fields.
|
||||
check::difficulty_is_valid(&block.header, &network, &height, &hash)?;
|
||||
check::equihash_solution_is_valid(&block.header)?;
|
||||
}
|
||||
|
||||
// Next, check the Merkle root validity, to ensure that
|
||||
|
|
|
@ -595,7 +595,14 @@ where
|
|||
.ok_or(VerifyCheckpointError::CoinbaseHeight { hash })?;
|
||||
self.check_height(height)?;
|
||||
|
||||
if !self.network.disable_pow() {
|
||||
if self.network.disable_pow() {
|
||||
crate::block::check::difficulty_threshold_is_valid(
|
||||
&block.header,
|
||||
&self.network,
|
||||
&height,
|
||||
&hash,
|
||||
)?;
|
||||
} else {
|
||||
crate::block::check::difficulty_is_valid(&block.header, &self.network, &height, &hash)?;
|
||||
crate::block::check::equihash_solution_is_valid(&block.header)?;
|
||||
}
|
||||
|
|
|
@ -45,6 +45,9 @@ pub mod error;
|
|||
pub mod router;
|
||||
pub mod transaction;
|
||||
|
||||
#[cfg(any(test, feature = "proptest-impl"))]
|
||||
pub use block::check::difficulty_is_valid;
|
||||
|
||||
pub use block::{
|
||||
subsidy::{
|
||||
funding_streams::{
|
||||
|
|
|
@ -3,13 +3,14 @@
|
|||
//! 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, time::Duration};
|
||||
use std::{net::SocketAddr, sync::Arc, time::Duration};
|
||||
|
||||
use color_eyre::eyre::{Context, Result};
|
||||
use tracing::*;
|
||||
|
||||
use zebra_chain::{
|
||||
parameters::{testnet::REGTEST_NU5_ACTIVATION_HEIGHT, Network, NetworkUpgrade},
|
||||
primitives::byte_array::increment_big_endian,
|
||||
serialization::ZcashSerialize,
|
||||
};
|
||||
use zebra_node_services::rpc_client::RpcRequestClient;
|
||||
|
@ -44,7 +45,7 @@ pub(crate) async fn submit_blocks_test() -> Result<()> {
|
|||
tokio::time::sleep(Duration::from_secs(30)).await;
|
||||
|
||||
info!("attempting to submit blocks");
|
||||
submit_blocks(rpc_address).await?;
|
||||
submit_blocks(network, rpc_address).await?;
|
||||
|
||||
zebrad.kill(false)?;
|
||||
|
||||
|
@ -58,7 +59,7 @@ pub(crate) async fn submit_blocks_test() -> Result<()> {
|
|||
}
|
||||
|
||||
/// Get block templates and submit blocks
|
||||
async fn submit_blocks(rpc_address: SocketAddr) -> 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 {
|
||||
|
@ -73,10 +74,20 @@ async fn submit_blocks(rpc_address: SocketAddr) -> Result<()> {
|
|||
NetworkUpgrade::Nu5
|
||||
};
|
||||
|
||||
let block_data = hex::encode(
|
||||
proposal_block_from_template(&block_template, TimeSource::default(), network_upgrade)?
|
||||
.zcash_serialize_to_vec()?,
|
||||
);
|
||||
let mut block =
|
||||
proposal_block_from_template(&block_template, TimeSource::default(), network_upgrade)?;
|
||||
let height = block
|
||||
.coinbase_height()
|
||||
.expect("should have a coinbase height");
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
let block_data = hex::encode(block.zcash_serialize_to_vec()?);
|
||||
|
||||
let submit_block_response = client
|
||||
.text_from_call("submitblock", format!(r#"["{block_data}"]"#))
|
||||
|
@ -84,7 +95,7 @@ async fn submit_blocks(rpc_address: SocketAddr) -> Result<()> {
|
|||
|
||||
let was_submission_successful = submit_block_response.contains(r#""result":null"#);
|
||||
|
||||
if height % 40 == 0 {
|
||||
if height.0 % 40 == 0 {
|
||||
info!(
|
||||
was_submission_successful,
|
||||
?block_template,
|
||||
|
|
Loading…
Reference in New Issue