Refactor Vote Program Account setup (#2992)
This commit is contained in:
@@ -190,7 +190,7 @@ impl BroadcastService {
|
||||
let mut broadcast_table = cluster_info
|
||||
.read()
|
||||
.unwrap()
|
||||
.sorted_tvu_peers(&staking_utils::staked_nodes(&bank));
|
||||
.sorted_tvu_peers(&staking_utils::node_stakes(&bank));
|
||||
// Layer 1, leader nodes are limited to the fanout size.
|
||||
broadcast_table.truncate(DATA_PLANE_FANOUT);
|
||||
inc_new_counter_info!("broadcast_service-num_peers", broadcast_table.len() + 1);
|
||||
|
@@ -878,7 +878,7 @@ impl ClusterInfo {
|
||||
let start = timestamp();
|
||||
let stakes: HashMap<_, _> = match bank_forks {
|
||||
Some(ref bank_forks) => {
|
||||
staking_utils::staked_nodes(&bank_forks.read().unwrap().working_bank())
|
||||
staking_utils::node_stakes(&bank_forks.read().unwrap().working_bank())
|
||||
}
|
||||
None => HashMap::new(),
|
||||
};
|
||||
|
@@ -462,7 +462,7 @@ pub fn make_active_set_entries(
|
||||
let vote_account_id = voting_keypair.pubkey();
|
||||
|
||||
let new_vote_account_tx =
|
||||
VoteTransaction::new_account(active_keypair, vote_account_id, *last_id, 1, 1);
|
||||
VoteTransaction::fund_staking_account(active_keypair, vote_account_id, *last_id, 1, 1);
|
||||
let new_vote_account_entry = next_entry_mut(&mut last_entry_id, 1, vec![new_vote_account_tx]);
|
||||
|
||||
// 3) Create vote entry
|
||||
|
@@ -35,12 +35,12 @@ impl LeaderConfirmationService {
|
||||
|
||||
// Hold an accounts_db read lock as briefly as possible, just long enough to collect all
|
||||
// the vote states
|
||||
let vote_states = bank.vote_states(|vote_state| leader_id != vote_state.node_id);
|
||||
let vote_states = bank.vote_states(|_, vote_state| leader_id != vote_state.delegate_id);
|
||||
|
||||
let mut ticks_and_stakes: Vec<(u64, u64)> = vote_states
|
||||
.iter()
|
||||
.filter_map(|vote_state| {
|
||||
let validator_stake = bank.get_balance(&vote_state.node_id);
|
||||
.filter_map(|(_, vote_state)| {
|
||||
let validator_stake = bank.get_balance(&vote_state.delegate_id);
|
||||
total_stake += validator_stake;
|
||||
// Filter out any validators that don't have at least one vote
|
||||
// by returning None
|
||||
|
@@ -5,13 +5,30 @@ use solana_sdk::pubkey::Pubkey;
|
||||
|
||||
/// Return the leader schedule for the given epoch.
|
||||
fn leader_schedule(epoch_height: u64, bank: &Bank) -> LeaderSchedule {
|
||||
let stakes = staking_utils::staked_nodes_at_epoch(bank, epoch_height);
|
||||
let stakes = staking_utils::node_stakes_at_epoch(bank, epoch_height);
|
||||
let mut seed = [0u8; 32];
|
||||
seed[0..8].copy_from_slice(&epoch_height.to_le_bytes());
|
||||
let stakes: Vec<_> = stakes.into_iter().collect();
|
||||
let mut stakes: Vec<_> = stakes.into_iter().collect();
|
||||
sort_stakes(&mut stakes);
|
||||
LeaderSchedule::new(&stakes, seed, bank.slots_per_epoch())
|
||||
}
|
||||
|
||||
fn sort_stakes(stakes: &mut Vec<(Pubkey, u64)>) {
|
||||
// Sort first by stake. If stakes are the same, sort by pubkey to ensure a
|
||||
// deterministic result.
|
||||
// Note: Use unstable sort, because we dedup right after to remove the equal elements.
|
||||
stakes.sort_unstable_by(|(l_id, l_stake), (r_id, r_stake)| {
|
||||
if r_stake == l_stake {
|
||||
r_id.cmp(&l_id)
|
||||
} else {
|
||||
r_stake.cmp(&l_stake)
|
||||
}
|
||||
});
|
||||
|
||||
// Now that it's sorted, we can do an O(n) dedup.
|
||||
stakes.dedup();
|
||||
}
|
||||
|
||||
/// Return the leader for the slot at the slot_index and epoch_height returned
|
||||
/// by the given function.
|
||||
fn slot_leader_by<F>(bank: &Bank, get_slot_index: F) -> Pubkey
|
||||
@@ -92,7 +109,7 @@ mod tests {
|
||||
let (genesis_block, _mint_keypair) = GenesisBlock::new_with_leader(2, pubkey, 2);
|
||||
let bank = Bank::new(&genesis_block);
|
||||
|
||||
let ids_and_stakes: Vec<_> = staking_utils::staked_nodes(&bank).into_iter().collect();
|
||||
let ids_and_stakes: Vec<_> = staking_utils::node_stakes(&bank).into_iter().collect();
|
||||
let seed = [0u8; 32];
|
||||
let leader_schedule =
|
||||
LeaderSchedule::new(&ids_and_stakes, seed, genesis_block.slots_per_epoch);
|
||||
@@ -122,4 +139,31 @@ mod tests {
|
||||
assert_eq!(next_slot_leader_index(0, 0, 2), (1, 0));
|
||||
assert_eq!(next_slot_leader_index(1, 0, 2), (0, 1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sort_stakes_basic() {
|
||||
let pubkey0 = Keypair::new().pubkey();
|
||||
let pubkey1 = Keypair::new().pubkey();
|
||||
let mut stakes = vec![(pubkey0, 1), (pubkey1, 2)];
|
||||
sort_stakes(&mut stakes);
|
||||
assert_eq!(stakes, vec![(pubkey1, 2), (pubkey0, 1)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sort_stakes_with_dup() {
|
||||
let pubkey0 = Keypair::new().pubkey();
|
||||
let pubkey1 = Keypair::new().pubkey();
|
||||
let mut stakes = vec![(pubkey0, 1), (pubkey1, 2), (pubkey0, 1)];
|
||||
sort_stakes(&mut stakes);
|
||||
assert_eq!(stakes, vec![(pubkey1, 2), (pubkey0, 1)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sort_stakes_with_equal_stakes() {
|
||||
let pubkey0 = Pubkey::default();
|
||||
let pubkey1 = Keypair::new().pubkey();
|
||||
let mut stakes = vec![(pubkey0, 1), (pubkey1, 1)];
|
||||
sort_stakes(&mut stakes);
|
||||
assert_eq!(stakes, vec![(pubkey1, 1), (pubkey0, 1)]);
|
||||
}
|
||||
}
|
||||
|
@@ -40,7 +40,7 @@ fn retransmit(
|
||||
.to_owned(),
|
||||
);
|
||||
let (neighbors, children) = compute_retransmit_peers(
|
||||
&staking_utils::staked_nodes(&bank_forks.read().unwrap().working_bank()),
|
||||
&staking_utils::node_stakes(&bank_forks.read().unwrap().working_bank()),
|
||||
cluster_info,
|
||||
DATA_PLANE_FANOUT,
|
||||
NEIGHBORHOOD_SIZE,
|
||||
|
@@ -15,18 +15,20 @@ pub fn get_supermajority_slot(bank: &Bank, epoch_height: u64) -> Option<u64> {
|
||||
find_supermajority_slot(supermajority_stake, stakes_and_lockouts.values())
|
||||
}
|
||||
|
||||
pub fn staked_nodes(bank: &Bank) -> HashMap<Pubkey, u64> {
|
||||
staked_nodes_extractor(bank, |stake, _| stake)
|
||||
/// Collect the node Pubkey and staker account balance for nodes
|
||||
/// that have non-zero balance in their corresponding staking accounts
|
||||
pub fn node_stakes(bank: &Bank) -> HashMap<Pubkey, u64> {
|
||||
node_stakes_extractor(bank, |stake, _| stake)
|
||||
}
|
||||
|
||||
/// Return the checkpointed stakes that should be used to generate a leader schedule.
|
||||
pub fn staked_nodes_at_epoch(bank: &Bank, epoch_height: u64) -> HashMap<Pubkey, u64> {
|
||||
staked_nodes_at_epoch_extractor(bank, epoch_height, |stake, _| stake)
|
||||
pub fn node_stakes_at_epoch(bank: &Bank, epoch_height: u64) -> HashMap<Pubkey, u64> {
|
||||
node_stakes_at_epoch_extractor(bank, epoch_height, |stake, _| stake)
|
||||
}
|
||||
|
||||
/// Return the checkpointed stakes that should be used to generate a leader schedule.
|
||||
/// state_extractor takes (stake, vote_state) and maps to an output.
|
||||
fn staked_nodes_at_epoch_extractor<F, T>(
|
||||
fn node_stakes_at_epoch_extractor<F, T>(
|
||||
bank: &Bank,
|
||||
epoch_height: u64,
|
||||
state_extractor: F,
|
||||
@@ -35,12 +37,12 @@ where
|
||||
F: Fn(u64, &VoteState) -> T,
|
||||
{
|
||||
let epoch_slot_height = epoch_height * bank.slots_per_epoch();
|
||||
staked_nodes_at_slot_extractor(bank, epoch_slot_height, state_extractor)
|
||||
node_stakes_at_slot_extractor(bank, epoch_slot_height, state_extractor)
|
||||
}
|
||||
|
||||
/// Return the checkpointed stakes that should be used to generate a leader schedule.
|
||||
/// state_extractor takes (stake, vote_state) and maps to an output
|
||||
fn staked_nodes_at_slot_extractor<F, T>(
|
||||
fn node_stakes_at_slot_extractor<F, T>(
|
||||
bank: &Bank,
|
||||
current_slot_height: u64,
|
||||
state_extractor: F,
|
||||
@@ -59,25 +61,23 @@ where
|
||||
.find(|bank| bank.id() <= slot_height)
|
||||
.unwrap_or_else(|| banks.last().unwrap());
|
||||
|
||||
staked_nodes_extractor(bank, state_extractor)
|
||||
node_stakes_extractor(bank, state_extractor)
|
||||
}
|
||||
|
||||
/// Collect the node Pubkey and staker account balance for nodes
|
||||
/// that have non-zero balance in their corresponding staker accounts.
|
||||
/// state_extractor takes (stake, vote_state) and maps to an output
|
||||
fn staked_nodes_extractor<F, T>(bank: &Bank, state_extractor: F) -> HashMap<Pubkey, T>
|
||||
fn node_stakes_extractor<F, T>(bank: &Bank, state_extractor: F) -> HashMap<Pubkey, T>
|
||||
where
|
||||
F: Fn(u64, &VoteState) -> T,
|
||||
{
|
||||
bank.vote_states(|_| true)
|
||||
bank.vote_states(|account_id, _| bank.get_balance(&account_id) > 0)
|
||||
.iter()
|
||||
.filter_map(|state| {
|
||||
let balance = bank.get_balance(&state.staker_id);
|
||||
if balance > 0 {
|
||||
Some((state.node_id, state_extractor(balance, &state)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
.map(|(account_id, state)| {
|
||||
(
|
||||
state.delegate_id,
|
||||
state_extractor(bank.get_balance(&account_id), &state),
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
@@ -86,7 +86,7 @@ fn epoch_stakes_and_lockouts(
|
||||
bank: &Bank,
|
||||
epoch_height: u64,
|
||||
) -> HashMap<Pubkey, (u64, Option<u64>)> {
|
||||
staked_nodes_at_epoch_extractor(bank, epoch_height, |stake, state| (stake, state.root_slot))
|
||||
node_stakes_at_epoch_extractor(bank, epoch_height, |stake, state| (stake, state.root_slot))
|
||||
}
|
||||
|
||||
fn find_supermajority_slot<'a, I>(supermajority_stake: u64, stakes_and_lockouts: I) -> Option<u64>
|
||||
@@ -146,7 +146,7 @@ mod tests {
|
||||
expected.insert(pubkey, bootstrap_tokens - 1);
|
||||
let bank = Bank::new_from_parent(&Arc::new(bank));
|
||||
assert_eq!(
|
||||
staked_nodes_at_slot_extractor(&bank, bank.slot_height(), |s, _| s),
|
||||
node_stakes_at_slot_extractor(&bank, bank.slot_height(), |s, _| s),
|
||||
expected
|
||||
);
|
||||
}
|
||||
@@ -154,13 +154,12 @@ mod tests {
|
||||
#[test]
|
||||
fn test_epoch_stakes_and_lockouts() {
|
||||
let validator = Keypair::new();
|
||||
let voter = Keypair::new();
|
||||
|
||||
let (genesis_block, mint_keypair) = GenesisBlock::new(500);
|
||||
let bank = Bank::new(&genesis_block);
|
||||
let bank_voter = Keypair::new();
|
||||
|
||||
// Give the validator some stake
|
||||
// Give the validator some stake but don't setup a staking account
|
||||
bank.transfer(
|
||||
1,
|
||||
&mint_keypair,
|
||||
@@ -169,9 +168,7 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
voting_keypair_tests::new_vote_account_with_vote(&validator, &voter, &bank, 1, 0);
|
||||
assert_eq!(bank.get_balance(&validator.pubkey()), 0);
|
||||
// Validator has zero balance, so they get filtered out. Only the bootstrap leader
|
||||
// Validator has no token staked, so they get filtered out. Only the bootstrap leader
|
||||
// created by the genesis block will get included
|
||||
let expected: Vec<_> = epoch_stakes_and_lockouts(&bank, 0)
|
||||
.values()
|
||||
@@ -179,11 +176,11 @@ mod tests {
|
||||
.collect();
|
||||
assert_eq!(expected, vec![(1, None)]);
|
||||
|
||||
voting_keypair_tests::new_vote_account_with_vote(&mint_keypair, &bank_voter, &bank, 1, 0);
|
||||
voting_keypair_tests::new_vote_account_with_vote(&mint_keypair, &bank_voter, &bank, 499, 0);
|
||||
|
||||
let result: HashSet<_> =
|
||||
HashSet::from_iter(epoch_stakes_and_lockouts(&bank, 0).values().cloned());
|
||||
let expected: HashSet<_> = HashSet::from_iter(vec![(1, None), (498, None)]);
|
||||
let expected: HashSet<_> = HashSet::from_iter(vec![(1, None), (499, None)]);
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
|
@@ -590,8 +590,13 @@ mod tests {
|
||||
let vote_account_id = validator_vote_account_keypair.pubkey();
|
||||
let last_id = client.get_last_id();
|
||||
|
||||
let transaction =
|
||||
VoteTransaction::new_account(&validator_keypair, vote_account_id, last_id, 1, 1);
|
||||
let transaction = VoteTransaction::fund_staking_account(
|
||||
&validator_keypair,
|
||||
vote_account_id,
|
||||
last_id,
|
||||
1,
|
||||
1,
|
||||
);
|
||||
let signature = client.transfer_signed(&transaction).unwrap();
|
||||
client.poll_for_signature(&signature).unwrap();
|
||||
|
||||
@@ -608,7 +613,8 @@ mod tests {
|
||||
|
||||
let vote_state = VoteState::deserialize(&account_user_data);
|
||||
|
||||
if vote_state.map(|vote_state| vote_state.node_id) == Ok(validator_keypair.pubkey()) {
|
||||
if vote_state.map(|vote_state| vote_state.delegate_id) == Ok(validator_keypair.pubkey())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
|
@@ -110,7 +110,13 @@ pub mod tests {
|
||||
num_tokens: u64,
|
||||
) {
|
||||
let last_id = bank.last_id();
|
||||
let tx = VoteTransaction::new_account(from_keypair, *voting_pubkey, last_id, num_tokens, 0);
|
||||
let tx = VoteTransaction::fund_staking_account(
|
||||
from_keypair,
|
||||
*voting_pubkey,
|
||||
last_id,
|
||||
num_tokens,
|
||||
0,
|
||||
);
|
||||
bank.process_transaction(&tx).unwrap();
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user