• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

stacks-network / stacks-core / 26829844658-1

02 Jun 2026 03:24PM UTC coverage: 86.038% (+0.08%) from 85.959%
26829844658-1

Pull #7249

github

06b7ec
web-flow
Merge e017b11f4 into b945664c8
Pull Request #7249: feat: seamless rollovers between bond and stx-only staking positions

382 of 407 new or added lines in 3 files covered. (93.86%)

6474 existing lines in 95 files now uncovered.

194813 of 226426 relevant lines covered (86.04%)

18725269.97 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.44
/stackslib/src/core/mod.rs
1
// Copyright (C) 2013-2020 Blockstack PBC, a public benefit corporation
2
// Copyright (C) 2020 Stacks Open Internet Foundation
3
//
4
// This program is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// This program is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

17
#[cfg(test)]
18
use std::cmp::Ordering;
19
use std::collections::HashSet;
20
#[cfg(test)]
21
use std::str::FromStr;
22

23
use clarity::vm::costs::ExecutionCost;
24
use lazy_static::lazy_static;
25
pub use stacks_common::consts::MICROSTACKS_PER_STACKS;
26
use stacks_common::types::chainstate::{BlockHeaderHash, StacksBlockId};
27
pub use stacks_common::types::StacksEpochId;
28
use stacks_common::types::{EpochList as GenericEpochList, StacksEpoch as GenericStacksEpoch};
29
#[cfg(test)]
30
use stacks_common::versions::STACKS_NODE_VERSION;
31

32
pub use self::mempool::MemPoolDB;
33
use crate::burnchains::bitcoin::indexer::get_bitcoin_stacks_epochs;
34
use crate::burnchains::bitcoin::BitcoinNetworkType;
35
use crate::burnchains::Burnchain;
36
use crate::chainstate::burn::ConsensusHash;
37
pub mod mempool;
38
pub mod nonce_cache;
39

40
#[cfg(any(test, feature = "testing"))]
41
pub mod test_util;
42
#[cfg(test)]
43
pub mod tests;
44

45
pub type StacksEpoch = GenericStacksEpoch<ExecutionCost>;
46
pub type EpochList = GenericEpochList<ExecutionCost>;
47

48
// fork set identifier -- to be mixed with the consensus hash (encodes the version)
49
pub const SYSTEM_FORK_SET_VERSION: [u8; 4] = [23u8, 0u8, 0u8, 0u8];
50

51
// chain id
52
pub use stacks_common::consts::{
53
    CHAIN_ID_MAINNET, CHAIN_ID_TESTNET, MINING_COMMITMENT_WINDOW, NETWORK_ID_MAINNET,
54
    NETWORK_ID_TESTNET, PEER_NETWORK_EPOCH, PEER_VERSION_EPOCH_1_0, PEER_VERSION_EPOCH_2_0,
55
    PEER_VERSION_EPOCH_2_05, PEER_VERSION_EPOCH_2_1, PEER_VERSION_EPOCH_2_2,
56
    PEER_VERSION_EPOCH_2_3, PEER_VERSION_EPOCH_2_4, PEER_VERSION_EPOCH_2_5, PEER_VERSION_EPOCH_3_0,
57
    PEER_VERSION_EPOCH_3_1, PEER_VERSION_EPOCH_3_2, PEER_VERSION_EPOCH_3_3, PEER_VERSION_EPOCH_3_4,
58
    PEER_VERSION_EPOCH_4_0, PEER_VERSION_MAINNET, PEER_VERSION_MAINNET_MAJOR, PEER_VERSION_TESTNET,
59
    PEER_VERSION_TESTNET_MAJOR, STACKS_EPOCH_MAX,
60
};
61

62
// default port
63
pub const NETWORK_P2P_PORT: u16 = 6265;
64

65
// Number of previous burnchain blocks to search to find burnchain-hosted Stacks operations
66
pub const BURNCHAIN_TX_SEARCH_WINDOW: u8 = 6;
67

68
// This controls a miner heuristic for dropping a transaction from repeated consideration
69
//  in the mempool. If the transaction caused the block limit to be reached when the block
70
//  was previously `TX_BLOCK_LIMIT_PROPORTION_HEURISTIC`% full, the transaction will be dropped
71
//  from the mempool. 20% is chosen as a heuristic here to allow for large transactions to be
72
//  attempted, but if they cannot be included in an otherwise mostly empty block, not to consider
73
//  them again.
74
pub const TX_BLOCK_LIMIT_PROPORTION_HEURISTIC: u64 = 20;
75

76
pub const GENESIS_EPOCH: StacksEpochId = StacksEpochId::Epoch20;
77

78
/// The x-coordinate public key with no known discrete logarithm.
79
///
80
/// # Notes
81
///
82
/// This particular X-coordinate was discussed in the original taproot BIP
83
/// on spending rules BIP-0341[1]. Specifically, the X-coordinate is formed
84
/// by taking the hash of the standard uncompressed encoding of the
85
/// secp256k1 base point G as the X-coordinate. In that BIP the authors
86
/// wrote the X-coordinate that is reproduced below.
87
///
88
/// [1]: https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#constructing-and-spending-taproot-outputs
89
#[rustfmt::skip]
90
pub const NUMS_X_COORDINATE: [u8; 32] = [
91
    0x50, 0x92, 0x9b, 0x74, 0xc1, 0xa0, 0x49, 0x54,
92
    0xb7, 0x8b, 0x4b, 0x60, 0x35, 0xe9, 0x7a, 0x5e,
93
    0x07, 0x8a, 0x5a, 0x0f, 0x28, 0xec, 0x96, 0xd5,
94
    0x47, 0xbf, 0xee, 0x9a, 0xce, 0x80, 0x3a, 0xc0,
95
];
96

97
/// Maximum BTC fee, in satoshis, encoded into the sBTC deposit script's
98
/// `<deposit-data>` payload. Scaffolding constant: chosen high enough that
99
/// real-world bitcoin fees never exceed it. Will be replaced by a
100
/// protocol-determined value before mainnet activation.
101
pub const POX_5_SBTC_DEPOSIT_MAX_FEE_SATS: u64 = 80_000;
102

103
/// The number of blocks which will share the block bonus
104
///   from burn blocks that occurred without a sortition.
105
///   (See: https://forum.stacks.org/t/pox-consensus-and-stx-future-supply)
106
#[cfg(test)]
107
pub const INITIAL_MINING_BONUS_WINDOW: u16 = 10;
108
#[cfg(not(test))]
109
pub const INITIAL_MINING_BONUS_WINDOW: u16 = 10_000;
110

111
pub const STACKS_2_0_LAST_BLOCK_TO_PROCESS: u64 = 700_000;
112
pub const MAINNET_2_0_GENESIS_ROOT_HASH: &str =
113
    "9653c92b1ad726e2dc17862a3786f7438ab9239c16dd8e7aaba8b0b5c34b52af";
114

115
/// This is the "dummy" parent to the actual first burnchain block that we process.
116
pub const FIRST_BURNCHAIN_CONSENSUS_HASH: ConsensusHash = ConsensusHash([0u8; 20]);
117

118
// TODO: TO BE SET BY STACKS_V1_MINER_THRESHOLD
119
pub const BITCOIN_MAINNET_FIRST_BLOCK_HEIGHT: u64 = 666050;
120
pub const BITCOIN_MAINNET_FIRST_BLOCK_TIMESTAMP: u32 = 1610643248;
121
pub const BITCOIN_MAINNET_FIRST_BLOCK_HASH: &str =
122
    "0000000000000000000ab248c8e35c574514d052a83dbc12669e19bc43df486e";
123
pub const BITCOIN_MAINNET_INITIAL_REWARD_START_BLOCK: u64 = 651389;
124
pub const BITCOIN_MAINNET_STACKS_2_05_BURN_HEIGHT: u64 = 713_000;
125
pub const BITCOIN_MAINNET_STACKS_21_BURN_HEIGHT: u64 = 781_551;
126
/// This is Epoch-2.2 activation height proposed in SIP-022
127
pub const BITCOIN_MAINNET_STACKS_22_BURN_HEIGHT: u64 = 787_651;
128
/// This is Epoch-2.3 activation height proposed in SIP-023
129
pub const BITCOIN_MAINNET_STACKS_23_BURN_HEIGHT: u64 = 788_240;
130
/// This is Epoch-2.3, now Epoch-2.4, activation height proposed in SIP-024
131
pub const BITCOIN_MAINNET_STACKS_24_BURN_HEIGHT: u64 = 791_551;
132
/// This is Epoch-2.5, activation height proposed in SIP-021
133
pub const BITCOIN_MAINNET_STACKS_25_BURN_HEIGHT: u64 = 840_360;
134
/// This is Epoch-3.0, activation height proposed in SIP-021
135
pub const BITCOIN_MAINNET_STACKS_30_BURN_HEIGHT: u64 = 867_867;
136
/// This is Epoch-3.1, activation height proposed in SIP-029
137
pub const BITCOIN_MAINNET_STACKS_31_BURN_HEIGHT: u64 = 875_000;
138
/// This is Epoch-3.2, activation height proposed in SIP-031
139
pub const BITCOIN_MAINNET_STACKS_32_BURN_HEIGHT: u64 = 907_740;
140
/// This is Epoch-3.3, activation timing proposed in SIP-033
141
pub const BITCOIN_MAINNET_STACKS_33_BURN_HEIGHT: u64 = 923_222;
142
/// This is Epoch-3.4, activation timing proposed in SIP-039
143
pub const BITCOIN_MAINNET_STACKS_34_BURN_HEIGHT: u64 = 943_333;
144
/// This is Epoch-4.0, activation timing TBD. Placeholder until scheduled.
145
pub const BITCOIN_MAINNET_STACKS_40_BURN_HEIGHT: u64 = 4_000_000;
146

