Hoist new leader scheduler up to protocol level

Attempt to feel similar to LeaderScheduler to easy migration.
This commit is contained in:
Greg Fitzgerald 2019-02-26 08:44:24 -07:00 committed by Grimes
parent ba50e1ac81
commit af206111e2
7 changed files with 146 additions and 123 deletions

1
Cargo.lock generated
View File

@ -2251,7 +2251,6 @@ dependencies = [
"hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -15,7 +15,6 @@ fnv = "1.0.6"
hashbrown = "0.1.8"
log = "0.4.2"
rand = "0.6.5"
rand_chacha = "0.1.1"
serde = "1.0.88"
serde_derive = "1.0.88"
serde_json = "1.0.38"

View File

@ -5,7 +5,6 @@
use crate::accounts::{Accounts, ErrorCounters, InstructionAccounts, InstructionLoaders};
use crate::last_id_queue::LastIdQueue;
use crate::leader_schedule::LeaderSchedule;
use crate::runtime::{self, RuntimeError};
use crate::status_cache::StatusCache;
use bincode::serialize;
@ -723,7 +722,9 @@ impl Bank {
}
/// Return the checkpointed stakes that should be used to generate a leader schedule.
fn staked_nodes_at_slot(&self, slot_height: u64) -> HashMap<Pubkey, u64> {
fn staked_nodes_at_slot(&self, current_slot_height: u64) -> HashMap<Pubkey, u64> {
let slot_height = current_slot_height.saturating_sub(self.stakers_slot_offset);
let parents = self.parents();
let mut banks = vec![self];
banks.extend(parents.iter().map(|x| x.as_ref()));
@ -741,87 +742,6 @@ impl Bank {
self.ticks_per_slot
}
/// Return the number of slots per tick that should be used calls to epoch_height().
pub fn slots_per_epoch(&self) -> u64 {
self.slots_per_epoch
}
/// Return the checkpointed stakes that should be used to generate a leader schedule.
fn staked_nodes_at_epoch(&self, epoch_height: u64) -> HashMap<Pubkey, u64> {
let epoch_slot_height = epoch_height * self.slots_per_epoch();
let slot_height = epoch_slot_height.saturating_sub(self.stakers_slot_offset);
self.staked_nodes_at_slot(slot_height)
}
/// Return the leader schedule for the given epoch.
fn leader_schedule(&self, epoch_height: u64) -> LeaderSchedule {
let stakes = self.staked_nodes_at_epoch(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();
LeaderSchedule::new(&stakes, seed, self.slots_per_epoch())
}
/// Return the leader for the slot at the slot_index and epoch_height returned
/// by the given function.
pub fn slot_leader_by<F>(&self, get_slot_index: F) -> Pubkey
where
F: Fn(u64, u64, u64) -> (u64, u64),
{
let (slot_index, epoch_height) = get_slot_index(
self.slot_index(),
self.epoch_height(),
self.slots_per_epoch(),
);
let leader_schedule = self.leader_schedule(epoch_height);
leader_schedule[slot_index as usize]
}
/// Return the leader for the current slot.
pub fn slot_leader(&self) -> Pubkey {
self.slot_leader_by(|slot_index, epoch_height, _| (slot_index, epoch_height))
}
/// Return the epoch height and slot index of the slot before the current slot.
fn prev_slot_leader_index(
slot_index: u64,
epoch_height: u64,
slots_per_epoch: u64,
) -> (u64, u64) {
if epoch_height == 0 && slot_index == 0 {
return (0, 0);
}
if slot_index == 0 {
(slots_per_epoch - 1, epoch_height - 1)
} else {
(slot_index - 1, epoch_height)
}
}
/// Return the slot_index and epoch height of the slot following the current slot.
fn next_slot_leader_index(
slot_index: u64,
epoch_height: u64,
slots_per_epoch: u64,
) -> (u64, u64) {
if slot_index + 1 == slots_per_epoch {
(0, epoch_height + 1)
} else {
(slot_index + 1, epoch_height)
}
}
/// Return the leader for the slot before the current slot.
pub fn prev_slot_leader(&self) -> Pubkey {
self.slot_leader_by(Self::prev_slot_leader_index)
}
/// Return the leader for the slot following the current slot.
pub fn next_slot_leader(&self) -> Pubkey {
self.slot_leader_by(Self::next_slot_leader_index)
}
/// Return the number of ticks since genesis.
pub fn tick_height(&self) -> u64 {
self.last_id_queue.read().unwrap().tick_height()
@ -837,6 +757,17 @@ impl Bank {
self.tick_height() / self.ticks_per_slot()
}
/// Return the number of slots per tick.
pub fn slots_per_epoch(&self) -> u64 {
self.slots_per_epoch
}
/// Return the checkpointed stakes that should be used to generate a leader schedule.
pub fn staked_nodes_at_epoch(&self, epoch_height: u64) -> HashMap<Pubkey, u64> {
let epoch_slot_height = epoch_height * self.slots_per_epoch();
self.staked_nodes_at_slot(epoch_slot_height)
}
/// Return the number of slots since the last epoch boundary.
pub fn slot_index(&self) -> u64 {
self.slot_height() % self.slots_per_epoch()
@ -1262,23 +1193,7 @@ mod tests {
let mut expected = HashMap::new();
expected.insert(pubkey, bootstrap_tokens - 1);
let bank = Bank::new_from_parent(&Arc::new(bank));
assert_eq!(bank.staked_nodes_at_epoch(bank.epoch_height()), expected,);
}
#[test]
fn test_bank_leader_schedule_basic() {
let pubkey = Keypair::new().pubkey();
let (genesis_block, _mint_keypair) = GenesisBlock::new_with_leader(2, pubkey, 2);
let bank = Bank::new(&genesis_block);
let ids_and_stakes: Vec<_> = bank.staked_nodes().into_iter().collect();
let mut seed = [0u8; 32];
seed[0..8].copy_from_slice(&bank.epoch_height().to_le_bytes());
let leader_schedule = LeaderSchedule::new(&ids_and_stakes, seed, bank.slots_per_epoch());
assert_eq!(leader_schedule[0], pubkey);
assert_eq!(leader_schedule[1], pubkey);
assert_eq!(leader_schedule[2], pubkey);
assert_eq!(bank.staked_nodes_at_slot(bank.slot_height()), expected);
}
#[test]
@ -1535,24 +1450,4 @@ mod tests {
bank.squash();
}
}
#[test]
fn test_bank_slot_leader_basic() {
let pubkey = Keypair::new().pubkey();
let bank = Bank::new(&GenesisBlock::new_with_leader(2, pubkey, 2).0);
assert_eq!(bank.slot_leader(), pubkey);
}
#[test]
fn test_bank_prev_slot_leader_index() {
assert_eq!(Bank::prev_slot_leader_index(0, 0, 2), (0, 0));
assert_eq!(Bank::prev_slot_leader_index(1, 0, 2), (0, 0));
assert_eq!(Bank::prev_slot_leader_index(0, 1, 2), (1, 0));
}
#[test]
fn test_bank_next_slot_leader_index() {
assert_eq!(Bank::next_slot_leader_index(0, 0, 2), (1, 0));
assert_eq!(Bank::next_slot_leader_index(1, 0, 2), (0, 1));
}
}

View File

@ -2,7 +2,6 @@ mod accounts;
pub mod bank;
pub mod bloom;
mod last_id_queue;
mod leader_schedule;
mod runtime;
mod status_cache;

129
src/leader_scheduler1.rs Normal file
View File

@ -0,0 +1,129 @@
//! The `bank` module tracks client accounts and the progress of on-chain
//! programs. It offers a high-level API that signs transactions
//! on behalf of the caller, and a low-level API for when they have
//! already been signed and verified.
use crate::leader_schedule::LeaderSchedule;
use solana_runtime::bank::Bank;
use solana_sdk::pubkey::Pubkey;
#[derive(Default)]
pub struct LeaderScheduler1 {}
impl LeaderScheduler1 {
/// Return the leader schedule for the given epoch.
fn leader_schedule(&self, epoch_height: u64, bank: &Bank) -> LeaderSchedule {
let stakes = bank.staked_nodes_at_epoch(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();
LeaderSchedule::new(&stakes, seed, bank.slots_per_epoch())
}
/// Return the leader for the slot at the slot_index and epoch_height returned
/// by the given function.
pub fn slot_leader_by<F>(&self, bank: &Bank, get_slot_index: F) -> Pubkey
where
F: Fn(u64, u64, u64) -> (u64, u64),
{
let (slot_index, epoch_height) = get_slot_index(
bank.slot_index(),
bank.epoch_height(),
bank.slots_per_epoch(),
);
let leader_schedule = self.leader_schedule(epoch_height, bank);
leader_schedule[slot_index as usize]
}
/// Return the leader for the current slot.
pub fn slot_leader(&self, bank: &Bank) -> Pubkey {
self.slot_leader_by(bank, |slot_index, epoch_height, _| {
(slot_index, epoch_height)
})
}
/// Return the epoch height and slot index of the slot before the current slot.
fn prev_slot_leader_index(
slot_index: u64,
epoch_height: u64,
slots_per_epoch: u64,
) -> (u64, u64) {
if epoch_height == 0 && slot_index == 0 {
return (0, 0);
}
if slot_index == 0 {
(slots_per_epoch - 1, epoch_height - 1)
} else {
(slot_index - 1, epoch_height)
}
}
/// Return the slot_index and epoch height of the slot following the current slot.
fn next_slot_leader_index(
slot_index: u64,
epoch_height: u64,
slots_per_epoch: u64,
) -> (u64, u64) {
if slot_index + 1 == slots_per_epoch {
(0, epoch_height + 1)
} else {
(slot_index + 1, epoch_height)
}
}
/// Return the leader for the slot before the current slot.
pub fn prev_slot_leader(&self, bank: &Bank) -> Pubkey {
self.slot_leader_by(bank, Self::prev_slot_leader_index)
}
/// Return the leader for the slot following the current slot.
pub fn next_slot_leader(&self, bank: &Bank) -> Pubkey {
self.slot_leader_by(bank, Self::next_slot_leader_index)
}
}
#[cfg(test)]
mod tests {
use super::*;
use solana_sdk::genesis_block::GenesisBlock;
use solana_sdk::signature::{Keypair, KeypairUtil};
#[test]
fn test_leader_schedule_via_bank() {
let pubkey = Keypair::new().pubkey();
let (genesis_block, _mint_keypair) = GenesisBlock::new_with_leader(2, pubkey, 2);
let bank = Bank::new(&genesis_block);
let ids_and_stakes: Vec<_> = bank.staked_nodes().into_iter().collect();
let seed = [0u8; 32];
let leader_schedule =
LeaderSchedule::new(&ids_and_stakes, seed, genesis_block.slots_per_epoch);
assert_eq!(leader_schedule[0], pubkey);
assert_eq!(leader_schedule[1], pubkey);
assert_eq!(leader_schedule[2], pubkey);
}
#[test]
fn test_leader_scheduler1_basic() {
let pubkey = Keypair::new().pubkey();
let genesis_block = GenesisBlock::new_with_leader(2, pubkey, 2).0;
let bank = Bank::new(&genesis_block);
let leader_scheduler = LeaderScheduler1::default();
assert_eq!(leader_scheduler.slot_leader(&bank), pubkey);
}
#[test]
fn test_leader_scheduler1_prev_slot_leader_index() {
assert_eq!(LeaderScheduler1::prev_slot_leader_index(0, 0, 2), (0, 0));
assert_eq!(LeaderScheduler1::prev_slot_leader_index(1, 0, 2), (0, 0));
assert_eq!(LeaderScheduler1::prev_slot_leader_index(0, 1, 2), (1, 0));
}
#[test]
fn test_leader_scheduler1_next_slot_leader_index() {
assert_eq!(LeaderScheduler1::next_slot_leader_index(0, 0, 2), (1, 0));
assert_eq!(LeaderScheduler1::next_slot_leader_index(1, 0, 2), (0, 1));
}
}

View File

@ -39,7 +39,9 @@ pub mod fullnode;
pub mod gen_keys;
pub mod gossip_service;
pub mod leader_confirmation_service;
pub mod leader_schedule;
pub mod leader_scheduler;
pub mod leader_scheduler1;
pub mod local_vote_signer_service;
pub mod packet;
pub mod poh;