Skip to content

Commit f87e42a

Browse files
committed
ensure same behaviour from miner and follower
1 parent 7cc5d26 commit f87e42a

File tree

7 files changed

+285
-48
lines changed

7 files changed

+285
-48
lines changed

stacks-node/src/tests/nakamoto_integrations.rs

Lines changed: 195 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,10 @@ use stacks::chainstate::nakamoto::coordinator::{load_nakamoto_reward_set, TEST_C
4848
use stacks::chainstate::nakamoto::miner::NakamotoBlockBuilder;
4949
use stacks::chainstate::nakamoto::shadow::shadow_chainstate_repair;
5050
use stacks::chainstate::nakamoto::test_signers::TestSigners;
51-
use stacks::chainstate::nakamoto::{NakamotoBlock, NakamotoBlockHeader, NakamotoChainState};
51+
use stacks::chainstate::nakamoto::{
52+
set_test_sip_031_emission_schedule, NakamotoBlock, NakamotoBlockHeader, NakamotoChainState,
53+
SIP031EmissionInterval,
54+
};
5255
use stacks::chainstate::stacks::address::{PoxAddress, StacksAddressExtensions};
5356
use stacks::chainstate::stacks::boot::{
5457
MINERS_NAME, SIGNERS_VOTING_FUNCTION_NAME, SIGNERS_VOTING_NAME,
@@ -3075,6 +3078,7 @@ fn block_proposal_api_endpoint() {
30753078
let mut miner_tenure_info = builder
30763079
.load_tenure_info(&mut chainstate, &burn_dbconn, tenure_cause)
30773080
.unwrap();
3081+
let burn_chain_height = miner_tenure_info.burn_tip_height;
30783082
let mut tenure_tx = builder
30793083
.tenure_begin(&burn_dbconn, &mut miner_tenure_info)
30803084
.unwrap();
@@ -3103,7 +3107,7 @@ fn block_proposal_api_endpoint() {
31033107
matches!(res, TransactionResult::Success(..)),
31043108
"Transaction failed"
31053109
);
3106-
builder.mine_nakamoto_block(&mut tenure_tx)
3110+
builder.mine_nakamoto_block(&mut tenure_tx, burn_chain_height)
31073111
};
31083112

31093113
// Construct a valid proposal. Make alterations to this to test failure cases
@@ -12918,3 +12922,192 @@ fn test_sip_031_activation() {
1291812922

1291912923
run_loop_thread.join().unwrap();
1292012924
}
12925+
12926+
/// Test SIP-031 last phase per-tenure-mint-and-transfer
12927+
///
12928+
/// - check epoch 3.2 is active
12929+
/// - check minting event and boot contract transfer for the first (and only the first) block of a tenure
12930+
#[test]
12931+
#[ignore]
12932+
fn test_sip_031_last_phase() {
12933+
if env::var("BITCOIND_TEST") != Ok("1".into()) {
12934+
return;
12935+
}
12936+
12937+
let (mut naka_conf, _miner_account) = naka_neon_integration_conf(None);
12938+
naka_conf.node.pox_sync_sample_secs = 180;
12939+
naka_conf.burnchain.max_rbf = 10_000_000;
12940+
12941+
let sender_sk = Secp256k1PrivateKey::random();
12942+
let sender_signer_sk = Secp256k1PrivateKey::random();
12943+
let sender_signer_addr = tests::to_addr(&sender_signer_sk);
12944+
let mut signers = TestSigners::new(vec![sender_signer_sk]);
12945+
let tenure_count = 5;
12946+
let inter_blocks_per_tenure = 9;
12947+
// setup sender + recipient for some test stx transfers
12948+
// these are necessary for the interim blocks to get mined at all
12949+
let sender_addr = tests::to_addr(&sender_sk);
12950+
let send_amt = 100;
12951+
let send_fee = 180;
12952+
naka_conf.add_initial_balance(
12953+
PrincipalData::from(sender_addr).to_string(),
12954+
(send_amt + send_fee) * tenure_count * inter_blocks_per_tenure,
12955+
);
12956+
naka_conf.add_initial_balance(PrincipalData::from(sender_signer_addr).to_string(), 100000);
12957+
let stacker_sk = setup_stacker(&mut naka_conf);
12958+
12959+
let epoch32_start_height =
12960+
naka_conf.burnchain.epochs.clone().unwrap()[StacksEpochId::Epoch32].start_height;
12961+
12962+
set_test_sip_031_emission_schedule(Some(vec![
12963+
SIP031EmissionInterval {
12964+
amount: 0,
12965+
start_height: epoch32_start_height + 40,
12966+
},
12967+
SIP031EmissionInterval {
12968+
amount: 300_000,
12969+
start_height: epoch32_start_height + 30,
12970+
},
12971+
SIP031EmissionInterval {
12972+
amount: 200_000,
12973+
start_height: epoch32_start_height + 20,
12974+
},
12975+
SIP031EmissionInterval {
12976+
amount: 100_000,
12977+
start_height: epoch32_start_height + 10,
12978+
},
12979+
]));
12980+
12981+
test_observer::spawn();
12982+
test_observer::register_any(&mut naka_conf);
12983+
12984+
let mut btcd_controller = BitcoinCoreController::new(naka_conf.clone());
12985+
btcd_controller
12986+
.start_bitcoind()
12987+
.expect("Failed starting bitcoind");
12988+
let mut btc_regtest_controller = BitcoinRegtestController::new(naka_conf.clone(), None);
12989+
btc_regtest_controller.bootstrap_chain(201);
12990+
12991+
let mut run_loop = boot_nakamoto::BootRunLoop::new(naka_conf.clone()).unwrap();
12992+
let run_loop_stopper = run_loop.get_termination_switch();
12993+
let Counters {
12994+
blocks_processed,
12995+
naka_submitted_commits: commits_submitted,
12996+
..
12997+
} = run_loop.counters();
12998+
let counters = run_loop.counters();
12999+
13000+
let coord_channel = run_loop.coordinator_channels();
13001+
13002+
let run_loop_thread = thread::Builder::new()
13003+
.name("run_loop".into())
13004+
.spawn(move || run_loop.start(None, 0))
13005+
.unwrap();
13006+
wait_for_runloop(&blocks_processed);
13007+
boot_to_epoch_3(
13008+
&naka_conf,
13009+
&blocks_processed,
13010+
&[stacker_sk],
13011+
&[sender_signer_sk],
13012+
&mut Some(&mut signers),
13013+
&mut btc_regtest_controller,
13014+
);
13015+
13016+
info!("Bootstrapped to Epoch-3.0 boundary, starting nakamoto miner");
13017+
13018+
let burnchain = naka_conf.get_burnchain();
13019+
let sortdb = burnchain.open_sortition_db(true).unwrap();
13020+
let (mut chainstate, _) = StacksChainState::open(
13021+
naka_conf.is_mainnet(),
13022+
naka_conf.burnchain.chain_id,
13023+
&naka_conf.get_chainstate_path_str(),
13024+
None,
13025+
)
13026+
.unwrap();
13027+
13028+
info!("Nakamoto miner started...");
13029+
blind_signer(&naka_conf, &signers, &counters);
13030+
13031+
wait_for_first_naka_block_commit(60, &commits_submitted);
13032+
13033+
// mine until epoch 3.2 height
13034+
loop {
13035+
let commits_before = commits_submitted.load(Ordering::SeqCst);
13036+
next_block_and_process_new_stacks_block(&mut btc_regtest_controller, 60, &coord_channel)
13037+
.unwrap();
13038+
wait_for(20, || {
13039+
Ok(commits_submitted.load(Ordering::SeqCst) > commits_before)
13040+
})
13041+
.unwrap();
13042+
13043+
let node_info = get_chain_info_opt(&naka_conf).unwrap();
13044+
if node_info.burn_block_height >= epoch32_start_height {
13045+
break;
13046+
}
13047+
}
13048+
13049+
info!(
13050+
"Nakamoto miner has advanced to bitcoin height {}",
13051+
get_chain_info_opt(&naka_conf).unwrap().burn_block_height
13052+
);
13053+
13054+
let latest_stacks_block_id = get_latest_block_proposal(&naka_conf, &sortdb)
13055+
.unwrap()
13056+
.0
13057+
.block_id();
13058+
13059+
// check if sip-031 boot contract has a balance of 200_000_000 STX
13060+
let sip_031_boot_contract_balance = chainstate.with_read_only_clarity_tx(
13061+
&sortdb
13062+
.index_handle_at_block(&chainstate, &latest_stacks_block_id)
13063+
.unwrap(),
13064+
&latest_stacks_block_id,
13065+
|conn| {
13066+
conn.with_clarity_db_readonly(|db| {
13067+
db.get_account_stx_balance(&PrincipalData::Contract(boot_code_id(
13068+
SIP_031_NAME,
13069+
naka_conf.is_mainnet(),
13070+
)))
13071+
})
13072+
},
13073+
);
13074+
13075+
assert_eq!(
13076+
sip_031_boot_contract_balance,
13077+
Some(Ok(STXBalance::Unlocked {
13078+
amount: SIP_031_INITIAL_MINT
13079+
}))
13080+
);
13081+
13082+
// get current liquidity
13083+
let sip_031_current_liquid_ustx = chainstate
13084+
.with_read_only_clarity_tx(
13085+
&sortdb
13086+
.index_handle_at_block(&chainstate, &latest_stacks_block_id)
13087+
.unwrap(),
13088+
&latest_stacks_block_id,
13089+
|conn| conn.with_clarity_db_readonly(|db| db.get_total_liquid_ustx().unwrap()),
13090+
)
13091+
.unwrap();
13092+
13093+
// 50 more tenures...
13094+
for _ in 0..50 {
13095+
let commits_before = commits_submitted.load(Ordering::SeqCst);
13096+
next_block_and_process_new_stacks_block(&mut btc_regtest_controller, 60, &coord_channel)
13097+
.unwrap();
13098+
wait_for(20, || {
13099+
Ok(commits_submitted.load(Ordering::SeqCst) > commits_before)
13100+
})
13101+
.unwrap();
13102+
13103+
let node_info = get_chain_info_opt(&naka_conf).unwrap();
13104+
}
13105+
13106+
coord_channel
13107+
.lock()
13108+
.expect("Mutex poisoned")
13109+
.stop_chains_coordinator();
13110+
run_loop_stopper.store(false, Ordering::SeqCst);
13111+
13112+
run_loop_thread.join().unwrap();
13113+
}

stackslib/src/chainstate/nakamoto/miner.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -478,9 +478,20 @@ impl NakamotoBlockBuilder {
478478
}
479479

480480
/// Finish building the Nakamoto block
481-
pub fn mine_nakamoto_block(&mut self, clarity_tx: &mut ClarityTx) -> NakamotoBlock {
481+
pub fn mine_nakamoto_block(
482+
&mut self,
483+
clarity_tx: &mut ClarityTx,
484+
burn_block_height: u32,
485+
) -> NakamotoBlock {
482486
NakamotoChainState::finish_block(clarity_tx, self.matured_miner_rewards_opt.as_ref())
483487
.expect("FATAL: call to `finish_block` failed");
488+
// if coinbase_tx is defined, we are at the start of a new tenure
489+
if self.coinbase_tx.is_some() {
490+
NakamotoChainState::sip_031_mint_and_transfer_on_new_tenure(
491+
clarity_tx,
492+
burn_block_height,
493+
);
494+
}
484495
self.finalize_block(clarity_tx)
485496
}
486497

@@ -530,6 +541,7 @@ impl NakamotoBlockBuilder {
530541

531542
let mut miner_tenure_info =
532543
builder.load_tenure_info(&mut chainstate, burn_dbconn, tenure_info.cause())?;
544+
let burn_chain_height = miner_tenure_info.burn_tip_height;
533545
let mut tenure_tx = builder.tenure_begin(burn_dbconn, &mut miner_tenure_info)?;
534546

535547
let tenure_budget = tenure_tx
@@ -607,7 +619,7 @@ impl NakamotoBlockBuilder {
607619
}
608620

609621
// save the block so we can build microblocks off of it
610-
let block = builder.mine_nakamoto_block(&mut tenure_tx);
622+
let block = builder.mine_nakamoto_block(&mut tenure_tx, burn_chain_height);
611623
let tenure_size = builder.bytes_so_far;
612624
let tenure_consumed = builder.tenure_finish(tenure_tx)?;
613625

0 commit comments

Comments
 (0)