@@ -1285,8 +1285,14 @@ impl ReplayStage {
1285
1285
) -> ( ProgressMap , HeaviestSubtreeForkChoice ) {
1286
1286
let ( root_bank, frozen_banks, duplicate_slot_hashes) = {
1287
1287
let bank_forks = bank_forks. read ( ) . unwrap ( ) ;
1288
+ let root_bank = bank_forks. root_bank ( ) ;
1288
1289
let duplicate_slots = blockstore
1289
- . duplicate_slots_iterator ( bank_forks. root_bank ( ) . slot ( ) )
1290
+ // It is important that the root bank is not marked as duplicate on initialization.
1291
+ // Although this bank could contain a duplicate proof, the fact that it was rooted
1292
+ // either during a previous run or artificially means that we should ignore any
1293
+ // duplicate proofs for the root slot, thus we start consuming duplicate proofs
1294
+ // from the root slot + 1
1295
+ . duplicate_slots_iterator ( root_bank. slot ( ) . saturating_add ( 1 ) )
1290
1296
. unwrap ( ) ;
1291
1297
let duplicate_slot_hashes = duplicate_slots. filter_map ( |slot| {
1292
1298
let bank = bank_forks. get ( slot) ?;
@@ -1295,7 +1301,7 @@ impl ReplayStage {
1295
1301
. then_some ( ( slot, bank. hash ( ) ) )
1296
1302
} ) ;
1297
1303
(
1298
- bank_forks . root_bank ( ) ,
1304
+ root_bank,
1299
1305
bank_forks. frozen_banks ( ) . values ( ) . cloned ( ) . collect ( ) ,
1300
1306
duplicate_slot_hashes. collect :: < Vec < ( Slot , Hash ) > > ( ) ,
1301
1307
)
@@ -4310,6 +4316,9 @@ pub(crate) mod tests {
4310
4316
replay_stage:: ReplayStage ,
4311
4317
vote_simulator:: { self , VoteSimulator } ,
4312
4318
} ,
4319
+ blockstore_processor:: {
4320
+ confirm_full_slot, fill_blockstore_slot_with_ticks, process_bank_0, ProcessOptions ,
4321
+ } ,
4313
4322
crossbeam_channel:: unbounded,
4314
4323
itertools:: Itertools ,
4315
4324
solana_entry:: entry:: { self , Entry } ,
@@ -8963,4 +8972,125 @@ pub(crate) mod tests {
8963
8972
& mut PurgeRepairSlotCounter :: default ( ) ,
8964
8973
) ;
8965
8974
}
8975
+
8976
+ #[ test]
8977
+ fn test_initialize_progress_and_fork_choice_with_duplicates ( ) {
8978
+ solana_logger:: setup ( ) ;
8979
+ let GenesisConfigInfo {
8980
+ mut genesis_config, ..
8981
+ } = create_genesis_config ( 123 ) ;
8982
+
8983
+ let ticks_per_slot = 1 ;
8984
+ genesis_config. ticks_per_slot = ticks_per_slot;
8985
+ let ( ledger_path, blockhash) =
8986
+ solana_ledger:: create_new_tmp_ledger_auto_delete!( & genesis_config) ;
8987
+ let blockstore = Blockstore :: open ( ledger_path. path ( ) ) . unwrap ( ) ;
8988
+
8989
+ /*
8990
+ Bank forks with:
8991
+ slot 0
8992
+ |
8993
+ slot 1 -> Duplicate before restart, the restart slot
8994
+ |
8995
+ slot 2
8996
+ |
8997
+ slot 3 -> Duplicate before restart, artificially rooted
8998
+ |
8999
+ slot 4 -> Duplicate before restart, artificially rooted
9000
+ |
9001
+ slot 5 -> Duplicate before restart
9002
+ |
9003
+ slot 6
9004
+ */
9005
+
9006
+ let mut last_hash = blockhash;
9007
+ for i in 0 ..6 {
9008
+ last_hash =
9009
+ fill_blockstore_slot_with_ticks ( & blockstore, ticks_per_slot, i + 1 , i, last_hash) ;
9010
+ }
9011
+ // Artifically root 3 and 4
9012
+ blockstore. set_roots ( [ 3 , 4 ] . iter ( ) ) . unwrap ( ) ;
9013
+
9014
+ // Set up bank0
9015
+ let bank_forks = BankForks :: new_rw_arc ( Bank :: new_for_tests ( & genesis_config) ) ;
9016
+ let bank0 = bank_forks. read ( ) . unwrap ( ) . get_with_scheduler ( 0 ) . unwrap ( ) ;
9017
+ let recyclers = VerifyRecyclers :: default ( ) ;
9018
+
9019
+ process_bank_0 (
9020
+ & bank0,
9021
+ & blockstore,
9022
+ & ProcessOptions :: default ( ) ,
9023
+ & recyclers,
9024
+ None ,
9025
+ None ,
9026
+ ) ;
9027
+
9028
+ // Mark block 1, 3, 4, 5 as duplicate
9029
+ blockstore. store_duplicate_slot ( 1 , vec ! [ ] , vec ! [ ] ) . unwrap ( ) ;
9030
+ blockstore. store_duplicate_slot ( 3 , vec ! [ ] , vec ! [ ] ) . unwrap ( ) ;
9031
+ blockstore. store_duplicate_slot ( 4 , vec ! [ ] , vec ! [ ] ) . unwrap ( ) ;
9032
+ blockstore. store_duplicate_slot ( 5 , vec ! [ ] , vec ! [ ] ) . unwrap ( ) ;
9033
+
9034
+ let bank1 = bank_forks. write ( ) . unwrap ( ) . insert ( Bank :: new_from_parent (
9035
+ bank0. clone_without_scheduler ( ) ,
9036
+ & Pubkey :: default ( ) ,
9037
+ 1 ,
9038
+ ) ) ;
9039
+ confirm_full_slot (
9040
+ & blockstore,
9041
+ & bank1,
9042
+ & ProcessOptions :: default ( ) ,
9043
+ & recyclers,
9044
+ & mut ConfirmationProgress :: new ( bank0. last_blockhash ( ) ) ,
9045
+ None ,
9046
+ None ,
9047
+ None ,
9048
+ & mut ExecuteTimings :: default ( ) ,
9049
+ )
9050
+ . unwrap ( ) ;
9051
+
9052
+ bank_forks. write ( ) . unwrap ( ) . set_root (
9053
+ 1 ,
9054
+ & solana_runtime:: accounts_background_service:: AbsRequestSender :: default ( ) ,
9055
+ None ,
9056
+ ) ;
9057
+
9058
+ let leader_schedule_cache = LeaderScheduleCache :: new_from_bank ( & bank1) ;
9059
+
9060
+ // process_blockstore_from_root() from slot 1 onwards
9061
+ blockstore_processor:: process_blockstore_from_root (
9062
+ & blockstore,
9063
+ & bank_forks,
9064
+ & leader_schedule_cache,
9065
+ & ProcessOptions :: default ( ) ,
9066
+ None ,
9067
+ None ,
9068
+ None ,
9069
+ & AbsRequestSender :: default ( ) ,
9070
+ )
9071
+ . unwrap ( ) ;
9072
+
9073
+ assert_eq ! ( bank_forks. read( ) . unwrap( ) . root( ) , 4 ) ;
9074
+
9075
+ // Verify that fork choice can be initialized and that the root is not marked duplicate
9076
+ let ( _progress, fork_choice) =
9077
+ ReplayStage :: initialize_progress_and_fork_choice_with_locked_bank_forks (
9078
+ & bank_forks,
9079
+ & Pubkey :: new_unique ( ) ,
9080
+ & Pubkey :: new_unique ( ) ,
9081
+ & blockstore,
9082
+ ) ;
9083
+
9084
+ let bank_forks = bank_forks. read ( ) . unwrap ( ) ;
9085
+ // 4 (the artificial root) is the tree root and no longer duplicate
9086
+ assert_eq ! ( fork_choice. tree_root( ) . 0 , 4 ) ;
9087
+ assert ! ( fork_choice
9088
+ . is_candidate( & ( 4 , bank_forks. bank_hash( 4 ) . unwrap( ) ) )
9089
+ . unwrap( ) ) ;
9090
+
9091
+ // 5 is still considered duplicate, so it is not a valid fork choice candidate
9092
+ assert ! ( !fork_choice
9093
+ . is_candidate( & ( 5 , bank_forks. bank_hash( 5 ) . unwrap( ) ) )
9094
+ . unwrap( ) ) ;
9095
+ }
8966
9096
}
0 commit comments