@@ -1992,6 +1992,14 @@ impl ReplayStage {
1992
1992
}
1993
1993
}
1994
1994
1995
+ /// Checks if it is time for us to start producing a leader block.
1996
+ /// Fails if:
1997
+ /// - Current PoH has not satisfied criteria to start my leader block
1998
+ /// - Startup verification is not complete,
1999
+ /// - Bank forks already contains a bank for this leader slot
2000
+ /// - We have not landed a vote yet and the `wait_for_vote_to_start_leader` flag is set
2001
+ /// - We have failed the propagated check
2002
+ /// Returns whether a new working bank was created and inserted into bank forks.
1995
2003
#[ allow( clippy:: too_many_arguments) ]
1996
2004
fn maybe_start_leader (
1997
2005
my_pubkey : & Pubkey ,
@@ -2005,7 +2013,7 @@ impl ReplayStage {
2005
2013
banking_tracer : & Arc < BankingTracer > ,
2006
2014
has_new_vote_been_rooted : bool ,
2007
2015
track_transaction_indexes : bool ,
2008
- ) {
2016
+ ) -> bool {
2009
2017
// all the individual calls to poh_recorder.read() are designed to
2010
2018
// increase granularity, decrease contention
2011
2019
@@ -2019,7 +2027,7 @@ impl ReplayStage {
2019
2027
} => ( poh_slot, parent_slot) ,
2020
2028
PohLeaderStatus :: NotReached => {
2021
2029
trace ! ( "{} poh_recorder hasn't reached_leader_slot" , my_pubkey) ;
2022
- return ;
2030
+ return false ;
2023
2031
}
2024
2032
} ;
2025
2033
@@ -2035,12 +2043,12 @@ impl ReplayStage {
2035
2043
2036
2044
if !parent. is_startup_verification_complete ( ) {
2037
2045
info ! ( "startup verification incomplete, so skipping my leader slot" ) ;
2038
- return ;
2046
+ return false ;
2039
2047
}
2040
2048
2041
2049
if bank_forks. read ( ) . unwrap ( ) . get ( poh_slot) . is_some ( ) {
2042
2050
warn ! ( "{} already have bank in forks at {}?" , my_pubkey, poh_slot) ;
2043
- return ;
2051
+ return false ;
2044
2052
}
2045
2053
trace ! (
2046
2054
"{} poh_slot {} parent_slot {}" ,
@@ -2052,7 +2060,7 @@ impl ReplayStage {
2052
2060
if let Some ( next_leader) = leader_schedule_cache. slot_leader_at ( poh_slot, Some ( & parent) ) {
2053
2061
if !has_new_vote_been_rooted {
2054
2062
info ! ( "Haven't landed a vote, so skipping my leader slot" ) ;
2055
- return ;
2063
+ return false ;
2056
2064
}
2057
2065
2058
2066
trace ! (
@@ -2064,7 +2072,7 @@ impl ReplayStage {
2064
2072
2065
2073
// I guess I missed my slot
2066
2074
if next_leader != * my_pubkey {
2067
- return ;
2075
+ return false ;
2068
2076
}
2069
2077
2070
2078
datapoint_info ! (
@@ -2098,7 +2106,7 @@ impl ReplayStage {
2098
2106
latest_unconfirmed_leader_slot,
2099
2107
) ;
2100
2108
}
2101
- return ;
2109
+ return false ;
2102
2110
}
2103
2111
2104
2112
let root_slot = bank_forks. read ( ) . unwrap ( ) . root ( ) ;
@@ -2133,8 +2141,10 @@ impl ReplayStage {
2133
2141
. write ( )
2134
2142
. unwrap ( )
2135
2143
. set_bank ( tpu_bank, track_transaction_indexes) ;
2144
+ true
2136
2145
} else {
2137
2146
error ! ( "{} No next leader found" , my_pubkey) ;
2147
+ false
2138
2148
}
2139
2149
}
2140
2150
@@ -9097,4 +9107,124 @@ pub(crate) mod tests {
9097
9107
. is_candidate( & ( 5 , bank_forks. bank_hash( 5 ) . unwrap( ) ) )
9098
9108
. unwrap( ) ) ;
9099
9109
}
9110
+
9111
+ #[ test]
9112
+ fn test_skip_leader_slot_for_existing_slot ( ) {
9113
+ solana_logger:: setup ( ) ;
9114
+
9115
+ let ReplayBlockstoreComponents {
9116
+ blockstore,
9117
+ my_pubkey,
9118
+ leader_schedule_cache,
9119
+ poh_recorder,
9120
+ vote_simulator,
9121
+ rpc_subscriptions,
9122
+ ..
9123
+ } = replay_blockstore_components ( None , 1 , None ) ;
9124
+ let VoteSimulator {
9125
+ bank_forks,
9126
+ mut progress,
9127
+ ..
9128
+ } = vote_simulator;
9129
+
9130
+ let working_bank = bank_forks. read ( ) . unwrap ( ) . working_bank ( ) ;
9131
+ assert ! ( working_bank. is_complete( ) ) ;
9132
+ assert ! ( working_bank. is_frozen( ) ) ;
9133
+ // Mark startup verification as complete to avoid skipping leader slots
9134
+ working_bank. set_startup_verification_complete ( ) ;
9135
+
9136
+ // Insert a block two slots greater than current bank. This slot does
9137
+ // not have a corresponding Bank in BankForks; this emulates a scenario
9138
+ // where the block had previously been created and added to BankForks,
9139
+ // but then got removed. This could be the case if the Bank was not on
9140
+ // the major fork.
9141
+ let dummy_slot = working_bank. slot ( ) + 2 ;
9142
+ let initial_slot = working_bank. slot ( ) ;
9143
+ let num_entries = 10 ;
9144
+ let merkle_variant = true ;
9145
+ let ( shreds, _) = make_slot_entries ( dummy_slot, initial_slot, num_entries, merkle_variant) ;
9146
+ blockstore. insert_shreds ( shreds, None , false ) . unwrap ( ) ;
9147
+
9148
+ // Reset PoH recorder to the completed bank to ensure consistent state
9149
+ ReplayStage :: reset_poh_recorder (
9150
+ & my_pubkey,
9151
+ & blockstore,
9152
+ working_bank. clone ( ) ,
9153
+ & poh_recorder,
9154
+ & leader_schedule_cache,
9155
+ ) ;
9156
+
9157
+ // Register just over one slot worth of ticks directly with PoH recorder
9158
+ let num_poh_ticks =
9159
+ ( working_bank. ticks_per_slot ( ) * working_bank. hashes_per_tick ( ) . unwrap ( ) ) + 1 ;
9160
+ poh_recorder
9161
+ . write ( )
9162
+ . map ( |mut poh_recorder| {
9163
+ for _ in 0 ..num_poh_ticks + 1 {
9164
+ poh_recorder. tick ( ) ;
9165
+ }
9166
+ } )
9167
+ . unwrap ( ) ;
9168
+
9169
+ let poh_recorder = Arc :: new ( poh_recorder) ;
9170
+ let ( retransmit_slots_sender, _) = unbounded ( ) ;
9171
+ let ( banking_tracer, _) = BankingTracer :: new ( None ) . unwrap ( ) ;
9172
+ // A vote has not technically been rooted, but it doesn't matter for
9173
+ // this test to use true to avoid skipping the leader slot
9174
+ let has_new_vote_been_rooted = true ;
9175
+ let track_transaction_indexes = false ;
9176
+
9177
+ // We should not attempt to start leader for the dummy_slot
9178
+ assert_matches ! (
9179
+ poh_recorder. read( ) . unwrap( ) . reached_leader_slot( & my_pubkey) ,
9180
+ PohLeaderStatus :: NotReached
9181
+ ) ;
9182
+ assert ! ( !ReplayStage :: maybe_start_leader(
9183
+ & my_pubkey,
9184
+ & bank_forks,
9185
+ & poh_recorder,
9186
+ & leader_schedule_cache,
9187
+ & rpc_subscriptions,
9188
+ & mut progress,
9189
+ & retransmit_slots_sender,
9190
+ & mut SkippedSlotsInfo :: default ( ) ,
9191
+ & banking_tracer,
9192
+ has_new_vote_been_rooted,
9193
+ track_transaction_indexes,
9194
+ ) ) ;
9195
+
9196
+ // Register another slots worth of ticks with PoH recorder
9197
+ poh_recorder
9198
+ . write ( )
9199
+ . map ( |mut poh_recorder| {
9200
+ for _ in 0 ..num_poh_ticks + 1 {
9201
+ poh_recorder. tick ( ) ;
9202
+ }
9203
+ } )
9204
+ . unwrap ( ) ;
9205
+
9206
+ // We should now start leader for dummy_slot + 1
9207
+ let good_slot = dummy_slot + 1 ;
9208
+ assert ! ( ReplayStage :: maybe_start_leader(
9209
+ & my_pubkey,
9210
+ & bank_forks,
9211
+ & poh_recorder,
9212
+ & leader_schedule_cache,
9213
+ & rpc_subscriptions,
9214
+ & mut progress,
9215
+ & retransmit_slots_sender,
9216
+ & mut SkippedSlotsInfo :: default ( ) ,
9217
+ & banking_tracer,
9218
+ has_new_vote_been_rooted,
9219
+ track_transaction_indexes,
9220
+ ) ) ;
9221
+ // Get the new working bank, which is also the new leader bank/slot
9222
+ let working_bank = bank_forks. read ( ) . unwrap ( ) . working_bank ( ) ;
9223
+ // The new bank's slot must NOT be dummy_slot as the blockstore already
9224
+ // had a shred inserted for dummy_slot prior to maybe_start_leader().
9225
+ // maybe_start_leader() must not pick dummy_slot to avoid creating a
9226
+ // duplicate block.
9227
+ assert_eq ! ( working_bank. slot( ) , good_slot) ;
9228
+ assert_eq ! ( working_bank. parent_slot( ) , initial_slot) ;
9229
+ }
9100
9230
}
0 commit comments