agave_validator/commands/repair_whitelist/
mod.rs1use {
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(); 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}