147
/// Bitcoin mainline testnet3 activation heights.
148
/// TODO: No longer used since testnet3 is dead, so remove.
149
pub const BITCOIN_TESTNET_FIRST_BLOCK_HEIGHT: u64 = 2000000;
150
pub const BITCOIN_TESTNET_FIRST_BLOCK_TIMESTAMP: u32 = 1622691840;
151
pub const BITCOIN_TESTNET_FIRST_BLOCK_HASH: &str =
152
    "000000000000010dd0863ec3d7a0bae17c1957ae1de9cbcdae8e77aad33e3b8c";
153
pub const BITCOIN_TESTNET_STACKS_2_05_BURN_HEIGHT: u64 = 2_104_380;
154
pub const BITCOIN_TESTNET_STACKS_21_BURN_HEIGHT: u64 = 2_422_101;
155
pub const BITCOIN_TESTNET_STACKS_22_BURN_HEIGHT: u64 = 2_431_300;
156
pub const BITCOIN_TESTNET_STACKS_23_BURN_HEIGHT: u64 = 2_431_633;
157
pub const BITCOIN_TESTNET_STACKS_24_BURN_HEIGHT: u64 = 2_432_545;
158
pub const BITCOIN_TESTNET_STACKS_25_BURN_HEIGHT: u64 = 2_583_893;
159
pub const BITCOIN_TESTNET_STACKS_30_BURN_HEIGHT: u64 = 30_000_000;
160
pub const BITCOIN_TESTNET_STACKS_31_BURN_HEIGHT: u64 = 30_000_001;
161
pub const BITCOIN_TESTNET_STACKS_32_BURN_HEIGHT: u64 = 30_000_002;
162
pub const BITCOIN_TESTNET_STACKS_33_BURN_HEIGHT: u64 = 30_000_003;
163
pub const BITCOIN_TESTNET_STACKS_34_BURN_HEIGHT: u64 = 30_000_004;
164
pub const BITCOIN_TESTNET_STACKS_40_BURN_HEIGHT: u64 = 40_000_000;
165

166
pub const BITCOIN_REGTEST_FIRST_BLOCK_HEIGHT: u64 = 0;
167
pub const BITCOIN_REGTEST_FIRST_BLOCK_TIMESTAMP: u32 = 0;
168
pub const BITCOIN_REGTEST_FIRST_BLOCK_HASH: &str =
169
    "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206";
170

171
pub const FIRST_STACKS_BLOCK_HASH: BlockHeaderHash = BlockHeaderHash([0u8; 32]);
172
pub const EMPTY_MICROBLOCK_PARENT_HASH: BlockHeaderHash = BlockHeaderHash([0u8; 32]);
173

174
lazy_static! {
175
    pub static ref FIRST_STACKS_BLOCK_ID: StacksBlockId =
176
        StacksBlockId::new(&FIRST_BURNCHAIN_CONSENSUS_HASH, &FIRST_STACKS_BLOCK_HASH);
177
}
178

179
pub const BOOT_BLOCK_HASH: BlockHeaderHash = BlockHeaderHash([0xff; 32]);
180
pub const BURNCHAIN_BOOT_CONSENSUS_HASH: ConsensusHash = ConsensusHash([0xff; 20]);
181

182
pub const POX_SUNSET_START: u64 = 100_000;
183
pub const POX_SUNSET_END: u64 = POX_SUNSET_START + 400_000;
184

185
pub const POX_PREPARE_WINDOW_LENGTH: u32 = 100;
186
pub const POX_REWARD_CYCLE_LENGTH: u32 = 2100;
187
/// The maximum amount that PoX rewards can be scaled by.
188
///  That is, if participation is very low, rewards are:
189
///      POX_MAXIMAL_SCALING x (rewards with 100% participation)
190
///  Set a 4x, this implies the lower bound of participation for scaling
191
///   is 25%
192
pub const POX_MAXIMAL_SCALING: u128 = 4;
193
/// This is the amount that PoX threshold adjustments are stepped by.
194
pub const POX_THRESHOLD_STEPS_USTX: u128 = 10_000 * (MICROSTACKS_PER_STACKS as u128);
195

196
pub const POX_MAX_NUM_CYCLES: u8 = 12;
197

198
// These values are taken from the corresponding variables in `pox-tesnet.clar`.
199
pub const POX_TESTNET_STACKING_THRESHOLD_25: u128 = 8000;
200
pub const POX_TESTNET_CYCLE_LENGTH: u128 = 1050;
201

202
pub const POX_V1_MAINNET_EARLY_UNLOCK_HEIGHT: u32 =
203
    (BITCOIN_MAINNET_STACKS_21_BURN_HEIGHT as u32) + 1;
204
pub const POX_V1_TESTNET_EARLY_UNLOCK_HEIGHT: u32 =
205
    (BITCOIN_TESTNET_STACKS_21_BURN_HEIGHT as u32) + 1;
206

207
pub const POX_V2_MAINNET_EARLY_UNLOCK_HEIGHT: u32 =
208
    (BITCOIN_MAINNET_STACKS_22_BURN_HEIGHT as u32) + 1;
209
pub const POX_V2_TESTNET_EARLY_UNLOCK_HEIGHT: u32 =
210
    (BITCOIN_TESTNET_STACKS_22_BURN_HEIGHT as u32) + 1;
211

212
pub const POX_V3_MAINNET_EARLY_UNLOCK_HEIGHT: u32 =
213
    (BITCOIN_MAINNET_STACKS_25_BURN_HEIGHT as u32) + 1;
214
pub const POX_V3_TESTNET_EARLY_UNLOCK_HEIGHT: u32 =
215
    (BITCOIN_TESTNET_STACKS_25_BURN_HEIGHT as u32) + 1;
216

217
// The threshold of weighted votes on a block to approve it in Nakamoto.
218
// This is out of 10, so 7 means "70%".
219
pub const NAKAMOTO_SIGNER_BLOCK_APPROVAL_THRESHOLD: u64 = 7;
220

221
// Stacks 1.0 did not allow smart contracts so all limits are 0.
222
pub const BLOCK_LIMIT_MAINNET_10: ExecutionCost = ExecutionCost {
223
    write_length: 0,
224
    write_count: 0,
225
    read_length: 0,
226
    read_count: 0,
227
    runtime: 0,
228
};
229

230
// Block limit in Stacks 2.0.
231
pub const BLOCK_LIMIT_MAINNET_20: ExecutionCost = ExecutionCost {
232
    write_length: 15_000_000, // roughly 15 mb
233
    write_count: 7_750,
234
    read_length: 100_000_000,
235
    read_count: 7_750,
236
    runtime: 5_000_000_000,
237
};
238

239
// Block limit in Stacks 2.05.
240
pub const BLOCK_LIMIT_MAINNET_205: ExecutionCost = ExecutionCost {
241
    write_length: 15_000_000,
242
    write_count: 15_000,
243
    read_length: 100_000_000,
244
    read_count: 15_000,
245
    runtime: 5_000_000_000,
246
};
247

248
// Block limit in Stacks 2.1
249
pub const BLOCK_LIMIT_MAINNET_21: ExecutionCost = ExecutionCost {
250
    write_length: 15_000_000,
251
    write_count: 15_000,
252
    read_length: 100_000_000,
253
    read_count: 15_000,
254
    runtime: 5_000_000_000,
255
};
256

257
// Block limit for the testnet in Stacks 2.0.
258
pub const HELIUM_BLOCK_LIMIT_20: ExecutionCost = ExecutionCost {
259
    write_length: 150_000_000,
260
    write_count: 50_000,
261
    read_length: 1_000_000_000,
262
    read_count: 50_000,
263
    // allow much more runtime in helium blocks than mainnet
264
    runtime: 100_000_000_000,
265
};
266

267
pub const FAULT_DISABLE_MICROBLOCKS_COST_CHECK: &str = "MICROBLOCKS_DISABLE_COST_CHECK";
268
pub const FAULT_DISABLE_MICROBLOCKS_BYTES_CHECK: &str = "MICROBLOCKS_DISABLE_BYTES_CHECK";
269

270
pub fn check_fault_injection(fault_name: &str) -> bool {
16,889,832✔
271
    use std::env;
272

273
    // only activates if we're testing
274
    if env::var("BITCOIND_TEST") != Ok("1".to_string()) {
16,889,832✔
UNCOV
275
        return false;
×
276
    }
16,889,832✔
277

278
    env::var(fault_name) == Ok("1".to_string())
16,889,832✔
279
}
16,889,832✔
280

