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,
|
// 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
|
// or is a filename that can be read as a keypair
|
||||||
pub fn pubkey_of(matches: &ArgMatches<'_>, name: &str) -> Option<Pubkey> {
|
pub fn pubkey_of(matches: &ArgMatches<'_>, name: &str) -> Option<Pubkey> {
|
||||||
|
|
|
@ -33,7 +33,10 @@ use solana_sdk::{
|
||||||
timing::{self, duration_as_ms},
|
timing::{self, duration_as_ms},
|
||||||
transaction::Transaction,
|
transaction::Transaction,
|
||||||
};
|
};
|
||||||
use solana_vote_program::vote_instruction;
|
use solana_vote_program::{
|
||||||
|
vote_instruction,
|
||||||
|
vote_state::{Vote, VoteState},
|
||||||
|
};
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
ops::Deref,
|
ops::Deref,
|
||||||
|
@ -89,7 +92,7 @@ struct SkippedSlotsInfo {
|
||||||
pub struct ReplayStageConfig {
|
pub struct ReplayStageConfig {
|
||||||
pub my_pubkey: Pubkey,
|
pub my_pubkey: Pubkey,
|
||||||
pub vote_account: Pubkey,
|
pub vote_account: Pubkey,
|
||||||
pub voting_keypair: Option<Arc<Keypair>>,
|
pub authorized_voter_keypairs: Vec<Arc<Keypair>>,
|
||||||
pub exit: Arc<AtomicBool>,
|
pub exit: Arc<AtomicBool>,
|
||||||
pub subscriptions: Arc<RpcSubscriptions>,
|
pub subscriptions: Arc<RpcSubscriptions>,
|
||||||
pub leader_schedule_cache: Arc<LeaderScheduleCache>,
|
pub leader_schedule_cache: Arc<LeaderScheduleCache>,
|
||||||
|
@ -121,7 +124,7 @@ impl ReplayStage {
|
||||||
let ReplayStageConfig {
|
let ReplayStageConfig {
|
||||||
my_pubkey,
|
my_pubkey,
|
||||||
vote_account,
|
vote_account,
|
||||||
voting_keypair,
|
authorized_voter_keypairs,
|
||||||
exit,
|
exit,
|
||||||
subscriptions,
|
subscriptions,
|
||||||
leader_schedule_cache,
|
leader_schedule_cache,
|
||||||
|
@ -328,7 +331,7 @@ impl ReplayStage {
|
||||||
&mut tower,
|
&mut tower,
|
||||||
&mut progress,
|
&mut progress,
|
||||||
&vote_account,
|
&vote_account,
|
||||||
&voting_keypair,
|
&authorized_voter_keypairs,
|
||||||
&cluster_info,
|
&cluster_info,
|
||||||
&blockstore,
|
&blockstore,
|
||||||
&leader_schedule_cache,
|
&leader_schedule_cache,
|
||||||
|
@ -706,8 +709,8 @@ impl ReplayStage {
|
||||||
bank_forks: &Arc<RwLock<BankForks>>,
|
bank_forks: &Arc<RwLock<BankForks>>,
|
||||||
tower: &mut Tower,
|
tower: &mut Tower,
|
||||||
progress: &mut ProgressMap,
|
progress: &mut ProgressMap,
|
||||||
vote_account: &Pubkey,
|
vote_account_pubkey: &Pubkey,
|
||||||
voting_keypair: &Option<Arc<Keypair>>,
|
authorized_voter_keypairs: &[Arc<Keypair>],
|
||||||
cluster_info: &Arc<RwLock<ClusterInfo>>,
|
cluster_info: &Arc<RwLock<ClusterInfo>>,
|
||||||
blockstore: &Arc<Blockstore>,
|
blockstore: &Arc<Blockstore>,
|
||||||
leader_schedule_cache: &Arc<LeaderScheduleCache>,
|
leader_schedule_cache: &Arc<LeaderScheduleCache>,
|
||||||
|
@ -723,7 +726,7 @@ impl ReplayStage {
|
||||||
inc_new_counter_info!("replay_stage-voted_empty_bank", 1);
|
inc_new_counter_info!("replay_stage-voted_empty_bank", 1);
|
||||||
}
|
}
|
||||||
trace!("handle votable bank {}", bank.slot());
|
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) {
|
if let Some(new_root) = tower.record_bank_vote(vote) {
|
||||||
// get the root bank before squash
|
// get the root bank before squash
|
||||||
let root_bank = bank_forks
|
let root_bank = bank_forks
|
||||||
|
@ -771,30 +774,91 @@ impl ReplayStage {
|
||||||
lockouts_sender,
|
lockouts_sender,
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(ref voting_keypair) = voting_keypair {
|
Self::push_vote(
|
||||||
let node_keypair = cluster_info.read().unwrap().keypair.clone();
|
cluster_info,
|
||||||
|
bank,
|
||||||
// Send our last few votes along with the new one
|
vote_account_pubkey,
|
||||||
let vote_ix = vote_instruction::vote(
|
authorized_voter_keypairs,
|
||||||
&vote_account,
|
tower.last_vote_and_timestamp(),
|
||||||
&voting_keypair.pubkey(),
|
tower_index,
|
||||||
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);
|
|
||||||
}
|
|
||||||
Ok(())
|
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(
|
fn update_commitment_cache(
|
||||||
bank: Arc<Bank>,
|
bank: Arc<Bank>,
|
||||||
root: Slot,
|
root: Slot,
|
||||||
|
@ -1621,7 +1685,7 @@ pub(crate) mod tests {
|
||||||
struct ValidatorInfo {
|
struct ValidatorInfo {
|
||||||
stake: u64,
|
stake: u64,
|
||||||
keypair: Keypair,
|
keypair: Keypair,
|
||||||
voting_keypair: Keypair,
|
authorized_voter_keypair: Keypair,
|
||||||
staking_keypair: Keypair,
|
staking_keypair: Keypair,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1676,7 +1740,7 @@ pub(crate) mod tests {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|validator| {
|
.map(|validator| {
|
||||||
vote_state::create_account(
|
vote_state::create_account(
|
||||||
&validator.voting_keypair.pubkey(),
|
&validator.authorized_voter_keypair.pubkey(),
|
||||||
&validator.keypair.pubkey(),
|
&validator.keypair.pubkey(),
|
||||||
0,
|
0,
|
||||||
validator.stake,
|
validator.stake,
|
||||||
|
@ -1690,7 +1754,7 @@ pub(crate) mod tests {
|
||||||
.map(|(i, validator)| {
|
.map(|(i, validator)| {
|
||||||
stake_state::create_account(
|
stake_state::create_account(
|
||||||
&validator.staking_keypair.pubkey(),
|
&validator.staking_keypair.pubkey(),
|
||||||
&validator.voting_keypair.pubkey(),
|
&validator.authorized_voter_keypair.pubkey(),
|
||||||
&genesis_vote_accounts[i],
|
&genesis_vote_accounts[i],
|
||||||
&Rent::default(),
|
&Rent::default(),
|
||||||
validator.stake,
|
validator.stake,
|
||||||
|
@ -1703,7 +1767,7 @@ pub(crate) mod tests {
|
||||||
|
|
||||||
for i in 0..validators.len() {
|
for i in 0..validators.len() {
|
||||||
genesis_config.accounts.insert(
|
genesis_config.accounts.insert(
|
||||||
validators[i].voting_keypair.pubkey(),
|
validators[i].authorized_voter_keypair.pubkey(),
|
||||||
genesis_vote_accounts[i].clone(),
|
genesis_vote_accounts[i].clone(),
|
||||||
);
|
);
|
||||||
genesis_config.accounts.insert(
|
genesis_config.accounts.insert(
|
||||||
|
@ -1737,7 +1801,7 @@ pub(crate) mod tests {
|
||||||
for validator in validators.iter() {
|
for validator in validators.iter() {
|
||||||
vote(
|
vote(
|
||||||
&bank_forks.banks[&neutral_fork.fork[index]].clone(),
|
&bank_forks.banks[&neutral_fork.fork[index]].clone(),
|
||||||
&validator.voting_keypair.pubkey(),
|
&validator.authorized_voter_keypair.pubkey(),
|
||||||
neutral_fork.fork[index - 1],
|
neutral_fork.fork[index - 1],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1777,7 +1841,7 @@ pub(crate) mod tests {
|
||||||
for voter_index in fork_info.voters.iter() {
|
for voter_index in fork_info.voters.iter() {
|
||||||
vote(
|
vote(
|
||||||
&bank_forks.banks[&fork_info.fork[index]].clone(),
|
&bank_forks.banks[&fork_info.fork[index]].clone(),
|
||||||
&validators[*voter_index].voting_keypair.pubkey(),
|
&validators[*voter_index].authorized_voter_keypair.pubkey(),
|
||||||
last_bank.slot(),
|
last_bank.slot(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1863,20 +1927,20 @@ pub(crate) mod tests {
|
||||||
ValidatorInfo {
|
ValidatorInfo {
|
||||||
stake: 34_000_000,
|
stake: 34_000_000,
|
||||||
keypair: Keypair::new(),
|
keypair: Keypair::new(),
|
||||||
voting_keypair: Keypair::new(),
|
authorized_voter_keypair: Keypair::new(),
|
||||||
staking_keypair: Keypair::new(),
|
staking_keypair: Keypair::new(),
|
||||||
},
|
},
|
||||||
ValidatorInfo {
|
ValidatorInfo {
|
||||||
stake: 33_000_000,
|
stake: 33_000_000,
|
||||||
keypair: Keypair::new(),
|
keypair: Keypair::new(),
|
||||||
voting_keypair: Keypair::new(),
|
authorized_voter_keypair: Keypair::new(),
|
||||||
staking_keypair: Keypair::new(),
|
staking_keypair: Keypair::new(),
|
||||||
},
|
},
|
||||||
// Malicious Node
|
// Malicious Node
|
||||||
ValidatorInfo {
|
ValidatorInfo {
|
||||||
stake: 33_000_000,
|
stake: 33_000_000,
|
||||||
keypair: Keypair::new(),
|
keypair: Keypair::new(),
|
||||||
voting_keypair: Keypair::new(),
|
authorized_voter_keypair: Keypair::new(),
|
||||||
staking_keypair: Keypair::new(),
|
staking_keypair: Keypair::new(),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -1906,18 +1970,18 @@ pub(crate) mod tests {
|
||||||
Blockstore::open(&ledger_path)
|
Blockstore::open(&ledger_path)
|
||||||
.expect("Expected to be able to open database ledger"),
|
.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()))
|
.map(|_| ValidatorVoteKeypairs::new(Keypair::new(), Keypair::new(), Keypair::new()))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let validator_voting_keys: HashMap<_, _> = validator_voting_keypairs
|
let validator_voting_keys: HashMap<_, _> = validator_authorized_voter_keypairs
|
||||||
.iter()
|
.iter()
|
||||||
.map(|v| (v.node_keypair.pubkey(), v.vote_keypair.pubkey()))
|
.map(|v| (v.node_keypair.pubkey(), v.vote_keypair.pubkey()))
|
||||||
.collect();
|
.collect();
|
||||||
let GenesisConfigInfo { genesis_config, .. } =
|
let GenesisConfigInfo { genesis_config, .. } =
|
||||||
genesis_utils::create_genesis_config_with_vote_accounts(
|
genesis_utils::create_genesis_config_with_vote_accounts(
|
||||||
10_000,
|
10_000,
|
||||||
&validator_voting_keypairs,
|
&validator_authorized_voter_keypairs,
|
||||||
100,
|
100,
|
||||||
);
|
);
|
||||||
let bank0 = Bank::new(&genesis_config);
|
let bank0 = Bank::new(&genesis_config);
|
||||||
|
|
|
@ -81,7 +81,7 @@ impl Tvu {
|
||||||
#[allow(clippy::new_ret_no_self, clippy::too_many_arguments)]
|
#[allow(clippy::new_ret_no_self, clippy::too_many_arguments)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
vote_account: &Pubkey,
|
vote_account: &Pubkey,
|
||||||
voting_keypair: Option<Arc<Keypair>>,
|
authorized_voter_keypairs: Vec<Arc<Keypair>>,
|
||||||
storage_keypair: &Arc<Keypair>,
|
storage_keypair: &Arc<Keypair>,
|
||||||
bank_forks: &Arc<RwLock<BankForks>>,
|
bank_forks: &Arc<RwLock<BankForks>>,
|
||||||
cluster_info: &Arc<RwLock<ClusterInfo>>,
|
cluster_info: &Arc<RwLock<ClusterInfo>>,
|
||||||
|
@ -179,7 +179,7 @@ impl Tvu {
|
||||||
let replay_stage_config = ReplayStageConfig {
|
let replay_stage_config = ReplayStageConfig {
|
||||||
my_pubkey: keypair.pubkey(),
|
my_pubkey: keypair.pubkey(),
|
||||||
vote_account: *vote_account,
|
vote_account: *vote_account,
|
||||||
voting_keypair,
|
authorized_voter_keypairs,
|
||||||
exit: exit.clone(),
|
exit: exit.clone(),
|
||||||
subscriptions: subscriptions.clone(),
|
subscriptions: subscriptions.clone(),
|
||||||
leader_schedule_cache: leader_schedule_cache.clone(),
|
leader_schedule_cache: leader_schedule_cache.clone(),
|
||||||
|
@ -287,14 +287,14 @@ pub mod tests {
|
||||||
let bank = bank_forks.working_bank();
|
let bank = bank_forks.working_bank();
|
||||||
let (exit, poh_recorder, poh_service, _entry_receiver) =
|
let (exit, poh_recorder, poh_service, _entry_receiver) =
|
||||||
create_test_recorder(&bank, &blockstore, None);
|
create_test_recorder(&bank, &blockstore, None);
|
||||||
let voting_keypair = Keypair::new();
|
let vote_keypair = Keypair::new();
|
||||||
let storage_keypair = Arc::new(Keypair::new());
|
let storage_keypair = Arc::new(Keypair::new());
|
||||||
let leader_schedule_cache = Arc::new(LeaderScheduleCache::new_from_bank(&bank));
|
let leader_schedule_cache = Arc::new(LeaderScheduleCache::new_from_bank(&bank));
|
||||||
let block_commitment_cache = Arc::new(RwLock::new(BlockCommitmentCache::default()));
|
let block_commitment_cache = Arc::new(RwLock::new(BlockCommitmentCache::default()));
|
||||||
let (retransmit_slots_sender, _retransmit_slots_receiver) = unbounded();
|
let (retransmit_slots_sender, _retransmit_slots_receiver) = unbounded();
|
||||||
let tvu = Tvu::new(
|
let tvu = Tvu::new(
|
||||||
&voting_keypair.pubkey(),
|
&vote_keypair.pubkey(),
|
||||||
Some(Arc::new(voting_keypair)),
|
vec![Arc::new(vote_keypair)],
|
||||||
&storage_keypair,
|
&storage_keypair,
|
||||||
&Arc::new(RwLock::new(bank_forks)),
|
&Arc::new(RwLock::new(bank_forks)),
|
||||||
&cref1,
|
&cref1,
|
||||||
|
|
|
@ -151,7 +151,7 @@ impl Validator {
|
||||||
keypair: &Arc<Keypair>,
|
keypair: &Arc<Keypair>,
|
||||||
ledger_path: &Path,
|
ledger_path: &Path,
|
||||||
vote_account: &Pubkey,
|
vote_account: &Pubkey,
|
||||||
authorized_voter: &Arc<Keypair>,
|
mut authorized_voter_keypairs: Vec<Arc<Keypair>>,
|
||||||
storage_keypair: &Arc<Keypair>,
|
storage_keypair: &Arc<Keypair>,
|
||||||
entrypoint_info_option: Option<&ContactInfo>,
|
entrypoint_info_option: Option<&ContactInfo>,
|
||||||
poh_verify: bool,
|
poh_verify: bool,
|
||||||
|
@ -162,7 +162,15 @@ impl Validator {
|
||||||
|
|
||||||
warn!("identity: {}", id);
|
warn!("identity: {}", id);
|
||||||
warn!("vote account: {}", vote_account);
|
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();
|
report_target_features();
|
||||||
|
|
||||||
info!("entrypoint: {:?}", entrypoint_info_option);
|
info!("entrypoint: {:?}", entrypoint_info_option);
|
||||||
|
@ -386,11 +394,7 @@ impl Validator {
|
||||||
let (retransmit_slots_sender, retransmit_slots_receiver) = unbounded();
|
let (retransmit_slots_sender, retransmit_slots_receiver) = unbounded();
|
||||||
let tvu = Tvu::new(
|
let tvu = Tvu::new(
|
||||||
vote_account,
|
vote_account,
|
||||||
if config.voting_disabled {
|
authorized_voter_keypairs,
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(authorized_voter.clone())
|
|
||||||
},
|
|
||||||
storage_keypair,
|
storage_keypair,
|
||||||
&bank_forks,
|
&bank_forks,
|
||||||
&cluster_info,
|
&cluster_info,
|
||||||
|
@ -728,7 +732,7 @@ impl TestValidator {
|
||||||
&node_keypair,
|
&node_keypair,
|
||||||
&ledger_path,
|
&ledger_path,
|
||||||
&leader_voting_keypair.pubkey(),
|
&leader_voting_keypair.pubkey(),
|
||||||
&leader_voting_keypair,
|
vec![leader_voting_keypair.clone()],
|
||||||
&storage_keypair,
|
&storage_keypair,
|
||||||
None,
|
None,
|
||||||
true,
|
true,
|
||||||
|
@ -836,7 +840,7 @@ mod tests {
|
||||||
&Arc::new(validator_keypair),
|
&Arc::new(validator_keypair),
|
||||||
&validator_ledger_path,
|
&validator_ledger_path,
|
||||||
&voting_keypair.pubkey(),
|
&voting_keypair.pubkey(),
|
||||||
&voting_keypair,
|
vec![voting_keypair.clone()],
|
||||||
&storage_keypair,
|
&storage_keypair,
|
||||||
Some(&leader_node.info),
|
Some(&leader_node.info),
|
||||||
true,
|
true,
|
||||||
|
@ -861,7 +865,7 @@ mod tests {
|
||||||
.genesis_config;
|
.genesis_config;
|
||||||
let (validator_ledger_path, _blockhash) = create_new_tmp_ledger!(&genesis_config);
|
let (validator_ledger_path, _blockhash) = create_new_tmp_ledger!(&genesis_config);
|
||||||
ledger_paths.push(validator_ledger_path.clone());
|
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 storage_keypair = Arc::new(Keypair::new());
|
||||||
let config = ValidatorConfig {
|
let config = ValidatorConfig {
|
||||||
rpc_ports: Some((
|
rpc_ports: Some((
|
||||||
|
@ -874,8 +878,8 @@ mod tests {
|
||||||
validator_node,
|
validator_node,
|
||||||
&Arc::new(validator_keypair),
|
&Arc::new(validator_keypair),
|
||||||
&validator_ledger_path,
|
&validator_ledger_path,
|
||||||
&voting_keypair.pubkey(),
|
&vote_account_keypair.pubkey(),
|
||||||
&voting_keypair,
|
vec![vote_account_keypair.clone()],
|
||||||
&storage_keypair,
|
&storage_keypair,
|
||||||
Some(&leader_node.info),
|
Some(&leader_node.info),
|
||||||
true,
|
true,
|
||||||
|
|
|
@ -219,7 +219,7 @@ impl LocalCluster {
|
||||||
&leader_keypair,
|
&leader_keypair,
|
||||||
&leader_ledger_path,
|
&leader_ledger_path,
|
||||||
&leader_voting_keypair.pubkey(),
|
&leader_voting_keypair.pubkey(),
|
||||||
&leader_voting_keypair,
|
vec![leader_voting_keypair.clone()],
|
||||||
&leader_storage_keypair,
|
&leader_storage_keypair,
|
||||||
None,
|
None,
|
||||||
true,
|
true,
|
||||||
|
@ -367,7 +367,7 @@ impl LocalCluster {
|
||||||
&validator_keypair,
|
&validator_keypair,
|
||||||
&ledger_path,
|
&ledger_path,
|
||||||
&voting_keypair.pubkey(),
|
&voting_keypair.pubkey(),
|
||||||
&voting_keypair,
|
vec![voting_keypair.clone()],
|
||||||
&storage_keypair,
|
&storage_keypair,
|
||||||
Some(&self.entry_point_info),
|
Some(&self.entry_point_info),
|
||||||
true,
|
true,
|
||||||
|
@ -687,7 +687,7 @@ impl Cluster for LocalCluster {
|
||||||
&validator_info.keypair,
|
&validator_info.keypair,
|
||||||
&validator_info.ledger_path,
|
&validator_info.ledger_path,
|
||||||
&validator_info.voting_keypair.pubkey(),
|
&validator_info.voting_keypair.pubkey(),
|
||||||
&validator_info.voting_keypair,
|
vec![validator_info.voting_keypair.clone()],
|
||||||
&validator_info.storage_keypair,
|
&validator_info.storage_keypair,
|
||||||
entry_point_info,
|
entry_point_info,
|
||||||
true,
|
true,
|
||||||
|
|
|
@ -1,26 +1,26 @@
|
||||||
use log::*;
|
use log::*;
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use solana_sdk::pubkey::Pubkey;
|
use solana_sdk::{clock::Epoch, pubkey::Pubkey};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
||||||
pub struct AuthorizedVoters {
|
pub struct AuthorizedVoters {
|
||||||
authorized_voters: BTreeMap<u64, Pubkey>,
|
authorized_voters: BTreeMap<Epoch, Pubkey>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AuthorizedVoters {
|
impl AuthorizedVoters {
|
||||||
pub fn new(epoch: u64, pubkey: Pubkey) -> Self {
|
pub fn new(epoch: Epoch, pubkey: Pubkey) -> Self {
|
||||||
let mut authorized_voters = BTreeMap::new();
|
let mut authorized_voters = BTreeMap::new();
|
||||||
authorized_voters.insert(epoch, pubkey);
|
authorized_voters.insert(epoch, pubkey);
|
||||||
Self { authorized_voters }
|
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)
|
self.get_or_calculate_authorized_voter_for_epoch(epoch)
|
||||||
.map(|(pubkey, _)| pubkey)
|
.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);
|
let res = self.get_or_calculate_authorized_voter_for_epoch(epoch);
|
||||||
|
|
||||||
res.map(|(pubkey, existed)| {
|
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);
|
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
|
// Iterate through the keys in order, filtering out the ones
|
||||||
// less than the current epoch
|
// less than the current epoch
|
||||||
let expired_keys: Vec<_> = self
|
let expired_keys: Vec<_> = self
|
||||||
|
@ -72,14 +72,18 @@ impl AuthorizedVoters {
|
||||||
self.authorized_voters.len()
|
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()
|
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
|
// 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
|
// current epoch, and a bool indicating whether the entry for this epoch
|
||||||
// exists in the self.authorized_voter map
|
// 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);
|
let res = self.authorized_voters.get(&epoch);
|
||||||
if res.is_none() {
|
if res.is_none() {
|
||||||
// If no authorized voter has been set yet for this epoch,
|
// If no authorized voter has been set yet for this epoch,
|
||||||
|
|
|
@ -161,7 +161,7 @@ pub struct VoteState {
|
||||||
pub commission: u8,
|
pub commission: u8,
|
||||||
|
|
||||||
pub votes: VecDeque<Lockout>,
|
pub votes: VecDeque<Lockout>,
|
||||||
pub root_slot: Option<u64>,
|
pub root_slot: Option<Slot>,
|
||||||
|
|
||||||
/// the signer for vote transactions
|
/// the signer for vote transactions
|
||||||
authorized_voters: AuthorizedVoters,
|
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)
|
self.authorized_voters.get_authorized_voter(epoch)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -436,8 +436,8 @@ impl VoteState {
|
||||||
fn set_new_authorized_voter<F>(
|
fn set_new_authorized_voter<F>(
|
||||||
&mut self,
|
&mut self,
|
||||||
authorized_pubkey: &Pubkey,
|
authorized_pubkey: &Pubkey,
|
||||||
current_epoch: u64,
|
current_epoch: Epoch,
|
||||||
target_epoch: u64,
|
target_epoch: Epoch,
|
||||||
verify: F,
|
verify: F,
|
||||||
) -> Result<(), InstructionError>
|
) -> Result<(), InstructionError>
|
||||||
where
|
where
|
||||||
|
@ -494,7 +494,7 @@ impl VoteState {
|
||||||
Ok(())
|
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
|
let pubkey = self
|
||||||
.authorized_voters
|
.authorized_voters
|
||||||
.get_and_cache_authorized_voter_for_epoch(current_epoch)
|
.get_and_cache_authorized_voter_for_epoch(current_epoch)
|
||||||
|
|
|
@ -5,7 +5,7 @@ use clap::{
|
||||||
use log::*;
|
use log::*;
|
||||||
use rand::{thread_rng, Rng};
|
use rand::{thread_rng, Rng};
|
||||||
use solana_clap_utils::{
|
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},
|
input_validators::{is_keypair_or_ask_keyword, is_pubkey, is_pubkey_or_keypair, is_slot},
|
||||||
keypair::SKIP_SEED_PHRASE_VALIDATION_ARG,
|
keypair::SKIP_SEED_PHRASE_VALIDATION_ARG,
|
||||||
};
|
};
|
||||||
|
@ -271,70 +271,58 @@ fn get_rpc_node(
|
||||||
|
|
||||||
fn check_vote_account(
|
fn check_vote_account(
|
||||||
rpc_client: &RpcClient,
|
rpc_client: &RpcClient,
|
||||||
vote_pubkey: &Pubkey,
|
identity_pubkey: &Pubkey,
|
||||||
voting_pubkey: &Pubkey,
|
vote_account_address: &Pubkey,
|
||||||
node_pubkey: &Pubkey,
|
authorized_voter_pubkeys: &[Pubkey],
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let found_vote_account = rpc_client
|
let vote_account = rpc_client
|
||||||
.get_account(vote_pubkey)
|
.get_account(vote_account_address)
|
||||||
.map_err(|err| format!("Vote account not found: {}", err.to_string()))?;
|
.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!(
|
return Err(format!(
|
||||||
"not a vote account (owned by {}): {}",
|
"not a vote account (owned by {}): {}",
|
||||||
found_vote_account.owner, vote_pubkey
|
vote_account.owner, vote_account_address
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let found_node_account = rpc_client
|
let identity_account = rpc_client
|
||||||
.get_account(node_pubkey)
|
.get_account(identity_pubkey)
|
||||||
.map_err(|err| format!("Identity account not found: {}", err.to_string()))?;
|
.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);
|
let vote_state = solana_vote_program::vote_state::VoteState::from(&vote_account);
|
||||||
if let Some(found_vote_account) = found_vote_account {
|
if let Some(vote_state) = vote_state {
|
||||||
if found_vote_account.authorized_voters().is_empty() {
|
if vote_state.authorized_voters().is_empty() {
|
||||||
return Err("Vote account not yet initialized".to_string());
|
return Err("Vote account not yet initialized".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
let epoch_info = rpc_client
|
if vote_state.node_pubkey != *identity_pubkey {
|
||||||
.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 {
|
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"account's authorized voter ({}) does not match to the given voting keypair ({}).",
|
"vote account's identity ({}) does not match the validator's identity {}).",
|
||||||
authorized_voter, voting_pubkey
|
vote_state.node_pubkey, identity_pubkey
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if found_vote_account.node_pubkey != *node_pubkey {
|
|
||||||
return Err(format!(
|
for (_, vote_account_authorized_voter_pubkey) in vote_state.authorized_voters().iter() {
|
||||||
"account's node pubkey ({}) does not match to the given identity keypair ({}).",
|
if !authorized_voter_pubkeys.contains(&vote_account_authorized_voter_pubkey) {
|
||||||
found_vote_account.node_pubkey, node_pubkey
|
return Err(format!(
|
||||||
));
|
"authorized voter {} not available",
|
||||||
|
vote_account_authorized_voter_pubkey
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} 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
|
// 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!(
|
return Err(format!(
|
||||||
"unfunded identity account ({}): only {} lamports (needs more fund to vote)",
|
"underfunded identity account ({}): only {} lamports available",
|
||||||
node_pubkey, found_node_account.lamports
|
identity_pubkey, identity_account.lamports
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -414,29 +402,22 @@ pub fn main() {
|
||||||
Arg::with_name("identity")
|
Arg::with_name("identity")
|
||||||
.short("i")
|
.short("i")
|
||||||
.long("identity")
|
.long("identity")
|
||||||
.alias("identity-keypair") // --identity-keypair is legacy for <= v1.0.6 users
|
|
||||||
.value_name("PATH")
|
.value_name("PATH")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.validator(is_keypair_or_ask_keyword)
|
.validator(is_keypair_or_ask_keyword)
|
||||||
.help("Validator identity keypair"),
|
.help("Validator identity keypair"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("authorized_voter")
|
Arg::with_name("authorized_voter_keypairs")
|
||||||
.long("authorized-voter")
|
.long("authorized-voter")
|
||||||
.value_name("PATH")
|
.value_name("PATH")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.validator(is_keypair_or_ask_keyword)
|
.validator(is_keypair_or_ask_keyword)
|
||||||
.requires("vote_account")
|
.requires("vote_account")
|
||||||
.help("Authorized voter keypair [default: value of --identity]"),
|
.multiple(true)
|
||||||
)
|
.help("Include an additional authorized voter keypair. \
|
||||||
.arg(
|
May be specified multiple times. \
|
||||||
Arg::with_name("deprecated_voting_keypair")
|
[default: the --identity 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),
|
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("vote_account")
|
Arg::with_name("vote_account")
|
||||||
|
@ -445,7 +426,9 @@ pub fn main() {
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.validator(is_pubkey_or_keypair)
|
.validator(is_pubkey_or_keypair)
|
||||||
.requires("identity")
|
.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(
|
||||||
Arg::with_name("storage_keypair")
|
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 identity_keypair = Arc::new(keypair_of(&matches, "identity").unwrap_or_else(Keypair::new));
|
||||||
|
|
||||||
let authorized_voter = keypair_of(&matches, "authorized_voter")
|
let authorized_voter_keypairs = keypairs_of(&matches, "authorized_voter_keypairs")
|
||||||
.or_else(|| {
|
.map(|keypairs| keypairs.into_iter().map(Arc::new).collect())
|
||||||
// Legacy v1.0.6 argument support
|
.unwrap_or_else(|| vec![identity_keypair.clone()]);
|
||||||
keypair_of(&matches, "deprecated_voting_keypair")
|
|
||||||
})
|
|
||||||
.map(Arc::new)
|
|
||||||
.unwrap_or_else(|| identity_keypair.clone());
|
|
||||||
|
|
||||||
let storage_keypair = keypair_of(&matches, "storage_keypair").unwrap_or_else(Keypair::new);
|
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(|| {
|
let vote_account = pubkey_of(&matches, "vote_account").unwrap_or_else(|| {
|
||||||
if matches.is_present("deprecated_voting_keypair") {
|
warn!("--vote-account not specified, validator will not vote");
|
||||||
// Legacy v1.0.6 behaviour of using `--voting-keypair` as `--vote-account`
|
validator_config.voting_disabled = true;
|
||||||
keypair_of(&matches, "deprecated_voting_keypair")
|
Keypair::new().pubkey()
|
||||||
.unwrap()
|
|
||||||
.pubkey()
|
|
||||||
} else {
|
|
||||||
warn!("--vote-account not specified, validator will not vote");
|
|
||||||
validator_config.voting_disabled = true;
|
|
||||||
Keypair::new().pubkey()
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let dynamic_port_range =
|
let dynamic_port_range =
|
||||||
|
@ -1032,7 +1004,6 @@ pub fn main() {
|
||||||
tcp_listeners,
|
tcp_listeners,
|
||||||
&udp_sockets,
|
&udp_sockets,
|
||||||
);
|
);
|
||||||
|
|
||||||
if !no_genesis_fetch {
|
if !no_genesis_fetch {
|
||||||
let (cluster_info, gossip_exit_flag, gossip_service) = start_gossip_node(
|
let (cluster_info, gossip_exit_flag, gossip_service) = start_gossip_node(
|
||||||
&identity_keypair,
|
&identity_keypair,
|
||||||
|
@ -1109,13 +1080,22 @@ pub fn main() {
|
||||||
if !validator_config.voting_disabled && !no_check_vote_account {
|
if !validator_config.voting_disabled && !no_check_vote_account {
|
||||||
check_vote_account(
|
check_vote_account(
|
||||||
&rpc_client,
|
&rpc_client,
|
||||||
&vote_account,
|
|
||||||
&authorized_voter.pubkey(),
|
|
||||||
&identity_keypair.pubkey(),
|
&identity_keypair.pubkey(),
|
||||||
)
|
&vote_account,
|
||||||
} else {
|
&authorized_voter_keypairs.iter().map(|k| k.pubkey()).collect::<Vec<_>>(),
|
||||||
Ok(())
|
|
||||||
|
).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() {
|
if result.is_ok() {
|
||||||
|
@ -1153,7 +1133,7 @@ pub fn main() {
|
||||||
&identity_keypair,
|
&identity_keypair,
|
||||||
&ledger_path,
|
&ledger_path,
|
||||||
&vote_account,
|
&vote_account,
|
||||||
&authorized_voter,
|
authorized_voter_keypairs,
|
||||||
&Arc::new(storage_keypair),
|
&Arc::new(storage_keypair),
|
||||||
cluster_entrypoint.as_ref(),
|
cluster_entrypoint.as_ref(),
|
||||||
!skip_poh_verify,
|
!skip_poh_verify,
|
||||||
|
|
Loading…
Reference in New Issue