`solana-validator set-identity` now supports the `--require-tower` flag

This commit is contained in:
Michael Vines 2022-02-14 10:27:11 -08:00
parent 6ae87109d2
commit a6d736572c
5 changed files with 113 additions and 35 deletions

View File

@ -297,6 +297,7 @@ pub struct Validator {
tvu: Tvu,
ip_echo_server: Option<solana_net_utils::IpEchoServer>,
pub cluster_info: Arc<ClusterInfo>,
pub bank_forks: Arc<RwLock<BankForks>>,
accountsdb_repl_service: Option<AccountsDbReplService>,
accountsdb_plugin_service: Option<AccountsDbPluginService>,
}
@ -897,7 +898,7 @@ impl Validator {
&exit,
node.info.shred_version,
vote_tracker,
bank_forks,
bank_forks.clone(),
verified_vote_sender,
gossip_verified_vote_hash_sender,
replay_vote_receiver,
@ -934,6 +935,7 @@ impl Validator {
ip_echo_server,
validator_exit: config.validator_exit.clone(),
cluster_info,
bank_forks,
accountsdb_repl_service,
accountsdb_plugin_service,
}
@ -975,6 +977,7 @@ impl Validator {
}
pub fn join(self) {
drop(self.bank_forks);
drop(self.cluster_info);
self.poh_service.join().expect("poh_service");

View File

@ -16,7 +16,7 @@ use {
solana_net_utils::PortRange,
solana_rpc::{rpc::JsonRpcConfig, rpc_pubsub_service::PubSubConfig},
solana_runtime::{
genesis_utils::create_genesis_config_with_leader_ex,
bank_forks::BankForks, genesis_utils::create_genesis_config_with_leader_ex,
hardened_unpack::MAX_GENESIS_ARCHIVE_UNPACKED_SIZE, snapshot_config::SnapshotConfig,
},
solana_sdk::{
@ -833,6 +833,10 @@ impl TestValidator {
pub fn cluster_info(&self) -> Arc<ClusterInfo> {
self.validator.as_ref().unwrap().cluster_info.clone()
}
pub fn bank_forks(&self) -> Arc<RwLock<BankForks>> {
self.validator.as_ref().unwrap().bank_forks.clone()
}
}
impl Drop for TestValidator {

View File

@ -10,8 +10,10 @@ use {
consensus::Tower, tower_storage::TowerStorage, validator::ValidatorStartProgress,
},
solana_gossip::{cluster_info::ClusterInfo, contact_info::ContactInfo},
solana_runtime::bank_forks::BankForks,
solana_sdk::{
exit::Exit,
pubkey::Pubkey,
signature::{read_keypair_file, Keypair, Signer},
},
std::{
@ -24,6 +26,13 @@ use {
},
};
#[derive(Clone)]
pub struct AdminRpcRequestMetadataPostInit {
pub cluster_info: Arc<ClusterInfo>,
pub bank_forks: Arc<RwLock<BankForks>>,
pub vote_account: Pubkey,
}
#[derive(Clone)]
pub struct AdminRpcRequestMetadata {
pub rpc_addr: Option<SocketAddr>,
@ -31,11 +40,26 @@ pub struct AdminRpcRequestMetadata {
pub start_progress: Arc<RwLock<ValidatorStartProgress>>,
pub validator_exit: Arc<RwLock<Exit>>,
pub authorized_voter_keypairs: Arc<RwLock<Vec<Arc<Keypair>>>>,
pub cluster_info: Arc<RwLock<Option<Arc<ClusterInfo>>>>,
pub tower_storage: Arc<dyn TowerStorage>,
pub post_init: Arc<RwLock<Option<AdminRpcRequestMetadataPostInit>>>,
}
impl Metadata for AdminRpcRequestMetadata {}
impl AdminRpcRequestMetadata {
fn with_post_init<F, R>(&self, func: F) -> Result<R>
where
F: FnOnce(&AdminRpcRequestMetadataPostInit) -> Result<R>,
{
if let Some(post_init) = self.post_init.read().unwrap().as_ref() {
func(post_init)
} else {
Err(jsonrpc_core::error::Error::invalid_params(
"Retry once validator start up is complete",
))
}
}
}
#[derive(Debug, Deserialize, Serialize)]
pub struct AdminRpcContactInfo {
pub id: String,
@ -132,7 +156,12 @@ pub trait AdminRpc {
fn remove_all_authorized_voters(&self, meta: Self::Metadata) -> Result<()>;
#[rpc(meta, name = "setIdentity")]
fn set_identity(&self, meta: Self::Metadata, keypair_file: String) -> Result<()>;
fn set_identity(
&self,
meta: Self::Metadata,
keypair_file: String,
require_tower: bool,
) -> Result<()>;
#[rpc(meta, name = "contactInfo")]
fn contact_info(&self, meta: Self::Metadata) -> Result<AdminRpcContactInfo>;
@ -212,7 +241,12 @@ impl AdminRpc for AdminRpcImpl {
Ok(())
}
fn set_identity(&self, meta: Self::Metadata, keypair_file: String) -> Result<()> {
fn set_identity(
&self,
meta: Self::Metadata,
keypair_file: String,
require_tower: bool,
) -> Result<()> {
debug!("set_identity request received");
let identity_keypair = read_keypair_file(&keypair_file).map_err(|err| {
@ -222,35 +256,55 @@ impl AdminRpc for AdminRpcImpl {
))
})?;
// Ensure a Tower exists for the new identity and exit gracefully.
// ReplayStage will be less forgiving if it fails to load the new tower.
Tower::restore(meta.tower_storage.as_ref(), &identity_keypair.pubkey()).map_err(|err| {
jsonrpc_core::error::Error::invalid_params(format!(
"Unable to load tower file for new identity: {}",
err
))
})?;
meta.with_post_init(|post_init| {
// Ensure a Tower exists for the new identity and exit gracefully.
// ReplayStage will be less forgiving if it fails to load the new tower.
if let Err(err) =
Tower::restore(meta.tower_storage.as_ref(), &identity_keypair.pubkey()).map_err(
|err| {
jsonrpc_core::error::Error::invalid_params(format!(
"Unable to load tower file for identity {}: {}",
identity_keypair.pubkey(),
err
))
},
)
{
if require_tower {
return Err(err);
}
let root_bank = post_init.bank_forks.read().unwrap().root_bank();
let mut tower = Tower::new(
&identity_keypair.pubkey(),
&post_init.vote_account,
root_bank.slot(),
&root_bank,
);
// Forge a single vote to pacify `Tower::adjust_lockouts_after_replay` when its called
// by replay_stage
tower.record_bank_vote(&root_bank, &post_init.vote_account);
tower
.save(meta.tower_storage.as_ref(), &identity_keypair)
.map_err(|err| {
jsonrpc_core::error::Error::invalid_params(format!(
"Unable to create default tower file for ephemeral identity: {}",
err
))
})?;
}
if let Some(cluster_info) = meta.cluster_info.read().unwrap().as_ref() {
solana_metrics::set_host_id(identity_keypair.pubkey().to_string());
cluster_info.set_keypair(Arc::new(identity_keypair));
warn!("Identity set to {}", cluster_info.id());
post_init
.cluster_info
.set_keypair(Arc::new(identity_keypair));
warn!("Identity set to {}", post_init.cluster_info.id());
Ok(())
} else {
Err(jsonrpc_core::error::Error::invalid_params(
"Retry once validator start up is complete",
))
}
})
}
fn contact_info(&self, meta: Self::Metadata) -> Result<AdminRpcContactInfo> {
if let Some(cluster_info) = meta.cluster_info.read().unwrap().as_ref() {
Ok(cluster_info.my_contact_info().into())
} else {
Err(jsonrpc_core::error::Error::invalid_params(
"Retry once validator start up is complete",
))
}
meta.with_post_init(|post_init| Ok(post_init.cluster_info.my_contact_info().into()))
}
}

View File

@ -589,7 +589,7 @@ fn main() {
let tower_storage = Arc::new(FileTowerStorage::new(ledger_path.clone()));
let admin_service_cluster_info = Arc::new(RwLock::new(None));
let admin_service_post_init = Arc::new(RwLock::new(None));
admin_rpc_service::run(
&ledger_path,
admin_rpc_service::AdminRpcRequestMetadata {
@ -601,7 +601,7 @@ fn main() {
start_time: std::time::SystemTime::now(),
validator_exit: genesis.validator_exit.clone(),
authorized_voter_keypairs: genesis.authorized_voter_keypairs.clone(),
cluster_info: admin_service_cluster_info.clone(),
post_init: admin_service_post_init.clone(),
tower_storage: tower_storage.clone(),
},
);
@ -695,7 +695,12 @@ fn main() {
match genesis.start_with_mint_address(mint_address, socket_addr_space) {
Ok(test_validator) => {
*admin_service_cluster_info.write().unwrap() = Some(test_validator.cluster_info());
*admin_service_post_init.write().unwrap() =
Some(admin_rpc_service::AdminRpcRequestMetadataPostInit {
bank_forks: test_validator.bank_forks(),
cluster_info: test_validator.cluster_info(),
vote_account: test_validator.vote_account_address(),
});
if let Some(dashboard) = dashboard {
dashboard.run(Duration::from_millis(250));
}

View File

@ -1668,6 +1668,12 @@ pub fn main() {
.validator(is_keypair)
.help("Validator identity keypair")
)
.arg(
clap::Arg::with_name("require_tower")
.long("require-tower")
.takes_value(false)
.help("Refuse to set the validator identity if saved tower state is not found"),
)
.after_help("Note: the new identity only applies to the \
currently running validator instance")
)
@ -1839,6 +1845,7 @@ pub fn main() {
return;
}
("set-identity", Some(subcommand_matches)) => {
let require_tower = subcommand_matches.is_present("require_tower");
let identity_keypair = value_t_or_exit!(subcommand_matches, "identity", String);
let identity_keypair = fs::canonicalize(&identity_keypair).unwrap_or_else(|err| {
@ -1852,7 +1859,7 @@ pub fn main() {
.block_on(async move {
admin_client
.await?
.set_identity(identity_keypair.display().to_string())
.set_identity(identity_keypair.display().to_string(), require_tower)
.await
})
.unwrap_or_else(|err| {
@ -2504,7 +2511,7 @@ pub fn main() {
let _ledger_write_guard = lock_ledger(&ledger_path, &mut ledger_lock);
let start_progress = Arc::new(RwLock::new(ValidatorStartProgress::default()));
let admin_service_cluster_info = Arc::new(RwLock::new(None));
let admin_service_post_init = Arc::new(RwLock::new(None));
admin_rpc_service::run(
&ledger_path,
admin_rpc_service::AdminRpcRequestMetadata {
@ -2513,7 +2520,7 @@ pub fn main() {
validator_exit: validator_config.validator_exit.clone(),
start_progress: start_progress.clone(),
authorized_voter_keypairs: authorized_voter_keypairs.clone(),
cluster_info: admin_service_cluster_info.clone(),
post_init: admin_service_post_init.clone(),
tower_storage: validator_config.tower_storage.clone(),
},
);
@ -2658,7 +2665,12 @@ pub fn main() {
start_progress,
socket_addr_space,
);
*admin_service_cluster_info.write().unwrap() = Some(validator.cluster_info.clone());
*admin_service_post_init.write().unwrap() =
Some(admin_rpc_service::AdminRpcRequestMetadataPostInit {
bank_forks: validator.bank_forks.clone(),
cluster_info: validator.cluster_info.clone(),
vote_account,
});
if let Some(filename) = init_complete_file {
File::create(filename).unwrap_or_else(|_| {