281
lazy_static! {
282
    pub static ref STACKS_EPOCHS_MAINNET: EpochList = EpochList::new(&[
283
        StacksEpoch {
284
            epoch_id: StacksEpochId::Epoch10,
285
            start_height: 0,
286
            end_height: BITCOIN_MAINNET_FIRST_BLOCK_HEIGHT,
287
            block_limit: BLOCK_LIMIT_MAINNET_10,
288
            network_epoch: PEER_VERSION_EPOCH_1_0
289
        },
290
        StacksEpoch {
291
            epoch_id: StacksEpochId::Epoch20,
292
            start_height: BITCOIN_MAINNET_FIRST_BLOCK_HEIGHT,
293
            end_height: BITCOIN_MAINNET_STACKS_2_05_BURN_HEIGHT,
294
            block_limit: BLOCK_LIMIT_MAINNET_20,
295
            network_epoch: PEER_VERSION_EPOCH_2_0
296
        },
297
        StacksEpoch {
298
            epoch_id: StacksEpochId::Epoch2_05,
299
            start_height: BITCOIN_MAINNET_STACKS_2_05_BURN_HEIGHT,
300
            end_height: BITCOIN_MAINNET_STACKS_21_BURN_HEIGHT,
301
            block_limit: BLOCK_LIMIT_MAINNET_205,
302
            network_epoch: PEER_VERSION_EPOCH_2_05
303
        },
304
        StacksEpoch {
305
            epoch_id: StacksEpochId::Epoch21,
306
            start_height: BITCOIN_MAINNET_STACKS_21_BURN_HEIGHT,
307
            end_height: BITCOIN_MAINNET_STACKS_22_BURN_HEIGHT,
308
            block_limit: BLOCK_LIMIT_MAINNET_21,
309
            network_epoch: PEER_VERSION_EPOCH_2_1
310
        },
311
        StacksEpoch {
312
            epoch_id: StacksEpochId::Epoch22,
313
            start_height: BITCOIN_MAINNET_STACKS_22_BURN_HEIGHT,
314
            end_height: BITCOIN_MAINNET_STACKS_23_BURN_HEIGHT,
315
            block_limit: BLOCK_LIMIT_MAINNET_21,
316
            network_epoch: PEER_VERSION_EPOCH_2_2
317
        },
318
        StacksEpoch {
319
            epoch_id: StacksEpochId::Epoch23,
320
            start_height: BITCOIN_MAINNET_STACKS_23_BURN_HEIGHT,
321
            end_height: BITCOIN_MAINNET_STACKS_24_BURN_HEIGHT,
322
            block_limit: BLOCK_LIMIT_MAINNET_21,
323
            network_epoch: PEER_VERSION_EPOCH_2_3
324
        },
325
        StacksEpoch {
326
            epoch_id: StacksEpochId::Epoch24,
327
            start_height: BITCOIN_MAINNET_STACKS_24_BURN_HEIGHT,
328
            end_height: BITCOIN_MAINNET_STACKS_25_BURN_HEIGHT,
329
            block_limit: BLOCK_LIMIT_MAINNET_21,
330
            network_epoch: PEER_VERSION_EPOCH_2_4
331
        },
332
        StacksEpoch {
333
            epoch_id: StacksEpochId::Epoch25,
334
            start_height: BITCOIN_MAINNET_STACKS_25_BURN_HEIGHT,
335
            end_height: BITCOIN_MAINNET_STACKS_30_BURN_HEIGHT,
336
            block_limit: BLOCK_LIMIT_MAINNET_21,
337
            network_epoch: PEER_VERSION_EPOCH_2_5
338
        },
339
        StacksEpoch {
340
            epoch_id: StacksEpochId::Epoch30,
341
            start_height: BITCOIN_MAINNET_STACKS_30_BURN_HEIGHT,
342
            end_height: BITCOIN_MAINNET_STACKS_31_BURN_HEIGHT,
343
            block_limit: BLOCK_LIMIT_MAINNET_21,
344
            network_epoch: PEER_VERSION_EPOCH_3_0
345
        },
346
        StacksEpoch {
347
            epoch_id: StacksEpochId::Epoch31,
348
            start_height: BITCOIN_MAINNET_STACKS_31_BURN_HEIGHT,
349
            end_height: BITCOIN_MAINNET_STACKS_32_BURN_HEIGHT,
350
            block_limit: BLOCK_LIMIT_MAINNET_21,
351
            network_epoch: PEER_VERSION_EPOCH_3_1
352
        },
353
        StacksEpoch {
354
            epoch_id: StacksEpochId::Epoch32,
355
            start_height: BITCOIN_MAINNET_STACKS_32_BURN_HEIGHT,
356
            end_height: BITCOIN_MAINNET_STACKS_33_BURN_HEIGHT,
357
            block_limit: BLOCK_LIMIT_MAINNET_21,
358
            network_epoch: PEER_VERSION_EPOCH_3_2
359
        },
360
        StacksEpoch {
361
            epoch_id: StacksEpochId::Epoch33,
362
            start_height: BITCOIN_MAINNET_STACKS_33_BURN_HEIGHT,
363
            end_height: BITCOIN_MAINNET_STACKS_34_BURN_HEIGHT,
364
            block_limit: BLOCK_LIMIT_MAINNET_21,
365
            network_epoch: PEER_VERSION_EPOCH_3_3
366
        },
367
        StacksEpoch {
368
            epoch_id: StacksEpochId::Epoch34,
369
            start_height: BITCOIN_MAINNET_STACKS_34_BURN_HEIGHT,
370
            end_height: BITCOIN_MAINNET_STACKS_40_BURN_HEIGHT,
371
            block_limit: BLOCK_LIMIT_MAINNET_21,
372
            network_epoch: PEER_VERSION_EPOCH_3_4
373
        },
374
        StacksEpoch {
375
            epoch_id: StacksEpochId::Epoch40,
376
            start_height: BITCOIN_MAINNET_STACKS_40_BURN_HEIGHT,
377
            end_height: STACKS_EPOCH_MAX,
378
            block_limit: BLOCK_LIMIT_MAINNET_21,
379
            network_epoch: PEER_VERSION_EPOCH_4_0
380
        },
381
    ]);
382
}
383

384
lazy_static! {
385
    pub static ref STACKS_EPOCHS_TESTNET: EpochList = EpochList::new(&[
386
        StacksEpoch {
387
            epoch_id: StacksEpochId::Epoch10,
388
            start_height: 0,
389
            end_height: BITCOIN_TESTNET_FIRST_BLOCK_HEIGHT,
390
            block_limit: BLOCK_LIMIT_MAINNET_10,
391
            network_epoch: PEER_VERSION_EPOCH_1_0
392
        },
393
        StacksEpoch {
394
            epoch_id: StacksEpochId::Epoch20,
395
            start_height: BITCOIN_TESTNET_FIRST_BLOCK_HEIGHT,
396
            end_height: BITCOIN_TESTNET_STACKS_2_05_BURN_HEIGHT,
397
            block_limit: BLOCK_LIMIT_MAINNET_20,
398
            network_epoch: PEER_VERSION_EPOCH_2_0
399
        },
400
        StacksEpoch {
401
            epoch_id: StacksEpochId::Epoch2_05,
402
            start_height: BITCOIN_TESTNET_STACKS_2_05_BURN_HEIGHT,
403
            end_height: BITCOIN_TESTNET_STACKS_21_BURN_HEIGHT,
404
            block_limit: BLOCK_LIMIT_MAINNET_205,
405
            network_epoch: PEER_VERSION_EPOCH_2_05
406
        },
407
        StacksEpoch {
408
            epoch_id: StacksEpochId::Epoch21,
409
            start_height: BITCOIN_TESTNET_STACKS_21_BURN_HEIGHT,
410
            end_height: BITCOIN_TESTNET_STACKS_22_BURN_HEIGHT,
411
            block_limit: BLOCK_LIMIT_MAINNET_21,
412
            network_epoch: PEER_VERSION_EPOCH_2_1
413
        },
414
        StacksEpoch {
415
            epoch_id: StacksEpochId::Epoch22,
416
            start_height: BITCOIN_TESTNET_STACKS_22_BURN_HEIGHT,
417
            end_height: BITCOIN_TESTNET_STACKS_23_BURN_HEIGHT,
418
            block_limit: BLOCK_LIMIT_MAINNET_21,
419
            network_epoch: PEER_VERSION_EPOCH_2_2
420
        },
421
        StacksEpoch {
422
            epoch_id: StacksEpochId::Epoch23,
423
            start_height: BITCOIN_TESTNET_STACKS_23_BURN_HEIGHT,
424
            end_height: BITCOIN_TESTNET_STACKS_24_BURN_HEIGHT,
425
            block_limit: BLOCK_LIMIT_MAINNET_21,
426
            network_epoch: PEER_VERSION_EPOCH_2_3
427
        },
428
        StacksEpoch {
429
            epoch_id: StacksEpochId::Epoch24,
430
            start_height: BITCOIN_TESTNET_STACKS_24_BURN_HEIGHT,
431
            end_height: BITCOIN_TESTNET_STACKS_25_BURN_HEIGHT,
432
            block_limit: BLOCK_LIMIT_MAINNET_21,
433
            network_epoch: PEER_VERSION_EPOCH_2_4
434
        },
435
        StacksEpoch {
436
            epoch_id: StacksEpochId::Epoch25,
437
            start_height: BITCOIN_TESTNET_STACKS_25_BURN_HEIGHT,
438
            end_height: BITCOIN_TESTNET_STACKS_30_BURN_HEIGHT,
439
            block_limit: BLOCK_LIMIT_MAINNET_21,
440
            network_epoch: PEER_VERSION_EPOCH_2_5
441
        },
442
        StacksEpoch {
443
            epoch_id: StacksEpochId::Epoch30,
444
            start_height: BITCOIN_TESTNET_STACKS_30_BURN_HEIGHT,
445
            end_height: BITCOIN_TESTNET_STACKS_31_BURN_HEIGHT,
446
            block_limit: BLOCK_LIMIT_MAINNET_21,
447
            network_epoch: PEER_VERSION_EPOCH_3_0
448
        },
449
        StacksEpoch {
450
            epoch_id: StacksEpochId::Epoch31,
451
            start_height: BITCOIN_TESTNET_STACKS_31_BURN_HEIGHT,
452
            end_height: BITCOIN_TESTNET_STACKS_32_BURN_HEIGHT,
453
            block_limit: BLOCK_LIMIT_MAINNET_21,
454
            network_epoch: PEER_VERSION_EPOCH_3_1
455
        },
456
        StacksEpoch {
457
            epoch_id: StacksEpochId::Epoch32,
458
            start_height: BITCOIN_TESTNET_STACKS_32_BURN_HEIGHT,
459
            end_height: BITCOIN_TESTNET_STACKS_33_BURN_HEIGHT,
460
            block_limit: BLOCK_LIMIT_MAINNET_21,
461
            network_epoch: PEER_VERSION_EPOCH_3_2
462
        },
463
        StacksEpoch {
464
            epoch_id: StacksEpochId::Epoch33,
465
            start_height: BITCOIN_TESTNET_STACKS_33_BURN_HEIGHT,
466
            end_height: BITCOIN_TESTNET_STACKS_34_BURN_HEIGHT,
467
            block_limit: BLOCK_LIMIT_MAINNET_21,
468
            network_epoch: PEER_VERSION_EPOCH_3_3
469
        },
470
        StacksEpoch {
471
            epoch_id: StacksEpochId::Epoch34,
472
            start_height: BITCOIN_TESTNET_STACKS_34_BURN_HEIGHT,
473
            end_height: BITCOIN_TESTNET_STACKS_40_BURN_HEIGHT,
474
            block_limit: BLOCK_LIMIT_MAINNET_21,
475
            network_epoch: PEER_VERSION_EPOCH_3_4
476
        },
477
        StacksEpoch {
478
            epoch_id: StacksEpochId::Epoch40,
479
            start_height: BITCOIN_TESTNET_STACKS_40_BURN_HEIGHT,
480
            end_height: STACKS_EPOCH_MAX,
481
            block_limit: BLOCK_LIMIT_MAINNET_21,
482
            network_epoch: PEER_VERSION_EPOCH_4_0
483
        },
484
    ]);
485
}
486

