diff --git a/validator/src/admin_rpc_service.rs b/validator/src/admin_rpc_service.rs index 25b2ef20e3..2c32cc8e24 100644 --- a/validator/src/admin_rpc_service.rs +++ b/validator/src/admin_rpc_service.rs @@ -152,6 +152,10 @@ pub trait AdminRpc { #[rpc(meta, name = "addAuthorizedVoter")] fn add_authorized_voter(&self, meta: Self::Metadata, keypair_file: String) -> Result<()>; + #[rpc(meta, name = "addAuthorizedVoterFromBytes")] + fn add_authorized_voter_from_bytes(&self, meta: Self::Metadata, keypair: Vec) + -> Result<()>; + #[rpc(meta, name = "removeAllAuthorizedVoters")] fn remove_all_authorized_voters(&self, meta: Self::Metadata) -> Result<()>; @@ -163,6 +167,14 @@ pub trait AdminRpc { require_tower: bool, ) -> Result<()>; + #[rpc(meta, name = "setIdentityFromBytes")] + fn set_identity_from_bytes( + &self, + meta: Self::Metadata, + identity_keypair: Vec, + require_tower: bool, + ) -> Result<()>; + #[rpc(meta, name = "contactInfo")] fn contact_info(&self, meta: Self::Metadata) -> Result; } @@ -220,19 +232,24 @@ impl AdminRpc for AdminRpcImpl { let authorized_voter = read_keypair_file(keypair_file) .map_err(|err| jsonrpc_core::error::Error::invalid_params(format!("{}", err)))?; - let mut authorized_voter_keypairs = meta.authorized_voter_keypairs.write().unwrap(); + AdminRpcImpl::add_authorized_voter_keypair(meta, authorized_voter) + } - if authorized_voter_keypairs - .iter() - .any(|x| x.pubkey() == authorized_voter.pubkey()) - { - Err(jsonrpc_core::error::Error::invalid_params( - "Authorized voter already present", + fn add_authorized_voter_from_bytes( + &self, + meta: Self::Metadata, + keypair: Vec, + ) -> Result<()> { + debug!("add_authorized_voter_from_bytes request received"); + + let authorized_voter = Keypair::from_bytes(&keypair).map_err(|err| { + jsonrpc_core::error::Error::invalid_params(format!( + "Failed to read authorized voter keypair from provided byte array: {}", + err )) - } else { - authorized_voter_keypairs.push(Arc::new(authorized_voter)); - Ok(()) - } + })?; + + AdminRpcImpl::add_authorized_voter_keypair(meta, authorized_voter) } fn remove_all_authorized_voters(&self, meta: Self::Metadata) -> Result<()> { @@ -256,6 +273,57 @@ impl AdminRpc for AdminRpcImpl { )) })?; + AdminRpcImpl::set_identity_keypair(meta, identity_keypair, require_tower) + } + + fn set_identity_from_bytes( + &self, + meta: Self::Metadata, + identity_keypair: Vec, + require_tower: bool, + ) -> Result<()> { + debug!("set_identity_from_bytes request received"); + + let identity_keypair = Keypair::from_bytes(&identity_keypair).map_err(|err| { + jsonrpc_core::error::Error::invalid_params(format!( + "Failed to read identity keypair from provided byte array: {}", + err + )) + })?; + + AdminRpcImpl::set_identity_keypair(meta, identity_keypair, require_tower) + } + + fn contact_info(&self, meta: Self::Metadata) -> Result { + meta.with_post_init(|post_init| Ok(post_init.cluster_info.my_contact_info().into())) + } +} + +impl AdminRpcImpl { + fn add_authorized_voter_keypair( + meta: AdminRpcRequestMetadata, + authorized_voter: Keypair, + ) -> Result<()> { + let mut authorized_voter_keypairs = meta.authorized_voter_keypairs.write().unwrap(); + + if authorized_voter_keypairs + .iter() + .any(|x| x.pubkey() == authorized_voter.pubkey()) + { + Err(jsonrpc_core::error::Error::invalid_params( + "Authorized voter already present", + )) + } else { + authorized_voter_keypairs.push(Arc::new(authorized_voter)); + Ok(()) + } + } + + fn set_identity_keypair( + meta: AdminRpcRequestMetadata, + identity_keypair: Keypair, + require_tower: bool, + ) -> Result<()> { meta.with_post_init(|post_init| { if require_tower { let _ = Tower::restore(meta.tower_storage.as_ref(), &identity_keypair.pubkey()) @@ -276,10 +344,6 @@ impl AdminRpc for AdminRpcImpl { Ok(()) }) } - - fn contact_info(&self, meta: Self::Metadata) -> Result { - meta.with_post_init(|post_init| Ok(post_init.cluster_info.my_contact_info().into())) - } } // Start the Admin RPC interface diff --git a/validator/src/main.rs b/validator/src/main.rs index f2f1767c0c..69fa203434 100644 --- a/validator/src/main.rs +++ b/validator/src/main.rs @@ -66,7 +66,7 @@ use { commitment_config::CommitmentConfig, hash::Hash, pubkey::Pubkey, - signature::{Keypair, Signer}, + signature::{read_keypair, Keypair, Signer}, }, solana_send_transaction_service::send_transaction_service::{ self, MAX_BATCH_SEND_RATE_MS, MAX_TRANSACTION_BATCH_SIZE, @@ -1822,9 +1822,11 @@ pub fn main() { Arg::with_name("authorized_voter_keypair") .index(1) .value_name("KEYPAIR") + .required(false) .takes_value(true) .validator(is_keypair) - .help("Keypair of the authorized voter to add"), + .help("Path to keypair of the authorized voter to add \ + [default: read JSON keypair from stdin]"), ) .after_help("Note: the new authorized voter only applies to the \ currently running validator instance") @@ -1867,9 +1869,11 @@ pub fn main() { Arg::with_name("identity") .index(1) .value_name("KEYPAIR") + .required(false) .takes_value(true) .validator(is_keypair) - .help("Validator identity keypair") + .help("Path to validator identity keypair \ + [default: read JSON keypair from stdin]") ) .arg( clap::Arg::with_name("require_tower") @@ -1938,36 +1942,64 @@ pub fn main() { ("authorized-voter", Some(authorized_voter_subcommand_matches)) => { match authorized_voter_subcommand_matches.subcommand() { ("add", Some(subcommand_matches)) => { - let authorized_voter_keypair = - value_t_or_exit!(subcommand_matches, "authorized_voter_keypair", String); + if let Some(authorized_voter_keypair) = + value_t!(subcommand_matches, "authorized_voter_keypair", String).ok() + { + let authorized_voter_keypair = fs::canonicalize(&authorized_voter_keypair) + .unwrap_or_else(|err| { + println!( + "Unable to access path: {}: {:?}", + authorized_voter_keypair, err + ); + exit(1); + }); + println!( + "Adding authorized voter path: {}", + authorized_voter_keypair.display() + ); - let authorized_voter_keypair = fs::canonicalize(&authorized_voter_keypair) - .unwrap_or_else(|err| { - println!( - "Unable to access path: {}: {:?}", - authorized_voter_keypair, err - ); - exit(1); - }); - println!( - "Adding authorized voter: {}", - authorized_voter_keypair.display() - ); + let admin_client = admin_rpc_service::connect(&ledger_path); + admin_rpc_service::runtime() + .block_on(async move { + admin_client + .await? + .add_authorized_voter( + authorized_voter_keypair.display().to_string(), + ) + .await + }) + .unwrap_or_else(|err| { + println!("addAuthorizedVoter request failed: {}", err); + exit(1); + }); + } else { + let mut stdin = std::io::stdin(); + let authorized_voter_keypair = + read_keypair(&mut stdin).unwrap_or_else(|err| { + println!("Unable to read JSON keypair from stdin: {:?}", err); + exit(1); + }); + println!( + "Adding authorized voter: {}", + authorized_voter_keypair.pubkey() + ); + + let admin_client = admin_rpc_service::connect(&ledger_path); + admin_rpc_service::runtime() + .block_on(async move { + admin_client + .await? + .add_authorized_voter_from_bytes(Vec::from( + authorized_voter_keypair.to_bytes(), + )) + .await + }) + .unwrap_or_else(|err| { + println!("addAuthorizedVoterFromBytes request failed: {}", err); + exit(1); + }); + } - let admin_client = admin_rpc_service::connect(&ledger_path); - admin_rpc_service::runtime() - .block_on(async move { - admin_client - .await? - .add_authorized_voter( - authorized_voter_keypair.display().to_string(), - ) - .await - }) - .unwrap_or_else(|err| { - println!("addAuthorizedVoter request failed: {}", err); - exit(1); - }); return; } ("remove-all", _) => { @@ -2049,26 +2081,54 @@ pub fn main() { } ("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| { - println!("Unable to access path: {}: {:?}", identity_keypair, err); - exit(1); - }); - println!("Validator identity: {}", identity_keypair.display()); - - let admin_client = admin_rpc_service::connect(&ledger_path); - admin_rpc_service::runtime() - .block_on(async move { - admin_client - .await? - .set_identity(identity_keypair.display().to_string(), require_tower) - .await - }) - .unwrap_or_else(|err| { - println!("setIdentity request failed: {}", err); + if let Some(identity_keypair) = value_t!(subcommand_matches, "identity", String).ok() { + let identity_keypair = fs::canonicalize(&identity_keypair).unwrap_or_else(|err| { + println!("Unable to access path: {}: {:?}", identity_keypair, err); exit(1); }); + println!( + "New validator identity path: {}", + identity_keypair.display() + ); + + let admin_client = admin_rpc_service::connect(&ledger_path); + admin_rpc_service::runtime() + .block_on(async move { + admin_client + .await? + .set_identity(identity_keypair.display().to_string(), require_tower) + .await + }) + .unwrap_or_else(|err| { + println!("setIdentity request failed: {}", err); + exit(1); + }); + } else { + let mut stdin = std::io::stdin(); + let identity_keypair = read_keypair(&mut stdin).unwrap_or_else(|err| { + println!("Unable to read JSON keypair from stdin: {:?}", err); + exit(1); + }); + println!("New validator identity: {}", identity_keypair.pubkey()); + + let admin_client = admin_rpc_service::connect(&ledger_path); + admin_rpc_service::runtime() + .block_on(async move { + admin_client + .await? + .set_identity_from_bytes( + Vec::from(identity_keypair.to_bytes()), + require_tower, + ) + .await + }) + .unwrap_or_else(|err| { + println!("setIdentityFromBytes request failed: {}", err); + exit(1); + }); + }; + return; } ("set-log-filter", Some(subcommand_matches)) => {