Skip to content

Commit dffe31c

Browse files
committed
Add an account command to enable/disable validators (sigp#2386)
## Issue Addressed Resolves sigp#2322 ## Proposed Changes Adds a `modify` command to `lighthouse account validator` with subcommands to enable and disable specific or all pubkeys.
1 parent 3b600ac commit dffe31c

File tree

3 files changed

+139
-3
lines changed

3 files changed

+139
-3
lines changed

account_manager/src/validator/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ pub mod create;
22
pub mod exit;
33
pub mod import;
44
pub mod list;
5+
pub mod modify;
56
pub mod recover;
67
pub mod slashing_protection;
78

@@ -29,6 +30,7 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
2930
.conflicts_with("datadir"),
3031
)
3132
.subcommand(create::cli_app())
33+
.subcommand(modify::cli_app())
3234
.subcommand(import::cli_app())
3335
.subcommand(list::cli_app())
3436
.subcommand(recover::cli_app())
@@ -47,6 +49,7 @@ pub fn cli_run<T: EthSpec>(matches: &ArgMatches, env: Environment<T>) -> Result<
4749

4850
match matches.subcommand() {
4951
(create::CMD, Some(matches)) => create::cli_run::<T>(matches, env, validator_base_dir),
52+
(modify::CMD, Some(matches)) => modify::cli_run(matches, validator_base_dir),
5053
(import::CMD, Some(matches)) => import::cli_run(matches, validator_base_dir),
5154
(list::CMD, Some(_)) => list::cli_run(validator_base_dir),
5255
(recover::CMD, Some(matches)) => recover::cli_run(matches, validator_base_dir),
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
use account_utils::validator_definitions::ValidatorDefinitions;
2+
use bls::PublicKey;
3+
use clap::{App, Arg, ArgMatches};
4+
use std::{collections::HashSet, path::PathBuf};
5+
6+
pub const CMD: &str = "modify";
7+
pub const ENABLE: &str = "enable";
8+
pub const DISABLE: &str = "disable";
9+
10+
pub const PUBKEY_FLAG: &str = "pubkey";
11+
pub const ALL: &str = "all";
12+
13+
pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
14+
App::new(CMD)
15+
.about("Modify validator status in validator_definitions.yml.")
16+
.subcommand(
17+
App::new(ENABLE)
18+
.about("Enable validator(s) in validator_definitions.yml.")
19+
.arg(
20+
Arg::with_name(PUBKEY_FLAG)
21+
.long(PUBKEY_FLAG)
22+
.value_name("PUBKEY")
23+
.help("Validator pubkey to enable")
24+
.takes_value(true),
25+
)
26+
.arg(
27+
Arg::with_name(ALL)
28+
.long(ALL)
29+
.help("Enable all validators in the validator directory")
30+
.takes_value(false)
31+
.conflicts_with(PUBKEY_FLAG),
32+
),
33+
)
34+
.subcommand(
35+
App::new(DISABLE)
36+
.about("Disable validator(s) in validator_definitions.yml.")
37+
.arg(
38+
Arg::with_name(PUBKEY_FLAG)
39+
.long(PUBKEY_FLAG)
40+
.value_name("PUBKEY")
41+
.help("Validator pubkey to disable")
42+
.takes_value(true),
43+
)
44+
.arg(
45+
Arg::with_name(ALL)
46+
.long(ALL)
47+
.help("Disable all validators in the validator directory")
48+
.takes_value(false)
49+
.conflicts_with(PUBKEY_FLAG),
50+
),
51+
)
52+
}
53+
54+
pub fn cli_run(matches: &ArgMatches, validator_dir: PathBuf) -> Result<(), String> {
55+
// `true` implies we are setting `validator_definition.enabled = true` and
56+
// vice versa.
57+
let (enabled, sub_matches) = match matches.subcommand() {
58+
(ENABLE, Some(sub_matches)) => (true, sub_matches),
59+
(DISABLE, Some(sub_matches)) => (false, sub_matches),
60+
(unknown, _) => {
61+
return Err(format!(
62+
"{} does not have a {} command. See --help",
63+
CMD, unknown
64+
))
65+
}
66+
};
67+
let mut defs = ValidatorDefinitions::open(&validator_dir).map_err(|e| {
68+
format!(
69+
"No validator definitions found in {:?}: {:?}",
70+
validator_dir, e
71+
)
72+
})?;
73+
let pubkeys_to_modify = if sub_matches.is_present(ALL) {
74+
defs.as_slice()
75+
.iter()
76+
.map(|def| def.voting_public_key.clone())
77+
.collect::<HashSet<_>>()
78+
} else {
79+
let public_key: PublicKey = clap_utils::parse_required(sub_matches, PUBKEY_FLAG)?;
80+
std::iter::once(public_key).collect::<HashSet<PublicKey>>()
81+
};
82+
83+
// Modify required entries from validator_definitions.
84+
for def in defs.as_mut_slice() {
85+
if pubkeys_to_modify.contains(&def.voting_public_key) {
86+
def.enabled = enabled;
87+
eprintln!(
88+
"Validator {} {}",
89+
def.voting_public_key,
90+
if enabled { "enabled" } else { "disabled" }
91+
);
92+
}
93+
}
94+
95+
defs.save(&validator_dir)
96+
.map_err(|e| format!("Unable to modify validator definitions: {:?}", e))?;
97+
98+
eprintln!("\nSuccessfully modified validator_definitions.yml");
99+
Ok(())
100+
}