487
lazy_static! {
488
    pub static ref STACKS_EPOCHS_REGTEST: EpochList = EpochList::new(&[
489
        StacksEpoch {
490
            epoch_id: StacksEpochId::Epoch10,
491
            start_height: 0,
492
            end_height: 0,
493
            block_limit: BLOCK_LIMIT_MAINNET_10,
494
            network_epoch: PEER_VERSION_EPOCH_1_0
495
        },
496
        StacksEpoch {
497
            epoch_id: StacksEpochId::Epoch20,
498
            start_height: 0,
499
            end_height: 1000,
500
            block_limit: HELIUM_BLOCK_LIMIT_20,
501
            network_epoch: PEER_VERSION_EPOCH_2_0
502
        },
503
        StacksEpoch {
504
            epoch_id: StacksEpochId::Epoch2_05,
505
            start_height: 1000,
506
            end_height: 2000,
507
            block_limit: HELIUM_BLOCK_LIMIT_20,
508
            network_epoch: PEER_VERSION_EPOCH_2_05
509
        },
510
        StacksEpoch {
511
            epoch_id: StacksEpochId::Epoch21,
512
            start_height: 2000,
513
            end_height: 3000,
514
            block_limit: HELIUM_BLOCK_LIMIT_20,
515
            network_epoch: PEER_VERSION_EPOCH_2_1
516
        },
517
        StacksEpoch {
518
            epoch_id: StacksEpochId::Epoch22,
519
            start_height: 3000,
520
            end_height: 4000,
521
            block_limit: HELIUM_BLOCK_LIMIT_20,
522
            network_epoch: PEER_VERSION_EPOCH_2_2
523
        },
524
        StacksEpoch {
525
            epoch_id: StacksEpochId::Epoch23,
526
            start_height: 4000,
527
            end_height: 5000,
528
            block_limit: HELIUM_BLOCK_LIMIT_20,
529
            network_epoch: PEER_VERSION_EPOCH_2_3
530
        },
531
        StacksEpoch {
532
            epoch_id: StacksEpochId::Epoch24,
533
            start_height: 5000,
534
            end_height: 6000,
535
            block_limit: HELIUM_BLOCK_LIMIT_20,
536
            network_epoch: PEER_VERSION_EPOCH_2_4
537
        },
538
        StacksEpoch {
539
            epoch_id: StacksEpochId::Epoch25,
540
            start_height: 6000,
541
            end_height: 7001,
542
            block_limit: BLOCK_LIMIT_MAINNET_21,
543
            network_epoch: PEER_VERSION_EPOCH_2_5
544
        },
545
        StacksEpoch {
546
            epoch_id: StacksEpochId::Epoch30,
547
            start_height: 7001,
548
            end_height: 8001,
549
            block_limit: BLOCK_LIMIT_MAINNET_21,
550
            network_epoch: PEER_VERSION_EPOCH_3_0
551
        },
552
        StacksEpoch {
553
            epoch_id: StacksEpochId::Epoch31,
554
            start_height: 8001,
555
            end_height: 9001,
556
            block_limit: BLOCK_LIMIT_MAINNET_21,
557
            network_epoch: PEER_VERSION_EPOCH_3_1
558
        },
559
        StacksEpoch {
560
            epoch_id: StacksEpochId::Epoch32,
561
            start_height: 9001,
562
            end_height: 10001,
563
            block_limit: BLOCK_LIMIT_MAINNET_21,
564
            network_epoch: PEER_VERSION_EPOCH_3_2
565
        },
566
        StacksEpoch {
567
            epoch_id: StacksEpochId::Epoch33,
568
            start_height: 10001,
569
            end_height: 11001,
570
            block_limit: BLOCK_LIMIT_MAINNET_21,
571
            network_epoch: PEER_VERSION_EPOCH_3_3
572
        },
573
        StacksEpoch {
574
            epoch_id: StacksEpochId::Epoch34,
575
            start_height: 11001,
576
            end_height: 12001,
577
            block_limit: BLOCK_LIMIT_MAINNET_21,
578
            network_epoch: PEER_VERSION_EPOCH_3_4
579
        },
580
        StacksEpoch {
581
            epoch_id: StacksEpochId::Epoch40,
582
            start_height: 12001,
583
            end_height: STACKS_EPOCH_MAX,
584
            block_limit: BLOCK_LIMIT_MAINNET_21,
585
            network_epoch: PEER_VERSION_EPOCH_4_0
586
        },
587
    ]);
588
}
589

590
/// Stacks 2.05 epoch marker.  All block-commits in 2.05 must have a memo bitfield with this value
591
/// *or greater*.
592
pub static STACKS_EPOCH_2_05_MARKER: u8 = 0x05;
593

594
/// Stacks 2.1 epoch marker.  All block-commits in 2.1 must have a memo bitfield with this value
595
/// *or greater*.
596
pub static STACKS_EPOCH_2_1_MARKER: u8 = 0x06;
597

598
/// Stacks 2.2 epoch marker.  All block-commits in 2.2 must have a memo bitfield with this value
599
/// *or greater*.
600
pub static STACKS_EPOCH_2_2_MARKER: u8 = 0x07;
601

602
/// Stacks 2.3 epoch marker.  All block-commits in 2.3 must have a memo bitfield with this value
603
/// *or greater*.
604
pub static STACKS_EPOCH_2_3_MARKER: u8 = 0x08;
605

606
/// Stacks 2.4 epoch marker.  All block-commits in 2.4 must have a memo bitfield with this value
607
/// *or greater*.
608
pub static STACKS_EPOCH_2_4_MARKER: u8 = 0x09;
609

610
/// Stacks 2.5 epoch marker.  All block-commits in 2.5 must have a memo bitfield with this value
611
/// *or greater*.
612
pub static STACKS_EPOCH_2_5_MARKER: u8 = 0x0a;
613

614
/// Stacks 3.0 epoch marker.  All block-commits in 3.0 must have a memo bitfield with this value
615
/// *or greater*.
616
pub static STACKS_EPOCH_3_0_MARKER: u8 = 0x0b;
617

618
/// Stacks 3.1 epoch marker.  All block-commits in 3.1 must have a memo bitfield with this value
619
/// *or greater*.
620
/// NOTE: it has to be 0x0d because a prior release of 3.1 with 0x0c before activation had a
621
/// consensus bug. This forces miners with this buggy release off the network if they are still
622
/// running it prior to 3.1 activation.
623
pub static STACKS_EPOCH_3_1_MARKER: u8 = 0x0d;
624

625
/// Stacks 3.2 epoch marker.  All block-commits in 3.2 must have a memo bitfield with this value
626
/// *or greater*.
627
pub static STACKS_EPOCH_3_2_MARKER: u8 = 0x0e;
628

629
/// Stacks 3.3 epoch marker.  All block-commits in 3.3 must have a memo bitfield with this value
630
/// *or greater*.
631
pub static STACKS_EPOCH_3_3_MARKER: u8 = 0x0f;
632

633
/// Stacks 3.4 epoch marker.  All block-commits in 3.4 must have a memo bitfield with this value
634
/// *or greater*.
635
pub static STACKS_EPOCH_3_4_MARKER: u8 = 0x10;
636

637
/// Stacks 4.0 epoch marker.  All block-commits in 4.0 must have a memo bitfield with this value
638
/// *or greater*.
639
pub static STACKS_EPOCH_4_0_MARKER: u8 = 0x11;
640

641
/// Latest Stacks epoch marker. Automatically uses the marker for the highest supported epoch.
642
pub static STACKS_EPOCH_LATEST_MARKER: u8 =
643
    marker_for_epoch(StacksEpochId::latest()).expect("Latest epoch should always have a marker");
644

645
pub const fn marker_for_epoch(epoch_id: StacksEpochId) -> Option<u8> {
114,017✔
646
    match epoch_id {
114,017✔
UNCOV
647
        StacksEpochId::Epoch10 => None,
×
648
        StacksEpochId::Epoch20 => None,
5,614✔
649
        StacksEpochId::Epoch2_05 => Some(STACKS_EPOCH_2_05_MARKER),
1,689✔
650
        StacksEpochId::Epoch21 => Some(STACKS_EPOCH_2_1_MARKER),
2,825✔
651
        StacksEpochId::Epoch22 => Some(STACKS_EPOCH_2_2_MARKER),
577✔
652
        StacksEpochId::Epoch23 => Some(STACKS_EPOCH_2_3_MARKER),
662✔
653
        StacksEpochId::Epoch24 => Some(STACKS_EPOCH_2_4_MARKER),
7,806✔
654
        StacksEpochId::Epoch25 => Some(STACKS_EPOCH_2_5_MARKER),
74,325✔
655
        StacksEpochId::Epoch30 => Some(STACKS_EPOCH_3_0_MARKER),
13,385✔
656
        StacksEpochId::Epoch31 => Some(STACKS_EPOCH_3_1_MARKER),
2,892✔
657
        StacksEpochId::Epoch32 => Some(STACKS_EPOCH_3_2_MARKER),
265✔
658
        StacksEpochId::Epoch33 => Some(STACKS_EPOCH_3_3_MARKER),
528✔
659
        StacksEpochId::Epoch34 => Some(STACKS_EPOCH_3_4_MARKER),
2,125✔
660
        StacksEpochId::Epoch40 => Some(STACKS_EPOCH_4_0_MARKER),
1,324✔
661
    }
662
}
114,017✔
663

