Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 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
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
54 changes: 43 additions & 11 deletions stacks-common/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,12 @@ use crate::address::{
C32_ADDRESS_VERSION_MAINNET_MULTISIG, C32_ADDRESS_VERSION_MAINNET_SINGLESIG,
C32_ADDRESS_VERSION_TESTNET_MULTISIG, C32_ADDRESS_VERSION_TESTNET_SINGLESIG,
};
use crate::consts::MICROSTACKS_PER_STACKS;
use crate::consts::{
MICROSTACKS_PER_STACKS, 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_EPOCH_3_2, PEER_VERSION_EPOCH_3_3,
};
use crate::types::chainstate::{StacksAddress, StacksPublicKey};
use crate::util::hash::Hash160;
use crate::util::secp256k1::{MessageSignature, Secp256k1PublicKey};
Expand Down Expand Up @@ -95,9 +100,23 @@ pub const MINING_COMMITMENT_WINDOW: u8 = 6;
// Only relevant for Nakamoto (epoch 3.x)
pub const MINING_COMMITMENT_FREQUENCY_NAKAMOTO: u8 = 3;

#[repr(u32)]
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Hash, Copy, Serialize, Deserialize)]
pub enum StacksEpochId {
macro_rules! define_stacks_epochs {
($($variant:ident = $value:expr),* $(,)?) => {
#[repr(u32)]
#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub enum StacksEpochId {
$($variant = $value),*
}

impl StacksEpochId {
pub const ALL: &'static [StacksEpochId] = &[
$(StacksEpochId::$variant),*
];
}
};
}

