Relax gossip to banking stage filtering to allow refreshed votes (#31879)

This commit is contained in:
Ashwin Sekar 2023-05-31 16:50:36 -07:00 committed by GitHub
parent 9216ff8c3e
commit 1b79875f51
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 131 additions and 7 deletions

View File

@ -8,7 +8,7 @@ use {
}, },
solana_sdk::{ solana_sdk::{
account::from_account, account::from_account,
clock::Slot, clock::{Slot, UnixTimestamp},
feature_set::{allow_votes_to_directly_update_vote_state, FeatureSet}, feature_set::{allow_votes_to_directly_update_vote_state, FeatureSet},
hash::Hash, hash::Hash,
pubkey::Pubkey, pubkey::Pubkey,
@ -138,6 +138,7 @@ impl<'a> Iterator for ValidatorGossipVotesIterator<'a> {
hash, hash,
packet_batch, packet_batch,
signature, signature,
..
}) => self }) => self
.filter_vote(slot, hash, packet_batch, signature) .filter_vote(slot, hash, packet_batch, signature)
.map(|packet| vec![packet]) .map(|packet| vec![packet])
@ -170,6 +171,7 @@ pub struct GossipVote {
hash: Hash, hash: Hash,
packet_batch: PacketBatch, packet_batch: PacketBatch,
signature: Signature, signature: Signature,
timestamp: Option<UnixTimestamp>,
} }
pub enum SingleValidatorVotes { pub enum SingleValidatorVotes {
@ -185,6 +187,13 @@ impl SingleValidatorVotes {
} }
} }
fn get_latest_timestamp(&self) -> Option<UnixTimestamp> {
match self {
Self::FullTowerVote(vote) => vote.timestamp,
_ => None,
}
}
#[cfg(test)] #[cfg(test)]
fn len(&self) -> usize { fn len(&self) -> usize {
match self { match self {
@ -229,15 +238,24 @@ impl VerifiedVotePackets {
} }
let slot = vote.last_voted_slot().unwrap(); let slot = vote.last_voted_slot().unwrap();
let hash = vote.hash(); let hash = vote.hash();
let timestamp = vote.timestamp();
match (vote, is_full_tower_vote_enabled) { match (vote, is_full_tower_vote_enabled) {
(VoteStateUpdate(_), true) => { (VoteStateUpdate(_), true) => {
let latest_gossip_slot = match self.0.get(&vote_account_key) { let (latest_gossip_slot, latest_timestamp) =
Some(vote) => vote.get_latest_gossip_slot(), self.0.get(&vote_account_key).map_or((0, None), |vote| {
_ => 0, (vote.get_latest_gossip_slot(), vote.get_latest_timestamp())
}; });
// Since votes are not incremental, we keep only the latest vote // Since votes are not incremental, we keep only the latest vote
if slot > latest_gossip_slot { // If the vote is for the same slot we will only allow it if
// it has a later timestamp (refreshed vote)
//
// Timestamp can be None if something was wrong with the senders clock.
// We directly compare as Options to ensure that votes with proper
// timestamps have precedence (Some is > None).
if slot > latest_gossip_slot
|| ((slot == latest_gossip_slot) && (timestamp > latest_timestamp))
{
self.0.insert( self.0.insert(
vote_account_key, vote_account_key,
FullTowerVote(GossipVote { FullTowerVote(GossipVote {
@ -245,6 +263,7 @@ impl VerifiedVotePackets {
hash, hash,
packet_batch, packet_batch,
signature, signature,
timestamp,
}), }),
); );
} }
@ -264,6 +283,7 @@ impl VerifiedVotePackets {
hash, hash,
packet_batch, packet_batch,
signature, signature,
..
} = std::mem::take(gossip_vote); } = std::mem::take(gossip_vote);
votes.insert((slot, hash), (packet_batch, signature)); votes.insert((slot, hash), (packet_batch, signature));
self.0.insert(vote_account_key, IncrementalVotes(votes)); self.0.insert(vote_account_key, IncrementalVotes(votes));
@ -301,7 +321,7 @@ mod tests {
use { use {
super::{SingleValidatorVotes::*, *}, super::{SingleValidatorVotes::*, *},
crate::{result::Error, vote_simulator::VoteSimulator}, crate::{result::Error, vote_simulator::VoteSimulator},
crossbeam_channel::unbounded, crossbeam_channel::{unbounded, Receiver, Sender},
solana_perf::packet::Packet, solana_perf::packet::Packet,
solana_sdk::slot_hashes::MAX_ENTRIES, solana_sdk::slot_hashes::MAX_ENTRIES,
solana_vote_program::vote_state::{Lockout, Vote, VoteStateUpdate}, solana_vote_program::vote_state::{Lockout, Vote, VoteStateUpdate},
@ -680,6 +700,110 @@ mod tests {
); );
} }
fn send_vote_state_update_and_process(
s: &Sender<Vec<VerifiedVoteMetadata>>,
r: &Receiver<Vec<VerifiedVoteMetadata>>,
vote: VoteStateUpdate,
vote_account_key: Pubkey,
feature_set: Option<Arc<FeatureSet>>,
verified_vote_packets: &mut VerifiedVotePackets,
) -> GossipVote {
s.send(vec![VerifiedVoteMetadata {
vote_account_key,
vote: VoteTransaction::from(vote),
packet_batch: PacketBatch::default(),
signature: Signature::new(&[1u8; 64]),
}])
.unwrap();
verified_vote_packets
.receive_and_process_vote_packets(r, true, feature_set)
.unwrap();
match verified_vote_packets.0.get(&vote_account_key).unwrap() {
SingleValidatorVotes::FullTowerVote(gossip_vote) => gossip_vote.clone(),
_ => panic!("Received incremental vote"),
}
}
#[test]
fn test_latest_vote_tie_break_with_feature() {
let (s, r) = unbounded();
let vote_account_key = solana_sdk::pubkey::new_rand();
// Send identical vote state updates with different timestamps
let mut vote = VoteStateUpdate::from(vec![(2, 4), (4, 3), (6, 2), (7, 1)]);
vote.timestamp = Some(5);
let mut vote_later_ts = vote.clone();
vote_later_ts.timestamp = Some(6);
let mut vote_earlier_ts = vote.clone();
vote_earlier_ts.timestamp = Some(4);
let mut vote_no_ts = vote.clone();
vote_no_ts.timestamp = None;
let mut verified_vote_packets = VerifiedVotePackets(HashMap::new());
let mut feature_set = FeatureSet::default();
feature_set.activate(&allow_votes_to_directly_update_vote_state::id(), 0);
let feature_set = Some(Arc::new(feature_set));
// Original vote
let GossipVote {
slot, timestamp, ..
} = send_vote_state_update_and_process(
&s,
&r,
vote.clone(),
vote_account_key,
feature_set.clone(),
&mut verified_vote_packets,
);
assert_eq!(slot, vote.last_voted_slot().unwrap());
assert_eq!(timestamp, vote.timestamp);
// Same vote with later timestamp should override
let GossipVote {
slot, timestamp, ..
} = send_vote_state_update_and_process(
&s,
&r,
vote_later_ts.clone(),
vote_account_key,
feature_set.clone(),
&mut verified_vote_packets,
);
assert_eq!(slot, vote_later_ts.last_voted_slot().unwrap());
assert_eq!(timestamp, vote_later_ts.timestamp);
// Same vote with earlier timestamp should not override
let GossipVote {
slot, timestamp, ..
} = send_vote_state_update_and_process(
&s,
&r,
vote_earlier_ts,
vote_account_key,
feature_set.clone(),
&mut verified_vote_packets,
);
assert_eq!(slot, vote_later_ts.last_voted_slot().unwrap());
assert_eq!(timestamp, vote_later_ts.timestamp);
// Same vote with no timestamp should not override
let GossipVote {
slot, timestamp, ..
} = send_vote_state_update_and_process(
&s,
&r,
vote_no_ts,
vote_account_key,
feature_set,
&mut verified_vote_packets,
);
assert_eq!(slot, vote_later_ts.last_voted_slot().unwrap());
assert_eq!(timestamp, vote_later_ts.timestamp);
}
#[test] #[test]
fn test_latest_vote_feature_upgrade() { fn test_latest_vote_feature_upgrade() {
let (s, r) = unbounded(); let (s, r) = unbounded();