664
// Pick the release epoch whose `network_epoch` must match `PEER_NETWORK_EPOCH`.
665
// `latest()` may be one-ahead in test/testing builds, so this check intentionally
666
// anchors to the release latest epoch instead.
667
fn peer_network_epoch_check_epoch_id() -> StacksEpochId {
4,410,434✔
668
    assert!(
4,410,434✔
669
        StacksEpochId::RELEASE_LATEST_EPOCH <= StacksEpochId::latest(),
4,410,434✔
670
        "FATAL: RELEASE_LATEST_EPOCH cannot be greater than latest()"
671
    );
672
    StacksEpochId::RELEASE_LATEST_EPOCH
4,410,434✔
673
}
4,410,434✔
674

675
#[test]
676
fn test_ord_for_stacks_epoch() {
1✔
677
    let epochs = &*STACKS_EPOCHS_MAINNET;
1✔
678
    assert_eq!(
1✔
679
        epochs[StacksEpochId::Epoch10].cmp(&epochs[StacksEpochId::Epoch20]),
1✔
680
        Ordering::Less
681
    );
682
    assert_eq!(
1✔
683
        epochs[StacksEpochId::Epoch20].cmp(&epochs[StacksEpochId::Epoch2_05]),
1✔
684
        Ordering::Less
685
    );
686
    assert_eq!(
1✔
687
        epochs[StacksEpochId::Epoch10].cmp(&epochs[StacksEpochId::Epoch2_05]),
1✔
688
        Ordering::Less
689
    );
690
    assert_eq!(
1✔
691
        epochs[StacksEpochId::Epoch10].cmp(&epochs[StacksEpochId::Epoch10]),
1✔
692
        Ordering::Equal
693
    );
694
    assert_eq!(
1✔
695
        epochs[StacksEpochId::Epoch20].cmp(&epochs[StacksEpochId::Epoch20]),
1✔
696
        Ordering::Equal
697
    );
698
    assert_eq!(
1✔
699
        epochs[StacksEpochId::Epoch2_05].cmp(&epochs[StacksEpochId::Epoch2_05]),
1✔
700
        Ordering::Equal
701
    );
702
    assert_eq!(
1✔
703
        epochs[StacksEpochId::Epoch21].cmp(&epochs[StacksEpochId::Epoch21]),
1✔
704
        Ordering::Equal
705
    );
706
    assert_eq!(
1✔
707
        epochs[StacksEpochId::Epoch22].cmp(&epochs[StacksEpochId::Epoch22]),
1✔
708
        Ordering::Equal
709
    );
710
    assert_eq!(
1✔
711
        epochs[StacksEpochId::Epoch2_05].cmp(&epochs[StacksEpochId::Epoch10]),
1✔
712
        Ordering::Greater
713
    );
714
    assert_eq!(
1✔
715
        epochs[StacksEpochId::Epoch2_05].cmp(&epochs[StacksEpochId::Epoch20]),
1✔
716
        Ordering::Greater
717
    );
718
    assert_eq!(
1✔
719
        epochs[StacksEpochId::Epoch20].cmp(&epochs[StacksEpochId::Epoch10]),
1✔
720
        Ordering::Greater
721
    );
722
    assert_eq!(
1✔
723
        epochs[StacksEpochId::Epoch21].cmp(&epochs[StacksEpochId::Epoch10]),
1✔
724
        Ordering::Greater
725
    );
726
    assert_eq!(
1✔
727
        epochs[StacksEpochId::Epoch21].cmp(&epochs[StacksEpochId::Epoch20]),
1✔
728
        Ordering::Greater
729
    );
730
    assert_eq!(
1✔
731
        epochs[StacksEpochId::Epoch21].cmp(&epochs[StacksEpochId::Epoch2_05]),
1✔
732
        Ordering::Greater
733
    );
734
    assert_eq!(
1✔
735
        epochs[StacksEpochId::Epoch22].cmp(&epochs[StacksEpochId::Epoch10]),
1✔
736
        Ordering::Greater
737
    );
738
    assert_eq!(
1✔
739
        epochs[StacksEpochId::Epoch22].cmp(&epochs[StacksEpochId::Epoch20]),
1✔
740
        Ordering::Greater
741
    );
742
    assert_eq!(
1✔
743
        epochs[StacksEpochId::Epoch22].cmp(&epochs[StacksEpochId::Epoch2_05]),
1✔
744
        Ordering::Greater
745
    );
746
    assert_eq!(
1✔
747
        epochs[StacksEpochId::Epoch22].cmp(&epochs[StacksEpochId::Epoch21]),
1✔
748
        Ordering::Greater
749
    );
750
    assert_eq!(
1✔
751
        epochs[StacksEpochId::Epoch23].cmp(&epochs[StacksEpochId::Epoch10]),
1✔
752
        Ordering::Greater
753
    );
754
    assert_eq!(
1✔
755
        epochs[StacksEpochId::Epoch23].cmp(&epochs[StacksEpochId::Epoch20]),
1✔
756
        Ordering::Greater
757
    );
758
    assert_eq!(
1✔
759
        epochs[StacksEpochId::Epoch23].cmp(&epochs[StacksEpochId::Epoch2_05]),
1✔
760
        Ordering::Greater
761
    );
762
    assert_eq!(
1✔
763
        epochs[StacksEpochId::Epoch23].cmp(&epochs[StacksEpochId::Epoch21]),
1✔
764
        Ordering::Greater
765
    );
766
    assert_eq!(
1✔
767
        epochs[StacksEpochId::Epoch23].cmp(&epochs[StacksEpochId::Epoch22]),
1✔
768
        Ordering::Greater
769
    );
770
    assert_eq!(
1✔
771
        epochs[StacksEpochId::Epoch24].cmp(&epochs[StacksEpochId::Epoch10]),
1✔
772
        Ordering::Greater
773
    );
774
    assert_eq!(
1✔
775
        epochs[StacksEpochId::Epoch24].cmp(&epochs[StacksEpochId::Epoch20]),
1✔
776
        Ordering::Greater
777
    );
778
    assert_eq!(
1✔
779
        epochs[StacksEpochId::Epoch24].cmp(&epochs[StacksEpochId::Epoch2_05]),
1✔
780
        Ordering::Greater
781
    );
782
    assert_eq!(
1✔
783
        epochs[StacksEpochId::Epoch24].cmp(&epochs[StacksEpochId::Epoch21]),
1✔
784
        Ordering::Greater
785
    );
786
    assert_eq!(
1✔
787
        epochs[StacksEpochId::Epoch24].cmp(&epochs[StacksEpochId::Epoch22]),
1✔
788
        Ordering::Greater
789
    );
790
    assert_eq!(
1✔
791
        epochs[StacksEpochId::Epoch24].cmp(&epochs[StacksEpochId::Epoch23]),
1✔
792
        Ordering::Greater
793
    );
794
    assert_eq!(
1✔
795
        epochs[StacksEpochId::Epoch25].cmp(&epochs[StacksEpochId::Epoch10]),
1✔
796
        Ordering::Greater
797
    );
798
    assert_eq!(
1✔
799
        epochs[StacksEpochId::Epoch25].cmp(&epochs[StacksEpochId::Epoch20]),
1✔
800
        Ordering::Greater
801
    );
802
    assert_eq!(
1✔
803
        epochs[StacksEpochId::Epoch25].cmp(&epochs[StacksEpochId::Epoch2_05]),
1✔
804
        Ordering::Greater
805
    );
806
    assert_eq!(
1✔
807
        epochs[StacksEpochId::Epoch25].cmp(&epochs[StacksEpochId::Epoch21]),
1✔
808
        Ordering::Greater
809
    );
810
    assert_eq!(
1✔
811
        epochs[StacksEpochId::Epoch25].cmp(&epochs[StacksEpochId::Epoch22]),
1✔
812
        Ordering::Greater
813
    );
814
    assert_eq!(
1✔
815
        epochs[StacksEpochId::Epoch25].cmp(&epochs[StacksEpochId::Epoch23]),
1✔
816
        Ordering::Greater
817
    );
818
    assert_eq!(
1✔
819
        epochs[StacksEpochId::Epoch25].cmp(&epochs[StacksEpochId::Epoch24]),
1✔
820
        Ordering::Greater
821
    );
822
    assert_eq!(
1✔
823
        epochs[StacksEpochId::Epoch30].cmp(&epochs[StacksEpochId::Epoch10]),
1✔
824
        Ordering::Greater
825
    );
826
    assert_eq!(
1✔
827
        epochs[StacksEpochId::Epoch30].cmp(&epochs[StacksEpochId::Epoch20]),
1✔
828
        Ordering::Greater
829
    );
830
    assert_eq!(
1✔
831
        epochs[StacksEpochId::Epoch30].cmp(&epochs[StacksEpochId::Epoch2_05]),
1✔
832
        Ordering::Greater
833
    );
834
    assert_eq!(
1✔
835
        epochs[StacksEpochId::Epoch30].cmp(&epochs[StacksEpochId::Epoch21]),
1✔
836
        Ordering::Greater
837
    );
838
    assert_eq!(
1✔
839
        epochs[StacksEpochId::Epoch30].cmp(&epochs[StacksEpochId::Epoch22]),
1✔
840
        Ordering::Greater
841
    );
842
    assert_eq!(
1✔
843
        epochs[StacksEpochId::Epoch30].cmp(&epochs[StacksEpochId::Epoch23]),
1✔
844
        Ordering::Greater
845
    );
846
    assert_eq!(
1✔
847
        epochs[StacksEpochId::Epoch30].cmp(&epochs[StacksEpochId::Epoch24]),
1✔
848
        Ordering::Greater
849
    );
850
    assert_eq!(
1✔
851
        epochs[StacksEpochId::Epoch30].cmp(&epochs[StacksEpochId::Epoch25]),
1✔
852
        Ordering::Greater
853
    );
854
    assert_eq!(
1✔
855
        epochs[StacksEpochId::Epoch31].cmp(&epochs[StacksEpochId::Epoch10]),
1✔
856
        Ordering::Greater
857
    );
858
    assert_eq!(
1✔
859
        epochs[StacksEpochId::Epoch31].cmp(&epochs[StacksEpochId::Epoch20]),
1✔
860
        Ordering::Greater
861
    );
862
    assert_eq!(
1✔
863
        epochs[StacksEpochId::Epoch31].cmp(&epochs[StacksEpochId::Epoch2_05]),
1✔
864
        Ordering::Greater
865
    );
866
    assert_eq!(
1✔
867
        epochs[StacksEpochId::Epoch31].cmp(&epochs[StacksEpochId::Epoch21]),
1✔
868
        Ordering::Greater
869
    );
870
    assert_eq!(
1✔
871
        epochs[StacksEpochId::Epoch31].cmp(&epochs[StacksEpochId::Epoch22]),
1✔
872
        Ordering::Greater
873
    );
874
    assert_eq!(
1✔
875
        epochs[StacksEpochId::Epoch31].cmp(&epochs[StacksEpochId::Epoch23]),
1✔
876
        Ordering::Greater
877
    );
878
    assert_eq!(
1✔
879
        epochs[StacksEpochId::Epoch31].cmp(&epochs[StacksEpochId::Epoch24]),
1✔
880
        Ordering::Greater
881
    );
882
    assert_eq!(
1✔
883
        epochs[StacksEpochId::Epoch31].cmp(&epochs[StacksEpochId::Epoch25]),
1✔
884
        Ordering::Greater
885
    );
886
    assert_eq!(
1✔
887
        epochs[StacksEpochId::Epoch31].cmp(&epochs[StacksEpochId::Epoch30]),
1✔
888
        Ordering::Greater
889
    );
890
}
1✔
891

