diff --git a/CHANGELOG.md b/CHANGELOG.md index f85ed6526b..9007e510ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to the versioning scheme outlined in the [README.md](RE ### Added +- Set the epoch to 3.1 in the Clarity DB upon activation. + ### Changed ## [3.1.0.0.1] diff --git a/stackslib/src/chainstate/nakamoto/mod.rs b/stackslib/src/chainstate/nakamoto/mod.rs index 35f6e5d1e1..adf9dddc0e 100644 --- a/stackslib/src/chainstate/nakamoto/mod.rs +++ b/stackslib/src/chainstate/nakamoto/mod.rs @@ -3937,7 +3937,11 @@ impl NakamotoChainState { // is this stacks block the first of a new epoch? let (applied_epoch_transition, mut tx_receipts) = - StacksChainState::process_epoch_transition(&mut clarity_tx, burn_header_height)?; + StacksChainState::process_epoch_transition( + &mut clarity_tx, + sortition_dbconn.as_burn_state_db(), + burn_header_height, + )?; debug!( "Setup block: Processed epoch transition"; diff --git a/stackslib/src/chainstate/stacks/db/blocks.rs b/stackslib/src/chainstate/stacks/db/blocks.rs index 233a9d5978..5f6b236973 100644 --- a/stackslib/src/chainstate/stacks/db/blocks.rs +++ b/stackslib/src/chainstate/stacks/db/blocks.rs @@ -4044,6 +4044,7 @@ impl StacksChainState { /// Return (applied?, receipts) pub fn process_epoch_transition( clarity_tx: &mut ClarityTx, + burn_dbconn: &dyn BurnStateDB, chain_tip_burn_header_height: u32, ) -> Result<(bool, Vec), Error> { // is this stacks block the first of a new epoch? @@ -4104,14 +4105,29 @@ impl StacksChainState { current_epoch = StacksEpochId::Epoch30; } StacksEpochId::Epoch30 => { - // no special initialization is needed, since only the coinbase emission - // schedule is changing. + receipts.append(&mut clarity_tx.block.initialize_epoch_3_1()?); current_epoch = StacksEpochId::Epoch31; } StacksEpochId::Epoch31 => { panic!("No defined transition from Epoch31 forward") } } + + if current_epoch > StacksEpochId::Epoch2_05 { + // clarity tx should now have the current epoch + assert_eq!( + clarity_tx.block.get_epoch(), + current_epoch, + "FATAL: clarity_tx does not have the current epoch" + ); + + // clarity DB should now have the current epoch + assert_eq!( + clarity_tx.block.get_clarity_db_epoch_version(burn_dbconn)?, + current_epoch, + "FATAL: clarity DB does not report the current epoch" + ); + } } } @@ -5198,7 +5214,11 @@ impl StacksChainState { // is this stacks block the first of a new epoch? let (applied_epoch_transition, mut tx_receipts) = - StacksChainState::process_epoch_transition(&mut clarity_tx, burn_tip_height)?; + StacksChainState::process_epoch_transition( + &mut clarity_tx, + burn_dbconn, + burn_tip_height, + )?; debug!( "Setup block: Processed epoch transition at {}/{}", diff --git a/stackslib/src/clarity_vm/clarity.rs b/stackslib/src/clarity_vm/clarity.rs index a5497cea24..ee8795cadd 100644 --- a/stackslib/src/clarity_vm/clarity.rs +++ b/stackslib/src/clarity_vm/clarity.rs @@ -226,6 +226,21 @@ impl<'a, 'b> ClarityBlockConnection<'a, 'b> { None => None, } } + + /// Load the epoch ID from the clarity DB. + /// Used to sanity-check epoch transitions. + pub fn get_clarity_db_epoch_version( + &mut self, + burn_state_db: &dyn BurnStateDB, + ) -> Result { + let mut db = self.datastore.as_clarity_db(self.header_db, burn_state_db); + // NOTE: the begin/roll_back shouldn't be necessary with how this gets used in practice, + // but is put here defensively. + db.begin(); + let result = db.get_clarity_epoch_version(); + db.roll_back()?; + Ok(result?) + } } impl ClarityInstance { @@ -1524,6 +1539,32 @@ impl<'a, 'b> ClarityBlockConnection<'a, 'b> { }) } + pub fn initialize_epoch_3_1(&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::Epoch31; + self.as_transaction(|tx_conn| { + // bump the epoch in the Clarity DB + tx_conn + .with_clarity_db(|db| { + db.set_clarity_epoch_version(StacksEpochId::Epoch31)?; + Ok(()) + }) + .unwrap(); + + // require 3.1 rules henceforth in this connection as well + tx_conn.epoch = StacksEpochId::Epoch31; + }); + + debug!("Epoch 3.1 initialized"); + (old_cost_tracker, Ok(vec![])) + }) + } + pub fn start_transaction_processing<'c>(&'c mut self) -> ClarityTransactionConnection<'c, 'a> { let store = &mut self.datastore; let cost_track = &mut self.cost_track; diff --git a/stackslib/src/core/mod.rs b/stackslib/src/core/mod.rs index ba4dbf14d2..0f43c40756 100644 --- a/stackslib/src/core/mod.rs +++ b/stackslib/src/core/mod.rs @@ -496,7 +496,10 @@ pub static STACKS_EPOCH_3_0_MARKER: u8 = 0x0b; /// Stacks 3.1 epoch marker. All block-commits in 3.1 must have a memo bitfield with this value /// *or greater*. -pub static STACKS_EPOCH_3_1_MARKER: u8 = 0x0c; +/// NOTE: it has to be 0x0d because a prior release of 3.1 with 0x0c before activation had a +/// consensus bug. This forces miners with this buggy release off the network if they are still +/// running it prior to 3.1 activation. +pub static STACKS_EPOCH_3_1_MARKER: u8 = 0x0d; #[test] fn test_ord_for_stacks_epoch() {