agave_validator/commands/set_identity/
mod.rs1use {
2 crate::{
3 admin_rpc_service,
4 commands::{FromClapArgMatches, Result},
5 },
6 clap::{value_t, App, Arg, ArgMatches, SubCommand},
7 solana_clap_utils::input_validators::is_keypair,
8 solana_keypair::read_keypair,
9 solana_signer::Signer,
10 std::{fs, path::Path},
11};
12
13const COMMAND: &str = "set-identity";
14
15#[derive(Debug, PartialEq)]
16#[cfg_attr(test, derive(Default))]
17pub struct SetIdentityArgs {
18 pub identity: Option<String>,
19 pub require_tower: bool,
20}
21
22impl FromClapArgMatches for SetIdentityArgs {
23 fn from_clap_arg_match(matches: &ArgMatches) -> Result<Self> {
24 Ok(SetIdentityArgs {
25 identity: value_t!(matches, "identity", String).ok(),
26 require_tower: matches.is_present("require_tower"),
27 })
28 }
29}
30pub fn command<'a>() -> App<'a, 'a> {
31 SubCommand::with_name(COMMAND)
32 .about("Set the validator identity")
33 .arg(
34 Arg::with_name("identity")
35 .index(1)
36 .value_name("KEYPAIR")
37 .required(false)
38 .takes_value(true)
39 .validator(is_keypair)
40 .help("Path to validator identity keypair [default: read JSON keypair from stdin]"),
41 )
42 .arg(
43 clap::Arg::with_name("require_tower")
44 .long("require-tower")
45 .takes_value(false)
46 .help("Refuse to set the validator identity if saved tower state is not found"),
47 )
48 .after_help(
49 "Note: the new identity only applies to the currently running validator instance",
50 )
51}
52
53pub fn execute(matches: &ArgMatches, ledger_path: &Path) -> Result<()> {
54 let SetIdentityArgs {
55 identity,
56 require_tower,
57 } = SetIdentityArgs::from_clap_arg_match(matches)?;
58
59 if let Some(identity_keypair) = identity {
60 let identity_keypair = fs::canonicalize(&identity_keypair)?;
61
62 println!(
63 "New validator identity path: {}",
64 identity_keypair.display()
65 );
66
67 let admin_client = admin_rpc_service::connect(ledger_path);
68 admin_rpc_service::runtime().block_on(async move {
69 admin_client
70 .await?
71 .set_identity(identity_keypair.display().to_string(), require_tower)
72 .await
73 })?;
74 } else {
75 let mut stdin = std::io::stdin();
76 let identity_keypair = read_keypair(&mut stdin)?;
77
78 println!("New validator identity: {}", identity_keypair.pubkey());
79
80 let admin_client = admin_rpc_service::connect(ledger_path);
81 admin_rpc_service::runtime().block_on(async move {
82 admin_client
83 .await?
84 .set_identity_from_bytes(Vec::from(identity_keypair.to_bytes()), require_tower)
85 .await
86 })?;
87 }
88
89 Ok(())
90}
91
92#[cfg(test)]
93mod tests {
94 use {
95 super::*, crate::commands::tests::verify_args_struct_by_command, solana_keypair::Keypair,
96 };
97
98 #[test]
99 fn verify_args_struct_by_command_set_identity_default() {
100 verify_args_struct_by_command(command(), vec![COMMAND], SetIdentityArgs::default());
101 }
102
103 #[test]
104 fn verify_args_struct_by_command_set_identity_with_identity_file() {
105 let tmp_dir = tempfile::tempdir().unwrap();
107 let file = tmp_dir.path().join("id.json");
108 let keypair = Keypair::new();
109 solana_keypair::write_keypair_file(&keypair, &file).unwrap();
110
111 verify_args_struct_by_command(
112 command(),
113 vec![COMMAND, file.to_str().unwrap()],
114 SetIdentityArgs {
115 identity: Some(file.to_str().unwrap().to_string()),
116 ..SetIdentityArgs::default()
117 },
118 );
119 }
120
121 #[test]
122 fn verify_args_struct_by_command_set_identity_with_require_tower() {
123 verify_args_struct_by_command(
124 command(),
125 vec![COMMAND, "--require-tower"],
126 SetIdentityArgs {
127 require_tower: true,
128 ..SetIdentityArgs::default()
129 },
130 );
131 }
132}