diff --git a/common/src/chain/config/mod.rs b/common/src/chain/config/mod.rs index 7d89424fea..eab298d42b 100644 --- a/common/src/chain/config/mod.rs +++ b/common/src/chain/config/mod.rs @@ -637,6 +637,57 @@ pub fn create_unit_test_config() -> ChainConfig { .build() } +/// This function ensure that IgnoreConsensus will never be used in anything other than regtest +pub fn assert_no_ignore_consensus_in_chain_config(chain_config: &ChainConfig) { + match chain_config.chain_type() { + ChainType::Regtest => { + return; + } + ChainType::Mainnet | ChainType::Testnet | ChainType::Signet => {} + } + + let upgrades = chain_config.net_upgrade(); + + let all_upgrades = upgrades.all_upgrades(); + + assert!( + !all_upgrades.is_empty(), + "Invalid chain config. There are no net-upgrades defined, not even for genesis." + ); + + assert!(all_upgrades.len() >= 2, "Invalid chain config. There must be at least 2 net-upgrades defined, one for genesis and one for the first block after genesis."); + + assert!( + all_upgrades[0].0 == 0.into(), + "Invalid chain config. The first net-upgrade must be at height 0" + ); + + assert!( + upgrades.consensus_status(0.into()) == RequiredConsensus::IgnoreConsensus, + "Invalid chain config. The genesis net-upgrade must be IgnoreConsensus" + ); + + assert!( + upgrades.consensus_status(1.into()) != RequiredConsensus::IgnoreConsensus, + "Invalid chain config. The net-upgrade at height 1 must not be IgnoreConsensus" + ); + + for upgrade in all_upgrades.iter().skip(1) { + let upgrade_height = &upgrade.0; + let upgrade_data = &upgrade.1; + + let consensus = upgrades.consensus_status(*upgrade_height); + assert_ne!( + RequiredConsensus::IgnoreConsensus, + consensus, + "Upgrade {:?} at height {} is ignoring consensus in net type {}. This is only allowed in regtest", + upgrade_data, + upgrade_height, + chain_config.chain_type().name() + ) + } +} + #[cfg(test)] mod tests { use super::*; @@ -816,4 +867,32 @@ mod tests { .build(); assert_eq!(expected_epoch, config.sealed_epoch_index(&block_height)); } + + #[test] + fn test_ignore_consensus_in_mainnet() { + let config = create_mainnet(); + + assert_no_ignore_consensus_in_chain_config(&config); + } + + #[test] + #[should_panic( + expected = "Invalid chain config. There must be at least 2 net-upgrades defined, one for genesis and one for the first block after genesis." + )] + fn test_ignore_consensus_outside_regtest_in_no_upgrades() { + let config = + Builder::new(ChainType::Mainnet).net_upgrades(NetUpgrades::unit_tests()).build(); + + assert_no_ignore_consensus_in_chain_config(&config); + } + + #[test] + #[should_panic(expected = "The net-upgrade at height 1 must not be IgnoreConsensus")] + fn test_ignore_consensus_outside_regtest_with_deliberate_bad_upgrades() { + let config = Builder::new(ChainType::Mainnet) + .net_upgrades(NetUpgrades::deliberate_ignore_consensus_twice()) + .build(); + + assert_no_ignore_consensus_in_chain_config(&config); + } } diff --git a/common/src/chain/upgrades/netupgrade.rs b/common/src/chain/upgrades/netupgrade.rs index 0e5277ab7c..85e45889a3 100644 --- a/common/src/chain/upgrades/netupgrade.rs +++ b/common/src/chain/upgrades/netupgrade.rs @@ -42,6 +42,20 @@ impl NetUpgrades { )]) } + #[cfg(test)] + pub fn deliberate_ignore_consensus_twice() -> Self { + Self(vec![ + ( + BlockHeight::zero(), + UpgradeVersion::ConsensusUpgrade(ConsensusUpgrade::IgnoreConsensus), + ), + ( + BlockHeight::new(1), + UpgradeVersion::ConsensusUpgrade(ConsensusUpgrade::IgnoreConsensus), + ), + ]) + } + pub fn regtest_with_pos() -> Self { Self(vec![ ( @@ -57,6 +71,10 @@ impl NetUpgrades { ), ]) } + + pub fn all_upgrades(&self) -> &[(BlockHeight, UpgradeVersion)] { + &self.0 + } } pub trait Activate { diff --git a/node-lib/src/runner.rs b/node-lib/src/runner.rs index 3af8a27061..185a5ce1be 100644 --- a/node-lib/src/runner.rs +++ b/node-lib/src/runner.rs @@ -32,6 +32,7 @@ use chainstate::{rpc::ChainstateRpcServer, ChainstateError, InitializationError} use common::{ chain::{ config::{ + assert_no_ignore_consensus_in_chain_config, regtest::{create_regtest_pos_genesis, create_regtest_pow_genesis}, Builder as ChainConfigBuilder, ChainConfig, ChainType, EmissionScheduleTabular, }, @@ -86,6 +87,8 @@ async fn initialize( ) -> Result<(subsystem::Manager, NodeController)> { let chain_config = Arc::new(chain_config); + assert_no_ignore_consensus_in_chain_config(&chain_config); + // INITIALIZE SUBSYSTEMS let mut manager = subsystem::Manager::new("mintlayer");