892
#[test]
893
fn test_ord_for_stacks_epoch_id() {
1✔
894
    assert_eq!(
1✔
895
        StacksEpochId::Epoch10.cmp(&StacksEpochId::Epoch20),
1✔
896
        Ordering::Less
897
    );
898
    assert_eq!(
1✔
899
        StacksEpochId::Epoch20.cmp(&StacksEpochId::Epoch2_05),
1✔
900
        Ordering::Less
901
    );
902
    assert_eq!(
1✔
903
        StacksEpochId::Epoch10.cmp(&StacksEpochId::Epoch2_05),
1✔
904
        Ordering::Less
905
    );
906
    assert_eq!(
1✔
907
        StacksEpochId::Epoch10.cmp(&StacksEpochId::Epoch10),
1✔
908
        Ordering::Equal
909
    );
910
    assert_eq!(
1✔
911
        StacksEpochId::Epoch20.cmp(&StacksEpochId::Epoch20),
1✔
912
        Ordering::Equal
913
    );
914
    assert_eq!(
1✔
915
        StacksEpochId::Epoch2_05.cmp(&StacksEpochId::Epoch2_05),
1✔
916
        Ordering::Equal
917
    );
918
    assert_eq!(
1✔
919
        StacksEpochId::Epoch2_05.cmp(&StacksEpochId::Epoch20),
1✔
920
        Ordering::Greater
921
    );
922
    assert_eq!(
1✔
923
        StacksEpochId::Epoch2_05.cmp(&StacksEpochId::Epoch10),
1✔
924
        Ordering::Greater
925
    );
926
    assert_eq!(
1✔
927
        StacksEpochId::Epoch20.cmp(&StacksEpochId::Epoch10),
1✔
928
        Ordering::Greater
929
    );
930
}
1✔
931

932
#[cfg(test)]
933
fn release_epoch_from_stacks_node_version() -> StacksEpochId {
1✔
934
    let mut parts = STACKS_NODE_VERSION.split('.');
1✔
935
    let major = parts
1✔
936
        .next()
1✔
937
        .expect("FATAL: STACKS_NODE_VERSION must include major version");
1✔
938
    let minor = parts
1✔
939
        .next()
1✔
940
        .expect("FATAL: STACKS_NODE_VERSION must include minor version");
1✔
941
    let release_epoch_str = format!("{major}.{minor}");
1✔
942
    StacksEpochId::from_str(&release_epoch_str).unwrap_or_else(|_| {
1✔
UNCOV
943
        panic!(
×
944
            "FATAL: STACKS_NODE_VERSION major.minor '{release_epoch_str}' must map to a StacksEpochId"
945
        )
946
    })
947
}
1✔
948

949
#[test]
950
fn test_release_epoch_matches_versions_and_peer_epoch() {
1✔
951
    let release_epoch_from_version = release_epoch_from_stacks_node_version();
1✔
952
    assert_eq!(
1✔
953
        release_epoch_from_version,
954
        StacksEpochId::RELEASE_LATEST_EPOCH,
955
        "versions.toml stacks_node_version major.minor must match RELEASE_LATEST_EPOCH"
956
    );
957
    assert_eq!(
1✔
958
        u32::from(StacksEpochId::network_epoch(
1✔
959
            StacksEpochId::RELEASE_LATEST_EPOCH
960
        )),
961
        PEER_NETWORK_EPOCH,
962
        "PEER_NETWORK_EPOCH must match RELEASE_LATEST_EPOCH's network_epoch"
963
    );
964
}
1✔
965

966
#[test]
967
fn test_peer_network_epoch_matches_static_epoch_tables() {
1✔
968
    let check_epoch_id = peer_network_epoch_check_epoch_id();
1✔
969
    for (network_name, epochs) in [
3✔
970
        ("mainnet", &*STACKS_EPOCHS_MAINNET),
1✔
971
        ("testnet", &*STACKS_EPOCHS_TESTNET),
1✔
972
        ("regtest", &*STACKS_EPOCHS_REGTEST),
1✔
973
    ] {
1✔
974
        let check_epoch = epochs.get(check_epoch_id).unwrap_or_else(|| {
3✔
UNCOV
975
            panic!("FATAL: {network_name} epoch table is missing check epoch {check_epoch_id:?}")
×
976
        });
977
        assert_eq!(
3✔
978
            u32::from(check_epoch.network_epoch),
3✔
979
            PEER_NETWORK_EPOCH,
980
            "stacks-blockchain static network epoch should match {network_name} epoch {check_epoch_id:?}"
981
        );
982
    }
983
}
1✔
984

985
#[test]
986
#[should_panic(expected = "stacks-blockchain static network epoch should match")]
987
fn test_validate_epochs_rejects_stale_peer_network_epoch() {
1✔
988
    let check_epoch_id = peer_network_epoch_check_epoch_id();
1✔
989
    let mut bad_epochs = (*STACKS_EPOCHS_MAINNET).clone();
1✔
990
    bad_epochs[check_epoch_id].network_epoch = (PEER_NETWORK_EPOCH + 1) as u8;
1✔
991
    let _ = StacksEpoch::validate_epochs(&bad_epochs);
1✔
992
}
1✔
993

994
pub trait StacksEpochExtension {
995
    /// Build an epoch list spanning `Epoch10..=last_epoch` with the standard
996
    /// test cadence: Epoch10 covers `[0, first_burnchain_height)`, each
997
    /// subsequent epoch occupies 4 blocks, and `last_epoch` ends at
998
    /// `STACKS_EPOCH_MAX`.
999
    #[cfg(test)]
1000
    fn unit_test_up_to(first_burnchain_height: u64, last_epoch: StacksEpochId) -> EpochList;
1001
    /// Build an epoch list where `target_epoch` is the only active epoch.
1002
    /// Pre-Epoch25 epochs are disabled (zero-width at height 0); Epoch25
1003
    /// spans `[0, first_burnchain_height)` as the transition point; Nakamoto
1004
    /// epochs before `target_epoch` are zero-width at `first_burnchain_height`;
1005
    /// `target_epoch` spans `[first_burnchain_height, STACKS_EPOCH_MAX)`.
1006
    /// Requires `target_epoch >= Epoch30`.
1007
    #[cfg(test)]
1008
    fn unit_test_epoch_only(first_burnchain_height: u64, target_epoch: StacksEpochId) -> EpochList;
1009
    /// Build a pre-Nakamoto fixture with custom transition heights for the
1010
    /// pre-2.1 epochs. Spans Epoch10..=Epoch21 with all `block_limit`s set to
1011
    /// `ExecutionCost::max_value()` (so cost limits are out of the way).
1012
    /// Each parameter is the `start_height` of the named epoch (which equals
1013
    /// the `end_height` of the preceding one).
1014
    fn unit_test_2_1_with_heights(
1015
        epoch_2_0_start: u64,
1016
        epoch_2_05_start: u64,
1017
        epoch_2_1_start: u64,
1018
    ) -> EpochList;
1019
    fn validate_epochs(epochs: &[StacksEpoch]) -> EpochList;
1020
    /// This method gets the epoch vector.
1021
    ///
1022
    /// Choose according to:
1023
    /// 1) Use the custom epochs defined on the underlying `BitcoinIndexerConfig`, if they exist.
1024
    /// 2) Use hard-coded static values, otherwise.
1025
    ///
1026
    /// It is an error (panic) to set custom epochs if running on `Mainnet`.
1027
    ///
1028
    fn get_epochs(
1029
        bitcoin_network: BitcoinNetworkType,
1030
        configured_epochs: Option<&EpochList>,
1031
    ) -> EpochList;
1032
    /// Validates that Epoch 3.0 activation (if present) satisfies all required safety
1033
    /// invariants for Nakamoto transition, using the provided burnchain configuration.
1034
    fn validate_nakamoto_transition_schedule(epochs: &[StacksEpoch], burnchain: &Burnchain);
1035
}
1036

