Skip to content

Commit 0abb85a

Browse files
committed
fix: explicit burnchain checks in miner thread
1 parent 1cfc225 commit 0abb85a

File tree

4 files changed

+231
-63
lines changed

4 files changed

+231
-63
lines changed

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

Lines changed: 22 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ pub enum MinerDirective {
8181
/// This is the block ID of the first block in the parent tenure
8282
parent_tenure_start: StacksBlockId,
8383
/// This is the snapshot that this miner won, and will produce a tenure for
84+
election_block: BlockSnapshot,
85+
/// This is the snapshot that caused the relayer to initiate this event (may be different
86+
/// than the election block in the case where the miner is trying to mine a late block).
8487
burnchain_tip: BlockSnapshot,
8588
/// This is `true` if the snapshot above is known not to be the the latest burnchain tip,
8689
/// but an ancestor of it (for example, the burnchain tip could be an empty flash block, but the
@@ -170,7 +173,7 @@ pub struct BlockMinerThread {
170173
burn_election_block: BlockSnapshot,
171174
/// Current burnchain tip as of the last TenureChange
172175
/// * if the last tenure-change was a BlockFound, then this is the same as the
173-
/// `burn_election_block`.
176+
/// `burn_election_block` (and it is also the `burn_view`)
174177
/// * otherwise, if the last tenure-change is an Extend, then this is the sortition of the burn
175178
/// view consensus hash in the TenureChange
176179
burn_block: BlockSnapshot,
@@ -185,6 +188,12 @@ pub struct BlockMinerThread {
185188
signer_set_cache: Option<RewardSet>,
186189
/// The time at which tenure change/extend was attempted
187190
tenure_change_time: Instant,
191+
/// The current tip when this miner thread was started.
192+
/// This *should not* be passed into any block building code, as it
193+
/// is not necessarily the burn view for the block being constructed.
194+
/// Rather, this burn block is used to determine whether or not a new
195+
/// burn block has arrived since this thread started.
196+
burn_tip_at_start: ConsensusHash,
188197
}
189198

190199
impl BlockMinerThread {
@@ -195,6 +204,7 @@ impl BlockMinerThread {
195204
burn_election_block: BlockSnapshot,
196205
burn_block: BlockSnapshot,
197206
parent_tenure_id: StacksBlockId,
207+
burn_tip_at_start: &ConsensusHash,
198208
reason: MinerReason,
199209
) -> BlockMinerThread {
200210
BlockMinerThread {
@@ -212,6 +222,7 @@ impl BlockMinerThread {
212222
reason,
213223
p2p_handle: rt.get_p2p_handle(),
214224
signer_set_cache: None,
225+
burn_tip_at_start: burn_tip_at_start.clone(),
215226
tenure_change_time: Instant::now(),
216227
}
217228
}
@@ -357,10 +368,11 @@ impl BlockMinerThread {
357368
self.event_dispatcher.stackerdb_channel.clone(),
358369
self.globals.should_keep_running.clone(),
359370
&reward_set,
360-
&burn_tip,
371+
&self.burn_election_block,
361372
&self.burnchain,
362373
miner_privkey,
363374
&self.config,
375+
&self.burn_tip_at_start,
364376
)
365377
.map_err(|e| {
366378
NakamotoNodeError::SigningCoordinatorFailure(format!(
@@ -433,7 +445,7 @@ impl BlockMinerThread {
433445
let mut burn_db =
434446
SortitionDB::open(&burn_db_path, true, self.burnchain.pox_constants.clone())
435447
.expect("FATAL: could not open sortition DB");
436-
let burn_tip_changed = self.check_burn_tip_changed(&burn_db, &mut chain_state);
448+
let burn_tip_changed = self.check_burn_tip_changed(&burn_db);
437449
match burn_tip_changed
438450
.and_then(|_| self.load_block_parent_info(&mut burn_db, &mut chain_state))
439451
{
@@ -571,10 +583,7 @@ impl BlockMinerThread {
571583
let wait_start = Instant::now();
572584
while wait_start.elapsed() < self.config.miner.wait_on_interim_blocks {
573585
thread::sleep(Duration::from_millis(ABORT_TRY_AGAIN_MS));
574-
if self
575-
.check_burn_tip_changed(&sort_db, &mut chain_state)
576-
.is_err()
577-
{
586+
if self.check_burn_tip_changed(&sort_db).is_err() {
578587
return Err(NakamotoNodeError::BurnchainTipChanged);
579588
}
580589
}
@@ -602,13 +611,12 @@ impl BlockMinerThread {
602611
})?;
603612
coordinator.propose_block(
604613
new_block,
605-
&self.burn_block,
606614
&self.burnchain,
607615
sortdb,
608616
&mut chain_state,
609617
stackerdbs,
610618
&self.globals.counters,
611-
&self.burn_election_block.consensus_hash,
619+
&self.burn_election_block,
612620
)
613621
}
614622

@@ -1116,7 +1124,7 @@ impl BlockMinerThread {
11161124
let mut chain_state = neon_node::open_chainstate_with_faults(&self.config)
11171125
.expect("FATAL: could not open chainstate DB");
11181126

1119-
self.check_burn_tip_changed(&burn_db, &mut chain_state)?;
1127+
self.check_burn_tip_changed(&burn_db)?;
11201128
neon_node::fault_injection_long_tenure();
11211129

11221130
let mut mem_pool = self
@@ -1220,7 +1228,7 @@ impl BlockMinerThread {
12201228
// last chance -- confirm that the stacks tip is unchanged (since it could have taken long
12211229
// enough to build this block that another block could have arrived), and confirm that all
12221230
// Stacks blocks with heights higher than the canonical tip are processed.
1223-
self.check_burn_tip_changed(&burn_db, &mut chain_state)?;
1231+
self.check_burn_tip_changed(&burn_db)?;
12241232
Ok(block)
12251233
}
12261234

@@ -1359,26 +1367,14 @@ impl BlockMinerThread {
13591367
/// Check if the tenure needs to change -- if so, return a BurnchainTipChanged error
13601368
/// The tenure should change if there is a new burnchain tip with a valid sortition,
13611369
/// or if the stacks chain state's burn view has advanced beyond our burn view.
1362-
fn check_burn_tip_changed(
1363-
&self,
1364-
sortdb: &SortitionDB,
1365-
_chain_state: &mut StacksChainState,
1366-
) -> Result<(), NakamotoNodeError> {
1367-
if let MinerReason::BlockFound { late } = &self.reason {
1368-
if *late && self.last_block_mined.is_none() {
1369-
// this is a late BlockFound tenure change that ought to be appended to the Stacks
1370-
// chain tip, and we haven't submitted it yet.
1371-
return Ok(());
1372-
}
1373-
}
1374-
1370+
fn check_burn_tip_changed(&self, sortdb: &SortitionDB) -> Result<(), NakamotoNodeError> {
13751371
let cur_burn_chain_tip = SortitionDB::get_canonical_burn_chain_tip(sortdb.conn())
13761372
.expect("FATAL: failed to query sortition DB for canonical burn chain tip");
13771373

1378-
if cur_burn_chain_tip.consensus_hash != self.burn_block.consensus_hash {
1374+
if cur_burn_chain_tip.consensus_hash != self.burn_tip_at_start {
13791375
info!("Miner: Cancel block assembly; burnchain tip has changed";
13801376
"new_tip" => %cur_burn_chain_tip.consensus_hash,
1381-
"local_tip" => %self.burn_block.consensus_hash);
1377+
"local_tip" => %self.burn_tip_at_start);
13821378
self.globals.counters.bump_missed_tenures();
13831379
Err(NakamotoNodeError::BurnchainTipChanged)
13841380
} else {

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

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,8 @@ impl RelayerThread {
451451
"winning_sortition" => %sn.consensus_hash);
452452
return Some(MinerDirective::BeginTenure {
453453
parent_tenure_start: committed_index_hash,
454-
burnchain_tip: sn,
454+
burnchain_tip: sn.clone(),
455+
election_block: sn,
455456
late: false,
456457
});
457458
}
@@ -589,7 +590,8 @@ impl RelayerThread {
589590
parent_tenure_start: StacksBlockId(
590591
last_winning_snapshot.winning_stacks_block_hash.clone().0,
591592
),
592-
burnchain_tip: last_winning_snapshot,
593+
burnchain_tip: sn,
594+
election_block: last_winning_snapshot,
593595
late: true,
594596
});
595597
}
@@ -975,6 +977,7 @@ impl RelayerThread {
975977
burn_tip: BlockSnapshot,
976978
parent_tenure_id: StacksBlockId,
977979
reason: MinerReason,
980+
burn_tip_at_start: &ConsensusHash,
978981
) -> Result<BlockMinerThread, NakamotoNodeError> {
979982
if fault_injection_skip_mining(&self.config.node.rpc_bind, burn_tip.block_height) {
980983
debug!(
@@ -991,14 +994,8 @@ impl RelayerThread {
991994

992995
let burn_chain_tip = burn_chain_sn.burn_header_hash;
993996

994-
let allow_late = if let MinerReason::BlockFound { late } = &reason {
995-
*late
996-
} else {
997-
false
998-
};
999-
1000-
if burn_chain_tip != burn_header_hash && !allow_late {
1001-
debug!(
997+
if &burn_chain_sn.consensus_hash != burn_tip_at_start {
998+
info!(
1002999
"Relayer: Drop stale RunTenure for {burn_header_hash}: current sortition is for {burn_chain_tip}"
10031000
);
10041001
self.globals.counters.bump_missed_tenures();
@@ -1021,6 +1018,7 @@ impl RelayerThread {
10211018
burn_election_block,
10221019
burn_tip,
10231020
parent_tenure_id,
1021+
burn_tip_at_start,
10241022
reason,
10251023
);
10261024
Ok(miner_thread_state)
@@ -1032,6 +1030,7 @@ impl RelayerThread {
10321030
block_election_snapshot: BlockSnapshot,
10331031
burn_tip: BlockSnapshot,
10341032
reason: MinerReason,
1033+
burn_tip_at_start: &ConsensusHash,
10351034
) -> Result<(), NakamotoNodeError> {
10361035
// when starting a new tenure, block the mining thread if its currently running.
10371036
// the new mining thread will join it (so that the new mining thread stalls, not the relayer)
@@ -1052,6 +1051,7 @@ impl RelayerThread {
10521051
burn_tip.clone(),
10531052
parent_tenure_start,
10541053
reason,
1054+
burn_tip_at_start,
10551055
)?;
10561056

10571057
debug!("Relayer: starting new tenure thread");
@@ -1372,14 +1372,15 @@ impl RelayerThread {
13721372
StacksBlockId::new(&canonical_stacks_tip_ch, &canonical_stacks_tip_bh);
13731373

13741374
let reason = MinerReason::Extended {
1375-
burn_view_consensus_hash: new_burn_view,
1375+
burn_view_consensus_hash: new_burn_view.clone(),
13761376
};
13771377

13781378
if let Err(e) = self.start_new_tenure(
13791379
canonical_stacks_tip.clone(),
13801380
canonical_stacks_tip_election_snapshot.clone(),
13811381
burn_tip.clone(),
13821382
reason.clone(),
1383+
&new_burn_view,
13831384
) {
13841385
error!("Relayer: Failed to start new tenure: {e:?}");
13851386
} else {
@@ -1415,12 +1416,14 @@ impl RelayerThread {
14151416
MinerDirective::BeginTenure {
14161417
parent_tenure_start,
14171418
burnchain_tip,
1419+
election_block,
14181420
late,
14191421
} => match self.start_new_tenure(
14201422
parent_tenure_start,
1421-
burnchain_tip.clone(),
1422-
burnchain_tip.clone(),
1423+
election_block.clone(),
1424+
election_block.clone(),
14231425
MinerReason::BlockFound { late },
1426+
&burnchain_tip.consensus_hash,
14241427
) {
14251428
Ok(()) => {
14261429
debug!("Relayer: successfully started new tenure.";

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

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ pub struct SignerCoordinator {
6161
keep_running: Arc<AtomicBool>,
6262
/// Handle for the signer DB listener thread
6363
listener_thread: Option<JoinHandle<()>>,
64+
/// The current tip when this miner thread was started.
65+
/// This *should not* be passed into any block building code, as it
66+
/// is not necessarily the burn view for the block being constructed.
67+
/// Rather, this burn block is used to determine whether or not a new
68+
/// burn block has arrived since this thread started.
69+
burn_tip_at_start: ConsensusHash,
6470
}
6571

6672
impl SignerCoordinator {
@@ -70,10 +76,11 @@ impl SignerCoordinator {
7076
stackerdb_channel: Arc<Mutex<StackerDBChannel>>,
7177
node_keep_running: Arc<AtomicBool>,
7278
reward_set: &RewardSet,
73-
burn_tip: &BlockSnapshot,
79+
election_block: &BlockSnapshot,
7480
burnchain: &Burnchain,
7581
message_key: StacksPrivateKey,
7682
config: &Config,
83+
burn_tip_at_start: &ConsensusHash,
7784
) -> Result<Self, ChainstateError> {
7885
info!("SignerCoordinator: starting up");
7986
let keep_running = Arc::new(AtomicBool::new(true));
@@ -84,7 +91,7 @@ impl SignerCoordinator {
8491
node_keep_running.clone(),
8592
keep_running.clone(),
8693
reward_set,
87-
burn_tip,
94+
election_block,
8895
burnchain,
8996
)?;
9097
let is_mainnet = config.is_mainnet();
@@ -104,11 +111,15 @@ impl SignerCoordinator {
104111
stackerdb_comms: listener.get_comms(),
105112
keep_running,
106113
listener_thread: None,
114+
burn_tip_at_start: burn_tip_at_start.clone(),
107115
};
108116

109117
// Spawn the signer DB listener thread
110118
let listener_thread = std::thread::Builder::new()
111-
.name(format!("stackerdb_listener_{}", burn_tip.block_height))
119+
.name(format!(
120+
"stackerdb_listener_{}",
121+
election_block.block_height
122+
))
112123
.spawn(move || {
113124
if let Err(e) = listener.run() {
114125
error!("StackerDBListener: exited with error: {e:?}");
@@ -208,24 +219,23 @@ impl SignerCoordinator {
208219
pub fn propose_block(
209220
&mut self,
210221
block: &NakamotoBlock,
211-
burn_tip: &BlockSnapshot,
212222
burnchain: &Burnchain,
213223
sortdb: &SortitionDB,
214224
chain_state: &mut StacksChainState,
215225
stackerdbs: &StackerDBs,
216226
counters: &Counters,
217-
election_sortition: &ConsensusHash,
227+
election_sortition: &BlockSnapshot,
218228
) -> Result<Vec<MessageSignature>, NakamotoNodeError> {
219229
// Add this block to the block status map.
220230
self.stackerdb_comms.insert_block(&block.header);
221231

222232
let reward_cycle_id = burnchain
223-
.block_height_to_reward_cycle(burn_tip.block_height)
233+
.block_height_to_reward_cycle(election_sortition.block_height)
224234
.expect("FATAL: tried to initialize coordinator before first burn block height");
225235

226236
let block_proposal = BlockProposal {
227237
block: block.clone(),
228-
burn_height: burn_tip.block_height,
238+
burn_height: election_sortition.block_height,
229239
reward_cycle: reward_cycle_id,
230240
};
231241

@@ -236,13 +246,13 @@ impl SignerCoordinator {
236246
Self::send_miners_message::<SignerMessageV0>(
237247
&self.message_key,
238248
sortdb,
239-
burn_tip,
249+
election_sortition,
240250
stackerdbs,
241251
block_proposal_message,
242252
MinerSlotID::BlockProposal,
243253
self.is_mainnet,
244254
&mut self.miners_session,
245-
election_sortition,
255+
&election_sortition.consensus_hash,
246256
)?;
247257
counters.bump_naka_proposed_blocks();
248258

@@ -267,7 +277,6 @@ impl SignerCoordinator {
267277
&block.block_id(),
268278
chain_state,
269279
sortdb,
270-
burn_tip,
271280
counters,
272281
)
273282
}
@@ -283,7 +292,6 @@ impl SignerCoordinator {
283292
block_id: &StacksBlockId,
284293
chain_state: &mut StacksChainState,
285294
sortdb: &SortitionDB,
286-
burn_tip: &BlockSnapshot,
287295
counters: &Counters,
288296
) -> Result<Vec<MessageSignature>, NakamotoNodeError> {
289297
loop {
@@ -324,7 +332,7 @@ impl SignerCoordinator {
324332
return Ok(stored_block.header.signer_signature);
325333
}
326334

327-
if Self::check_burn_tip_changed(sortdb, chain_state, burn_tip) {
335+
if self.check_burn_tip_changed(sortdb) {
328336
debug!("SignCoordinator: Exiting due to new burnchain tip");
329337
return Err(NakamotoNodeError::BurnchainTipChanged);
330338
}
@@ -366,15 +374,11 @@ impl SignerCoordinator {
366374
}
367375

368376
/// Check if the tenure needs to change
369-
fn check_burn_tip_changed(
370-
sortdb: &SortitionDB,
371-
_chain_state: &mut StacksChainState,
372-
burn_block: &BlockSnapshot,
373-
) -> bool {
377+
fn check_burn_tip_changed(&self, sortdb: &SortitionDB) -> bool {
374378
let cur_burn_chain_tip = SortitionDB::get_canonical_burn_chain_tip(sortdb.conn())
375379
.expect("FATAL: failed to query sortition DB for canonical burn chain tip");
376380

377-
if cur_burn_chain_tip.consensus_hash != burn_block.consensus_hash {
381+
if cur_burn_chain_tip.consensus_hash != self.burn_tip_at_start {
378382
info!("SignCoordinator: Cancel signature aggregation; burnchain tip has changed");
379383
true
380384
} else {

0 commit comments

Comments
 (0)