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:
Arya 2024-05-17 19:06:08 -04:00 committed by GitHub
parent 827599e1aa
commit 99b017e2a4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 86 additions and 28 deletions

View File

@ -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

View File

@ -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

View File

@ -176,11 +176,10 @@ 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() {
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
@ -188,7 +187,6 @@ where
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
// the header binds to the transactions in the blocks.

View File

@ -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)?;
}

View File

@ -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::{

View File

@ -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,