Vote contract (#1552)

* Add Vote Contract

* Move ownership of LeaderScheduler from Fullnode to the bank

* Modified ReplicateStage to consume leader information from bank

* Restart RPC Services in Leader To Validator Transition

* Make VoteContract Context Free

* Remove voting from ClusterInfo and Tpu

* Remove dependency on ActiveValidators in LeaderScheduler

* Switch VoteContract to have two steps 1) Register 2) Vote. Change thin client to create + register a voting account on fullnode startup

* Remove check in leader_to_validator transition for unique references to bank, b/c jsonrpc service and rpcpubsub hold references through jsonhttpserver
This commit is contained in:
carllin
2018-10-25 16:58:40 -07:00
committed by GitHub
parent 160cff4a30
commit f6c8e1a4bf
27 changed files with 1190 additions and 1195 deletions

View File

@@ -4,82 +4,24 @@
use bank::Bank;
use bincode::serialize;
use budget_instruction::Vote;
use budget_transaction::BudgetTransaction;
use byteorder::{LittleEndian, ReadBytesExt};
use entry::Entry;
use hash::{hash, Hash};
use ledger::create_ticks;
use signature::{Keypair, KeypairUtil};
#[cfg(test)]
use solana_sdk::account::Account;
use solana_sdk::pubkey::Pubkey;
use std::collections::HashMap;
use std::collections::HashSet;
use std::io::Cursor;
use system_transaction::SystemTransaction;
use transaction::Transaction;
use vote_program::{Vote, VoteProgram};
use vote_transaction::VoteTransaction;
pub const DEFAULT_BOOTSTRAP_HEIGHT: u64 = 1000;
pub const DEFAULT_LEADER_ROTATION_INTERVAL: u64 = 100;
pub const DEFAULT_SEED_ROTATION_INTERVAL: u64 = 1000;
pub const DEFAULT_ACTIVE_WINDOW_LENGTH: u64 = 1000;
#[derive(Debug)]
pub struct ActiveValidators {
// Map from validator id to the last PoH height at which they voted,
pub active_validators: HashMap<Pubkey, u64>,
pub active_window_length: u64,
}
impl ActiveValidators {
pub fn new(active_window_length_option: Option<u64>) -> Self {
let mut active_window_length = DEFAULT_ACTIVE_WINDOW_LENGTH;
if let Some(input) = active_window_length_option {
active_window_length = input;
}
ActiveValidators {
active_validators: HashMap::new(),
active_window_length,
}
}
// Finds all the active voters who have voted in the range
// (height - active_window_length, height], and removes
// anybody who hasn't voted in that range from the map
pub fn get_active_set(&mut self, height: u64) -> Vec<Pubkey> {
// Don't filter anything if height is less than the
// size of the active window. Otherwise, calculate the acceptable
// window and filter the active_validators
// Note: height == 0 will only be included for all
// height < self.active_window_length
let upper_bound = height;
if height >= self.active_window_length {
let lower_bound = height - self.active_window_length;
self.active_validators
.retain(|_, height| *height > lower_bound);
}
self.active_validators
.iter()
.filter_map(|(k, v)| if *v <= upper_bound { Some(*k) } else { None })
.collect()
}
// Push a vote for a validator with id == "id" who voted at PoH height == "height"
pub fn push_vote(&mut self, id: Pubkey, height: u64) -> () {
let old_height = self.active_validators.entry(id).or_insert(height);
if height > *old_height {
*old_height = height;
}
}
pub fn reset(&mut self) -> () {
self.active_validators.clear();
}
}
pub struct LeaderSchedulerConfig {
// The first leader who will bootstrap the network
pub bootstrap_leader: Pubkey,
@@ -119,7 +61,7 @@ impl LeaderSchedulerConfig {
}
}
#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct LeaderScheduler {
// Set to true if we want the default implementation of the LeaderScheduler,
// where ony the bootstrap leader is used
@@ -139,12 +81,13 @@ pub struct LeaderScheduler {
// the leader rotation process begins to pick future leaders
pub bootstrap_height: u64,
// Maintain the set of active validators
pub active_validators: ActiveValidators,
// The last height at which the seed + schedule was generated
pub last_seed_height: Option<u64>,
// The length of time in ticks for which a vote qualifies a candidate for leader
// selection
pub active_window_length: u64,
// Round-robin ordering for the validators
leader_schedule: Vec<Pubkey>,
@@ -193,6 +136,11 @@ impl LeaderScheduler {
seed_rotation_interval = input;
}
let mut active_window_length = DEFAULT_ACTIVE_WINDOW_LENGTH;
if let Some(input) = config.active_window_length_option {
active_window_length = input;
}
// Enforced invariants
assert!(seed_rotation_interval >= leader_rotation_interval);
assert!(bootstrap_height > 0);
@@ -200,13 +148,13 @@ impl LeaderScheduler {
LeaderScheduler {
use_only_bootstrap_leader: false,
active_validators: ActiveValidators::new(config.active_window_length_option),
leader_rotation_interval,
seed_rotation_interval,
leader_schedule: Vec::new(),
last_seed_height: None,
bootstrap_leader: config.bootstrap_leader,
bootstrap_height,
active_window_length,
seed: 0,
}
}
@@ -280,15 +228,6 @@ impl LeaderScheduler {
pub fn reset(&mut self) {
self.last_seed_height = None;
self.active_validators.reset();
}
pub fn push_vote(&mut self, id: Pubkey, height: u64) {
if self.use_only_bootstrap_leader {
return;
}
self.active_validators.push_vote(id, height);
}
pub fn update_height(&mut self, height: u64, bank: &Bank) {
@@ -343,8 +282,34 @@ impl LeaderScheduler {
Some(self.leader_schedule[validator_index])
}
fn get_active_set(&mut self, height: u64) -> Vec<Pubkey> {
self.active_validators.get_active_set(height)
// TODO: We use a HashSet for now because a single validator could potentially register
// multiple vote account. Once that is no longer possible (see the TODO in vote_program.rs,
// process_transaction(), case VoteInstruction::RegisterAccount), we can use a vector.
fn get_active_set(&mut self, height: u64, bank: &Bank) -> HashSet<Pubkey> {
let upper_bound = height;
let lower_bound = height.saturating_sub(self.active_window_length);
{
let bank_accounts = &*bank.accounts.read().unwrap();
bank_accounts
.values()
.filter_map(|account| {
if VoteProgram::check_id(&account.program_id) {
if let Ok(vote_state) = VoteProgram::deserialize(&account.userdata) {
return vote_state
.votes
.back()
.filter(|vote| {
vote.tick_height > lower_bound
&& vote.tick_height <= upper_bound
}).map(|_| vote_state.node_id);
}
}
None
}).collect()
}
}
// Called every seed_rotation_interval entries, generates the leader schedule
@@ -354,8 +319,8 @@ impl LeaderScheduler {
assert!((height - self.bootstrap_height) % self.seed_rotation_interval == 0);
let seed = Self::calculate_seed(height);
self.seed = seed;
let active_set = self.get_active_set(height);
let ranked_active_set = Self::rank_active_set(bank, &active_set[..]);
let active_set = self.get_active_set(height, &bank);
let ranked_active_set = Self::rank_active_set(bank, active_set.iter());
// Handle case where there are no active validators with
// non-zero stake. In this case, use the bootstrap leader for
@@ -417,9 +382,11 @@ impl LeaderScheduler {
bank.get_balance(id)
}
fn rank_active_set<'a>(bank: &Bank, active: &'a [Pubkey]) -> Vec<(&'a Pubkey, u64)> {
fn rank_active_set<'a, I>(bank: &Bank, active: I) -> Vec<(&'a Pubkey, u64)>
where
I: Iterator<Item = &'a Pubkey>,
{
let mut active_accounts: Vec<(&'a Pubkey, u64)> = active
.iter()
.filter_map(|pk| {
let stake = Self::get_stake(pk, bank);
if stake > 0 {
@@ -478,24 +445,6 @@ impl Default for LeaderScheduler {
}
}
// Remove all candiates for leader selection from the active set by clearing the bank,
// and then set a single new candidate who will be eligible starting at height = vote_height
// by adding one new account to the bank
#[cfg(test)]
pub fn set_new_leader(bank: &Bank, leader_scheduler: &mut LeaderScheduler, vote_height: u64) {
// Set the scheduled next leader to some other node
let new_leader_keypair = Keypair::new();
let new_leader_id = new_leader_keypair.pubkey();
leader_scheduler.push_vote(new_leader_id, vote_height);
let dummy_id = Keypair::new().pubkey();
let new_account = Account::new(1, 10, dummy_id.clone());
// Remove the previous acounts from the active set
let mut accounts = bank.accounts().write().unwrap();
accounts.clear();
accounts.insert(new_leader_id, new_account);
}
// Create two entries so that the node with keypair == active_keypair
// is in the active set for leader selection:
// 1) Give the node a nonzero number of tokens,
@@ -506,50 +455,107 @@ pub fn make_active_set_entries(
last_entry_id: &Hash,
last_tick_id: &Hash,
num_ending_ticks: usize,
) -> Vec<Entry> {
) -> (Vec<Entry>, Keypair) {
// 1) Create transfer token entry
let transfer_tx =
Transaction::system_new(&token_source, active_keypair.pubkey(), 1, *last_tick_id);
Transaction::system_new(&token_source, active_keypair.pubkey(), 2, *last_tick_id);
let transfer_entry = Entry::new(last_entry_id, 1, vec![transfer_tx]);
let mut last_entry_id = transfer_entry.id;
// 2) Create vote entry
let vote = Vote {
version: 0,
contact_info_version: 0,
};
let vote_tx = Transaction::budget_new_vote(&active_keypair, vote, *last_tick_id, 0);
// 2) Create the vote account
let vote_account = Keypair::new();
let create_vote_account_tx =
Transaction::vote_account_new(active_keypair, vote_account.pubkey(), *last_tick_id, 1);
let create_vote_account_entry = Entry::new(&last_entry_id, 1, vec![create_vote_account_tx]);
last_entry_id = create_vote_account_entry.id;
// 3) Register the vote account
let register_vote_account_tx =
Transaction::vote_account_register(active_keypair, vote_account.pubkey(), *last_tick_id, 0);
let register_vote_account_entry = Entry::new(&last_entry_id, 1, vec![register_vote_account_tx]);
last_entry_id = register_vote_account_entry.id;
// 4) Create vote entry
let vote = Vote { tick_height: 1 };
let vote_tx = Transaction::vote_new(&vote_account, vote, *last_tick_id, 0);
let vote_entry = Entry::new(&last_entry_id, 1, vec![vote_tx]);
last_entry_id = vote_entry.id;
// 3) Create the ending empty ticks
let mut txs = vec![transfer_entry, vote_entry];
// 5) Create the ending empty ticks
let mut txs = vec![
transfer_entry,
create_vote_account_entry,
register_vote_account_entry,
vote_entry,
];
let empty_ticks = create_ticks(num_ending_ticks, last_entry_id);
txs.extend(empty_ticks);
txs
(txs, vote_account)
}
#[cfg(test)]
mod tests {
use bank::Bank;
use hash::Hash;
use leader_scheduler::{
ActiveValidators, LeaderScheduler, LeaderSchedulerConfig, DEFAULT_ACTIVE_WINDOW_LENGTH,
DEFAULT_BOOTSTRAP_HEIGHT, DEFAULT_LEADER_ROTATION_INTERVAL, DEFAULT_SEED_ROTATION_INTERVAL,
LeaderScheduler, LeaderSchedulerConfig, DEFAULT_BOOTSTRAP_HEIGHT,
DEFAULT_LEADER_ROTATION_INTERVAL, DEFAULT_SEED_ROTATION_INTERVAL,
};
use mint::Mint;
use result::Result;
use signature::{Keypair, KeypairUtil};
use solana_sdk::pubkey::Pubkey;
use std::collections::HashSet;
use std::hash::Hash;
use std::hash::Hash as StdHash;
use std::iter::FromIterator;
use transaction::Transaction;
use vote_program::Vote;
use vote_transaction::VoteTransaction;
fn to_hashset_owned<T>(slice: &[T]) -> HashSet<T>
where
T: Eq + Hash + Clone,
T: Eq + StdHash + Clone,
{
HashSet::from_iter(slice.iter().cloned())
}
fn push_vote(vote_account: &Keypair, bank: &Bank, height: u64, last_id: Hash) {
let vote = Vote {
tick_height: height,
};
let new_vote_tx = Transaction::vote_new(vote_account, vote, last_id, 0);
bank.process_transaction(&new_vote_tx).unwrap();
}
fn create_vote_account(
node_keypair: &Keypair,
bank: &Bank,
num_tokens: i64,
last_id: Hash,
) -> Result<Keypair> {
let new_vote_account = Keypair::new();
// Create the new vote account
let tx = Transaction::vote_account_new(
node_keypair,
new_vote_account.pubkey(),
last_id,
num_tokens,
);
bank.process_transaction(&tx)?;
// Register the vote account to the validator
let tx =
Transaction::vote_account_register(node_keypair, new_vote_account.pubkey(), last_id, 0);
bank.process_transaction(&tx)?;
Ok(new_vote_account)
}
fn run_scheduler_test(
num_validators: usize,
bootstrap_height: u64,
@@ -572,7 +578,11 @@ mod tests {
let mut leader_scheduler = LeaderScheduler::new(&leader_scheduler_config);
// Create the bank and validators, which are inserted in order of account balance
let mint = Mint::new((((num_validators + 1) / 2) * (num_validators + 1)) as i64);
let num_vote_account_tokens = 1;
let mint = Mint::new(
(((num_validators + 1) / 2) * (num_validators + 1)
+ num_vote_account_tokens * num_validators) as i64,
);
let bank = Bank::new(&mint);
let mut validators = vec![];
let last_id = mint
@@ -584,11 +594,24 @@ mod tests {
let new_validator = Keypair::new();
let new_pubkey = new_validator.pubkey();
validators.push(new_pubkey);
// Give the validator some tokens
bank.transfer(
(i + 1 + num_vote_account_tokens) as i64,
&mint.keypair(),
new_pubkey,
last_id,
).unwrap();
// Create a vote account
let new_vote_account = create_vote_account(
&new_validator,
&bank,
num_vote_account_tokens as i64,
mint.last_id(),
).unwrap();
// Vote to make the validator part of the active set for the entire test
// (we made the active_window_length large enough at the beginning of the test)
leader_scheduler.push_vote(new_pubkey, 1);
bank.transfer((i + 1) as i64, &mint.keypair(), new_pubkey, last_id)
.unwrap();
push_vote(&new_vote_account, &bank, 1, mint.last_id());
}
// The scheduled leader during the bootstrapping period (assuming a seed + schedule
@@ -666,6 +689,9 @@ mod tests {
fn test_active_set() {
let leader_id = Keypair::new().pubkey();
let active_window_length = 1000;
let mint = Mint::new(10000);
let bank = Bank::new(&mint);
let leader_scheduler_config = LeaderSchedulerConfig::new(
leader_id,
Some(100),
@@ -681,40 +707,60 @@ mod tests {
let num_old_ids = 20;
let mut old_ids = HashSet::new();
for _ in 0..num_old_ids {
let pk = Keypair::new().pubkey();
old_ids.insert(pk);
leader_scheduler.push_vote(pk, start_height);
let new_keypair = Keypair::new();
let pk = new_keypair.pubkey();
old_ids.insert(pk.clone());
// Give the account some stake
bank.transfer(5, &mint.keypair(), pk, mint.last_id())
.unwrap();
// Create a vote account
let new_vote_account =
create_vote_account(&new_keypair, &bank, 1, mint.last_id()).unwrap();
// Push a vote for the account
push_vote(&new_vote_account, &bank, start_height, mint.last_id());
}
// Insert a bunch of votes at height "start_height + active_window_length"
let num_new_ids = 10;
let mut new_ids = HashSet::new();
for _ in 0..num_new_ids {
let pk = Keypair::new().pubkey();
let new_keypair = Keypair::new();
let pk = new_keypair.pubkey();
new_ids.insert(pk);
leader_scheduler.push_vote(pk, start_height + active_window_length);
// Give the account some stake
bank.transfer(5, &mint.keypair(), pk, mint.last_id())
.unwrap();
// Create a vote account
let new_vote_account =
create_vote_account(&new_keypair, &bank, 1, mint.last_id()).unwrap();
push_vote(
&new_vote_account,
&bank,
start_height + active_window_length,
mint.last_id(),
);
}
// Queries for the active set
let result = leader_scheduler.get_active_set(active_window_length + start_height - 1);
assert_eq!(result.len(), num_old_ids);
let result_set = to_hashset_owned(&result);
assert_eq!(result_set, old_ids);
let result =
leader_scheduler.get_active_set(active_window_length + start_height - 1, &bank);
assert_eq!(result, old_ids);
let result = leader_scheduler.get_active_set(active_window_length + start_height);
assert_eq!(result.len(), num_new_ids);
let result_set = to_hashset_owned(&result);
assert_eq!(result_set, new_ids);
let result = leader_scheduler.get_active_set(active_window_length + start_height, &bank);
assert_eq!(result, new_ids);
let result = leader_scheduler.get_active_set(2 * active_window_length + start_height - 1);
assert_eq!(result.len(), num_new_ids);
let result_set = to_hashset_owned(&result);
assert_eq!(result_set, new_ids);
let result =
leader_scheduler.get_active_set(2 * active_window_length + start_height - 1, &bank);
assert_eq!(result, new_ids);
let result = leader_scheduler.get_active_set(2 * active_window_length + start_height);
assert_eq!(result.len(), 0);
let result_set = to_hashset_owned(&result);
assert!(result_set.is_empty());
let result =
leader_scheduler.get_active_set(2 * active_window_length + start_height, &bank);
assert!(result.is_empty());
}
#[test]
@@ -754,7 +800,7 @@ mod tests {
}
let validators_pk: Vec<Pubkey> = validators.iter().map(Keypair::pubkey).collect();
let result = LeaderScheduler::rank_active_set(&bank, &validators_pk[..]);
let result = LeaderScheduler::rank_active_set(&bank, validators_pk.iter());
assert_eq!(result.len(), validators.len());
@@ -784,7 +830,7 @@ mod tests {
.chain(new_validators.iter())
.map(Keypair::pubkey)
.collect();
let result = LeaderScheduler::rank_active_set(&bank, &all_validators[..]);
let result = LeaderScheduler::rank_active_set(&bank, all_validators.iter());
assert_eq!(result.len(), new_validators.len());
for (i, (pk, balance)) in result.into_iter().enumerate() {
@@ -810,7 +856,7 @@ mod tests {
.unwrap();
}
let result = LeaderScheduler::rank_active_set(&bank, &tied_validators_pk[..]);
let result = LeaderScheduler::rank_active_set(&bank, tied_validators_pk.iter());
let mut sorted: Vec<&Pubkey> = tied_validators_pk.iter().map(|x| x).collect();
sorted.sort_by(|pk1, pk2| pk1.cmp(pk2));
assert_eq!(result.len(), tied_validators_pk.len());
@@ -922,6 +968,7 @@ mod tests {
#[test]
fn test_scheduler_active_window() {
let num_validators = 10;
let num_vote_account_tokens = 1;
// Set up the LeaderScheduler struct
let bootstrap_leader_id = Keypair::new().pubkey();
let bootstrap_height = 500;
@@ -943,7 +990,10 @@ mod tests {
let mut leader_scheduler = LeaderScheduler::new(&leader_scheduler_config);
// Create the bank and validators
let mint = Mint::new((((num_validators + 1) / 2) * (num_validators + 1)) as i64);
let mint = Mint::new(
((((num_validators + 1) / 2) * (num_validators + 1))
+ (num_vote_account_tokens * num_validators)) as i64,
);
let bank = Bank::new(&mint);
let mut validators = vec![];
let last_id = mint
@@ -955,10 +1005,29 @@ mod tests {
let new_validator = Keypair::new();
let new_pubkey = new_validator.pubkey();
validators.push(new_pubkey);
// Give the validator some tokens
bank.transfer(
(i + 1 + num_vote_account_tokens) as i64,
&mint.keypair(),
new_pubkey,
last_id,
).unwrap();
// Create a vote account
let new_vote_account = create_vote_account(
&new_validator,
&bank,
num_vote_account_tokens as i64,
mint.last_id(),
).unwrap();
// Vote at height i * active_window_length for validator i
leader_scheduler.push_vote(new_pubkey, i * active_window_length + bootstrap_height);
bank.transfer((i + 1) as i64, &mint.keypair(), new_pubkey, last_id)
.unwrap();
push_vote(
&new_vote_account,
&bank,
i * active_window_length + bootstrap_height,
mint.last_id(),
);
}
// Generate schedule every active_window_length entries and check that
@@ -979,8 +1048,12 @@ mod tests {
#[test]
fn test_multiple_vote() {
let leader_id = Keypair::new().pubkey();
let leader_keypair = Keypair::new();
let leader_id = leader_keypair.pubkey();
let active_window_length = 1000;
let mint = Mint::new(10000);
let bank = Bank::new(&mint);
let leader_scheduler_config = LeaderSchedulerConfig::new(
leader_id,
Some(100),
@@ -991,18 +1064,38 @@ mod tests {
let mut leader_scheduler = LeaderScheduler::new(&leader_scheduler_config);
// Check that a validator that votes twice in a row will get included in the active
// Give the node some tokens
bank.transfer(5, &mint.keypair(), leader_id, bank.last_id())
.unwrap();
// Check that a node that votes twice in a row will get included in the active
// window
let initial_vote_height = 1;
// Create a vote account
let new_vote_account =
create_vote_account(&leader_keypair, &bank, 1, mint.last_id()).unwrap();
// Vote twice
leader_scheduler.push_vote(leader_id, initial_vote_height);
leader_scheduler.push_vote(leader_id, initial_vote_height + 1);
let result = leader_scheduler.get_active_set(initial_vote_height + active_window_length);
assert_eq!(result, vec![leader_id]);
push_vote(
&new_vote_account,
&bank,
initial_vote_height,
mint.last_id(),
);
push_vote(
&new_vote_account,
&bank,
initial_vote_height + 1,
mint.last_id(),
);
let result =
leader_scheduler.get_active_set(initial_vote_height + active_window_length + 1);
assert_eq!(result, vec![]);
leader_scheduler.get_active_set(initial_vote_height + active_window_length, &bank);
assert_eq!(result, to_hashset_owned(&vec![leader_id]));
let result =
leader_scheduler.get_active_set(initial_vote_height + active_window_length + 1, &bank);
assert!(result.is_empty());
}
#[test]
@@ -1063,13 +1156,6 @@ mod tests {
DEFAULT_SEED_ROTATION_INTERVAL
);
// Check defaults for ActiveValidators
let active_validators = ActiveValidators::new(None);
assert_eq!(
active_validators.active_window_length,
DEFAULT_ACTIVE_WINDOW_LENGTH
);
// Check actual arguments for LeaderScheduler
let bootstrap_height = 500;
let leader_rotation_interval = 100;
@@ -1096,14 +1182,11 @@ mod tests {
leader_scheduler.seed_rotation_interval,
seed_rotation_interval
);
// Check actual arguments for ActiveValidators
let active_validators = ActiveValidators::new(Some(active_window_length));
assert_eq!(active_validators.active_window_length, active_window_length);
}
fn run_consecutive_leader_test(num_slots_per_epoch: u64, add_validator: bool) {
let bootstrap_leader_id = Keypair::new().pubkey();
let bootstrap_leader_keypair = Keypair::new();
let bootstrap_leader_id = bootstrap_leader_keypair.pubkey();
let bootstrap_height = 500;
let leader_rotation_interval = 100;
let seed_rotation_interval = num_slots_per_epoch * leader_rotation_interval;
@@ -1130,11 +1213,20 @@ mod tests {
let initial_vote_height = 1;
// Create and add validator to the active set
let validator_id = Keypair::new().pubkey();
let validator_keypair = Keypair::new();
let validator_id = validator_keypair.pubkey();
if add_validator {
leader_scheduler.push_vote(validator_id, initial_vote_height);
bank.transfer(1, &mint.keypair(), validator_id, last_id)
bank.transfer(5, &mint.keypair(), validator_id, last_id)
.unwrap();
// Create a vote account
let new_vote_account =
create_vote_account(&validator_keypair, &bank, 1, mint.last_id()).unwrap();
push_vote(
&new_vote_account,
&bank,
initial_vote_height,
mint.last_id(),
);
}
// Make sure the bootstrap leader, not the validator, is picked again on next slot
@@ -1151,10 +1243,29 @@ mod tests {
}
};
let vote_account_tokens = 1;
bank.transfer(
leader_stake + vote_account_tokens,
&mint.keypair(),
bootstrap_leader_id,
last_id,
).unwrap();
// Create a vote account
let new_vote_account = create_vote_account(
&bootstrap_leader_keypair,
&bank,
vote_account_tokens,
mint.last_id(),
).unwrap();
// Add leader to the active set
leader_scheduler.push_vote(bootstrap_leader_id, initial_vote_height);
bank.transfer(leader_stake, &mint.keypair(), bootstrap_leader_id, last_id)
.unwrap();
push_vote(
&new_vote_account,
&bank,
initial_vote_height,
mint.last_id(),
);
leader_scheduler.generate_schedule(bootstrap_height, &bank);
@@ -1182,7 +1293,8 @@ mod tests {
#[test]
fn test_max_height_for_leader() {
let bootstrap_leader_id = Keypair::new().pubkey();
let bootstrap_leader_keypair = Keypair::new();
let bootstrap_leader_id = bootstrap_leader_keypair.pubkey();
let bootstrap_height = 500;
let leader_rotation_interval = 100;
let seed_rotation_interval = 2 * leader_rotation_interval;
@@ -1254,15 +1366,34 @@ mod tests {
// Now test when the active set > 1 node
// Create and add validator to the active set
let validator_id = Keypair::new().pubkey();
leader_scheduler.push_vote(validator_id, initial_vote_height);
bank.transfer(1, &mint.keypair(), validator_id, last_id)
let validator_keypair = Keypair::new();
let validator_id = validator_keypair.pubkey();
// Create a vote account for the validator
bank.transfer(5, &mint.keypair(), validator_id, last_id)
.unwrap();
let new_validator_vote_account =
create_vote_account(&validator_keypair, &bank, 1, mint.last_id()).unwrap();
push_vote(
&new_validator_vote_account,
&bank,
initial_vote_height,
mint.last_id(),
);
// Create a vote account for the leader
bank.transfer(5, &mint.keypair(), bootstrap_leader_id, last_id)
.unwrap();
let new_leader_vote_account =
create_vote_account(&bootstrap_leader_keypair, &bank, 1, mint.last_id()).unwrap();
// Add leader to the active set
leader_scheduler.push_vote(bootstrap_leader_id, initial_vote_height);
bank.transfer(1, &mint.keypair(), bootstrap_leader_id, last_id)
.unwrap();
push_vote(
&new_leader_vote_account,
&bank,
initial_vote_height,
mint.last_id(),
);
// Generate the schedule
leader_scheduler.generate_schedule(bootstrap_height, &bank);