agave_validator/commands/repair_whitelist/
mod.rs

1use {
2    crate::{
3        admin_rpc_service,
4        commands::{FromClapArgMatches, Result},
5    },
6    clap::{values_t, App, AppSettings, Arg, ArgMatches, SubCommand},
7    itertools::Itertools,
8    solana_clap_utils::input_validators::is_pubkey,
9    solana_cli_output::OutputFormat,
10    solana_pubkey::Pubkey,
11    std::path::Path,
12};
13
14pub const COMMAND: &str = "repair-whitelist";
15
16#[derive(Debug, PartialEq)]
17pub struct RepairWhitelistGetArgs {
18    pub output: OutputFormat,
19}
20
21impl FromClapArgMatches for RepairWhitelistGetArgs {
22    fn from_clap_arg_match(matches: &ArgMatches) -> Result<Self> {
23        Ok(RepairWhitelistGetArgs {
24            output: OutputFormat::from_matches(matches, "output", false),
25        })
26    }
27}
28
29#[derive(Debug, PartialEq)]
30pub struct RepairWhitelistSetArgs {
31    pub whitelist: Vec<Pubkey>,
32}
33
34impl FromClapArgMatches for RepairWhitelistSetArgs {
35    fn from_clap_arg_match(matches: &ArgMatches) -> Result<Self> {
36        let whitelist = values_t!(matches, "whitelist", Pubkey)?
37            .into_iter()
38            .unique()
39            .collect::<Vec<_>>();
40        Ok(RepairWhitelistSetArgs { whitelist })
41    }
42}
43
44pub fn command<'a>() -> App<'a, 'a> {
45    SubCommand::with_name(COMMAND)
46        .about("Manage the validator's repair protocol whitelist")
47        .setting(AppSettings::SubcommandRequiredElseHelp)
48        .setting(AppSettings::InferSubcommands)
49        .subcommand(
50            SubCommand::with_name("get")
51                .about("Display the validator's repair protocol whitelist")
52                .arg(
53                    Arg::with_name("output")
54                        .long("output")
55                        .takes_value(true)
56                        .value_name("MODE")
57                        .possible_values(&["json", "json-compact"])
58                        .help("Output display mode"),
59                ),
60        )
61        .subcommand(
62            SubCommand::with_name("set")
63                .about("Set the validator's repair protocol whitelist")
64                .setting(AppSettings::ArgRequiredElseHelp)
65                .arg(
66                    Arg::with_name("whitelist")
67                        .long("whitelist")
68                        .validator(is_pubkey)
69                        .value_name("VALIDATOR IDENTITY")
70                        .multiple(true)
71                        .takes_value(true)
72                        .required(true)
73                        .help("Set the validator's repair protocol whitelist"),
74                )
75                .after_help(
76                    "Note: repair protocol whitelist changes only apply to the currently running validator instance",
77                ),
78        )
79        .subcommand(
80            SubCommand::with_name("remove-all")
81                .about("Clear the validator's repair protocol whitelist")
82                .after_help(
83                    "Note: repair protocol whitelist changes only apply to the currently running validator instance",
84                ),
85        )
86}
87
88pub fn execute(matches: &ArgMatches, ledger_path: &Path) -> Result<()> {
89    match matches.subcommand() {
90        ("get", Some(subcommand_matches)) => {
91            let repair_whitelist_get_args =
92                RepairWhitelistGetArgs::from_clap_arg_match(subcommand_matches)?;
93
94            let admin_client = admin_rpc_service::connect(ledger_path);
95            let repair_whitelist = admin_rpc_service::runtime()
96                .block_on(async move { admin_client.await?.repair_whitelist().await })?;
97
98            println!(
99                "{}",
100                repair_whitelist_get_args
101                    .output
102                    .formatted_string(&repair_whitelist)
103            );
104        }
105        ("set", Some(subcommand_matches)) => {
106            let RepairWhitelistSetArgs { whitelist } =
107                RepairWhitelistSetArgs::from_clap_arg_match(subcommand_matches)?;
108
109            if whitelist.is_empty() {
110                return Ok(());
111            }
112
113            set_repair_whitelist(ledger_path, whitelist)?;
114        }
115        ("remove-all", _) => {
116            set_repair_whitelist(ledger_path, Vec::default())?;
117        }
118        _ => unreachable!(),
119    }
120
121    Ok(())
122}
123
124fn set_repair_whitelist(ledger_path: &Path, whitelist: Vec<Pubkey>) -> Result<()> {
125    let admin_client = admin_rpc_service::connect(ledger_path);
126    admin_rpc_service::runtime()
127        .block_on(async move { admin_client.await?.set_repair_whitelist(whitelist).await })?;
128
129    Ok(())
130}
131
132#[cfg(test)]
133mod tests {
134    use {super::*, std::str::FromStr};
135
136    #[test]
137    fn verify_args_struct_by_command_repair_whitelist_get_default() {
138        let app = command();
139        let matches = app.get_matches_from(vec![COMMAND, "get"]);
140        let subcommand_matches = matches.subcommand_matches("get").unwrap();
141        let args = RepairWhitelistGetArgs::from_clap_arg_match(subcommand_matches).unwrap();
142        assert_eq!(
143            args,
144            RepairWhitelistGetArgs {
145                output: OutputFormat::Display
146            }
147        );
148    }
149
150    #[test]
151    fn verify_args_struct_by_command_repair_whitelist_get_with_output() {
152        let app = command();
153        let matches = app.get_matches_from(vec![COMMAND, "get", "--output", "json"]);
154        let subcommand_matches = matches.subcommand_matches("get").unwrap();
155        let args = RepairWhitelistGetArgs::from_clap_arg_match(subcommand_matches).unwrap();
156        assert_eq!(
157            args,
158            RepairWhitelistGetArgs {
159                output: OutputFormat::Json
160            }
161        );
162    }
163
164    #[test]
165    fn verify_args_struct_by_command_repair_whitelist_set_with_single_whitelist() {
166        let app = command();
167        let matches = app.get_matches_from(vec![
168            COMMAND,
169            "set",
170            "--whitelist",
171            "ch1do11111111111111111111111111111111111111",
172        ]);
173        let subcommand_matches = matches.subcommand_matches("set").unwrap();
174        let args = RepairWhitelistSetArgs::from_clap_arg_match(subcommand_matches).unwrap();
175        assert_eq!(
176            args,
177            RepairWhitelistSetArgs {
178                whitelist: vec![
179                    Pubkey::from_str("ch1do11111111111111111111111111111111111111").unwrap(),
180                ]
181            }
182        );
183    }
184
185    #[test]
186    fn verify_args_struct_by_command_repair_whitelist_set_with_multiple_whitelist() {
187        let app = command();
188        let matches = app.get_matches_from(vec![
189            COMMAND,
190            "set",
191            "--whitelist",
192            "ch1do11111111111111111111111111111111111111",
193            "--whitelist",
194            "ch1do11111111111111111111111111111111111112",
195        ]);
196        let subcommand_matches = matches.subcommand_matches("set").unwrap();
197        let mut args = RepairWhitelistSetArgs::from_clap_arg_match(subcommand_matches).unwrap();
198        args.whitelist.sort(); // the order of the whitelist is not guaranteed. sort it before asserting
199        assert_eq!(
200            args,
201            RepairWhitelistSetArgs {
202                whitelist: vec![
203                    Pubkey::from_str("ch1do11111111111111111111111111111111111111").unwrap(),
204                    Pubkey::from_str("ch1do11111111111111111111111111111111111112").unwrap(),
205                ]
206            }
207        );
208    }
209}