Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
1eb48de
Add support for pre naka blocks
jacinta-stacks Oct 27, 2025
59c48ea
Merge branch 'develop' of https://github.com/stacks-network/stacks-co…
jacinta-stacks Oct 27, 2025
0d2e5fa
Set lower block limits
jacinta-stacks Oct 27, 2025
9f8c670
Merge branch 'develop' of https://github.com/stacks-network/stacks-co…
jacinta-stacks Oct 28, 2025
ed30abc
Generate Epoch List on the Fly
jacinta-stacks Oct 28, 2025
eafad79
Fix Clarity versions per Epoch and update contract_call_consensus_tes…
jacinta-stacks Oct 28, 2025
3650058
Merge branch 'develop' of https://github.com/stacks-network/stacks-co…
jacinta-stacks Oct 28, 2025
7500516
Merge branch 'develop' of https://github.com/stacks-network/stacks-co…
jacinta-stacks Oct 30, 2025
13659d1
CRC: check for epoch at deployment time to enable better clarity vers…
jacinta-stacks Oct 30, 2025
d7c728a
CRC: rename some functions and improve their scope
jacinta-stacks Oct 30, 2025
84306cd
CRC: remove coinbase txs from epoch receipt for pre nakamoto tenures
jacinta-stacks Oct 30, 2025
c37946e
CRC: fix duplicate calls and cleanup ContractConsensusTest struct
jacinta-stacks Nov 1, 2025
76edefd
Merge branch 'develop' of https://github.com/stacks-network/stacks-co…
jacinta-stacks Nov 1, 2025
74e94fc
Merge branch 'develop' of https://github.com/stacks-network/stacks-co…
jacinta-stacks Nov 3, 2025
14e13e2
Automatically create StacksEpochId::ALL from varients to prevent manu…
jacinta-stacks Nov 3, 2025
0e71860
Move functions around for easier understanding
jacinta-stacks Nov 3, 2025
6069c36
Split ConsensusTest's chain logic out to underlying ConsensusChain an…
jacinta-stacks Nov 3, 2025
0a44f6c
Create network_epoch helper function
jacinta-stacks Nov 3, 2025
779315b
Fmt
jacinta-stacks Nov 3, 2025
a7c544d
CRC: use the prepare phase start to determine if Epoch 2.5 or Epoch 3…
jacinta-stacks Nov 3, 2025
3ea9d35
CRC: use saturating sub in load_nakamoto_reward_set to ensure 0th cyc…
jacinta-stacks Nov 3, 2025
381398d
Move asserts about nakamoto boot state to a helper function that can …
jacinta-stacks Nov 3, 2025
e4e5ab6
Cannot use prepare phase length to determine reward set calculation
jacinta-stacks Nov 3, 2025
7fec308
CRC: cleanup comments and move epoch assertions into build_nakamoto_c…
jacinta-stacks Nov 4, 2025
6da8514
Cleanup assert_valid_epoch_3_0_activation and rename to validate_naka…
jacinta-stacks Nov 4, 2025
70727c6
Fix tests that have reward cycle starting on boundary
jacinta-stacks Nov 5, 2025
b299a8c
Merge branch 'develop' of https://github.com/stacks-network/stacks-co…
jacinta-stacks Nov 5, 2025
52fc4c4
CRC: use the prepare phase start to determine if Epoch 2.5 or Epoch 3…
jacinta-stacks Nov 5, 2025
1ff2cd8
Do not check epochs in new_with_observer
jacinta-stacks Nov 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 0 additions & 32 deletions stackslib/src/chainstate/tests/consensus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -399,38 +399,6 @@ impl ConsensusChain<'_> {
});
current_height = end_height;
}
// Validate Epoch 2.5 and 3.0 constraints
if let Some(epoch_3_0) = epochs.iter().find(|e| e.epoch_id == StacksEpochId::Epoch30) {
let epoch_2_5 = epochs
.iter()
.find(|e| e.epoch_id == StacksEpochId::Epoch25)
.expect("Epoch 2.5 not found");
let epoch_2_5_start = epoch_2_5.start_height;
let epoch_3_0_start = epoch_3_0.start_height;
let epoch_2_5_end = epoch_2_5.end_height;

let epoch_2_5_reward_cycle = epoch_2_5_start / reward_cycle_length;
let epoch_3_0_reward_cycle = epoch_3_0_start / reward_cycle_length;
let prior_cycle = epoch_3_0_reward_cycle.saturating_sub(1);
let epoch_3_0_prepare_phase =
prior_cycle * reward_cycle_length + (reward_cycle_length - prepare_length);
assert!(
epoch_2_5_start < epoch_3_0_prepare_phase,
"Epoch 2.5 must start before the prepare phase of the cycle prior to Epoch 3.0. (Epoch 2.5 activation height: {epoch_2_5_start}. Epoch 3.0 prepare phase start: {epoch_3_0_prepare_phase})"
);
assert_eq!(
epoch_2_5_end, epoch_3_0.start_height,
"Epoch 2.5 end must equal Epoch 3.0 start (epoch_2_5_end: {epoch_2_5_end}, epoch_3_0_start: {epoch_3_0_start})"
);
assert_ne!(
epoch_2_5_reward_cycle, epoch_3_0_reward_cycle,
"Epoch 2.5 and Epoch 3.0 must not be in the same reward cycle (epoch_2_5_reward_cycle: {epoch_2_5_reward_cycle}, epoch_3_0_reward_cycle: {epoch_3_0_reward_cycle})"
);
assert!(
!is_reward_cycle_boundary(epoch_3_0_start, reward_cycle_length),
"Epoch 3.0 must not start at a reward cycle boundary (epoch_3_0_start: {epoch_3_0_start})"
);
}
// Validate test vector block counts
for (epoch_id, num_blocks) in num_blocks_per_epoch {
let epoch = epochs
Expand Down
10 changes: 10 additions & 0 deletions stackslib/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,16 @@ impl Config {
"FATAL: v1 unlock height is at a reward cycle boundary\nburnchain: {burnchain:?}"
);
}
if epochs
.iter()
.any(|epoch| epoch.epoch_id == StacksEpochId::Epoch30)
{
StacksEpoch::assert_valid_epoch_3_0_activation(
epochs,
burnchain.pox_constants.reward_cycle_length as u64,
burnchain.pox_constants.prepare_length as u64,
);
}
}

