From 841c4492ef9dd463470e29368ed37ec6876c29d3 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Mon, 7 Jul 2025 08:24:01 +0200 Subject: [PATCH 01/10] first boilerplate code for epoch 3.2 --- clarity/src/vm/analysis/mod.rs | 3 +- clarity/src/vm/analysis/type_checker/mod.rs | 6 +- clarity/src/vm/costs/mod.rs | 3 +- clarity/src/vm/functions/mod.rs | 2 + clarity/src/vm/test_util/mod.rs | 3 +- clarity/src/vm/tests/mod.rs | 4 + clarity/src/vm/types/signatures.rs | 9 +- clarity/src/vm/version.rs | 1 + stacks-common/src/libcommon.rs | 3 +- stacks-common/src/types/mod.rs | 26 ++- stacks-common/src/types/tests.rs | 5 + stackslib/src/chainstate/burn/db/sortdb.rs | 1 + .../burn/operations/leader_block_commit.rs | 6 +- stackslib/src/chainstate/coordinator/mod.rs | 7 +- stackslib/src/chainstate/stacks/db/blocks.rs | 24 ++- stackslib/src/chainstate/stacks/db/mod.rs | 1 + .../src/chainstate/stacks/db/transactions.rs | 5 + stackslib/src/clarity_vm/clarity.rs | 28 +++ .../src/clarity_vm/tests/large_contract.rs | 3 +- stackslib/src/config/mod.rs | 4 + stackslib/src/core/mod.rs | 174 +++++++++++++++++- stackslib/src/cost_estimates/pessimistic.rs | 2 + .../stacks-node/src/nakamoto_node/relayer.rs | 4 +- .../src/tests/nakamoto_integrations.rs | 14 +- 24 files changed, 298 insertions(+), 40 deletions(-) diff --git a/clarity/src/vm/analysis/mod.rs b/clarity/src/vm/analysis/mod.rs index 19183f5f67..ddbcadb0c3 100644 --- a/clarity/src/vm/analysis/mod.rs +++ b/clarity/src/vm/analysis/mod.rs @@ -152,7 +152,8 @@ pub fn run_analysis( | StacksEpochId::Epoch24 | StacksEpochId::Epoch25 | StacksEpochId::Epoch30 - | StacksEpochId::Epoch31 => { + | StacksEpochId::Epoch31 + | StacksEpochId::Epoch32 => { TypeChecker2_1::run_pass(&epoch, &mut contract_analysis, db, build_type_map) } StacksEpochId::Epoch10 => { diff --git a/clarity/src/vm/analysis/type_checker/mod.rs b/clarity/src/vm/analysis/type_checker/mod.rs index 68bbe1873e..4e6aec42da 100644 --- a/clarity/src/vm/analysis/type_checker/mod.rs +++ b/clarity/src/vm/analysis/type_checker/mod.rs @@ -45,7 +45,8 @@ impl FunctionType { | StacksEpochId::Epoch24 | StacksEpochId::Epoch25 | StacksEpochId::Epoch30 - | StacksEpochId::Epoch31 => self.check_args_2_1(accounting, args, clarity_version), + | StacksEpochId::Epoch31 + | StacksEpochId::Epoch32 => self.check_args_2_1(accounting, args, clarity_version), StacksEpochId::Epoch10 => { Err(CheckErrors::Expects("Epoch10 is not supported".into()).into()) } @@ -69,7 +70,8 @@ impl FunctionType { | StacksEpochId::Epoch24 | StacksEpochId::Epoch25 | StacksEpochId::Epoch30 - | StacksEpochId::Epoch31 => { + | StacksEpochId::Epoch31 + | StacksEpochId::Epoch32 => { self.check_args_by_allowing_trait_cast_2_1(db, clarity_version, func_args) } StacksEpochId::Epoch10 => { diff --git a/clarity/src/vm/costs/mod.rs b/clarity/src/vm/costs/mod.rs index 0b1559795f..f680435479 100644 --- a/clarity/src/vm/costs/mod.rs +++ b/clarity/src/vm/costs/mod.rs @@ -857,7 +857,8 @@ impl LimitedCostTracker { | StacksEpochId::Epoch24 | StacksEpochId::Epoch25 | StacksEpochId::Epoch30 - | StacksEpochId::Epoch31 => COSTS_3_NAME.to_string(), + | StacksEpochId::Epoch31 + | StacksEpochId::Epoch32 => COSTS_3_NAME.to_string(), }; Ok(result) } diff --git a/clarity/src/vm/functions/mod.rs b/clarity/src/vm/functions/mod.rs index 3eac4fb19e..7c92d9a929 100644 --- a/clarity/src/vm/functions/mod.rs +++ b/clarity/src/vm/functions/mod.rs @@ -56,6 +56,8 @@ macro_rules! switch_on_global_epoch { StacksEpochId::Epoch30 => $Epoch205Version(args, env, context), // Note: We reuse 2.05 for 3.1. StacksEpochId::Epoch31 => $Epoch205Version(args, env, context), + // Note: We reuse 2.05 for 3.2. + StacksEpochId::Epoch32 => $Epoch205Version(args, env, context), } } }; diff --git a/clarity/src/vm/test_util/mod.rs b/clarity/src/vm/test_util/mod.rs index 37a40182eb..f1b354ebc7 100644 --- a/clarity/src/vm/test_util/mod.rs +++ b/clarity/src/vm/test_util/mod.rs @@ -53,7 +53,8 @@ pub fn generate_test_burn_state_db(epoch_id: StacksEpochId) -> UnitTestBurnState | StacksEpochId::Epoch24 | StacksEpochId::Epoch25 | StacksEpochId::Epoch30 - | StacksEpochId::Epoch31 => UnitTestBurnStateDB { + | StacksEpochId::Epoch31 + | StacksEpochId::Epoch32 => UnitTestBurnStateDB { epoch_id, ast_rules: ASTRules::PrecheckSize, }, diff --git a/clarity/src/vm/tests/mod.rs b/clarity/src/vm/tests/mod.rs index f261733191..7d58fb515e 100644 --- a/clarity/src/vm/tests/mod.rs +++ b/clarity/src/vm/tests/mod.rs @@ -125,6 +125,7 @@ epochs_template! { Epoch25, Epoch30, Epoch31, + Epoch32, } clarity_template! { @@ -146,6 +147,9 @@ clarity_template! { (Epoch31, Clarity1), (Epoch31, Clarity2), (Epoch31, Clarity3), + (Epoch32, Clarity1), + (Epoch32, Clarity2), + (Epoch32, Clarity3), } #[cfg(test)] diff --git a/clarity/src/vm/types/signatures.rs b/clarity/src/vm/types/signatures.rs index f41b8ed1a3..88295eb104 100644 --- a/clarity/src/vm/types/signatures.rs +++ b/clarity/src/vm/types/signatures.rs @@ -585,7 +585,8 @@ impl TypeSignature { | StacksEpochId::Epoch24 | StacksEpochId::Epoch25 | StacksEpochId::Epoch30 - | StacksEpochId::Epoch31 => self.admits_type_v2_1(other), + | StacksEpochId::Epoch31 + | StacksEpochId::Epoch32 => self.admits_type_v2_1(other), StacksEpochId::Epoch10 => Err(CheckErrors::Expects("epoch 1.0 not supported".into())), } } @@ -793,7 +794,8 @@ impl TypeSignature { | StacksEpochId::Epoch24 | StacksEpochId::Epoch25 | StacksEpochId::Epoch30 - | StacksEpochId::Epoch31 => self.canonicalize_v2_1(), + | StacksEpochId::Epoch31 + | StacksEpochId::Epoch32 => self.canonicalize_v2_1(), } } @@ -1152,7 +1154,8 @@ impl TypeSignature { | StacksEpochId::Epoch24 | StacksEpochId::Epoch25 | StacksEpochId::Epoch30 - | StacksEpochId::Epoch31 => Self::least_supertype_v2_1(a, b), + | StacksEpochId::Epoch31 + | StacksEpochId::Epoch32 => Self::least_supertype_v2_1(a, b), StacksEpochId::Epoch10 => Err(CheckErrors::Expects("epoch 1.0 not supported".into())), } } diff --git a/clarity/src/vm/version.rs b/clarity/src/vm/version.rs index 7050d5dbd9..adc801f3e9 100644 --- a/clarity/src/vm/version.rs +++ b/clarity/src/vm/version.rs @@ -41,6 +41,7 @@ impl ClarityVersion { StacksEpochId::Epoch25 => ClarityVersion::Clarity2, StacksEpochId::Epoch30 => ClarityVersion::Clarity3, StacksEpochId::Epoch31 => ClarityVersion::Clarity3, + StacksEpochId::Epoch32 => ClarityVersion::Clarity3, } } } diff --git a/stacks-common/src/libcommon.rs b/stacks-common/src/libcommon.rs index 48f8cd5970..6936f0e29f 100644 --- a/stacks-common/src/libcommon.rs +++ b/stacks-common/src/libcommon.rs @@ -80,10 +80,11 @@ pub mod consts { pub const PEER_VERSION_EPOCH_2_5: u8 = 0x0a; pub const PEER_VERSION_EPOCH_3_0: u8 = 0x0b; pub const PEER_VERSION_EPOCH_3_1: u8 = 0x0c; + pub const PEER_VERSION_EPOCH_3_2: u8 = 0x0d; /// this should be updated to the latest network epoch version supported by /// this node. this will be checked by the `validate_epochs()` method. - pub const PEER_NETWORK_EPOCH: u32 = PEER_VERSION_EPOCH_3_1 as u32; + pub const PEER_NETWORK_EPOCH: u32 = PEER_VERSION_EPOCH_3_2 as u32; /// set the fourth byte of the peer version pub const PEER_VERSION_MAINNET: u32 = PEER_VERSION_MAINNET_MAJOR | PEER_NETWORK_EPOCH; diff --git a/stacks-common/src/types/mod.rs b/stacks-common/src/types/mod.rs index a4cc64aadc..81874e52e9 100644 --- a/stacks-common/src/types/mod.rs +++ b/stacks-common/src/types/mod.rs @@ -102,6 +102,7 @@ pub enum StacksEpochId { Epoch25 = 0x0201a, Epoch30 = 0x03000, Epoch31 = 0x03001, + Epoch32 = 0x03002, } #[derive(Debug)] @@ -269,7 +270,7 @@ impl StacksEpochId { | StacksEpochId::Epoch23 | StacksEpochId::Epoch24 | StacksEpochId::Epoch25 => MempoolCollectionBehavior::ByStacksHeight, - StacksEpochId::Epoch30 | StacksEpochId::Epoch31 => { + StacksEpochId::Epoch30 | StacksEpochId::Epoch31 | StacksEpochId::Epoch32 => { MempoolCollectionBehavior::ByReceiveTime } } @@ -286,7 +287,10 @@ impl StacksEpochId { | StacksEpochId::Epoch22 | StacksEpochId::Epoch23 | StacksEpochId::Epoch24 => false, - StacksEpochId::Epoch25 | StacksEpochId::Epoch30 | StacksEpochId::Epoch31 => true, + StacksEpochId::Epoch25 + | StacksEpochId::Epoch30 + | StacksEpochId::Epoch31 + | StacksEpochId::Epoch32 => true, } } @@ -303,7 +307,8 @@ impl StacksEpochId { StacksEpochId::Epoch24 | StacksEpochId::Epoch25 | StacksEpochId::Epoch30 - | StacksEpochId::Epoch31 => true, + | StacksEpochId::Epoch31 + | StacksEpochId::Epoch32 => true, } } @@ -319,7 +324,7 @@ impl StacksEpochId { | StacksEpochId::Epoch23 | StacksEpochId::Epoch24 | StacksEpochId::Epoch25 => false, - StacksEpochId::Epoch30 | StacksEpochId::Epoch31 => true, + StacksEpochId::Epoch30 | StacksEpochId::Epoch31 | StacksEpochId::Epoch32 => true, } } @@ -335,7 +340,7 @@ impl StacksEpochId { | StacksEpochId::Epoch23 | StacksEpochId::Epoch24 | StacksEpochId::Epoch25 => false, - StacksEpochId::Epoch30 | StacksEpochId::Epoch31 => true, + StacksEpochId::Epoch30 | StacksEpochId::Epoch31 | StacksEpochId::Epoch32 => true, } } @@ -350,7 +355,7 @@ impl StacksEpochId { | StacksEpochId::Epoch23 | StacksEpochId::Epoch24 | StacksEpochId::Epoch25 => false, - StacksEpochId::Epoch30 | StacksEpochId::Epoch31 => true, + StacksEpochId::Epoch30 | StacksEpochId::Epoch31 | StacksEpochId::Epoch32 => true, } } @@ -381,7 +386,9 @@ impl StacksEpochId { | StacksEpochId::Epoch23 | StacksEpochId::Epoch24 | StacksEpochId::Epoch25 => 0, - StacksEpochId::Epoch30 | StacksEpochId::Epoch31 => MINING_COMMITMENT_FREQUENCY_NAKAMOTO, + StacksEpochId::Epoch30 | StacksEpochId::Epoch31 | StacksEpochId::Epoch32 => { + MINING_COMMITMENT_FREQUENCY_NAKAMOTO + } } } @@ -417,7 +424,7 @@ impl StacksEpochId { | StacksEpochId::Epoch23 | StacksEpochId::Epoch24 | StacksEpochId::Epoch25 => false, - StacksEpochId::Epoch30 | StacksEpochId::Epoch31 => { + StacksEpochId::Epoch30 | StacksEpochId::Epoch31 | StacksEpochId::Epoch32 => { cur_reward_cycle > first_epoch30_reward_cycle } } @@ -535,7 +542,7 @@ impl StacksEpochId { | StacksEpochId::Epoch30 => { self.coinbase_reward_pre_sip029(first_burnchain_height, current_burnchain_height) } - StacksEpochId::Epoch31 => self.coinbase_reward_sip029( + StacksEpochId::Epoch31 | StacksEpochId::Epoch32 => self.coinbase_reward_sip029( mainnet, first_burnchain_height, current_burnchain_height, @@ -557,6 +564,7 @@ impl std::fmt::Display for StacksEpochId { StacksEpochId::Epoch25 => write!(f, "2.5"), StacksEpochId::Epoch30 => write!(f, "3.0"), StacksEpochId::Epoch31 => write!(f, "3.1"), + StacksEpochId::Epoch32 => write!(f, "3.2"), } } } diff --git a/stacks-common/src/types/tests.rs b/stacks-common/src/types/tests.rs index 20676999e7..33e235515b 100644 --- a/stacks-common/src/types/tests.rs +++ b/stacks-common/src/types/tests.rs @@ -245,6 +245,11 @@ fn test_epoch_coinbase_reward() { 62_500_000 ); + assert_eq!( + StacksEpochId::Epoch32.coinbase_reward(true, 666050, 666050), + 1_000_000_000 + ); + // old coinbase schedule for epoch in [ StacksEpochId::Epoch20, diff --git a/stackslib/src/chainstate/burn/db/sortdb.rs b/stackslib/src/chainstate/burn/db/sortdb.rs index e5f5bdde96..88fc138a40 100644 --- a/stackslib/src/chainstate/burn/db/sortdb.rs +++ b/stackslib/src/chainstate/burn/db/sortdb.rs @@ -3236,6 +3236,7 @@ impl SortitionDB { StacksEpochId::Epoch25 => version_u32 >= 3, StacksEpochId::Epoch30 => version_u32 >= 3, StacksEpochId::Epoch31 => version_u32 >= 3, + StacksEpochId::Epoch32 => version_u32 >= 3, } } diff --git a/stackslib/src/chainstate/burn/operations/leader_block_commit.rs b/stackslib/src/chainstate/burn/operations/leader_block_commit.rs index 562d409802..abaa31e224 100644 --- a/stackslib/src/chainstate/burn/operations/leader_block_commit.rs +++ b/stackslib/src/chainstate/burn/operations/leader_block_commit.rs @@ -36,7 +36,7 @@ use crate::chainstate::stacks::address::PoxAddress; use crate::core::{ StacksEpochId, STACKS_EPOCH_2_05_MARKER, STACKS_EPOCH_2_1_MARKER, STACKS_EPOCH_2_2_MARKER, STACKS_EPOCH_2_3_MARKER, STACKS_EPOCH_2_4_MARKER, STACKS_EPOCH_2_5_MARKER, - STACKS_EPOCH_3_0_MARKER, STACKS_EPOCH_3_1_MARKER, + STACKS_EPOCH_3_0_MARKER, STACKS_EPOCH_3_1_MARKER, STACKS_EPOCH_3_2_MARKER, }; // return type from parse_data below @@ -877,6 +877,7 @@ impl LeaderBlockCommitOp { StacksEpochId::Epoch25 => self.check_epoch_commit_marker(STACKS_EPOCH_2_5_MARKER), StacksEpochId::Epoch30 => self.check_epoch_commit_marker(STACKS_EPOCH_3_0_MARKER), StacksEpochId::Epoch31 => self.check_epoch_commit_marker(STACKS_EPOCH_3_1_MARKER), + StacksEpochId::Epoch32 => self.check_epoch_commit_marker(STACKS_EPOCH_3_2_MARKER), } } @@ -897,7 +898,8 @@ impl LeaderBlockCommitOp { | StacksEpochId::Epoch24 | StacksEpochId::Epoch25 | StacksEpochId::Epoch30 - | StacksEpochId::Epoch31 => { + | StacksEpochId::Epoch31 + | StacksEpochId::Epoch32 => { // correct behavior -- uses *sortition height* to find the intended sortition ID let sortition_height = self .block_height diff --git a/stackslib/src/chainstate/coordinator/mod.rs b/stackslib/src/chainstate/coordinator/mod.rs index caf2a90ffb..cc21170543 100644 --- a/stackslib/src/chainstate/coordinator/mod.rs +++ b/stackslib/src/chainstate/coordinator/mod.rs @@ -425,8 +425,11 @@ impl OnChainRewardSetProvider<'_, T> { return Ok(RewardSet::empty()); } } - StacksEpochId::Epoch25 | StacksEpochId::Epoch30 | StacksEpochId::Epoch31 => { - // Epoch 2.5, 3.0, and 3.1 compute reward sets, but *only* if PoX-4 is active + StacksEpochId::Epoch25 + | StacksEpochId::Epoch30 + | StacksEpochId::Epoch31 + | StacksEpochId::Epoch32 => { + // Epoch 2.5, 3.0, 3.1 and 3.2 compute reward sets, but *only* if PoX-4 is active if burnchain .pox_constants .active_pox_contract(current_burn_height) diff --git a/stackslib/src/chainstate/stacks/db/blocks.rs b/stackslib/src/chainstate/stacks/db/blocks.rs index 2c35ac1fe7..873cae15a5 100644 --- a/stackslib/src/chainstate/stacks/db/blocks.rs +++ b/stackslib/src/chainstate/stacks/db/blocks.rs @@ -4092,6 +4092,10 @@ impl StacksChainState { current_epoch = StacksEpochId::Epoch31; } StacksEpochId::Epoch31 => { + receipts.append(&mut clarity_tx.block.initialize_epoch_3_2()?); + current_epoch = StacksEpochId::Epoch32; + } + StacksEpochId::Epoch32 => { panic!("No defined transition from Epoch31 forward") } } @@ -4921,7 +4925,10 @@ impl StacksChainState { )?; Ok((stack_ops, transfer_ops, delegate_ops, vec![])) } - StacksEpochId::Epoch25 | StacksEpochId::Epoch30 | StacksEpochId::Epoch31 => { + StacksEpochId::Epoch25 + | StacksEpochId::Epoch30 + | StacksEpochId::Epoch31 + | StacksEpochId::Epoch32 => { StacksChainState::get_stacking_and_transfer_and_delegate_burn_ops_v210( chainstate_tx, parent_index_hash, @@ -5011,13 +5018,14 @@ impl StacksChainState { pox_reward_cycle, pox_start_cycle_info, ), - StacksEpochId::Epoch25 | StacksEpochId::Epoch30 | StacksEpochId::Epoch31 => { - Self::handle_pox_cycle_start_pox_4( - clarity_tx, - pox_reward_cycle, - pox_start_cycle_info, - ) - } + StacksEpochId::Epoch25 + | StacksEpochId::Epoch30 + | StacksEpochId::Epoch31 + | StacksEpochId::Epoch32 => Self::handle_pox_cycle_start_pox_4( + clarity_tx, + pox_reward_cycle, + pox_start_cycle_info, + ), } })?; debug!("check_and_handle_reward_start: handled pox cycle start"); diff --git a/stackslib/src/chainstate/stacks/db/mod.rs b/stackslib/src/chainstate/stacks/db/mod.rs index 09064efae2..51b38fd4b8 100644 --- a/stackslib/src/chainstate/stacks/db/mod.rs +++ b/stackslib/src/chainstate/stacks/db/mod.rs @@ -292,6 +292,7 @@ impl DBConfig { StacksEpochId::Epoch25 => version_u32 >= 3 && version_u32 <= 10, StacksEpochId::Epoch30 => version_u32 >= 3 && version_u32 <= 10, StacksEpochId::Epoch31 => version_u32 >= 3 && version_u32 <= 10, + StacksEpochId::Epoch32 => version_u32 >= 3 && version_u32 <= 10, } } } diff --git a/stackslib/src/chainstate/stacks/db/transactions.rs b/stackslib/src/chainstate/stacks/db/transactions.rs index e432ab902f..2df237e8fa 100644 --- a/stackslib/src/chainstate/stacks/db/transactions.rs +++ b/stackslib/src/chainstate/stacks/db/transactions.rs @@ -1658,6 +1658,10 @@ pub mod test { epoch_id: StacksEpochId::Epoch31, ast_rules: ASTRules::PrecheckSize, }; + pub const TestBurnStateDB_32: UnitTestBurnStateDB = UnitTestBurnStateDB { + epoch_id: StacksEpochId::Epoch32, + ast_rules: ASTRules::PrecheckSize, + }; pub const ALL_BURN_DBS: &[&dyn BurnStateDB] = &[ &TestBurnStateDB_20 as &dyn BurnStateDB, @@ -8731,6 +8735,7 @@ pub mod test { StacksEpochId::Epoch25 => self.get_stacks_epoch(6), StacksEpochId::Epoch30 => self.get_stacks_epoch(7), StacksEpochId::Epoch31 => self.get_stacks_epoch(8), + StacksEpochId::Epoch32 => self.get_stacks_epoch(9), } } fn get_pox_payout_addrs( diff --git a/stackslib/src/clarity_vm/clarity.rs b/stackslib/src/clarity_vm/clarity.rs index e9abf82f47..5c465b1a9c 100644 --- a/stackslib/src/clarity_vm/clarity.rs +++ b/stackslib/src/clarity_vm/clarity.rs @@ -1596,6 +1596,34 @@ impl<'a> ClarityBlockConnection<'a, '_> { }) } + pub fn initialize_epoch_3_2(&mut self) -> Result, Error> { + // use the `using!` statement to ensure that the old cost_tracker is placed + // back in all branches after initialization + using!(self.cost_track, "cost tracker", |old_cost_tracker| { + // epoch initialization is *free*. + // NOTE: this also means that cost functions won't be evaluated. + self.cost_track.replace(LimitedCostTracker::new_free()); + self.epoch = StacksEpochId::Epoch32; + self.as_transaction(|tx_conn| { + // bump the epoch in the Clarity DB + tx_conn + .with_clarity_db(|db| { + db.set_clarity_epoch_version(StacksEpochId::Epoch32)?; + Ok(()) + }) + .unwrap(); + + // require 3.2 rules henceforth in this connection as well + tx_conn.epoch = StacksEpochId::Epoch32; + }); + + // TODO: SIP-031 setup (minting and transfer to the boot contract) + + debug!("Epoch 3.2 initialized"); + (old_cost_tracker, Ok(vec![])) + }) + } + pub fn start_transaction_processing(&mut self) -> ClarityTransactionConnection { ClarityTransactionConnection::new( &mut self.datastore, diff --git a/stackslib/src/clarity_vm/tests/large_contract.rs b/stackslib/src/clarity_vm/tests/large_contract.rs index 36e24054d7..1915d3db36 100644 --- a/stackslib/src/clarity_vm/tests/large_contract.rs +++ b/stackslib/src/clarity_vm/tests/large_contract.rs @@ -163,7 +163,8 @@ fn test_simple_token_system(#[case] version: ClarityVersion, #[case] epoch: Stac | StacksEpochId::Epoch24 | StacksEpochId::Epoch25 | StacksEpochId::Epoch30 - | StacksEpochId::Epoch31 => { + | StacksEpochId::Epoch31 + | StacksEpochId::Epoch32 => { let (ast, _analysis) = tx .analyze_smart_contract( &boot_code_id("costs-3", false), diff --git a/stackslib/src/config/mod.rs b/stackslib/src/config/mod.rs index 4689b73ace..c994d37b23 100644 --- a/stackslib/src/config/mod.rs +++ b/stackslib/src/config/mod.rs @@ -718,6 +718,8 @@ impl Config { Ok(StacksEpochId::Epoch30) } else if epoch_name == EPOCH_CONFIG_3_1_0 { Ok(StacksEpochId::Epoch31) + } else if epoch_name == EPOCH_CONFIG_3_2_0 { + Ok(StacksEpochId::Epoch32) } else { Err(format!("Unknown epoch name specified: {epoch_name}")) }?; @@ -745,6 +747,7 @@ impl Config { StacksEpochId::Epoch25, StacksEpochId::Epoch30, StacksEpochId::Epoch31, + StacksEpochId::Epoch32, ]; for (expected_epoch, configured_epoch) in expected_list .iter() @@ -1726,6 +1729,7 @@ pub const EPOCH_CONFIG_2_4_0: &str = "2.4"; pub const EPOCH_CONFIG_2_5_0: &str = "2.5"; pub const EPOCH_CONFIG_3_0_0: &str = "3.0"; pub const EPOCH_CONFIG_3_1_0: &str = "3.1"; +pub const EPOCH_CONFIG_3_2_0: &str = "3.2"; #[derive(Clone, Deserialize, Default, Debug)] pub struct AffirmationOverride { diff --git a/stackslib/src/core/mod.rs b/stackslib/src/core/mod.rs index 76e194aaff..a2c42a916d 100644 --- a/stackslib/src/core/mod.rs +++ b/stackslib/src/core/mod.rs @@ -49,8 +49,8 @@ pub use stacks_common::consts::{ NETWORK_ID_TESTNET, PEER_NETWORK_EPOCH, PEER_VERSION_EPOCH_1_0, PEER_VERSION_EPOCH_2_0, PEER_VERSION_EPOCH_2_05, PEER_VERSION_EPOCH_2_1, PEER_VERSION_EPOCH_2_2, PEER_VERSION_EPOCH_2_3, PEER_VERSION_EPOCH_2_4, PEER_VERSION_EPOCH_2_5, PEER_VERSION_EPOCH_3_0, - PEER_VERSION_EPOCH_3_1, PEER_VERSION_MAINNET, PEER_VERSION_MAINNET_MAJOR, PEER_VERSION_TESTNET, - PEER_VERSION_TESTNET_MAJOR, STACKS_EPOCH_MAX, + PEER_VERSION_EPOCH_3_1, PEER_VERSION_EPOCH_3_2, PEER_VERSION_MAINNET, + PEER_VERSION_MAINNET_MAJOR, PEER_VERSION_TESTNET, PEER_VERSION_TESTNET_MAJOR, STACKS_EPOCH_MAX, }; // default port @@ -104,6 +104,8 @@ pub const BITCOIN_MAINNET_STACKS_25_BURN_HEIGHT: u64 = 840_360; pub const BITCOIN_MAINNET_STACKS_30_BURN_HEIGHT: u64 = 867_867; /// This is Epoch-3.1, activation height proposed in SIP-029 pub const BITCOIN_MAINNET_STACKS_31_BURN_HEIGHT: u64 = 875_000; +/// This is Epoch-3.2, activation height proposed in SIP-031 +pub const BITCOIN_MAINNET_STACKS_32_BURN_HEIGHT: u64 = 907_740; /// Bitcoin mainline testnet3 activation heights. /// TODO: No longer used since testnet3 is dead, so remove. @@ -119,6 +121,7 @@ pub const BITCOIN_TESTNET_STACKS_24_BURN_HEIGHT: u64 = 2_432_545; pub const BITCOIN_TESTNET_STACKS_25_BURN_HEIGHT: u64 = 2_583_893; pub const BITCOIN_TESTNET_STACKS_30_BURN_HEIGHT: u64 = 30_000_000; pub const BITCOIN_TESTNET_STACKS_31_BURN_HEIGHT: u64 = 30_000_001; +pub const BITCOIN_TESTNET_STACKS_32_BURN_HEIGHT: u64 = 30_000_002; /// This constant sets the approximate testnet bitcoin height at which 2.5 Xenon /// was reorged back to 2.5 instantiation. This is only used to calculate the @@ -311,10 +314,17 @@ lazy_static! { StacksEpoch { epoch_id: StacksEpochId::Epoch31, start_height: BITCOIN_MAINNET_STACKS_31_BURN_HEIGHT, - end_height: STACKS_EPOCH_MAX, + end_height: BITCOIN_MAINNET_STACKS_32_BURN_HEIGHT, block_limit: BLOCK_LIMIT_MAINNET_21.clone(), network_epoch: PEER_VERSION_EPOCH_3_1 }, + StacksEpoch { + epoch_id: StacksEpochId::Epoch32, + start_height: BITCOIN_MAINNET_STACKS_32_BURN_HEIGHT, + end_height: STACKS_EPOCH_MAX, + block_limit: BLOCK_LIMIT_MAINNET_21.clone(), + network_epoch: PEER_VERSION_EPOCH_3_2 + }, ]); } @@ -386,10 +396,17 @@ lazy_static! { StacksEpoch { epoch_id: StacksEpochId::Epoch31, start_height: BITCOIN_TESTNET_STACKS_31_BURN_HEIGHT, - end_height: STACKS_EPOCH_MAX, + end_height: BITCOIN_TESTNET_STACKS_32_BURN_HEIGHT, block_limit: BLOCK_LIMIT_MAINNET_21.clone(), network_epoch: PEER_VERSION_EPOCH_3_1 }, + StacksEpoch { + epoch_id: StacksEpochId::Epoch32, + start_height: BITCOIN_TESTNET_STACKS_32_BURN_HEIGHT, + end_height: STACKS_EPOCH_MAX, + block_limit: BLOCK_LIMIT_MAINNET_21.clone(), + network_epoch: PEER_VERSION_EPOCH_3_2 + }, ]); } @@ -503,6 +520,10 @@ pub static STACKS_EPOCH_3_0_MARKER: u8 = 0x0b; /// running it prior to 3.1 activation. pub static STACKS_EPOCH_3_1_MARKER: u8 = 0x0d; +/// Stacks 3.0 epoch marker. All block-commits in 3.2 must have a memo bitfield with this value +/// *or greater*. +pub static STACKS_EPOCH_3_2_MARKER: u8 = 0x0e; + #[test] fn test_ord_for_stacks_epoch() { let epochs = &*STACKS_EPOCHS_MAINNET; @@ -783,6 +804,8 @@ pub trait StacksEpochExtension { #[cfg(test)] fn unit_test_3_1(epoch_2_0_block_height: u64) -> EpochList; #[cfg(test)] + fn unit_test_3_2(epoch_2_0_block_height: u64) -> EpochList; + #[cfg(test)] fn unit_test_2_1_only(epoch_2_0_block_height: u64) -> EpochList; #[cfg(test)] fn unit_test_3_0_only(first_burnchain_height: u64) -> EpochList; @@ -1551,6 +1574,148 @@ impl StacksEpochExtension for StacksEpoch { ]) } + #[cfg(test)] + fn unit_test_3_2(first_burnchain_height: u64) -> EpochList { + info!( + "StacksEpoch unit_test_3_2 first_burn_height = {}", + first_burnchain_height + ); + + EpochList::new(&[ + StacksEpoch { + epoch_id: StacksEpochId::Epoch10, + start_height: 0, + end_height: first_burnchain_height, + block_limit: ExecutionCost::max_value(), + network_epoch: PEER_VERSION_EPOCH_1_0, + }, + StacksEpoch { + epoch_id: StacksEpochId::Epoch20, + start_height: first_burnchain_height, + end_height: first_burnchain_height + 4, + block_limit: ExecutionCost::max_value(), + network_epoch: PEER_VERSION_EPOCH_2_0, + }, + StacksEpoch { + epoch_id: StacksEpochId::Epoch2_05, + start_height: first_burnchain_height + 4, + end_height: first_burnchain_height + 8, + block_limit: ExecutionCost { + write_length: 205205, + write_count: 205205, + read_length: 205205, + read_count: 205205, + runtime: 205205, + }, + network_epoch: PEER_VERSION_EPOCH_2_05, + }, + StacksEpoch { + epoch_id: StacksEpochId::Epoch21, + start_height: first_burnchain_height + 8, + end_height: first_burnchain_height + 12, + block_limit: ExecutionCost { + write_length: 210210, + write_count: 210210, + read_length: 210210, + read_count: 210210, + runtime: 210210, + }, + network_epoch: PEER_VERSION_EPOCH_2_1, + }, + StacksEpoch { + epoch_id: StacksEpochId::Epoch22, + start_height: first_burnchain_height + 12, + end_height: first_burnchain_height + 16, + block_limit: ExecutionCost { + write_length: 210210, + write_count: 210210, + read_length: 210210, + read_count: 210210, + runtime: 210210, + }, + network_epoch: PEER_VERSION_EPOCH_2_2, + }, + StacksEpoch { + epoch_id: StacksEpochId::Epoch23, + start_height: first_burnchain_height + 16, + end_height: first_burnchain_height + 20, + block_limit: ExecutionCost { + write_length: 210210, + write_count: 210210, + read_length: 210210, + read_count: 210210, + runtime: 210210, + }, + network_epoch: PEER_VERSION_EPOCH_2_3, + }, + StacksEpoch { + epoch_id: StacksEpochId::Epoch24, + start_height: first_burnchain_height + 20, + end_height: first_burnchain_height + 24, + block_limit: ExecutionCost { + write_length: 210210, + write_count: 210210, + read_length: 210210, + read_count: 210210, + runtime: 210210, + }, + network_epoch: PEER_VERSION_EPOCH_2_4, + }, + StacksEpoch { + epoch_id: StacksEpochId::Epoch25, + start_height: first_burnchain_height + 24, + end_height: first_burnchain_height + 28, + block_limit: ExecutionCost { + write_length: 210210, + write_count: 210210, + read_length: 210210, + read_count: 210210, + runtime: 210210, + }, + network_epoch: PEER_VERSION_EPOCH_2_5, + }, + StacksEpoch { + epoch_id: StacksEpochId::Epoch30, + start_height: first_burnchain_height + 28, + end_height: first_burnchain_height + 32, + block_limit: ExecutionCost { + write_length: 210210, + write_count: 210210, + read_length: 210210, + read_count: 210210, + runtime: 210210, + }, + network_epoch: PEER_VERSION_EPOCH_3_0, + }, + StacksEpoch { + epoch_id: StacksEpochId::Epoch31, + start_height: first_burnchain_height + 32, + end_height: first_burnchain_height + 36, + block_limit: ExecutionCost { + write_length: 210210, + write_count: 210210, + read_length: 210210, + read_count: 210210, + runtime: 210210, + }, + network_epoch: PEER_VERSION_EPOCH_3_1, + }, + StacksEpoch { + epoch_id: StacksEpochId::Epoch32, + start_height: first_burnchain_height + 36, + end_height: STACKS_EPOCH_MAX, + block_limit: ExecutionCost { + write_length: 210210, + write_count: 210210, + read_length: 210210, + read_count: 210210, + runtime: 210210, + }, + network_epoch: PEER_VERSION_EPOCH_3_2, + }, + ]) + } + #[cfg(test)] fn unit_test_2_1_only(first_burnchain_height: u64) -> EpochList { info!( @@ -1690,6 +1855,7 @@ impl StacksEpochExtension for StacksEpoch { StacksEpochId::Epoch25 => StacksEpoch::unit_test_2_5(first_burnchain_height), StacksEpochId::Epoch30 => StacksEpoch::unit_test_3_0(first_burnchain_height), StacksEpochId::Epoch31 => StacksEpoch::unit_test_3_1(first_burnchain_height), + StacksEpochId::Epoch32 => StacksEpoch::unit_test_3_2(first_burnchain_height), } } diff --git a/stackslib/src/cost_estimates/pessimistic.rs b/stackslib/src/cost_estimates/pessimistic.rs index 997fe8e8cb..314cbdc6aa 100644 --- a/stackslib/src/cost_estimates/pessimistic.rs +++ b/stackslib/src/cost_estimates/pessimistic.rs @@ -222,6 +222,8 @@ impl PessimisticEstimator { StacksEpochId::Epoch30 => ":2.1", // reuse cost estimates in Epoch31 StacksEpochId::Epoch31 => ":2.1", + // reuse cost estimates in Epoch32 + StacksEpochId::Epoch32 => ":2.1", }; format!( "cc{}:{}:{}.{}", diff --git a/testnet/stacks-node/src/nakamoto_node/relayer.rs b/testnet/stacks-node/src/nakamoto_node/relayer.rs index e1a19dea30..fa05bf6591 100644 --- a/testnet/stacks-node/src/nakamoto_node/relayer.rs +++ b/testnet/stacks-node/src/nakamoto_node/relayer.rs @@ -44,7 +44,7 @@ use stacks::chainstate::stacks::miner::{ use stacks::chainstate::stacks::Error as ChainstateError; use stacks::config::BurnchainConfig; use stacks::core::mempool::MemPoolDB; -use stacks::core::STACKS_EPOCH_3_1_MARKER; +use stacks::core::STACKS_EPOCH_3_2_MARKER; use stacks::monitoring::increment_stx_blocks_mined_counter; use stacks::net::db::LocalPeer; use stacks::net::p2p::NetworkHandle; @@ -1139,7 +1139,7 @@ impl RelayerThread { key_block_ptr: u32::try_from(key.block_height) .expect("FATAL: burn block height exceeded u32"), key_vtxindex: u16::try_from(key.op_vtxindex).expect("FATAL: vtxindex exceeded u16"), - memo: vec![STACKS_EPOCH_3_1_MARKER], + memo: vec![STACKS_EPOCH_3_2_MARKER], new_seed: VRFSeed::from_proof(&tip_vrf_proof), parent_block_ptr: u32::try_from(commit_parent_block_burn_height) .expect("FATAL: burn block height exceeded u32"), diff --git a/testnet/stacks-node/src/tests/nakamoto_integrations.rs b/testnet/stacks-node/src/tests/nakamoto_integrations.rs index 6adc8255e3..9c43ef2fbc 100644 --- a/testnet/stacks-node/src/tests/nakamoto_integrations.rs +++ b/testnet/stacks-node/src/tests/nakamoto_integrations.rs @@ -74,7 +74,8 @@ use stacks::core::{ EpochList, StacksEpoch, StacksEpochId, BLOCK_LIMIT_MAINNET_10, HELIUM_BLOCK_LIMIT_20, PEER_VERSION_EPOCH_1_0, PEER_VERSION_EPOCH_2_0, PEER_VERSION_EPOCH_2_05, PEER_VERSION_EPOCH_2_1, PEER_VERSION_EPOCH_2_2, PEER_VERSION_EPOCH_2_3, PEER_VERSION_EPOCH_2_4, - PEER_VERSION_EPOCH_2_5, PEER_VERSION_EPOCH_3_0, PEER_VERSION_EPOCH_3_1, PEER_VERSION_TESTNET, + PEER_VERSION_EPOCH_2_5, PEER_VERSION_EPOCH_3_0, PEER_VERSION_EPOCH_3_1, PEER_VERSION_EPOCH_3_2, + PEER_VERSION_TESTNET, }; use stacks::libstackerdb::{SlotMetadata, StackerDBChunkData}; use stacks::net::api::callreadonly::CallReadOnlyRequestBody; @@ -128,7 +129,7 @@ pub static POX_4_DEFAULT_STACKER_BALANCE: u64 = 100_000_000_000_000; pub static POX_4_DEFAULT_STACKER_STX_AMT: u128 = 99_000_000_000_000; lazy_static! { - pub static ref NAKAMOTO_INTEGRATION_EPOCHS: [StacksEpoch; 10] = [ + pub static ref NAKAMOTO_INTEGRATION_EPOCHS: [StacksEpoch; 11] = [ StacksEpoch { epoch_id: StacksEpochId::Epoch10, start_height: 0, @@ -195,10 +196,17 @@ lazy_static! { StacksEpoch { epoch_id: StacksEpochId::Epoch31, start_height: 241, - end_height: STACKS_EPOCH_MAX, + end_height: 251, block_limit: HELIUM_BLOCK_LIMIT_20.clone(), network_epoch: PEER_VERSION_EPOCH_3_1 }, + StacksEpoch { + epoch_id: StacksEpochId::Epoch32, + start_height: 251, + end_height: STACKS_EPOCH_MAX, + block_limit: HELIUM_BLOCK_LIMIT_20.clone(), + network_epoch: PEER_VERSION_EPOCH_3_2 + }, ]; } From a3222719af963d36b951098fc776473c08095f5d Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Mon, 7 Jul 2025 10:36:47 +0200 Subject: [PATCH 02/10] fixed epoch3.2 unit tests --- stacks-common/src/types/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stacks-common/src/types/mod.rs b/stacks-common/src/types/mod.rs index 81874e52e9..cfb932a74a 100644 --- a/stacks-common/src/types/mod.rs +++ b/stacks-common/src/types/mod.rs @@ -256,7 +256,7 @@ impl CoinbaseInterval { impl StacksEpochId { pub fn latest() -> StacksEpochId { - StacksEpochId::Epoch31 + StacksEpochId::Epoch32 } /// In this epoch, how should the mempool perform garbage collection? @@ -584,6 +584,7 @@ impl TryFrom for StacksEpochId { x if x == StacksEpochId::Epoch25 as u32 => Ok(StacksEpochId::Epoch25), x if x == StacksEpochId::Epoch30 as u32 => Ok(StacksEpochId::Epoch30), x if x == StacksEpochId::Epoch31 as u32 => Ok(StacksEpochId::Epoch31), + x if x == StacksEpochId::Epoch32 as u32 => Ok(StacksEpochId::Epoch32), _ => Err("Invalid epoch"), } } From 41ef6ae19ca0f239fe744ba8555d0481e773ccf7 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Mon, 7 Jul 2025 10:41:10 +0200 Subject: [PATCH 03/10] fixed formatting --- clarity/src/vm/types/signatures.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clarity/src/vm/types/signatures.rs b/clarity/src/vm/types/signatures.rs index 88295eb104..33531c0810 100644 --- a/clarity/src/vm/types/signatures.rs +++ b/clarity/src/vm/types/signatures.rs @@ -1154,7 +1154,7 @@ impl TypeSignature { | StacksEpochId::Epoch24 | StacksEpochId::Epoch25 | StacksEpochId::Epoch30 - | StacksEpochId::Epoch31 + | StacksEpochId::Epoch31 | StacksEpochId::Epoch32 => Self::least_supertype_v2_1(a, b), StacksEpochId::Epoch10 => Err(CheckErrors::Expects("epoch 1.0 not supported".into())), } From 03d0f50f718d3043f86bbd0beb6a5023766d1119 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Mon, 7 Jul 2025 10:42:15 +0200 Subject: [PATCH 04/10] formatting [2] --- clarity/src/vm/types/signatures.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clarity/src/vm/types/signatures.rs b/clarity/src/vm/types/signatures.rs index 33531c0810..19da888621 100644 --- a/clarity/src/vm/types/signatures.rs +++ b/clarity/src/vm/types/signatures.rs @@ -794,7 +794,7 @@ impl TypeSignature { | StacksEpochId::Epoch24 | StacksEpochId::Epoch25 | StacksEpochId::Epoch30 - | StacksEpochId::Epoch31 + | StacksEpochId::Epoch31 | StacksEpochId::Epoch32 => self.canonicalize_v2_1(), } } From c3a015ea6f345ac60f1dcd088744eacf2e0f44ad Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Mon, 7 Jul 2025 13:03:36 +0200 Subject: [PATCH 05/10] added integration test base for sip031 --- .../src/tests/nakamoto_integrations.rs | 135 ++++++++++++++++++ 1 file changed, 135 insertions(+) diff --git a/testnet/stacks-node/src/tests/nakamoto_integrations.rs b/testnet/stacks-node/src/tests/nakamoto_integrations.rs index 9c43ef2fbc..068a75c983 100644 --- a/testnet/stacks-node/src/tests/nakamoto_integrations.rs +++ b/testnet/stacks-node/src/tests/nakamoto_integrations.rs @@ -128,6 +128,8 @@ use crate::{tests, BitcoinRegtestController, BurnchainController, Config, Config pub static POX_4_DEFAULT_STACKER_BALANCE: u64 = 100_000_000_000_000; pub static POX_4_DEFAULT_STACKER_STX_AMT: u128 = 99_000_000_000_000; +use crate::clarity::vm::clarity::ClarityConnection; + lazy_static! { pub static ref NAKAMOTO_INTEGRATION_EPOCHS: [StacksEpoch; 11] = [ StacksEpoch { @@ -12675,3 +12677,136 @@ fn write_signer_update( ); } } + +/// Test SIP-031 activation +/// +/// - check epoch 3.2 is activa +/// - check sip031 boot contract has a balance of 200_000_000 STX (TODO) +#[test] +#[ignore] +fn test_sip_031_activation() { + if env::var("BITCOIND_TEST") != Ok("1".into()) { + return; + } + + let (mut naka_conf, _miner_account) = naka_neon_integration_conf(None); + naka_conf.node.pox_sync_sample_secs = 180; + naka_conf.burnchain.max_rbf = 10_000_000; + + let sender_sk = Secp256k1PrivateKey::random(); + let sender_signer_sk = Secp256k1PrivateKey::random(); + let sender_signer_addr = tests::to_addr(&sender_signer_sk); + let mut signers = TestSigners::new(vec![sender_signer_sk]); + let tenure_count = 5; + let inter_blocks_per_tenure = 9; + // setup sender + recipient for some test stx transfers + // these are necessary for the interim blocks to get mined at all + let sender_addr = tests::to_addr(&sender_sk); + let send_amt = 100; + let send_fee = 180; + naka_conf.add_initial_balance( + PrincipalData::from(sender_addr).to_string(), + (send_amt + send_fee) * tenure_count * inter_blocks_per_tenure, + ); + naka_conf.add_initial_balance(PrincipalData::from(sender_signer_addr).to_string(), 100000); + let stacker_sk = setup_stacker(&mut naka_conf); + + test_observer::spawn(); + test_observer::register_any(&mut naka_conf); + + let mut btcd_controller = BitcoinCoreController::new(naka_conf.clone()); + btcd_controller + .start_bitcoind() + .expect("Failed starting bitcoind"); + let mut btc_regtest_controller = BitcoinRegtestController::new(naka_conf.clone(), None); + btc_regtest_controller.bootstrap_chain(201); + + let mut run_loop = boot_nakamoto::BootRunLoop::new(naka_conf.clone()).unwrap(); + let run_loop_stopper = run_loop.get_termination_switch(); + let Counters { + blocks_processed, + naka_submitted_commits: commits_submitted, + .. + } = run_loop.counters(); + let counters = run_loop.counters(); + + let coord_channel = run_loop.coordinator_channels(); + + let run_loop_thread = thread::Builder::new() + .name("run_loop".into()) + .spawn(move || run_loop.start(None, 0)) + .unwrap(); + wait_for_runloop(&blocks_processed); + boot_to_epoch_3( + &naka_conf, + &blocks_processed, + &[stacker_sk], + &[sender_signer_sk], + &mut Some(&mut signers), + &mut btc_regtest_controller, + ); + + info!("Bootstrapped to Epoch-3.0 boundary, starting nakamoto miner"); + + let burnchain = naka_conf.get_burnchain(); + let sortdb = burnchain.open_sortition_db(true).unwrap(); + let (mut chainstate, _) = StacksChainState::open( + naka_conf.is_mainnet(), + naka_conf.burnchain.chain_id, + &naka_conf.get_chainstate_path_str(), + None, + ) + .unwrap(); + + info!("Nakamoto miner started..."); + blind_signer(&naka_conf, &signers, &counters); + + wait_for_first_naka_block_commit(60, &commits_submitted); + + // mine until epooch 3.2 height + loop { + let commits_before = commits_submitted.load(Ordering::SeqCst); + next_block_and_process_new_stacks_block(&mut btc_regtest_controller, 60, &coord_channel) + .unwrap(); + wait_for(20, || { + Ok(commits_submitted.load(Ordering::SeqCst) > commits_before) + }) + .unwrap(); + + let node_info = get_chain_info_opt(&naka_conf).unwrap(); + if node_info.burn_block_height + >= naka_conf.burnchain.epochs.clone().unwrap()[StacksEpochId::Epoch32].start_height + { + break; + } + } + + info!( + "Nakamoto miner has advanced to bitcoin height {}", + get_chain_info_opt(&naka_conf).unwrap().burn_block_height + ); + + // check for Epoch 3.2 in clarity db + let latest_stacks_block_id = get_latest_block_proposal(&naka_conf, &sortdb) + .unwrap() + .0 + .block_id(); + + let epoch_version = chainstate.with_read_only_clarity_tx( + &sortdb + .index_handle_at_block(&chainstate, &latest_stacks_block_id) + .unwrap(), + &latest_stacks_block_id, + |conn| conn.with_clarity_db_readonly(|db| db.get_clarity_epoch_version().unwrap()), + ); + + assert_eq!(epoch_version, Some(StacksEpochId::Epoch32)); + + coord_channel + .lock() + .expect("Mutex poisoned") + .stop_chains_coordinator(); + run_loop_stopper.store(false, Ordering::SeqCst); + + run_loop_thread.join().unwrap(); +} From 9b02f177364d41500246ffd233d7948c40f9c10b Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Tue, 8 Jul 2025 07:24:30 +0200 Subject: [PATCH 06/10] refactored coinbase schedule test --- stacks-common/src/types/tests.rs | 78 +++++++------------------------- 1 file changed, 17 insertions(+), 61 deletions(-) diff --git a/stacks-common/src/types/tests.rs b/stacks-common/src/types/tests.rs index 33e235515b..cf4668b976 100644 --- a/stacks-common/src/types/tests.rs +++ b/stacks-common/src/types/tests.rs @@ -184,71 +184,26 @@ fn test_get_coinbase_at_effective_height() { #[test] fn test_epoch_coinbase_reward() { // new coinbase schedule - assert_eq!( - StacksEpochId::Epoch31.coinbase_reward(true, 666050, 666050), - 1_000_000_000 - ); - assert_eq!( - StacksEpochId::Epoch31.coinbase_reward(true, 666050, 666051), - 1_000_000_000 - ); + for epoch in [StacksEpochId::Epoch31, StacksEpochId::Epoch32].iter() { + assert_eq!(epoch.coinbase_reward(true, 666050, 666050), 1_000_000_000); + assert_eq!(epoch.coinbase_reward(true, 666050, 666051), 1_000_000_000); - assert_eq!( - StacksEpochId::Epoch31.coinbase_reward(true, 666050, 944_999), - 1_000_000_000 - ); - assert_eq!( - StacksEpochId::Epoch31.coinbase_reward(true, 666050, 945_000), - 500_000_000 - ); - assert_eq!( - StacksEpochId::Epoch31.coinbase_reward(true, 666050, 945_001), - 500_000_000 - ); + assert_eq!(epoch.coinbase_reward(true, 666050, 944_999), 1_000_000_000); + assert_eq!(epoch.coinbase_reward(true, 666050, 945_000), 500_000_000); + assert_eq!(epoch.coinbase_reward(true, 666050, 945_001), 500_000_000); - assert_eq!( - StacksEpochId::Epoch31.coinbase_reward(true, 666050, 1_049_999), - 500_000_000 - ); - assert_eq!( - StacksEpochId::Epoch31.coinbase_reward(true, 666050, 1_050_000), - 250_000_000 - ); - assert_eq!( - StacksEpochId::Epoch31.coinbase_reward(true, 666050, 1_050_001), - 250_000_000 - ); + assert_eq!(epoch.coinbase_reward(true, 666050, 1_049_999), 500_000_000); + assert_eq!(epoch.coinbase_reward(true, 666050, 1_050_000), 250_000_000); + assert_eq!(epoch.coinbase_reward(true, 666050, 1_050_001), 250_000_000); - assert_eq!( - StacksEpochId::Epoch31.coinbase_reward(true, 666050, 1_259_999), - 250_000_000 - ); - assert_eq!( - StacksEpochId::Epoch31.coinbase_reward(true, 666050, 1_260_000), - 125_000_000 - ); - assert_eq!( - StacksEpochId::Epoch31.coinbase_reward(true, 666050, 1_260_001), - 125_000_000 - ); + assert_eq!(epoch.coinbase_reward(true, 666050, 1_259_999), 250_000_000); + assert_eq!(epoch.coinbase_reward(true, 666050, 1_260_000), 125_000_000); + assert_eq!(epoch.coinbase_reward(true, 666050, 1_260_001), 125_000_000); - assert_eq!( - StacksEpochId::Epoch31.coinbase_reward(true, 666050, 1_469_999), - 125_000_000 - ); - assert_eq!( - StacksEpochId::Epoch31.coinbase_reward(true, 666050, 1_470_000), - 62_500_000 - ); - assert_eq!( - StacksEpochId::Epoch31.coinbase_reward(true, 666050, 1_470_001), - 62_500_000 - ); - - assert_eq!( - StacksEpochId::Epoch32.coinbase_reward(true, 666050, 666050), - 1_000_000_000 - ); + assert_eq!(epoch.coinbase_reward(true, 666050, 1_469_999), 125_000_000); + assert_eq!(epoch.coinbase_reward(true, 666050, 1_470_000), 62_500_000); + assert_eq!(epoch.coinbase_reward(true, 666050, 1_470_001), 62_500_000); + } // old coinbase schedule for epoch in [ @@ -259,6 +214,7 @@ fn test_epoch_coinbase_reward() { StacksEpochId::Epoch23, StacksEpochId::Epoch24, StacksEpochId::Epoch25, + StacksEpochId::Epoch30, ] .iter() { From f5b04a3735cea32eb3d4d89c4fdeb872c7dc76aa Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Tue, 8 Jul 2025 07:24:59 +0200 Subject: [PATCH 07/10] Update stackslib/src/chainstate/stacks/db/blocks.rs Co-authored-by: Brice --- stackslib/src/chainstate/stacks/db/blocks.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stackslib/src/chainstate/stacks/db/blocks.rs b/stackslib/src/chainstate/stacks/db/blocks.rs index 873cae15a5..7fe642ee71 100644 --- a/stackslib/src/chainstate/stacks/db/blocks.rs +++ b/stackslib/src/chainstate/stacks/db/blocks.rs @@ -4096,7 +4096,7 @@ impl StacksChainState { current_epoch = StacksEpochId::Epoch32; } StacksEpochId::Epoch32 => { - panic!("No defined transition from Epoch31 forward") + panic!("No defined transition from Epoch32 forward") } } From 2fdfb07c7a71a7377405c560c5c45031c2f56657 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Tue, 8 Jul 2025 07:25:42 +0200 Subject: [PATCH 08/10] fixed spelling --- testnet/stacks-node/src/tests/nakamoto_integrations.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testnet/stacks-node/src/tests/nakamoto_integrations.rs b/testnet/stacks-node/src/tests/nakamoto_integrations.rs index 068a75c983..376ebdd19b 100644 --- a/testnet/stacks-node/src/tests/nakamoto_integrations.rs +++ b/testnet/stacks-node/src/tests/nakamoto_integrations.rs @@ -12680,7 +12680,7 @@ fn write_signer_update( /// Test SIP-031 activation /// -/// - check epoch 3.2 is activa +/// - check epoch 3.2 is active /// - check sip031 boot contract has a balance of 200_000_000 STX (TODO) #[test] #[ignore] From 64cbcfec4d9f66cb3b01f983cafeb22f2acf674c Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Tue, 8 Jul 2025 07:48:45 +0200 Subject: [PATCH 09/10] added testnet config for epoch 3.2 --- sample/conf/testnet-signer.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sample/conf/testnet-signer.toml b/sample/conf/testnet-signer.toml index 2fbff7a235..84e2a66d46 100644 --- a/sample/conf/testnet-signer.toml +++ b/sample/conf/testnet-signer.toml @@ -80,3 +80,7 @@ start_height = 1900 [[burnchain.epochs]] epoch_name = "3.1" start_height = 2000 + +[[burnchain.epochs]] +epoch_name = "3.2" +start_height = 2100 \ No newline at end of file From 91c9300b95807b17f8073a1499be0d89a556b069 Mon Sep 17 00:00:00 2001 From: Roberto De Ioris Date: Fri, 11 Jul 2025 15:44:54 +0200 Subject: [PATCH 10/10] postpone epoch 3.2 activation --- stacks-common/src/libcommon.rs | 2 +- stacks-common/src/types/mod.rs | 2 +- stackslib/src/core/mod.rs | 4 ++-- testnet/stacks-node/src/nakamoto_node/relayer.rs | 4 ++-- testnet/stacks-node/src/tests/nakamoto_integrations.rs | 5 +++++ 5 files changed, 11 insertions(+), 6 deletions(-) diff --git a/stacks-common/src/libcommon.rs b/stacks-common/src/libcommon.rs index 6936f0e29f..7d9136f20b 100644 --- a/stacks-common/src/libcommon.rs +++ b/stacks-common/src/libcommon.rs @@ -84,7 +84,7 @@ pub mod consts { /// this should be updated to the latest network epoch version supported by /// this node. this will be checked by the `validate_epochs()` method. - pub const PEER_NETWORK_EPOCH: u32 = PEER_VERSION_EPOCH_3_2 as u32; + pub const PEER_NETWORK_EPOCH: u32 = PEER_VERSION_EPOCH_3_1 as u32; /// set the fourth byte of the peer version pub const PEER_VERSION_MAINNET: u32 = PEER_VERSION_MAINNET_MAJOR | PEER_NETWORK_EPOCH; diff --git a/stacks-common/src/types/mod.rs b/stacks-common/src/types/mod.rs index cfb932a74a..0ead5475c1 100644 --- a/stacks-common/src/types/mod.rs +++ b/stacks-common/src/types/mod.rs @@ -256,7 +256,7 @@ impl CoinbaseInterval { impl StacksEpochId { pub fn latest() -> StacksEpochId { - StacksEpochId::Epoch32 + StacksEpochId::Epoch31 } /// In this epoch, how should the mempool perform garbage collection? diff --git a/stackslib/src/core/mod.rs b/stackslib/src/core/mod.rs index a2c42a916d..3661c2504a 100644 --- a/stackslib/src/core/mod.rs +++ b/stackslib/src/core/mod.rs @@ -104,8 +104,8 @@ pub const BITCOIN_MAINNET_STACKS_25_BURN_HEIGHT: u64 = 840_360; pub const BITCOIN_MAINNET_STACKS_30_BURN_HEIGHT: u64 = 867_867; /// This is Epoch-3.1, activation height proposed in SIP-029 pub const BITCOIN_MAINNET_STACKS_31_BURN_HEIGHT: u64 = 875_000; -/// This is Epoch-3.2, activation height proposed in SIP-031 -pub const BITCOIN_MAINNET_STACKS_32_BURN_HEIGHT: u64 = 907_740; +/// This is Epoch-3.2, activation height proposed in SIP-031 (placeholder for now) +pub const BITCOIN_MAINNET_STACKS_32_BURN_HEIGHT: u64 = u64::MAX; /// Bitcoin mainline testnet3 activation heights. /// TODO: No longer used since testnet3 is dead, so remove. diff --git a/testnet/stacks-node/src/nakamoto_node/relayer.rs b/testnet/stacks-node/src/nakamoto_node/relayer.rs index fa05bf6591..e1a19dea30 100644 --- a/testnet/stacks-node/src/nakamoto_node/relayer.rs +++ b/testnet/stacks-node/src/nakamoto_node/relayer.rs @@ -44,7 +44,7 @@ use stacks::chainstate::stacks::miner::{ use stacks::chainstate::stacks::Error as ChainstateError; use stacks::config::BurnchainConfig; use stacks::core::mempool::MemPoolDB; -use stacks::core::STACKS_EPOCH_3_2_MARKER; +use stacks::core::STACKS_EPOCH_3_1_MARKER; use stacks::monitoring::increment_stx_blocks_mined_counter; use stacks::net::db::LocalPeer; use stacks::net::p2p::NetworkHandle; @@ -1139,7 +1139,7 @@ impl RelayerThread { key_block_ptr: u32::try_from(key.block_height) .expect("FATAL: burn block height exceeded u32"), key_vtxindex: u16::try_from(key.op_vtxindex).expect("FATAL: vtxindex exceeded u16"), - memo: vec![STACKS_EPOCH_3_2_MARKER], + memo: vec![STACKS_EPOCH_3_1_MARKER], new_seed: VRFSeed::from_proof(&tip_vrf_proof), parent_block_ptr: u32::try_from(commit_parent_block_burn_height) .expect("FATAL: burn block height exceeded u32"), diff --git a/testnet/stacks-node/src/tests/nakamoto_integrations.rs b/testnet/stacks-node/src/tests/nakamoto_integrations.rs index 376ebdd19b..53489a03a7 100644 --- a/testnet/stacks-node/src/tests/nakamoto_integrations.rs +++ b/testnet/stacks-node/src/tests/nakamoto_integrations.rs @@ -12689,6 +12689,11 @@ fn test_sip_031_activation() { return; } + // skip the test til we move to epoch 3.2 + if StacksEpochId::latest() != StacksEpochId::Epoch32 { + return; + } + let (mut naka_conf, _miner_account) = naka_neon_integration_conf(None); naka_conf.node.pox_sync_sample_secs = 180; naka_conf.burnchain.max_rbf = 10_000_000;