diff --git a/blockprod/src/detail/tests.rs b/blockprod/src/detail/tests.rs index e26792078c..4fd6591d93 100644 --- a/blockprod/src/detail/tests.rs +++ b/blockprod/src/detail/tests.rs @@ -883,21 +883,15 @@ mod produce_block { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn cancel_received(#[case] seed: Seed) { let override_chain_config = { - let net_upgrades = NetUpgrades::initialize(vec![ - ( - BlockHeight::new(0), - UpgradeVersion::ConsensusUpgrade(ConsensusUpgrade::IgnoreConsensus), - ), - ( - BlockHeight::new(1), - UpgradeVersion::ConsensusUpgrade(ConsensusUpgrade::PoW { - // Make difficulty impossible so the cancel from - // the mock job manager is always seen before - // solving the block - initial_difficulty: Uint256::ZERO.into(), - }), - ), - ]) + let net_upgrades = NetUpgrades::initialize(vec![( + BlockHeight::new(0), + UpgradeVersion::ConsensusUpgrade(ConsensusUpgrade::PoW { + // Make difficulty impossible so the cancel from + // the mock job manager is always seen before + // solving the block + initial_difficulty: Uint256::ZERO.into(), + }), + )]) .expect("Net upgrade is valid"); Builder::new(ChainType::Regtest).net_upgrades(net_upgrades).build() @@ -1014,18 +1008,12 @@ mod produce_block { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn solved_pow_consensus() { let override_chain_config = { - let net_upgrades = NetUpgrades::initialize(vec![ - ( - BlockHeight::new(0), - UpgradeVersion::ConsensusUpgrade(ConsensusUpgrade::IgnoreConsensus), - ), - ( - BlockHeight::new(1), - UpgradeVersion::ConsensusUpgrade(ConsensusUpgrade::PoW { - initial_difficulty: Uint256::MAX.into(), - }), - ), - ]) + let net_upgrades = NetUpgrades::initialize(vec![( + BlockHeight::new(0), + UpgradeVersion::ConsensusUpgrade(ConsensusUpgrade::PoW { + initial_difficulty: Uint256::MAX.into(), + }), + )]) .expect("Net upgrade is valid"); Builder::new(ChainType::Regtest).net_upgrades(net_upgrades).build() diff --git a/chainstate/src/detail/ban_score.rs b/chainstate/src/detail/ban_score.rs index 13219a2f5a..b6427d43da 100644 --- a/chainstate/src/detail/ban_score.rs +++ b/chainstate/src/detail/ban_score.rs @@ -323,7 +323,6 @@ impl BanScore for ConsensusPoWError { ConsensusPoWError::DecodingBitsFailed(_) => 100, ConsensusPoWError::PreviousBitsDecodingFailed(_) => 0, ConsensusPoWError::InvalidTargetBits(_, _) => 100, - ConsensusPoWError::GenesisCannotHaveOngoingDifficulty => 100, ConsensusPoWError::InvalidBlockRewardMaturityDistance(_) => 0, } } diff --git a/common/src/chain/config/builder.rs b/common/src/chain/config/builder.rs index 11144e50ad..db85df172b 100644 --- a/common/src/chain/config/builder.rs +++ b/common/src/chain/config/builder.rs @@ -23,6 +23,7 @@ use crate::{ EmissionScheduleTabular, }, pos::get_initial_randomness, + pow::PoWChainConfigBuilder, ConsensusUpgrade, Destination, GenBlock, Genesis, Mlt, NetUpgrades, PoWChainConfig, UpgradeVersion, }, @@ -249,6 +250,27 @@ impl Builder { .collect::>>() .into(); + let pow_chain_config = { + let (_, genesis_upgrade_version) = net_upgrades + .version_at_height(BlockHeight::new(0)) + .expect("Genesis must have an upgrade version"); + + let limit = match genesis_upgrade_version { + UpgradeVersion::SomeUpgrade => None, + UpgradeVersion::ConsensusUpgrade(consensus_upgrade) => match consensus_upgrade { + ConsensusUpgrade::IgnoreConsensus | ConsensusUpgrade::PoS { .. } => None, + ConsensusUpgrade::PoW { initial_difficulty } => { + let limit = (*initial_difficulty) + .try_into() + .expect("Genesis initial difficulty to be valid"); + Some(limit) + } + }, + }; + + PoWChainConfigBuilder::new(chain_type).limit(limit).build() + }; + ChainConfig { chain_type, bip44_coin_type, @@ -263,6 +285,7 @@ impl Builder { max_future_block_time_offset, max_no_signature_data_size, max_depth_for_reorg, + pow_chain_config, epoch_length, sealed_epoch_distance_from_tip, initial_randomness, diff --git a/common/src/chain/config/mod.rs b/common/src/chain/config/mod.rs index 095ae92673..693e7218bc 100644 --- a/common/src/chain/config/mod.rs +++ b/common/src/chain/config/mod.rs @@ -162,6 +162,7 @@ pub struct ChainConfig { max_block_size_with_smart_contracts: usize, max_no_signature_data_size: usize, max_depth_for_reorg: BlockDistance, + pow_chain_config: PoWChainConfig, epoch_length: NonZeroU64, sealed_epoch_distance_from_tip: usize, initial_randomness: H256, @@ -456,8 +457,8 @@ impl ChainConfig { // TODO: this should be part of net-upgrades. There should be no canonical definition of PoW for any chain config #[must_use] - pub const fn get_proof_of_work_config(&self) -> PoWChainConfig { - PoWChainConfig::new(self.chain_type) + pub fn get_proof_of_work_config(&self) -> &PoWChainConfig { + &self.pow_chain_config } /// The minimum number of blocks required to be able to spend a utxo coming from a decommissioned pool diff --git a/common/src/chain/mod.rs b/common/src/chain/mod.rs index d54332f240..bdb693a60e 100644 --- a/common/src/chain/mod.rs +++ b/common/src/chain/mod.rs @@ -38,5 +38,5 @@ pub use pos::{ create_regtest_pos_config, create_testnet_pos_config, create_unittest_pos_config, get_initial_randomness, initial_difficulty, DelegationId, PoSChainConfig, PoolId, }; -pub use pow::PoWChainConfig; +pub use pow::{PoWChainConfig, PoWChainConfigBuilder}; pub use upgrades::*; diff --git a/common/src/chain/pow.rs b/common/src/chain/pow.rs index c18edfa827..70a8938f28 100644 --- a/common/src/chain/pow.rs +++ b/common/src/chain/pow.rs @@ -21,7 +21,7 @@ use std::time::Duration; /// Chain Parameters for Proof of Work. /// /// See in Bitcoin's [chainparams.cpp](https://github.com/bitcoin/bitcoin/blob/eca694a4e78d54ce4e29b388b3e81b06e55c2293/src/chainparams.cpp) -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct PoWChainConfig { no_retargeting: bool, /// Checks whether minimum difficulty can be used for the block @@ -33,14 +33,8 @@ pub struct PoWChainConfig { } impl PoWChainConfig { - pub(crate) const fn new(chain_type: ChainType) -> Self { - PoWChainConfig { - no_retargeting: no_retargeting(chain_type), - allow_min_difficulty_blocks: allow_min_difficulty_blocks(chain_type), - limit: limit(chain_type), - // If block time is 2 minutes (which is my goal eventually), then 500 is equivalent to 100 in bitcoin's 10 minutes. - reward_maturity_distance: BlockDistance::new(500), - } + pub(crate) fn new(chain_type: ChainType) -> Self { + PoWChainConfigBuilder::new(chain_type).build() } pub const fn no_retargeting(&self) -> bool { @@ -77,6 +71,49 @@ impl PoWChainConfig { } } +#[derive(Copy, Clone)] +pub struct PoWChainConfigBuilder { + chain_type: ChainType, + no_retargeting: Option, + allow_min_difficulty_blocks: Option, + limit: Option, + reward_maturity_distance: Option, +} + +impl PoWChainConfigBuilder { + pub fn new(chain_type: ChainType) -> Self { + Self { + chain_type, + no_retargeting: None, + allow_min_difficulty_blocks: None, + limit: None, + reward_maturity_distance: None, + } + } + + pub fn limit(mut self, value: Option) -> Self { + self.limit = value; + self + } + + pub fn build(self) -> PoWChainConfig { + PoWChainConfig { + no_retargeting: self.no_retargeting.unwrap_or_else(|| no_retargeting(self.chain_type)), + allow_min_difficulty_blocks: self + .allow_min_difficulty_blocks + .unwrap_or_else(|| allow_min_difficulty_blocks(self.chain_type)), + limit: self.limit.unwrap_or_else(|| limit(self.chain_type)), + + // If block time is 2 minutes (which is my goal + // eventually), then 500 is equivalent to 100 in bitcoin's + // 10 minutes. + reward_maturity_distance: self + .reward_maturity_distance + .unwrap_or_else(|| BlockDistance::new(500)), + } + } +} + const fn no_retargeting(chain_type: ChainType) -> bool { match chain_type { ChainType::Mainnet | ChainType::Testnet | ChainType::Signet => false, diff --git a/consensus/src/pow/error.rs b/consensus/src/pow/error.rs index edc90903a1..fd4dbf3807 100644 --- a/consensus/src/pow/error.rs +++ b/consensus/src/pow/error.rs @@ -44,8 +44,6 @@ pub enum ConsensusPoWError { PoSInputDataProvided, #[error("No input data was provided for PoW block generation")] NoInputDataProvided, - #[error("Genesis block cannot have an ongoing difficulty")] - GenesisCannotHaveOngoingDifficulty, #[error("Block reward maturity value {0} is invalid")] InvalidBlockRewardMaturityDistance(BlockDistance), } diff --git a/consensus/src/pow/mod.rs b/consensus/src/pow/mod.rs index e42e221e8e..629451bafe 100644 --- a/consensus/src/pow/mod.rs +++ b/consensus/src/pow/mod.rs @@ -34,7 +34,7 @@ struct PoW(PoWChainConfig); impl PoW { pub fn new(chain_config: &ChainConfig) -> Self { - PoW(chain_config.get_proof_of_work_config()) + PoW(chain_config.get_proof_of_work_config().clone()) } pub fn difficulty_limit(&self) -> Uint256 { diff --git a/consensus/src/pow/work.rs b/consensus/src/pow/work.rs index 86d3a4820f..376de2f727 100644 --- a/consensus/src/pow/work.rs +++ b/consensus/src/pow/work.rs @@ -63,7 +63,7 @@ pub fn check_pow_consensus( let work_required = match pow_status { PoWStatus::Threshold { initial_difficulty } => *initial_difficulty, PoWStatus::Ongoing => match header.prev_block_id().classify(chain_config) { - GenBlockId::Genesis(_) => Err(ConsensusPoWError::GenesisCannotHaveOngoingDifficulty)?, + GenBlockId::Genesis(_) => PoW::new(chain_config).difficulty_limit().into(), GenBlockId::Block(prev_id) => { let prev_block_index = block_index_handle .get_block_index(&prev_id) @@ -119,9 +119,7 @@ where match pow_status { PoWStatus::Threshold { initial_difficulty } => Ok(*initial_difficulty), PoWStatus::Ongoing => match prev_gen_block_index { - GenBlockIndex::Genesis(_) => { - Err(ConsensusPoWError::GenesisCannotHaveOngoingDifficulty)? - } + GenBlockIndex::Genesis(_) => Ok(PoW::new(chain_config).difficulty_limit().into()), GenBlockIndex::Block(prev_block_index) => PoW::new(chain_config).get_work_required( prev_block_index, block_timestamp,