1037
impl StacksEpochExtension for StacksEpoch {
1038
    fn get_epochs(
14,185,332✔
1039
        bitcoin_network: BitcoinNetworkType,
14,185,332✔
1040
        configured_epochs: Option<&EpochList>,
14,185,332✔
1041
    ) -> EpochList {
14,185,332✔
1042
        match configured_epochs {
14,185,332✔
1043
            Some(epochs) => {
14,185,044✔
1044
                assert!(bitcoin_network != BitcoinNetworkType::Mainnet);
14,185,044✔
1045
                epochs.clone()
14,185,044✔
1046
            }
1047
            None => get_bitcoin_stacks_epochs(bitcoin_network),
288✔
1048
        }
1049
    }
14,185,332✔
1050

1051
    #[cfg(test)]
1052
    fn unit_test_up_to(first_burnchain_height: u64, last_epoch: StacksEpochId) -> EpochList {
1,044✔
1053
        info!(
1,044✔
1054
            "StacksEpoch unit_test_up_to last_epoch={last_epoch} first_burn_height={first_burnchain_height}"
1055
        );
1056

1057
        // Block limits used by the test epoch fixtures.
1058
        let block_limit_for = |epoch_id: StacksEpochId| -> ExecutionCost {
2,253✔
1059
            match epoch_id {
2,253✔
1060
                StacksEpochId::Epoch10 | StacksEpochId::Epoch20 => ExecutionCost::max_value(),
2,088✔
1061
                StacksEpochId::Epoch2_05 => ExecutionCost {
41✔
1062
                    write_length: 205205,
41✔
1063
                    write_count: 205205,
41✔
1064
                    read_length: 205205,
41✔
1065
                    read_count: 205205,
41✔
1066
                    runtime: 205205,
41✔
1067
                },
41✔
1068
                _ => ExecutionCost {
124✔
1069
                    write_length: 210210,
124✔
1070
                    write_count: 210210,
124✔
1071
                    read_length: 210210,
124✔
1072
                    read_count: 210210,
124✔
1073
                    runtime: 210210,
124✔
1074
                },
124✔
1075
            }
1076
        };
2,253✔
1077

1078
        let mut epochs = Vec::new();
1,044✔
1079
        let mut start_height = 0u64;
1,044✔
1080
        for epoch_id in StacksEpochId::ALL.iter() {
2,253✔
1081
            let end_height = if *epoch_id == last_epoch {
2,253✔
1082
                STACKS_EPOCH_MAX
1,044✔
1083
            } else if *epoch_id == StacksEpochId::Epoch10 {
1,209✔
1084
                first_burnchain_height
1,044✔
1085
            } else {
1086
                start_height + 4
165✔
1087
            };
1088
            epochs.push(StacksEpoch {
2,253✔
1089
                epoch_id: *epoch_id,
2,253✔
1090
                start_height,
2,253✔
1091
                end_height,
2,253✔
1092
                block_limit: block_limit_for(*epoch_id),
2,253✔
1093
                network_epoch: StacksEpochId::network_epoch(*epoch_id),
2,253✔
1094
            });
2,253✔
1095
            if *epoch_id == last_epoch {
2,253✔
1096
                break;
1,044✔
1097
            }
1,209✔
1098
            start_height = end_height;
1,209✔
1099
        }
1100
        EpochList::new(&epochs)
1,044✔
1101
    }
1,044✔
1102

1103
    #[cfg(test)]
1104
    fn unit_test_epoch_only(first_burnchain_height: u64, target_epoch: StacksEpochId) -> EpochList {
344✔
1105
        info!(
344✔
1106
            "StacksEpoch unit_test_epoch_only target={target_epoch} first_burn_height={first_burnchain_height}"
1107
        );
1108
        assert!(
344✔
1109
            target_epoch >= StacksEpochId::Epoch30,
344✔
1110
            "unit_test_epoch_only requires a Nakamoto-era target (Epoch30+), got {target_epoch}"
1111
        );
1112

1113
        // Height layout, iterating through StacksEpochId::ALL:
1114
        //  - Pre-Epoch25 epochs sit in a dead zone at height 0 (start == end == 0).
1115
        //  - Epoch25 spans [0, first_burnchain_height), the transition point.
1116
        //  - Nakamoto epochs before `target_epoch` are zero-width at
1117
        //    `first_burnchain_height`.
1118
        //  - `target_epoch` spans [first_burnchain_height, STACKS_EPOCH_MAX).
1119
        //
1120
        // Each epoch's `start_height` is the previous epoch's `end_height`,
1121
        // which falls out of that layout for free.
1122
        let mut epochs = Vec::new();
344✔
1123
        let mut start_height = 0u64;
344✔
1124
        for epoch_id in StacksEpochId::ALL.iter() {
3,118✔
1125
            let end_height = if *epoch_id == target_epoch {
3,118✔
1126
                STACKS_EPOCH_MAX
344✔
1127
            } else if *epoch_id < StacksEpochId::Epoch25 {
2,774✔
1128
                0
2,408✔
1129
            } else {
1130
                first_burnchain_height
366✔
1131
            };
1132
            let block_limit = if *epoch_id < StacksEpochId::Epoch25 {
3,118✔
1133
                ExecutionCost::max_value()
2,408✔
1134
            } else {
1135
                BLOCK_LIMIT_MAINNET_21
710✔
1136
            };
1137
            epochs.push(StacksEpoch {
3,118✔
1138
                epoch_id: *epoch_id,
3,118✔
1139
                start_height,
3,118✔
1140
                end_height,
3,118✔
1141
                block_limit,
3,118✔
1142
                network_epoch: StacksEpochId::network_epoch(*epoch_id),
3,118✔
1143
            });
3,118✔
1144
            if *epoch_id == target_epoch {
3,118✔
1145
                break;
344✔
1146
            }
2,774✔
1147
            start_height = end_height;
2,774✔
1148
        }
1149
        EpochList::new(&epochs)
344✔
1150
    }
344✔
1151

1152
    fn unit_test_2_1_with_heights(
2,345✔
1153
        epoch_2_0_start: u64,
2,345✔
1154
        epoch_2_05_start: u64,
2,345✔
1155
        epoch_2_1_start: u64,
2,345✔
1156
    ) -> EpochList {
2,345✔
1157
        let make = |epoch_id, start_height, end_height| StacksEpoch {
2,345✔
1158
            epoch_id,
9,380✔
1159
            start_height,
9,380✔
1160
            end_height,
9,380✔
1161
            block_limit: ExecutionCost::max_value(),
9,380✔
1162
            network_epoch: StacksEpochId::network_epoch(epoch_id),
9,380✔
1163
        };
9,380✔
1164
        EpochList::new(&[
2,345✔
1165
            make(StacksEpochId::Epoch10, 0, epoch_2_0_start),
2,345✔
1166
            make(StacksEpochId::Epoch20, epoch_2_0_start, epoch_2_05_start),
2,345✔
1167
            make(StacksEpochId::Epoch2_05, epoch_2_05_start, epoch_2_1_start),
2,345✔
1168
            make(StacksEpochId::Epoch21, epoch_2_1_start, STACKS_EPOCH_MAX),
2,345✔
1169
        ])
2,345✔
1170
    }
2,345✔
1171

1172
    /// Verify that a list of epochs is well-formed, and if so, return the list of epochs.
1173
    /// Epochs must proceed in order, and must represent contiguous block ranges.
1174
    /// Panic if the list is not well-formed.
1175
    fn validate_epochs(epochs_ref: &[StacksEpoch]) -> EpochList {
4,410,432✔
1176
        // sanity check -- epochs must all be contiguous, each epoch must be unique,
1177
        // and the range of epochs should span the whole non-negative i64 space.
1178
        let mut epochs = epochs_ref.to_vec();
4,410,432✔
1179
        let mut seen_epochs = HashSet::new();
4,410,432✔
1180
        epochs.sort();
4,410,432✔
1181

1182
        let latest_epoch = StacksEpochId::latest();
4,410,432✔
1183
        let max_epoch = epochs_ref
4,410,432✔
1184
            .iter()
4,410,432✔
1185
            .max()
4,410,432✔
1186
            .expect("FATAL: expect at least one epoch");
4,410,432✔
1187

1188
        let check_epoch_id = peer_network_epoch_check_epoch_id();
4,410,432✔
1189
        assert_peer_network_epoch(epochs_ref, max_epoch, check_epoch_id);
4,410,432✔
1190

1191
        let latest_epoch_idx = StacksEpochId::ALL
4,410,432✔
1192
            .iter()
4,410,432✔
1193
            .position(|epoch_id| *epoch_id == latest_epoch)
61,746,034✔
1194
            .expect("FATAL: latest epoch id missing from StacksEpochId::ALL");
4,410,432✔
1195
        let max_epoch_idx = StacksEpochId::ALL
4,410,432✔
1196
            .iter()
4,410,432✔
1197
            .position(|epoch_id| *epoch_id == max_epoch.epoch_id)
53,338,247✔
1198
            .expect("FATAL: max epoch id missing from StacksEpochId::ALL");
4,410,432✔
1199

1200
        // Allow epochs up to one version ahead of latest() for development purposes
1201
        assert!(max_epoch_idx <= latest_epoch_idx.saturating_add(1),
4,410,432✔
1202
            "StacksEpochId::latest() should be greater than or equal to any epoch defined in the node (except for development epochs)"
1203
        );
1204

1205
        let mut epoch_end_height = 0;
4,410,431✔
1206
        for epoch in epochs.iter() {
53,299,657✔
1207
            assert!(
53,299,657✔
1208
                epoch.start_height <= epoch.end_height,
53,299,657✔
1209
                "{} > {} for {:?}",
1210
                epoch.start_height,
1211
                epoch.end_height,
1212
                &epoch.epoch_id
1✔
1213
            );
1214

1215
            if epoch_end_height == 0 {
53,299,656✔
1216
                // first ever epoch must be defined for all of the prior chain history
1217
                assert_eq!(epoch.start_height, 0);
8,789,469✔
1218
                epoch_end_height = epoch.end_height;
8,789,468✔
1219
            } else {
1220
                assert_eq!(epoch_end_height, epoch.start_height);
44,510,187✔
1221
                epoch_end_height = epoch.end_height;
44,510,185✔
1222
            }
1223
            if seen_epochs.contains(&epoch.epoch_id) {
53,299,653✔
UNCOV
1224
                panic!("BUG: duplicate epoch");
×
1225
            }
53,299,653✔
1226

1227
            seen_epochs.insert(epoch.epoch_id);
53,299,653✔
1228
        }
1229

1230
        assert_eq!(epoch_end_height, STACKS_EPOCH_MAX);
4,410,427✔
1231

1232
        EpochList::new(&epochs)
4,410,426✔
1233
    }
4,410,426✔
1234

1235
    /// Validates that Epoch 3.0 activation (if present) satisfies all required safety
1236
    /// invariants for Nakamoto transition, using the provided burnchain configuration.
1237
    ///
1238
    /// This function is only relevant when **Nakamoto epochs** (Epoch 3.0+) exist in the
1239
    /// epoch list. If no post Epoch 2.5 is defined, the function returns early with no checks.
1240
    ///
1241
    /// ### Required Invariants for Safe Epoch 3.0 Activation
1242
    ///
1243
    /// 1. **Epoch 2.5 must exist** and start **before** the prepare phase of the reward
1244
    ///    cycle immediately preceding Epoch 3.0.
1245
    /// 2. **Epoch 2.5 must end exactly at the start of Epoch 3.0** — they are contiguous.
1246
    /// 3. **Epoch 2.5 and Epoch 3.0 must be in different reward cycles**
1247
    /// 4. **Epoch 3.0 must start during a reward phase**, not in a prepare phase.
1248
    /// 5. **Epoch 3.0 must not start at a reward cycle boundary** (i.e., block height
1249
    ///    modulo `reward_cycle_length` must not be 0 or 1).
1250
    /// 6. **Epoch 3.0 must activate at or after reward cycle 2** (cycle 0 and 1 are
1251
    ///    reserved for early network bootstrapping).
1252
    ///
1253
    /// # Parameters
1254
    ///
1255
    /// - `epochs`: List of defined Stacks epochs.
1256
    /// - `burnchain`: Burnchain configuration, providing PoX reward cycle parameters
1257
    ///   (`reward_cycle_length`, `prepare_length`) and height-to-cycle utilities.
1258
    ///
1259
    /// # Panics
1260
    ///
1261
    /// This function panics if any of the invariants fail.
1262
    /// These panics are intended to catch **misconfigured networks** at startup
1263
    fn validate_nakamoto_transition_schedule(epochs: &[StacksEpoch], burnchain: &Burnchain) {
8,261,039✔
1264
        // Early return if no Nakamoto epochs are defined
1265
        if epochs
8,261,039✔
1266
            .iter()
8,261,039✔
1267
            .all(|epoch| epoch.epoch_id < StacksEpochId::Epoch30)
74,346,714✔
1268
        {
1269
            return;
531✔
1270
        }
8,260,508✔
1271
        let epoch_3_0 = epochs
8,260,508✔
1272
            .iter()
8,260,508✔
1273
            .find(|e| e.epoch_id == StacksEpochId::Epoch30)
74,344,572✔
1274
            .expect("FATAL: Cannot activate Epoch 3.0 without specifying its activation height");
8,260,508✔
1275
        let epoch_2_5 = epochs
8,260,508✔
1276
            .iter()
8,260,508✔
1277
            .find(|e| e.epoch_id == StacksEpochId::Epoch25)
66,084,064✔
1278
            .expect("FATAL: Epoch 2.5 not found");
8,260,508✔
1279
        let epoch_3_0_start = epoch_3_0.start_height;
8,260,508✔
1280
        let epoch_2_5_start = epoch_2_5.start_height;
8,260,508✔
1281
        let epoch_2_5_end = epoch_2_5.end_height;
8,260,508✔
1282

1283
        let reward_cycle_length = u64::from(burnchain.pox_constants.reward_cycle_length);
8,260,508✔
1284
        let prepare_length = u64::from(burnchain.pox_constants.prepare_length);
8,260,508✔
1285

1286
        assert!(
8,260,508✔
1287
            !burnchain.is_in_prepare_phase(epoch_3_0_start),
8,260,508✔
1288
            "FATAL: Epoch 3.0 must start *during* a reward phase, not prepare phase. \
1289
            Activation height: {epoch_3_0_start}, PoX Parameters: {:?}",
1290
            burnchain.pox_constants
1291
        );
1292

1293
        let activation_reward_cycle = burnchain
8,260,508✔
1294
            .block_height_to_reward_cycle(epoch_3_0_start)
8,260,508✔
1295
            .expect("FATAL: Epoch 3.0 cannot start before the first burnchain block");
8,260,508✔
1296
        assert!(
8,260,508✔
1297
            activation_reward_cycle >= 2,
8,260,508✔
1298
            "FATAL: Epoch 3.0 must start at or after reward cycle 2. \
1299
            Activation height: {epoch_3_0_start}, cycle: {activation_reward_cycle}, \
1300
            PoX Parameters: {:?}",
1301
            burnchain.pox_constants
1302
        );
1303

1304
        let epoch_2_5_reward_cycle = epoch_2_5_start / reward_cycle_length;
8,260,508✔
1305
        let epoch_3_0_reward_cycle = epoch_3_0_start / reward_cycle_length;
8,260,508✔
1306
        // Start of prepare phase in the cycle before Epoch 3.0
1307
        let prior_cycle = epoch_3_0_reward_cycle.saturating_sub(1);
8,260,508✔
1308
        let epoch_3_0_prepare_phase_start =
8,260,508✔
1309
            prior_cycle * reward_cycle_length + (reward_cycle_length - prepare_length);
8,260,508✔
1310
        assert!(
8,260,508✔
1311
            epoch_2_5_start < epoch_3_0_prepare_phase_start,
8,260,508✔
1312
            "FATAL: Epoch 2.5 must start before the prepare phase of the cycle prior to Epoch 3.0. \
1313
            Epoch 2.5 start: {epoch_2_5_start}, \
1314
            Epoch 3.0 prior cycle prepare phase start: {epoch_3_0_prepare_phase_start}, \
1315
            PoX Parameters: {:?}",
1316
            burnchain.pox_constants
1317
        );
1318

1319
        assert_eq!(
8,260,508✔
1320
            epoch_2_5_end, epoch_3_0_start,
1321
            "FATAL: Epoch 2.5 end must equal Epoch 3.0 start. \
1322
            End: {epoch_2_5_end}, Start: {epoch_3_0_start}"
1323
        );
1324

1325
        assert_ne!(
8,260,508✔
1326
            epoch_2_5_reward_cycle, epoch_3_0_reward_cycle,
1327
            "FATAL: Epoch 2.5 and Epoch 3.0 must not be in the same reward cycle. \
1328
            Epoch 2.5 cycle: {epoch_2_5_reward_cycle}, \
1329
            Epoch 3.0 cycle: {epoch_3_0_reward_cycle}, \
1330
            PoX Parameters: {:?}",
1331
            burnchain.pox_constants
1332
        );
1333

1334
        // Epoch 2.5 has some confusing boundary logic for calculating the reward set hence why
1335
        // the boundary is viewed as both 0 and 1.
1336
        assert!(
8,260,508✔
1337
            epoch_3_0_start % reward_cycle_length > 1,
8,260,508✔
1338
            "FATAL: Epoch 3.0 must not start at a reward cycle boundary (offset 0 or 1). \
1339
            Activation height: {epoch_3_0_start}, \
1340
            offset: {}, PoX Parameters: {:?}",
UNCOV
1341
            epoch_3_0_start % reward_cycle_length,
×
1342
            burnchain.pox_constants
1343
        );
1344
    }
8,261,039✔
1345
}
1346

