Skip to content

Commit 01694b7

Browse files
committed
Merge remote-tracking branch 'origin/develop' into feat/block-info-handling
2 parents 4172711 + ce46c93 commit 01694b7

File tree

5 files changed

+182
-26
lines changed

5 files changed

+182
-26
lines changed

.github/workflows/bitcoin-tests.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ jobs:
118118
- tests::signer::v0::mine_2_nakamoto_reward_cycles
119119
- tests::signer::v0::signer_set_rollover
120120
- tests::signer::v0::signing_in_0th_tenure_of_reward_cycle
121+
- tests::signer::v0::continue_after_tenure_extend
121122
- tests::nakamoto_integrations::burn_ops_integration_test
122123
- tests::nakamoto_integrations::check_block_heights
123124
- tests::nakamoto_integrations::clarity_burn_state

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

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -531,22 +531,6 @@ impl BlockMinerThread {
531531
))
532532
})?;
533533

534-
let tip = SortitionDB::get_block_snapshot_consensus(
535-
sort_db.conn(),
536-
&new_block.header.consensus_hash,
537-
)
538-
.map_err(|e| {
539-
NakamotoNodeError::SigningCoordinatorFailure(format!(
540-
"Failed to retrieve chain tip: {:?}",
541-
e
542-
))
543-
})
544-
.and_then(|result| {
545-
result.ok_or_else(|| {
546-
NakamotoNodeError::SigningCoordinatorFailure("Failed to retrieve chain tip".into())
547-
})
548-
})?;
549-
550534
let reward_set = self.load_signer_set()?;
551535

552536
if self.config.get_node_config(false).mock_mining {
@@ -574,7 +558,7 @@ impl BlockMinerThread {
574558

575559
let signature = coordinator.run_sign_v0(
576560
new_block,
577-
&tip,
561+
&self.burn_block,
578562
&self.burnchain,
579563
&sort_db,
580564
&mut chain_state,
@@ -1201,6 +1185,7 @@ impl BlockMinerThread {
12011185
}
12021186

12031187
/// Check if the tenure needs to change -- if so, return a BurnchainTipChanged error
1188+
/// The tenure should change if there is a new burnchain tip with a valid sortition
12041189
fn check_burn_tip_changed(&self, sortdb: &SortitionDB) -> Result<(), NakamotoNodeError> {
12051190
let cur_burn_chain_tip = SortitionDB::get_canonical_burn_chain_tip(sortdb.conn())
12061191
.expect("FATAL: failed to query sortition DB for canonical burn chain tip");

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -251,11 +251,11 @@ 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 {
259259
info!("SignCoordinator: Cancel signature aggregation; burnchain tip has changed");
260260
true
261261
} else {
@@ -365,7 +365,7 @@ impl SignCoordinator {
365365
return Ok(stored_block.header.signer_signature);
366366
}
367367

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

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

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6922,10 +6922,10 @@ fn continue_tenure_extend() {
69226922
// setup sender + recipient for a test stx transfer
69236923
let sender_addr = tests::to_addr(&sender_sk);
69246924
let send_amt = 1000;
6925-
let send_fee = 100;
6925+
let send_fee = 200;
69266926
naka_conf.add_initial_balance(
69276927
PrincipalData::from(sender_addr.clone()).to_string(),
6928-
send_amt * 2 + send_fee,
6928+
(send_amt + send_fee) * 20,
69296929
);
69306930
let sender_signer_sk = Secp256k1PrivateKey::new();
69316931
let sender_signer_addr = tests::to_addr(&sender_signer_sk);
@@ -6935,6 +6935,7 @@ fn continue_tenure_extend() {
69356935
);
69366936
let recipient = PrincipalData::from(StacksAddress::burn_address(false));
69376937
let stacker_sk = setup_stacker(&mut naka_conf);
6938+
let mut transfer_nonce = 0;
69386939

69396940
test_observer::spawn();
69406941
test_observer::register_any(&mut naka_conf);
@@ -7055,7 +7056,7 @@ fn continue_tenure_extend() {
70557056
// Submit a TX
70567057
let transfer_tx = make_stacks_transfer(
70577058
&sender_sk,
7058-
0,
7059+
transfer_nonce,
70597060
send_fee,
70607061
naka_conf.burnchain.chain_id,
70617062
&recipient,
@@ -7117,6 +7118,26 @@ fn continue_tenure_extend() {
71177118
})
71187119
.unwrap();
71197120

7121+
// Mine 3 nakamoto blocks
7122+
for i in 0..3 {
7123+
info!("Triggering Nakamoto blocks after extend ({})", i + 1);
7124+
transfer_nonce += 1;
7125+
let transfer_tx = make_stacks_transfer(
7126+
&sender_sk,
7127+
transfer_nonce,
7128+
send_fee,
7129+
naka_conf.burnchain.chain_id,
7130+
&recipient,
7131+
send_amt,
7132+
);
7133+
submit_tx(&http_origin, &transfer_tx);
7134+
wait_for(10, || {
7135+
let sender_nonce = get_account(&http_origin, &to_addr(&sender_sk)).nonce;
7136+
Ok(sender_nonce >= transfer_nonce)
7137+
})
7138+
.expect("Timed out waiting for transfer TX to confirm");
7139+
}
7140+
71207141
info!("Resuming commit ops to mine regular tenures.");
71217142
test_skip_commit_op.0.lock().unwrap().replace(false);
71227143

@@ -7154,7 +7175,9 @@ fn continue_tenure_extend() {
71547175
let mut tenure_extends = vec![];
71557176
let mut tenure_block_founds = vec![];
71567177
let mut transfer_tx_included = false;
7178+
let mut last_block_had_extend = false;
71577179
for block in test_observer::get_blocks() {
7180+
let mut has_extend = false;
71587181
for tx in block["transactions"].as_array().unwrap() {
71597182
let raw_tx = tx["raw_tx"].as_str().unwrap();
71607183
if raw_tx == &transfer_tx_hex {
@@ -7168,12 +7191,21 @@ fn continue_tenure_extend() {
71687191
let parsed = StacksTransaction::consensus_deserialize(&mut &tx_bytes[..]).unwrap();
71697192
match &parsed.payload {
71707193
TransactionPayload::TenureChange(payload) => match payload.cause {
7171-
TenureChangeCause::Extended => tenure_extends.push(parsed),
7172-
TenureChangeCause::BlockFound => tenure_block_founds.push(parsed),
7194+
TenureChangeCause::Extended => {
7195+
has_extend = true;
7196+
tenure_extends.push(parsed);
7197+
}
7198+
TenureChangeCause::BlockFound => {
7199+
if last_block_had_extend {
7200+
panic!("Expected a Nakamoto block to happen after tenure extend block");
7201+
}
7202+
tenure_block_founds.push(parsed);
7203+
}
71737204
},
71747205
_ => {}
71757206
};
71767207
}
7208+
last_block_had_extend = has_extend;
71777209
}
71787210
assert!(
71797211
!tenure_extends.is_empty(),

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

Lines changed: 139 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::{
@@ -5077,6 +5078,143 @@ fn miner_recovers_when_broadcast_block_delay_across_tenures_occurs() {
50775078
assert_ne!(block_n_2, block_n);
50785079
}
50795080

5081+
#[test]
5082+
#[ignore]
5083+
/// Test that we can mine a tenure extend and then continue mining afterwards.
5084+
fn continue_after_tenure_extend() {
5085+
if env::var("BITCOIND_TEST") != Ok("1".into()) {
5086+
return;
5087+
}
5088+
5089+
tracing_subscriber::registry()
5090+
.with(fmt::layer())
5091+
.with(EnvFilter::from_default_env())
5092+
.init();
5093+
5094+
info!("------------------------- Test Setup -------------------------");
5095+
let num_signers = 5;
5096+
let sender_sk = Secp256k1PrivateKey::new();
5097+
let sender_addr = tests::to_addr(&sender_sk);
5098+
let recipient = PrincipalData::from(StacksAddress::burn_address(false));
5099+
let send_amt = 100;
5100+
let send_fee = 180;
5101+
let mut signer_test: SignerTest<SpawnedSigner> = SignerTest::new(
5102+
num_signers,
5103+
vec![(sender_addr.clone(), (send_amt + send_fee) * 5)],
5104+
);
5105+
let timeout = Duration::from_secs(200);
5106+
let coord_channel = signer_test.running_nodes.coord_channel.clone();
5107+
let http_origin = format!("http://{}", &signer_test.running_nodes.conf.node.rpc_bind);
5108+
5109+
signer_test.boot_to_epoch_3();
5110+
5111+
info!("------------------------- Mine Normal Tenure -------------------------");
5112+
signer_test.mine_and_verify_confirmed_naka_block(timeout, num_signers);
5113+
5114+
info!("------------------------- Extend Tenure -------------------------");
5115+
signer_test
5116+
.running_nodes
5117+
.nakamoto_test_skip_commit_op
5118+
.0
5119+
.lock()
5120+
.unwrap()
5121+
.replace(true);
5122+
5123+
// It's possible that we have a pending block commit already.
5124+
// Mine two BTC blocks to "flush" this commit.
5125+
let burn_height = signer_test
5126+
.stacks_client
5127+
.get_peer_info()
5128+
.expect("Failed to get peer info")
5129+
.burn_block_height;
5130+
for i in 0..2 {
5131+
info!(
5132+
"------------- After pausing commits, triggering 2 BTC blocks: ({} of 2) -----------",
5133+
i + 1
5134+
);
5135+
5136+
let blocks_processed_before = coord_channel
5137+
.lock()
5138+
.expect("Mutex poisoned")
5139+
.get_stacks_blocks_processed();
5140+
signer_test
5141+
.running_nodes
5142+
.btc_regtest_controller
5143+
.build_next_block(1);
5144+
5145+
wait_for(60, || {
5146+
let blocks_processed_after = coord_channel
5147+
.lock()
5148+
.expect("Mutex poisoned")
5149+
.get_stacks_blocks_processed();
5150+
Ok(blocks_processed_after > blocks_processed_before)
5151+
})
5152+
.expect("Timed out waiting for tenure extend block");
5153+
}
5154+
5155+
wait_for(30, || {
5156+
let new_burn_height = signer_test
5157+
.stacks_client
5158+
.get_peer_info()
5159+
.expect("Failed to get peer info")
5160+
.burn_block_height;
5161+
Ok(new_burn_height == burn_height + 2)
5162+
})
5163+
.expect("Timed out waiting for burnchain to advance");
5164+
5165+
// The last block should have a single instruction in it, the tenure extend
5166+
let blocks = test_observer::get_blocks();
5167+
let last_block = blocks.last().unwrap();
5168+
let transactions = last_block["transactions"].as_array().unwrap();
5169+
let tx = transactions.first().expect("No transactions in block");
5170+
let raw_tx = tx["raw_tx"].as_str().unwrap();
5171+
let tx_bytes = hex_bytes(&raw_tx[2..]).unwrap();
5172+
let parsed = StacksTransaction::consensus_deserialize(&mut &tx_bytes[..]).unwrap();
5173+
match &parsed.payload {
5174+
TransactionPayload::TenureChange(payload)
5175+
if payload.cause == TenureChangeCause::Extended => {}
5176+
_ => panic!("Expected tenure extend transaction, got {:?}", parsed),
5177+
};
5178+
5179+
// Verify that the miner can continue mining in the tenure with the tenure extend
5180+
info!("------------------------- Mine After Tenure Extend -------------------------");
5181+
let mut sender_nonce = 0;
5182+
let mut blocks_processed_before = coord_channel
5183+
.lock()
5184+
.expect("Mutex poisoned")
5185+
.get_stacks_blocks_processed();
5186+
for _ in 0..5 {
5187+
// submit a tx so that the miner will mine an extra block
5188+
let transfer_tx = make_stacks_transfer(
5189+
&sender_sk,
5190+
sender_nonce,
5191+
send_fee,
5192+
signer_test.running_nodes.conf.burnchain.chain_id,
5193+
&recipient,
5194+
send_amt,
5195+
);
5196+
sender_nonce += 1;
5197+
submit_tx(&http_origin, &transfer_tx);
5198+
5199+
info!("Submitted transfer tx and waiting for block proposal");
5200+
wait_for(30, || {
5201+
let blocks_processed_after = coord_channel
5202+
.lock()
5203+
.expect("Mutex poisoned")
5204+
.get_stacks_blocks_processed();
5205+
Ok(blocks_processed_after > blocks_processed_before)
5206+
})
5207+
.expect("Timed out waiting for block proposal");
5208+
blocks_processed_before = coord_channel
5209+
.lock()
5210+
.expect("Mutex poisoned")
5211+
.get_stacks_blocks_processed();
5212+
info!("Block {blocks_processed_before} processed, continuing");
5213+
}
5214+
5215+
signer_test.shutdown();
5216+
}
5217+
50805218
#[test]
50815219
#[ignore]
50825220
/// Test that signers can successfully sign a block proposal in the 0th tenure of a reward cycle

0 commit comments

Comments
 (0)