2020-03-15 20:31:05 -07:00
|
|
|
use crate::{
|
|
|
|
cluster_info::{ClusterInfo, GOSSIP_SLEEP_MILLIS},
|
|
|
|
crds_value::CrdsValueLabel,
|
2020-07-28 02:33:27 -07:00
|
|
|
optimistic_confirmation_verifier::OptimisticConfirmationVerifier,
|
2020-09-28 19:43:05 -07:00
|
|
|
optimistically_confirmed_bank_tracker::{BankNotification, BankNotificationSender},
|
2020-03-15 20:31:05 -07:00
|
|
|
poh_recorder::PohRecorder,
|
|
|
|
result::{Error, Result},
|
2020-05-17 14:01:08 -07:00
|
|
|
rpc_subscriptions::RpcSubscriptions,
|
2020-03-15 20:31:05 -07:00
|
|
|
sigverify,
|
|
|
|
verified_vote_packets::VerifiedVotePackets,
|
2020-07-28 02:33:27 -07:00
|
|
|
vote_stake_tracker::VoteStakeTracker,
|
2020-03-15 20:31:05 -07:00
|
|
|
};
|
2020-03-09 22:03:09 -07:00
|
|
|
use crossbeam_channel::{
|
2020-07-20 17:29:07 -07:00
|
|
|
unbounded, Receiver as CrossbeamReceiver, RecvTimeoutError, Select, Sender as CrossbeamSender,
|
2020-03-09 22:03:09 -07:00
|
|
|
};
|
2020-03-15 20:31:05 -07:00
|
|
|
use itertools::izip;
|
2020-03-09 22:03:09 -07:00
|
|
|
use log::*;
|
2020-08-07 11:21:35 -07:00
|
|
|
use solana_ledger::blockstore::Blockstore;
|
2019-05-17 07:00:06 -07:00
|
|
|
use solana_metrics::inc_new_counter_debug;
|
2020-03-17 23:30:23 -07:00
|
|
|
use solana_perf::packet::{self, Packets};
|
2020-05-22 14:53:47 -07:00
|
|
|
use solana_runtime::{
|
|
|
|
bank::Bank,
|
2020-06-17 08:27:03 -07:00
|
|
|
bank_forks::BankForks,
|
2020-05-22 14:53:47 -07:00
|
|
|
epoch_stakes::{EpochAuthorizedVoters, EpochStakes},
|
2020-07-28 02:33:27 -07:00
|
|
|
stakes::Stakes,
|
2020-08-07 11:21:35 -07:00
|
|
|
vote_sender_types::{ReplayVoteReceiver, ReplayedVote},
|
2020-05-22 14:53:47 -07:00
|
|
|
};
|
2020-03-09 22:03:09 -07:00
|
|
|
use solana_sdk::{
|
2020-10-23 18:19:12 -07:00
|
|
|
clock::{Epoch, Slot, DEFAULT_MS_PER_SLOT},
|
2020-03-09 22:03:09 -07:00
|
|
|
epoch_schedule::EpochSchedule,
|
2020-07-28 02:33:27 -07:00
|
|
|
hash::Hash,
|
2020-03-09 22:03:09 -07:00
|
|
|
pubkey::Pubkey,
|
|
|
|
transaction::Transaction,
|
|
|
|
};
|
2020-07-29 23:17:40 -07:00
|
|
|
use solana_vote_program::{self, vote_state::Vote, vote_transaction};
|
2020-03-15 20:31:05 -07:00
|
|
|
use std::{
|
2020-07-29 23:17:40 -07:00
|
|
|
collections::HashMap,
|
2020-03-15 20:31:05 -07:00
|
|
|
sync::{
|
|
|
|
atomic::{AtomicBool, Ordering},
|
|
|
|
{Arc, Mutex, RwLock},
|
|
|
|
},
|
|
|
|
thread::{self, sleep, Builder, JoinHandle},
|
|
|
|
time::{Duration, Instant},
|
|
|
|
};
|
2019-01-31 15:51:29 -08:00
|
|
|
|
2020-03-09 22:03:09 -07:00
|
|
|
// Map from a vote account to the authorized voter for an epoch
|
2021-03-03 11:07:16 -08:00
|
|
|
pub type VerifiedLabelVotePacketsSender = CrossbeamSender<Vec<(CrdsValueLabel, Slot, Packets)>>;
|
|
|
|
pub type VerifiedLabelVotePacketsReceiver = CrossbeamReceiver<Vec<(CrdsValueLabel, Slot, Packets)>>;
|
2020-03-15 20:31:05 -07:00
|
|
|
pub type VerifiedVoteTransactionsSender = CrossbeamSender<Vec<Transaction>>;
|
|
|
|
pub type VerifiedVoteTransactionsReceiver = CrossbeamReceiver<Vec<Transaction>>;
|
2020-07-20 17:29:07 -07:00
|
|
|
pub type VerifiedVoteSender = CrossbeamSender<(Pubkey, Vec<Slot>)>;
|
|
|
|
pub type VerifiedVoteReceiver = CrossbeamReceiver<(Pubkey, Vec<Slot>)>;
|
2020-03-09 22:03:09 -07:00
|
|
|
|
2020-03-26 17:55:17 -07:00
|
|
|
#[derive(Default)]
|
2020-03-09 22:03:09 -07:00
|
|
|
pub struct SlotVoteTracker {
|
2020-07-20 17:29:07 -07:00
|
|
|
// Maps pubkeys that have voted for this slot
|
|
|
|
// to whether or not we've seen the vote on gossip.
|
|
|
|
// True if seen on gossip, false if only seen in replay.
|
2021-02-07 18:07:00 -08:00
|
|
|
voted: HashMap<Pubkey, bool>,
|
2020-07-28 02:33:27 -07:00
|
|
|
optimistic_votes_tracker: HashMap<Hash, VoteStakeTracker>,
|
2021-02-07 18:07:00 -08:00
|
|
|
updates: Option<Vec<Pubkey>>,
|
2020-07-20 17:29:07 -07:00
|
|
|
gossip_only_stake: u64,
|
2020-03-09 22:03:09 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl SlotVoteTracker {
|
|
|
|
#[allow(dead_code)]
|
2021-02-07 18:07:00 -08:00
|
|
|
pub fn get_updates(&mut self) -> Option<Vec<Pubkey>> {
|
2020-03-09 22:03:09 -07:00
|
|
|
self.updates.take()
|
|
|
|
}
|
2020-07-28 02:33:27 -07:00
|
|
|
|
|
|
|
pub fn get_or_insert_optimistic_votes_tracker(&mut self, hash: Hash) -> &mut VoteStakeTracker {
|
|
|
|
self.optimistic_votes_tracker.entry(hash).or_default()
|
|
|
|
}
|
|
|
|
pub fn optimistic_votes_tracker(&self, hash: &Hash) -> Option<&VoteStakeTracker> {
|
|
|
|
self.optimistic_votes_tracker.get(hash)
|
|
|
|
}
|
2020-03-09 22:03:09 -07:00
|
|
|
}
|
|
|
|
|
2020-03-26 17:55:17 -07:00
|
|
|
#[derive(Default)]
|
2020-03-09 22:03:09 -07:00
|
|
|
pub struct VoteTracker {
|
|
|
|
// Map from a slot to a set of validators who have voted for that slot
|
2020-07-09 22:52:54 -07:00
|
|
|
slot_vote_trackers: RwLock<HashMap<Slot, Arc<RwLock<SlotVoteTracker>>>>,
|
2020-03-09 22:03:09 -07:00
|
|
|
// Don't track votes from people who are not staked, acts as a spam filter
|
2020-03-26 17:55:17 -07:00
|
|
|
epoch_authorized_voters: RwLock<HashMap<Epoch, Arc<EpochAuthorizedVoters>>>,
|
|
|
|
leader_schedule_epoch: RwLock<Epoch>,
|
|
|
|
current_epoch: RwLock<Epoch>,
|
2020-03-09 22:03:09 -07:00
|
|
|
epoch_schedule: EpochSchedule,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl VoteTracker {
|
|
|
|
pub fn new(root_bank: &Bank) -> Self {
|
|
|
|
let current_epoch = root_bank.epoch();
|
|
|
|
let vote_tracker = Self {
|
2020-03-26 17:55:17 -07:00
|
|
|
leader_schedule_epoch: RwLock::new(current_epoch),
|
|
|
|
current_epoch: RwLock::new(current_epoch),
|
2020-03-09 22:03:09 -07:00
|
|
|
epoch_schedule: *root_bank.epoch_schedule(),
|
2020-03-26 17:55:17 -07:00
|
|
|
..VoteTracker::default()
|
2020-03-09 22:03:09 -07:00
|
|
|
};
|
2020-10-23 18:19:12 -07:00
|
|
|
vote_tracker.progress_with_new_root_bank(&root_bank);
|
2020-03-26 17:55:17 -07:00
|
|
|
assert_eq!(
|
|
|
|
*vote_tracker.leader_schedule_epoch.read().unwrap(),
|
|
|
|
root_bank.get_leader_schedule_epoch(root_bank.slot())
|
|
|
|
);
|
|
|
|
assert_eq!(*vote_tracker.current_epoch.read().unwrap(), current_epoch,);
|
2020-03-09 22:03:09 -07:00
|
|
|
vote_tracker
|
|
|
|
}
|
|
|
|
|
2020-07-28 02:33:27 -07:00
|
|
|
pub fn get_or_insert_slot_tracker(&self, slot: Slot) -> Arc<RwLock<SlotVoteTracker>> {
|
|
|
|
let mut slot_tracker = self.slot_vote_trackers.read().unwrap().get(&slot).cloned();
|
|
|
|
|
|
|
|
if slot_tracker.is_none() {
|
|
|
|
let new_slot_tracker = Arc::new(RwLock::new(SlotVoteTracker {
|
|
|
|
voted: HashMap::new(),
|
|
|
|
optimistic_votes_tracker: HashMap::default(),
|
|
|
|
updates: None,
|
|
|
|
gossip_only_stake: 0,
|
|
|
|
}));
|
|
|
|
self.slot_vote_trackers
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
.insert(slot, new_slot_tracker.clone());
|
|
|
|
slot_tracker = Some(new_slot_tracker);
|
|
|
|
}
|
|
|
|
|
|
|
|
slot_tracker.unwrap()
|
|
|
|
}
|
|
|
|
|
2020-03-09 22:03:09 -07:00
|
|
|
pub fn get_slot_vote_tracker(&self, slot: Slot) -> Option<Arc<RwLock<SlotVoteTracker>>> {
|
|
|
|
self.slot_vote_trackers.read().unwrap().get(&slot).cloned()
|
|
|
|
}
|
|
|
|
|
2020-03-26 17:55:17 -07:00
|
|
|
pub fn get_authorized_voter(&self, pubkey: &Pubkey, slot: Slot) -> Option<Pubkey> {
|
2020-03-09 22:03:09 -07:00
|
|
|
let epoch = self.epoch_schedule.get_epoch(slot);
|
|
|
|
self.epoch_authorized_voters
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.get(&epoch)
|
|
|
|
.map(|epoch_authorized_voters| epoch_authorized_voters.get(pubkey))
|
|
|
|
.unwrap_or(None)
|
|
|
|
.cloned()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn vote_contains_authorized_voter(
|
|
|
|
vote_tx: &Transaction,
|
|
|
|
authorized_voter: &Pubkey,
|
|
|
|
) -> bool {
|
|
|
|
let message = &vote_tx.message;
|
|
|
|
for (i, key) in message.account_keys.iter().enumerate() {
|
|
|
|
if message.is_signer(i) && key == authorized_voter {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
false
|
|
|
|
}
|
|
|
|
|
2020-03-26 17:55:17 -07:00
|
|
|
#[cfg(test)]
|
2021-02-07 18:07:00 -08:00
|
|
|
pub fn insert_vote(&self, slot: Slot, pubkey: Pubkey) {
|
2020-03-26 17:55:17 -07:00
|
|
|
let mut w_slot_vote_trackers = self.slot_vote_trackers.write().unwrap();
|
|
|
|
|
|
|
|
let slot_vote_tracker = w_slot_vote_trackers.entry(slot).or_default();
|
|
|
|
|
|
|
|
let mut w_slot_vote_tracker = slot_vote_tracker.write().unwrap();
|
|
|
|
|
2021-02-07 18:07:00 -08:00
|
|
|
w_slot_vote_tracker.voted.insert(pubkey, true);
|
2020-03-26 17:55:17 -07:00
|
|
|
if let Some(ref mut updates) = w_slot_vote_tracker.updates {
|
2021-02-07 18:07:00 -08:00
|
|
|
updates.push(pubkey)
|
2020-03-09 22:03:09 -07:00
|
|
|
} else {
|
2021-02-07 18:07:00 -08:00
|
|
|
w_slot_vote_tracker.updates = Some(vec![pubkey]);
|
2020-03-09 22:03:09 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-23 18:19:12 -07:00
|
|
|
fn progress_leader_schedule_epoch(&self, root_bank: &Bank) {
|
2020-03-26 17:55:17 -07:00
|
|
|
// Update with any newly calculated epoch state about future epochs
|
|
|
|
let start_leader_schedule_epoch = *self.leader_schedule_epoch.read().unwrap();
|
|
|
|
let mut greatest_leader_schedule_epoch = start_leader_schedule_epoch;
|
|
|
|
for leader_schedule_epoch in
|
|
|
|
start_leader_schedule_epoch..=root_bank.get_leader_schedule_epoch(root_bank.slot())
|
|
|
|
{
|
|
|
|
let exists = self
|
|
|
|
.epoch_authorized_voters
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.contains_key(&leader_schedule_epoch);
|
|
|
|
if !exists {
|
|
|
|
let epoch_authorized_voters = root_bank
|
|
|
|
.epoch_stakes(leader_schedule_epoch)
|
|
|
|
.unwrap()
|
|
|
|
.epoch_authorized_voters()
|
|
|
|
.clone();
|
|
|
|
self.epoch_authorized_voters
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
.insert(leader_schedule_epoch, epoch_authorized_voters);
|
|
|
|
greatest_leader_schedule_epoch = leader_schedule_epoch;
|
|
|
|
}
|
2020-03-09 22:03:09 -07:00
|
|
|
}
|
|
|
|
|
2020-03-26 17:55:17 -07:00
|
|
|
if greatest_leader_schedule_epoch != start_leader_schedule_epoch {
|
|
|
|
*self.leader_schedule_epoch.write().unwrap() = greatest_leader_schedule_epoch;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-23 18:19:12 -07:00
|
|
|
fn purge_stale_state(&self, root_bank: &Bank) {
|
2020-03-26 17:55:17 -07:00
|
|
|
// Purge any outdated slot data
|
|
|
|
let new_root = root_bank.slot();
|
|
|
|
let root_epoch = root_bank.epoch();
|
|
|
|
self.slot_vote_trackers
|
2020-03-09 22:03:09 -07:00
|
|
|
.write()
|
|
|
|
.unwrap()
|
2020-03-26 17:55:17 -07:00
|
|
|
.retain(|slot, _| *slot >= new_root);
|
|
|
|
|
|
|
|
let current_epoch = *self.current_epoch.read().unwrap();
|
|
|
|
if root_epoch != current_epoch {
|
|
|
|
// If root moved to a new epoch, purge outdated state
|
|
|
|
self.epoch_authorized_voters
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
2020-10-23 18:19:12 -07:00
|
|
|
.retain(|epoch, _| *epoch >= root_epoch);
|
2020-03-26 17:55:17 -07:00
|
|
|
*self.current_epoch.write().unwrap() = root_epoch;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-23 18:19:12 -07:00
|
|
|
fn progress_with_new_root_bank(&self, root_bank: &Bank) {
|
|
|
|
self.progress_leader_schedule_epoch(root_bank);
|
|
|
|
self.purge_stale_state(root_bank);
|
2020-03-09 22:03:09 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-31 15:51:29 -08:00
|
|
|
pub struct ClusterInfoVoteListener {
|
|
|
|
thread_hdls: Vec<JoinHandle<()>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ClusterInfoVoteListener {
|
2020-07-28 02:33:27 -07:00
|
|
|
#[allow(clippy::too_many_arguments)]
|
2019-01-31 15:51:29 -08:00
|
|
|
pub fn new(
|
2019-03-04 19:53:50 -08:00
|
|
|
exit: &Arc<AtomicBool>,
|
2020-04-21 12:54:45 -07:00
|
|
|
cluster_info: Arc<ClusterInfo>,
|
2020-07-09 22:52:54 -07:00
|
|
|
verified_packets_sender: CrossbeamSender<Vec<Packets>>,
|
2019-04-09 22:06:32 -07:00
|
|
|
poh_recorder: &Arc<Mutex<PohRecorder>>,
|
2020-03-09 22:03:09 -07:00
|
|
|
vote_tracker: Arc<VoteTracker>,
|
|
|
|
bank_forks: Arc<RwLock<BankForks>>,
|
2020-05-17 14:01:08 -07:00
|
|
|
subscriptions: Arc<RpcSubscriptions>,
|
2020-07-09 22:52:54 -07:00
|
|
|
verified_vote_sender: VerifiedVoteSender,
|
2020-08-07 11:21:35 -07:00
|
|
|
replay_votes_receiver: ReplayVoteReceiver,
|
2020-07-28 02:33:27 -07:00
|
|
|
blockstore: Arc<Blockstore>,
|
2020-09-28 19:43:05 -07:00
|
|
|
bank_notification_sender: Option<BankNotificationSender>,
|
2019-01-31 15:51:29 -08:00
|
|
|
) -> Self {
|
2020-03-09 22:03:09 -07:00
|
|
|
let exit_ = exit.clone();
|
2020-03-15 20:31:05 -07:00
|
|
|
|
2020-07-09 22:52:54 -07:00
|
|
|
let (verified_vote_label_packets_sender, verified_vote_label_packets_receiver) =
|
|
|
|
unbounded();
|
2020-03-15 20:31:05 -07:00
|
|
|
let (verified_vote_transactions_sender, verified_vote_transactions_receiver) = unbounded();
|
2020-03-09 22:03:09 -07:00
|
|
|
let listen_thread = Builder::new()
|
2019-01-31 15:51:29 -08:00
|
|
|
.name("solana-cluster_info_vote_listener".to_string())
|
|
|
|
.spawn(move || {
|
2019-04-09 22:06:32 -07:00
|
|
|
let _ = Self::recv_loop(
|
2020-03-09 22:03:09 -07:00
|
|
|
exit_,
|
2019-04-09 22:06:32 -07:00
|
|
|
&cluster_info,
|
2020-07-09 22:52:54 -07:00
|
|
|
verified_vote_label_packets_sender,
|
2020-03-15 20:31:05 -07:00
|
|
|
verified_vote_transactions_sender,
|
|
|
|
);
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let exit_ = exit.clone();
|
|
|
|
let poh_recorder = poh_recorder.clone();
|
|
|
|
let bank_send_thread = Builder::new()
|
|
|
|
.name("solana-cluster_info_bank_send".to_string())
|
|
|
|
.spawn(move || {
|
|
|
|
let _ = Self::bank_send_loop(
|
|
|
|
exit_,
|
2020-07-09 22:52:54 -07:00
|
|
|
verified_vote_label_packets_receiver,
|
2019-04-09 22:06:32 -07:00
|
|
|
poh_recorder,
|
2020-07-09 22:52:54 -07:00
|
|
|
&verified_packets_sender,
|
2019-04-09 22:06:32 -07:00
|
|
|
);
|
2019-01-31 15:51:29 -08:00
|
|
|
})
|
|
|
|
.unwrap();
|
2020-03-09 22:03:09 -07:00
|
|
|
|
|
|
|
let exit_ = exit.clone();
|
|
|
|
let send_thread = Builder::new()
|
|
|
|
.name("solana-cluster_info_process_votes".to_string())
|
|
|
|
.spawn(move || {
|
2020-03-15 20:31:05 -07:00
|
|
|
let _ = Self::process_votes_loop(
|
|
|
|
exit_,
|
|
|
|
verified_vote_transactions_receiver,
|
|
|
|
vote_tracker,
|
2020-07-28 02:33:27 -07:00
|
|
|
bank_forks,
|
2020-05-17 14:01:08 -07:00
|
|
|
subscriptions,
|
2020-07-09 22:52:54 -07:00
|
|
|
verified_vote_sender,
|
2020-07-20 17:29:07 -07:00
|
|
|
replay_votes_receiver,
|
2020-07-28 02:33:27 -07:00
|
|
|
blockstore,
|
2020-09-28 19:43:05 -07:00
|
|
|
bank_notification_sender,
|
2020-03-15 20:31:05 -07:00
|
|
|
);
|
2020-03-09 22:03:09 -07:00
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
|
2019-01-31 15:51:29 -08:00
|
|
|
Self {
|
2020-03-15 20:31:05 -07:00
|
|
|
thread_hdls: vec![listen_thread, send_thread, bank_send_thread],
|
2020-03-09 22:03:09 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn join(self) -> thread::Result<()> {
|
|
|
|
for thread_hdl in self.thread_hdls {
|
|
|
|
thread_hdl.join()?;
|
2019-01-31 15:51:29 -08:00
|
|
|
}
|
2020-03-09 22:03:09 -07:00
|
|
|
Ok(())
|
2019-01-31 15:51:29 -08:00
|
|
|
}
|
2020-03-09 22:03:09 -07:00
|
|
|
|
2019-01-31 15:51:29 -08:00
|
|
|
fn recv_loop(
|
2019-03-04 19:53:50 -08:00
|
|
|
exit: Arc<AtomicBool>,
|
2020-04-21 12:54:45 -07:00
|
|
|
cluster_info: &ClusterInfo,
|
2020-07-09 22:52:54 -07:00
|
|
|
verified_vote_label_packets_sender: VerifiedLabelVotePacketsSender,
|
2020-03-15 20:31:05 -07:00
|
|
|
verified_vote_transactions_sender: VerifiedVoteTransactionsSender,
|
2019-01-31 15:51:29 -08:00
|
|
|
) -> Result<()> {
|
2020-03-15 20:31:05 -07:00
|
|
|
let mut last_ts = 0;
|
2019-01-31 15:51:29 -08:00
|
|
|
loop {
|
|
|
|
if exit.load(Ordering::Relaxed) {
|
|
|
|
return Ok(());
|
|
|
|
}
|
2020-04-21 12:54:45 -07:00
|
|
|
let (labels, votes, new_ts) = cluster_info.get_votes(last_ts);
|
2020-03-15 20:31:05 -07:00
|
|
|
inc_new_counter_debug!("cluster_info_vote_listener-recv_count", votes.len());
|
|
|
|
|
|
|
|
last_ts = new_ts;
|
2020-04-23 17:04:09 -07:00
|
|
|
if !votes.is_empty() {
|
2020-05-08 10:00:23 -07:00
|
|
|
let (vote_txs, packets) = Self::verify_votes(votes, labels);
|
2020-03-15 20:31:05 -07:00
|
|
|
verified_vote_transactions_sender.send(vote_txs)?;
|
2020-07-09 22:52:54 -07:00
|
|
|
verified_vote_label_packets_sender.send(packets)?;
|
2020-03-15 20:31:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
sleep(Duration::from_millis(GOSSIP_SLEEP_MILLIS));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-03 11:07:16 -08:00
|
|
|
#[allow(clippy::type_complexity)]
|
2020-04-23 17:04:09 -07:00
|
|
|
fn verify_votes(
|
|
|
|
votes: Vec<Transaction>,
|
|
|
|
labels: Vec<CrdsValueLabel>,
|
2021-03-03 11:07:16 -08:00
|
|
|
) -> (Vec<Transaction>, Vec<(CrdsValueLabel, Slot, Packets)>) {
|
2021-03-08 19:31:00 -08:00
|
|
|
let mut msgs = packet::to_packets_chunked(&votes, 1);
|
|
|
|
sigverify::ed25519_verify_cpu(&mut msgs);
|
|
|
|
|
|
|
|
let (vote_txs, packets) = izip!(labels.into_iter(), votes.into_iter(), msgs,)
|
|
|
|
.filter_map(|(label, vote, packet)| {
|
|
|
|
let slot = vote_transaction::parse_vote_transaction(&vote)
|
|
|
|
.and_then(|(_, vote, _)| vote.slots.last().copied())?;
|
|
|
|
|
|
|
|
// to_packets_chunked() above split into 1 packet long chunks
|
|
|
|
assert_eq!(packet.packets.len(), 1);
|
|
|
|
if !packet.packets[0].meta.discard {
|
|
|
|
Some((vote, (label, slot, packet)))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.unzip();
|
2020-04-23 17:04:09 -07:00
|
|
|
(vote_txs, packets)
|
|
|
|
}
|
|
|
|
|
2020-03-15 20:31:05 -07:00
|
|
|
fn bank_send_loop(
|
|
|
|
exit: Arc<AtomicBool>,
|
2020-07-09 22:52:54 -07:00
|
|
|
verified_vote_label_packets_receiver: VerifiedLabelVotePacketsReceiver,
|
2020-03-15 20:31:05 -07:00
|
|
|
poh_recorder: Arc<Mutex<PohRecorder>>,
|
2020-07-09 22:52:54 -07:00
|
|
|
verified_packets_sender: &CrossbeamSender<Vec<Packets>>,
|
2020-03-15 20:31:05 -07:00
|
|
|
) -> Result<()> {
|
|
|
|
let mut verified_vote_packets = VerifiedVotePackets::default();
|
|
|
|
let mut time_since_lock = Instant::now();
|
|
|
|
let mut update_version = 0;
|
|
|
|
loop {
|
|
|
|
if exit.load(Ordering::Relaxed) {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
|
2021-01-25 01:01:47 -08:00
|
|
|
if let Err(e) = verified_vote_packets.receive_and_process_vote_packets(
|
2020-07-09 22:52:54 -07:00
|
|
|
&verified_vote_label_packets_receiver,
|
|
|
|
&mut update_version,
|
|
|
|
) {
|
2020-03-15 20:31:05 -07:00
|
|
|
match e {
|
|
|
|
Error::CrossbeamRecvTimeoutError(RecvTimeoutError::Disconnected) => {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
Error::CrossbeamRecvTimeoutError(RecvTimeoutError::Timeout) => (),
|
|
|
|
_ => {
|
|
|
|
error!("thread {:?} error {:?}", thread::current().name(), e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if time_since_lock.elapsed().as_millis() > GOSSIP_SLEEP_MILLIS as u128 {
|
|
|
|
let bank = poh_recorder.lock().unwrap().bank();
|
|
|
|
if let Some(bank) = bank {
|
|
|
|
let last_version = bank.last_vote_sync.load(Ordering::Relaxed);
|
|
|
|
let (new_version, msgs) = verified_vote_packets.get_latest_votes(last_version);
|
2021-03-02 07:44:35 -08:00
|
|
|
inc_new_counter_info!("bank_send_loop_batch_size", msgs.packets.len());
|
|
|
|
inc_new_counter_info!("bank_send_loop_num_batches", 1);
|
2021-02-26 15:23:08 -08:00
|
|
|
verified_packets_sender.send(vec![msgs])?;
|
2021-01-23 11:55:15 -08:00
|
|
|
#[allow(deprecated)]
|
2020-03-15 20:31:05 -07:00
|
|
|
bank.last_vote_sync.compare_and_swap(
|
|
|
|
last_version,
|
|
|
|
new_version,
|
|
|
|
Ordering::Relaxed,
|
|
|
|
);
|
|
|
|
time_since_lock = Instant::now();
|
2019-04-26 17:27:31 -07:00
|
|
|
}
|
2019-04-09 22:06:32 -07:00
|
|
|
}
|
2019-01-31 15:51:29 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-09 22:03:09 -07:00
|
|
|
fn process_votes_loop(
|
|
|
|
exit: Arc<AtomicBool>,
|
2020-07-29 23:17:40 -07:00
|
|
|
gossip_vote_txs_receiver: VerifiedVoteTransactionsReceiver,
|
2020-03-09 22:03:09 -07:00
|
|
|
vote_tracker: Arc<VoteTracker>,
|
2020-07-28 02:33:27 -07:00
|
|
|
bank_forks: Arc<RwLock<BankForks>>,
|
2020-05-17 14:01:08 -07:00
|
|
|
subscriptions: Arc<RpcSubscriptions>,
|
2020-07-09 22:52:54 -07:00
|
|
|
verified_vote_sender: VerifiedVoteSender,
|
2020-08-07 11:21:35 -07:00
|
|
|
replay_votes_receiver: ReplayVoteReceiver,
|
2020-07-28 02:33:27 -07:00
|
|
|
blockstore: Arc<Blockstore>,
|
2020-09-28 19:43:05 -07:00
|
|
|
bank_notification_sender: Option<BankNotificationSender>,
|
2020-03-09 22:03:09 -07:00
|
|
|
) -> Result<()> {
|
2020-10-23 18:19:12 -07:00
|
|
|
let mut confirmation_verifier =
|
2020-07-28 02:33:27 -07:00
|
|
|
OptimisticConfirmationVerifier::new(bank_forks.read().unwrap().root());
|
|
|
|
let mut last_process_root = Instant::now();
|
2020-03-09 22:03:09 -07:00
|
|
|
loop {
|
|
|
|
if exit.load(Ordering::Relaxed) {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
|
|
|
|
let root_bank = bank_forks.read().unwrap().root_bank().clone();
|
2020-10-23 18:19:12 -07:00
|
|
|
if last_process_root.elapsed().as_millis() > DEFAULT_MS_PER_SLOT as u128 {
|
|
|
|
let unrooted_optimistic_slots = confirmation_verifier
|
|
|
|
.verify_for_unrooted_optimistic_slots(&root_bank, &blockstore);
|
2020-07-28 02:33:27 -07:00
|
|
|
// SlotVoteTracker's for all `slots` in `unrooted_optimistic_slots`
|
|
|
|
// should still be available because we haven't purged in
|
2020-10-23 18:19:12 -07:00
|
|
|
// `progress_with_new_root_bank()` yet, which is called below
|
2020-07-28 02:33:27 -07:00
|
|
|
OptimisticConfirmationVerifier::log_unrooted_optimistic_slots(
|
|
|
|
&root_bank,
|
|
|
|
&vote_tracker,
|
|
|
|
&unrooted_optimistic_slots,
|
|
|
|
);
|
2020-10-23 18:19:12 -07:00
|
|
|
vote_tracker.progress_with_new_root_bank(&root_bank);
|
2020-07-28 02:33:27 -07:00
|
|
|
last_process_root = Instant::now();
|
|
|
|
}
|
2020-10-23 18:19:12 -07:00
|
|
|
let confirmed_slots = Self::listen_and_confirm_votes(
|
2020-07-29 23:17:40 -07:00
|
|
|
&gossip_vote_txs_receiver,
|
2020-05-17 14:01:08 -07:00
|
|
|
&vote_tracker,
|
2020-07-28 02:33:27 -07:00
|
|
|
&root_bank,
|
2020-06-08 17:38:14 -07:00
|
|
|
&subscriptions,
|
2020-07-09 22:52:54 -07:00
|
|
|
&verified_vote_sender,
|
2020-07-20 17:29:07 -07:00
|
|
|
&replay_votes_receiver,
|
2020-09-28 19:43:05 -07:00
|
|
|
&bank_notification_sender,
|
2020-07-28 02:33:27 -07:00
|
|
|
);
|
2020-10-23 18:19:12 -07:00
|
|
|
match confirmed_slots {
|
|
|
|
Ok(confirmed_slots) => {
|
|
|
|
confirmation_verifier.add_new_optimistic_confirmed_slots(confirmed_slots);
|
|
|
|
}
|
|
|
|
Err(e) => match e {
|
2020-07-20 17:29:07 -07:00
|
|
|
Error::CrossbeamRecvTimeoutError(RecvTimeoutError::Timeout)
|
|
|
|
| Error::ReadyTimeoutError => (),
|
2020-03-09 22:03:09 -07:00
|
|
|
_ => {
|
|
|
|
error!("thread {:?} error {:?}", thread::current().name(), e);
|
|
|
|
}
|
2020-10-23 18:19:12 -07:00
|
|
|
},
|
2020-03-09 22:03:09 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-17 14:01:08 -07:00
|
|
|
#[cfg(test)]
|
|
|
|
pub fn get_and_process_votes_for_tests(
|
2020-07-29 23:17:40 -07:00
|
|
|
gossip_vote_txs_receiver: &VerifiedVoteTransactionsReceiver,
|
2020-06-08 17:38:14 -07:00
|
|
|
vote_tracker: &VoteTracker,
|
2020-07-28 02:33:27 -07:00
|
|
|
root_bank: &Bank,
|
2020-06-08 17:38:14 -07:00
|
|
|
subscriptions: &RpcSubscriptions,
|
2020-07-09 22:52:54 -07:00
|
|
|
verified_vote_sender: &VerifiedVoteSender,
|
2020-08-07 11:21:35 -07:00
|
|
|
replay_votes_receiver: &ReplayVoteReceiver,
|
2020-07-28 02:33:27 -07:00
|
|
|
) -> Result<Vec<(Slot, Hash)>> {
|
2020-10-23 18:19:12 -07:00
|
|
|
Self::listen_and_confirm_votes(
|
2020-07-29 23:17:40 -07:00
|
|
|
gossip_vote_txs_receiver,
|
2020-05-22 14:53:47 -07:00
|
|
|
vote_tracker,
|
2020-07-28 02:33:27 -07:00
|
|
|
root_bank,
|
2020-05-22 14:53:47 -07:00
|
|
|
subscriptions,
|
2020-07-09 22:52:54 -07:00
|
|
|
verified_vote_sender,
|
2020-07-20 17:29:07 -07:00
|
|
|
replay_votes_receiver,
|
2020-09-28 19:43:05 -07:00
|
|
|
&None,
|
2020-05-22 14:53:47 -07:00
|
|
|
)
|
2020-05-17 14:01:08 -07:00
|
|
|
}
|
|
|
|
|
2020-10-23 18:19:12 -07:00
|
|
|
fn listen_and_confirm_votes(
|
2020-07-29 23:17:40 -07:00
|
|
|
gossip_vote_txs_receiver: &VerifiedVoteTransactionsReceiver,
|
2020-06-08 17:38:14 -07:00
|
|
|
vote_tracker: &VoteTracker,
|
2020-07-28 02:33:27 -07:00
|
|
|
root_bank: &Bank,
|
2020-06-08 17:38:14 -07:00
|
|
|
subscriptions: &RpcSubscriptions,
|
2020-07-09 22:52:54 -07:00
|
|
|
verified_vote_sender: &VerifiedVoteSender,
|
2020-08-07 11:21:35 -07:00
|
|
|
replay_votes_receiver: &ReplayVoteReceiver,
|
2020-09-28 19:43:05 -07:00
|
|
|
bank_notification_sender: &Option<BankNotificationSender>,
|
2020-07-28 02:33:27 -07:00
|
|
|
) -> Result<Vec<(Slot, Hash)>> {
|
2020-07-20 17:29:07 -07:00
|
|
|
let mut sel = Select::new();
|
2020-07-29 23:17:40 -07:00
|
|
|
sel.recv(gossip_vote_txs_receiver);
|
2020-07-20 17:29:07 -07:00
|
|
|
sel.recv(replay_votes_receiver);
|
|
|
|
let mut remaining_wait_time = 200;
|
|
|
|
loop {
|
|
|
|
if remaining_wait_time == 0 {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
let start = Instant::now();
|
|
|
|
// Wait for one of the receivers to be ready. `ready_timeout`
|
|
|
|
// will return if channels either have something, or are
|
|
|
|
// disconnected. `ready_timeout` can wake up spuriously,
|
|
|
|
// hence the loop
|
|
|
|
let _ = sel.ready_timeout(Duration::from_millis(remaining_wait_time))?;
|
2020-07-28 02:33:27 -07:00
|
|
|
|
|
|
|
// Should not early return from this point onwards until `process_votes()`
|
|
|
|
// returns below to avoid missing any potential `optimistic_confirmed_slots`
|
2020-07-29 23:17:40 -07:00
|
|
|
let gossip_vote_txs: Vec<_> = gossip_vote_txs_receiver.try_iter().flatten().collect();
|
2020-07-20 17:29:07 -07:00
|
|
|
let replay_votes: Vec<_> = replay_votes_receiver.try_iter().collect();
|
2020-07-29 23:17:40 -07:00
|
|
|
if !gossip_vote_txs.is_empty() || !replay_votes.is_empty() {
|
2020-10-23 18:19:12 -07:00
|
|
|
return Ok(Self::filter_and_confirm_with_new_votes(
|
2020-07-20 17:29:07 -07:00
|
|
|
vote_tracker,
|
2020-07-29 23:17:40 -07:00
|
|
|
gossip_vote_txs,
|
|
|
|
replay_votes,
|
2020-07-28 02:33:27 -07:00
|
|
|
root_bank,
|
2020-07-20 17:29:07 -07:00
|
|
|
subscriptions,
|
|
|
|
verified_vote_sender,
|
2020-09-28 19:43:05 -07:00
|
|
|
bank_notification_sender,
|
2020-07-28 02:33:27 -07:00
|
|
|
));
|
2020-07-20 17:29:07 -07:00
|
|
|
} else {
|
|
|
|
remaining_wait_time = remaining_wait_time
|
|
|
|
.saturating_sub(std::cmp::max(start.elapsed().as_millis() as u64, 1));
|
|
|
|
}
|
2019-01-31 15:51:29 -08:00
|
|
|
}
|
2020-07-28 02:33:27 -07:00
|
|
|
Ok(vec![])
|
|
|
|
}
|
|
|
|
|
2020-07-29 23:17:40 -07:00
|
|
|
#[allow(clippy::too_many_arguments)]
|
2020-10-23 18:19:12 -07:00
|
|
|
fn track_new_votes_and_notify_confirmations(
|
2020-07-29 23:17:40 -07:00
|
|
|
vote: Vote,
|
|
|
|
vote_pubkey: &Pubkey,
|
2020-05-17 14:01:08 -07:00
|
|
|
vote_tracker: &VoteTracker,
|
2020-07-28 02:33:27 -07:00
|
|
|
root_bank: &Bank,
|
2020-06-08 17:38:14 -07:00
|
|
|
subscriptions: &RpcSubscriptions,
|
2020-07-09 22:52:54 -07:00
|
|
|
verified_vote_sender: &VerifiedVoteSender,
|
2021-02-07 18:07:00 -08:00
|
|
|
diff: &mut HashMap<Slot, HashMap<Pubkey, bool>>,
|
2020-07-29 23:17:40 -07:00
|
|
|
new_optimistic_confirmed_slots: &mut Vec<(Slot, Hash)>,
|
|
|
|
is_gossip_vote: bool,
|
2020-09-28 19:43:05 -07:00
|
|
|
bank_notification_sender: &Option<BankNotificationSender>,
|
2020-07-29 23:17:40 -07:00
|
|
|
) {
|
|
|
|
if vote.slots.is_empty() {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-10-23 18:19:12 -07:00
|
|
|
let last_vote_slot = *vote.slots.last().unwrap();
|
|
|
|
let last_vote_hash = vote.hash;
|
2020-07-29 23:17:40 -07:00
|
|
|
|
2020-07-28 02:33:27 -07:00
|
|
|
let root = root_bank.slot();
|
2020-07-29 23:17:40 -07:00
|
|
|
let mut is_new_vote = false;
|
2020-10-23 18:19:12 -07:00
|
|
|
// If slot is before the root, ignore it
|
|
|
|
for slot in vote.slots.iter().filter(|slot| **slot > root).rev() {
|
|
|
|
let slot = *slot;
|
|
|
|
|
|
|
|
// if we don't have stake information, ignore it
|
|
|
|
let epoch = root_bank.epoch_schedule().get_epoch(slot);
|
2020-07-29 23:17:40 -07:00
|
|
|
let epoch_stakes = root_bank.epoch_stakes(epoch);
|
2020-10-23 18:19:12 -07:00
|
|
|
if epoch_stakes.is_none() {
|
2020-07-29 23:17:40 -07:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
let epoch_stakes = epoch_stakes.unwrap();
|
|
|
|
|
|
|
|
// The last vote slot, which is the greatest slot in the stack
|
|
|
|
// of votes in a vote transaction, qualifies for optimistic confirmation.
|
2020-10-23 18:19:12 -07:00
|
|
|
if slot == last_vote_slot {
|
|
|
|
let vote_accounts = Stakes::vote_accounts(epoch_stakes.stakes());
|
|
|
|
let stake = vote_accounts
|
2020-07-29 23:17:40 -07:00
|
|
|
.get(&vote_pubkey)
|
|
|
|
.map(|(stake, _)| *stake)
|
2020-10-23 18:19:12 -07:00
|
|
|
.unwrap_or_default();
|
|
|
|
let total_stake = epoch_stakes.total_stake();
|
2020-07-29 23:17:40 -07:00
|
|
|
|
|
|
|
// Fast track processing of the last slot in a vote transactions
|
|
|
|
// so that notifications for optimistic confirmation can be sent
|
|
|
|
// as soon as possible.
|
2020-10-23 18:19:12 -07:00
|
|
|
let (is_confirmed, is_new) = Self::track_optimistic_confirmation_vote(
|
2020-07-29 23:17:40 -07:00
|
|
|
vote_tracker,
|
2020-10-23 18:19:12 -07:00
|
|
|
last_vote_slot,
|
|
|
|
last_vote_hash,
|
2021-02-07 18:07:00 -08:00
|
|
|
*vote_pubkey,
|
2020-07-29 23:17:40 -07:00
|
|
|
stake,
|
2020-10-23 18:19:12 -07:00
|
|
|
total_stake,
|
2020-07-29 23:17:40 -07:00
|
|
|
);
|
2020-03-09 22:03:09 -07:00
|
|
|
|
2020-07-29 23:17:40 -07:00
|
|
|
if is_confirmed {
|
2020-10-23 18:19:12 -07:00
|
|
|
new_optimistic_confirmed_slots.push((last_vote_slot, last_vote_hash));
|
2020-07-30 02:52:27 -07:00
|
|
|
// Notify subscribers about new optimistic confirmation
|
2020-09-28 19:43:05 -07:00
|
|
|
if let Some(sender) = bank_notification_sender {
|
|
|
|
sender
|
2020-10-23 18:19:12 -07:00
|
|
|
.send(BankNotification::OptimisticallyConfirmed(last_vote_slot))
|
2020-09-28 19:43:05 -07:00
|
|
|
.unwrap_or_else(|err| {
|
|
|
|
warn!("bank_notification_sender failed: {:?}", err)
|
|
|
|
});
|
|
|
|
}
|
2020-07-29 23:17:40 -07:00
|
|
|
}
|
2020-03-09 22:03:09 -07:00
|
|
|
|
2020-07-29 23:17:40 -07:00
|
|
|
if !is_new && !is_gossip_vote {
|
|
|
|
// By now:
|
|
|
|
// 1) The vote must have come from ReplayStage,
|
|
|
|
// 2) We've seen this vote from replay for this hash before
|
2020-10-23 18:19:12 -07:00
|
|
|
// (`track_optimistic_confirmation_vote()` will not set `is_new == true`
|
2020-07-29 23:17:40 -07:00
|
|
|
// for same slot different hash), so short circuit because this vote
|
|
|
|
// has no new information
|
|
|
|
|
|
|
|
// Note gossip votes will always be processed because those should be unique
|
|
|
|
// and we need to update the gossip-only stake in the `VoteTracker`.
|
|
|
|
return;
|
|
|
|
}
|
2020-03-09 22:03:09 -07:00
|
|
|
|
2020-07-29 23:17:40 -07:00
|
|
|
is_new_vote = is_new;
|
|
|
|
}
|
2020-03-09 22:03:09 -07:00
|
|
|
|
2020-10-23 18:19:12 -07:00
|
|
|
diff.entry(slot)
|
2020-07-29 23:17:40 -07:00
|
|
|
.or_default()
|
2021-02-07 18:07:00 -08:00
|
|
|
.entry(*vote_pubkey)
|
2020-07-29 23:17:40 -07:00
|
|
|
.and_modify(|seen_in_gossip_previously| {
|
|
|
|
*seen_in_gossip_previously = *seen_in_gossip_previously || is_gossip_vote
|
|
|
|
})
|
|
|
|
.or_insert(is_gossip_vote);
|
|
|
|
}
|
|
|
|
|
|
|
|
if is_new_vote {
|
|
|
|
subscriptions.notify_vote(&vote);
|
|
|
|
let _ = verified_vote_sender.send((*vote_pubkey, vote.slots));
|
|
|
|
}
|
|
|
|
}
|
2020-03-09 22:03:09 -07:00
|
|
|
|
2020-10-23 18:19:12 -07:00
|
|
|
fn filter_gossip_votes(
|
|
|
|
vote_tracker: &VoteTracker,
|
|
|
|
vote_pubkey: &Pubkey,
|
|
|
|
vote: &Vote,
|
|
|
|
gossip_tx: &Transaction,
|
|
|
|
) -> bool {
|
|
|
|
if vote.slots.is_empty() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
let last_vote_slot = vote.slots.last().unwrap();
|
|
|
|
// Votes from gossip need to be verified as they have not been
|
|
|
|
// verified by the replay pipeline. Determine the authorized voter
|
|
|
|
// based on the last vote slot. This will drop votes from authorized
|
|
|
|
// voters trying to make votes for slots earlier than the epoch for
|
|
|
|
// which they are authorized
|
|
|
|
let actual_authorized_voter =
|
|
|
|
vote_tracker.get_authorized_voter(&vote_pubkey, *last_vote_slot);
|
|
|
|
|
|
|
|
if actual_authorized_voter.is_none() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Voting without the correct authorized pubkey, dump the vote
|
|
|
|
if !VoteTracker::vote_contains_authorized_voter(
|
|
|
|
&gossip_tx,
|
|
|
|
&actual_authorized_voter.unwrap(),
|
|
|
|
) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
|
|
|
fn filter_and_confirm_with_new_votes(
|
2020-07-29 23:17:40 -07:00
|
|
|
vote_tracker: &VoteTracker,
|
|
|
|
gossip_vote_txs: Vec<Transaction>,
|
|
|
|
replayed_votes: Vec<ReplayedVote>,
|
|
|
|
root_bank: &Bank,
|
|
|
|
subscriptions: &RpcSubscriptions,
|
|
|
|
verified_vote_sender: &VerifiedVoteSender,
|
2020-09-28 19:43:05 -07:00
|
|
|
bank_notification_sender: &Option<BankNotificationSender>,
|
2020-07-29 23:17:40 -07:00
|
|
|
) -> Vec<(Slot, Hash)> {
|
2021-02-07 18:07:00 -08:00
|
|
|
let mut diff: HashMap<Slot, HashMap<Pubkey, bool>> = HashMap::new();
|
2020-07-29 23:17:40 -07:00
|
|
|
let mut new_optimistic_confirmed_slots = vec![];
|
|
|
|
|
|
|
|
// Process votes from gossip and ReplayStage
|
2020-09-05 21:27:50 -07:00
|
|
|
for (is_gossip, (vote_pubkey, vote, _)) in gossip_vote_txs
|
2020-07-29 23:17:40 -07:00
|
|
|
.iter()
|
|
|
|
.filter_map(|gossip_tx| {
|
2020-09-05 21:27:50 -07:00
|
|
|
vote_transaction::parse_vote_transaction(gossip_tx)
|
|
|
|
.filter(|(vote_pubkey, vote, _)| {
|
2020-10-23 18:19:12 -07:00
|
|
|
Self::filter_gossip_votes(vote_tracker, vote_pubkey, vote, gossip_tx)
|
2020-09-05 21:27:50 -07:00
|
|
|
})
|
|
|
|
.map(|v| (true, v))
|
2020-07-29 23:17:40 -07:00
|
|
|
})
|
2020-09-05 21:27:50 -07:00
|
|
|
.chain(replayed_votes.into_iter().map(|v| (false, v)))
|
2020-07-29 23:17:40 -07:00
|
|
|
{
|
2020-10-23 18:19:12 -07:00
|
|
|
Self::track_new_votes_and_notify_confirmations(
|
2020-07-29 23:17:40 -07:00
|
|
|
vote,
|
|
|
|
&vote_pubkey,
|
|
|
|
&vote_tracker,
|
|
|
|
root_bank,
|
|
|
|
subscriptions,
|
|
|
|
verified_vote_sender,
|
|
|
|
&mut diff,
|
|
|
|
&mut new_optimistic_confirmed_slots,
|
2020-09-05 21:27:50 -07:00
|
|
|
is_gossip,
|
2020-09-28 19:43:05 -07:00
|
|
|
bank_notification_sender,
|
2020-07-29 23:17:40 -07:00
|
|
|
);
|
2020-07-20 17:29:07 -07:00
|
|
|
}
|
|
|
|
|
2020-07-28 02:33:27 -07:00
|
|
|
// Process all the slots accumulated from replay and gossip.
|
2020-07-20 17:29:07 -07:00
|
|
|
for (slot, mut slot_diff) in diff {
|
2020-07-28 02:33:27 -07:00
|
|
|
let slot_tracker = vote_tracker.get_or_insert_slot_tracker(slot);
|
|
|
|
{
|
|
|
|
let r_slot_tracker = slot_tracker.read().unwrap();
|
|
|
|
// Only keep the pubkeys we haven't seen voting for this slot
|
|
|
|
slot_diff.retain(|pubkey, seen_in_gossip_above| {
|
|
|
|
let seen_in_gossip_previously = r_slot_tracker.voted.get(pubkey);
|
|
|
|
let is_new = seen_in_gossip_previously.is_none();
|
|
|
|
// `is_new_from_gossip` means we observed a vote for this slot
|
|
|
|
// for the first time in gossip
|
|
|
|
let is_new_from_gossip = !seen_in_gossip_previously.cloned().unwrap_or(false)
|
|
|
|
&& *seen_in_gossip_above;
|
|
|
|
is_new || is_new_from_gossip
|
|
|
|
});
|
|
|
|
}
|
|
|
|
let mut w_slot_tracker = slot_tracker.write().unwrap();
|
|
|
|
if w_slot_tracker.updates.is_none() {
|
|
|
|
w_slot_tracker.updates = Some(vec![]);
|
|
|
|
}
|
|
|
|
let mut gossip_only_stake = 0;
|
|
|
|
let epoch = root_bank.epoch_schedule().get_epoch(slot);
|
|
|
|
let epoch_stakes = root_bank.epoch_stakes(epoch);
|
|
|
|
|
|
|
|
for (pubkey, seen_in_gossip_above) in slot_diff {
|
2020-07-30 02:52:27 -07:00
|
|
|
if seen_in_gossip_above {
|
2020-07-28 02:33:27 -07:00
|
|
|
// By this point we know if the vote was seen in gossip above,
|
2020-07-30 02:52:27 -07:00
|
|
|
// it was not seen in gossip at any point in the past (if it was seen
|
|
|
|
// in gossip in the past, `is_new` would be false and it would have
|
|
|
|
// been filtered out above), so it's safe to increment the gossip-only
|
|
|
|
// stake
|
|
|
|
Self::sum_stake(&mut gossip_only_stake, epoch_stakes, &pubkey);
|
|
|
|
}
|
2020-07-28 02:33:27 -07:00
|
|
|
|
|
|
|
// From the `slot_diff.retain` earlier, we know because there are
|
|
|
|
// no other writers to `slot_vote_tracker` that
|
|
|
|
// `is_new || is_new_from_gossip`. In both cases we want to record
|
|
|
|
// `is_new_from_gossip` for the `pubkey` entry.
|
2021-02-07 18:07:00 -08:00
|
|
|
w_slot_tracker.voted.insert(pubkey, seen_in_gossip_above);
|
2020-07-28 02:33:27 -07:00
|
|
|
w_slot_tracker.updates.as_mut().unwrap().push(pubkey);
|
2020-03-09 22:03:09 -07:00
|
|
|
}
|
2020-07-30 02:52:27 -07:00
|
|
|
|
2020-07-28 02:33:27 -07:00
|
|
|
w_slot_tracker.gossip_only_stake += gossip_only_stake
|
2020-03-09 22:03:09 -07:00
|
|
|
}
|
2020-07-28 02:33:27 -07:00
|
|
|
new_optimistic_confirmed_slots
|
|
|
|
}
|
|
|
|
|
2020-07-29 23:17:40 -07:00
|
|
|
// Returns if the slot was optimistically confirmed, and whether
|
|
|
|
// the slot was new
|
2020-10-23 18:19:12 -07:00
|
|
|
fn track_optimistic_confirmation_vote(
|
2020-07-28 02:33:27 -07:00
|
|
|
vote_tracker: &VoteTracker,
|
|
|
|
slot: Slot,
|
|
|
|
hash: Hash,
|
2021-02-07 18:07:00 -08:00
|
|
|
pubkey: Pubkey,
|
2020-07-28 02:33:27 -07:00
|
|
|
stake: u64,
|
|
|
|
total_epoch_stake: u64,
|
2020-07-29 23:17:40 -07:00
|
|
|
) -> (bool, bool) {
|
2020-07-28 02:33:27 -07:00
|
|
|
let slot_tracker = vote_tracker.get_or_insert_slot_tracker(slot);
|
|
|
|
// Insert vote and check for optimistic confirmation
|
|
|
|
let mut w_slot_tracker = slot_tracker.write().unwrap();
|
|
|
|
|
|
|
|
w_slot_tracker
|
|
|
|
.get_or_insert_optimistic_votes_tracker(hash)
|
|
|
|
.add_vote_pubkey(pubkey, stake, total_epoch_stake)
|
2020-03-09 22:03:09 -07:00
|
|
|
}
|
2020-05-22 14:53:47 -07:00
|
|
|
|
2020-07-30 02:52:27 -07:00
|
|
|
fn sum_stake(sum: &mut u64, epoch_stakes: Option<&EpochStakes>, pubkey: &Pubkey) {
|
2020-05-22 14:53:47 -07:00
|
|
|
if let Some(stakes) = epoch_stakes {
|
|
|
|
if let Some(vote_account) = stakes.stakes().vote_accounts().get(pubkey) {
|
2020-07-30 02:52:27 -07:00
|
|
|
*sum += vote_account.0;
|
2020-05-22 14:53:47 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-01-31 15:51:29 -08:00
|
|
|
}
|
2019-05-21 21:45:38 -07:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2020-03-09 22:03:09 -07:00
|
|
|
use super::*;
|
2020-09-28 19:43:05 -07:00
|
|
|
use crate::optimistically_confirmed_bank_tracker::OptimisticallyConfirmedBank;
|
2020-03-17 23:30:23 -07:00
|
|
|
use solana_perf::packet;
|
2020-03-09 22:03:09 -07:00
|
|
|
use solana_runtime::{
|
|
|
|
bank::Bank,
|
2020-06-25 21:06:58 -07:00
|
|
|
commitment::BlockCommitmentCache,
|
2020-03-09 22:03:09 -07:00
|
|
|
genesis_utils::{self, GenesisConfigInfo, ValidatorVoteKeypairs},
|
2020-08-07 11:21:35 -07:00
|
|
|
vote_sender_types::ReplayVoteSender,
|
2020-03-09 22:03:09 -07:00
|
|
|
};
|
2020-07-29 23:17:40 -07:00
|
|
|
use solana_sdk::{
|
|
|
|
hash::Hash,
|
|
|
|
signature::{Keypair, Signature, Signer},
|
|
|
|
};
|
|
|
|
use solana_vote_program::vote_state::Vote;
|
2020-07-20 17:29:07 -07:00
|
|
|
use std::collections::BTreeSet;
|
2019-05-21 21:45:38 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_max_vote_tx_fits() {
|
|
|
|
solana_logger::setup();
|
|
|
|
let node_keypair = Keypair::new();
|
|
|
|
let vote_keypair = Keypair::new();
|
2020-05-15 09:35:43 -07:00
|
|
|
let slots: Vec<_> = (0..31).collect();
|
2019-05-31 11:45:17 -07:00
|
|
|
|
2020-03-09 22:03:09 -07:00
|
|
|
let vote_tx = vote_transaction::new_vote_transaction(
|
|
|
|
slots,
|
|
|
|
Hash::default(),
|
|
|
|
Hash::default(),
|
|
|
|
&node_keypair,
|
|
|
|
&vote_keypair,
|
|
|
|
&vote_keypair,
|
2020-07-28 02:33:27 -07:00
|
|
|
Some(Hash::default()),
|
2020-03-09 22:03:09 -07:00
|
|
|
);
|
2019-05-21 21:45:38 -07:00
|
|
|
|
|
|
|
use bincode::serialized_size;
|
|
|
|
info!("max vote size {}", serialized_size(&vote_tx).unwrap());
|
|
|
|
|
2021-01-28 16:32:38 -08:00
|
|
|
let msgs = packet::to_packets_chunked(&[vote_tx], 1); // panics if won't fit
|
2019-05-21 21:45:38 -07:00
|
|
|
|
|
|
|
assert_eq!(msgs.len(), 1);
|
|
|
|
}
|
2020-03-09 22:03:09 -07:00
|
|
|
|
2020-07-28 02:33:27 -07:00
|
|
|
fn run_vote_contains_authorized_voter(hash: Option<Hash>) {
|
2020-03-09 22:03:09 -07:00
|
|
|
let node_keypair = Keypair::new();
|
|
|
|
let vote_keypair = Keypair::new();
|
|
|
|
let authorized_voter = Keypair::new();
|
|
|
|
|
|
|
|
let vote_tx = vote_transaction::new_vote_transaction(
|
|
|
|
vec![0],
|
|
|
|
Hash::default(),
|
|
|
|
Hash::default(),
|
|
|
|
&node_keypair,
|
|
|
|
&vote_keypair,
|
|
|
|
&authorized_voter,
|
2020-07-28 02:33:27 -07:00
|
|
|
hash,
|
2020-03-09 22:03:09 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
// Check that the two signing keys pass the check
|
|
|
|
assert!(VoteTracker::vote_contains_authorized_voter(
|
|
|
|
&vote_tx,
|
|
|
|
&node_keypair.pubkey()
|
|
|
|
));
|
|
|
|
|
|
|
|
assert!(VoteTracker::vote_contains_authorized_voter(
|
|
|
|
&vote_tx,
|
|
|
|
&authorized_voter.pubkey()
|
|
|
|
));
|
|
|
|
|
|
|
|
// Non signing key shouldn't pass the check
|
|
|
|
assert!(!VoteTracker::vote_contains_authorized_voter(
|
|
|
|
&vote_tx,
|
|
|
|
&vote_keypair.pubkey()
|
|
|
|
));
|
|
|
|
|
|
|
|
// Set the authorized voter == vote keypair
|
|
|
|
let vote_tx = vote_transaction::new_vote_transaction(
|
|
|
|
vec![0],
|
|
|
|
Hash::default(),
|
|
|
|
Hash::default(),
|
|
|
|
&node_keypair,
|
|
|
|
&vote_keypair,
|
|
|
|
&vote_keypair,
|
2020-07-28 02:33:27 -07:00
|
|
|
hash,
|
2020-03-09 22:03:09 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
// Check that the node_keypair and vote keypair pass the authorized voter check
|
|
|
|
assert!(VoteTracker::vote_contains_authorized_voter(
|
|
|
|
&vote_tx,
|
|
|
|
&node_keypair.pubkey()
|
|
|
|
));
|
|
|
|
|
|
|
|
assert!(VoteTracker::vote_contains_authorized_voter(
|
|
|
|
&vote_tx,
|
|
|
|
&vote_keypair.pubkey()
|
|
|
|
));
|
|
|
|
|
2020-07-28 02:33:27 -07:00
|
|
|
// The other keypair should not pass the check
|
2020-03-09 22:03:09 -07:00
|
|
|
assert!(!VoteTracker::vote_contains_authorized_voter(
|
|
|
|
&vote_tx,
|
|
|
|
&authorized_voter.pubkey()
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2020-07-28 02:33:27 -07:00
|
|
|
#[test]
|
|
|
|
fn test_vote_contains_authorized_voter() {
|
|
|
|
run_vote_contains_authorized_voter(None);
|
|
|
|
run_vote_contains_authorized_voter(Some(Hash::default()));
|
|
|
|
}
|
|
|
|
|
2020-03-26 17:55:17 -07:00
|
|
|
#[test]
|
|
|
|
fn test_update_new_root() {
|
2020-05-17 14:01:08 -07:00
|
|
|
let (vote_tracker, bank, _, _) = setup();
|
2020-03-26 17:55:17 -07:00
|
|
|
|
|
|
|
// Check outdated slots are purged with new root
|
2021-02-07 18:07:00 -08:00
|
|
|
let new_voter = solana_sdk::pubkey::new_rand();
|
2020-03-26 17:55:17 -07:00
|
|
|
// Make separate copy so the original doesn't count toward
|
|
|
|
// the ref count, which would prevent cleanup
|
2021-02-07 18:07:00 -08:00
|
|
|
let new_voter_ = new_voter;
|
2020-03-26 17:55:17 -07:00
|
|
|
vote_tracker.insert_vote(bank.slot(), new_voter_);
|
|
|
|
assert!(vote_tracker
|
|
|
|
.slot_vote_trackers
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.contains_key(&bank.slot()));
|
|
|
|
let bank1 = Bank::new_from_parent(&bank, &Pubkey::default(), bank.slot() + 1);
|
2020-10-23 18:19:12 -07:00
|
|
|
vote_tracker.progress_with_new_root_bank(&bank1);
|
2020-03-26 17:55:17 -07:00
|
|
|
assert!(!vote_tracker
|
|
|
|
.slot_vote_trackers
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.contains_key(&bank.slot()));
|
|
|
|
|
|
|
|
// Check `keys` and `epoch_authorized_voters` are purged when new
|
|
|
|
// root bank moves to the next epoch
|
|
|
|
let current_epoch = bank.epoch();
|
|
|
|
let new_epoch_bank = Bank::new_from_parent(
|
|
|
|
&bank,
|
|
|
|
&Pubkey::default(),
|
|
|
|
bank.epoch_schedule()
|
|
|
|
.get_first_slot_in_epoch(current_epoch + 1),
|
|
|
|
);
|
2020-10-23 18:19:12 -07:00
|
|
|
vote_tracker.progress_with_new_root_bank(&new_epoch_bank);
|
2020-03-26 17:55:17 -07:00
|
|
|
assert_eq!(
|
|
|
|
*vote_tracker.current_epoch.read().unwrap(),
|
|
|
|
current_epoch + 1
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_update_new_leader_schedule_epoch() {
|
2020-05-17 14:01:08 -07:00
|
|
|
let (vote_tracker, bank, _, _) = setup();
|
2020-03-26 17:55:17 -07:00
|
|
|
|
|
|
|
// Check outdated slots are purged with new root
|
|
|
|
let leader_schedule_epoch = bank.get_leader_schedule_epoch(bank.slot());
|
|
|
|
let next_leader_schedule_epoch = leader_schedule_epoch + 1;
|
|
|
|
let mut next_leader_schedule_computed = bank.slot();
|
|
|
|
loop {
|
|
|
|
next_leader_schedule_computed += 1;
|
|
|
|
if bank.get_leader_schedule_epoch(next_leader_schedule_computed)
|
|
|
|
== next_leader_schedule_epoch
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert_eq!(
|
|
|
|
bank.get_leader_schedule_epoch(next_leader_schedule_computed),
|
|
|
|
next_leader_schedule_epoch
|
|
|
|
);
|
|
|
|
let next_leader_schedule_bank =
|
|
|
|
Bank::new_from_parent(&bank, &Pubkey::default(), next_leader_schedule_computed);
|
2020-10-23 18:19:12 -07:00
|
|
|
vote_tracker.progress_leader_schedule_epoch(&next_leader_schedule_bank);
|
2020-03-26 17:55:17 -07:00
|
|
|
assert_eq!(
|
|
|
|
*vote_tracker.leader_schedule_epoch.read().unwrap(),
|
|
|
|
next_leader_schedule_epoch
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
vote_tracker
|
|
|
|
.epoch_authorized_voters
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.get(&next_leader_schedule_epoch)
|
|
|
|
.unwrap(),
|
|
|
|
next_leader_schedule_bank
|
|
|
|
.epoch_stakes(next_leader_schedule_epoch)
|
|
|
|
.unwrap()
|
|
|
|
.epoch_authorized_voters()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-03-09 22:03:09 -07:00
|
|
|
#[test]
|
2020-07-28 02:33:27 -07:00
|
|
|
fn test_votes_in_range() {
|
2020-03-09 22:03:09 -07:00
|
|
|
// Create some voters at genesis
|
2020-07-28 02:33:27 -07:00
|
|
|
let stake_per_validator = 100;
|
2020-05-17 14:01:08 -07:00
|
|
|
let (vote_tracker, _, validator_voting_keypairs, subscriptions) = setup();
|
2020-03-09 22:03:09 -07:00
|
|
|
let (votes_sender, votes_receiver) = unbounded();
|
2020-07-28 02:33:27 -07:00
|
|
|
let (verified_vote_sender, _verified_vote_receiver) = unbounded();
|
2020-07-20 17:29:07 -07:00
|
|
|
let (replay_votes_sender, replay_votes_receiver) = unbounded();
|
2020-03-09 22:03:09 -07:00
|
|
|
|
2020-07-28 02:33:27 -07:00
|
|
|
let GenesisConfigInfo { genesis_config, .. } =
|
|
|
|
genesis_utils::create_genesis_config_with_vote_accounts(
|
|
|
|
10_000,
|
|
|
|
&validator_voting_keypairs,
|
|
|
|
vec![stake_per_validator; validator_voting_keypairs.len()],
|
|
|
|
);
|
|
|
|
|
|
|
|
let bank0 = Bank::new(&genesis_config);
|
|
|
|
// Votes for slots less than the provided root bank's slot should not be processed
|
|
|
|
let bank3 = Arc::new(Bank::new_from_parent(
|
|
|
|
&Arc::new(bank0),
|
|
|
|
&Pubkey::default(),
|
|
|
|
3,
|
|
|
|
));
|
2020-03-09 22:03:09 -07:00
|
|
|
let vote_slots = vec![1, 2];
|
2020-07-28 02:33:27 -07:00
|
|
|
send_vote_txs(
|
|
|
|
vote_slots,
|
|
|
|
vec![],
|
|
|
|
&validator_voting_keypairs,
|
|
|
|
None,
|
|
|
|
&votes_sender,
|
|
|
|
&replay_votes_sender,
|
|
|
|
);
|
2020-10-23 18:19:12 -07:00
|
|
|
ClusterInfoVoteListener::listen_and_confirm_votes(
|
2020-07-28 02:33:27 -07:00
|
|
|
&votes_receiver,
|
|
|
|
&vote_tracker,
|
|
|
|
&bank3,
|
|
|
|
&subscriptions,
|
|
|
|
&verified_vote_sender,
|
|
|
|
&replay_votes_receiver,
|
2020-09-28 19:43:05 -07:00
|
|
|
&None,
|
2020-07-28 02:33:27 -07:00
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
// Vote slots for slots greater than root bank's set of currently calculated epochs
|
|
|
|
// are ignored
|
|
|
|
let max_epoch = bank3.get_leader_schedule_epoch(bank3.slot());
|
|
|
|
assert!(bank3.epoch_stakes(max_epoch).is_some());
|
|
|
|
let unknown_epoch = max_epoch + 1;
|
|
|
|
assert!(bank3.epoch_stakes(unknown_epoch).is_none());
|
|
|
|
let first_slot_in_unknown_epoch = bank3
|
|
|
|
.epoch_schedule()
|
|
|
|
.get_first_slot_in_epoch(unknown_epoch);
|
|
|
|
let vote_slots = vec![first_slot_in_unknown_epoch, first_slot_in_unknown_epoch + 1];
|
|
|
|
send_vote_txs(
|
|
|
|
vote_slots,
|
|
|
|
vec![],
|
|
|
|
&validator_voting_keypairs,
|
|
|
|
None,
|
|
|
|
&votes_sender,
|
|
|
|
&replay_votes_sender,
|
|
|
|
);
|
2020-10-23 18:19:12 -07:00
|
|
|
ClusterInfoVoteListener::listen_and_confirm_votes(
|
2020-07-28 02:33:27 -07:00
|
|
|
&votes_receiver,
|
|
|
|
&vote_tracker,
|
|
|
|
&bank3,
|
|
|
|
&subscriptions,
|
|
|
|
&verified_vote_sender,
|
|
|
|
&replay_votes_receiver,
|
2020-09-28 19:43:05 -07:00
|
|
|
&None,
|
2020-07-28 02:33:27 -07:00
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
// Should be no updates since everything was ignored
|
|
|
|
assert!(vote_tracker.slot_vote_trackers.read().unwrap().is_empty());
|
|
|
|
}
|
|
|
|
|
|
|
|
fn send_vote_txs(
|
|
|
|
gossip_vote_slots: Vec<Slot>,
|
|
|
|
replay_vote_slots: Vec<Slot>,
|
|
|
|
validator_voting_keypairs: &[ValidatorVoteKeypairs],
|
2020-07-29 23:17:40 -07:00
|
|
|
switch_proof_hash: Option<Hash>,
|
2020-07-28 02:33:27 -07:00
|
|
|
votes_sender: &VerifiedVoteTransactionsSender,
|
2020-08-07 11:21:35 -07:00
|
|
|
replay_votes_sender: &ReplayVoteSender,
|
2020-07-28 02:33:27 -07:00
|
|
|
) {
|
2020-03-09 22:03:09 -07:00
|
|
|
validator_voting_keypairs.iter().for_each(|keypairs| {
|
|
|
|
let node_keypair = &keypairs.node_keypair;
|
|
|
|
let vote_keypair = &keypairs.vote_keypair;
|
|
|
|
let vote_tx = vote_transaction::new_vote_transaction(
|
2020-07-28 02:33:27 -07:00
|
|
|
gossip_vote_slots.clone(),
|
2020-03-09 22:03:09 -07:00
|
|
|
Hash::default(),
|
|
|
|
Hash::default(),
|
|
|
|
node_keypair,
|
|
|
|
vote_keypair,
|
|
|
|
vote_keypair,
|
2020-07-29 23:17:40 -07:00
|
|
|
switch_proof_hash,
|
2020-03-09 22:03:09 -07:00
|
|
|
);
|
|
|
|
votes_sender.send(vec![vote_tx]).unwrap();
|
2020-07-29 23:17:40 -07:00
|
|
|
let replay_vote = Vote::new(replay_vote_slots.clone(), Hash::default());
|
|
|
|
// Send same vote twice, but should only notify once
|
|
|
|
for _ in 0..2 {
|
2020-07-20 17:29:07 -07:00
|
|
|
replay_votes_sender
|
2020-07-29 23:17:40 -07:00
|
|
|
.send((
|
|
|
|
vote_keypair.pubkey(),
|
|
|
|
replay_vote.clone(),
|
|
|
|
switch_proof_hash,
|
|
|
|
))
|
2020-07-20 17:29:07 -07:00
|
|
|
.unwrap();
|
|
|
|
}
|
2020-03-09 22:03:09 -07:00
|
|
|
});
|
2020-07-28 02:33:27 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fn run_test_process_votes(hash: Option<Hash>) {
|
|
|
|
// Create some voters at genesis
|
|
|
|
let stake_per_validator = 100;
|
|
|
|
let (vote_tracker, _, validator_voting_keypairs, subscriptions) = setup();
|
|
|
|
let (votes_txs_sender, votes_txs_receiver) = unbounded();
|
|
|
|
let (replay_votes_sender, replay_votes_receiver) = unbounded();
|
|
|
|
let (verified_vote_sender, verified_vote_receiver) = unbounded();
|
|
|
|
|
|
|
|
let GenesisConfigInfo { genesis_config, .. } =
|
|
|
|
genesis_utils::create_genesis_config_with_vote_accounts(
|
|
|
|
10_000,
|
|
|
|
&validator_voting_keypairs,
|
|
|
|
vec![stake_per_validator; validator_voting_keypairs.len()],
|
|
|
|
);
|
|
|
|
let bank0 = Bank::new(&genesis_config);
|
|
|
|
|
|
|
|
let gossip_vote_slots = vec![1, 2];
|
|
|
|
let replay_vote_slots = vec![3, 4];
|
|
|
|
send_vote_txs(
|
|
|
|
gossip_vote_slots.clone(),
|
|
|
|
replay_vote_slots.clone(),
|
|
|
|
&validator_voting_keypairs,
|
|
|
|
hash,
|
|
|
|
&votes_txs_sender,
|
|
|
|
&replay_votes_sender,
|
|
|
|
);
|
2020-03-09 22:03:09 -07:00
|
|
|
|
|
|
|
// Check that all the votes were registered for each validator correctly
|
2020-10-23 18:19:12 -07:00
|
|
|
ClusterInfoVoteListener::listen_and_confirm_votes(
|
2020-07-28 02:33:27 -07:00
|
|
|
&votes_txs_receiver,
|
2020-05-17 14:01:08 -07:00
|
|
|
&vote_tracker,
|
2020-07-28 02:33:27 -07:00
|
|
|
&bank0,
|
2020-06-08 17:38:14 -07:00
|
|
|
&subscriptions,
|
2020-07-09 22:52:54 -07:00
|
|
|
&verified_vote_sender,
|
2020-07-20 17:29:07 -07:00
|
|
|
&replay_votes_receiver,
|
2020-09-28 19:43:05 -07:00
|
|
|
&None,
|
2020-05-17 14:01:08 -07:00
|
|
|
)
|
|
|
|
.unwrap();
|
2020-07-09 22:52:54 -07:00
|
|
|
|
|
|
|
// Check that the received votes were pushed to other commponents
|
2020-07-20 17:29:07 -07:00
|
|
|
// subscribing via `verified_vote_receiver`
|
2020-07-28 02:33:27 -07:00
|
|
|
let all_expected_slots: BTreeSet<_> = gossip_vote_slots
|
2020-07-20 17:29:07 -07:00
|
|
|
.into_iter()
|
|
|
|
.chain(replay_vote_slots.into_iter())
|
|
|
|
.collect();
|
|
|
|
let mut pubkey_to_votes: HashMap<Pubkey, BTreeSet<Slot>> = HashMap::new();
|
|
|
|
for (received_pubkey, new_votes) in verified_vote_receiver.try_iter() {
|
|
|
|
let already_received_votes = pubkey_to_votes.entry(received_pubkey).or_default();
|
|
|
|
for new_vote in new_votes {
|
|
|
|
// `new_vote` should only be received once
|
|
|
|
assert!(already_received_votes.insert(new_vote));
|
|
|
|
}
|
2020-07-09 22:52:54 -07:00
|
|
|
}
|
2020-07-20 17:29:07 -07:00
|
|
|
assert_eq!(pubkey_to_votes.len(), validator_voting_keypairs.len());
|
|
|
|
for keypairs in &validator_voting_keypairs {
|
|
|
|
assert_eq!(
|
|
|
|
*pubkey_to_votes
|
|
|
|
.get(&keypairs.vote_keypair.pubkey())
|
|
|
|
.unwrap(),
|
|
|
|
all_expected_slots
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the vote trackers were updated correctly
|
|
|
|
for vote_slot in all_expected_slots {
|
2020-03-09 22:03:09 -07:00
|
|
|
let slot_vote_tracker = vote_tracker.get_slot_vote_tracker(vote_slot).unwrap();
|
|
|
|
let r_slot_vote_tracker = slot_vote_tracker.read().unwrap();
|
|
|
|
for voting_keypairs in &validator_voting_keypairs {
|
|
|
|
let pubkey = voting_keypairs.vote_keypair.pubkey();
|
2020-07-20 17:29:07 -07:00
|
|
|
assert!(r_slot_vote_tracker.voted.contains_key(&pubkey));
|
2020-03-09 22:03:09 -07:00
|
|
|
assert!(r_slot_vote_tracker
|
|
|
|
.updates
|
|
|
|
.as_ref()
|
|
|
|
.unwrap()
|
|
|
|
.contains(&Arc::new(pubkey)));
|
2020-07-28 02:33:27 -07:00
|
|
|
// Only the last vote in the stack of `gossip_votes` should count towards
|
|
|
|
// the `optimistic` vote set.
|
|
|
|
let optimistic_votes_tracker =
|
|
|
|
r_slot_vote_tracker.optimistic_votes_tracker(&Hash::default());
|
2020-07-29 23:17:40 -07:00
|
|
|
if vote_slot == 2 || vote_slot == 4 {
|
2020-07-28 02:33:27 -07:00
|
|
|
let optimistic_votes_tracker = optimistic_votes_tracker.unwrap();
|
|
|
|
assert!(optimistic_votes_tracker.voted().contains(&pubkey));
|
|
|
|
assert_eq!(
|
|
|
|
optimistic_votes_tracker.stake(),
|
|
|
|
stake_per_validator * validator_voting_keypairs.len() as u64
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
assert!(optimistic_votes_tracker.is_none())
|
|
|
|
}
|
2020-03-09 22:03:09 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-28 02:33:27 -07:00
|
|
|
#[test]
|
|
|
|
fn test_process_votes1() {
|
|
|
|
run_test_process_votes(None);
|
|
|
|
run_test_process_votes(Some(Hash::default()));
|
|
|
|
}
|
|
|
|
|
2020-03-09 22:03:09 -07:00
|
|
|
#[test]
|
|
|
|
fn test_process_votes2() {
|
|
|
|
// Create some voters at genesis
|
2020-05-17 14:01:08 -07:00
|
|
|
let (vote_tracker, _, validator_voting_keypairs, subscriptions) = setup();
|
2020-07-28 02:33:27 -07:00
|
|
|
|
|
|
|
// Create bank with the voters
|
|
|
|
let stake_per_validator = 100;
|
|
|
|
let GenesisConfigInfo { genesis_config, .. } =
|
|
|
|
genesis_utils::create_genesis_config_with_vote_accounts(
|
|
|
|
10_000,
|
|
|
|
&validator_voting_keypairs,
|
|
|
|
vec![stake_per_validator; validator_voting_keypairs.len()],
|
|
|
|
);
|
|
|
|
let bank0 = Bank::new(&genesis_config);
|
|
|
|
|
2020-03-09 22:03:09 -07:00
|
|
|
// Send some votes to process
|
2020-07-09 22:52:54 -07:00
|
|
|
let (votes_txs_sender, votes_txs_receiver) = unbounded();
|
|
|
|
let (verified_vote_sender, verified_vote_receiver) = unbounded();
|
2020-07-20 17:29:07 -07:00
|
|
|
let (_replay_votes_sender, replay_votes_receiver) = unbounded();
|
2020-03-09 22:03:09 -07:00
|
|
|
|
2020-07-09 22:52:54 -07:00
|
|
|
let mut expected_votes = vec![];
|
2020-07-28 02:33:27 -07:00
|
|
|
let num_voters_per_slot = 2;
|
|
|
|
let bank_hash = Hash::default();
|
|
|
|
for (i, keyset) in validator_voting_keypairs
|
|
|
|
.chunks(num_voters_per_slot)
|
|
|
|
.enumerate()
|
|
|
|
{
|
2020-03-09 22:03:09 -07:00
|
|
|
let validator_votes: Vec<_> = keyset
|
|
|
|
.iter()
|
|
|
|
.map(|keypairs| {
|
|
|
|
let node_keypair = &keypairs.node_keypair;
|
|
|
|
let vote_keypair = &keypairs.vote_keypair;
|
2020-07-09 22:52:54 -07:00
|
|
|
expected_votes.push((vote_keypair.pubkey(), vec![i as Slot + 1]));
|
2020-05-15 09:35:43 -07:00
|
|
|
vote_transaction::new_vote_transaction(
|
2020-03-09 22:03:09 -07:00
|
|
|
vec![i as u64 + 1],
|
2020-07-28 02:33:27 -07:00
|
|
|
bank_hash,
|
2020-03-09 22:03:09 -07:00
|
|
|
Hash::default(),
|
|
|
|
node_keypair,
|
|
|
|
vote_keypair,
|
|
|
|
vote_keypair,
|
2020-07-28 02:33:27 -07:00
|
|
|
None,
|
2020-05-15 09:35:43 -07:00
|
|
|
)
|
2020-03-09 22:03:09 -07:00
|
|
|
})
|
|
|
|
.collect();
|
2020-07-09 22:52:54 -07:00
|
|
|
votes_txs_sender.send(validator_votes).unwrap();
|
2020-03-09 22:03:09 -07:00
|
|
|
}
|
|
|
|
|
2020-07-09 22:52:54 -07:00
|
|
|
// Read and process votes from channel `votes_receiver`
|
2020-10-23 18:19:12 -07:00
|
|
|
ClusterInfoVoteListener::listen_and_confirm_votes(
|
2020-07-09 22:52:54 -07:00
|
|
|
&votes_txs_receiver,
|
2020-05-17 14:01:08 -07:00
|
|
|
&vote_tracker,
|
2020-07-28 02:33:27 -07:00
|
|
|
&bank0,
|
2020-06-08 17:38:14 -07:00
|
|
|
&subscriptions,
|
2020-07-09 22:52:54 -07:00
|
|
|
&verified_vote_sender,
|
2020-07-20 17:29:07 -07:00
|
|
|
&replay_votes_receiver,
|
2020-09-28 19:43:05 -07:00
|
|
|
&None,
|
2020-05-17 14:01:08 -07:00
|
|
|
)
|
|
|
|
.unwrap();
|
2020-07-09 22:52:54 -07:00
|
|
|
|
|
|
|
// Check that the received votes were pushed to other commponents
|
|
|
|
// subscribing via a channel
|
2020-07-20 17:29:07 -07:00
|
|
|
let received_votes: Vec<_> = verified_vote_receiver.try_iter().collect();
|
2020-07-09 22:52:54 -07:00
|
|
|
assert_eq!(received_votes.len(), validator_voting_keypairs.len());
|
|
|
|
for (expected_pubkey_vote, received_pubkey_vote) in
|
|
|
|
expected_votes.iter().zip(received_votes.iter())
|
|
|
|
{
|
|
|
|
assert_eq!(expected_pubkey_vote, received_pubkey_vote);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that all the votes were registered for each validator correctly
|
2020-03-09 22:03:09 -07:00
|
|
|
for (i, keyset) in validator_voting_keypairs.chunks(2).enumerate() {
|
|
|
|
let slot_vote_tracker = vote_tracker.get_slot_vote_tracker(i as u64 + 1).unwrap();
|
|
|
|
let r_slot_vote_tracker = &slot_vote_tracker.read().unwrap();
|
|
|
|
for voting_keypairs in keyset {
|
|
|
|
let pubkey = voting_keypairs.vote_keypair.pubkey();
|
2020-07-20 17:29:07 -07:00
|
|
|
assert!(r_slot_vote_tracker.voted.contains_key(&pubkey));
|
2020-03-09 22:03:09 -07:00
|
|
|
assert!(r_slot_vote_tracker
|
|
|
|
.updates
|
|
|
|
.as_ref()
|
|
|
|
.unwrap()
|
|
|
|
.contains(&Arc::new(pubkey)));
|
2020-07-28 02:33:27 -07:00
|
|
|
// All the votes were single votes, so they should all count towards
|
|
|
|
// the optimistic confirmation vote set
|
|
|
|
let optimistic_votes_tracker = r_slot_vote_tracker
|
|
|
|
.optimistic_votes_tracker(&bank_hash)
|
|
|
|
.unwrap();
|
|
|
|
assert!(optimistic_votes_tracker.voted().contains(&pubkey));
|
|
|
|
assert_eq!(
|
|
|
|
optimistic_votes_tracker.stake(),
|
|
|
|
num_voters_per_slot as u64 * stake_per_validator
|
|
|
|
);
|
2020-03-09 22:03:09 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-29 23:17:40 -07:00
|
|
|
fn run_test_process_votes3(switch_proof_hash: Option<Hash>) {
|
2020-07-20 17:29:07 -07:00
|
|
|
let (votes_sender, votes_receiver) = unbounded();
|
|
|
|
let (verified_vote_sender, _verified_vote_receiver) = unbounded();
|
|
|
|
let (replay_votes_sender, replay_votes_receiver) = unbounded();
|
|
|
|
|
|
|
|
let vote_slot = 1;
|
2020-07-30 02:52:27 -07:00
|
|
|
let vote_bank_hash = Hash::default();
|
2020-07-20 17:29:07 -07:00
|
|
|
// Events:
|
|
|
|
// 0: Send gossip vote
|
|
|
|
// 1: Send replay vote
|
|
|
|
// 2: Send both
|
|
|
|
let ordered_events = vec![
|
|
|
|
vec![0],
|
|
|
|
vec![1],
|
|
|
|
vec![0, 1],
|
|
|
|
vec![1, 0],
|
|
|
|
vec![2],
|
|
|
|
vec![0, 1, 2],
|
|
|
|
vec![1, 0, 2],
|
2020-07-29 23:17:40 -07:00
|
|
|
vec![0, 1, 2, 0, 1, 2],
|
2020-07-20 17:29:07 -07:00
|
|
|
];
|
|
|
|
for events in ordered_events {
|
|
|
|
let (vote_tracker, bank, validator_voting_keypairs, subscriptions) = setup();
|
|
|
|
let node_keypair = &validator_voting_keypairs[0].node_keypair;
|
|
|
|
let vote_keypair = &validator_voting_keypairs[0].vote_keypair;
|
|
|
|
for &e in &events {
|
|
|
|
if e == 0 || e == 2 {
|
|
|
|
// Create vote transaction
|
|
|
|
let vote_tx = vote_transaction::new_vote_transaction(
|
|
|
|
vec![vote_slot],
|
2020-07-30 02:52:27 -07:00
|
|
|
vote_bank_hash,
|
2020-07-20 17:29:07 -07:00
|
|
|
Hash::default(),
|
|
|
|
node_keypair,
|
|
|
|
vote_keypair,
|
|
|
|
vote_keypair,
|
2020-07-29 23:17:40 -07:00
|
|
|
switch_proof_hash,
|
2020-07-20 17:29:07 -07:00
|
|
|
);
|
|
|
|
votes_sender.send(vec![vote_tx.clone()]).unwrap();
|
|
|
|
}
|
|
|
|
if e == 1 || e == 2 {
|
|
|
|
replay_votes_sender
|
2020-07-29 23:17:40 -07:00
|
|
|
.send((
|
|
|
|
vote_keypair.pubkey(),
|
|
|
|
Vote::new(vec![vote_slot], Hash::default()),
|
|
|
|
switch_proof_hash,
|
|
|
|
))
|
2020-07-20 17:29:07 -07:00
|
|
|
.unwrap();
|
|
|
|
}
|
2020-10-23 18:19:12 -07:00
|
|
|
let _ = ClusterInfoVoteListener::listen_and_confirm_votes(
|
2020-07-20 17:29:07 -07:00
|
|
|
&votes_receiver,
|
|
|
|
&vote_tracker,
|
2020-07-28 02:33:27 -07:00
|
|
|
&bank,
|
2020-07-20 17:29:07 -07:00
|
|
|
&subscriptions,
|
|
|
|
&verified_vote_sender,
|
|
|
|
&replay_votes_receiver,
|
2020-09-28 19:43:05 -07:00
|
|
|
&None,
|
2020-07-20 17:29:07 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
let slot_vote_tracker = vote_tracker.get_slot_vote_tracker(vote_slot).unwrap();
|
|
|
|
let r_slot_vote_tracker = &slot_vote_tracker.read().unwrap();
|
|
|
|
|
|
|
|
if events == vec![1] {
|
|
|
|
// Check `gossip_only_stake` is not incremented
|
2020-07-30 02:52:27 -07:00
|
|
|
assert_eq!(
|
|
|
|
r_slot_vote_tracker
|
|
|
|
.optimistic_votes_tracker(&vote_bank_hash)
|
|
|
|
.unwrap()
|
|
|
|
.stake(),
|
|
|
|
100
|
|
|
|
);
|
2020-07-20 17:29:07 -07:00
|
|
|
assert_eq!(r_slot_vote_tracker.gossip_only_stake, 0);
|
|
|
|
} else {
|
2020-07-28 02:33:27 -07:00
|
|
|
// Check that both the `gossip_only_stake` and `total_voted_stake` both
|
2020-07-20 17:29:07 -07:00
|
|
|
// increased
|
2020-07-30 02:52:27 -07:00
|
|
|
assert_eq!(
|
|
|
|
r_slot_vote_tracker
|
|
|
|
.optimistic_votes_tracker(&vote_bank_hash)
|
|
|
|
.unwrap()
|
|
|
|
.stake(),
|
|
|
|
100
|
|
|
|
);
|
2020-07-20 17:29:07 -07:00
|
|
|
assert_eq!(r_slot_vote_tracker.gossip_only_stake, 100);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-28 02:33:27 -07:00
|
|
|
#[test]
|
|
|
|
fn test_run_test_process_votes3() {
|
|
|
|
run_test_process_votes3(None);
|
|
|
|
run_test_process_votes3(Some(Hash::default()));
|
|
|
|
}
|
|
|
|
|
2020-03-09 22:03:09 -07:00
|
|
|
#[test]
|
|
|
|
fn test_get_voters_by_epoch() {
|
|
|
|
// Create some voters at genesis
|
2020-05-17 14:01:08 -07:00
|
|
|
let (vote_tracker, bank, validator_voting_keypairs, _) = setup();
|
2020-03-09 22:03:09 -07:00
|
|
|
let last_known_epoch = bank.get_leader_schedule_epoch(bank.slot());
|
|
|
|
let last_known_slot = bank
|
|
|
|
.epoch_schedule()
|
|
|
|
.get_last_slot_in_epoch(last_known_epoch);
|
|
|
|
|
2020-03-26 17:55:17 -07:00
|
|
|
// Check we can get the authorized voters
|
2020-03-09 22:03:09 -07:00
|
|
|
for keypairs in &validator_voting_keypairs {
|
|
|
|
assert!(vote_tracker
|
|
|
|
.get_authorized_voter(&keypairs.vote_keypair.pubkey(), last_known_slot)
|
|
|
|
.is_some());
|
|
|
|
assert!(vote_tracker
|
|
|
|
.get_authorized_voter(&keypairs.vote_keypair.pubkey(), last_known_slot + 1)
|
|
|
|
.is_none());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create the set of relevant voters for the next epoch
|
|
|
|
let new_epoch = last_known_epoch + 1;
|
|
|
|
let first_slot_in_new_epoch = bank.epoch_schedule().get_first_slot_in_epoch(new_epoch);
|
2020-07-23 18:50:42 -07:00
|
|
|
let new_keypairs: Vec<_> = (0..10).map(|_| ValidatorVoteKeypairs::new_rand()).collect();
|
2020-03-26 17:55:17 -07:00
|
|
|
let new_epoch_authorized_voters: HashMap<_, _> = new_keypairs
|
2020-03-09 22:03:09 -07:00
|
|
|
.iter()
|
|
|
|
.chain(validator_voting_keypairs[0..5].iter())
|
2020-03-26 17:55:17 -07:00
|
|
|
.map(|keypair| (keypair.vote_keypair.pubkey(), keypair.vote_keypair.pubkey()))
|
2020-03-09 22:03:09 -07:00
|
|
|
.collect();
|
|
|
|
|
2020-03-26 17:55:17 -07:00
|
|
|
vote_tracker
|
|
|
|
.epoch_authorized_voters
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
.insert(new_epoch, Arc::new(new_epoch_authorized_voters));
|
2020-03-09 22:03:09 -07:00
|
|
|
|
|
|
|
// These keypairs made it into the new epoch
|
|
|
|
for keypairs in new_keypairs
|
|
|
|
.iter()
|
|
|
|
.chain(validator_voting_keypairs[0..5].iter())
|
|
|
|
{
|
|
|
|
assert!(vote_tracker
|
|
|
|
.get_authorized_voter(&keypairs.vote_keypair.pubkey(), first_slot_in_new_epoch)
|
|
|
|
.is_some());
|
|
|
|
}
|
|
|
|
|
|
|
|
// These keypairs were not refreshed in new epoch
|
|
|
|
for keypairs in validator_voting_keypairs[5..10].iter() {
|
|
|
|
assert!(vote_tracker
|
|
|
|
.get_authorized_voter(&keypairs.vote_keypair.pubkey(), first_slot_in_new_epoch)
|
|
|
|
.is_none());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_vote_tracker_references() {
|
|
|
|
// Create some voters at genesis
|
2020-07-23 18:50:42 -07:00
|
|
|
let validator_keypairs: Vec<_> =
|
|
|
|
(0..2).map(|_| ValidatorVoteKeypairs::new_rand()).collect();
|
2020-03-09 22:03:09 -07:00
|
|
|
|
|
|
|
let GenesisConfigInfo { genesis_config, .. } =
|
|
|
|
genesis_utils::create_genesis_config_with_vote_accounts(
|
|
|
|
10_000,
|
2020-07-20 17:29:07 -07:00
|
|
|
&validator_keypairs,
|
2020-07-23 18:50:42 -07:00
|
|
|
vec![100; validator_keypairs.len()],
|
2020-03-09 22:03:09 -07:00
|
|
|
);
|
|
|
|
let bank = Bank::new(&genesis_config);
|
2020-05-17 14:01:08 -07:00
|
|
|
let exit = Arc::new(AtomicBool::new(false));
|
2020-09-28 19:43:05 -07:00
|
|
|
let bank_forks = Arc::new(RwLock::new(BankForks::new(bank)));
|
|
|
|
let bank = bank_forks.read().unwrap().get(0).unwrap().clone();
|
2020-05-17 14:01:08 -07:00
|
|
|
let vote_tracker = VoteTracker::new(&bank);
|
2020-09-28 19:43:05 -07:00
|
|
|
let optimistically_confirmed_bank =
|
|
|
|
OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks);
|
2020-05-17 14:01:08 -07:00
|
|
|
let subscriptions = Arc::new(RpcSubscriptions::new(
|
|
|
|
&exit,
|
2020-09-28 19:43:05 -07:00
|
|
|
bank_forks,
|
2020-06-25 21:06:58 -07:00
|
|
|
Arc::new(RwLock::new(BlockCommitmentCache::default())),
|
2020-09-28 19:43:05 -07:00
|
|
|
optimistically_confirmed_bank,
|
2020-05-17 14:01:08 -07:00
|
|
|
));
|
2020-03-09 22:03:09 -07:00
|
|
|
|
|
|
|
// Send a vote to process, should add a reference to the pubkey for that voter
|
|
|
|
// in the tracker
|
2020-07-20 17:29:07 -07:00
|
|
|
let validator0_keypairs = &validator_keypairs[0];
|
|
|
|
let voted_slot = bank.slot() + 1;
|
2020-03-09 22:03:09 -07:00
|
|
|
let vote_tx = vec![vote_transaction::new_vote_transaction(
|
|
|
|
// Must vote > root to be processed
|
2020-07-20 17:29:07 -07:00
|
|
|
vec![voted_slot],
|
2020-03-09 22:03:09 -07:00
|
|
|
Hash::default(),
|
|
|
|
Hash::default(),
|
|
|
|
&validator0_keypairs.node_keypair,
|
|
|
|
&validator0_keypairs.vote_keypair,
|
|
|
|
&validator0_keypairs.vote_keypair,
|
2020-07-28 02:33:27 -07:00
|
|
|
None,
|
2020-03-09 22:03:09 -07:00
|
|
|
)];
|
|
|
|
|
2020-07-09 22:52:54 -07:00
|
|
|
let (verified_vote_sender, _verified_vote_receiver) = unbounded();
|
2020-10-23 18:19:12 -07:00
|
|
|
ClusterInfoVoteListener::filter_and_confirm_with_new_votes(
|
2020-07-09 22:52:54 -07:00
|
|
|
&vote_tracker,
|
|
|
|
vote_tx,
|
2020-07-29 23:17:40 -07:00
|
|
|
// Add gossip vote for same slot, should not affect outcome
|
|
|
|
vec![(
|
|
|
|
validator0_keypairs.vote_keypair.pubkey(),
|
|
|
|
Vote::new(vec![voted_slot], Hash::default()),
|
|
|
|
None,
|
|
|
|
)],
|
2020-07-28 02:33:27 -07:00
|
|
|
&bank,
|
2020-07-09 22:52:54 -07:00
|
|
|
&subscriptions,
|
|
|
|
&verified_vote_sender,
|
2020-09-28 19:43:05 -07:00
|
|
|
&None,
|
2020-07-09 22:52:54 -07:00
|
|
|
);
|
2020-03-09 22:03:09 -07:00
|
|
|
|
2020-03-26 17:55:17 -07:00
|
|
|
// Setup next epoch
|
|
|
|
let old_epoch = bank.get_leader_schedule_epoch(bank.slot());
|
|
|
|
let new_epoch = old_epoch + 1;
|
|
|
|
let new_epoch_vote_accounts: HashMap<_, _> = vec![(
|
|
|
|
validator0_keypairs.vote_keypair.pubkey(),
|
|
|
|
validator0_keypairs.vote_keypair.pubkey(),
|
|
|
|
)]
|
2020-03-09 22:03:09 -07:00
|
|
|
.into_iter()
|
|
|
|
.collect();
|
2020-03-26 17:55:17 -07:00
|
|
|
vote_tracker
|
|
|
|
.epoch_authorized_voters
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
.insert(new_epoch, Arc::new(new_epoch_vote_accounts));
|
2020-03-09 22:03:09 -07:00
|
|
|
|
2020-03-26 17:55:17 -07:00
|
|
|
// Test with votes across two epochs
|
|
|
|
let first_slot_in_new_epoch = bank.epoch_schedule().get_first_slot_in_epoch(new_epoch);
|
2020-03-09 22:03:09 -07:00
|
|
|
|
2020-07-20 17:29:07 -07:00
|
|
|
// Make 2 new votes in two different epochs for the same pubkey,
|
|
|
|
// the ref count should go up by 3 * ref_count_per_vote
|
2020-07-28 02:33:27 -07:00
|
|
|
// Add 1 vote through the replay channel for a different pubkey,
|
|
|
|
// ref count should equal `current_ref_count` for that pubkey.
|
|
|
|
let vote_txs: Vec<_> = [first_slot_in_new_epoch - 1, first_slot_in_new_epoch]
|
2020-03-09 22:03:09 -07:00
|
|
|
.iter()
|
|
|
|
.map(|slot| {
|
|
|
|
vote_transaction::new_vote_transaction(
|
|
|
|
// Must vote > root to be processed
|
|
|
|
vec![*slot],
|
|
|
|
Hash::default(),
|
|
|
|
Hash::default(),
|
|
|
|
&validator0_keypairs.node_keypair,
|
|
|
|
&validator0_keypairs.vote_keypair,
|
|
|
|
&validator0_keypairs.vote_keypair,
|
2020-07-28 02:33:27 -07:00
|
|
|
None,
|
2020-03-09 22:03:09 -07:00
|
|
|
)
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
|
2020-07-28 02:33:27 -07:00
|
|
|
let new_root_bank =
|
|
|
|
Bank::new_from_parent(&bank, &Pubkey::default(), first_slot_in_new_epoch - 2);
|
2020-10-23 18:19:12 -07:00
|
|
|
ClusterInfoVoteListener::filter_and_confirm_with_new_votes(
|
2020-07-09 22:52:54 -07:00
|
|
|
&vote_tracker,
|
|
|
|
vote_txs,
|
2020-07-29 23:17:40 -07:00
|
|
|
vec![(
|
|
|
|
validator_keypairs[1].vote_keypair.pubkey(),
|
|
|
|
Vote::new(vec![first_slot_in_new_epoch], Hash::default()),
|
|
|
|
None,
|
|
|
|
)],
|
2020-07-28 02:33:27 -07:00
|
|
|
&new_root_bank,
|
2020-07-09 22:52:54 -07:00
|
|
|
&subscriptions,
|
|
|
|
&verified_vote_sender,
|
2020-09-28 19:43:05 -07:00
|
|
|
&None,
|
2020-07-20 17:29:07 -07:00
|
|
|
);
|
2020-03-09 22:03:09 -07:00
|
|
|
}
|
2020-03-26 17:55:17 -07:00
|
|
|
|
2020-05-17 14:01:08 -07:00
|
|
|
fn setup() -> (
|
|
|
|
Arc<VoteTracker>,
|
|
|
|
Arc<Bank>,
|
|
|
|
Vec<ValidatorVoteKeypairs>,
|
|
|
|
Arc<RpcSubscriptions>,
|
|
|
|
) {
|
2020-07-23 18:50:42 -07:00
|
|
|
let validator_voting_keypairs: Vec<_> =
|
|
|
|
(0..10).map(|_| ValidatorVoteKeypairs::new_rand()).collect();
|
2020-03-26 17:55:17 -07:00
|
|
|
let GenesisConfigInfo { genesis_config, .. } =
|
|
|
|
genesis_utils::create_genesis_config_with_vote_accounts(
|
|
|
|
10_000,
|
|
|
|
&validator_voting_keypairs,
|
2020-07-23 18:50:42 -07:00
|
|
|
vec![100; validator_voting_keypairs.len()],
|
2020-03-26 17:55:17 -07:00
|
|
|
);
|
|
|
|
let bank = Bank::new(&genesis_config);
|
|
|
|
let vote_tracker = VoteTracker::new(&bank);
|
2020-05-17 14:01:08 -07:00
|
|
|
let exit = Arc::new(AtomicBool::new(false));
|
2020-09-28 19:43:05 -07:00
|
|
|
let bank_forks = Arc::new(RwLock::new(BankForks::new(bank)));
|
|
|
|
let bank = bank_forks.read().unwrap().get(0).unwrap().clone();
|
|
|
|
let optimistically_confirmed_bank =
|
|
|
|
OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks);
|
2020-05-17 14:01:08 -07:00
|
|
|
let subscriptions = Arc::new(RpcSubscriptions::new(
|
|
|
|
&exit,
|
2020-09-28 19:43:05 -07:00
|
|
|
bank_forks,
|
2020-06-25 21:06:58 -07:00
|
|
|
Arc::new(RwLock::new(BlockCommitmentCache::default())),
|
2020-09-28 19:43:05 -07:00
|
|
|
optimistically_confirmed_bank,
|
2020-05-17 14:01:08 -07:00
|
|
|
));
|
2020-03-26 17:55:17 -07:00
|
|
|
|
|
|
|
// Integrity Checks
|
|
|
|
let current_epoch = bank.epoch();
|
|
|
|
let leader_schedule_epoch = bank.get_leader_schedule_epoch(bank.slot());
|
|
|
|
|
|
|
|
// Check the vote tracker has all the known epoch state on construction
|
|
|
|
for epoch in current_epoch..=leader_schedule_epoch {
|
|
|
|
assert_eq!(
|
|
|
|
vote_tracker
|
|
|
|
.epoch_authorized_voters
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.get(&epoch)
|
|
|
|
.unwrap(),
|
|
|
|
bank.epoch_stakes(epoch).unwrap().epoch_authorized_voters()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the epoch state is correct
|
|
|
|
assert_eq!(
|
|
|
|
*vote_tracker.leader_schedule_epoch.read().unwrap(),
|
|
|
|
leader_schedule_epoch,
|
|
|
|
);
|
|
|
|
assert_eq!(*vote_tracker.current_epoch.read().unwrap(), current_epoch);
|
|
|
|
(
|
|
|
|
Arc::new(vote_tracker),
|
2020-05-17 14:01:08 -07:00
|
|
|
bank,
|
2020-03-26 17:55:17 -07:00
|
|
|
validator_voting_keypairs,
|
2020-05-17 14:01:08 -07:00
|
|
|
subscriptions,
|
2020-03-26 17:55:17 -07:00
|
|
|
)
|
|
|
|
}
|
2020-04-23 17:04:09 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_verify_votes_empty() {
|
|
|
|
solana_logger::setup();
|
|
|
|
let votes = vec![];
|
|
|
|
let labels = vec![];
|
2020-05-08 10:00:23 -07:00
|
|
|
let (vote_txs, packets) = ClusterInfoVoteListener::verify_votes(votes, labels);
|
2020-04-23 17:04:09 -07:00
|
|
|
assert!(vote_txs.is_empty());
|
|
|
|
assert!(packets.is_empty());
|
|
|
|
}
|
|
|
|
|
2021-03-03 11:07:16 -08:00
|
|
|
fn verify_packets_len(packets: &[(CrdsValueLabel, Slot, Packets)], ref_value: usize) {
|
|
|
|
let num_packets: usize = packets.iter().map(|(_, _, p)| p.packets.len()).sum();
|
2020-04-23 17:04:09 -07:00
|
|
|
assert_eq!(num_packets, ref_value);
|
|
|
|
}
|
|
|
|
|
2020-07-28 02:33:27 -07:00
|
|
|
fn test_vote_tx(hash: Option<Hash>) -> Transaction {
|
2020-04-23 17:04:09 -07:00
|
|
|
let node_keypair = Keypair::new();
|
|
|
|
let vote_keypair = Keypair::new();
|
|
|
|
let auth_voter_keypair = Keypair::new();
|
2020-05-15 09:35:43 -07:00
|
|
|
vote_transaction::new_vote_transaction(
|
2020-04-23 17:04:09 -07:00
|
|
|
vec![0],
|
|
|
|
Hash::default(),
|
|
|
|
Hash::default(),
|
|
|
|
&node_keypair,
|
|
|
|
&vote_keypair,
|
|
|
|
&auth_voter_keypair,
|
2020-07-28 02:33:27 -07:00
|
|
|
hash,
|
2020-05-15 09:35:43 -07:00
|
|
|
)
|
2020-04-23 17:04:09 -07:00
|
|
|
}
|
|
|
|
|
2020-07-28 02:33:27 -07:00
|
|
|
fn run_test_verify_votes_1_pass(hash: Option<Hash>) {
|
|
|
|
let vote_tx = test_vote_tx(hash);
|
2020-05-15 09:35:43 -07:00
|
|
|
let votes = vec![vote_tx];
|
2020-10-19 12:12:08 -07:00
|
|
|
let labels = vec![CrdsValueLabel::Vote(0, solana_sdk::pubkey::new_rand())];
|
2020-05-08 10:00:23 -07:00
|
|
|
let (vote_txs, packets) = ClusterInfoVoteListener::verify_votes(votes, labels);
|
2020-04-23 17:04:09 -07:00
|
|
|
assert_eq!(vote_txs.len(), 1);
|
|
|
|
verify_packets_len(&packets, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2020-07-28 02:33:27 -07:00
|
|
|
fn test_verify_votes_1_pass() {
|
|
|
|
run_test_verify_votes_1_pass(None);
|
|
|
|
run_test_verify_votes_1_pass(Some(Hash::default()));
|
|
|
|
}
|
|
|
|
|
|
|
|
fn run_test_bad_vote(hash: Option<Hash>) {
|
|
|
|
let vote_tx = test_vote_tx(hash);
|
2020-04-23 17:04:09 -07:00
|
|
|
let mut bad_vote = vote_tx.clone();
|
|
|
|
bad_vote.signatures[0] = Signature::default();
|
|
|
|
let votes = vec![vote_tx.clone(), bad_vote, vote_tx];
|
2020-10-19 12:12:08 -07:00
|
|
|
let label = CrdsValueLabel::Vote(0, solana_sdk::pubkey::new_rand());
|
2020-05-15 09:35:43 -07:00
|
|
|
let labels: Vec<_> = (0..votes.len()).map(|_| label.clone()).collect();
|
2020-05-08 10:00:23 -07:00
|
|
|
let (vote_txs, packets) = ClusterInfoVoteListener::verify_votes(votes, labels);
|
2020-04-23 17:04:09 -07:00
|
|
|
assert_eq!(vote_txs.len(), 2);
|
|
|
|
verify_packets_len(&packets, 2);
|
|
|
|
}
|
2020-07-20 17:29:07 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_sum_stake() {
|
|
|
|
let (_, bank, validator_voting_keypairs, _) = setup();
|
|
|
|
let vote_keypair = &validator_voting_keypairs[0].vote_keypair;
|
|
|
|
let epoch_stakes = bank.epoch_stakes(bank.epoch()).unwrap();
|
|
|
|
let mut gossip_only_stake = 0;
|
|
|
|
|
|
|
|
ClusterInfoVoteListener::sum_stake(
|
|
|
|
&mut gossip_only_stake,
|
|
|
|
Some(epoch_stakes),
|
|
|
|
&vote_keypair.pubkey(),
|
|
|
|
);
|
|
|
|
assert_eq!(gossip_only_stake, 100);
|
|
|
|
}
|
2020-07-28 02:33:27 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_bad_vote() {
|
|
|
|
run_test_bad_vote(None);
|
|
|
|
run_test_bad_vote(Some(Hash::default()));
|
|
|
|
}
|
2019-05-21 21:45:38 -07:00
|
|
|
}
|