// TODO: add tests from mutation testing results #4866
Expand Down
48 changes: 48 additions & 0 deletions stackslib/src/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -883,6 +883,12 @@ pub trait StacksEpochExtension {
bitcoin_network: BitcoinNetworkType,
configured_epochs: Option<&EpochList>,
) -> EpochList;
/// Assert that the epochs list will satisfy the Epoch 3.0 activation requirements given the reward cycle length and prepare phase length
fn assert_valid_epoch_3_0_activation(
epochs: &[StacksEpoch],
reward_cycle_length: u64,
prepare_length: u64,
);
}

impl StacksEpochExtension for StacksEpoch {
Expand Down Expand Up @@ -2347,6 +2353,48 @@ impl StacksEpochExtension for StacksEpoch {
}

assert_eq!(epoch_end_height, STACKS_EPOCH_MAX);

EpochList::new(&epochs)
}

fn assert_valid_epoch_3_0_activation(
epochs: &[StacksEpoch],
reward_cycle_length: u64,
prepare_length: u64,
) {
// Validate Epoch 2.5 and 3.0 constraints
let epoch_3_0 = epochs
.iter()
.find(|e| e.epoch_id == StacksEpochId::Epoch30)
.expect("Cannot activate Epoch 3.0 without specifying its activation height");
let epoch_2_5 = epochs
.iter()
.find(|e| e.epoch_id == StacksEpochId::Epoch25)
.expect("Epoch 2.5 not found");
let epoch_2_5_start = epoch_2_5.start_height;
let epoch_3_0_start = epoch_3_0.start_height;
let epoch_2_5_end = epoch_2_5.end_height;

let epoch_2_5_reward_cycle = epoch_2_5_start / reward_cycle_length;
let epoch_3_0_reward_cycle = epoch_3_0_start / reward_cycle_length;
let prior_cycle = epoch_3_0_reward_cycle.saturating_sub(1);
let epoch_3_0_prepare_phase =
prior_cycle * reward_cycle_length + (reward_cycle_length - prepare_length);
assert!(
epoch_2_5_start < epoch_3_0_prepare_phase,
"Epoch 2.5 must start before the prepare phase of the cycle prior to Epoch 3.0. (Epoch 2.5 activation height: {epoch_2_5_start}. Epoch 3.0 prepare phase start: {epoch_3_0_prepare_phase})"
);
assert_eq!(
epoch_2_5_end, epoch_3_0.start_height,
"Epoch 2.5 end must equal Epoch 3.0 start (epoch_2_5_end: {epoch_2_5_end}, epoch_3_0_start: {epoch_3_0_start})"
);
assert_ne!(
epoch_2_5_reward_cycle, epoch_3_0_reward_cycle,
"Epoch 2.5 and Epoch 3.0 must not be in the same reward cycle (epoch_2_5_reward_cycle: {epoch_2_5_reward_cycle}, epoch_3_0_reward_cycle: {epoch_3_0_reward_cycle})"
);
assert!(
epoch_3_0_start % reward_cycle_length > 1,
"Epoch 3.0 must not start at a reward cycle boundary (epoch_3_0_start: {epoch_3_0_start})"
);
}
}
14 changes: 14 additions & 0 deletions stackslib/src/net/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,13 @@ impl NakamotoBootPlan {
if let Some(current_block) = current_block {
chainstate_config.current_block = current_block;
}
if let Some(epochs) = self.epochs.as_ref() {
StacksEpoch::assert_valid_epoch_3_0_activation(
epochs,
self.pox_constants.reward_cycle_length as u64,
self.pox_constants.prepare_length as u64,
);
}
let mut chain = TestChainstate::new_with_observer(chainstate_config, observer);
chain.mine_malleablized_blocks = self.malleablized_blocks;
chain
Expand All @@ -449,6 +456,13 @@ impl NakamotoBootPlan {
self,
observer: Option<&TestEventObserver>,
) -> (TestPeer<'_>, Vec<TestPeer<'_>>) {
if let Some(epochs) = self.epochs.as_ref() {
StacksEpoch::assert_valid_epoch_3_0_activation(
epochs,
self.pox_constants.reward_cycle_length as u64,
self.pox_constants.prepare_length as u64,
);
}
let mut peer_config = TestPeerConfig::new(&self.test_name, 0, 0);
peer_config.chain_config = self.build_nakamoto_chainstate_config();
peer_config.private_key = self.private_key.clone();
Expand Down