use live stakes for consensus (#5426)
* use live stakes for consensus * lint * re-enable leader_failure_4 * fixups * re-ignore leader_failure_4
This commit is contained in:
parent
79416381dc
commit
d22848f9b1
|
@ -1,5 +1,4 @@
|
||||||
use crate::bank_forks::BankForks;
|
use crate::bank_forks::BankForks;
|
||||||
use crate::staking_utils;
|
|
||||||
use solana_metrics::datapoint_info;
|
use solana_metrics::datapoint_info;
|
||||||
use solana_runtime::bank::Bank;
|
use solana_runtime::bank::Bank;
|
||||||
use solana_sdk::account::Account;
|
use solana_sdk::account::Account;
|
||||||
|
@ -13,15 +12,6 @@ pub const VOTE_THRESHOLD_DEPTH: usize = 8;
|
||||||
pub const VOTE_THRESHOLD_SIZE: f64 = 2f64 / 3f64;
|
pub const VOTE_THRESHOLD_SIZE: f64 = 2f64 / 3f64;
|
||||||
pub const MAX_RECENT_VOTES: usize = 16;
|
pub const MAX_RECENT_VOTES: usize = 16;
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct EpochStakes {
|
|
||||||
epoch: u64,
|
|
||||||
stakes: HashMap<Pubkey, u64>,
|
|
||||||
self_staked: u64,
|
|
||||||
total_staked: u64,
|
|
||||||
delegate_pubkey: Pubkey,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct StakeLockout {
|
pub struct StakeLockout {
|
||||||
lockout: u64,
|
lockout: u64,
|
||||||
|
@ -39,89 +29,49 @@ impl StakeLockout {
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Tower {
|
pub struct Tower {
|
||||||
epoch_stakes: EpochStakes,
|
node_pubkey: Pubkey,
|
||||||
threshold_depth: usize,
|
threshold_depth: usize,
|
||||||
threshold_size: f64,
|
threshold_size: f64,
|
||||||
lockouts: VoteState,
|
lockouts: VoteState,
|
||||||
recent_votes: VecDeque<Vote>,
|
recent_votes: VecDeque<Vote>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EpochStakes {
|
|
||||||
pub fn new(epoch: u64, stakes: HashMap<Pubkey, u64>, delegate_pubkey: &Pubkey) -> Self {
|
|
||||||
let total_staked = stakes.values().sum();
|
|
||||||
let self_staked = *stakes.get(&delegate_pubkey).unwrap_or(&0);
|
|
||||||
Self {
|
|
||||||
epoch,
|
|
||||||
stakes,
|
|
||||||
total_staked,
|
|
||||||
self_staked,
|
|
||||||
delegate_pubkey: *delegate_pubkey,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn new_for_tests(lamports: u64) -> Self {
|
|
||||||
Self::new(
|
|
||||||
0,
|
|
||||||
vec![(Pubkey::default(), lamports)].into_iter().collect(),
|
|
||||||
&Pubkey::default(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
pub fn new_from_stakes(epoch: u64, accounts: &[(Pubkey, (u64, Account))]) -> Self {
|
|
||||||
let stakes = accounts.iter().map(|(k, (v, _))| (*k, *v)).collect();
|
|
||||||
Self::new(epoch, stakes, &accounts[0].0)
|
|
||||||
}
|
|
||||||
pub fn new_from_bank(bank: &Bank, my_pubkey: &Pubkey) -> Self {
|
|
||||||
let bank_epoch = bank.get_epoch_and_slot_index(bank.slot()).0;
|
|
||||||
let stakes = staking_utils::vote_account_stakes_at_epoch(bank, bank_epoch)
|
|
||||||
.expect("voting require a bank with stakes");
|
|
||||||
Self::new(bank_epoch, stakes, my_pubkey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Tower {
|
impl Tower {
|
||||||
pub fn new_from_forks(bank_forks: &BankForks, my_pubkey: &Pubkey) -> Self {
|
pub fn new(node_pubkey: &Pubkey, vote_account_pubkey: &Pubkey, bank_forks: &BankForks) -> Self {
|
||||||
let mut frozen_banks: Vec<_> = bank_forks.frozen_banks().values().cloned().collect();
|
|
||||||
frozen_banks.sort_by_key(|b| (b.parents().len(), b.slot()));
|
|
||||||
let epoch_stakes = {
|
|
||||||
if let Some(bank) = frozen_banks.last() {
|
|
||||||
EpochStakes::new_from_bank(bank, my_pubkey)
|
|
||||||
} else {
|
|
||||||
return Self::default();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut tower = Self {
|
let mut tower = Self {
|
||||||
epoch_stakes,
|
node_pubkey: *node_pubkey,
|
||||||
threshold_depth: VOTE_THRESHOLD_DEPTH,
|
threshold_depth: VOTE_THRESHOLD_DEPTH,
|
||||||
threshold_size: VOTE_THRESHOLD_SIZE,
|
threshold_size: VOTE_THRESHOLD_SIZE,
|
||||||
lockouts: VoteState::default(),
|
lockouts: VoteState::default(),
|
||||||
recent_votes: VecDeque::default(),
|
recent_votes: VecDeque::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let bank = tower.find_heaviest_bank(bank_forks).unwrap();
|
tower.initialize_lockouts_from_bank_forks(&bank_forks, vote_account_pubkey);
|
||||||
tower.lockouts = Self::initialize_lockouts_from_bank(&bank, tower.epoch_stakes.epoch);
|
|
||||||
tower
|
tower
|
||||||
}
|
}
|
||||||
pub fn new(epoch_stakes: EpochStakes, threshold_depth: usize, threshold_size: f64) -> Self {
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn new_for_tests(threshold_depth: usize, threshold_size: f64) -> Self {
|
||||||
Self {
|
Self {
|
||||||
epoch_stakes,
|
|
||||||
threshold_depth,
|
threshold_depth,
|
||||||
threshold_size,
|
threshold_size,
|
||||||
lockouts: VoteState::default(),
|
..Tower::default()
|
||||||
recent_votes: VecDeque::default(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn collect_vote_lockouts<F>(
|
pub fn collect_vote_lockouts<F>(
|
||||||
&self,
|
&self,
|
||||||
bank_slot: u64,
|
bank_slot: u64,
|
||||||
vote_accounts: F,
|
vote_accounts: F,
|
||||||
ancestors: &HashMap<u64, HashSet<u64>>,
|
ancestors: &HashMap<u64, HashSet<u64>>,
|
||||||
) -> HashMap<u64, StakeLockout>
|
) -> (HashMap<u64, StakeLockout>, u64)
|
||||||
where
|
where
|
||||||
F: Iterator<Item = (Pubkey, (u64, Account))>,
|
F: Iterator<Item = (Pubkey, (u64, Account))>,
|
||||||
{
|
{
|
||||||
let mut stake_lockouts = HashMap::new();
|
let mut stake_lockouts = HashMap::new();
|
||||||
for (key, (_, account)) in vote_accounts {
|
let mut total_stake = 0;
|
||||||
let lamports: u64 = *self.epoch_stakes.stakes.get(&key).unwrap_or(&0);
|
for (key, (lamports, account)) in vote_accounts {
|
||||||
if lamports == 0 {
|
if lamports == 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -139,9 +89,7 @@ impl Tower {
|
||||||
}
|
}
|
||||||
let mut vote_state = vote_state.unwrap();
|
let mut vote_state = vote_state.unwrap();
|
||||||
|
|
||||||
if key == self.epoch_stakes.delegate_pubkey
|
if key == self.node_pubkey || vote_state.node_pubkey == self.node_pubkey {
|
||||||
|| vote_state.node_pubkey == self.epoch_stakes.delegate_pubkey
|
|
||||||
{
|
|
||||||
debug!("vote state {:?}", vote_state);
|
debug!("vote state {:?}", vote_state);
|
||||||
debug!(
|
debug!(
|
||||||
"observed slot {}",
|
"observed slot {}",
|
||||||
|
@ -200,50 +148,23 @@ impl Tower {
|
||||||
// Update all the parents of this last vote with the stake of this vote account
|
// Update all the parents of this last vote with the stake of this vote account
|
||||||
Self::update_ancestor_stakes(&mut stake_lockouts, vote.slot, lamports, ancestors);
|
Self::update_ancestor_stakes(&mut stake_lockouts, vote.slot, lamports, ancestors);
|
||||||
}
|
}
|
||||||
|
total_stake += lamports;
|
||||||
}
|
}
|
||||||
stake_lockouts
|
(stake_lockouts, total_stake)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_slot_confirmed(&self, slot: u64, lockouts: &HashMap<u64, StakeLockout>) -> bool {
|
pub fn is_slot_confirmed(
|
||||||
|
&self,
|
||||||
|
slot: u64,
|
||||||
|
lockouts: &HashMap<u64, StakeLockout>,
|
||||||
|
total_staked: u64,
|
||||||
|
) -> bool {
|
||||||
lockouts
|
lockouts
|
||||||
.get(&slot)
|
.get(&slot)
|
||||||
.map(|lockout| {
|
.map(|lockout| (lockout.stake as f64 / total_staked as f64) > self.threshold_size)
|
||||||
(lockout.stake as f64 / self.epoch_stakes.total_staked as f64) > self.threshold_size
|
|
||||||
})
|
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_recent_epoch(&self, bank: &Bank) -> bool {
|
|
||||||
bank.epoch() >= self.epoch_stakes.epoch
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update_epoch(&mut self, bank: &Bank) {
|
|
||||||
trace!(
|
|
||||||
"updating bank epoch slot: {} epoch: {}",
|
|
||||||
bank.slot(),
|
|
||||||
self.epoch_stakes.epoch
|
|
||||||
);
|
|
||||||
if bank.epoch() != self.epoch_stakes.epoch {
|
|
||||||
assert!(
|
|
||||||
self.is_recent_epoch(bank),
|
|
||||||
"epoch_stakes cannot move backwards"
|
|
||||||
);
|
|
||||||
info!(
|
|
||||||
"Tower updated epoch bank slot: {} epoch: {}",
|
|
||||||
bank.slot(),
|
|
||||||
self.epoch_stakes.epoch
|
|
||||||
);
|
|
||||||
self.epoch_stakes =
|
|
||||||
EpochStakes::new_from_bank(bank, &self.epoch_stakes.delegate_pubkey);
|
|
||||||
datapoint_info!(
|
|
||||||
"tower-epoch",
|
|
||||||
("epoch", self.epoch_stakes.epoch, i64),
|
|
||||||
("self_staked", self.epoch_stakes.self_staked, i64),
|
|
||||||
("total_staked", self.epoch_stakes.total_staked, i64)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_vote(&mut self, slot: u64, hash: Hash) -> Option<u64> {
|
pub fn record_vote(&mut self, slot: u64, hash: Hash) -> Option<u64> {
|
||||||
let root_slot = self.lockouts.root_slot;
|
let root_slot = self.lockouts.root_slot;
|
||||||
let vote = Vote { slot, hash };
|
let vote = Vote { slot, hash };
|
||||||
|
@ -281,10 +202,6 @@ impl Tower {
|
||||||
self.lockouts.root_slot
|
self.lockouts.root_slot
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn total_epoch_stakes(&self) -> u64 {
|
|
||||||
self.epoch_stakes.total_staked
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn calculate_weight(&self, stake_lockouts: &HashMap<u64, StakeLockout>) -> u128 {
|
pub fn calculate_weight(&self, stake_lockouts: &HashMap<u64, StakeLockout>) -> u128 {
|
||||||
let mut sum = 0u128;
|
let mut sum = 0u128;
|
||||||
let root_slot = self.lockouts.root_slot.unwrap_or(0);
|
let root_slot = self.lockouts.root_slot.unwrap_or(0);
|
||||||
|
@ -328,14 +245,14 @@ impl Tower {
|
||||||
&self,
|
&self,
|
||||||
slot: u64,
|
slot: u64,
|
||||||
stake_lockouts: &HashMap<u64, StakeLockout>,
|
stake_lockouts: &HashMap<u64, StakeLockout>,
|
||||||
|
total_staked: u64,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let mut lockouts = self.lockouts.clone();
|
let mut lockouts = self.lockouts.clone();
|
||||||
lockouts.process_slot_vote_unchecked(slot);
|
lockouts.process_slot_vote_unchecked(slot);
|
||||||
let vote = lockouts.nth_recent_vote(self.threshold_depth);
|
let vote = lockouts.nth_recent_vote(self.threshold_depth);
|
||||||
if let Some(vote) = vote {
|
if let Some(vote) = vote {
|
||||||
if let Some(fork_stake) = stake_lockouts.get(&vote.slot) {
|
if let Some(fork_stake) = stake_lockouts.get(&vote.slot) {
|
||||||
(fork_stake.stake as f64 / self.epoch_stakes.total_staked as f64)
|
(fork_stake.stake as f64 / total_staked as f64) > self.threshold_size
|
||||||
> self.threshold_size
|
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
@ -396,7 +313,7 @@ impl Tower {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bank_weight(&self, bank: &Bank, ancestors: &HashMap<u64, HashSet<u64>>) -> u128 {
|
fn bank_weight(&self, bank: &Bank, ancestors: &HashMap<u64, HashSet<u64>>) -> u128 {
|
||||||
let stake_lockouts =
|
let (stake_lockouts, _) =
|
||||||
self.collect_vote_lockouts(bank.slot(), bank.vote_accounts().into_iter(), ancestors);
|
self.collect_vote_lockouts(bank.slot(), bank.vote_accounts().into_iter(), ancestors);
|
||||||
self.calculate_weight(&stake_lockouts)
|
self.calculate_weight(&stake_lockouts)
|
||||||
}
|
}
|
||||||
|
@ -418,19 +335,22 @@ impl Tower {
|
||||||
bank_weights.pop().map(|b| b.2)
|
bank_weights.pop().map(|b| b.2)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initialize_lockouts_from_bank(bank: &Bank, current_epoch: u64) -> VoteState {
|
fn initialize_lockouts_from_bank_forks(
|
||||||
let mut lockouts = VoteState::default();
|
&mut self,
|
||||||
if let Some(iter) = bank.epoch_vote_accounts(current_epoch) {
|
bank_forks: &BankForks,
|
||||||
for (delegate_pubkey, (_, account)) in iter {
|
vote_account_pubkey: &Pubkey,
|
||||||
if delegate_pubkey == bank.collector_id() {
|
) {
|
||||||
let state = VoteState::deserialize(&account.data).expect("votes");
|
if let Some(bank) = self.find_heaviest_bank(bank_forks) {
|
||||||
if lockouts.votes.len() < state.votes.len() {
|
if let Some((_stake, vote_account)) = bank.vote_accounts().get(vote_account_pubkey) {
|
||||||
lockouts = state;
|
let vote_state = VoteState::deserialize(&vote_account.data)
|
||||||
}
|
.expect("vote_account isn't a VoteState?");
|
||||||
}
|
assert_eq!(
|
||||||
|
vote_state.node_pubkey, self.node_pubkey,
|
||||||
|
"vote account's node_pubkey doesn't match",
|
||||||
|
);
|
||||||
|
self.lockouts = vote_state;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
lockouts
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -456,32 +376,19 @@ mod test {
|
||||||
stakes
|
stakes
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_collect_vote_lockouts_no_epoch_stakes() {
|
|
||||||
let accounts = gen_stakes(&[(1, &[0])]);
|
|
||||||
let epoch_stakes = EpochStakes::new_for_tests(2);
|
|
||||||
let tower = Tower::new(epoch_stakes, 0, 0.67);
|
|
||||||
let ancestors = vec![(1, vec![0].into_iter().collect()), (0, HashSet::new())]
|
|
||||||
.into_iter()
|
|
||||||
.collect();
|
|
||||||
let staked_lockouts = tower.collect_vote_lockouts(1, accounts.into_iter(), &ancestors);
|
|
||||||
assert!(staked_lockouts.is_empty());
|
|
||||||
assert_eq!(tower.epoch_stakes.total_staked, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_collect_vote_lockouts_sums() {
|
fn test_collect_vote_lockouts_sums() {
|
||||||
//two accounts voting for slot 0 with 1 token staked
|
//two accounts voting for slot 0 with 1 token staked
|
||||||
let accounts = gen_stakes(&[(1, &[0]), (1, &[0])]);
|
let accounts = gen_stakes(&[(1, &[0]), (1, &[0])]);
|
||||||
let epoch_stakes = EpochStakes::new_from_stakes(0, &accounts);
|
let tower = Tower::new_for_tests(0, 0.67);
|
||||||
let tower = Tower::new(epoch_stakes, 0, 0.67);
|
|
||||||
let ancestors = vec![(1, vec![0].into_iter().collect()), (0, HashSet::new())]
|
let ancestors = vec![(1, vec![0].into_iter().collect()), (0, HashSet::new())]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect();
|
.collect();
|
||||||
let staked_lockouts = tower.collect_vote_lockouts(1, accounts.into_iter(), &ancestors);
|
let (staked_lockouts, total_staked) =
|
||||||
|
tower.collect_vote_lockouts(1, accounts.into_iter(), &ancestors);
|
||||||
assert_eq!(staked_lockouts[&0].stake, 2);
|
assert_eq!(staked_lockouts[&0].stake, 2);
|
||||||
assert_eq!(staked_lockouts[&0].lockout, 2 + 2 + 4 + 4);
|
assert_eq!(staked_lockouts[&0].lockout, 2 + 2 + 4 + 4);
|
||||||
assert_eq!(tower.epoch_stakes.total_staked, 2);
|
assert_eq!(total_staked, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -489,15 +396,14 @@ mod test {
|
||||||
let votes: Vec<u64> = (0..MAX_LOCKOUT_HISTORY as u64).into_iter().collect();
|
let votes: Vec<u64> = (0..MAX_LOCKOUT_HISTORY as u64).into_iter().collect();
|
||||||
//two accounts voting for slot 0 with 1 token staked
|
//two accounts voting for slot 0 with 1 token staked
|
||||||
let accounts = gen_stakes(&[(1, &votes), (1, &votes)]);
|
let accounts = gen_stakes(&[(1, &votes), (1, &votes)]);
|
||||||
let epoch_stakes = EpochStakes::new_from_stakes(0, &accounts);
|
let mut tower = Tower::new_for_tests(0, 0.67);
|
||||||
let mut tower = Tower::new(epoch_stakes, 0, 0.67);
|
|
||||||
let mut ancestors = HashMap::new();
|
let mut ancestors = HashMap::new();
|
||||||
for i in 0..(MAX_LOCKOUT_HISTORY + 1) {
|
for i in 0..(MAX_LOCKOUT_HISTORY + 1) {
|
||||||
tower.record_vote(i as u64, Hash::default());
|
tower.record_vote(i as u64, Hash::default());
|
||||||
ancestors.insert(i as u64, (0..i as u64).into_iter().collect());
|
ancestors.insert(i as u64, (0..i as u64).into_iter().collect());
|
||||||
}
|
}
|
||||||
assert_eq!(tower.lockouts.root_slot, Some(0));
|
assert_eq!(tower.lockouts.root_slot, Some(0));
|
||||||
let staked_lockouts = tower.collect_vote_lockouts(
|
let (staked_lockouts, _total_staked) = tower.collect_vote_lockouts(
|
||||||
MAX_LOCKOUT_HISTORY as u64,
|
MAX_LOCKOUT_HISTORY as u64,
|
||||||
accounts.into_iter(),
|
accounts.into_iter(),
|
||||||
&ancestors,
|
&ancestors,
|
||||||
|
@ -511,7 +417,7 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_calculate_weight_skips_root() {
|
fn test_calculate_weight_skips_root() {
|
||||||
let mut tower = Tower::new(EpochStakes::new_for_tests(2), 0, 0.67);
|
let mut tower = Tower::new_for_tests(0, 0.67);
|
||||||
tower.lockouts.root_slot = Some(1);
|
tower.lockouts.root_slot = Some(1);
|
||||||
let stakes = vec![
|
let stakes = vec![
|
||||||
(
|
(
|
||||||
|
@ -536,7 +442,7 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_calculate_weight() {
|
fn test_calculate_weight() {
|
||||||
let tower = Tower::new(EpochStakes::new_for_tests(2), 0, 0.67);
|
let tower = Tower::new_for_tests(0, 0.67);
|
||||||
let stakes = vec![(
|
let stakes = vec![(
|
||||||
0,
|
0,
|
||||||
StakeLockout {
|
StakeLockout {
|
||||||
|
@ -551,7 +457,7 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_check_vote_threshold_without_votes() {
|
fn test_check_vote_threshold_without_votes() {
|
||||||
let tower = Tower::new(EpochStakes::new_for_tests(2), 1, 0.67);
|
let tower = Tower::new_for_tests(1, 0.67);
|
||||||
let stakes = vec![(
|
let stakes = vec![(
|
||||||
0,
|
0,
|
||||||
StakeLockout {
|
StakeLockout {
|
||||||
|
@ -561,12 +467,12 @@ mod test {
|
||||||
)]
|
)]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect();
|
.collect();
|
||||||
assert!(tower.check_vote_stake_threshold(0, &stakes));
|
assert!(tower.check_vote_stake_threshold(0, &stakes, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_aggregate_stake_lockouts() {
|
fn test_aggregate_stake_lockouts() {
|
||||||
let mut tower = Tower::new(EpochStakes::new_for_tests(2), 0, 0.67);
|
let mut tower = Tower::new_for_tests(0, 0.67);
|
||||||
tower.lockouts.root_slot = Some(1);
|
tower.lockouts.root_slot = Some(1);
|
||||||
let stakes = vec![
|
let stakes = vec![
|
||||||
(
|
(
|
||||||
|
@ -619,7 +525,7 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_is_slot_confirmed_not_enough_stake_failure() {
|
fn test_is_slot_confirmed_not_enough_stake_failure() {
|
||||||
let tower = Tower::new(EpochStakes::new_for_tests(2), 1, 0.67);
|
let tower = Tower::new_for_tests(1, 0.67);
|
||||||
let stakes = vec![(
|
let stakes = vec![(
|
||||||
0,
|
0,
|
||||||
StakeLockout {
|
StakeLockout {
|
||||||
|
@ -629,19 +535,19 @@ mod test {
|
||||||
)]
|
)]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect();
|
.collect();
|
||||||
assert!(!tower.is_slot_confirmed(0, &stakes));
|
assert!(!tower.is_slot_confirmed(0, &stakes, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_is_slot_confirmed_unknown_slot() {
|
fn test_is_slot_confirmed_unknown_slot() {
|
||||||
let tower = Tower::new(EpochStakes::new_for_tests(2), 1, 0.67);
|
let tower = Tower::new_for_tests(1, 0.67);
|
||||||
let stakes = HashMap::new();
|
let stakes = HashMap::new();
|
||||||
assert!(!tower.is_slot_confirmed(0, &stakes));
|
assert!(!tower.is_slot_confirmed(0, &stakes, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_is_slot_confirmed_pass() {
|
fn test_is_slot_confirmed_pass() {
|
||||||
let tower = Tower::new(EpochStakes::new_for_tests(2), 1, 0.67);
|
let tower = Tower::new_for_tests(1, 0.67);
|
||||||
let stakes = vec![(
|
let stakes = vec![(
|
||||||
0,
|
0,
|
||||||
StakeLockout {
|
StakeLockout {
|
||||||
|
@ -651,19 +557,19 @@ mod test {
|
||||||
)]
|
)]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect();
|
.collect();
|
||||||
assert!(tower.is_slot_confirmed(0, &stakes));
|
assert!(tower.is_slot_confirmed(0, &stakes, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_is_locked_out_empty() {
|
fn test_is_locked_out_empty() {
|
||||||
let tower = Tower::new(EpochStakes::new_for_tests(2), 0, 0.67);
|
let tower = Tower::new_for_tests(0, 0.67);
|
||||||
let descendants = HashMap::new();
|
let descendants = HashMap::new();
|
||||||
assert!(!tower.is_locked_out(0, &descendants));
|
assert!(!tower.is_locked_out(0, &descendants));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_is_locked_out_root_slot_child_pass() {
|
fn test_is_locked_out_root_slot_child_pass() {
|
||||||
let mut tower = Tower::new(EpochStakes::new_for_tests(2), 0, 0.67);
|
let mut tower = Tower::new_for_tests(0, 0.67);
|
||||||
let descendants = vec![(0, vec![1].into_iter().collect())]
|
let descendants = vec![(0, vec![1].into_iter().collect())]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -673,7 +579,7 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_is_locked_out_root_slot_sibling_fail() {
|
fn test_is_locked_out_root_slot_sibling_fail() {
|
||||||
let mut tower = Tower::new(EpochStakes::new_for_tests(2), 0, 0.67);
|
let mut tower = Tower::new_for_tests(0, 0.67);
|
||||||
let descendants = vec![(0, vec![1].into_iter().collect())]
|
let descendants = vec![(0, vec![1].into_iter().collect())]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -683,7 +589,7 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_check_already_voted() {
|
fn test_check_already_voted() {
|
||||||
let mut tower = Tower::new(EpochStakes::new_for_tests(2), 0, 0.67);
|
let mut tower = Tower::new_for_tests(0, 0.67);
|
||||||
tower.record_vote(0, Hash::default());
|
tower.record_vote(0, Hash::default());
|
||||||
assert!(tower.has_voted(0));
|
assert!(tower.has_voted(0));
|
||||||
assert!(!tower.has_voted(1));
|
assert!(!tower.has_voted(1));
|
||||||
|
@ -691,7 +597,7 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_is_locked_out_double_vote() {
|
fn test_is_locked_out_double_vote() {
|
||||||
let mut tower = Tower::new(EpochStakes::new_for_tests(2), 0, 0.67);
|
let mut tower = Tower::new_for_tests(0, 0.67);
|
||||||
let descendants = vec![(0, vec![1].into_iter().collect()), (1, HashSet::new())]
|
let descendants = vec![(0, vec![1].into_iter().collect()), (1, HashSet::new())]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -702,7 +608,7 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_is_locked_out_child() {
|
fn test_is_locked_out_child() {
|
||||||
let mut tower = Tower::new(EpochStakes::new_for_tests(2), 0, 0.67);
|
let mut tower = Tower::new_for_tests(0, 0.67);
|
||||||
let descendants = vec![(0, vec![1].into_iter().collect())]
|
let descendants = vec![(0, vec![1].into_iter().collect())]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -712,7 +618,7 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_is_locked_out_sibling() {
|
fn test_is_locked_out_sibling() {
|
||||||
let mut tower = Tower::new(EpochStakes::new_for_tests(2), 0, 0.67);
|
let mut tower = Tower::new_for_tests(0, 0.67);
|
||||||
let descendants = vec![
|
let descendants = vec![
|
||||||
(0, vec![1, 2].into_iter().collect()),
|
(0, vec![1, 2].into_iter().collect()),
|
||||||
(1, HashSet::new()),
|
(1, HashSet::new()),
|
||||||
|
@ -727,7 +633,7 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_is_locked_out_last_vote_expired() {
|
fn test_is_locked_out_last_vote_expired() {
|
||||||
let mut tower = Tower::new(EpochStakes::new_for_tests(2), 0, 0.67);
|
let mut tower = Tower::new_for_tests(0, 0.67);
|
||||||
let descendants = vec![(0, vec![1, 4].into_iter().collect()), (1, HashSet::new())]
|
let descendants = vec![(0, vec![1, 4].into_iter().collect()), (1, HashSet::new())]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -743,7 +649,7 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_check_vote_threshold_below_threshold() {
|
fn test_check_vote_threshold_below_threshold() {
|
||||||
let mut tower = Tower::new(EpochStakes::new_for_tests(2), 1, 0.67);
|
let mut tower = Tower::new_for_tests(1, 0.67);
|
||||||
let stakes = vec![(
|
let stakes = vec![(
|
||||||
0,
|
0,
|
||||||
StakeLockout {
|
StakeLockout {
|
||||||
|
@ -754,11 +660,11 @@ mod test {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect();
|
.collect();
|
||||||
tower.record_vote(0, Hash::default());
|
tower.record_vote(0, Hash::default());
|
||||||
assert!(!tower.check_vote_stake_threshold(1, &stakes));
|
assert!(!tower.check_vote_stake_threshold(1, &stakes, 2));
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_check_vote_threshold_above_threshold() {
|
fn test_check_vote_threshold_above_threshold() {
|
||||||
let mut tower = Tower::new(EpochStakes::new_for_tests(2), 1, 0.67);
|
let mut tower = Tower::new_for_tests(1, 0.67);
|
||||||
let stakes = vec![(
|
let stakes = vec![(
|
||||||
0,
|
0,
|
||||||
StakeLockout {
|
StakeLockout {
|
||||||
|
@ -769,12 +675,12 @@ mod test {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect();
|
.collect();
|
||||||
tower.record_vote(0, Hash::default());
|
tower.record_vote(0, Hash::default());
|
||||||
assert!(tower.check_vote_stake_threshold(1, &stakes));
|
assert!(tower.check_vote_stake_threshold(1, &stakes, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_check_vote_threshold_above_threshold_after_pop() {
|
fn test_check_vote_threshold_above_threshold_after_pop() {
|
||||||
let mut tower = Tower::new(EpochStakes::new_for_tests(2), 1, 0.67);
|
let mut tower = Tower::new_for_tests(1, 0.67);
|
||||||
let stakes = vec![(
|
let stakes = vec![(
|
||||||
0,
|
0,
|
||||||
StakeLockout {
|
StakeLockout {
|
||||||
|
@ -787,15 +693,15 @@ mod test {
|
||||||
tower.record_vote(0, Hash::default());
|
tower.record_vote(0, Hash::default());
|
||||||
tower.record_vote(1, Hash::default());
|
tower.record_vote(1, Hash::default());
|
||||||
tower.record_vote(2, Hash::default());
|
tower.record_vote(2, Hash::default());
|
||||||
assert!(tower.check_vote_stake_threshold(6, &stakes));
|
assert!(tower.check_vote_stake_threshold(6, &stakes, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_check_vote_threshold_above_threshold_no_stake() {
|
fn test_check_vote_threshold_above_threshold_no_stake() {
|
||||||
let mut tower = Tower::new(EpochStakes::new_for_tests(2), 1, 0.67);
|
let mut tower = Tower::new_for_tests(1, 0.67);
|
||||||
let stakes = HashMap::new();
|
let stakes = HashMap::new();
|
||||||
tower.record_vote(0, Hash::default());
|
tower.record_vote(0, Hash::default());
|
||||||
assert!(!tower.check_vote_stake_threshold(1, &stakes));
|
assert!(!tower.check_vote_stake_threshold(1, &stakes, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -875,9 +781,7 @@ mod test {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Initialize tower
|
// Initialize tower
|
||||||
let stakes: HashMap<_, _> = accounts.iter().map(|(pk, (s, _))| (*pk, *s)).collect();
|
let mut tower = Tower::new_for_tests(VOTE_THRESHOLD_DEPTH, threshold_size);
|
||||||
let epoch_stakes = EpochStakes::new(0, stakes, &Pubkey::default());
|
|
||||||
let mut tower = Tower::new(epoch_stakes, VOTE_THRESHOLD_DEPTH, threshold_size);
|
|
||||||
|
|
||||||
// CASE 1: Record the first VOTE_THRESHOLD tower votes for fork 2. We want to
|
// CASE 1: Record the first VOTE_THRESHOLD tower votes for fork 2. We want to
|
||||||
// evaluate a vote on slot VOTE_THRESHOLD_DEPTH. The nth most recent vote should be
|
// evaluate a vote on slot VOTE_THRESHOLD_DEPTH. The nth most recent vote should be
|
||||||
|
@ -887,21 +791,25 @@ mod test {
|
||||||
for vote in &tower_votes {
|
for vote in &tower_votes {
|
||||||
tower.record_vote(*vote, Hash::default());
|
tower.record_vote(*vote, Hash::default());
|
||||||
}
|
}
|
||||||
let staked_lockouts =
|
let (staked_lockouts, total_staked) =
|
||||||
tower.collect_vote_lockouts(vote_to_evaluate, accounts.clone().into_iter(), &ancestors);
|
tower.collect_vote_lockouts(vote_to_evaluate, accounts.clone().into_iter(), &ancestors);
|
||||||
assert!(tower.check_vote_stake_threshold(vote_to_evaluate, &staked_lockouts));
|
assert!(tower.check_vote_stake_threshold(vote_to_evaluate, &staked_lockouts, total_staked));
|
||||||
|
|
||||||
// CASE 2: Now we want to evaluate a vote for slot VOTE_THRESHOLD_DEPTH + 1. This slot
|
// CASE 2: Now we want to evaluate a vote for slot VOTE_THRESHOLD_DEPTH + 1. This slot
|
||||||
// will expire the vote in one of the vote accounts, so we should have insufficient
|
// will expire the vote in one of the vote accounts, so we should have insufficient
|
||||||
// stake to pass the threshold
|
// stake to pass the threshold
|
||||||
let vote_to_evaluate = VOTE_THRESHOLD_DEPTH as u64 + 1;
|
let vote_to_evaluate = VOTE_THRESHOLD_DEPTH as u64 + 1;
|
||||||
let staked_lockouts =
|
let (staked_lockouts, total_staked) =
|
||||||
tower.collect_vote_lockouts(vote_to_evaluate, accounts.into_iter(), &ancestors);
|
tower.collect_vote_lockouts(vote_to_evaluate, accounts.into_iter(), &ancestors);
|
||||||
assert!(!tower.check_vote_stake_threshold(vote_to_evaluate, &staked_lockouts));
|
assert!(!tower.check_vote_stake_threshold(
|
||||||
|
vote_to_evaluate,
|
||||||
|
&staked_lockouts,
|
||||||
|
total_staked
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn vote_and_check_recent(num_votes: usize) {
|
fn vote_and_check_recent(num_votes: usize) {
|
||||||
let mut tower = Tower::new(EpochStakes::new_for_tests(2), 1, 0.67);
|
let mut tower = Tower::new_for_tests(1, 0.67);
|
||||||
let start = num_votes.saturating_sub(MAX_RECENT_VOTES);
|
let start = num_votes.saturating_sub(MAX_RECENT_VOTES);
|
||||||
let expected: Vec<_> = (start..num_votes)
|
let expected: Vec<_> = (start..num_votes)
|
||||||
.map(|i| Vote::new(i as u64, Hash::default()))
|
.map(|i| Vote::new(i as u64, Hash::default()))
|
||||||
|
|
|
@ -104,7 +104,7 @@ impl ReplayStage {
|
||||||
let bank_forks = bank_forks.clone();
|
let bank_forks = bank_forks.clone();
|
||||||
let poh_recorder = poh_recorder.clone();
|
let poh_recorder = poh_recorder.clone();
|
||||||
let my_pubkey = *my_pubkey;
|
let my_pubkey = *my_pubkey;
|
||||||
let mut tower = Tower::new_from_forks(&bank_forks.read().unwrap(), &my_pubkey);
|
let mut tower = Tower::new(&my_pubkey, &vote_account, &bank_forks.read().unwrap());
|
||||||
// Start the replay stage loop
|
// Start the replay stage loop
|
||||||
let leader_schedule_cache = leader_schedule_cache.clone();
|
let leader_schedule_cache = leader_schedule_cache.clone();
|
||||||
let vote_account = *vote_account;
|
let vote_account = *vote_account;
|
||||||
|
@ -144,7 +144,7 @@ impl ReplayStage {
|
||||||
|
|
||||||
let votable = Self::generate_votable_banks(&bank_forks, &tower, &mut progress);
|
let votable = Self::generate_votable_banks(&bank_forks, &tower, &mut progress);
|
||||||
|
|
||||||
if let Some((_, bank, lockouts)) = votable.into_iter().last() {
|
if let Some((_, bank, lockouts, total_staked)) = votable.into_iter().last() {
|
||||||
subscriptions.notify_subscribers(bank.slot(), &bank_forks);
|
subscriptions.notify_subscribers(bank.slot(), &bank_forks);
|
||||||
|
|
||||||
if let Some(votable_leader) =
|
if let Some(votable_leader) =
|
||||||
|
@ -181,6 +181,7 @@ impl ReplayStage {
|
||||||
&leader_schedule_cache,
|
&leader_schedule_cache,
|
||||||
&root_bank_sender,
|
&root_bank_sender,
|
||||||
lockouts,
|
lockouts,
|
||||||
|
total_staked,
|
||||||
&lockouts_sender,
|
&lockouts_sender,
|
||||||
&snapshot_package_sender,
|
&snapshot_package_sender,
|
||||||
)?;
|
)?;
|
||||||
|
@ -399,6 +400,7 @@ impl ReplayStage {
|
||||||
leader_schedule_cache: &Arc<LeaderScheduleCache>,
|
leader_schedule_cache: &Arc<LeaderScheduleCache>,
|
||||||
root_bank_sender: &Sender<Vec<Arc<Bank>>>,
|
root_bank_sender: &Sender<Vec<Arc<Bank>>>,
|
||||||
lockouts: HashMap<u64, StakeLockout>,
|
lockouts: HashMap<u64, StakeLockout>,
|
||||||
|
total_staked: u64,
|
||||||
lockouts_sender: &Sender<LockoutAggregationData>,
|
lockouts_sender: &Sender<LockoutAggregationData>,
|
||||||
snapshot_package_sender: &Option<SnapshotPackageSender>,
|
snapshot_package_sender: &Option<SnapshotPackageSender>,
|
||||||
) -> Result<()>
|
) -> Result<()>
|
||||||
|
@ -435,8 +437,8 @@ impl ReplayStage {
|
||||||
Err(e)?;
|
Err(e)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::update_confidence_cache(bank_forks, tower, lockouts, lockouts_sender);
|
Self::update_confidence_cache(bank_forks, tower, lockouts, total_staked, lockouts_sender);
|
||||||
tower.update_epoch(&bank);
|
|
||||||
if let Some(ref voting_keypair) = voting_keypair {
|
if let Some(ref voting_keypair) = voting_keypair {
|
||||||
let node_keypair = cluster_info.read().unwrap().keypair.clone();
|
let node_keypair = cluster_info.read().unwrap().keypair.clone();
|
||||||
|
|
||||||
|
@ -462,21 +464,23 @@ impl ReplayStage {
|
||||||
bank_forks: &Arc<RwLock<BankForks>>,
|
bank_forks: &Arc<RwLock<BankForks>>,
|
||||||
tower: &Tower,
|
tower: &Tower,
|
||||||
lockouts: HashMap<u64, StakeLockout>,
|
lockouts: HashMap<u64, StakeLockout>,
|
||||||
|
total_staked: u64,
|
||||||
lockouts_sender: &Sender<LockoutAggregationData>,
|
lockouts_sender: &Sender<LockoutAggregationData>,
|
||||||
) {
|
) {
|
||||||
let total_epoch_stakes = tower.total_epoch_stakes();
|
{
|
||||||
let mut w_bank_forks = bank_forks.write().unwrap();
|
let mut bank_forks = bank_forks.write().unwrap();
|
||||||
for (fork, stake_lockout) in lockouts.iter() {
|
for (fork, stake_lockout) in lockouts.iter() {
|
||||||
if tower.root().is_none() || *fork >= tower.root().unwrap() {
|
if tower.root().is_none() || *fork >= tower.root().unwrap() {
|
||||||
w_bank_forks.cache_fork_confidence(
|
bank_forks.cache_fork_confidence(
|
||||||
*fork,
|
*fork,
|
||||||
stake_lockout.stake(),
|
stake_lockout.stake(),
|
||||||
total_epoch_stakes,
|
total_staked,
|
||||||
stake_lockout.lockout(),
|
stake_lockout.lockout(),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
drop(w_bank_forks);
|
|
||||||
let bank_forks_clone = bank_forks.clone();
|
let bank_forks_clone = bank_forks.clone();
|
||||||
let root = tower.root();
|
let root = tower.root();
|
||||||
|
|
||||||
|
@ -560,11 +564,12 @@ impl ReplayStage {
|
||||||
did_complete_bank
|
did_complete_bank
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
fn generate_votable_banks(
|
fn generate_votable_banks(
|
||||||
bank_forks: &Arc<RwLock<BankForks>>,
|
bank_forks: &Arc<RwLock<BankForks>>,
|
||||||
tower: &Tower,
|
tower: &Tower,
|
||||||
progress: &mut HashMap<u64, ForkProgress>,
|
progress: &mut HashMap<u64, ForkProgress>,
|
||||||
) -> Vec<(u128, Arc<Bank>, HashMap<u64, StakeLockout>)> {
|
) -> Vec<(u128, Arc<Bank>, HashMap<u64, StakeLockout>, u64)> {
|
||||||
let tower_start = Instant::now();
|
let tower_start = Instant::now();
|
||||||
// Tower voting
|
// Tower voting
|
||||||
let descendants = bank_forks.read().unwrap().descendants();
|
let descendants = bank_forks.read().unwrap().descendants();
|
||||||
|
@ -572,18 +577,13 @@ impl ReplayStage {
|
||||||
let frozen_banks = bank_forks.read().unwrap().frozen_banks();
|
let frozen_banks = bank_forks.read().unwrap().frozen_banks();
|
||||||
|
|
||||||
trace!("frozen_banks {}", frozen_banks.len());
|
trace!("frozen_banks {}", frozen_banks.len());
|
||||||
let mut votable: Vec<(u128, Arc<Bank>, HashMap<u64, StakeLockout>)> = frozen_banks
|
let mut votable: Vec<(u128, Arc<Bank>, HashMap<u64, StakeLockout>, u64)> = frozen_banks
|
||||||
.values()
|
.values()
|
||||||
.filter(|b| {
|
.filter(|b| {
|
||||||
let is_votable = b.is_votable();
|
let is_votable = b.is_votable();
|
||||||
trace!("bank is votable: {} {}", b.slot(), is_votable);
|
trace!("bank is votable: {} {}", b.slot(), is_votable);
|
||||||
is_votable
|
is_votable
|
||||||
})
|
})
|
||||||
.filter(|b| {
|
|
||||||
let is_recent_epoch = tower.is_recent_epoch(b);
|
|
||||||
trace!("bank is is_recent_epoch: {} {}", b.slot(), is_recent_epoch);
|
|
||||||
is_recent_epoch
|
|
||||||
})
|
|
||||||
.filter(|b| {
|
.filter(|b| {
|
||||||
let has_voted = tower.has_voted(b.slot());
|
let has_voted = tower.has_voted(b.slot());
|
||||||
trace!("bank is has_voted: {} {}", b.slot(), has_voted);
|
trace!("bank is has_voted: {} {}", b.slot(), has_voted);
|
||||||
|
@ -604,17 +604,19 @@ impl ReplayStage {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.filter(|(b, stake_lockouts)| {
|
.filter(|(b, (stake_lockouts, total_staked))| {
|
||||||
let vote_threshold = tower.check_vote_stake_threshold(b.slot(), &stake_lockouts);
|
let vote_threshold =
|
||||||
Self::confirm_forks(tower, stake_lockouts, progress, bank_forks);
|
tower.check_vote_stake_threshold(b.slot(), &stake_lockouts, *total_staked);
|
||||||
|
Self::confirm_forks(tower, &stake_lockouts, *total_staked, progress, bank_forks);
|
||||||
debug!("bank vote_threshold: {} {}", b.slot(), vote_threshold);
|
debug!("bank vote_threshold: {} {}", b.slot(), vote_threshold);
|
||||||
vote_threshold
|
vote_threshold
|
||||||
})
|
})
|
||||||
.map(|(b, stake_lockouts)| {
|
.map(|(b, (stake_lockouts, total_staked))| {
|
||||||
(
|
(
|
||||||
tower.calculate_weight(&stake_lockouts),
|
tower.calculate_weight(&stake_lockouts),
|
||||||
b.clone(),
|
b.clone(),
|
||||||
stake_lockouts,
|
stake_lockouts,
|
||||||
|
total_staked,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -641,12 +643,13 @@ impl ReplayStage {
|
||||||
fn confirm_forks(
|
fn confirm_forks(
|
||||||
tower: &Tower,
|
tower: &Tower,
|
||||||
stake_lockouts: &HashMap<u64, StakeLockout>,
|
stake_lockouts: &HashMap<u64, StakeLockout>,
|
||||||
|
total_staked: u64,
|
||||||
progress: &mut HashMap<u64, ForkProgress>,
|
progress: &mut HashMap<u64, ForkProgress>,
|
||||||
bank_forks: &Arc<RwLock<BankForks>>,
|
bank_forks: &Arc<RwLock<BankForks>>,
|
||||||
) {
|
) {
|
||||||
progress.retain(|slot, prog| {
|
progress.retain(|slot, prog| {
|
||||||
let duration = timing::timestamp() - prog.started_ms;
|
let duration = timing::timestamp() - prog.started_ms;
|
||||||
if tower.is_slot_confirmed(*slot, stake_lockouts)
|
if tower.is_slot_confirmed(*slot, stake_lockouts, total_staked)
|
||||||
&& bank_forks
|
&& bank_forks
|
||||||
.read()
|
.read()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -1062,13 +1065,19 @@ mod test {
|
||||||
vec![0],
|
vec![0],
|
||||||
)));
|
)));
|
||||||
let pubkey = Pubkey::new_rand();
|
let pubkey = Pubkey::new_rand();
|
||||||
let mut tower = Tower::new_from_forks(&bank_forks.read().unwrap(), &pubkey);
|
let mut tower = Tower::new(&pubkey, &Pubkey::new_rand(), &bank_forks.read().unwrap());
|
||||||
let mut progress = HashMap::new();
|
let mut progress = HashMap::new();
|
||||||
|
|
||||||
leader_vote(&arc_bank0, &leader_voting_pubkey);
|
leader_vote(&arc_bank0, &leader_voting_pubkey);
|
||||||
let votable = ReplayStage::generate_votable_banks(&bank_forks, &tower, &mut progress);
|
let votable = ReplayStage::generate_votable_banks(&bank_forks, &tower, &mut progress);
|
||||||
if let Some((_, _, lockouts)) = votable.into_iter().last() {
|
if let Some((_, _, lockouts, total_staked)) = votable.into_iter().last() {
|
||||||
ReplayStage::update_confidence_cache(&bank_forks, &tower, lockouts, &lockouts_sender);
|
ReplayStage::update_confidence_cache(
|
||||||
|
&bank_forks,
|
||||||
|
&tower,
|
||||||
|
lockouts,
|
||||||
|
total_staked,
|
||||||
|
&lockouts_sender,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -1089,8 +1098,14 @@ mod test {
|
||||||
let arc_bank1 = bank_forks.read().unwrap().get(1).unwrap().clone();
|
let arc_bank1 = bank_forks.read().unwrap().get(1).unwrap().clone();
|
||||||
leader_vote(&arc_bank1, &leader_voting_pubkey);
|
leader_vote(&arc_bank1, &leader_voting_pubkey);
|
||||||
let votable = ReplayStage::generate_votable_banks(&bank_forks, &tower, &mut progress);
|
let votable = ReplayStage::generate_votable_banks(&bank_forks, &tower, &mut progress);
|
||||||
if let Some((_, _, lockouts)) = votable.into_iter().last() {
|
if let Some((_, _, lockouts, total_staked)) = votable.into_iter().last() {
|
||||||
ReplayStage::update_confidence_cache(&bank_forks, &tower, lockouts, &lockouts_sender);
|
ReplayStage::update_confidence_cache(
|
||||||
|
&bank_forks,
|
||||||
|
&tower,
|
||||||
|
lockouts,
|
||||||
|
total_staked,
|
||||||
|
&lockouts_sender,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
tower.record_vote(arc_bank1.slot(), arc_bank1.hash());
|
tower.record_vote(arc_bank1.slot(), arc_bank1.hash());
|
||||||
|
@ -1105,8 +1120,14 @@ mod test {
|
||||||
let arc_bank2 = bank_forks.read().unwrap().get(2).unwrap().clone();
|
let arc_bank2 = bank_forks.read().unwrap().get(2).unwrap().clone();
|
||||||
leader_vote(&arc_bank2, &leader_voting_pubkey);
|
leader_vote(&arc_bank2, &leader_voting_pubkey);
|
||||||
let votable = ReplayStage::generate_votable_banks(&bank_forks, &tower, &mut progress);
|
let votable = ReplayStage::generate_votable_banks(&bank_forks, &tower, &mut progress);
|
||||||
if let Some((_, _, lockouts)) = votable.into_iter().last() {
|
if let Some((_, _, lockouts, total_staked)) = votable.into_iter().last() {
|
||||||
ReplayStage::update_confidence_cache(&bank_forks, &tower, lockouts, &lockouts_sender);
|
ReplayStage::update_confidence_cache(
|
||||||
|
&bank_forks,
|
||||||
|
&tower,
|
||||||
|
lockouts,
|
||||||
|
total_staked,
|
||||||
|
&lockouts_sender,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
thread::sleep(Duration::from_millis(200));
|
thread::sleep(Duration::from_millis(200));
|
||||||
|
|
||||||
|
|
|
@ -29,20 +29,6 @@ pub fn staked_nodes(bank: &Bank) -> HashMap<Pubkey, u64> {
|
||||||
to_staked_nodes(to_vote_states(bank.vote_accounts().into_iter()))
|
to_staked_nodes(to_vote_states(bank.vote_accounts().into_iter()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// At the specified epoch, collect the node account balance and vote states for nodes that
|
|
||||||
/// have non-zero balance in their corresponding staking accounts
|
|
||||||
pub fn vote_account_stakes_at_epoch(
|
|
||||||
bank: &Bank,
|
|
||||||
epoch_height: u64,
|
|
||||||
) -> Option<HashMap<Pubkey, u64>> {
|
|
||||||
bank.epoch_vote_accounts(epoch_height).map(|accounts| {
|
|
||||||
accounts
|
|
||||||
.iter()
|
|
||||||
.map(|(id, (stake, _))| (*id, *stake))
|
|
||||||
.collect()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// At the specified epoch, collect the delegate account balance and vote states for delegates
|
/// At the specified epoch, collect the delegate account balance and vote states for delegates
|
||||||
/// that have non-zero balance in any of their managed staking accounts
|
/// that have non-zero balance in any of their managed staking accounts
|
||||||
pub fn staked_nodes_at_epoch(bank: &Bank, epoch_height: u64) -> Option<HashMap<Pubkey, u64>> {
|
pub fn staked_nodes_at_epoch(bank: &Bank, epoch_height: u64) -> Option<HashMap<Pubkey, u64>> {
|
||||||
|
@ -112,10 +98,7 @@ where
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) mod tests {
|
pub(crate) mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::genesis_utils::{
|
use crate::genesis_utils::{create_genesis_block, GenesisBlockInfo, BOOTSTRAP_LEADER_LAMPORTS};
|
||||||
create_genesis_block, create_genesis_block_with_leader, GenesisBlockInfo,
|
|
||||||
BOOTSTRAP_LEADER_LAMPORTS,
|
|
||||||
};
|
|
||||||
use solana_sdk::instruction::Instruction;
|
use solana_sdk::instruction::Instruction;
|
||||||
use solana_sdk::pubkey::Pubkey;
|
use solana_sdk::pubkey::Pubkey;
|
||||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||||
|
@ -130,39 +113,6 @@ pub(crate) mod tests {
|
||||||
Bank::new_from_parent(parent, &Pubkey::default(), slot)
|
Bank::new_from_parent(parent, &Pubkey::default(), slot)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vote_account_stakes_at_epoch() {
|
|
||||||
let GenesisBlockInfo {
|
|
||||||
genesis_block,
|
|
||||||
voting_keypair,
|
|
||||||
..
|
|
||||||
} = create_genesis_block_with_leader(1, &Pubkey::new_rand(), BOOTSTRAP_LEADER_LAMPORTS);
|
|
||||||
|
|
||||||
let bank = Bank::new(&genesis_block);
|
|
||||||
|
|
||||||
// Epoch doesn't exist
|
|
||||||
let mut expected = HashMap::new();
|
|
||||||
assert_eq!(vote_account_stakes_at_epoch(&bank, 10), None);
|
|
||||||
|
|
||||||
let leader_stake = Stake {
|
|
||||||
stake: BOOTSTRAP_LEADER_LAMPORTS,
|
|
||||||
activated: std::u64::MAX, // exempt from warmup
|
|
||||||
..Stake::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
// First epoch has the bootstrap leader
|
|
||||||
expected.insert(voting_keypair.pubkey(), leader_stake.stake(0, None));
|
|
||||||
|
|
||||||
// henceforth, verify that we have snapshots of stake at epoch 0
|
|
||||||
let expected = Some(expected);
|
|
||||||
assert_eq!(vote_account_stakes_at_epoch(&bank, 0), expected);
|
|
||||||
|
|
||||||
// Second epoch carries same information
|
|
||||||
let bank = new_from_parent(&Arc::new(bank), 1);
|
|
||||||
assert_eq!(vote_account_stakes_at_epoch(&bank, 0), expected);
|
|
||||||
assert_eq!(vote_account_stakes_at_epoch(&bank, 1), expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn setup_vote_and_stake_accounts(
|
pub(crate) fn setup_vote_and_stake_accounts(
|
||||||
bank: &Bank,
|
bank: &Bank,
|
||||||
from_account: &Keypair,
|
from_account: &Keypair,
|
||||||
|
|
Loading…
Reference in New Issue