Skip to content

Commit 7224b9b

Browse files
authored
Merge pull request #5411 from stacks-network/fix/fast-block-test
Miner can continue mining after an empty tenure followed by empty sortition
2 parents 7dc541f + 7e70420 commit 7224b9b

File tree

6 files changed

+837
-176
lines changed

6 files changed

+837
-176
lines changed

.github/workflows/bitcoin-tests.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ jobs:
124124
- tests::signer::v0::continue_after_tenure_extend
125125
- tests::signer::v0::multiple_miners_with_custom_chain_id
126126
- tests::signer::v0::block_commit_delay
127+
- tests::signer::v0::continue_after_fast_block_no_sortition
127128
- tests::nakamoto_integrations::burn_ops_integration_test
128129
- tests::nakamoto_integrations::check_block_heights
129130
- tests::nakamoto_integrations::clarity_burn_state

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

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ pub enum MinerReason {
109109
/// sortition.
110110
burn_view_consensus_hash: ConsensusHash,
111111
},
112+
/// The miner thread was spawned to initialize a prior empty tenure
113+
EmptyTenure,
112114
}
113115

114116
impl std::fmt::Display for MinerReason {
@@ -121,6 +123,7 @@ impl std::fmt::Display for MinerReason {
121123
f,
122124
"Extended: burn_view_consensus_hash = {burn_view_consensus_hash:?}",
123125
),
126+
MinerReason::EmptyTenure => write!(f, "EmptyTenure"),
124127
}
125128
}
126129
}
@@ -921,19 +924,19 @@ impl BlockMinerThread {
921924
let vrf_proof = if self.config.get_node_config(false).mock_mining {
922925
self.keychain.generate_proof(
923926
VRF_MOCK_MINER_KEY,
924-
self.burn_block.sortition_hash.as_bytes(),
927+
self.burn_election_block.sortition_hash.as_bytes(),
925928
)
926929
} else {
927930
self.keychain.generate_proof(
928931
self.registered_key.target_block_height,
929-
self.burn_block.sortition_hash.as_bytes(),
932+
self.burn_election_block.sortition_hash.as_bytes(),
930933
)
931934
};
932935

933936
debug!(
934937
"Generated VRF Proof: {} over {} ({},{}) with key {}",
935938
vrf_proof.to_hex(),
936-
&self.burn_block.sortition_hash,
939+
&self.burn_election_block.sortition_hash,
937940
&self.burn_block.block_height,
938941
&self.burn_block.burn_header_hash,
939942
&self.registered_key.vrf_public_key.to_hex()
@@ -1154,7 +1157,7 @@ impl BlockMinerThread {
11541157
};
11551158

11561159
let (tenure_change_tx, coinbase_tx) = match &self.reason {
1157-
MinerReason::BlockFound => {
1160+
MinerReason::BlockFound | MinerReason::EmptyTenure => {
11581161
let tenure_change_tx =
11591162
self.generate_tenure_change_tx(current_miner_nonce, payload)?;
11601163
let coinbase_tx =

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

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -874,23 +874,14 @@ impl RelayerThread {
874874
SortitionDB::get_canonical_stacks_chain_tip_hash(self.sortdb.conn()).unwrap();
875875
let canonical_stacks_tip =
876876
StacksBlockId::new(&canonical_stacks_tip_ch, &canonical_stacks_tip_bh);
877-
let block_election_snapshot =
878-
SortitionDB::get_block_snapshot_consensus(self.sortdb.conn(), &canonical_stacks_tip_ch)
879-
.map_err(|e| {
880-
error!("Relayer: failed to get block snapshot for canonical tip: {e:?}");
881-
NakamotoNodeError::SnapshotNotFoundForChainTip
882-
})?
883-
.ok_or_else(|| {
884-
error!("Relayer: failed to get block snapshot for canonical tip");
885-
NakamotoNodeError::SnapshotNotFoundForChainTip
886-
})?;
887877

888878
let Some(ref mining_key) = self.config.miner.mining_key else {
889879
return Ok(());
890880
};
891881
let mining_pkh = Hash160::from_node_public_key(&StacksPublicKey::from_private(mining_key));
892882

893-
let last_winner_snapshot = {
883+
// If we won the last sortition, then we should start a new tenure off of it.
884+
let last_block_election_snapshot = {
894885
let ih = self.sortdb.index_handle(&burn_tip.sortition_id);
895886
ih.get_last_snapshot_with_sortition(burn_tip.block_height)
896887
.map_err(|e| {
@@ -899,29 +890,59 @@ impl RelayerThread {
899890
})?
900891
};
901892

902-
let won_last_sortition = last_winner_snapshot.miner_pk_hash == Some(mining_pkh);
893+
let won_last_sortition = last_block_election_snapshot.miner_pk_hash == Some(mining_pkh);
903894
debug!(
904895
"Relayer: Current burn block had no sortition. Checking for tenure continuation.";
905896
"won_last_sortition" => won_last_sortition,
906897
"current_mining_pkh" => %mining_pkh,
907-
"last_winner_snapshot.miner_pk_hash" => ?last_winner_snapshot.miner_pk_hash,
898+
"last_block_election_snapshot.consensus_hash" => %last_block_election_snapshot.consensus_hash,
899+
"last_block_election_snapshot.miner_pk_hash" => ?last_block_election_snapshot.miner_pk_hash,
908900
"canonical_stacks_tip_id" => %canonical_stacks_tip,
909901
"canonical_stacks_tip_ch" => %canonical_stacks_tip_ch,
910-
"block_election_ch" => %block_election_snapshot.consensus_hash,
911902
"burn_view_ch" => %new_burn_view,
912903
);
913904

914905
if !won_last_sortition {
915906
return Ok(());
916907
}
917908

909+
let canonical_block_snapshot =
910+
SortitionDB::get_block_snapshot_consensus(self.sortdb.conn(), &canonical_stacks_tip_ch)
911+
.map_err(|e| {
912+
error!("Relayer: failed to get block snapshot for canonical tip: {e:?}");
913+
NakamotoNodeError::SnapshotNotFoundForChainTip
914+
})?
915+
.ok_or_else(|| {
916+
error!("Relayer: failed to get block snapshot for canonical tip");
917+
NakamotoNodeError::SnapshotNotFoundForChainTip
918+
})?;
919+
920+
let won_canonical_block_snapshot =
921+
canonical_block_snapshot.miner_pk_hash == Some(mining_pkh);
922+
923+
let (parent_tenure_start, block_election_snapshot, reason) =
924+
if !won_canonical_block_snapshot {
925+
debug!("Relayer: Failed to issue a tenure change payload in our last tenure. Issue a new tenure change payload.");
926+
(
927+
StacksBlockId(last_block_election_snapshot.winning_stacks_block_hash.0),
928+
last_block_election_snapshot,
929+
MinerReason::EmptyTenure,
930+
)
931+
} else {
932+
debug!("Relayer: Successfully issued a tenure change payload in its tenure. Issue a continue extend from the chain tip.");
933+
(
934+
canonical_stacks_tip, //For tenure extend, we should be extending off the canonical tip
935+
canonical_block_snapshot,
936+
MinerReason::Extended {
937+
burn_view_consensus_hash: new_burn_view,
938+
},
939+
)
940+
};
918941
match self.start_new_tenure(
919-
canonical_stacks_tip, // For tenure extend, we should be extending off the canonical tip
942+
parent_tenure_start,
920943
block_election_snapshot,
921944
burn_tip,
922-
MinerReason::Extended {
923-
burn_view_consensus_hash: new_burn_view,
924-
},
945+
reason,
925946
) {
926947
Ok(()) => {
927948
debug!("Relayer: successfully started new tenure.");
@@ -994,13 +1015,7 @@ impl RelayerThread {
9941015

9951016
#[cfg(test)]
9961017
fn fault_injection_skip_block_commit(&self) -> bool {
997-
self.globals
998-
.counters
999-
.naka_skip_commit_op
1000-
.0
1001-
.lock()
1002-
.unwrap()
1003-
.unwrap_or(false)
1018+
self.globals.counters.naka_skip_commit_op.get()
10041019
}
10051020

10061021
#[cfg(not(test))]

testnet/stacks-node/src/run_loop/neon.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,19 @@ impl Default for TestFlag {
9393
}
9494
}
9595

96+
#[cfg(test)]
97+
impl TestFlag {
98+
/// Set the test flag to the given value
99+
pub fn set(&self, value: bool) {
100+
*self.0.lock().unwrap() = Some(value);
101+
}
102+
103+
/// Get the test flag value. Defaults to false if the flag is not set.
104+
pub fn get(&self) -> bool {
105+
self.0.lock().unwrap().unwrap_or(false)
106+
}
107+
}
108+
96109
#[derive(Clone, Default)]
97110
pub struct Counters {
98111
pub blocks_processed: RunLoopCounter,
@@ -1022,7 +1035,7 @@ impl RunLoop {
10221035
/// This function will block by looping infinitely.
10231036
/// It will start the burnchain (separate thread), set-up a channel in
10241037
/// charge of coordinating the new blocks coming from the burnchain and
1025-
/// the nodes, taking turns on tenures.
1038+
/// the nodes, taking turns on tenures.
10261039
///
10271040
/// Returns `Option<NeonGlobals>` so that data can be passed to `NakamotoNode`
10281041
pub fn start(

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4984,7 +4984,7 @@ fn forked_tenure_is_ignored() {
49844984

49854985
// Unpause the broadcast of Tenure B's block, do not submit commits, and do not allow blocks to
49864986
// be processed
4987-
test_skip_commit_op.0.lock().unwrap().replace(true);
4987+
test_skip_commit_op.set(true);
49884988
TEST_BROADCAST_STALL.lock().unwrap().replace(false);
49894989

49904990
// Wait for a stacks block to be broadcasted.
@@ -5037,7 +5037,7 @@ fn forked_tenure_is_ignored() {
50375037
.expect("Mutex poisoned")
50385038
.get_stacks_blocks_processed();
50395039
next_block_and(&mut btc_regtest_controller, 60, || {
5040-
test_skip_commit_op.0.lock().unwrap().replace(false);
5040+
test_skip_commit_op.set(false);
50415041
TEST_BLOCK_ANNOUNCE_STALL.lock().unwrap().replace(false);
50425042
let commits_count = commits_submitted.load(Ordering::SeqCst);
50435043
let blocks_count = mined_blocks.load(Ordering::SeqCst);
@@ -6972,7 +6972,7 @@ fn continue_tenure_extend() {
69726972
.get_stacks_blocks_processed();
69736973

69746974
info!("Pausing commit ops to trigger a tenure extend.");
6975-
test_skip_commit_op.0.lock().unwrap().replace(true);
6975+
test_skip_commit_op.set(true);
69766976

69776977
next_block_and(&mut btc_regtest_controller, 60, || Ok(true)).unwrap();
69786978

@@ -7071,7 +7071,7 @@ fn continue_tenure_extend() {
70717071
}
70727072

70737073
info!("Resuming commit ops to mine regular tenures.");
7074-
test_skip_commit_op.0.lock().unwrap().replace(false);
7074+
test_skip_commit_op.set(false);
70757075

70767076
// Mine 15 more regular nakamoto tenures
70777077
for _i in 0..15 {

0 commit comments

Comments
 (0)