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:
parent
66946a4680
commit
0e2722c638
|
@ -62,6 +62,21 @@ pub fn keypair_of(matches: &ArgMatches<'_>, name: &str) -> Option<Keypair> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn keypairs_of(matches: &ArgMatches<'_>, name: &str) -> Option<Vec<Keypair>> {
|
||||
matches.values_of(name).map(|values| {
|
||||
values
|
||||
.filter_map(|value| {
|
||||
if value == ASK_KEYWORD {
|
||||
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
|
||||
keypair_from_seed_phrase(name, skip_validation, true).ok()
|
||||
} else {
|
||||
read_keypair_file(value).ok()
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
|
||||
// Return a pubkey for an argument that can itself be parsed into a pubkey,
|
||||
// or is a filename that can be read as a keypair
|
||||
pub fn pubkey_of(matches: &ArgMatches<'_>, name: &str) -> Option<Pubkey> {
|
||||
|
|
|
@ -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,29 +774,90 @@ impl ReplayStage {
|
|||
lockouts_sender,
|
||||
);
|
||||
|
||||
if let Some(ref voting_keypair) = voting_keypair {
|
||||
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,
|
||||
&voting_keypair.pubkey(),
|
||||
tower.last_vote_and_timestamp(),
|
||||
&vote_account_pubkey,
|
||||
&authorized_voter_keypair.pubkey(),
|
||||
vote,
|
||||
);
|
||||
|
||||
let mut vote_tx =
|
||||
Transaction::new_with_payer(vec![vote_ix], Some(&node_keypair.pubkey()));
|
||||
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);
|
||||
vote_tx.partial_sign(&[authorized_voter_keypair.as_ref()], blockhash);
|
||||
cluster_info
|
||||
.write()
|
||||
.unwrap()
|
||||
.push_vote(tower_index, vote_tx);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_commitment_cache(
|
||||
bank: Arc<Bank>,
|
||||
|
@ -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,
|
||||
|
|
|
@ -219,7 +219,7 @@ impl LocalCluster {
|
|||
&leader_keypair,
|
||||
&leader_ledger_path,
|
||||
&leader_voting_keypair.pubkey(),
|
||||
&leader_voting_keypair,
|
||||
vec![leader_voting_keypair.clone()],
|
||||
&leader_storage_keypair,
|
||||
None,
|
||||
true,
|
||||
|
@ -367,7 +367,7 @@ impl LocalCluster {
|
|||
&validator_keypair,
|
||||
&ledger_path,
|
||||
&voting_keypair.pubkey(),
|
||||
&voting_keypair,
|
||||
vec![voting_keypair.clone()],
|
||||
&storage_keypair,
|
||||
Some(&self.entry_point_info),
|
||||
true,
|
||||
|
@ -687,7 +687,7 @@ impl Cluster for LocalCluster {
|
|||
&validator_info.keypair,
|
||||
&validator_info.ledger_path,
|
||||
&validator_info.voting_keypair.pubkey(),
|
||||
&validator_info.voting_keypair,
|
||||
vec![validator_info.voting_keypair.clone()],
|
||||
&validator_info.storage_keypair,
|
||||
entry_point_info,
|
||||
true,
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
use log::*;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use solana_sdk::{clock::Epoch, pubkey::Pubkey};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
||||
pub struct AuthorizedVoters {
|
||||
authorized_voters: BTreeMap<u64, Pubkey>,
|
||||
authorized_voters: BTreeMap<Epoch, Pubkey>,
|
||||
}
|
||||
|
||||
impl AuthorizedVoters {
|
||||
pub fn new(epoch: u64, pubkey: Pubkey) -> Self {
|
||||
pub fn new(epoch: Epoch, pubkey: Pubkey) -> Self {
|
||||
let mut authorized_voters = BTreeMap::new();
|
||||
authorized_voters.insert(epoch, pubkey);
|
||||
Self { authorized_voters }
|
||||
}
|
||||
|
||||
pub fn get_authorized_voter(&self, epoch: u64) -> Option<Pubkey> {
|
||||
pub fn get_authorized_voter(&self, epoch: Epoch) -> Option<Pubkey> {
|
||||
self.get_or_calculate_authorized_voter_for_epoch(epoch)
|
||||
.map(|(pubkey, _)| pubkey)
|
||||
}
|
||||
|
||||
pub fn get_and_cache_authorized_voter_for_epoch(&mut self, epoch: u64) -> Option<Pubkey> {
|
||||
pub fn get_and_cache_authorized_voter_for_epoch(&mut self, epoch: Epoch) -> Option<Pubkey> {
|
||||
let res = self.get_or_calculate_authorized_voter_for_epoch(epoch);
|
||||
|
||||
res.map(|(pubkey, existed)| {
|
||||
|
@ -31,11 +31,11 @@ impl AuthorizedVoters {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, epoch: u64, authorized_voter: Pubkey) {
|
||||
pub fn insert(&mut self, epoch: Epoch, authorized_voter: Pubkey) {
|
||||
self.authorized_voters.insert(epoch, authorized_voter);
|
||||
}
|
||||
|
||||
pub fn purge_authorized_voters(&mut self, current_epoch: u64) -> bool {
|
||||
pub fn purge_authorized_voters(&mut self, current_epoch: Epoch) -> bool {
|
||||
// Iterate through the keys in order, filtering out the ones
|
||||
// less than the current epoch
|
||||
let expired_keys: Vec<_> = self
|
||||
|
@ -72,14 +72,18 @@ impl AuthorizedVoters {
|
|||
self.authorized_voters.len()
|
||||
}
|
||||
|
||||
pub fn contains(&self, epoch: u64) -> bool {
|
||||
pub fn contains(&self, epoch: Epoch) -> bool {
|
||||
self.authorized_voters.get(&epoch).is_some()
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> std::collections::btree_map::Iter<Epoch, Pubkey> {
|
||||
self.authorized_voters.iter()
|
||||
}
|
||||
|
||||
// Returns the authorized voter at the given epoch if the epoch is >= the
|
||||
// current epoch, and a bool indicating whether the entry for this epoch
|
||||
// exists in the self.authorized_voter map
|
||||
fn get_or_calculate_authorized_voter_for_epoch(&self, epoch: u64) -> Option<(Pubkey, bool)> {
|
||||
fn get_or_calculate_authorized_voter_for_epoch(&self, epoch: Epoch) -> Option<(Pubkey, bool)> {
|
||||
let res = self.authorized_voters.get(&epoch);
|
||||
if res.is_none() {
|
||||
// If no authorized voter has been set yet for this epoch,
|
||||
|
|
|
@ -161,7 +161,7 @@ pub struct VoteState {
|
|||
pub commission: u8,
|
||||
|
||||
pub votes: VecDeque<Lockout>,
|
||||
pub root_slot: Option<u64>,
|
||||
pub root_slot: Option<Slot>,
|
||||
|
||||
/// the signer for vote transactions
|
||||
authorized_voters: AuthorizedVoters,
|
||||
|
@ -190,7 +190,7 @@ impl VoteState {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_authorized_voter(&self, epoch: u64) -> Option<Pubkey> {
|
||||
pub fn get_authorized_voter(&self, epoch: Epoch) -> Option<Pubkey> {
|
||||
self.authorized_voters.get_authorized_voter(epoch)
|
||||
}
|
||||
|
||||
|
@ -436,8 +436,8 @@ impl VoteState {
|
|||
fn set_new_authorized_voter<F>(
|
||||
&mut self,
|
||||
authorized_pubkey: &Pubkey,
|
||||
current_epoch: u64,
|
||||
target_epoch: u64,
|
||||
current_epoch: Epoch,
|
||||
target_epoch: Epoch,
|
||||
verify: F,
|
||||
) -> Result<(), InstructionError>
|
||||
where
|
||||
|
@ -494,7 +494,7 @@ impl VoteState {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn get_and_update_authorized_voter(&mut self, current_epoch: u64) -> Option<Pubkey> {
|
||||
fn get_and_update_authorized_voter(&mut self, current_epoch: Epoch) -> Option<Pubkey> {
|
||||
let pubkey = self
|
||||
.authorized_voters
|
||||
.get_and_cache_authorized_voter_for_epoch(current_epoch)
|
||||
|
|
|
@ -5,7 +5,7 @@ use clap::{
|
|||
use log::*;
|
||||
use rand::{thread_rng, Rng};
|
||||
use solana_clap_utils::{
|
||||
input_parsers::{keypair_of, pubkey_of},
|
||||
input_parsers::{keypair_of, keypairs_of, pubkey_of},
|
||||
input_validators::{is_keypair_or_ask_keyword, is_pubkey, is_pubkey_or_keypair, is_slot},
|
||||
keypair::SKIP_SEED_PHRASE_VALIDATION_ARG,
|
||||
};
|
||||
|
@ -271,70 +271,58 @@ fn get_rpc_node(
|
|||
|
||||
fn check_vote_account(
|
||||
rpc_client: &RpcClient,
|
||||
vote_pubkey: &Pubkey,
|
||||
voting_pubkey: &Pubkey,
|
||||
node_pubkey: &Pubkey,
|
||||
identity_pubkey: &Pubkey,
|
||||
vote_account_address: &Pubkey,
|
||||
authorized_voter_pubkeys: &[Pubkey],
|
||||
) -> Result<(), String> {
|
||||
let found_vote_account = rpc_client
|
||||
.get_account(vote_pubkey)
|
||||
.map_err(|err| format!("Vote account not found: {}", err.to_string()))?;
|
||||
let vote_account = rpc_client
|
||||
.get_account(vote_account_address)
|
||||
.map_err(|err| format!("vote account not found: {}", err.to_string()))?;
|
||||
|
||||
if found_vote_account.owner != solana_vote_program::id() {
|
||||
if vote_account.owner != solana_vote_program::id() {
|
||||
return Err(format!(
|
||||
"not a vote account (owned by {}): {}",
|
||||
found_vote_account.owner, vote_pubkey
|
||||
vote_account.owner, vote_account_address
|
||||
));
|
||||
}
|
||||
|
||||
let found_node_account = rpc_client
|
||||
.get_account(node_pubkey)
|
||||
let identity_account = rpc_client
|
||||
.get_account(identity_pubkey)
|
||||
.map_err(|err| format!("Identity account not found: {}", err.to_string()))?;
|
||||
|
||||
let found_vote_account = solana_vote_program::vote_state::VoteState::from(&found_vote_account);
|
||||
if let Some(found_vote_account) = found_vote_account {
|
||||
if found_vote_account.authorized_voters().is_empty() {
|
||||
let vote_state = solana_vote_program::vote_state::VoteState::from(&vote_account);
|
||||
if let Some(vote_state) = vote_state {
|
||||
if vote_state.authorized_voters().is_empty() {
|
||||
return Err("Vote account not yet initialized".to_string());
|
||||
}
|
||||
|
||||
let epoch_info = rpc_client
|
||||
.get_epoch_info()
|
||||
.map_err(|err| format!("Failed to get epoch info: {}", err.to_string()))?;
|
||||
|
||||
let mut authorized_voter;
|
||||
authorized_voter = found_vote_account.get_authorized_voter(epoch_info.epoch);
|
||||
if authorized_voter.is_none() {
|
||||
// Must have gotten a clock on the boundary
|
||||
authorized_voter = found_vote_account.get_authorized_voter(epoch_info.epoch + 1);
|
||||
}
|
||||
|
||||
let authorized_voter = authorized_voter.expect(
|
||||
"Could not get the authorized voter, which only happens if the
|
||||
client gets an epoch earlier than the current epoch,
|
||||
but the received epoch should not be off by more than
|
||||
one epoch",
|
||||
);
|
||||
|
||||
if authorized_voter != *voting_pubkey {
|
||||
if vote_state.node_pubkey != *identity_pubkey {
|
||||
return Err(format!(
|
||||
"account's authorized voter ({}) does not match to the given voting keypair ({}).",
|
||||
authorized_voter, voting_pubkey
|
||||
"vote account's identity ({}) does not match the validator's identity {}).",
|
||||
vote_state.node_pubkey, identity_pubkey
|
||||
));
|
||||
}
|
||||
if found_vote_account.node_pubkey != *node_pubkey {
|
||||
|
||||
for (_, vote_account_authorized_voter_pubkey) in vote_state.authorized_voters().iter() {
|
||||
if !authorized_voter_pubkeys.contains(&vote_account_authorized_voter_pubkey) {
|
||||
return Err(format!(
|
||||
"account's node pubkey ({}) does not match to the given identity keypair ({}).",
|
||||
found_vote_account.node_pubkey, node_pubkey
|
||||
"authorized voter {} not available",
|
||||
vote_account_authorized_voter_pubkey
|
||||
));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(format!("invalid vote account data: {}", vote_pubkey));
|
||||
return Err(format!(
|
||||
"invalid vote account data for {}",
|
||||
vote_account_address
|
||||
));
|
||||
}
|
||||
|
||||
// Maybe we can calculate minimum voting fee; rather than 1 lamport
|
||||
if found_node_account.lamports <= 1 {
|
||||
if identity_account.lamports <= 1 {
|
||||
return Err(format!(
|
||||
"unfunded identity account ({}): only {} lamports (needs more fund to vote)",
|
||||
node_pubkey, found_node_account.lamports
|
||||
"underfunded identity account ({}): only {} lamports available",
|
||||
identity_pubkey, identity_account.lamports
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -414,29 +402,22 @@ pub fn main() {
|
|||
Arg::with_name("identity")
|
||||
.short("i")
|
||||
.long("identity")
|
||||
.alias("identity-keypair") // --identity-keypair is legacy for <= v1.0.6 users
|
||||
.value_name("PATH")
|
||||
.takes_value(true)
|
||||
.validator(is_keypair_or_ask_keyword)
|
||||
.help("Validator identity keypair"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("authorized_voter")
|
||||
Arg::with_name("authorized_voter_keypairs")
|
||||
.long("authorized-voter")
|
||||
.value_name("PATH")
|
||||
.takes_value(true)
|
||||
.validator(is_keypair_or_ask_keyword)
|
||||
.requires("vote_account")
|
||||
.help("Authorized voter keypair [default: value of --identity]"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("deprecated_voting_keypair")
|
||||
.long("voting-keypair")
|
||||
.value_name("PATH")
|
||||
.takes_value(true)
|
||||
.hidden(true) // Don't document this argument, it's legacy for <= v1.0.6 users
|
||||
.conflicts_with_all(&["authorized_voter", "vote_account"])
|
||||
.validator(is_keypair_or_ask_keyword),
|
||||
.multiple(true)
|
||||
.help("Include an additional authorized voter keypair. \
|
||||
May be specified multiple times. \
|
||||
[default: the --identity keypair]"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("vote_account")
|
||||
|
@ -445,7 +426,9 @@ pub fn main() {
|
|||
.takes_value(true)
|
||||
.validator(is_pubkey_or_keypair)
|
||||
.requires("identity")
|
||||
.help("Validator vote account public key. If unspecified voting will be disabled")
|
||||
.help("Validator vote account public key. If unspecified voting will be disabled. \
|
||||
The authorized voter for the account must either be the --identity keypair \
|
||||
or with the --authorized-voter argument")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("storage_keypair")
|
||||
|
@ -734,13 +717,9 @@ pub fn main() {
|
|||
|
||||
let identity_keypair = Arc::new(keypair_of(&matches, "identity").unwrap_or_else(Keypair::new));
|
||||
|
||||
let authorized_voter = keypair_of(&matches, "authorized_voter")
|
||||
.or_else(|| {
|
||||
// Legacy v1.0.6 argument support
|
||||
keypair_of(&matches, "deprecated_voting_keypair")
|
||||
})
|
||||
.map(Arc::new)
|
||||
.unwrap_or_else(|| identity_keypair.clone());
|
||||
let authorized_voter_keypairs = keypairs_of(&matches, "authorized_voter_keypairs")
|
||||
.map(|keypairs| keypairs.into_iter().map(Arc::new).collect())
|
||||
.unwrap_or_else(|| vec![identity_keypair.clone()]);
|
||||
|
||||
let storage_keypair = keypair_of(&matches, "storage_keypair").unwrap_or_else(Keypair::new);
|
||||
|
||||
|
@ -808,16 +787,9 @@ pub fn main() {
|
|||
};
|
||||
|
||||
let vote_account = pubkey_of(&matches, "vote_account").unwrap_or_else(|| {
|
||||
if matches.is_present("deprecated_voting_keypair") {
|
||||
// Legacy v1.0.6 behaviour of using `--voting-keypair` as `--vote-account`
|
||||
keypair_of(&matches, "deprecated_voting_keypair")
|
||||
.unwrap()
|
||||
.pubkey()
|
||||
} else {
|
||||
warn!("--vote-account not specified, validator will not vote");
|
||||
validator_config.voting_disabled = true;
|
||||
Keypair::new().pubkey()
|
||||
}
|
||||
});
|
||||
|
||||
let dynamic_port_range =
|
||||
|
@ -1032,7 +1004,6 @@ pub fn main() {
|
|||
tcp_listeners,
|
||||
&udp_sockets,
|
||||
);
|
||||
|
||||
if !no_genesis_fetch {
|
||||
let (cluster_info, gossip_exit_flag, gossip_service) = start_gossip_node(
|
||||
&identity_keypair,
|
||||
|
@ -1109,13 +1080,22 @@ pub fn main() {
|
|||
if !validator_config.voting_disabled && !no_check_vote_account {
|
||||
check_vote_account(
|
||||
&rpc_client,
|
||||
&vote_account,
|
||||
&authorized_voter.pubkey(),
|
||||
&identity_keypair.pubkey(),
|
||||
)
|
||||
} else {
|
||||
Ok(())
|
||||
&vote_account,
|
||||
&authorized_voter_keypairs.iter().map(|k| k.pubkey()).collect::<Vec<_>>(),
|
||||
|
||||
).unwrap_or_else(|err| {
|
||||
// Consider failures here to be more likely due to user error (eg,
|
||||
// incorrect `solana-validator` command-line arguments) rather than the
|
||||
// RPC node failing.
|
||||
//
|
||||
// Power users can always use the `--no-check-vote-account` option to
|
||||
// bypass this check entirely
|
||||
error!("{}", err);
|
||||
exit(1);
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
|
||||
if result.is_ok() {
|
||||
|
@ -1153,7 +1133,7 @@ pub fn main() {
|
|||
&identity_keypair,
|
||||
&ledger_path,
|
||||
&vote_account,
|
||||
&authorized_voter,
|
||||
authorized_voter_keypairs,
|
||||
&Arc::new(storage_keypair),
|
||||
cluster_entrypoint.as_ref(),
|
||||
!skip_poh_verify,
|
||||
|
|
Loading…
Reference in New Issue