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:
Michael Vines 2020-03-31 08:23:42 -07:00 committed by GitHub
parent 66946a4680
commit 0e2722c638
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 222 additions and 155 deletions

View File

@ -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> {

View File

@ -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);

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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)

View File

@ -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 {
return Err(format!(
"account's node pubkey ({}) does not match to the given identity keypair ({}).",
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!(
"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()
}
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,