define_stacks_epochs! {
Epoch10 = 0x01000,
Epoch20 = 0x02000,
Epoch2_05 = 0x02005,
Expand All @@ -112,6 +131,26 @@ pub enum StacksEpochId {
Epoch33 = 0x03003,
}

impl StacksEpochId {
/// Return the network epoch associated with the StacksEpochId
pub fn network_epoch(epoch: StacksEpochId) -> u8 {
match epoch {
StacksEpochId::Epoch10 => PEER_VERSION_EPOCH_1_0,
StacksEpochId::Epoch20 => PEER_VERSION_EPOCH_2_0,
StacksEpochId::Epoch2_05 => PEER_VERSION_EPOCH_2_05,
StacksEpochId::Epoch21 => PEER_VERSION_EPOCH_2_1,
StacksEpochId::Epoch22 => PEER_VERSION_EPOCH_2_2,
StacksEpochId::Epoch23 => PEER_VERSION_EPOCH_2_3,
StacksEpochId::Epoch24 => PEER_VERSION_EPOCH_2_4,
StacksEpochId::Epoch25 => PEER_VERSION_EPOCH_2_5,
StacksEpochId::Epoch30 => PEER_VERSION_EPOCH_3_0,
StacksEpochId::Epoch31 => PEER_VERSION_EPOCH_3_1,
StacksEpochId::Epoch32 => PEER_VERSION_EPOCH_3_2,
StacksEpochId::Epoch33 => PEER_VERSION_EPOCH_3_3,
}
}
}

#[derive(Debug)]
pub enum MempoolCollectionBehavior {
ByStacksHeight,
Expand Down Expand Up @@ -447,13 +486,6 @@ impl StacksEpochId {
StacksEpochId::Epoch33
}

pub const ALL_GTE_30: &'static [StacksEpochId] = &[
StacksEpochId::Epoch30,
StacksEpochId::Epoch31,
StacksEpochId::Epoch32,
StacksEpochId::Epoch33,
];

/// In this epoch, how should the mempool perform garbage collection?
pub fn mempool_garbage_behavior(&self) -> MempoolCollectionBehavior {
match self {
Expand Down
10 changes: 10 additions & 0 deletions stackslib/src/chainstate/coordinator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1732,6 +1732,16 @@ impl<
Ok(None)
}

/// A helper function for exposing the private process_new_pox_anchor_test function
#[cfg(test)]
pub fn process_new_pox_anchor_test(
&mut self,
block_id: BlockHeaderHash,
already_processed_burn_blocks: &mut HashSet<BurnchainHeaderHash>,
) -> Result<Option<BlockHeaderHash>, Error> {
self.process_new_pox_anchor(block_id, already_processed_burn_blocks)
}

/// Process a new PoX anchor block, possibly resulting in the PoX history being unwound and
/// replayed through a different sequence of consensus hashes. If the new anchor block causes
/// the node to reach a prepare-phase that elects a network-affirmed anchor block that we don't
Expand Down
31 changes: 8 additions & 23 deletions stackslib/src/chainstate/nakamoto/coordinator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -363,36 +363,21 @@ pub fn load_nakamoto_reward_set<U: RewardSetProvider>(
provider: &U,
) -> Result<Option<(RewardCycleInfo, StacksHeaderInfo)>, Error> {
let cycle_start_height = burnchain.nakamoto_first_block_of_cycle(reward_cycle);

let epoch_at_height = SortitionDB::get_stacks_epoch(sort_db.conn(), cycle_start_height)?
.unwrap_or_else(|| {
panic!(
"FATAL: no epoch defined for burn height {}",
cycle_start_height
)
});

// Find the first Stacks block in this reward cycle's preceding prepare phase.
// This block will have invoked `.signers.stackerdb-set-signer-slots()` with the reward set.
// Note that we may not have processed it yet. But, if we do find it, then it's
// unique (and since Nakamoto Stacks blocks are processed in order, the anchor block
// cannot change later).
let first_epoch30_reward_cycle = burnchain
.block_height_to_reward_cycle(epoch_at_height.start_height)
.expect("FATAL: no reward cycle for epoch 3.0 start height");

if !epoch_at_height
.epoch_id
.uses_nakamoto_reward_set(reward_cycle, first_epoch30_reward_cycle)
{
let prepare_phase_start_height =
cycle_start_height.saturating_sub(u64::from(burnchain.pox_constants.prepare_length));
let epoch_at_height =
SortitionDB::get_stacks_epoch(sort_db.conn(), prepare_phase_start_height)?.unwrap_or_else(
|| panic!("FATAL: no epoch defined for burn height {prepare_phase_start_height}"),
);
if epoch_at_height.epoch_id < StacksEpochId::Epoch30 {
// in epoch 2.5, and in the first reward cycle of epoch 3.0, the reward set can *only* be found in the sortition DB.
// The nakamoto chain-processing rules aren't active yet, so we can't look for the reward
// cycle info in the nakamoto chain state.
let Some(prepare_end_sortition_id) =
get_ancestor_sort_id(&sort_db.index_conn(), cycle_start_height, sortition_tip)?
else {
// reward cycle is too far in the future
warn!("Requested reward cycle start ancestor sortition ID for cycle {} prepare-end height {}, but tip is {}", reward_cycle, cycle_start_height, sortition_tip);
warn!("Requested reward cycle start ancestor sortition ID for cycle {reward_cycle} prepare-end height {cycle_start_height}, but tip is {sortition_tip}");
return Ok(None);
};

Expand Down
12 changes: 6 additions & 6 deletions stackslib/src/chainstate/nakamoto/coordinator/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -942,7 +942,7 @@ fn block_descendant() {
pox_constants.v2_unlock_height = 21;
pox_constants.pox_3_activation_height = 26;
pox_constants.v3_unlock_height = 27;
pox_constants.pox_4_activation_height = 28;
pox_constants.pox_4_activation_height = 33;

let mut boot_plan = NakamotoBootPlan::new(function_name!())
.with_test_stackers(test_stackers)
Expand Down Expand Up @@ -1031,7 +1031,7 @@ fn block_info_tests(use_primary_testnet: bool) {
pox_constants.v2_unlock_height = 21;
pox_constants.pox_3_activation_height = 26;
pox_constants.v3_unlock_height = 27;
pox_constants.pox_4_activation_height = 28;
pox_constants.pox_4_activation_height = 33;

let chain_id = if use_primary_testnet {
CHAIN_ID_TESTNET
Expand Down Expand Up @@ -1466,7 +1466,7 @@ fn pox_treatment() {
pox_constants.v2_unlock_height = 21;
pox_constants.pox_3_activation_height = 26;
pox_constants.v3_unlock_height = 27;
pox_constants.pox_4_activation_height = 28;
pox_constants.pox_4_activation_height = 33;

let mut boot_plan = NakamotoBootPlan::new(function_name!())
.with_test_stackers(test_stackers.clone())
Expand Down Expand Up @@ -1719,7 +1719,7 @@ fn transactions_indexing() {
pox_constants.v2_unlock_height = 21;
pox_constants.pox_3_activation_height = 26;
pox_constants.v3_unlock_height = 27;
pox_constants.pox_4_activation_height = 28;
pox_constants.pox_4_activation_height = 33;

let mut boot_plan = NakamotoBootPlan::new(function_name!())
.with_test_stackers(test_stackers.clone())
Expand Down Expand Up @@ -1784,7 +1784,7 @@ fn transactions_not_indexing() {
pox_constants.v2_unlock_height = 21;
pox_constants.pox_3_activation_height = 26;
pox_constants.v3_unlock_height = 27;
pox_constants.pox_4_activation_height = 28;
pox_constants.pox_4_activation_height = 33;

let mut boot_plan = NakamotoBootPlan::new(function_name!())
.with_test_stackers(test_stackers.clone())
Expand Down Expand Up @@ -3897,7 +3897,7 @@ fn process_next_nakamoto_block_deadlock() {
pox_constants.v2_unlock_height = 21;
pox_constants.pox_3_activation_height = 26;
pox_constants.v3_unlock_height = 27;
pox_constants.pox_4_activation_height = 28;
pox_constants.pox_4_activation_height = 33;

let mut boot_plan = NakamotoBootPlan::new(function_name!())
.with_test_stackers(test_stackers)
Expand Down
69 changes: 69 additions & 0 deletions stackslib/src/chainstate/nakamoto/tests/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1064,6 +1064,75 @@ impl TestStacksNode {
Ok((block, size, cost))
}

/// Insert a staging pre-Nakamoto block and microblocks
/// then process them as the next ready block
/// NOTE: Will panic if called with unprocessed staging
/// blocks already in the queue.
pub fn process_pre_nakamoto_next_ready_block<'a>(
stacks_node: &mut TestStacksNode,
sortdb: &mut SortitionDB,
miner: &mut TestMiner,
tenure_id_consensus_hash: &ConsensusHash,
coord: &mut ChainsCoordinator<
'a,
TestEventObserver,
(),
OnChainRewardSetProvider<'a, TestEventObserver>,
(),
(),
BitcoinIndexer,
>,
block: &StacksBlock,
microblocks: &[StacksMicroblock],
) -> Result<Option<StacksEpochReceipt>, ChainstateError> {
// First append the block to the staging blocks
{
let ic = sortdb.index_conn();
let tip = SortitionDB::get_canonical_burn_chain_tip(&ic).unwrap();
stacks_node
.chainstate
.preprocess_stacks_epoch(&ic, &tip, block, microblocks)
.unwrap();
}

let canonical_sortition_tip = coord.canonical_sortition_tip.clone().expect(
"FAIL: processing a new Stacks block, but don't have a canonical sortition tip",
);
let mut sort_tx = sortdb.tx_begin_at_tip();
let res = stacks_node
.chainstate
.process_next_staging_block(&mut sort_tx, coord.dispatcher)
.map(|(epoch_receipt, _)| epoch_receipt)?;
sort_tx.commit()?;
if let Some(block_receipt) = res.as_ref() {
let in_sortition_set = coord
.sortition_db
.is_stacks_block_in_sortition_set(
&canonical_sortition_tip,
&block_receipt.header.anchored_header.block_hash(),
)
.unwrap();
if in_sortition_set {
let block_hash = block_receipt.header.anchored_header.block_hash();
// Was this block sufficiently confirmed by the prepare phase that it was a PoX
// anchor block? And if we're in epoch 2.1, does it match the heaviest-confirmed
// block-commit in the burnchain DB, and is it affirmed by the majority of the
// network?
if let Some(pox_anchor) = coord
.sortition_db
.is_stacks_block_pox_anchor(&block_hash, &canonical_sortition_tip)
.unwrap()
{
debug!("Discovered PoX anchor block {block_hash} off of canonical sortition tip {canonical_sortition_tip}");
coord
.process_new_pox_anchor_test(pox_anchor, &mut HashSet::new())
.unwrap();
}
}
}
Ok(res)
}

/// Insert a staging Nakamoto block as a pushed block and
/// then process it as the next ready block
/// NOTE: Will panic if called with unprocessed staging
Expand Down
Loading
Loading