@@ -2579,9 +2579,18 @@ impl ReplayStage {
2579
2579
) ;
2580
2580
}
2581
2581
2582
- // Given a heaviest bank, `heaviest_bank` and the next votable bank
2583
- // `heaviest_bank_on_same_voted_fork` as the validator's last vote, return
2584
- // a bank to vote on, a bank to reset to,
2582
+ /// Given a `heaviest_bank` and a `heaviest_bank_on_same_voted_fork`, return
2583
+ /// a bank to vote on, a bank to reset to, and a list of switch failure
2584
+ /// reasons.
2585
+ ///
2586
+ /// If `heaviest_bank_on_same_voted_fork` is `None` due to that fork no
2587
+ /// longer being valid to vote on, it's possible that a validator will not
2588
+ /// be able to reset away from the invalid fork that they last voted on. To
2589
+ /// resolve this scenario, validators need to wait until they can create a
2590
+ /// switch proof for another fork or until the invalid fork is be marked
2591
+ /// valid again if it was confirmed by the cluster.
2592
+ /// Until this is resolved, leaders will build each of their
2593
+ /// blocks from the last reset bank on the invalid fork.
2585
2594
pub fn select_vote_and_reset_forks (
2586
2595
heaviest_bank : & Arc < Bank > ,
2587
2596
// Should only be None if there was no previous vote
@@ -5260,6 +5269,139 @@ pub mod tests {
5260
5269
) ;
5261
5270
}
5262
5271
5272
+ #[ test]
5273
+ fn test_unconfirmed_duplicate_slots_and_lockouts_for_non_heaviest_fork ( ) {
5274
+ /*
5275
+ Build fork structure:
5276
+
5277
+ slot 0
5278
+ |
5279
+ slot 1
5280
+ / \
5281
+ slot 2 |
5282
+ | |
5283
+ slot 3 |
5284
+ | |
5285
+ slot 4 |
5286
+ slot 5
5287
+ */
5288
+ let forks = tr ( 0 ) / ( tr ( 1 ) / ( tr ( 2 ) / ( tr ( 3 ) / ( tr ( 4 ) ) ) ) / tr ( 5 ) ) ;
5289
+
5290
+ let mut vote_simulator = VoteSimulator :: new ( 1 ) ;
5291
+ vote_simulator. fill_bank_forks ( forks, & HashMap :: < Pubkey , Vec < u64 > > :: new ( ) , true ) ;
5292
+ let ( bank_forks, mut progress) = ( vote_simulator. bank_forks , vote_simulator. progress ) ;
5293
+ let ledger_path = get_tmp_ledger_path ! ( ) ;
5294
+ let blockstore = Arc :: new (
5295
+ Blockstore :: open ( & ledger_path) . expect ( "Expected to be able to open database ledger" ) ,
5296
+ ) ;
5297
+ let mut tower = Tower :: new_for_tests ( 8 , 2.0 / 3.0 ) ;
5298
+
5299
+ // All forks have same weight so heaviest bank to vote/reset on should be the tip of
5300
+ // the fork with the lower slot
5301
+ let ( vote_fork, reset_fork) = run_compute_and_select_forks (
5302
+ & bank_forks,
5303
+ & mut progress,
5304
+ & mut tower,
5305
+ & mut vote_simulator. heaviest_subtree_fork_choice ,
5306
+ & mut vote_simulator. latest_validator_votes_for_frozen_banks ,
5307
+ ) ;
5308
+ assert_eq ! ( vote_fork. unwrap( ) , 4 ) ;
5309
+ assert_eq ! ( reset_fork. unwrap( ) , 4 ) ;
5310
+
5311
+ // Record the vote for 5 which is not on the heaviest fork.
5312
+ tower. record_bank_vote (
5313
+ & bank_forks. read ( ) . unwrap ( ) . get ( 5 ) . unwrap ( ) ,
5314
+ & Pubkey :: default ( ) ,
5315
+ ) ;
5316
+
5317
+ // 4 should be the heaviest slot, but should not be votable
5318
+ // because of lockout. 5 is the heaviest slot on the same fork as the last vote.
5319
+ let ( vote_fork, reset_fork) = run_compute_and_select_forks (
5320
+ & bank_forks,
5321
+ & mut progress,
5322
+ & mut tower,
5323
+ & mut vote_simulator. heaviest_subtree_fork_choice ,
5324
+ & mut vote_simulator. latest_validator_votes_for_frozen_banks ,
5325
+ ) ;
5326
+ assert ! ( vote_fork. is_none( ) ) ;
5327
+ assert_eq ! ( reset_fork, Some ( 5 ) ) ;
5328
+
5329
+ // Mark 5 as duplicate
5330
+ blockstore. store_duplicate_slot ( 5 , vec ! [ ] , vec ! [ ] ) . unwrap ( ) ;
5331
+ let mut duplicate_slots_tracker = DuplicateSlotsTracker :: default ( ) ;
5332
+ let mut gossip_duplicate_confirmed_slots = GossipDuplicateConfirmedSlots :: default ( ) ;
5333
+ let mut epoch_slots_frozen_slots = EpochSlotsFrozenSlots :: default ( ) ;
5334
+ let bank5_hash = bank_forks. read ( ) . unwrap ( ) . bank_hash ( 5 ) . unwrap ( ) ;
5335
+ assert_ne ! ( bank5_hash, Hash :: default ( ) ) ;
5336
+ let duplicate_state = DuplicateState :: new_from_state (
5337
+ 5 ,
5338
+ & gossip_duplicate_confirmed_slots,
5339
+ & mut vote_simulator. heaviest_subtree_fork_choice ,
5340
+ || progress. is_dead ( 5 ) . unwrap_or ( false ) ,
5341
+ || Some ( bank5_hash) ,
5342
+ ) ;
5343
+ let ( ancestor_hashes_replay_update_sender, _ancestor_hashes_replay_update_receiver) =
5344
+ unbounded ( ) ;
5345
+ check_slot_agrees_with_cluster (
5346
+ 5 ,
5347
+ bank_forks. read ( ) . unwrap ( ) . root ( ) ,
5348
+ & blockstore,
5349
+ & mut duplicate_slots_tracker,
5350
+ & mut epoch_slots_frozen_slots,
5351
+ & mut vote_simulator. heaviest_subtree_fork_choice ,
5352
+ & mut DuplicateSlotsToRepair :: default ( ) ,
5353
+ & ancestor_hashes_replay_update_sender,
5354
+ SlotStateUpdate :: Duplicate ( duplicate_state) ,
5355
+ ) ;
5356
+
5357
+ // 4 should be the heaviest slot, but should not be votable
5358
+ // because of lockout. 5 is no longer valid due to it being a duplicate.
5359
+ let ( vote_fork, reset_fork) = run_compute_and_select_forks (
5360
+ & bank_forks,
5361
+ & mut progress,
5362
+ & mut tower,
5363
+ & mut vote_simulator. heaviest_subtree_fork_choice ,
5364
+ & mut vote_simulator. latest_validator_votes_for_frozen_banks ,
5365
+ ) ;
5366
+ assert ! ( vote_fork. is_none( ) ) ;
5367
+ assert ! ( reset_fork. is_none( ) ) ;
5368
+
5369
+ // If slot 5 is marked as confirmed, it becomes the heaviest bank on same slot again
5370
+ let mut duplicate_slots_to_repair = DuplicateSlotsToRepair :: default ( ) ;
5371
+ gossip_duplicate_confirmed_slots. insert ( 5 , bank5_hash) ;
5372
+ let duplicate_confirmed_state = DuplicateConfirmedState :: new_from_state (
5373
+ bank5_hash,
5374
+ || progress. is_dead ( 5 ) . unwrap_or ( false ) ,
5375
+ || Some ( bank5_hash) ,
5376
+ ) ;
5377
+ check_slot_agrees_with_cluster (
5378
+ 5 ,
5379
+ bank_forks. read ( ) . unwrap ( ) . root ( ) ,
5380
+ & blockstore,
5381
+ & mut duplicate_slots_tracker,
5382
+ & mut epoch_slots_frozen_slots,
5383
+ & mut vote_simulator. heaviest_subtree_fork_choice ,
5384
+ & mut duplicate_slots_to_repair,
5385
+ & ancestor_hashes_replay_update_sender,
5386
+ SlotStateUpdate :: DuplicateConfirmed ( duplicate_confirmed_state) ,
5387
+ ) ;
5388
+ // The confirmed hash is detected in `progress`, which means
5389
+ // it's confirmation on the replayed block. This means we have
5390
+ // the right version of the block, so `duplicate_slots_to_repair`
5391
+ // should be empty
5392
+ assert ! ( duplicate_slots_to_repair. is_empty( ) ) ;
5393
+ let ( vote_fork, reset_fork) = run_compute_and_select_forks (
5394
+ & bank_forks,
5395
+ & mut progress,
5396
+ & mut tower,
5397
+ & mut vote_simulator. heaviest_subtree_fork_choice ,
5398
+ & mut vote_simulator. latest_validator_votes_for_frozen_banks ,
5399
+ ) ;
5400
+ // Should now pick 5 as the heaviest fork from last vote again.
5401
+ assert ! ( vote_fork. is_none( ) ) ;
5402
+ assert_eq ! ( reset_fork. unwrap( ) , 5 ) ;
5403
+ }
5404
+
5263
5405
#[ test]
5264
5406
fn test_unconfirmed_duplicate_slots_and_lockouts ( ) {
5265
5407
/*
@@ -5352,7 +5494,7 @@ pub mod tests {
5352
5494
& mut vote_simulator. latest_validator_votes_for_frozen_banks ,
5353
5495
) ;
5354
5496
assert ! ( vote_fork. is_none( ) ) ;
5355
- assert_eq ! ( reset_fork. unwrap ( ) , 3 ) ;
5497
+ assert_eq ! ( reset_fork, Some ( 3 ) ) ;
5356
5498
5357
5499
// Now mark 2, an ancestor of 4, as duplicate
5358
5500
blockstore. store_duplicate_slot ( 2 , vec ! [ ] , vec ! [ ] ) . unwrap ( ) ;
@@ -6427,7 +6569,6 @@ pub mod tests {
6427
6569
) ;
6428
6570
let ( heaviest_bank, heaviest_bank_on_same_fork) = heaviest_subtree_fork_choice
6429
6571
. select_forks ( & frozen_banks, tower, progress, ancestors, bank_forks) ;
6430
- assert ! ( heaviest_bank_on_same_fork. is_none( ) ) ;
6431
6572
let SelectVoteAndResetForkResult {
6432
6573
vote_bank,
6433
6574
reset_bank,
0 commit comments