Skip to content

Commit e8a60a7

Browse files
authored
Merge pull request #6336 from rdeioris/fix/sip_031_test_flakiness
Fix sip 031 test flakiness and refactoring of base types and functions
2 parents 3ece8b5 + 8fa1399 commit e8a60a7

File tree

3 files changed

+243
-209
lines changed

3 files changed

+243
-209
lines changed

stacks-common/src/types/mod.rs

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,181 @@ impl CoinbaseInterval {
254254
}
255255
}
256256

257+
/// Struct describing the intervals in which SIP-031 emission are applied.
258+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
259+
pub struct SIP031EmissionInterval {
260+
/// amount of uSTX to emit
261+
pub amount: u128,
262+
/// height of the burn chain in which the interval starts
263+
pub start_height: u64,
264+
}
265+
266+
// From SIP-031:
267+
//
268+
// | Bitcoin Height | STX Emission |
269+
// |----------------|------------ |
270+
// | 907,740 | 475 |
271+
// | 960,300 | 1,140 |
272+
// | 1,012,860 | 1,705 |
273+
// | 1,065,420 | 1,305 |
274+
// | 1,117,980 | 1,155 |
275+
// | 1,170,540 | 0 |
276+
277+
/// Mainnet sip-031 emission intervals
278+
pub static SIP031_EMISSION_INTERVALS_MAINNET: LazyLock<[SIP031EmissionInterval; 6]> =
279+
LazyLock::new(|| {
280+
let emissions_schedule = [
281+
SIP031EmissionInterval {
282+
amount: 0,
283+
start_height: 1_170_540,
284+
},
285+
SIP031EmissionInterval {
286+
amount: 1_155 * u128::from(MICROSTACKS_PER_STACKS),
287+
start_height: 1_117_980,
288+
},
289+
SIP031EmissionInterval {
290+
amount: 1_305 * u128::from(MICROSTACKS_PER_STACKS),
291+
start_height: 1_065_420,
292+
},
293+
SIP031EmissionInterval {
294+
amount: 1_705 * u128::from(MICROSTACKS_PER_STACKS),
295+
start_height: 1_012_860,
296+
},
297+
SIP031EmissionInterval {
298+
amount: 1_140 * u128::from(MICROSTACKS_PER_STACKS),
299+
start_height: 960_300,
300+
},
301+
SIP031EmissionInterval {
302+
amount: 475 * u128::from(MICROSTACKS_PER_STACKS),
303+
start_height: 907_740,
304+
},
305+
];
306+
assert!(SIP031EmissionInterval::check_inversed_order(
307+
&emissions_schedule
308+
));
309+
emissions_schedule
310+
});
311+
312+
/// Testnet sip-031 emission intervals (starting from 71_525, 1 interval every 360 bitcoin blocks)
313+
pub static SIP031_EMISSION_INTERVALS_TESTNET: LazyLock<[SIP031EmissionInterval; 6]> =
314+
LazyLock::new(|| {
315+
let emissions_schedule = [
316+
SIP031EmissionInterval {
317+
amount: 0,
318+
start_height: 71_525 + (360 * 6),
319+
},
320+
SIP031EmissionInterval {
321+
amount: 5_000,
322+
start_height: 71_525 + (360 * 5),
323+
},
324+
SIP031EmissionInterval {
325+
amount: 4_000,
326+
start_height: 71_525 + (360 * 4),
327+
},
328+
SIP031EmissionInterval {
329+
amount: 3_000,
330+
start_height: 71_525 + (360 * 3),
331+
},
332+
SIP031EmissionInterval {
333+
amount: 2_000,
334+
start_height: 71_525 + (360 * 2),
335+
},
336+
SIP031EmissionInterval {
337+
amount: 1_000,
338+
start_height: 71_525 + 360,
339+
},
340+
];
341+
assert!(SIP031EmissionInterval::check_inversed_order(
342+
&emissions_schedule
343+
));
344+
emissions_schedule
345+
});
346+
347+
/// Used for testing to substitute a sip-031 emission schedule
348+
#[cfg(any(test, feature = "testing"))]
349+
pub static SIP031_EMISSION_INTERVALS_TEST: std::sync::Mutex<Option<Vec<SIP031EmissionInterval>>> =
350+
std::sync::Mutex::new(None);
351+
352+
#[cfg(any(test, feature = "testing"))]
353+
pub fn set_test_sip_031_emission_schedule(emission_schedule: Option<Vec<SIP031EmissionInterval>>) {
354+
if let Some(emission_schedule_vec) = &emission_schedule {
355+
assert!(SIP031EmissionInterval::check_inversed_order(
356+
emission_schedule_vec
357+
));
358+
}
359+
match SIP031_EMISSION_INTERVALS_TEST.lock() {
360+
Ok(mut schedule_guard) => {
361+
*schedule_guard = emission_schedule;
362+
}
363+
Err(_e) => {
364+
panic!("SIP031_EMISSION_INTERVALS_TEST mutex poisoned");
365+
}
366+
}
367+
}
368+
369+
#[cfg(any(test, feature = "testing"))]
370+
fn get_sip_031_emission_schedule(_mainnet: bool) -> Vec<SIP031EmissionInterval> {
371+
match SIP031_EMISSION_INTERVALS_TEST.lock() {
372+
Ok(schedule_opt) => {
373+
if let Some(schedule) = (*schedule_opt).as_ref() {
374+
info!("Use overridden SIP-031 emission schedule {:?}", &schedule);
375+
schedule.clone()
376+
} else {
377+
vec![]
378+
}
379+
}
380+
Err(_e) => {
381+
panic!("COINBASE_INTERVALS_TEST mutex poisoned");
382+
}
383+
}
384+
}
385+
386+
#[cfg(not(any(test, feature = "testing")))]
387+
fn get_sip_031_emission_schedule(mainnet: bool) -> Vec<SIP031EmissionInterval> {
388+
if mainnet {
389+
SIP031_EMISSION_INTERVALS_MAINNET.to_vec()
390+
} else {
391+
SIP031_EMISSION_INTERVALS_TESTNET.to_vec()
392+
}
393+
}
394+
395+
impl SIP031EmissionInterval {
396+
/// Look up the amount of STX to emit at the start of the tenure at the specified height.
397+
/// Precondition: `intervals` must be sorted in descending order by `start_height`
398+
pub fn get_sip_031_emission_at_height(burn_height: u64, mainnet: bool) -> u128 {
399+
let intervals = get_sip_031_emission_schedule(mainnet);
400+
401+
if intervals.is_empty() {
402+
return 0;
403+
}
404+
405+
for interval in intervals {
406+
if burn_height >= interval.start_height {
407+
return interval.amount;
408+
}
409+
}
410+
411+
// default emission (out of SIP-031 ranges)
412+
0
413+
}
414+
415+
/// Verify that a list of intervals is sorted in descending order by `start_height`
416+
pub fn check_inversed_order(intervals: &[SIP031EmissionInterval]) -> bool {
417+
let Some(mut ht) = intervals.first().map(|x| x.start_height) else {
418+
// if the interval list is empty, its sorted
419+
return true;
420+
};
421+
422+
for interval in intervals.iter().skip(1) {
423+
if interval.start_height > ht {
424+
return false;
425+
}
426+
ht = interval.start_height;
427+
}
428+
true
429+
}
430+
}
431+
257432
impl StacksEpochId {
258433
pub fn latest() -> StacksEpochId {
259434
StacksEpochId::Epoch32

stacks-node/src/tests/nakamoto_integrations.rs

Lines changed: 66 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,7 @@ use stacks::chainstate::nakamoto::coordinator::{load_nakamoto_reward_set, TEST_C
5151
use stacks::chainstate::nakamoto::miner::NakamotoBlockBuilder;
5252
use stacks::chainstate::nakamoto::shadow::shadow_chainstate_repair;
5353
use stacks::chainstate::nakamoto::test_signers::TestSigners;
54-
use stacks::chainstate::nakamoto::{
55-
set_test_sip_031_emission_schedule, NakamotoBlock, NakamotoBlockHeader, NakamotoChainState,
56-
SIP031EmissionInterval,
57-
};
54+
use stacks::chainstate::nakamoto::{NakamotoBlock, NakamotoBlockHeader, NakamotoChainState};
5855
use stacks::chainstate::stacks::address::{PoxAddress, StacksAddressExtensions};
5956
use stacks::chainstate::stacks::boot::{
6057
MINERS_NAME, SIGNERS_VOTING_FUNCTION_NAME, SIGNERS_VOTING_NAME, SIP_031_TESTNET_ADDR,
@@ -105,7 +102,10 @@ use stacks_common::types::chainstate::{
105102
BlockHeaderHash, BurnchainHeaderHash, StacksAddress, StacksPrivateKey, StacksPublicKey,
106103
TrieHash,
107104
};
108-
use stacks_common::types::{set_test_coinbase_schedule, CoinbaseInterval, StacksPublicKeyBuffer};
105+
use stacks_common::types::{
106+
set_test_coinbase_schedule, set_test_sip_031_emission_schedule, CoinbaseInterval,
107+
SIP031EmissionInterval, StacksPublicKeyBuffer,
108+
};
109109
use stacks_common::util::hash::{to_hex, Hash160, Sha512Trunc256Sum};
110110
use stacks_common::util::secp256k1::{MessageSignature, Secp256k1PrivateKey, Secp256k1PublicKey};
111111
use stacks_common::util::{get_epoch_time_secs, sleep_ms};
@@ -12701,6 +12701,7 @@ fn write_signer_update(
1270112701
/// - check sip031 boot contract has a balance of 200_000_000 STX
1270212702
#[test]
1270312703
#[ignore]
12704+
#[serial]
1270412705
fn test_sip_031_activation() {
1270512706
if env::var("BITCOIND_TEST") != Ok("1".into()) {
1270612707
return;
@@ -12826,10 +12827,16 @@ fn test_sip_031_activation() {
1282612827
);
1282712828

1282812829
// check for Epoch 3.2 in clarity db
12829-
let latest_stacks_block_id = get_latest_block_proposal(&naka_conf, &sortdb)
12830-
.unwrap()
12831-
.0
12832-
.block_id();
12830+
let latest_stacks_block_id = StacksBlockId::from_hex(
12831+
&test_observer::get_blocks()
12832+
.last()
12833+
.unwrap()
12834+
.get("index_block_hash")
12835+
.unwrap()
12836+
.as_str()
12837+
.unwrap()[2..],
12838+
)
12839+
.unwrap();
1283312840

1283412841
let epoch_version = chainstate.with_read_only_clarity_tx(
1283512842
&sortdb
@@ -13122,10 +13129,16 @@ fn test_sip_031_last_phase() {
1312213129
get_chain_info_opt(&naka_conf).unwrap().burn_block_height
1312313130
);
1312413131

13125-
let latest_stacks_block_id = get_latest_block_proposal(&naka_conf, &sortdb)
13126-
.unwrap()
13127-
.0
13128-
.block_id();
13132+
let latest_stacks_block_id = StacksBlockId::from_hex(
13133+
&test_observer::get_blocks()
13134+
.last()
13135+
.unwrap()
13136+
.get("index_block_hash")
13137+
.unwrap()
13138+
.as_str()
13139+
.unwrap()[2..],
13140+
)
13141+
.unwrap();
1312913142

1313013143
// check if sip-031 boot contract has a balance of 200_000_000 STX
1313113144
let sip_031_boot_contract_balance = chainstate.with_read_only_clarity_tx(
@@ -13244,10 +13257,16 @@ fn test_sip_031_last_phase() {
1324413257
// (100_000 + 200_000 + 300_000) * 10
1324513258
assert_eq!(total_minted_and_transferred, 6_000_000);
1324613259

13247-
let latest_stacks_block_id = get_latest_block_proposal(&naka_conf, &sortdb)
13248-
.unwrap()
13249-
.0
13250-
.block_id();
13260+
let latest_stacks_block_id = StacksBlockId::from_hex(
13261+
&test_observer::get_blocks()
13262+
.last()
13263+
.unwrap()
13264+
.get("index_block_hash")
13265+
.unwrap()
13266+
.as_str()
13267+
.unwrap()[2..],
13268+
)
13269+
.unwrap();
1325113270

1325213271
// get sip-031 boot contract balance (will be checked for 200_000_000 STX + total_minted_and_transferred)
1325313272
let sip_031_boot_contract_balance = chainstate.with_read_only_clarity_tx(
@@ -13430,10 +13449,16 @@ fn test_sip_031_last_phase_out_of_epoch() {
1343013449
get_chain_info_opt(&naka_conf).unwrap().burn_block_height
1343113450
);
1343213451

13433-
let latest_stacks_block_id = get_latest_block_proposal(&naka_conf, &sortdb)
13434-
.unwrap()
13435-
.0
13436-
.block_id();
13452+
let latest_stacks_block_id = StacksBlockId::from_hex(
13453+
&test_observer::get_blocks()
13454+
.last()
13455+
.unwrap()
13456+
.get("index_block_hash")
13457+
.unwrap()
13458+
.as_str()
13459+
.unwrap()[2..],
13460+
)
13461+
.unwrap();
1343713462

1343813463
// check if sip-031 boot contract has a balance of 200_000_000 STX
1343913464
let sip_031_boot_contract_balance = chainstate.with_read_only_clarity_tx(
@@ -13620,10 +13645,16 @@ fn test_sip_031_last_phase_coinbase_matches_activation() {
1362013645
get_chain_info_opt(&naka_conf).unwrap().burn_block_height
1362113646
);
1362213647

13623-
let latest_stacks_block_id = get_latest_block_proposal(&naka_conf, &sortdb)
13624-
.unwrap()
13625-
.0
13626-
.block_id();
13648+
let latest_stacks_block_id = StacksBlockId::from_hex(
13649+
&test_observer::get_blocks()
13650+
.last()
13651+
.unwrap()
13652+
.get("index_block_hash")
13653+
.unwrap()
13654+
.as_str()
13655+
.unwrap()[2..],
13656+
)
13657+
.unwrap();
1362713658

1362813659
// check if sip-031 boot contract has a balance of 200_000_000 STX + coinbase-mint-and-transfer
1362913660
let sip_031_boot_contract_balance = chainstate.with_read_only_clarity_tx(
@@ -13801,10 +13832,16 @@ fn test_sip_031_last_phase_coinbase_matches_activation() {
1380113832
// 100_000
1380213833
assert_eq!(total_minted_and_transferred, 100_000);
1380313834

13804-
let latest_stacks_block_id = get_latest_block_proposal(&naka_conf, &sortdb)
13805-
.unwrap()
13806-
.0
13807-
.block_id();
13835+
let latest_stacks_block_id = StacksBlockId::from_hex(
13836+
&test_observer::get_blocks()
13837+
.last()
13838+
.unwrap()
13839+
.get("index_block_hash")
13840+
.unwrap()
13841+
.as_str()
13842+
.unwrap()[2..],
13843+
)
13844+
.unwrap();
1380813845

1380913846
// get sip-031 boot contract balance (will be checked for 200_000_000 STX + 100_000 + total_minted_and_transferred)
1381013847
let sip_031_boot_contract_balance = chainstate.with_read_only_clarity_tx(

0 commit comments

Comments
 (0)