8
8
solana_perf:: packet:: Packet ,
9
9
solana_runtime:: bank:: Bank ,
10
10
solana_sdk:: {
11
+ account:: from_account,
11
12
clock:: { Slot , UnixTimestamp } ,
13
+ hash:: Hash ,
12
14
program_utils:: limited_deserialize,
13
15
pubkey:: Pubkey ,
16
+ slot_hashes:: SlotHashes ,
17
+ sysvar,
14
18
} ,
15
19
solana_vote_program:: vote_instruction:: VoteInstruction ,
16
20
std:: {
@@ -36,6 +40,7 @@ pub struct LatestValidatorVotePacket {
36
40
pubkey : Pubkey ,
37
41
vote : Option < Arc < ImmutableDeserializedPacket > > ,
38
42
slot : Slot ,
43
+ hash : Hash ,
39
44
forwarded : bool ,
40
45
timestamp : Option < UnixTimestamp > ,
41
46
}
@@ -70,11 +75,13 @@ impl LatestValidatorVotePacket {
70
75
. first ( )
71
76
. ok_or ( DeserializedPacketError :: VoteTransactionError ) ?;
72
77
let slot = vote_state_update_instruction. last_voted_slot ( ) . unwrap_or ( 0 ) ;
78
+ let hash = vote_state_update_instruction. hash ( ) ;
73
79
let timestamp = vote_state_update_instruction. timestamp ( ) ;
74
80
75
81
Ok ( Self {
76
82
vote : Some ( vote) ,
77
83
slot,
84
+ hash,
78
85
pubkey,
79
86
vote_source,
80
87
forwarded : false ,
@@ -97,6 +104,10 @@ impl LatestValidatorVotePacket {
97
104
self . slot
98
105
}
99
106
107
+ pub ( crate ) fn hash ( & self ) -> Hash {
108
+ self . hash
109
+ }
110
+
100
111
pub fn timestamp ( & self ) -> Option < UnixTimestamp > {
101
112
self . timestamp
102
113
}
@@ -115,9 +126,6 @@ impl LatestValidatorVotePacket {
115
126
}
116
127
}
117
128
118
- // TODO: replace this with rand::seq::index::sample_weighted once we can update rand to 0.8+
119
- // This requires updating dependencies of ed25519-dalek as rand_core is not compatible cross
120
- // version https://github.com/dalek-cryptography/ed25519-dalek/pull/214
121
129
pub ( crate ) fn weighted_random_order_by_stake < ' a > (
122
130
bank : & Bank ,
123
131
pubkeys : impl Iterator < Item = & ' a Pubkey > ,
@@ -322,17 +330,30 @@ impl LatestUnprocessedVotes {
322
330
}
323
331
324
332
/// Drains all votes yet to be processed sorted by a weighted random ordering by stake
333
+ /// Do not touch votes that are for a different fork from `bank` as we know they will fail,
334
+ /// however the next bank could be built on a different fork and consume these votes.
325
335
pub fn drain_unprocessed ( & self , bank : Arc < Bank > ) -> Vec < Arc < ImmutableDeserializedPacket > > {
326
- let pubkeys_by_stake = weighted_random_order_by_stake (
327
- & bank,
328
- self . latest_votes_per_pubkey . read ( ) . unwrap ( ) . keys ( ) ,
329
- )
330
- . collect_vec ( ) ;
336
+ let slot_hashes = bank
337
+ . get_account ( & sysvar:: slot_hashes:: id ( ) )
338
+ . and_then ( |account| from_account :: < SlotHashes , _ > ( & account) ) ;
339
+ if slot_hashes. is_none ( ) {
340
+ error ! (
341
+ "Slot hashes sysvar doesn't exist on bank {}. Including all votes without filtering" ,
342
+ bank. slot( )
343
+ ) ;
344
+ }
345
+
346
+ let pubkeys_by_stake = {
347
+ let binding = self . latest_votes_per_pubkey . read ( ) . unwrap ( ) ;
348
+ weighted_random_order_by_stake ( & bank, binding. keys ( ) )
349
+ } ;
331
350
pubkeys_by_stake
332
- . into_iter ( )
333
351
. filter_map ( |pubkey| {
334
352
self . get_entry ( pubkey) . and_then ( |lock| {
335
353
let mut latest_vote = lock. write ( ) . unwrap ( ) ;
354
+ if !Self :: is_valid_for_our_fork ( & latest_vote, & slot_hashes) {
355
+ return None ;
356
+ }
336
357
latest_vote. take_vote ( ) . map ( |vote| {
337
358
self . num_unprocessed_votes . fetch_sub ( 1 , Ordering :: Relaxed ) ;
338
359
vote
@@ -342,6 +363,21 @@ impl LatestUnprocessedVotes {
342
363
. collect_vec ( )
343
364
}
344
365
366
+ /// Check if `vote` can land in our fork based on `slot_hashes`
367
+ fn is_valid_for_our_fork (
368
+ vote : & LatestValidatorVotePacket ,
369
+ slot_hashes : & Option < SlotHashes > ,
370
+ ) -> bool {
371
+ let Some ( slot_hashes) = slot_hashes else {
372
+ // When slot hashes is not present we do not filter
373
+ return true ;
374
+ } ;
375
+ slot_hashes
376
+ . get ( & vote. slot ( ) )
377
+ . map ( |found_hash| * found_hash == vote. hash ( ) )
378
+ . unwrap_or ( false )
379
+ }
380
+
345
381
/// Sometimes we forward and hold the packets, sometimes we forward and clear.
346
382
/// This also clears all gossip votes since by definition they have been forwarded
347
383
pub fn clear_forwarded_packets ( & self ) {
0 commit comments