Skip to content

Commit 738db8d

Browse files
committed
feat: signer test for naka blocks after TenureExtend
1 parent 98f187c commit 738db8d

File tree

3 files changed

+133
-6
lines changed

3 files changed

+133
-6
lines changed

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,7 @@ impl BlockMinerThread {
383383
"block_height" => new_block.header.chain_length,
384384
"consensus_hash" => %new_block.header.consensus_hash,
385385
);
386-
return Err(e);
386+
continue;
387387
}
388388
_ => {
389389
error!("Error while gathering signatures: {e:?}. Will try mining again.";
@@ -1247,7 +1247,9 @@ impl ParentStacksBlockInfo {
12471247
let burn_chain_tip = SortitionDB::get_canonical_burn_chain_tip(burn_db.conn())
12481248
.expect("FATAL: failed to query sortition DB for canonical burn chain tip");
12491249

1250-
if burn_chain_tip.consensus_hash != check_burn_block.consensus_hash {
1250+
if burn_chain_tip.consensus_hash != check_burn_block.consensus_hash
1251+
&& burn_chain_tip.sortition_id != check_burn_block.sortition_id
1252+
{
12511253
info!(
12521254
"New canonical burn chain tip detected. Will not try to mine.";
12531255
"new_consensus_hash" => %burn_chain_tip.consensus_hash,

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -251,11 +251,13 @@ impl SignCoordinator {
251251
}
252252

253253
/// Check if the tenure needs to change
254-
fn check_burn_tip_changed(sortdb: &SortitionDB, consensus_hash: &ConsensusHash) -> bool {
254+
fn check_burn_tip_changed(sortdb: &SortitionDB, burn_block: &BlockSnapshot) -> bool {
255255
let cur_burn_chain_tip = SortitionDB::get_canonical_burn_chain_tip(sortdb.conn())
256256
.expect("FATAL: failed to query sortition DB for canonical burn chain tip");
257257

258-
if cur_burn_chain_tip.consensus_hash != *consensus_hash {
258+
if cur_burn_chain_tip.consensus_hash != burn_block.consensus_hash
259+
&& cur_burn_chain_tip.sortition_id != burn_block.sortition_id
260+
{
259261
info!("SignCoordinator: Cancel signature aggregation; burnchain tip has changed");
260262
true
261263
} else {
@@ -365,7 +367,7 @@ impl SignCoordinator {
365367
return Ok(stored_block.header.signer_signature);
366368
}
367369

368-
if Self::check_burn_tip_changed(&sortdb, &burn_tip.consensus_hash) {
370+
if Self::check_burn_tip_changed(&sortdb, &burn_tip) {
369371
debug!("SignCoordinator: Exiting due to new burnchain tip");
370372
return Err(NakamotoNodeError::BurnchainTipChanged);
371373
}

testnet/stacks-node/src/tests/signer/v0.rs

Lines changed: 124 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ use stacks::chainstate::nakamoto::{NakamotoBlock, NakamotoBlockHeader, NakamotoC
3434
use stacks::chainstate::stacks::address::PoxAddress;
3535
use stacks::chainstate::stacks::boot::MINERS_NAME;
3636
use stacks::chainstate::stacks::db::{StacksBlockHeaderTypes, StacksChainState, StacksHeaderInfo};
37+
use stacks::chainstate::stacks::{StacksTransaction, TenureChangeCause, TransactionPayload};
3738
use stacks::codec::StacksMessageCodec;
3839
use stacks::core::{StacksEpochId, CHAIN_ID_TESTNET};
3940
use stacks::libstackerdb::StackerDBChunkData;
@@ -42,7 +43,7 @@ use stacks::net::api::postblock_proposal::{ValidateRejectCode, TEST_VALIDATE_STA
4243
use stacks::net::relay::fault_injection::set_ignore_block;
4344
use stacks::types::chainstate::{StacksAddress, StacksBlockId, StacksPrivateKey, StacksPublicKey};
4445
use stacks::types::PublicKey;
45-
use stacks::util::hash::MerkleHashFunc;
46+
use stacks::util::hash::{hex_bytes, MerkleHashFunc};
4647
use stacks::util::secp256k1::{Secp256k1PrivateKey, Secp256k1PublicKey};
4748
use stacks::util_lib::boot::boot_code_id;
4849
use stacks::util_lib::signed_structured_data::pox4::{
@@ -4942,6 +4943,128 @@ fn miner_recovers_when_broadcast_block_delay_across_tenures_occurs() {
49424943
assert_ne!(block_n_2, block_n);
49434944
}
49444945

4946+
#[test]
4947+
#[ignore]
4948+
/// Test that we can mine a tenure extend and then continue mining afterwards.
4949+
fn continue_after_tenure_extend() {
4950+
if env::var("BITCOIND_TEST") != Ok("1".into()) {
4951+
return;
4952+
}
4953+
4954+
tracing_subscriber::registry()
4955+
.with(fmt::layer())
4956+
.with(EnvFilter::from_default_env())
4957+
.init();
4958+
4959+
info!("------------------------- Test Setup -------------------------");
4960+
let num_signers = 5;
4961+
let sender_sk = Secp256k1PrivateKey::new();
4962+
let sender_addr = tests::to_addr(&sender_sk);
4963+
let recipient = PrincipalData::from(StacksAddress::burn_address(false));
4964+
let send_amt = 100;
4965+
let send_fee = 180;
4966+
let mut signer_test: SignerTest<SpawnedSigner> = SignerTest::new(
4967+
num_signers,
4968+
vec![(sender_addr.clone(), (send_amt + send_fee) * 5)],
4969+
);
4970+
let timeout = Duration::from_secs(200);
4971+
let coord_channel = signer_test.running_nodes.coord_channel.clone();
4972+
let http_origin = format!("http://{}", &signer_test.running_nodes.conf.node.rpc_bind);
4973+
4974+
signer_test.boot_to_epoch_3();
4975+
4976+
info!("------------------------- Mine Normal Tenure -------------------------");
4977+
signer_test.mine_and_verify_confirmed_naka_block(timeout, num_signers);
4978+
4979+
info!("------------------------- Extend Tenure -------------------------");
4980+
signer_test
4981+
.running_nodes
4982+
.nakamoto_test_skip_commit_op
4983+
.0
4984+
.lock()
4985+
.unwrap()
4986+
.replace(true);
4987+
4988+
// It's possible that we have a pending block commit already.
4989+
// Mine two BTC blocks to "flush" this commit.
4990+
4991+
let mut blocks_processed_before = coord_channel
4992+
.lock()
4993+
.expect("Mutex poisoned")
4994+
.get_stacks_blocks_processed();
4995+
4996+
for i in 0..2 {
4997+
info!(
4998+
"------------- After pausing commits, triggering 2 BTC blocks: ({} of 2) -----------",
4999+
i + 1
5000+
);
5001+
5002+
blocks_processed_before = coord_channel
5003+
.lock()
5004+
.expect("Mutex poisoned")
5005+
.get_stacks_blocks_processed();
5006+
signer_test
5007+
.running_nodes
5008+
.btc_regtest_controller
5009+
.build_next_block(1);
5010+
5011+
wait_for(60, || {
5012+
let blocks_processed_after = coord_channel
5013+
.lock()
5014+
.expect("Mutex poisoned")
5015+
.get_stacks_blocks_processed();
5016+
Ok(blocks_processed_after > blocks_processed_before)
5017+
})
5018+
.expect("Timed out waiting for tenure extend block");
5019+
}
5020+
5021+
// The last block should have a single instruction in it, the tenure extend
5022+
let blocks = test_observer::get_blocks();
5023+
let last_block = blocks.last().unwrap();
5024+
let transactions = last_block["transactions"].as_array().unwrap();
5025+
let tx = transactions.first().expect("No transactions in block");
5026+
let raw_tx = tx["raw_tx"].as_str().unwrap();
5027+
let tx_bytes = hex_bytes(&raw_tx[2..]).unwrap();
5028+
let parsed = StacksTransaction::consensus_deserialize(&mut &tx_bytes[..]).unwrap();
5029+
match &parsed.payload {
5030+
TransactionPayload::TenureChange(payload)
5031+
if payload.cause == TenureChangeCause::Extended => {}
5032+
_ => panic!("Expected tenure extend transaction, got {:?}", parsed),
5033+
};
5034+
5035+
// Verify that the miner can continue mining in the tenure with the tenure extend
5036+
info!("------------------------- Mine After Tenure Extend -------------------------");
5037+
let mut sender_nonce = 0;
5038+
blocks_processed_before = coord_channel
5039+
.lock()
5040+
.expect("Mutex poisoned")
5041+
.get_stacks_blocks_processed();
5042+
for _ in 0..5 {
5043+
// submit a tx so that the miner will mine an extra block
5044+
let transfer_tx =
5045+
make_stacks_transfer(&sender_sk, sender_nonce, send_fee, &recipient, send_amt);
5046+
sender_nonce += 1;
5047+
submit_tx(&http_origin, &transfer_tx);
5048+
5049+
info!("Submitted transfer tx and waiting for block proposal");
5050+
wait_for(30, || {
5051+
let blocks_processed_after = coord_channel
5052+
.lock()
5053+
.expect("Mutex poisoned")
5054+
.get_stacks_blocks_processed();
5055+
Ok(blocks_processed_after > blocks_processed_before)
5056+
})
5057+
.expect("Timed out waiting for block proposal");
5058+
blocks_processed_before = coord_channel
5059+
.lock()
5060+
.expect("Mutex poisoned")
5061+
.get_stacks_blocks_processed();
5062+
info!("Block {blocks_processed_before} processed, continuing");
5063+
}
5064+
5065+
signer_test.shutdown();
5066+
}
5067+
49455068
#[test]
49465069
#[ignore]
49475070
/// Test that signers can successfully sign a block proposal in the 0th tenure of a reward cycle

0 commit comments

Comments
 (0)