agave_validator/commands/authorized_voter/
mod.rs1use {
2 crate::{
3 admin_rpc_service,
4 commands::{FromClapArgMatches, Result},
5 },
6 clap::{value_t, App, AppSettings, 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 = "authorized-voter";
14
15#[derive(Debug, PartialEq)]
16#[cfg_attr(test, derive(Default))]
17pub struct AuthorizedVoterAddArgs {
18 pub authorized_voter_keypair: Option<String>,
19}
20
21impl FromClapArgMatches for AuthorizedVoterAddArgs {
22 fn from_clap_arg_match(matches: &ArgMatches) -> Result<Self> {
23 Ok(AuthorizedVoterAddArgs {
24 authorized_voter_keypair: value_t!(matches, "authorized_voter_keypair", String).ok(),
25 })
26 }
27}
28
29pub fn command<'a>() -> App<'a, 'a> {
30 SubCommand::with_name(COMMAND)
31 .about("Adjust the validator authorized voters")
32 .setting(AppSettings::SubcommandRequiredElseHelp)
33 .setting(AppSettings::InferSubcommands)
34 .subcommand(
35 SubCommand::with_name("add")
36 .about("Add an authorized voter")
37 .arg(
38 Arg::with_name("authorized_voter_keypair")
39 .index(1)
40 .value_name("KEYPAIR")
41 .required(false)
42 .takes_value(true)
43 .validator(is_keypair)
44 .help(
45 "Path to keypair of the authorized voter to add [default: read JSON keypair from stdin]",
46 ),
47 )
48 .after_help(
49 "Note: the new authorized voter only applies to the currently running validator instance",
50 ),
51 )
52 .subcommand(
53 SubCommand::with_name("remove-all")
54 .about("Remove all authorized voters")
55 .after_help(
56 "Note: the removal only applies to the currently running validator instance",
57 ),
58 )
59}
60
61pub fn execute(matches: &ArgMatches, ledger_path: &Path) -> Result<()> {
62 match matches.subcommand() {
63 ("add", Some(subcommand_matches)) => {
64 let authorized_voter_add_args =
65 AuthorizedVoterAddArgs::from_clap_arg_match(subcommand_matches)?;
66
67 if let Some(authorized_voter_keypair) =
68 authorized_voter_add_args.authorized_voter_keypair
69 {
70 let authorized_voter_keypair = fs::canonicalize(&authorized_voter_keypair)?;
71 println!(
72 "Adding authorized voter path: {}",
73 authorized_voter_keypair.display()
74 );
75
76 let admin_client = admin_rpc_service::connect(ledger_path);
77 admin_rpc_service::runtime().block_on(async move {
78 admin_client
79 .await?
80 .add_authorized_voter(authorized_voter_keypair.display().to_string())
81 .await
82 })?;
83 } else {
84 let mut stdin = std::io::stdin();
85 let authorized_voter_keypair = read_keypair(&mut stdin)?;
86 println!(
87 "Adding authorized voter: {}",
88 authorized_voter_keypair.pubkey()
89 );
90
91 let admin_client = admin_rpc_service::connect(ledger_path);
92 admin_rpc_service::runtime().block_on(async move {
93 admin_client
94 .await?
95 .add_authorized_voter_from_bytes(Vec::from(
96 authorized_voter_keypair.to_bytes(),
97 ))
98 .await
99 })?;
100 }
101 }
102 ("remove-all", _) => {
103 let admin_client = admin_rpc_service::connect(ledger_path);
104 admin_rpc_service::runtime().block_on(async move {
105 admin_client.await?.remove_all_authorized_voters().await
106 })?;
107 println!("All authorized voters removed");
108 }
109 _ => unreachable!(),
110 }
111
112 Ok(())
113}
114
115#[cfg(test)]
116mod tests {
117 use {super::*, solana_keypair::Keypair};
118
119 #[test]
120 fn verify_args_struct_by_command_authorized_voter_add_default() {
121 let app = command();
122 let matches = app.get_matches_from(vec![COMMAND, "add"]);
123 let subcommand_matches = matches.subcommand_matches("add").unwrap();
124 let args = AuthorizedVoterAddArgs::from_clap_arg_match(subcommand_matches).unwrap();
125
126 assert_eq!(args, AuthorizedVoterAddArgs::default());
127 }
128
129 #[test]
130 fn verify_args_struct_by_command_authorized_voter_add_with_authorized_voter_keypair() {
131 let tmp_dir = tempfile::tempdir().unwrap();
133 let file = tmp_dir.path().join("id.json");
134 let keypair = Keypair::new();
135 solana_keypair::write_keypair_file(&keypair, &file).unwrap();
136
137 let app = command();
138 let matches = app.get_matches_from(vec![COMMAND, "add", file.to_str().unwrap()]);
139 let subcommand_matches = matches.subcommand_matches("add").unwrap();
140 let args = AuthorizedVoterAddArgs::from_clap_arg_match(subcommand_matches).unwrap();
141
142 assert_eq!(
143 args,
144 AuthorizedVoterAddArgs {
145 authorized_voter_keypair: Some(file.to_str().unwrap().to_string()),
146 }
147 );
148 }
149}