solana-validator now supports multiple --authorized-voter arguments (#9174)
* Use Epoch type * Vote account's authorized voter is now supported without a validator restart
This commit is contained in:
@ -33,7 +33,10 @@ use solana_sdk::{
|
||||
timing::{self, duration_as_ms},
|
||||
transaction::Transaction,
|
||||
};
|
||||
use solana_vote_program::vote_instruction;
|
||||
use solana_vote_program::{
|
||||
vote_instruction,
|
||||
vote_state::{Vote, VoteState},
|
||||
};
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
ops::Deref,
|
||||
@ -89,7 +92,7 @@ struct SkippedSlotsInfo {
|
||||
pub struct ReplayStageConfig {
|
||||
pub my_pubkey: Pubkey,
|
||||
pub vote_account: Pubkey,
|
||||
pub voting_keypair: Option<Arc<Keypair>>,
|
||||
pub authorized_voter_keypairs: Vec<Arc<Keypair>>,
|
||||
pub exit: Arc<AtomicBool>,
|
||||
pub subscriptions: Arc<RpcSubscriptions>,
|
||||
pub leader_schedule_cache: Arc<LeaderScheduleCache>,
|
||||
@ -121,7 +124,7 @@ impl ReplayStage {
|
||||
let ReplayStageConfig {
|
||||
my_pubkey,
|
||||
vote_account,
|
||||
voting_keypair,
|
||||
authorized_voter_keypairs,
|
||||
exit,
|
||||
subscriptions,
|
||||
leader_schedule_cache,
|
||||
@ -328,7 +331,7 @@ impl ReplayStage {
|
||||
&mut tower,
|
||||
&mut progress,
|
||||
&vote_account,
|
||||
&voting_keypair,
|
||||
&authorized_voter_keypairs,
|
||||
&cluster_info,
|
||||
&blockstore,
|
||||
&leader_schedule_cache,
|
||||
@ -706,8 +709,8 @@ impl ReplayStage {
|
||||
bank_forks: &Arc<RwLock<BankForks>>,
|
||||
tower: &mut Tower,
|
||||
progress: &mut ProgressMap,
|
||||
vote_account: &Pubkey,
|
||||
voting_keypair: &Option<Arc<Keypair>>,
|
||||
vote_account_pubkey: &Pubkey,
|
||||
authorized_voter_keypairs: &[Arc<Keypair>],
|
||||
cluster_info: &Arc<RwLock<ClusterInfo>>,
|
||||
blockstore: &Arc<Blockstore>,
|
||||
leader_schedule_cache: &Arc<LeaderScheduleCache>,
|
||||
@ -723,7 +726,7 @@ impl ReplayStage {
|
||||
inc_new_counter_info!("replay_stage-voted_empty_bank", 1);
|
||||
}
|
||||
trace!("handle votable bank {}", bank.slot());
|
||||
let (vote, tower_index) = tower.new_vote_from_bank(bank, vote_account);
|
||||
let (vote, tower_index) = tower.new_vote_from_bank(bank, vote_account_pubkey);
|
||||
if let Some(new_root) = tower.record_bank_vote(vote) {
|
||||
// get the root bank before squash
|
||||
let root_bank = bank_forks
|
||||
@ -771,30 +774,91 @@ impl ReplayStage {
|
||||
lockouts_sender,
|
||||
);
|
||||
|
||||
if let Some(ref voting_keypair) = voting_keypair {
|
||||
let node_keypair = cluster_info.read().unwrap().keypair.clone();
|
||||
|
||||
// Send our last few votes along with the new one
|
||||
let vote_ix = vote_instruction::vote(
|
||||
&vote_account,
|
||||
&voting_keypair.pubkey(),
|
||||
tower.last_vote_and_timestamp(),
|
||||
);
|
||||
|
||||
let mut vote_tx =
|
||||
Transaction::new_with_payer(vec![vote_ix], Some(&node_keypair.pubkey()));
|
||||
|
||||
let blockhash = bank.last_blockhash();
|
||||
vote_tx.partial_sign(&[node_keypair.as_ref()], blockhash);
|
||||
vote_tx.partial_sign(&[voting_keypair.as_ref()], blockhash);
|
||||
cluster_info
|
||||
.write()
|
||||
.unwrap()
|
||||
.push_vote(tower_index, vote_tx);
|
||||
}
|
||||
Self::push_vote(
|
||||
cluster_info,
|
||||
bank,
|
||||
vote_account_pubkey,
|
||||
authorized_voter_keypairs,
|
||||
tower.last_vote_and_timestamp(),
|
||||
tower_index,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn push_vote(
|
||||
cluster_info: &Arc<RwLock<ClusterInfo>>,
|
||||
bank: &Arc<Bank>,
|
||||
vote_account_pubkey: &Pubkey,
|
||||
authorized_voter_keypairs: &[Arc<Keypair>],
|
||||
vote: Vote,
|
||||
tower_index: usize,
|
||||
) {
|
||||
if authorized_voter_keypairs.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let vote_state =
|
||||
if let Some((_, vote_account)) = bank.vote_accounts().get(vote_account_pubkey) {
|
||||
if let Some(vote_state) = VoteState::from(&vote_account) {
|
||||
vote_state
|
||||
} else {
|
||||
warn!(
|
||||
"Vote account {} is unreadable. Unable to vote",
|
||||
vote_account_pubkey,
|
||||
);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
warn!(
|
||||
"Vote account {} does not exist. Unable to vote",
|
||||
vote_account_pubkey,
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
let authorized_voter_pubkey =
|
||||
if let Some(authorized_voter_pubkey) = vote_state.get_authorized_voter(bank.epoch()) {
|
||||
authorized_voter_pubkey
|
||||
} else {
|
||||
warn!(
|
||||
"Vote account {} has no authorized voter for epoch {}. Unable to vote",
|
||||
vote_account_pubkey,
|
||||
bank.epoch()
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
let authorized_voter_keypair = match authorized_voter_keypairs
|
||||
.iter()
|
||||
.find(|keypair| keypair.pubkey() == authorized_voter_pubkey)
|
||||
{
|
||||
None => {
|
||||
warn!("The authorized keypair {} for vote account {} is not available. Unable to vote",
|
||||
authorized_voter_pubkey, vote_account_pubkey);
|
||||
return;
|
||||
}
|
||||
Some(authorized_voter_keypair) => authorized_voter_keypair,
|
||||
};
|
||||
let node_keypair = cluster_info.read().unwrap().keypair.clone();
|
||||
|
||||
// Send our last few votes along with the new one
|
||||
let vote_ix = vote_instruction::vote(
|
||||
&vote_account_pubkey,
|
||||
&authorized_voter_keypair.pubkey(),
|
||||
vote,
|
||||
);
|
||||
|
||||
let mut vote_tx = Transaction::new_with_payer(vec![vote_ix], Some(&node_keypair.pubkey()));
|
||||
|
||||
let blockhash = bank.last_blockhash();
|
||||
vote_tx.partial_sign(&[node_keypair.as_ref()], blockhash);
|
||||
vote_tx.partial_sign(&[authorized_voter_keypair.as_ref()], blockhash);
|
||||
cluster_info
|
||||
.write()
|
||||
.unwrap()
|
||||
.push_vote(tower_index, vote_tx);
|
||||
}
|
||||
|
||||
fn update_commitment_cache(
|
||||
bank: Arc<Bank>,
|
||||
root: Slot,
|
||||
@ -1621,7 +1685,7 @@ pub(crate) mod tests {
|
||||
struct ValidatorInfo {
|
||||
stake: u64,
|
||||
keypair: Keypair,
|
||||
voting_keypair: Keypair,
|
||||
authorized_voter_keypair: Keypair,
|
||||
staking_keypair: Keypair,
|
||||
}
|
||||
|
||||
@ -1676,7 +1740,7 @@ pub(crate) mod tests {
|
||||
.iter()
|
||||
.map(|validator| {
|
||||
vote_state::create_account(
|
||||
&validator.voting_keypair.pubkey(),
|
||||
&validator.authorized_voter_keypair.pubkey(),
|
||||
&validator.keypair.pubkey(),
|
||||
0,
|
||||
validator.stake,
|
||||
@ -1690,7 +1754,7 @@ pub(crate) mod tests {
|
||||
.map(|(i, validator)| {
|
||||
stake_state::create_account(
|
||||
&validator.staking_keypair.pubkey(),
|
||||
&validator.voting_keypair.pubkey(),
|
||||
&validator.authorized_voter_keypair.pubkey(),
|
||||
&genesis_vote_accounts[i],
|
||||
&Rent::default(),
|
||||
validator.stake,
|
||||
@ -1703,7 +1767,7 @@ pub(crate) mod tests {
|
||||
|
||||
for i in 0..validators.len() {
|
||||
genesis_config.accounts.insert(
|
||||
validators[i].voting_keypair.pubkey(),
|
||||
validators[i].authorized_voter_keypair.pubkey(),
|
||||
genesis_vote_accounts[i].clone(),
|
||||
);
|
||||
genesis_config.accounts.insert(
|
||||
@ -1737,7 +1801,7 @@ pub(crate) mod tests {
|
||||
for validator in validators.iter() {
|
||||
vote(
|
||||
&bank_forks.banks[&neutral_fork.fork[index]].clone(),
|
||||
&validator.voting_keypair.pubkey(),
|
||||
&validator.authorized_voter_keypair.pubkey(),
|
||||
neutral_fork.fork[index - 1],
|
||||
);
|
||||
}
|
||||
@ -1777,7 +1841,7 @@ pub(crate) mod tests {
|
||||
for voter_index in fork_info.voters.iter() {
|
||||
vote(
|
||||
&bank_forks.banks[&fork_info.fork[index]].clone(),
|
||||
&validators[*voter_index].voting_keypair.pubkey(),
|
||||
&validators[*voter_index].authorized_voter_keypair.pubkey(),
|
||||
last_bank.slot(),
|
||||
);
|
||||
}
|
||||
@ -1863,20 +1927,20 @@ pub(crate) mod tests {
|
||||
ValidatorInfo {
|
||||
stake: 34_000_000,
|
||||
keypair: Keypair::new(),
|
||||
voting_keypair: Keypair::new(),
|
||||
authorized_voter_keypair: Keypair::new(),
|
||||
staking_keypair: Keypair::new(),
|
||||
},
|
||||
ValidatorInfo {
|
||||
stake: 33_000_000,
|
||||
keypair: Keypair::new(),
|
||||
voting_keypair: Keypair::new(),
|
||||
authorized_voter_keypair: Keypair::new(),
|
||||
staking_keypair: Keypair::new(),
|
||||
},
|
||||
// Malicious Node
|
||||
ValidatorInfo {
|
||||
stake: 33_000_000,
|
||||
keypair: Keypair::new(),
|
||||
voting_keypair: Keypair::new(),
|
||||
authorized_voter_keypair: Keypair::new(),
|
||||
staking_keypair: Keypair::new(),
|
||||
},
|
||||
];
|
||||
@ -1906,18 +1970,18 @@ pub(crate) mod tests {
|
||||
Blockstore::open(&ledger_path)
|
||||
.expect("Expected to be able to open database ledger"),
|
||||
);
|
||||
let validator_voting_keypairs: Vec<_> = (0..20)
|
||||
let validator_authorized_voter_keypairs: Vec<_> = (0..20)
|
||||
.map(|_| ValidatorVoteKeypairs::new(Keypair::new(), Keypair::new(), Keypair::new()))
|
||||
.collect();
|
||||
|
||||
let validator_voting_keys: HashMap<_, _> = validator_voting_keypairs
|
||||
let validator_voting_keys: HashMap<_, _> = validator_authorized_voter_keypairs
|
||||
.iter()
|
||||
.map(|v| (v.node_keypair.pubkey(), v.vote_keypair.pubkey()))
|
||||
.collect();
|
||||
let GenesisConfigInfo { genesis_config, .. } =
|
||||
genesis_utils::create_genesis_config_with_vote_accounts(
|
||||
10_000,
|
||||
&validator_voting_keypairs,
|
||||
&validator_authorized_voter_keypairs,
|
||||
100,
|
||||
);
|
||||
let bank0 = Bank::new(&genesis_config);
|
||||
|
@ -81,7 +81,7 @@ impl Tvu {
|
||||
#[allow(clippy::new_ret_no_self, clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
vote_account: &Pubkey,
|
||||
voting_keypair: Option<Arc<Keypair>>,
|
||||
authorized_voter_keypairs: Vec<Arc<Keypair>>,
|
||||
storage_keypair: &Arc<Keypair>,
|
||||
bank_forks: &Arc<RwLock<BankForks>>,
|
||||
cluster_info: &Arc<RwLock<ClusterInfo>>,
|
||||
@ -179,7 +179,7 @@ impl Tvu {
|
||||
let replay_stage_config = ReplayStageConfig {
|
||||
my_pubkey: keypair.pubkey(),
|
||||
vote_account: *vote_account,
|
||||
voting_keypair,
|
||||
authorized_voter_keypairs,
|
||||
exit: exit.clone(),
|
||||
subscriptions: subscriptions.clone(),
|
||||
leader_schedule_cache: leader_schedule_cache.clone(),
|
||||
@ -287,14 +287,14 @@ pub mod tests {
|
||||
let bank = bank_forks.working_bank();
|
||||
let (exit, poh_recorder, poh_service, _entry_receiver) =
|
||||
create_test_recorder(&bank, &blockstore, None);
|
||||
let voting_keypair = Keypair::new();
|
||||
let vote_keypair = Keypair::new();
|
||||
let storage_keypair = Arc::new(Keypair::new());
|
||||
let leader_schedule_cache = Arc::new(LeaderScheduleCache::new_from_bank(&bank));
|
||||
let block_commitment_cache = Arc::new(RwLock::new(BlockCommitmentCache::default()));
|
||||
let (retransmit_slots_sender, _retransmit_slots_receiver) = unbounded();
|
||||
let tvu = Tvu::new(
|
||||
&voting_keypair.pubkey(),
|
||||
Some(Arc::new(voting_keypair)),
|
||||
&vote_keypair.pubkey(),
|
||||
vec![Arc::new(vote_keypair)],
|
||||
&storage_keypair,
|
||||
&Arc::new(RwLock::new(bank_forks)),
|
||||
&cref1,
|
||||
|
@ -151,7 +151,7 @@ impl Validator {
|
||||
keypair: &Arc<Keypair>,
|
||||
ledger_path: &Path,
|
||||
vote_account: &Pubkey,
|
||||
authorized_voter: &Arc<Keypair>,
|
||||
mut authorized_voter_keypairs: Vec<Arc<Keypair>>,
|
||||
storage_keypair: &Arc<Keypair>,
|
||||
entrypoint_info_option: Option<&ContactInfo>,
|
||||
poh_verify: bool,
|
||||
@ -162,7 +162,15 @@ impl Validator {
|
||||
|
||||
warn!("identity: {}", id);
|
||||
warn!("vote account: {}", vote_account);
|
||||
warn!("authorized voter: {}", authorized_voter.pubkey());
|
||||
|
||||
if config.voting_disabled {
|
||||
warn!("voting disabled");
|
||||
authorized_voter_keypairs.clear();
|
||||
} else {
|
||||
for authorized_voter_keypair in &authorized_voter_keypairs {
|
||||
warn!("authorized voter: {}", authorized_voter_keypair.pubkey());
|
||||
}
|
||||
}
|
||||
report_target_features();
|
||||
|
||||
info!("entrypoint: {:?}", entrypoint_info_option);
|
||||
@ -386,11 +394,7 @@ impl Validator {
|
||||
let (retransmit_slots_sender, retransmit_slots_receiver) = unbounded();
|
||||
let tvu = Tvu::new(
|
||||
vote_account,
|
||||
if config.voting_disabled {
|
||||
None
|
||||
} else {
|
||||
Some(authorized_voter.clone())
|
||||
},
|
||||
authorized_voter_keypairs,
|
||||
storage_keypair,
|
||||
&bank_forks,
|
||||
&cluster_info,
|
||||
@ -728,7 +732,7 @@ impl TestValidator {
|
||||
&node_keypair,
|
||||
&ledger_path,
|
||||
&leader_voting_keypair.pubkey(),
|
||||
&leader_voting_keypair,
|
||||
vec![leader_voting_keypair.clone()],
|
||||
&storage_keypair,
|
||||
None,
|
||||
true,
|
||||
@ -836,7 +840,7 @@ mod tests {
|
||||
&Arc::new(validator_keypair),
|
||||
&validator_ledger_path,
|
||||
&voting_keypair.pubkey(),
|
||||
&voting_keypair,
|
||||
vec![voting_keypair.clone()],
|
||||
&storage_keypair,
|
||||
Some(&leader_node.info),
|
||||
true,
|
||||
@ -861,7 +865,7 @@ mod tests {
|
||||
.genesis_config;
|
||||
let (validator_ledger_path, _blockhash) = create_new_tmp_ledger!(&genesis_config);
|
||||
ledger_paths.push(validator_ledger_path.clone());
|
||||
let voting_keypair = Arc::new(Keypair::new());
|
||||
let vote_account_keypair = Arc::new(Keypair::new());
|
||||
let storage_keypair = Arc::new(Keypair::new());
|
||||
let config = ValidatorConfig {
|
||||
rpc_ports: Some((
|
||||
@ -874,8 +878,8 @@ mod tests {
|
||||
validator_node,
|
||||
&Arc::new(validator_keypair),
|
||||
&validator_ledger_path,
|
||||
&voting_keypair.pubkey(),
|
||||
&voting_keypair,
|
||||
&vote_account_keypair.pubkey(),
|
||||
vec![vote_account_keypair.clone()],
|
||||
&storage_keypair,
|
||||
Some(&leader_node.info),
|
||||
true,
|
||||
|
Reference in New Issue
Block a user