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