Skip to content

Commit 13097c6

Browse files
authored
Merge pull request #6323 from rdeioris/fix/sip_031_coinbase_matches_activation
fixed sip-031 coinbase mint event assignment, updated changelog
2 parents b94c17d + f47976e commit 13097c6

File tree

4 files changed

+366
-10
lines changed

4 files changed

+366
-10
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to the versioning scheme outlined in the [README.md](README.md).
77

8-
## Unreleased
8+
## [3.2.0.0.0]
99

1010
### Added
1111

1212
- Added `/v3/contracts/fast-call-read/:principal/:contract_name/:func_name` api endpoint. It allows to run read-only calls faster by disabling the cost and memory trackers. This endpoint requires authentication.
13+
- **SIP-031 consensus rules, activating in epoch 3.2 at block 907_740**
1314

1415
### Changed
1516

stacks-node/src/tests/nakamoto_integrations.rs

Lines changed: 354 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13502,3 +13502,357 @@ fn test_sip_031_last_phase_out_of_epoch() {
1350213502

1350313503
run_loop_thread.join().unwrap();
1350413504
}
13505+
13506+
/// Test SIP-031 last phase per-tenure-mint-and-transfer
13507+
///
13508+
/// - check epoch 3.2 is active
13509+
/// - check minting event on coinbase and boot contract transfer for the first (and only the first) block of a tenure
13510+
/// - ensure liquidity is updated accordingly to SIP-031
13511+
#[test]
13512+
#[ignore]
13513+
#[serial]
13514+
fn test_sip_031_last_phase_coinbase_matches_activation() {
13515+
if env::var("BITCOIND_TEST") != Ok("1".into()) {
13516+
return;
13517+
}
13518+
13519+
let (mut naka_conf, _miner_account) = naka_neon_integration_conf(None);
13520+
naka_conf.node.pox_sync_sample_secs = 180;
13521+
naka_conf.burnchain.max_rbf = 10_000_000;
13522+
13523+
let sender_sk = Secp256k1PrivateKey::random();
13524+
let sender_signer_sk = Secp256k1PrivateKey::random();
13525+
let sender_signer_addr = tests::to_addr(&sender_signer_sk);
13526+
let mut signers = TestSigners::new(vec![sender_signer_sk]);
13527+
// let's assume funds for 200 tenures
13528+
let tenure_count = 200;
13529+
let inter_blocks_per_tenure = 9;
13530+
// setup sender + recipient for some test stx transfers
13531+
// these are necessary for the interim blocks to get mined at all
13532+
let sender_addr = tests::to_addr(&sender_sk);
13533+
let send_amt = 100;
13534+
let send_fee = 180;
13535+
naka_conf.add_initial_balance(
13536+
PrincipalData::from(sender_addr).to_string(),
13537+
(send_amt + send_fee) * tenure_count * inter_blocks_per_tenure,
13538+
);
13539+
naka_conf.add_initial_balance(PrincipalData::from(sender_signer_addr).to_string(), 100000);
13540+
let stacker_sk = setup_stacker(&mut naka_conf);
13541+
13542+
let epoch32_start_height =
13543+
naka_conf.burnchain.epochs.clone().unwrap()[StacksEpochId::Epoch32].start_height;
13544+
13545+
set_test_sip_031_emission_schedule(Some(vec![SIP031EmissionInterval {
13546+
amount: 100_000,
13547+
start_height: epoch32_start_height,
13548+
}]));
13549+
13550+
test_observer::spawn();
13551+
test_observer::register_any(&mut naka_conf);
13552+
13553+
let mut btcd_controller = BitcoinCoreController::new(naka_conf.clone());
13554+
btcd_controller
13555+
.start_bitcoind()
13556+
.expect("Failed starting bitcoind");
13557+
let mut btc_regtest_controller = BitcoinRegtestController::new(naka_conf.clone(), None);
13558+
btc_regtest_controller.bootstrap_chain(201);
13559+
13560+
let mut run_loop = boot_nakamoto::BootRunLoop::new(naka_conf.clone()).unwrap();
13561+
let run_loop_stopper = run_loop.get_termination_switch();
13562+
let Counters {
13563+
blocks_processed,
13564+
naka_submitted_commits: commits_submitted,
13565+
..
13566+
} = run_loop.counters();
13567+
let counters = run_loop.counters();
13568+
13569+
let coord_channel = run_loop.coordinator_channels();
13570+
13571+
let run_loop_thread = thread::Builder::new()
13572+
.name("run_loop".into())
13573+
.spawn(move || run_loop.start(None, 0))
13574+
.unwrap();
13575+
wait_for_runloop(&blocks_processed);
13576+
boot_to_epoch_3(
13577+
&naka_conf,
13578+
&blocks_processed,
13579+
&[stacker_sk],
13580+
&[sender_signer_sk],
13581+
&mut Some(&mut signers),
13582+
&mut btc_regtest_controller,
13583+
);
13584+
13585+
info!("Bootstrapped to Epoch-3.0 boundary, starting nakamoto miner");
13586+
13587+
let burnchain = naka_conf.get_burnchain();
13588+
let sortdb = burnchain.open_sortition_db(true).unwrap();
13589+
let (mut chainstate, _) = StacksChainState::open(
13590+
naka_conf.is_mainnet(),
13591+
naka_conf.burnchain.chain_id,
13592+
&naka_conf.get_chainstate_path_str(),
13593+
None,
13594+
)
13595+
.unwrap();
13596+
13597+
info!("Nakamoto miner started...");
13598+
blind_signer(&naka_conf, &signers, &counters);
13599+
13600+
wait_for_first_naka_block_commit(60, &commits_submitted);
13601+
13602+
// mine until epoch 3.2 height
13603+
loop {
13604+
let commits_before = commits_submitted.load(Ordering::SeqCst);
13605+
next_block_and_process_new_stacks_block(&mut btc_regtest_controller, 60, &coord_channel)
13606+
.unwrap();
13607+
wait_for(20, || {
13608+
Ok(commits_submitted.load(Ordering::SeqCst) > commits_before)
13609+
})
13610+
.unwrap();
13611+
13612+
let node_info = get_chain_info_opt(&naka_conf).unwrap();
13613+
if node_info.burn_block_height >= epoch32_start_height {
13614+
break;
13615+
}
13616+
}
13617+
13618+
info!(
13619+
"Nakamoto miner has advanced to bitcoin height {}",
13620+
get_chain_info_opt(&naka_conf).unwrap().burn_block_height
13621+
);
13622+
13623+
let latest_stacks_block_id = get_latest_block_proposal(&naka_conf, &sortdb)
13624+
.unwrap()
13625+
.0
13626+
.block_id();
13627+
13628+
// check if sip-031 boot contract has a balance of 200_000_000 STX + coinbase-mint-and-transfer
13629+
let sip_031_boot_contract_balance = chainstate.with_read_only_clarity_tx(
13630+
&sortdb
13631+
.index_handle_at_block(&chainstate, &latest_stacks_block_id)
13632+
.unwrap(),
13633+
&latest_stacks_block_id,
13634+
|conn| {
13635+
conn.with_clarity_db_readonly(|db| {
13636+
db.get_account_stx_balance(&PrincipalData::Contract(boot_code_id(
13637+
SIP_031_NAME,
13638+
naka_conf.is_mainnet(),
13639+
)))
13640+
})
13641+
},
13642+
);
13643+
13644+
assert_eq!(
13645+
sip_031_boot_contract_balance,
13646+
Some(Ok(STXBalance::Unlocked {
13647+
amount: SIP_031_INITIAL_MINT + 100_000
13648+
}))
13649+
);
13650+
13651+
// check the mint event has been attached to the coinbase
13652+
for block in test_observer::get_blocks() {
13653+
let burn_block_height = block.get("burn_block_height").unwrap().as_u64().unwrap();
13654+
13655+
if burn_block_height == epoch32_start_height {
13656+
// check for mint event for the SIP-031 coinbase minting events (activation mint and coinbase one)
13657+
let events = block.get("events").unwrap().as_array().unwrap();
13658+
for event in events {
13659+
if let Some(mint_event) = event.get("stx_mint_event") {
13660+
if mint_event.get("recipient").unwrap().as_str().unwrap()
13661+
== boot_code_id(SIP_031_NAME, naka_conf.is_mainnet()).to_string()
13662+
{
13663+
let minted_amount = mint_event
13664+
.get("amount")
13665+
.unwrap()
13666+
.as_str()
13667+
.unwrap()
13668+
.parse::<u128>()
13669+
.unwrap();
13670+
if minted_amount == SIP_031_INITIAL_MINT {
13671+
let boot_contract_deploy_tx = block
13672+
.get("transactions")
13673+
.unwrap()
13674+
.as_array()
13675+
.unwrap()
13676+
.get(0)
13677+
.unwrap()
13678+
.get("txid")
13679+
.unwrap()
13680+
.as_str()
13681+
.unwrap();
13682+
13683+
// check the event txid is mapped to the boot_contract_deploy
13684+
assert_eq!(
13685+
event.get("txid").unwrap().as_str().unwrap(),
13686+
boot_contract_deploy_tx
13687+
);
13688+
} else {
13689+
let coinbase_txid = block
13690+
.get("transactions")
13691+
.unwrap()
13692+
.as_array()
13693+
.unwrap()
13694+
.get(2)
13695+
.unwrap()
13696+
.get("txid")
13697+
.unwrap()
13698+
.as_str()
13699+
.unwrap();
13700+
13701+
// check the event txid is mapped to the coinbase
13702+
assert_eq!(event.get("txid").unwrap().as_str().unwrap(), coinbase_txid);
13703+
}
13704+
}
13705+
}
13706+
}
13707+
}
13708+
}
13709+
13710+
// get current liquidity
13711+
let sip_031_current_liquid_ustx_before = chainstate
13712+
.with_read_only_clarity_tx(
13713+
&sortdb
13714+
.index_handle_at_block(&chainstate, &latest_stacks_block_id)
13715+
.unwrap(),
13716+
&latest_stacks_block_id,
13717+
|conn| conn.with_clarity_db_readonly(|db| db.get_total_liquid_ustx().unwrap()),
13718+
)
13719+
.unwrap();
13720+
13721+
assert!(sip_031_current_liquid_ustx_before >= SIP_031_INITIAL_MINT + 100_000);
13722+
13723+
let http_origin = format!("http://{}", &naka_conf.node.rpc_bind);
13724+
13725+
let mut sender_nonce = 0;
13726+
// 1 more tenures (with 5 stacks blocks)
13727+
let commits_before = commits_submitted.load(Ordering::SeqCst);
13728+
next_block_and_process_new_stacks_blocks(
13729+
&mut btc_regtest_controller,
13730+
5,
13731+
60,
13732+
&coord_channel,
13733+
|| {
13734+
let transfer_tx = make_stacks_transfer_serialized(
13735+
&sender_sk,
13736+
sender_nonce,
13737+
send_fee,
13738+
naka_conf.burnchain.chain_id,
13739+
&PrincipalData::from(sender_signer_addr),
13740+
send_amt,
13741+
);
13742+
submit_tx(&http_origin, &transfer_tx);
13743+
sender_nonce += 1;
13744+
Ok(())
13745+
},
13746+
)
13747+
.unwrap();
13748+
wait_for(20, || {
13749+
Ok(commits_submitted.load(Ordering::SeqCst) > commits_before)
13750+
})
13751+
.unwrap();
13752+
13753+
let mut total_minted_and_transferred: u128 = 0;
13754+
13755+
for block in test_observer::get_blocks() {
13756+
let burn_block_height = block.get("burn_block_height").unwrap().as_u64().unwrap();
13757+
13758+
let sip_031_mint_and_transfer_amount =
13759+
SIP031EmissionInterval::get_sip_031_emission_at_height(
13760+
burn_block_height,
13761+
naka_conf.is_mainnet(),
13762+
);
13763+
13764+
// check for mint events for the SIP-031 boot contract (excluding the activation one)
13765+
let events = block.get("events").unwrap().as_array().unwrap();
13766+
for event in events {
13767+
if let Some(mint_event) = event.get("stx_mint_event") {
13768+
if mint_event.get("recipient").unwrap().as_str().unwrap()
13769+
== boot_code_id(SIP_031_NAME, naka_conf.is_mainnet()).to_string()
13770+
&& burn_block_height != epoch32_start_height
13771+
{
13772+
let minted_amount = mint_event
13773+
.get("amount")
13774+
.unwrap()
13775+
.as_str()
13776+
.unwrap()
13777+
.parse::<u128>()
13778+
.unwrap();
13779+
assert_eq!(sip_031_mint_and_transfer_amount, minted_amount);
13780+
total_minted_and_transferred += minted_amount;
13781+
13782+
let coinbase_txid = block
13783+
.get("transactions")
13784+
.unwrap()
13785+
.as_array()
13786+
.unwrap()
13787+
.get(1)
13788+
.unwrap()
13789+
.get("txid")
13790+
.unwrap()
13791+
.as_str()
13792+
.unwrap();
13793+
13794+
// check the event txid is mapped to the coinbase
13795+
assert_eq!(event.get("txid").unwrap().as_str().unwrap(), coinbase_txid);
13796+
}
13797+
}
13798+
}
13799+
}
13800+
13801+
// 100_000
13802+
assert_eq!(total_minted_and_transferred, 100_000);
13803+
13804+
let latest_stacks_block_id = get_latest_block_proposal(&naka_conf, &sortdb)
13805+
.unwrap()
13806+
.0
13807+
.block_id();
13808+
13809+
// get sip-031 boot contract balance (will be checked for 200_000_000 STX + 100_000 + total_minted_and_transferred)
13810+
let sip_031_boot_contract_balance = chainstate.with_read_only_clarity_tx(
13811+
&sortdb
13812+
.index_handle_at_block(&chainstate, &latest_stacks_block_id)
13813+
.unwrap(),
13814+
&latest_stacks_block_id,
13815+
|conn| {
13816+
conn.with_clarity_db_readonly(|db| {
13817+
db.get_account_stx_balance(&PrincipalData::Contract(boot_code_id(
13818+
SIP_031_NAME,
13819+
naka_conf.is_mainnet(),
13820+
)))
13821+
})
13822+
},
13823+
);
13824+
13825+
assert_eq!(
13826+
sip_031_boot_contract_balance,
13827+
Some(Ok(STXBalance::Unlocked {
13828+
amount: SIP_031_INITIAL_MINT + 100_000 + total_minted_and_transferred
13829+
}))
13830+
);
13831+
13832+
// get current liquidity
13833+
let sip_031_current_liquid_ustx = chainstate
13834+
.with_read_only_clarity_tx(
13835+
&sortdb
13836+
.index_handle_at_block(&chainstate, &latest_stacks_block_id)
13837+
.unwrap(),
13838+
&latest_stacks_block_id,
13839+
|conn| conn.with_clarity_db_readonly(|db| db.get_total_liquid_ustx().unwrap()),
13840+
)
13841+
.unwrap();
13842+
13843+
// check liquidity has been updated accordingly
13844+
assert!(
13845+
sip_031_current_liquid_ustx - sip_031_current_liquid_ustx_before
13846+
>= total_minted_and_transferred
13847+
);
13848+
13849+
set_test_sip_031_emission_schedule(None);
13850+
13851+
coord_channel
13852+
.lock()
13853+
.expect("Mutex poisoned")
13854+
.stop_chains_coordinator();
13855+
run_loop_stopper.store(false, Ordering::SeqCst);
13856+
13857+
run_loop_thread.join().unwrap();
13858+
}

stacks-signer/CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to the versioning scheme outlined in the [README.md](README.md).
77

8-
## [Unreleased]
8+
## [3.2.0.0.0.0]
99

1010
### Added
1111

0 commit comments

Comments
 (0)