Skip to content

Commit d9c002c

Browse files
committed
CRC: ensure that the tenure change transaction and contract call get mined in the same stacks block
Signed-off-by: Jacinta Ferrant <[email protected]>
1 parent 7c073fa commit d9c002c

File tree

2 files changed

+67
-7
lines changed

2 files changed

+67
-7
lines changed

testnet/stacks-node/src/nakamoto_node/miner.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ use crate::neon_node;
6060
use crate::run_loop::nakamoto::Globals;
6161
use crate::run_loop::RegisteredKey;
6262

63+
#[cfg(test)]
64+
pub static TEST_MINE_STALL: std::sync::Mutex<Option<bool>> = std::sync::Mutex::new(None);
6365
#[cfg(test)]
6466
pub static TEST_BROADCAST_STALL: std::sync::Mutex<Option<bool>> = std::sync::Mutex::new(None);
6567
#[cfg(test)]
@@ -291,6 +293,15 @@ impl BlockMinerThread {
291293
let mut attempts = 0;
292294
// now, actually run this tenure
293295
loop {
296+
#[cfg(test)]
297+
if *TEST_MINE_STALL.lock().unwrap() == Some(true) {
298+
// Do an extra check just so we don't log EVERY time.
299+
warn!("Mining is stalled due to testing directive");
300+
while *TEST_MINE_STALL.lock().unwrap() == Some(true) {
301+
std::thread::sleep(std::time::Duration::from_millis(10));
302+
}
303+
warn!("Mining is no longer stalled due to testing directive. Continuing...");
304+
}
294305
let new_block = loop {
295306
// If we're mock mining, we may not have processed the block that the
296307
// actual tenure winner committed to yet. So, before attempting to

testnet/stacks-node/src/tests/nakamoto_integrations.rs

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,9 @@ use wsts::net::Message;
9494

9595
use super::bitcoin_regtest::BitcoinCoreController;
9696
use crate::config::{EventKeyType, EventObserverConfig, InitialBalance};
97-
use crate::nakamoto_node::miner::{TEST_BLOCK_ANNOUNCE_STALL, TEST_BROADCAST_STALL};
97+
use crate::nakamoto_node::miner::{
98+
TEST_BLOCK_ANNOUNCE_STALL, TEST_BROADCAST_STALL, TEST_MINE_STALL,
99+
};
98100
use crate::neon::{Counters, RunLoopCounter};
99101
use crate::operations::BurnchainOpSigner;
100102
use crate::run_loop::boot_nakamoto;
@@ -5185,21 +5187,68 @@ fn clarity_burn_state() {
51855187
vec![&Value::UInt(burn_block_height)],
51865188
);
51875189
result.expect_result_ok().expect("Read-only call failed");
5190+
5191+
// Pause mining to prevent the stacks block from being mined before the tenure change is processed
5192+
TEST_MINE_STALL.lock().unwrap().replace(true);
5193+
// Submit a tx for the next block (the next block will be a new tenure, so the burn block height will increment)
5194+
let call_tx = tests::make_contract_call(
5195+
&sender_sk,
5196+
sender_nonce,
5197+
tx_fee,
5198+
&sender_addr,
5199+
contract_name,
5200+
"bar",
5201+
&[Value::UInt(burn_block_height + 1)],
5202+
);
5203+
sender_nonce += 1;
5204+
submit_tx(&http_origin, &call_tx);
51885205
}
51895206

51905207
let commits_before = commits_submitted.load(Ordering::SeqCst);
5191-
next_block_and_mine_commit(
5192-
&mut btc_regtest_controller,
5193-
60,
5194-
&coord_channel,
5195-
&commits_submitted,
5196-
)
5208+
let blocks_processed_before = coord_channel
5209+
.lock()
5210+
.expect("Mutex poisoned")
5211+
.get_stacks_blocks_processed();
5212+
next_block_and(&mut btc_regtest_controller, 60, || {
5213+
Ok(commits_submitted.load(Ordering::SeqCst) > commits_before)
5214+
})
5215+
.unwrap();
5216+
TEST_MINE_STALL.lock().unwrap().replace(false);
5217+
wait_for(20, || {
5218+
Ok(coord_channel
5219+
.lock()
5220+
.expect("Mutex poisoned")
5221+
.get_stacks_blocks_processed()
5222+
> blocks_processed_before)
5223+
})
51975224
.unwrap();
51985225

51995226
let info = get_chain_info(&naka_conf);
52005227
burn_block_height = info.burn_block_height as u128;
52015228
info!("Expecting burn block height to be {}", burn_block_height);
52025229

5230+
// Assert that the contract call was successful
5231+
test_observer::get_mined_nakamoto_blocks()
5232+
.last()
5233+
.unwrap()
5234+
.tx_events
5235+
.iter()
5236+
.for_each(|event| match event {
5237+
TransactionEvent::Success(TransactionSuccessEvent { result, fee, .. }) => {
5238+
// Ignore coinbase and tenure transactions
5239+
if *fee == 0 {
5240+
return;
5241+
}
5242+
5243+
info!("Contract call result: {}", result);
5244+
result.clone().expect_result_ok().expect("Ok result");
5245+
}
5246+
_ => {
5247+
info!("Unsuccessful event: {:?}", event);
5248+
panic!("Expected a successful transaction");
5249+
}
5250+
});
5251+
52035252
// mine the interim blocks
52045253
for interim_block_ix in 0..inter_blocks_per_tenure {
52055254
info!("Mining interim block {interim_block_ix}");

0 commit comments

Comments
 (0)