Skip to content

Commit 2ce58bd

Browse files
authored
Merge of #6612
2 parents c1f94d9 + 1c1f686 commit 2ce58bd

File tree

14 files changed

+983
-23
lines changed

14 files changed

+983
-23
lines changed

Cargo.lock

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

book/src/help_vm.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ Commands:
2828
delete
2929
Deletes one or more validators from a validator client using the HTTP
3030
API.
31+
exit
32+
Exits one or more validators using the HTTP API. It can also be used
33+
to generate a presigned voluntary exit message for a particular future
34+
epoch.
3135
help
3236
Print this message or the help of the given subcommand(s)
3337

book/src/validator_manager.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,4 @@ The `validator-manager` boasts the following features:
3232

3333
- [Creating and importing validators using the `create` and `import` commands.](./validator_manager_create.md)
3434
- [Moving validators between two VCs using the `move` command.](./validator_manager_move.md)
35-
- [Managing validators such as delete, import and list validators.](./validator_manager_api.md)
35+
- [Managing validators such as exit, delete, import and list validators.](./validator_manager_api.md)

book/src/validator_manager_api.md

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,54 @@
22

33
The `lighthouse validator-manager` uses the [Keymanager API](https://ethereum.github.io/keymanager-APIs/#/) to list, import and delete keystores via the HTTP API. This requires the validator client running with the flag `--http`. By default, the validator client HTTP address is `http://localhost:5062`. If a different IP address or port is used, add the flag `--vc-url http://IP:port_number` to the command below.
44

5+
## Exit
6+
7+
The `exit` command exits one or more validators from the validator client. To `exit`:
8+
9+
> **Important note: Once the --beacon-node flag is used, it will publish the voluntary exit to the network. This action is irreversible.**
10+
11+
```bash
12+
lighthouse vm exit --vc-token <API-TOKEN-PATH> --validators pubkey1,pubkey2 --beacon-node http://beacon-node-url:5052
13+
```
14+
15+
Example:
16+
17+
```bash
18+
lighthouse vm exit --vc-token ~/.lighthouse/mainnet/validators/api-token.txt --validators 0x8885c29b8f88ee9b9a37b480fd4384fed74bda33d85bc8171a904847e65688b6c9bb4362d6597fd30109fb2def6c3ae4,0xa262dae3dcd2b2e280af534effa16bedb27c06f2959e114d53bd2a248ca324a018dc73179899a066149471a94a1bc92f --beacon-node http://localhost:5052
19+
```
20+
21+
If successful, the following log will be returned:
22+
23+
```text
24+
Successfully validated and published voluntary exit for validator 0x8885c29b8f88ee9b9a37b480fd4384fed74bda33d85bc8171a904847e65688b6c9bb4362d6597fd30109fb2def6c3ae4
25+
Successfully validated and published voluntary exit for validator
26+
0xa262dae3dcd2b2e280af534effa16bedb27c06f2959e114d53bd2a248ca324a018dc73179899a066149471a94a1bc92f
27+
```
28+
29+
To exit all validators on the validator client, use the keyword `all`:
30+
31+
```bash
32+
lighthouse vm exit --vc-token ~/.lighthouse/mainnet/validators/api-token.txt --validators all --beacon-node http://localhost:5052
33+
```
34+
35+
To check the voluntary exit status, refer to [the list command](./validator_manager_api.md#list).
36+
37+
The following command will only generate a presigned voluntary exit message and save it to a file named `{validator_pubkey}.json`. It **will not** publish the voluntary exit to the network.
38+
39+
To generate a presigned exit message and save it to a file, use the flag `--presign`:
40+
41+
```bash
42+
lighthouse vm exit --vc-token ~/.lighthouse/mainnet/validators/api-token.txt --validators all --presign
43+
```
44+
45+
To generate a presigned exit message for a particular (future) epoch, use the flag `--exit-epoch`:
46+
47+
```bash
48+
lighthouse vm exit --vc-token ~/.lighthouse/mainnet/validators/api-token.txt --validators all --presign --exit-epoch 1234567
49+
```
50+
51+
The generated presigned exit message will only be valid at or after the specified exit-epoch, in this case, epoch 1234567.
52+
553
## Delete
654

755
The `delete` command deletes one or more validators from the validator client. It will also modify the `validator_definitions.yml` file automatically so there is no manual action required from the user after the delete. To `delete`:
@@ -16,6 +64,12 @@ Example:
1664
lighthouse vm delete --vc-token ~/.lighthouse/mainnet/validators/api-token.txt --validators 0x8885c29b8f88ee9b9a37b480fd4384fed74bda33d85bc8171a904847e65688b6c9bb4362d6597fd30109fb2def6c3ae4,0xa262dae3dcd2b2e280af534effa16bedb27c06f2959e114d53bd2a248ca324a018dc73179899a066149471a94a1bc92f
1765
```
1866

67+
To delete all validators on the validator client, use the keyword `all`:
68+
69+
```bash
70+
lighthouse vm delete --vc-token ~/.lighthouse/mainnet/validators/api-token.txt --validators all
71+
```
72+
1973
## Import
2074

2175
The `import` command imports validator keystores generated by the `ethstaker-deposit-cli`. To import a validator keystore:
@@ -37,3 +91,26 @@ To list the validators running on the validator client:
3791
```bash
3892
lighthouse vm list --vc-token ~/.lighthouse/mainnet/validators/api-token.txt
3993
```
94+
95+
The `list` command can also be used to check the voluntary exit status of validators. To do so, use both `--beacon-node` and `--validators` flags. The `--validators` flag accepts a comma-separated list of validator public keys, or the keyword `all` to check the voluntary exit status of all validators attached to the validator client.
96+
97+
```bash
98+
lighthouse vm list --vc-token ~/.lighthouse/mainnet/validators/api-token.txt --validators 0x8de7ec501d574152f52a962bf588573df2fc3563fd0c6077651208ed20f24f3d8572425706b343117b48bdca56808416 --beacon-node http://localhost:5052
99+
```
100+
101+
If the validator voluntary exit has been accepted by the chain, the following log will be returned:
102+
103+
```text
104+
Voluntary exit for validator 0x8de7ec501d574152f52a962bf588573df2fc3563fd0c6077651208ed20f24f3d8572425706b343117b48bdca56808416 has been accepted into the beacon chain, but not yet finalized. Finalization may take several minutes or longer. Before finalization there is a low probability that the exit may be reverted.
105+
Current epoch: 2, Exit epoch: 7, Withdrawable epoch: 263
106+
Please keep your validator running till exit epoch
107+
Exit epoch in approximately 480 secs
108+
```
109+
110+
When the exit epoch is reached, querying the status will return:
111+
112+
```text
113+
Validator 0x8de7ec501d574152f52a962bf588573df2fc3563fd0c6077651208ed20f24f3d8572425706b343117b48bdca56808416 has exited at epoch: 7
114+
```
115+
116+
You can safely shut down the validator client at this point.

book/src/validator_voluntary_exit.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ A validator can initiate a voluntary exit provided that the validator is current
1010
It takes at a minimum 5 epochs (32 minutes) for a validator to exit after initiating a voluntary exit.
1111
This number can be much higher depending on how many other validators are queued to exit.
1212

13+
You can also perform voluntary exit for one or more validators using the validator manager, see [Managing Validators](./validator_manager_api.md#exit) for more details.
14+
1315
## Initiating a voluntary exit
1416

1517
In order to initiate an exit, users can use the `lighthouse account validator exit` command.

lighthouse/tests/validator_manager.rs

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use types::*;
1010
use validator_manager::{
1111
create_validators::CreateConfig,
1212
delete_validators::DeleteConfig,
13+
exit_validators::ExitConfig,
1314
import_validators::ImportConfig,
1415
list_validators::ListConfig,
1516
move_validators::{MoveConfig, PasswordSource, Validators},
@@ -119,6 +120,12 @@ impl CommandLineTest<DeleteConfig> {
119120
}
120121
}
121122

123+
impl CommandLineTest<ExitConfig> {
124+
fn validators_exit() -> Self {
125+
Self::default().flag("exit", None)
126+
}
127+
}
128+
122129
#[test]
123130
pub fn validator_create_without_output_path() {
124131
CommandLineTest::validators_create().assert_failed();
@@ -443,6 +450,8 @@ pub fn validator_list_defaults() {
443450
let expected = ListConfig {
444451
vc_url: SensitiveUrl::parse("http://localhost:5062").unwrap(),
445452
vc_token_path: PathBuf::from("./token.json"),
453+
beacon_url: None,
454+
validators_to_display: vec![],
446455
};
447456
assert_eq!(expected, config);
448457
});
@@ -468,3 +477,106 @@ pub fn validator_delete_defaults() {
468477
assert_eq!(expected, config);
469478
});
470479
}
480+
481+
#[test]
482+
pub fn validator_delete_missing_validator_flag() {
483+
CommandLineTest::validators_delete()
484+
.flag("--vc-token", Some("./token.json"))
485+
.assert_failed();
486+
}
487+
488+
#[test]
489+
pub fn validator_exit_defaults() {
490+
CommandLineTest::validators_exit()
491+
.flag(
492+
"--validators",
493+
Some(&format!("{},{}", EXAMPLE_PUBKEY_0, EXAMPLE_PUBKEY_1)),
494+
)
495+
.flag("--vc-token", Some("./token.json"))
496+
.flag("--beacon-node", Some("http://localhost:5052"))
497+
.assert_success(|config| {
498+
let expected = ExitConfig {
499+
vc_url: SensitiveUrl::parse("http://localhost:5062").unwrap(),
500+
vc_token_path: PathBuf::from("./token.json"),
501+
validators_to_exit: vec![
502+
PublicKeyBytes::from_str(EXAMPLE_PUBKEY_0).unwrap(),
503+
PublicKeyBytes::from_str(EXAMPLE_PUBKEY_1).unwrap(),
504+
],
505+
beacon_url: Some(SensitiveUrl::parse("http://localhost:5052").unwrap()),
506+
exit_epoch: None,
507+
presign: false,
508+
};
509+
assert_eq!(expected, config);
510+
});
511+
}
512+
513+
#[test]
514+
pub fn validator_exit_exit_epoch_and_presign_flags() {
515+
CommandLineTest::validators_exit()
516+
.flag(
517+
"--validators",
518+
Some(&format!("{},{}", EXAMPLE_PUBKEY_0, EXAMPLE_PUBKEY_1)),
519+
)
520+
.flag("--vc-token", Some("./token.json"))
521+
.flag("--exit-epoch", Some("1234567"))
522+
.flag("--presign", None)
523+
.assert_success(|config| {
524+
let expected = ExitConfig {
525+
vc_url: SensitiveUrl::parse("http://localhost:5062").unwrap(),
526+
vc_token_path: PathBuf::from("./token.json"),
527+
validators_to_exit: vec![
528+
PublicKeyBytes::from_str(EXAMPLE_PUBKEY_0).unwrap(),
529+
PublicKeyBytes::from_str(EXAMPLE_PUBKEY_1).unwrap(),
530+
],
531+
beacon_url: None,
532+
exit_epoch: Some(Epoch::new(1234567)),
533+
presign: true,
534+
};
535+
assert_eq!(expected, config);
536+
});
537+
}
538+
539+
#[test]
540+
pub fn validator_exit_missing_validator_flag() {
541+
CommandLineTest::validators_exit()
542+
.flag("--vc-token", Some("./token.json"))
543+
.assert_failed();
544+
}
545+
546+
#[test]
547+
pub fn validator_exit_using_beacon_and_presign_flags() {
548+
CommandLineTest::validators_exit()
549+
.flag("--vc-token", Some("./token.json"))
550+
.flag(
551+
"--validators",
552+
Some(&format!("{},{}", EXAMPLE_PUBKEY_0, EXAMPLE_PUBKEY_1)),
553+
)
554+
.flag("--beacon-node", Some("http://localhost:1001"))
555+
.flag("--presign", None)
556+
.assert_failed();
557+
}
558+
559+
#[test]
560+
pub fn validator_exit_using_beacon_and_exit_epoch_flags() {
561+
CommandLineTest::validators_exit()
562+
.flag("--vc-token", Some("./token.json"))
563+
.flag(
564+
"--validators",
565+
Some(&format!("{},{}", EXAMPLE_PUBKEY_0, EXAMPLE_PUBKEY_1)),
566+
)
567+
.flag("--beacon-node", Some("http://localhost:1001"))
568+
.flag("--exit-epoch", Some("1234567"))
569+
.assert_failed();
570+
}
571+
572+
#[test]
573+
pub fn validator_exit_exit_epoch_flag_without_presign_flag() {
574+
CommandLineTest::validators_exit()
575+
.flag("--vc-token", Some("./token.json"))
576+
.flag(
577+
"--validators",
578+
Some(&format!("{},{}", EXAMPLE_PUBKEY_0, EXAMPLE_PUBKEY_1)),
579+
)
580+
.flag("--exit-epoch", Some("1234567"))
581+
.assert_failed();
582+
}

validator_client/http_api/src/test_utils.rs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use std::time::Duration;
2626
use task_executor::test_utils::TestRuntime;
2727
use tempfile::{tempdir, TempDir};
2828
use tokio::sync::oneshot;
29+
use types::ChainSpec;
2930
use validator_services::block_service::BlockService;
3031
use zeroize::Zeroizing;
3132

@@ -61,6 +62,7 @@ pub struct ApiTester {
6162
pub _server_shutdown: oneshot::Sender<()>,
6263
pub validator_dir: TempDir,
6364
pub secrets_dir: TempDir,
65+
pub spec: Arc<ChainSpec>,
6466
}
6567

6668
impl ApiTester {
@@ -69,6 +71,19 @@ impl ApiTester {
6971
}
7072

7173
pub async fn new_with_http_config(http_config: HttpConfig) -> Self {
74+
let slot_clock =
75+
TestingSlotClock::new(Slot::new(0), Duration::from_secs(0), Duration::from_secs(1));
76+
let genesis_validators_root = Hash256::repeat_byte(42);
77+
let spec = Arc::new(E::default_spec());
78+
Self::new_with_options(http_config, slot_clock, genesis_validators_root, spec).await
79+
}
80+
81+
pub async fn new_with_options(
82+
http_config: HttpConfig,
83+
slot_clock: TestingSlotClock,
84+
genesis_validators_root: Hash256,
85+
spec: Arc<ChainSpec>,
86+
) -> Self {
7287
let validator_dir = tempdir().unwrap();
7388
let secrets_dir = tempdir().unwrap();
7489
let token_path = tempdir().unwrap().path().join(PK_FILENAME);
@@ -91,20 +106,15 @@ impl ApiTester {
91106
..Default::default()
92107
};
93108

94-
let spec = Arc::new(E::default_spec());
95-
96109
let slashing_db_path = validator_dir.path().join(SLASHING_PROTECTION_FILENAME);
97110
let slashing_protection = SlashingDatabase::open_or_create(&slashing_db_path).unwrap();
98111

99-
let slot_clock =
100-
TestingSlotClock::new(Slot::new(0), Duration::from_secs(0), Duration::from_secs(1));
101-
102112
let test_runtime = TestRuntime::default();
103113

104114
let validator_store = Arc::new(LighthouseValidatorStore::new(
105115
initialized_validators,
106116
slashing_protection,
107-
Hash256::repeat_byte(42),
117+
genesis_validators_root,
108118
spec.clone(),
109119
Some(Arc::new(DoppelgangerService::default())),
110120
slot_clock.clone(),
@@ -127,7 +137,7 @@ impl ApiTester {
127137
validator_store: Some(validator_store.clone()),
128138
graffiti_file: None,
129139
graffiti_flag: Some(Graffiti::default()),
130-
spec,
140+
spec: spec.clone(),
131141
config: http_config,
132142
sse_logging_components: None,
133143
slot_clock,
@@ -161,6 +171,7 @@ impl ApiTester {
161171
_server_shutdown: shutdown_tx,
162172
validator_dir,
163173
secrets_dir,
174+
spec,
164175
}
165176
}
166177

validator_manager/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,15 @@ ethereum_serde_utils = { workspace = true }
1717
hex = { workspace = true }
1818
serde = { workspace = true }
1919
serde_json = { workspace = true }
20+
slot_clock = { workspace = true }
2021
tokio = { workspace = true }
2122
tree_hash = { workspace = true }
2223
types = { workspace = true }
2324
zeroize = { workspace = true }
2425

2526
[dev-dependencies]
27+
beacon_chain = { workspace = true }
28+
http_api = { workspace = true }
2629
regex = { workspace = true }
2730
tempfile = { workspace = true }
2831
validator_http_api = { workspace = true }

0 commit comments

Comments
 (0)