1347
// Test/testing variant:
1348
// - Allows older epoch markers (`<= PEER_NETWORK_EPOCH`) to cover back-compat fixtures
1349
//   (e.g. the historical 3.3 release that advertised 3.2).
1350
// - Allows partial epoch lists used by unit tests that intentionally stop before
1351
//   `RELEASE_LATEST_EPOCH`.
1352
#[cfg(any(test, feature = "testing"))]
1353
fn assert_peer_network_epoch(
4,410,432✔
1354
    epochs_ref: &[StacksEpoch],
4,410,432✔
1355
    max_epoch: &StacksEpoch,
4,410,432✔
1356
    check_epoch_id: StacksEpochId,
4,410,432✔
1357
) {
4,410,432✔
1358
    if epochs_ref
4,410,432✔
1359
        .iter()
4,410,432✔
1360
        .find(|epoch| epoch.epoch_id == check_epoch_id)
49,817,251✔
1361
        .is_none()
4,410,432✔
1362
    {
1363
        // Some test-only epoch lists intentionally end before the release latest epoch.
1364
        assert!(
893,123✔
1365
            max_epoch.network_epoch as u32 <= PEER_NETWORK_EPOCH,
893,123✔
1366
            "stacks-blockchain static network epoch should be greater than or equal to the max epoch's"
1367
        );
1368
    } else {
1369
        let check_epoch = epochs_ref
3,517,309✔
1370
            .iter()
3,517,309✔
1371
            .find(|epoch| epoch.epoch_id == check_epoch_id)
45,725,017✔
1372
            .expect("FATAL: check epoch must exist");
3,517,309✔
1373
        assert!(
3,517,309✔
1374
            u32::from(check_epoch.network_epoch) <= PEER_NETWORK_EPOCH,
3,517,309✔
1375
            "stacks-blockchain static network epoch should match {:?}'s network_epoch",
1376
            check_epoch_id
1377
        );
1378
    }
1379
}
4,410,431✔
1380

1381
// Runtime variant:
1382
// - Enforces strict equality between `PEER_NETWORK_EPOCH` and the release epoch marker.
1383
// - Requires the release epoch to be present in the configured epoch list.
1384
#[cfg(not(any(test, feature = "testing")))]
1385
fn assert_peer_network_epoch(
1386
    epochs_ref: &[StacksEpoch],
1387
    _max_epoch: &StacksEpoch,
1388
    check_epoch_id: StacksEpochId,
1389
) {
1390
    let check_epoch = epochs_ref
1391
        .iter()
1392
        .find(|epoch| epoch.epoch_id == check_epoch_id)
1393
        .unwrap_or_else(|| {
1394
            panic!(
1395
                "FATAL: no {:?} defined in epoch list, cannot validate PEER_NETWORK_EPOCH",
1396
                check_epoch_id
1397
            )
1398
        });
1399
    assert_eq!(
1400
        u32::from(check_epoch.network_epoch),
1401
        PEER_NETWORK_EPOCH,
1402
        "stacks-blockchain static network epoch should match {:?}'s network_epoch",
1403
        check_epoch_id
1404
    );
1405
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc