Skip to content

Commit 54ff7cb

Browse files
committed
Merge branch 'develop' of https://github.com/stacks-network/stacks-core into feat/5304
2 parents 8c0f967 + ce46c93 commit 54ff7cb

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
@@ -6938,10 +6938,10 @@ fn continue_tenure_extend() {
69386938
// setup sender + recipient for a test stx transfer
69396939
let sender_addr = tests::to_addr(&sender_sk);
69406940
let send_amt = 1000;
6941-
let send_fee = 100;
6941+
let send_fee = 200;
69426942
naka_conf.add_initial_balance(
69436943
PrincipalData::from(sender_addr.clone()).to_string(),
6944-
send_amt * 2 + send_fee,
6944+
(send_amt + send_fee) * 20,
69456945
);
69466946
let sender_signer_sk = Secp256k1PrivateKey::new();
69476947
let sender_signer_addr = tests::to_addr(&sender_signer_sk);
@@ -6951,6 +6951,7 @@ fn continue_tenure_extend() {
69516951
);
69526952
let recipient = PrincipalData::from(StacksAddress::burn_address(false));
69536953
let stacker_sk = setup_stacker(&mut naka_conf);
6954+
let mut transfer_nonce = 0;
69546955

69556956
test_observer::spawn();
69566957
test_observer::register_any(&mut naka_conf);
@@ -7071,7 +7072,7 @@ fn continue_tenure_extend() {
70717072
// Submit a TX
70727073
let transfer_tx = make_stacks_transfer(
70737074
&sender_sk,
7074-
0,
7075+
transfer_nonce,
70757076
send_fee,
70767077
naka_conf.burnchain.chain_id,
70777078
&recipient,
@@ -7133,6 +7134,26 @@ fn continue_tenure_extend() {
71337134
})
71347135
.unwrap();
71357136

7137+
// Mine 3 nakamoto blocks
7138+
for i in 0..3 {
7139+
info!("Triggering Nakamoto blocks after extend ({})", i + 1);
7140+
transfer_nonce += 1;
7141+
let transfer_tx = make_stacks_transfer(
7142+
&sender_sk,
7143+
transfer_nonce,
7144+
send_fee,
7145+
naka_conf.burnchain.chain_id,
7146+
&recipient,
7147+
send_amt,
7148+
);
7149+
submit_tx(&http_origin, &transfer_tx);
7150+
wait_for(10, || {
7151+
let sender_nonce = get_account(&http_origin, &to_addr(&sender_sk)).nonce;
7152+
Ok(sender_nonce >= transfer_nonce)
7153+
})
7154+
.expect("Timed out waiting for transfer TX to confirm");
7155+
}
7156+
71367157
info!("Resuming commit ops to mine regular tenures.");
71377158
test_skip_commit_op.0.lock().unwrap().replace(false);
71387159

@@ -7170,7 +7191,9 @@ fn continue_tenure_extend() {
71707191
let mut tenure_extends = vec![];
71717192
let mut tenure_block_founds = vec![];
71727193
let mut transfer_tx_included = false;
7194+
let mut last_block_had_extend = false;
71737195
for block in test_observer::get_blocks() {
7196+
let mut has_extend = false;
71747197
for tx in block["transactions"].as_array().unwrap() {
71757198
let raw_tx = tx["raw_tx"].as_str().unwrap();
71767199
if raw_tx == &transfer_tx_hex {
@@ -7184,12 +7207,21 @@ fn continue_tenure_extend() {
71847207
let parsed = StacksTransaction::consensus_deserialize(&mut &tx_bytes[..]).unwrap();
71857208
match &parsed.payload {
71867209
TransactionPayload::TenureChange(payload) => match payload.cause {
7187-
TenureChangeCause::Extended => tenure_extends.push(parsed),
7188-
TenureChangeCause::BlockFound => tenure_block_founds.push(parsed),
7210+
TenureChangeCause::Extended => {
7211+
has_extend = true;
7212+
tenure_extends.push(parsed);
7213+
}
7214+
TenureChangeCause::BlockFound => {
7215+
if last_block_had_extend {
7216+
panic!("Expected a Nakamoto block to happen after tenure extend block");
7217+
}
7218+
tenure_block_founds.push(parsed);
7219+
}
71897220
},
71907221
_ => {}
71917222
};
71927223
}
7224+
last_block_had_extend = has_extend;
71937225
}
71947226
assert!(
71957227
!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)