tower consensus naming (#4598)

s/locktower/tower/g
This commit is contained in:
anatoly yakovenko 2019-06-24 13:41:23 -07:00 committed by GitHub
parent 407b1d3e6f
commit 29611fb61d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 152 additions and 159 deletions

View File

@ -62,11 +62,11 @@
- [Blocktree](blocktree.md)
- [Cluster Software Installation and Updates](installer.md)
- [Deterministic Transaction Fees](transaction-fees.md)
- [Fork Selection](fork-selection.md)
- [Tower BFT](tower-bft.md)
- [Leader-to-Leader Transition](leader-leader-transition.md)
- [Leader-to-Validator Transition](leader-validator-transition.md)
- [Passive Stake Delegation and Rewards](passive-stake-delegation-and-rewards.md)
- [Persistent Account Storage](persistent-account-storage.md)
- [Reliable Vote Transmission](reliable-vote-transmission.md)
- [Repair Service](repair-service.md)
- [Testing Programs](testing-programs.md)
- [Testing Programs](testing-programs.md)

View File

@ -4,7 +4,7 @@ A validator votes on a PoH hash for two purposes. First, the vote indicates it
believes the ledger is valid up until that point in time. Second, since many
valid forks may exist at a given height, the vote also indicates exclusive
support for the fork. This document describes only the former. The latter is
described in [fork selection](fork-selection.md).
described in [Tower BFT](tower-bft.md).
## Current Design
@ -50,12 +50,11 @@ log the time since the NewBlock transaction was submitted.
### Finality and Payouts
Locktower is the proposed [fork selection](fork-selection.md) algorithm. It
proposes that payment to miners be postponed until the *stack* of validator
votes reaches a certain depth, at which point rollback is not economically
feasible. The vote program may therefore implement locktower. Vote instructions
would need to reference a global locktower account so that it can track
cross-block state.
[Tower BFT](tower-bft.md) is the proposed fork selection algorithm. It proposes
that payment to miners be postponed until the *stack* of validator votes reaches
a certain depth, at which point rollback is not economically feasible. The vote
program may therefore implement Tower BFT. Vote instructions would need to
reference a global Tower account so that it can track cross-block state.
## Challenges

View File

@ -55,7 +55,7 @@ Validators can ignore forks at other points (e.g. from the wrong leader), or
slash the leader responsible for the fork.
Validators vote based on a greedy choice to maximize their reward described in
[forks selection](fork-selection.md).
[Tower BFT](tower-bft.md).
### Validator's View

View File

@ -96,7 +96,7 @@ ends up scheduled for the first two epochs because the leader schedule is also
generated at slot 0 for the next epoch. The length of the first two epochs can
be specified in the genesis block as well. The minimum length of the first
epochs must be greater than or equal to the maximum rollback depth as defined in
[fork selection](fork-selection.md).
[Tower BFT](tower-bft.md).
## Leader Schedule Generation Algorithm

View File

@ -74,7 +74,7 @@ The program should have a list of slots which are valid storage mining slots.
This list should be maintained by keeping track of slots which are rooted slots in which a significant
portion of the network has voted on with a high lockout value, maybe 32-votes old. Every SLOTS\_PER\_SEGMENT
number of slots would be added to this set. The program should check that the slot is in this set. The set can
be maintained by receiving a AdvertiseStorageRecentBlockHash and checking with its bank/locktower state.
be maintained by receiving a AdvertiseStorageRecentBlockHash and checking with its bank/Tower BFT state.
The program should do a signature verify check on the signature, public key from the transaction submitter and the message of
the previous storage epoch PoH value.

View File

@ -60,7 +60,7 @@ The read is satisfied by pointing to a memory-mapped location in the
## Root Forks
The [fork selection algorithm](fork-selection.md) eventually selects a fork as a
[Tower BFT](tower-bft.md) eventually selects a fork as a
root fork and the fork is squashed. A squashed/root fork cannot be rolled back.
When a fork is squashed, all accounts in its parents not already present in the

View File

@ -77,7 +77,7 @@ count as stakes.
### VoteInstruction::Vote(Vec<Vote>)
* `account[0]` - RW - The VoteState
`VoteState::lockouts` and `VoteState::credits` are updated according to voting lockout rules see [Fork Selection](fork-selection.md)
`VoteState::lockouts` and `VoteState::credits` are updated according to voting lockout rules see [Tower BFT](tower-bft.md)
* `account[1]` - RO - A list of some N most recent slots and their hashes for the vote to be verified against.

View File

@ -1,7 +1,7 @@
# Fork Selection
# Tower BFT
This design describes a *Fork Selection* algorithm. It addresses the following
problems:
This design describes Solana's *Tower BFT* algorithm. It addresses the
following problems:
* Some forks may not end up accepted by the super-majority of the cluster, and
voters need to recover from voting on such forks.

View File

@ -85,7 +85,7 @@ impl Service for ClusterInfoVoteListener {
#[cfg(test)]
mod tests {
use crate::locktower::MAX_RECENT_VOTES;
use crate::consensus::MAX_RECENT_VOTES;
use crate::packet;
use solana_sdk::hash::Hash;
use solana_sdk::signature::{Keypair, KeypairUtil};

View File

@ -4,10 +4,10 @@ use crate::blocktree::Blocktree;
/// All tests must start from an entry point and a funding keypair and
/// discover the rest of the network.
use crate::cluster_info::FULLNODE_PORT_RANGE;
use crate::consensus::VOTE_THRESHOLD_DEPTH;
use crate::contact_info::ContactInfo;
use crate::entry::{Entry, EntrySlice};
use crate::gossip_service::discover_cluster;
use crate::locktower::VOTE_THRESHOLD_DEPTH;
use hashbrown::HashSet;
use solana_client::thin_client::create_client;
use solana_runtime::epoch_schedule::MINIMUM_SLOTS_PER_EPOCH;

View File

@ -29,7 +29,7 @@ pub struct StakeLockout {
}
#[derive(Default)]
pub struct Locktower {
pub struct Tower {
epoch_stakes: EpochStakes,
threshold_depth: usize,
threshold_size: f64,
@ -68,7 +68,7 @@ impl EpochStakes {
}
}
impl Locktower {
impl Tower {
pub fn new_from_forks(bank_forks: &BankForks, my_pubkey: &Pubkey) -> Self {
let mut frozen_banks: Vec<_> = bank_forks.frozen_banks().values().cloned().collect();
frozen_banks.sort_by_key(|b| (b.parents().len(), b.slot()));
@ -80,7 +80,7 @@ impl Locktower {
}
};
let mut locktower = Self {
let mut tower = Self {
epoch_stakes,
threshold_depth: VOTE_THRESHOLD_DEPTH,
threshold_size: VOTE_THRESHOLD_SIZE,
@ -88,10 +88,9 @@ impl Locktower {
recent_votes: VecDeque::default(),
};
let bank = locktower.find_heaviest_bank(bank_forks).unwrap();
locktower.lockouts =
Self::initialize_lockouts_from_bank(&bank, locktower.epoch_stakes.epoch);
locktower
let bank = tower.find_heaviest_bank(bank_forks).unwrap();
tower.lockouts = Self::initialize_lockouts_from_bank(&bank, tower.epoch_stakes.epoch);
tower
}
pub fn new(epoch_stakes: EpochStakes, threshold_depth: usize, threshold_size: f64) -> Self {
Self {
@ -120,7 +119,7 @@ impl Locktower {
let vote_state = VoteState::from(&account);
if vote_state.is_none() {
datapoint_warn!(
"locktower_warn",
"tower_warn",
(
"warn",
format!("Unable to get vote_state from account {}", key),
@ -141,7 +140,7 @@ impl Locktower {
);
debug!("observed root {}", vote_state.root_slot.unwrap_or(0) as i64);
datapoint_info!(
"locktower-observed",
"tower-observed",
(
"slot",
vote_state.nth_recent_vote(0).map(|v| v.slot).unwrap_or(0),
@ -223,14 +222,14 @@ impl Locktower {
"epoch_stakes cannot move backwards"
);
info!(
"Locktower updated epoch bank slot: {} epoch: {}",
"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!(
"locktower-epoch",
"tower-epoch",
("epoch", self.epoch_stakes.epoch, i64),
("self_staked", self.epoch_stakes.self_staked, i64),
("total_staked", self.epoch_stakes.total_staked, i64)
@ -256,7 +255,7 @@ impl Locktower {
.retain(|vote| slots.iter().any(|slot| vote.slot == *slot));
datapoint_info!(
"locktower-vote",
"tower-vote",
("latest", slot, i64),
("root", self.lockouts.root_slot.unwrap_or(0), i64)
);
@ -429,11 +428,11 @@ mod test {
fn test_collect_vote_lockouts_no_epoch_stakes() {
let accounts = gen_stakes(&[(1, &[0])]);
let epoch_stakes = EpochStakes::new_for_tests(2);
let locktower = Locktower::new(epoch_stakes, 0, 0.67);
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 = locktower.collect_vote_lockouts(1, accounts.into_iter(), &ancestors);
let staked_lockouts = tower.collect_vote_lockouts(1, accounts.into_iter(), &ancestors);
assert!(staked_lockouts.is_empty());
}
@ -442,11 +441,11 @@ mod test {
//two accounts voting for slot 0 with 1 token staked
let accounts = gen_stakes(&[(1, &[0]), (1, &[0])]);
let epoch_stakes = EpochStakes::new_from_stakes(0, &accounts);
let locktower = Locktower::new(epoch_stakes, 0, 0.67);
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 = locktower.collect_vote_lockouts(1, accounts.into_iter(), &ancestors);
let staked_lockouts = tower.collect_vote_lockouts(1, accounts.into_iter(), &ancestors);
assert_eq!(staked_lockouts[&0].stake, 2);
assert_eq!(staked_lockouts[&0].lockout, 2 + 2 + 4 + 4);
}
@ -457,14 +456,14 @@ mod test {
//two accounts voting for slot 0 with 1 token staked
let accounts = gen_stakes(&[(1, &votes), (1, &votes)]);
let epoch_stakes = EpochStakes::new_from_stakes(0, &accounts);
let mut locktower = Locktower::new(epoch_stakes, 0, 0.67);
let mut tower = Tower::new(epoch_stakes, 0, 0.67);
let mut ancestors = HashMap::new();
for i in 0..(MAX_LOCKOUT_HISTORY + 1) {
locktower.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());
}
assert_eq!(locktower.lockouts.root_slot, Some(0));
let staked_lockouts = locktower.collect_vote_lockouts(
assert_eq!(tower.lockouts.root_slot, Some(0));
let staked_lockouts = tower.collect_vote_lockouts(
MAX_LOCKOUT_HISTORY as u64,
accounts.into_iter(),
&ancestors,
@ -478,8 +477,8 @@ mod test {
#[test]
fn test_calculate_weight_skips_root() {
let mut locktower = Locktower::new(EpochStakes::new_for_tests(2), 0, 0.67);
locktower.lockouts.root_slot = Some(1);
let mut tower = Tower::new(EpochStakes::new_for_tests(2), 0, 0.67);
tower.lockouts.root_slot = Some(1);
let stakes = vec![
(
0,
@ -498,12 +497,12 @@ mod test {
]
.into_iter()
.collect();
assert_eq!(locktower.calculate_weight(&stakes), 0u128);
assert_eq!(tower.calculate_weight(&stakes), 0u128);
}
#[test]
fn test_calculate_weight() {
let locktower = Locktower::new(EpochStakes::new_for_tests(2), 0, 0.67);
let tower = Tower::new(EpochStakes::new_for_tests(2), 0, 0.67);
let stakes = vec![(
0,
StakeLockout {
@ -513,12 +512,12 @@ mod test {
)]
.into_iter()
.collect();
assert_eq!(locktower.calculate_weight(&stakes), 8u128);
assert_eq!(tower.calculate_weight(&stakes), 8u128);
}
#[test]
fn test_check_vote_threshold_without_votes() {
let locktower = Locktower::new(EpochStakes::new_for_tests(2), 1, 0.67);
let tower = Tower::new(EpochStakes::new_for_tests(2), 1, 0.67);
let stakes = vec![(
0,
StakeLockout {
@ -528,12 +527,12 @@ mod test {
)]
.into_iter()
.collect();
assert!(locktower.check_vote_stake_threshold(0, &stakes));
assert!(tower.check_vote_stake_threshold(0, &stakes));
}
#[test]
fn test_is_slot_confirmed_not_enough_stake_failure() {
let locktower = Locktower::new(EpochStakes::new_for_tests(2), 1, 0.67);
let tower = Tower::new(EpochStakes::new_for_tests(2), 1, 0.67);
let stakes = vec![(
0,
StakeLockout {
@ -543,19 +542,19 @@ mod test {
)]
.into_iter()
.collect();
assert!(!locktower.is_slot_confirmed(0, &stakes));
assert!(!tower.is_slot_confirmed(0, &stakes));
}
#[test]
fn test_is_slot_confirmed_unknown_slot() {
let locktower = Locktower::new(EpochStakes::new_for_tests(2), 1, 0.67);
let tower = Tower::new(EpochStakes::new_for_tests(2), 1, 0.67);
let stakes = HashMap::new();
assert!(!locktower.is_slot_confirmed(0, &stakes));
assert!(!tower.is_slot_confirmed(0, &stakes));
}
#[test]
fn test_is_slot_confirmed_pass() {
let locktower = Locktower::new(EpochStakes::new_for_tests(2), 1, 0.67);
let tower = Tower::new(EpochStakes::new_for_tests(2), 1, 0.67);
let stakes = vec![(
0,
StakeLockout {
@ -565,68 +564,68 @@ mod test {
)]
.into_iter()
.collect();
assert!(locktower.is_slot_confirmed(0, &stakes));
assert!(tower.is_slot_confirmed(0, &stakes));
}
#[test]
fn test_is_locked_out_empty() {
let locktower = Locktower::new(EpochStakes::new_for_tests(2), 0, 0.67);
let tower = Tower::new(EpochStakes::new_for_tests(2), 0, 0.67);
let descendants = HashMap::new();
assert!(!locktower.is_locked_out(0, &descendants));
assert!(!tower.is_locked_out(0, &descendants));
}
#[test]
fn test_is_locked_out_root_slot_child_pass() {
let mut locktower = Locktower::new(EpochStakes::new_for_tests(2), 0, 0.67);
let mut tower = Tower::new(EpochStakes::new_for_tests(2), 0, 0.67);
let descendants = vec![(0, vec![1].into_iter().collect())]
.into_iter()
.collect();
locktower.lockouts.root_slot = Some(0);
assert!(!locktower.is_locked_out(1, &descendants));
tower.lockouts.root_slot = Some(0);
assert!(!tower.is_locked_out(1, &descendants));
}
#[test]
fn test_is_locked_out_root_slot_sibling_fail() {
let mut locktower = Locktower::new(EpochStakes::new_for_tests(2), 0, 0.67);
let mut tower = Tower::new(EpochStakes::new_for_tests(2), 0, 0.67);
let descendants = vec![(0, vec![1].into_iter().collect())]
.into_iter()
.collect();
locktower.lockouts.root_slot = Some(0);
assert!(locktower.is_locked_out(2, &descendants));
tower.lockouts.root_slot = Some(0);
assert!(tower.is_locked_out(2, &descendants));
}
#[test]
fn test_check_already_voted() {
let mut locktower = Locktower::new(EpochStakes::new_for_tests(2), 0, 0.67);
locktower.record_vote(0, Hash::default());
assert!(locktower.has_voted(0));
assert!(!locktower.has_voted(1));
let mut tower = Tower::new(EpochStakes::new_for_tests(2), 0, 0.67);
tower.record_vote(0, Hash::default());
assert!(tower.has_voted(0));
assert!(!tower.has_voted(1));
}
#[test]
fn test_is_locked_out_double_vote() {
let mut locktower = Locktower::new(EpochStakes::new_for_tests(2), 0, 0.67);
let mut tower = Tower::new(EpochStakes::new_for_tests(2), 0, 0.67);
let descendants = vec![(0, vec![1].into_iter().collect()), (1, HashSet::new())]
.into_iter()
.collect();
locktower.record_vote(0, Hash::default());
locktower.record_vote(1, Hash::default());
assert!(locktower.is_locked_out(0, &descendants));
tower.record_vote(0, Hash::default());
tower.record_vote(1, Hash::default());
assert!(tower.is_locked_out(0, &descendants));
}
#[test]
fn test_is_locked_out_child() {
let mut locktower = Locktower::new(EpochStakes::new_for_tests(2), 0, 0.67);
let mut tower = Tower::new(EpochStakes::new_for_tests(2), 0, 0.67);
let descendants = vec![(0, vec![1].into_iter().collect())]
.into_iter()
.collect();
locktower.record_vote(0, Hash::default());
assert!(!locktower.is_locked_out(1, &descendants));
tower.record_vote(0, Hash::default());
assert!(!tower.is_locked_out(1, &descendants));
}
#[test]
fn test_is_locked_out_sibling() {
let mut locktower = Locktower::new(EpochStakes::new_for_tests(2), 0, 0.67);
let mut tower = Tower::new(EpochStakes::new_for_tests(2), 0, 0.67);
let descendants = vec![
(0, vec![1, 2].into_iter().collect()),
(1, HashSet::new()),
@ -634,30 +633,30 @@ mod test {
]
.into_iter()
.collect();
locktower.record_vote(0, Hash::default());
locktower.record_vote(1, Hash::default());
assert!(locktower.is_locked_out(2, &descendants));
tower.record_vote(0, Hash::default());
tower.record_vote(1, Hash::default());
assert!(tower.is_locked_out(2, &descendants));
}
#[test]
fn test_is_locked_out_last_vote_expired() {
let mut locktower = Locktower::new(EpochStakes::new_for_tests(2), 0, 0.67);
let mut tower = Tower::new(EpochStakes::new_for_tests(2), 0, 0.67);
let descendants = vec![(0, vec![1, 4].into_iter().collect()), (1, HashSet::new())]
.into_iter()
.collect();
locktower.record_vote(0, Hash::default());
locktower.record_vote(1, Hash::default());
assert!(!locktower.is_locked_out(4, &descendants));
locktower.record_vote(4, Hash::default());
assert_eq!(locktower.lockouts.votes[0].slot, 0);
assert_eq!(locktower.lockouts.votes[0].confirmation_count, 2);
assert_eq!(locktower.lockouts.votes[1].slot, 4);
assert_eq!(locktower.lockouts.votes[1].confirmation_count, 1);
tower.record_vote(0, Hash::default());
tower.record_vote(1, Hash::default());
assert!(!tower.is_locked_out(4, &descendants));
tower.record_vote(4, Hash::default());
assert_eq!(tower.lockouts.votes[0].slot, 0);
assert_eq!(tower.lockouts.votes[0].confirmation_count, 2);
assert_eq!(tower.lockouts.votes[1].slot, 4);
assert_eq!(tower.lockouts.votes[1].confirmation_count, 1);
}
#[test]
fn test_check_vote_threshold_below_threshold() {
let mut locktower = Locktower::new(EpochStakes::new_for_tests(2), 1, 0.67);
let mut tower = Tower::new(EpochStakes::new_for_tests(2), 1, 0.67);
let stakes = vec![(
0,
StakeLockout {
@ -667,12 +666,12 @@ mod test {
)]
.into_iter()
.collect();
locktower.record_vote(0, Hash::default());
assert!(!locktower.check_vote_stake_threshold(1, &stakes));
tower.record_vote(0, Hash::default());
assert!(!tower.check_vote_stake_threshold(1, &stakes));
}
#[test]
fn test_check_vote_threshold_above_threshold() {
let mut locktower = Locktower::new(EpochStakes::new_for_tests(2), 1, 0.67);
let mut tower = Tower::new(EpochStakes::new_for_tests(2), 1, 0.67);
let stakes = vec![(
0,
StakeLockout {
@ -682,13 +681,13 @@ mod test {
)]
.into_iter()
.collect();
locktower.record_vote(0, Hash::default());
assert!(locktower.check_vote_stake_threshold(1, &stakes));
tower.record_vote(0, Hash::default());
assert!(tower.check_vote_stake_threshold(1, &stakes));
}
#[test]
fn test_check_vote_threshold_above_threshold_after_pop() {
let mut locktower = Locktower::new(EpochStakes::new_for_tests(2), 1, 0.67);
let mut tower = Tower::new(EpochStakes::new_for_tests(2), 1, 0.67);
let stakes = vec![(
0,
StakeLockout {
@ -698,18 +697,18 @@ mod test {
)]
.into_iter()
.collect();
locktower.record_vote(0, Hash::default());
locktower.record_vote(1, Hash::default());
locktower.record_vote(2, Hash::default());
assert!(locktower.check_vote_stake_threshold(6, &stakes));
tower.record_vote(0, Hash::default());
tower.record_vote(1, Hash::default());
tower.record_vote(2, Hash::default());
assert!(tower.check_vote_stake_threshold(6, &stakes));
}
#[test]
fn test_check_vote_threshold_above_threshold_no_stake() {
let mut locktower = Locktower::new(EpochStakes::new_for_tests(2), 1, 0.67);
let mut tower = Tower::new(EpochStakes::new_for_tests(2), 1, 0.67);
let stakes = HashMap::new();
locktower.record_vote(0, Hash::default());
assert!(!locktower.check_vote_stake_threshold(1, &stakes));
tower.record_vote(0, Hash::default());
assert!(!tower.check_vote_stake_threshold(1, &stakes));
}
#[test]
@ -724,7 +723,7 @@ mod test {
ancestors.insert(2, set);
let set: HashSet<u64> = vec![0u64].into_iter().collect();
ancestors.insert(1, set);
Locktower::update_ancestor_lockouts(&mut stake_lockouts, &vote, &ancestors);
Tower::update_ancestor_lockouts(&mut stake_lockouts, &vote, &ancestors);
assert_eq!(stake_lockouts[&0].lockout, 2);
assert_eq!(stake_lockouts[&1].lockout, 2);
assert_eq!(stake_lockouts[&2].lockout, 2);
@ -742,12 +741,12 @@ mod test {
slot: 2,
confirmation_count: 1,
};
Locktower::update_ancestor_lockouts(&mut stake_lockouts, &vote, &ancestors);
Tower::update_ancestor_lockouts(&mut stake_lockouts, &vote, &ancestors);
let vote = Lockout {
slot: 1,
confirmation_count: 2,
};
Locktower::update_ancestor_lockouts(&mut stake_lockouts, &vote, &ancestors);
Tower::update_ancestor_lockouts(&mut stake_lockouts, &vote, &ancestors);
assert_eq!(stake_lockouts[&0].lockout, 2 + 4);
assert_eq!(stake_lockouts[&1].lockout, 2 + 4);
assert_eq!(stake_lockouts[&2].lockout, 2);
@ -760,7 +759,7 @@ mod test {
account.lamports = 1;
let set: HashSet<u64> = vec![0u64, 1u64].into_iter().collect();
let ancestors: HashMap<u64, HashSet<u64>> = [(2u64, set)].into_iter().cloned().collect();
Locktower::update_ancestor_stakes(&mut stake_lockouts, 2, account.lamports, &ancestors);
Tower::update_ancestor_stakes(&mut stake_lockouts, 2, account.lamports, &ancestors);
assert_eq!(stake_lockouts[&0].stake, 1);
assert_eq!(stake_lockouts[&1].stake, 1);
assert_eq!(stake_lockouts[&2].stake, 1);
@ -782,51 +781,48 @@ mod test {
let total_stake = 4;
let threshold_size = 0.67;
let threshold_stake = (f64::ceil(total_stake as f64 * threshold_size)) as u64;
let locktower_votes: Vec<u64> = (0..VOTE_THRESHOLD_DEPTH as u64).collect();
let tower_votes: Vec<u64> = (0..VOTE_THRESHOLD_DEPTH as u64).collect();
let accounts = gen_stakes(&[
(threshold_stake, &[(VOTE_THRESHOLD_DEPTH - 2) as u64]),
(total_stake - threshold_stake, &locktower_votes[..]),
(total_stake - threshold_stake, &tower_votes[..]),
]);
// Initialize locktower
// Initialize tower
let stakes: HashMap<_, _> = accounts.iter().map(|(pk, (s, _))| (*pk, *s)).collect();
let epoch_stakes = EpochStakes::new(0, stakes, &Pubkey::default());
let mut locktower = Locktower::new(epoch_stakes, VOTE_THRESHOLD_DEPTH, threshold_size);
let mut tower = Tower::new(epoch_stakes, VOTE_THRESHOLD_DEPTH, threshold_size);
// CASE 1: Record the first VOTE_THRESHOLD locktower 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
// for slot 0, which is common to all account vote states, so we should pass the
// threshold check
let vote_to_evaluate = VOTE_THRESHOLD_DEPTH as u64;
for vote in &locktower_votes {
locktower.record_vote(*vote, Hash::default());
for vote in &tower_votes {
tower.record_vote(*vote, Hash::default());
}
let stakes_lockouts = locktower.collect_vote_lockouts(
vote_to_evaluate,
accounts.clone().into_iter(),
&ancestors,
);
assert!(locktower.check_vote_stake_threshold(vote_to_evaluate, &stakes_lockouts));
let stakes_lockouts =
tower.collect_vote_lockouts(vote_to_evaluate, accounts.clone().into_iter(), &ancestors);
assert!(tower.check_vote_stake_threshold(vote_to_evaluate, &stakes_lockouts));
// 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
// stake to pass the threshold
let vote_to_evaluate = VOTE_THRESHOLD_DEPTH as u64 + 1;
let stakes_lockouts =
locktower.collect_vote_lockouts(vote_to_evaluate, accounts.into_iter(), &ancestors);
assert!(!locktower.check_vote_stake_threshold(vote_to_evaluate, &stakes_lockouts));
tower.collect_vote_lockouts(vote_to_evaluate, accounts.into_iter(), &ancestors);
assert!(!tower.check_vote_stake_threshold(vote_to_evaluate, &stakes_lockouts));
}
fn vote_and_check_recent(num_votes: usize) {
let mut locktower = Locktower::new(EpochStakes::new_for_tests(2), 1, 0.67);
let mut tower = Tower::new(EpochStakes::new_for_tests(2), 1, 0.67);
let start = num_votes.saturating_sub(MAX_RECENT_VOTES);
let expected: Vec<_> = (start..num_votes)
.map(|i| Vote::new(i as u64, Hash::default()))
.collect();
for i in 0..num_votes {
locktower.record_vote(i as u64, Hash::default());
tower.record_vote(i as u64, Hash::default());
}
assert_eq!(expected, locktower.recent_votes())
assert_eq!(expected, tower.recent_votes())
}
#[test]

View File

@ -30,6 +30,7 @@ pub mod cluster;
pub mod cluster_info;
pub mod cluster_info_repair_listener;
pub mod cluster_tests;
pub mod consensus;
pub mod entry;
pub mod erasure;
pub mod fetch_stage;
@ -41,7 +42,6 @@ pub mod leader_schedule_cache;
pub mod leader_schedule_utils;
pub mod local_cluster;
pub mod local_vote_signer_service;
pub mod locktower;
pub mod packet;
pub mod poh;
pub mod poh_recorder;

View File

@ -4,10 +4,10 @@ use crate::bank_forks::BankForks;
use crate::blocktree::Blocktree;
use crate::blocktree_processor;
use crate::cluster_info::ClusterInfo;
use crate::consensus::{StakeLockout, Tower};
use crate::entry::{Entry, EntrySlice};
use crate::leader_schedule_cache::LeaderScheduleCache;
use crate::leader_schedule_utils;
use crate::locktower::{Locktower, StakeLockout};
use crate::packet::BlobError;
use crate::poh_recorder::PohRecorder;
use crate::result::{Error, Result};
@ -102,7 +102,7 @@ impl ReplayStage {
let poh_recorder = poh_recorder.clone();
let my_pubkey = *my_pubkey;
let mut ticks_per_slot = 0;
let mut locktower = Locktower::new_from_forks(&bank_forks.read().unwrap(), &my_pubkey);
let mut tower = Tower::new_from_forks(&bank_forks.read().unwrap(), &my_pubkey);
// Start the replay stage loop
let leader_schedule_cache = leader_schedule_cache.clone();
let vote_account = *vote_account;
@ -142,8 +142,7 @@ impl ReplayStage {
ticks_per_slot = bank.ticks_per_slot();
}
let votable =
Self::generate_votable_banks(&bank_forks, &locktower, &mut progress);
let votable = Self::generate_votable_banks(&bank_forks, &tower, &mut progress);
if let Some((_, bank)) = votable.last() {
subscriptions.notify_subscribers(bank.slot(), &bank_forks);
@ -151,7 +150,7 @@ impl ReplayStage {
Self::handle_votable_bank(
&bank,
&bank_forks,
&mut locktower,
&mut tower,
&mut progress,
&vote_account,
&voting_keypair,
@ -318,7 +317,7 @@ impl ReplayStage {
fn handle_votable_bank<T>(
bank: &Arc<Bank>,
bank_forks: &Arc<RwLock<BankForks>>,
locktower: &mut Locktower,
tower: &mut Tower,
progress: &mut HashMap<u64, ForkProgress>,
vote_account: &Pubkey,
voting_keypair: &Option<Arc<T>>,
@ -330,7 +329,7 @@ impl ReplayStage {
where
T: 'static + KeypairUtil + Send + Sync,
{
if let Some(new_root) = locktower.record_vote(bank.slot(), bank.hash()) {
if let Some(new_root) = tower.record_vote(bank.slot(), bank.hash()) {
// get the root bank before squash
let root_bank = bank_forks
.read()
@ -352,7 +351,7 @@ impl ReplayStage {
Self::handle_new_root(&bank_forks, progress);
root_bank_sender.send(rooted_banks)?;
}
locktower.update_epoch(&bank);
tower.update_epoch(&bank);
if let Some(ref voting_keypair) = voting_keypair {
let node_keypair = cluster_info.read().unwrap().keypair.clone();
@ -360,7 +359,7 @@ impl ReplayStage {
let vote_ix = vote_instruction::vote(
&vote_account,
&voting_keypair.pubkey(),
locktower.recent_votes(),
tower.recent_votes(),
);
let mut vote_tx =
@ -438,11 +437,11 @@ impl ReplayStage {
fn generate_votable_banks(
bank_forks: &Arc<RwLock<BankForks>>,
locktower: &Locktower,
tower: &Tower,
progress: &mut HashMap<u64, ForkProgress>,
) -> Vec<(u128, Arc<Bank>)> {
let locktower_start = Instant::now();
// Locktower voting
let tower_start = Instant::now();
// Tower voting
let descendants = bank_forks.read().unwrap().descendants();
let ancestors = bank_forks.read().unwrap().ancestors();
let frozen_banks = bank_forks.read().unwrap().frozen_banks();
@ -456,24 +455,24 @@ impl ReplayStage {
is_votable
})
.filter(|b| {
let is_recent_epoch = locktower.is_recent_epoch(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| {
let has_voted = locktower.has_voted(b.slot());
let has_voted = tower.has_voted(b.slot());
trace!("bank is has_voted: {} {}", b.slot(), has_voted);
!has_voted
})
.filter(|b| {
let is_locked_out = locktower.is_locked_out(b.slot(), &descendants);
let is_locked_out = tower.is_locked_out(b.slot(), &descendants);
trace!("bank is is_locked_out: {} {}", b.slot(), is_locked_out);
!is_locked_out
})
.map(|bank| {
(
bank,
locktower.collect_vote_lockouts(
tower.collect_vote_lockouts(
bank.slot(),
bank.vote_accounts().into_iter(),
&ancestors,
@ -481,43 +480,42 @@ impl ReplayStage {
)
})
.filter(|(b, stake_lockouts)| {
let vote_threshold =
locktower.check_vote_stake_threshold(b.slot(), &stake_lockouts);
Self::confirm_forks(locktower, stake_lockouts, progress, bank_forks);
let vote_threshold = tower.check_vote_stake_threshold(b.slot(), &stake_lockouts);
Self::confirm_forks(tower, stake_lockouts, progress, bank_forks);
debug!("bank vote_threshold: {} {}", b.slot(), vote_threshold);
vote_threshold
})
.map(|(b, stake_lockouts)| (locktower.calculate_weight(&stake_lockouts), b.clone()))
.map(|(b, stake_lockouts)| (tower.calculate_weight(&stake_lockouts), b.clone()))
.collect();
votable.sort_by_key(|b| b.0);
let ms = timing::duration_as_ms(&locktower_start.elapsed());
let ms = timing::duration_as_ms(&tower_start.elapsed());
trace!("votable_banks {}", votable.len());
if !votable.is_empty() {
let weights: Vec<u128> = votable.iter().map(|x| x.0).collect();
info!(
"@{:?} locktower duration: {:?} len: {} weights: {:?}",
"@{:?} tower duration: {:?} len: {} weights: {:?}",
timing::timestamp(),
ms,
votable.len(),
weights
);
}
inc_new_counter_info!("replay_stage-locktower_duration", ms as usize);
inc_new_counter_info!("replay_stage-tower_duration", ms as usize);
votable
}
fn confirm_forks(
locktower: &Locktower,
tower: &Tower,
stake_lockouts: &HashMap<u64, StakeLockout>,
progress: &mut HashMap<u64, ForkProgress>,
bank_forks: &Arc<RwLock<BankForks>>,
) {
progress.retain(|slot, prog| {
let duration = timing::timestamp() - prog.started_ms;
if locktower.is_slot_confirmed(*slot, stake_lockouts)
if tower.is_slot_confirmed(*slot, stake_lockouts)
&& bank_forks
.read()
.unwrap()

View File

@ -5366,8 +5366,8 @@
{
"aliasColors": {
"cluster-info.repair": "#ba43a9",
"locktower-observed.squash_account": "#0a437c",
"locktower-observed.squash_cache": "#ea6460",
"tower-observed.squash_account": "#0a437c",
"tower-observed.squash_cache": "#ea6460",
"replay_stage-new_leader.last": "#00ffbb",
"window-service.receive": "#b7dbab",
"window-stage.consumed": "#5195ce"
@ -5465,7 +5465,7 @@
"measurement": "cluster_info-vote-count",
"orderByTime": "ASC",
"policy": "autogen",
"query": "SELECT mean(\"squash_accounts_ms\") AS \"squash_account\" FROM \"$testnet\".\"autogen\".\"locktower-observed\" WHERE host_id =~ /$hostid/ AND $timeFilter GROUP BY time($__interval)",
"query": "SELECT mean(\"squash_accounts_ms\") AS \"squash_account\" FROM \"$testnet\".\"autogen\".\"tower-observed\" WHERE host_id =~ /$hostid/ AND $timeFilter GROUP BY time($__interval)",
"rawQuery": true,
"refId": "B",
"resultFormat": "time_series",
@ -5504,7 +5504,7 @@
"measurement": "cluster_info-vote-count",
"orderByTime": "ASC",
"policy": "autogen",
"query": "SELECT mean(\"squash_cache_ms\") AS \"squash_cache\" FROM \"$testnet\".\"autogen\".\"locktower-observed\" WHERE host_id =~ /$hostid/ AND $timeFilter GROUP BY time($__interval)",
"query": "SELECT mean(\"squash_cache_ms\") AS \"squash_cache\" FROM \"$testnet\".\"autogen\".\"tower-observed\" WHERE host_id =~ /$hostid/ AND $timeFilter GROUP BY time($__interval)",
"rawQuery": true,
"refId": "C",
"resultFormat": "time_series",
@ -5576,7 +5576,7 @@
"cluster-info.repair": "#ba43a9",
"fetch_stage-discard_forwards.sum": "#00ffbb",
"fetch_stage-honor_forwards.sum": "#bf1b00",
"locktower-vote.last": "#00ffbb",
"tower-vote.last": "#00ffbb",
"replay_stage-new_leader.last": "#00ffbb",
"window-service.receive": "#b7dbab",
"window-stage.consumed": "#5195ce"
@ -5892,7 +5892,7 @@
"cluster-info.repair": "#ba43a9",
"fetch_stage-discard_forwards.sum": "#00ffbb",
"fetch_stage-honor_forwards.sum": "#bf1b00",
"locktower-vote.last": "#00ffbb",
"tower-vote.last": "#00ffbb",
"poh_recorder-record_lock_contention.sum": "#5195ce",
"poh_recorder-tick_lock_contention.sum": "#962d82",
"replay_stage-new_leader.last": "#00ffbb",
@ -7089,7 +7089,7 @@
"measurement": "cluster_info-vote-count",
"orderByTime": "ASC",
"policy": "autogen",
"query": "SELECT last(\"latest\") - last(\"root\") FROM \"$testnet\".\"autogen\".\"locktower-vote\" WHERE host_id =~ /$hostid/ AND $timeFilter GROUP BY time($__interval)",
"query": "SELECT last(\"latest\") - last(\"root\") FROM \"$testnet\".\"autogen\".\"tower-vote\" WHERE host_id =~ /$hostid/ AND $timeFilter GROUP BY time($__interval)",
"rawQuery": true,
"refId": "A",
"resultFormat": "time_series",
@ -7126,7 +7126,7 @@
],
"orderByTime": "ASC",
"policy": "default",
"query": "SELECT last(\"slot\") - last(\"root\") FROM \"$testnet\".\"autogen\".\"locktower-observed\" WHERE host_id =~ /$hostid/ AND $timeFilter GROUP BY time($__interval)",
"query": "SELECT last(\"slot\") - last(\"root\") FROM \"$testnet\".\"autogen\".\"tower-observed\" WHERE host_id =~ /$hostid/ AND $timeFilter GROUP BY time($__interval)",
"rawQuery": true,
"refId": "B",
"resultFormat": "time_series",
@ -7190,7 +7190,7 @@
{
"aliasColors": {
"cluster-info.repair": "#ba43a9",
"locktower-vote.last": "#00ffbb",
"tower-vote.last": "#00ffbb",
"replay_stage-new_leader.last": "#00ffbb",
"window-service.receive": "#b7dbab",
"window-stage.consumed": "#5195ce"
@ -7249,7 +7249,7 @@
"measurement": "cluster_info-vote-count",
"orderByTime": "ASC",
"policy": "autogen",
"query": "SELECT last(\"root\") FROM \"$testnet\".\"autogen\".\"locktower-vote\" WHERE host_id =~ /$hostid/ AND $timeFilter GROUP BY time($__interval)",
"query": "SELECT last(\"root\") FROM \"$testnet\".\"autogen\".\"tower-vote\" WHERE host_id =~ /$hostid/ AND $timeFilter GROUP BY time($__interval)",
"rawQuery": true,
"refId": "A",
"resultFormat": "time_series",
@ -7286,7 +7286,7 @@
],
"orderByTime": "ASC",
"policy": "default",
"query": "SELECT last(\"root\") FROM \"$testnet\".\"autogen\".\"locktower-observed\" WHERE host_id =~ /$hostid/ AND $timeFilter GROUP BY time($__interval)",
"query": "SELECT last(\"root\") FROM \"$testnet\".\"autogen\".\"tower-observed\" WHERE host_id =~ /$hostid/ AND $timeFilter GROUP BY time($__interval)",
"rawQuery": true,
"refId": "B",
"resultFormat": "time_series",
@ -8100,4 +8100,4 @@
"title": "Testnet Monitor (edge)",
"uid": "testnet-edge",
"version": 3
}
}

View File

@ -525,7 +525,7 @@ impl Bank {
let squash_cache_ms = duration_as_ms(&squash_cache_start.elapsed());
datapoint_info!(
"locktower-observed",
"tower-observed",
("squash_accounts_ms", squash_accounts_ms, i64),
("squash_cache_ms", squash_cache_ms, i64)
);