Make solana-validator check vote account at start (#6790)

* Make solana-validator check vote account at start

* Don't abort tests...

* Fix test breakage

* Remove extra semicolon

* Attempt to fix cluster-tests

* rustfmt

* Change behavior of vote_account ephemeral pubkeys

* save

* clean up

* clean up

* rustfmt && clippy

* Reorder for simpler diff

* Fix rebase...

* Fix message a bit

* Still more rebase fixes....

* Fix yet more

* Use find_map over filter_map & next and revert message

* More through error checks

* rustfmt & clippy

* Revert

* Revert core/src/validator.rs

* Cleanup

* Cleanup

* Cleanup

* Rebase fix

* Make clippy & rustfmt happy

* save

* Clean up

* Show rpc error detail

* Check node lamports only after pubkey matching

* rustfmt
This commit is contained in:
Ryo Onodera 2019-11-13 16:48:55 +09:00 committed by GitHub
parent 361eab1bf7
commit e5c5f34f9a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 122 additions and 35 deletions

View File

@ -19,6 +19,7 @@ use solana_ledger::bank_forks::SnapshotConfig;
use solana_perf::recycler::enable_recycler_warming;
use solana_sdk::clock::Slot;
use solana_sdk::hash::Hash;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::{read_keypair_file, Keypair, KeypairUtil};
use std::fs::{self, File};
use std::io::{self, Read};
@ -168,12 +169,9 @@ fn download_tar_bz2(
Ok(())
}
fn initialize_ledger_path(
fn create_rpc_client(
entrypoint: &ContactInfo,
ledger_path: &Path,
no_genesis_fetch: bool,
no_snapshot_fetch: bool,
) -> Result<Hash, String> {
) -> Result<(std::net::SocketAddr, RpcClient), String> {
let (nodes, _archivers) = discover(
&entrypoint.gossip,
Some(1),
@ -184,28 +182,86 @@ fn initialize_ledger_path(
)
.map_err(|err| err.to_string())?;
let rpc_addr = nodes
.iter()
.filter_map(|contact_info| {
if contact_info.gossip == entrypoint.gossip
&& ContactInfo::is_valid_address(&contact_info.rpc)
{
Some(contact_info.rpc)
} else {
None
}
})
.next()
.unwrap_or_else(|| {
error!(
"Entrypoint ({:?}) is not running the RPC service",
entrypoint.gossip
);
exit(1);
});
let rpc_addr = nodes.iter().find_map(|contact_info| {
if contact_info.gossip == entrypoint.gossip
&& ContactInfo::is_valid_address(&contact_info.rpc)
{
Some(contact_info.rpc)
} else {
None
}
});
let client = RpcClient::new_socket(rpc_addr);
let genesis_hash = client.get_genesis_hash().map_err(|err| err.to_string())?;
if let Some(rpc_addr) = rpc_addr {
Ok((rpc_addr, RpcClient::new_socket(rpc_addr)))
} else {
Err(format!(
"Entrypoint ({:?}) is not running the RPC service",
entrypoint.gossip
))
}
}
fn check_vote_account(
rpc_client: &RpcClient,
vote_pubkey: &Pubkey,
voting_pubkey: &Pubkey,
node_pubkey: &Pubkey,
) -> Result<(), String> {
let found_vote_account = rpc_client
.get_account(vote_pubkey)
.map_err(|err| format!("Failed to get vote account: {}", err.to_string()))?;
if found_vote_account.owner != solana_vote_api::id() {
return Err(format!(
"not a vote account (owned by {}): {}",
found_vote_account.owner, vote_pubkey
));
}
let found_node_account = rpc_client
.get_account(node_pubkey)
.map_err(|err| format!("Failed to get identity account: {}", err.to_string()))?;
let found_vote_account = solana_vote_api::vote_state::VoteState::from(&found_vote_account);
if let Some(found_vote_account) = found_vote_account {
if found_vote_account.authorized_voter != *voting_pubkey {
return Err(format!(
"account's authorized voter ({}) does not match to the given voting keypair ({}).",
found_vote_account.authorized_voter, voting_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
));
}
} else {
return Err(format!("invalid vote account data: {}", vote_pubkey));
}
// Maybe we can calculate minimum voting fee; rather than 1 lamport
if found_node_account.lamports <= 1 {
return Err(format!(
"unfunded identity account ({}): only {} lamports (needs more fund to vote)",
node_pubkey, found_node_account.lamports
));
}
Ok(())
}
fn initialize_ledger_path(
rpc_addr: &std::net::SocketAddr,
rpc_client: &RpcClient,
ledger_path: &Path,
no_genesis_fetch: bool,
no_snapshot_fetch: bool,
) -> Result<Hash, String> {
let genesis_hash = rpc_client
.get_genesis_hash()
.map_err(|err| err.to_string())?;
fs::create_dir_all(ledger_path).map_err(|err| err.to_string())?;
@ -228,7 +284,7 @@ fn initialize_ledger_path(
.unwrap_or_else(|err| warn!("Unable to fetch snapshot: {:?}", err));
}
match client.get_slot() {
match rpc_client.get_slot() {
Ok(slot) => info!("Entrypoint currently at slot {}", slot),
Err(err) => warn!("Failed to get_slot from entrypoint: {}", err),
}
@ -265,7 +321,7 @@ pub fn main() {
.value_name("PATH")
.takes_value(true)
.validator(is_keypair)
.help("File containing the authorized voting keypair. Default is an ephemeral keypair"),
.help("File containing the authorized voting keypair. Default is an ephemeral keypair, which may disable voting without --vote-account."),
)
.arg(
Arg::with_name("vote_account")
@ -273,7 +329,7 @@ pub fn main() {
.value_name("PUBKEY")
.takes_value(true)
.validator(is_pubkey_or_keypair)
.help("Public key of the vote account to vote with. Default is the public key of the voting keypair"),
.help("Public key of the vote account to vote with. Default is the public key of --voting-keypair"),
)
.arg(
Arg::with_name("storage_keypair")
@ -448,12 +504,14 @@ pub fn main() {
Keypair::new()
};
let mut ephemeral_voting_keypair = false;
let voting_keypair = if let Some(identity) = matches.value_of("voting_keypair") {
read_keypair_file(identity).unwrap_or_else(|err| {
error!("{}: Unable to open keypair file: {}", err, identity);
exit(1);
})
} else {
ephemeral_voting_keypair = true;
Keypair::new()
};
let storage_keypair = if let Some(storage_keypair) = matches.value_of("storage_keypair") {
@ -465,9 +523,6 @@ pub fn main() {
Keypair::new()
};
let vote_account =
pubkey_of(&matches, "vote_account").unwrap_or_else(|| voting_keypair.pubkey());
let ledger_path = PathBuf::from(matches.value_of("ledger_path").unwrap());
let entrypoint = matches.value_of("entrypoint");
let init_complete_file = matches.value_of("init_complete_file");
@ -481,8 +536,6 @@ pub fn main() {
validator_config.dev_sigverify_disabled = matches.is_present("dev_no_sigverify");
validator_config.dev_halt_at_slot = value_t!(matches, "dev_halt_at_slot", Slot).ok();
validator_config.voting_disabled = matches.is_present("no_voting");
validator_config.rpc_config.enable_validator_exit = matches.is_present("enable_rpc_exit");
validator_config.rpc_config.drone_addr = matches.value_of("rpc_drone_addr").map(|address| {
@ -578,6 +631,21 @@ pub fn main() {
]
.join(","),
);
if matches.is_present("no_voting") {
validator_config.voting_disabled = true;
}
let vote_account = pubkey_of(&matches, "vote_account").unwrap_or_else(|| {
// Disable voting because normal (=not bootstrapping) validator rejects
// non-voting accounts (= ephemeral keypairs).
if ephemeral_voting_keypair && entrypoint.is_some() {
warn!("Disabled voting due to the use of ephemeral key for vote account");
validator_config.voting_disabled = true;
};
voting_keypair.pubkey()
});
solana_metrics::set_host_id(identity_keypair.pubkey().to_string());
solana_metrics::set_panic_hook("validator");
@ -653,8 +721,27 @@ pub fn main() {
&udp_sockets,
);
let (rpc_addr, rpc_client) = create_rpc_client(cluster_entrypoint).unwrap_or_else(|err| {
error!("unable to create rpc client: {}", err);
std::process::exit(1);
});
if !validator_config.voting_disabled {
check_vote_account(
&rpc_client,
&vote_account,
&voting_keypair.pubkey(),
&identity_keypair.pubkey(),
)
.unwrap_or_else(|err| {
error!("Failed to check vote account: {}", err);
exit(1);
});
}
let genesis_hash = initialize_ledger_path(
cluster_entrypoint,
&rpc_addr,
&rpc_client,
&ledger_path,
no_genesis_fetch,
no_snapshot_fetch,