Relax gossip to banking stage filtering to allow refreshed votes (#31879)
This commit is contained in:
parent
9216ff8c3e
commit
1b79875f51
|
@ -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();
|
||||||
|
|
Loading…
Reference in New Issue