agave_validator/commands/authorized_voter/
mod.rs

1use {
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        // generate a keypair
132        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}