lighthouse/tests/account_manager.rs

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use account_manager::{
22
validator::{
33
create::*,
44
import::{self, CMD as IMPORT_CMD},
5+
modify::{ALL, CMD as MODIFY_CMD, DISABLE, ENABLE, PUBKEY_FLAG},
56
CMD as VALIDATOR_CMD,
67
},
78
wallet::{
@@ -475,10 +476,21 @@ fn validator_import_launchpad() {
475476
// Validator should be registered with slashing protection.
476477
check_slashing_protection(&dst_dir, std::iter::once(keystore.public_key().unwrap()));
477478

479+
// Disable all the validators in validator_definition.
480+
output_result(
481+
validator_cmd()
482+
.arg(format!("--{}", VALIDATOR_DIR_FLAG))
483+
.arg(dst_dir.path().as_os_str())
484+
.arg(MODIFY_CMD)
485+
.arg(DISABLE)
486+
.arg(format!("--{}", ALL)),
487+
)
488+
.unwrap();
489+
478490
let defs = ValidatorDefinitions::open(&dst_dir).unwrap();
479491

480-
let expected_def = ValidatorDefinition {
481-
enabled: true,
492+
let mut expected_def = ValidatorDefinition {
493+
enabled: false,
482494
description: "".into(),
483495
graffiti: None,
484496
voting_public_key: keystore.public_key().unwrap(),
@@ -490,7 +502,28 @@ fn validator_import_launchpad() {
490502
};
491503

492504
assert!(
493-
defs.as_slice() == &[expected_def],
505+
defs.as_slice() == &[expected_def.clone()],
506+
"validator defs file should be accurate"
507+
);
508+
509+
// Enable keystore validator again
510+
output_result(
511+
validator_cmd()
512+
.arg(format!("--{}", VALIDATOR_DIR_FLAG))
513+
.arg(dst_dir.path().as_os_str())
514+
.arg(MODIFY_CMD)
515+
.arg(ENABLE)
516+
.arg(format!("--{}", PUBKEY_FLAG))
517+
.arg(format!("{}", keystore.public_key().unwrap())),
518+
)
519+
.unwrap();
520+
521+
let defs = ValidatorDefinitions::open(&dst_dir).unwrap();
522+
523+
expected_def.enabled = true;
524+
525+
assert!(
526+
defs.as_slice() == &[expected_def.clone()],
494527
"validator defs file should be accurate"
495528
);
496529
}

0 commit comments

Comments
 (0)