@@ -81,6 +81,9 @@ pub enum MinerDirective {
81
81
/// This is the block ID of the first block in the parent tenure
82
82
parent_tenure_start : StacksBlockId ,
83
83
/// 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).
84
87
burnchain_tip : BlockSnapshot ,
85
88
/// This is `true` if the snapshot above is known not to be the the latest burnchain tip,
86
89
/// but an ancestor of it (for example, the burnchain tip could be an empty flash block, but the
@@ -135,6 +138,15 @@ pub enum MinerReason {
135
138
} ,
136
139
}
137
140
141
+ impl MinerReason {
142
+ pub fn is_late_block ( & self ) -> bool {
143
+ match self {
144
+ Self :: BlockFound { ref late } => * late,
145
+ Self :: Extended { .. } => false ,
146
+ }
147
+ }
148
+ }
149
+
138
150
impl std:: fmt:: Display for MinerReason {
139
151
fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
140
152
match self {
@@ -170,7 +182,7 @@ pub struct BlockMinerThread {
170
182
burn_election_block : BlockSnapshot ,
171
183
/// Current burnchain tip as of the last TenureChange
172
184
/// * if the last tenure-change was a BlockFound, then this is the same as the
173
- /// `burn_election_block`.
185
+ /// `burn_election_block` (and it is also the `burn_view`)
174
186
/// * otherwise, if the last tenure-change is an Extend, then this is the sortition of the burn
175
187
/// view consensus hash in the TenureChange
176
188
burn_block : BlockSnapshot ,
@@ -185,6 +197,12 @@ pub struct BlockMinerThread {
185
197
signer_set_cache : Option < RewardSet > ,
186
198
/// The time at which tenure change/extend was attempted
187
199
tenure_change_time : Instant ,
200
+ /// The current tip when this miner thread was started.
201
+ /// This *should not* be passed into any block building code, as it
202
+ /// is not necessarily the burn view for the block being constructed.
203
+ /// Rather, this burn block is used to determine whether or not a new
204
+ /// burn block has arrived since this thread started.
205
+ burn_tip_at_start : ConsensusHash ,
188
206
/// flag to indicate an abort driven from the relayer
189
207
abort_flag : Arc < AtomicBool > ,
190
208
}
@@ -197,6 +215,7 @@ impl BlockMinerThread {
197
215
burn_election_block : BlockSnapshot ,
198
216
burn_block : BlockSnapshot ,
199
217
parent_tenure_id : StacksBlockId ,
218
+ burn_tip_at_start : & ConsensusHash ,
200
219
reason : MinerReason ,
201
220
) -> BlockMinerThread {
202
221
BlockMinerThread {
@@ -214,6 +233,7 @@ impl BlockMinerThread {
214
233
reason,
215
234
p2p_handle : rt. get_p2p_handle ( ) ,
216
235
signer_set_cache : None ,
236
+ burn_tip_at_start : burn_tip_at_start. clone ( ) ,
217
237
tenure_change_time : Instant :: now ( ) ,
218
238
abort_flag : Arc :: new ( AtomicBool :: new ( false ) ) ,
219
239
}
@@ -338,18 +358,17 @@ impl BlockMinerThread {
338
358
self . burnchain . pox_constants . clone ( ) ,
339
359
)
340
360
. expect ( "FATAL: could not open sortition DB" ) ;
341
- let burn_tip = SortitionDB :: get_canonical_burn_chain_tip ( sortdb. conn ( ) )
342
- . expect ( "FATAL: failed to query sortition DB for canonical burn chain tip" ) ;
343
361
344
362
// Start the signer coordinator
345
363
let mut coordinator = SignerCoordinator :: new (
346
364
self . event_dispatcher . stackerdb_channel . clone ( ) ,
347
365
self . globals . should_keep_running . clone ( ) ,
348
366
& reward_set,
349
- & burn_tip ,
367
+ & self . burn_election_block ,
350
368
& self . burnchain ,
351
369
miner_privkey,
352
370
& self . config ,
371
+ & self . burn_tip_at_start ,
353
372
)
354
373
. map_err ( |e| {
355
374
NakamotoNodeError :: SigningCoordinatorFailure ( format ! (
@@ -413,6 +432,16 @@ impl BlockMinerThread {
413
432
"Failed to open chainstate DB. Cannot mine! {e:?}"
414
433
) )
415
434
} ) ?;
435
+ // Late block tenures are initiated only to issue the BlockFound
436
+ // tenure change tx (because they can be immediately extended to
437
+ // the next burn view). This checks whether or not we're in such a
438
+ // tenure and have produced a block already. If so, it exits the
439
+ // mining thread to allow the tenure extension thread to take over.
440
+ if self . last_block_mined . is_some ( ) && self . reason . is_late_block ( ) {
441
+ info ! ( "Miner: finished mining a late tenure" ) ;
442
+ return Err ( NakamotoNodeError :: StacksTipChanged ) ;
443
+ }
444
+
416
445
let new_block = loop {
417
446
// If we're mock mining, we may not have processed the block that the
418
447
// actual tenure winner committed to yet. So, before attempting to
@@ -422,7 +451,7 @@ impl BlockMinerThread {
422
451
let mut burn_db =
423
452
SortitionDB :: open ( & burn_db_path, true , self . burnchain . pox_constants . clone ( ) )
424
453
. expect ( "FATAL: could not open sortition DB" ) ;
425
- let burn_tip_changed = self . check_burn_tip_changed ( & burn_db, & mut chain_state ) ;
454
+ let burn_tip_changed = self . check_burn_tip_changed ( & burn_db) ;
426
455
match burn_tip_changed
427
456
. and_then ( |_| self . load_block_parent_info ( & mut burn_db, & mut chain_state) )
428
457
{
@@ -567,10 +596,7 @@ impl BlockMinerThread {
567
596
let wait_start = Instant :: now ( ) ;
568
597
while wait_start. elapsed ( ) < self . config . miner . wait_on_interim_blocks {
569
598
thread:: sleep ( Duration :: from_millis ( ABORT_TRY_AGAIN_MS ) ) ;
570
- if self
571
- . check_burn_tip_changed ( & sort_db, & mut chain_state)
572
- . is_err ( )
573
- {
599
+ if self . check_burn_tip_changed ( & sort_db) . is_err ( ) {
574
600
return Err ( NakamotoNodeError :: BurnchainTipChanged ) ;
575
601
}
576
602
}
@@ -598,13 +624,12 @@ impl BlockMinerThread {
598
624
} ) ?;
599
625
coordinator. propose_block (
600
626
new_block,
601
- & self . burn_block ,
602
627
& self . burnchain ,
603
628
sortdb,
604
629
& mut chain_state,
605
630
stackerdbs,
606
631
& self . globals . counters ,
607
- & self . burn_election_block . consensus_hash ,
632
+ & self . burn_election_block ,
608
633
)
609
634
}
610
635
@@ -1112,7 +1137,7 @@ impl BlockMinerThread {
1112
1137
let mut chain_state = neon_node:: open_chainstate_with_faults ( & self . config )
1113
1138
. expect ( "FATAL: could not open chainstate DB" ) ;
1114
1139
1115
- self . check_burn_tip_changed ( & burn_db, & mut chain_state ) ?;
1140
+ self . check_burn_tip_changed ( & burn_db) ?;
1116
1141
neon_node:: fault_injection_long_tenure ( ) ;
1117
1142
1118
1143
let mut mem_pool = self
@@ -1216,7 +1241,7 @@ impl BlockMinerThread {
1216
1241
// last chance -- confirm that the stacks tip is unchanged (since it could have taken long
1217
1242
// enough to build this block that another block could have arrived), and confirm that all
1218
1243
// Stacks blocks with heights higher than the canonical tip are processed.
1219
- self . check_burn_tip_changed ( & burn_db, & mut chain_state ) ?;
1244
+ self . check_burn_tip_changed ( & burn_db) ?;
1220
1245
Ok ( block)
1221
1246
}
1222
1247
@@ -1328,26 +1353,14 @@ impl BlockMinerThread {
1328
1353
/// Check if the tenure needs to change -- if so, return a BurnchainTipChanged error
1329
1354
/// The tenure should change if there is a new burnchain tip with a valid sortition,
1330
1355
/// or if the stacks chain state's burn view has advanced beyond our burn view.
1331
- fn check_burn_tip_changed (
1332
- & self ,
1333
- sortdb : & SortitionDB ,
1334
- _chain_state : & mut StacksChainState ,
1335
- ) -> Result < ( ) , NakamotoNodeError > {
1336
- if let MinerReason :: BlockFound { late } = & self . reason {
1337
- if * late && self . last_block_mined . is_none ( ) {
1338
- // this is a late BlockFound tenure change that ought to be appended to the Stacks
1339
- // chain tip, and we haven't submitted it yet.
1340
- return Ok ( ( ) ) ;
1341
- }
1342
- }
1343
-
1356
+ fn check_burn_tip_changed ( & self , sortdb : & SortitionDB ) -> Result < ( ) , NakamotoNodeError > {
1344
1357
let cur_burn_chain_tip = SortitionDB :: get_canonical_burn_chain_tip ( sortdb. conn ( ) )
1345
1358
. expect ( "FATAL: failed to query sortition DB for canonical burn chain tip" ) ;
1346
1359
1347
- if cur_burn_chain_tip. consensus_hash != self . burn_block . consensus_hash {
1360
+ if cur_burn_chain_tip. consensus_hash != self . burn_tip_at_start {
1348
1361
info ! ( "Miner: Cancel block assembly; burnchain tip has changed" ;
1349
1362
"new_tip" => %cur_burn_chain_tip. consensus_hash,
1350
- "local_tip" => %self . burn_block . consensus_hash ) ;
1363
+ "local_tip" => %self . burn_tip_at_start ) ;
1351
1364
self . globals . counters . bump_missed_tenures ( ) ;
1352
1365
Err ( NakamotoNodeError :: BurnchainTipChanged )
1353
1366
} else {
0 commit comments