Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add voluntary exit via validator manager #6612

Open
wants to merge 62 commits into
base: unstable
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
537c9c8
Exit validator
chong-he Nov 26, 2024
f6d6663
minor fix
chong-he Nov 28, 2024
30c9c8e
Revert "minor fix"
chong-he Nov 28, 2024
36a85e0
Test
chong-he Nov 29, 2024
3c7548c
Spec for vc
chong-he Nov 29, 2024
b40b1b1
Ensure time and genesis validators root match
michaelsproul Dec 3, 2024
036473f
Merge branch 'unstable' into vm-voluntary-exit
chong-he Dec 10, 2024
6f11781
Remove types
chong-he Dec 11, 2024
585498d
make cli
chong-he Dec 11, 2024
4f6426f
Cargo.lock
chong-he Dec 11, 2024
d4bc9f6
Merge branch 'vm-voluntary-exit' of https://github.com/chong-he/light…
chong-he Dec 11, 2024
d9c423d
Add exit multiple validators
chong-he Dec 11, 2024
0301d55
Add "all" to exit all validators
chong-he Dec 12, 2024
63b9a08
check if beacon node is syncing
chong-he Dec 13, 2024
b2b6442
Add validator status after exit
chong-he Dec 16, 2024
28fec0a
For testing
chong-he Dec 16, 2024
04f85e2
Display signature only when the flag is used
chong-he Dec 16, 2024
0f268d7
Merge branch 'vm-voluntary-exit' of https://github.com/chong-he/light…
chong-he Dec 16, 2024
e5d16ec
Fix signature flag
chong-he Dec 16, 2024
93e47b2
Fix signature flag
chong-he Dec 16, 2024
375f8a2
signature flag
chong-he Dec 17, 2024
d7421b0
Add test to lighthouse/tests
chong-he Dec 17, 2024
19f9fe0
Merge branch 'unstable' into vm-voluntary-exit
chong-he Dec 17, 2024
6bf8d58
Update test to compile
chong-he Dec 17, 2024
e4a99d8
simplify
chong-he Dec 17, 2024
48d1a02
Add exit multiple validators
chong-he Dec 17, 2024
8556f85
reduce diff
chong-he Dec 17, 2024
ba45cc5
Add delete all validators
chong-he Dec 17, 2024
97bddf8
update validator data
chong-he Dec 17, 2024
84a70a9
Improve UX
chong-he Dec 17, 2024
2789436
Merge branch 'unstable' into vm-voluntary-exit
chong-he Dec 17, 2024
b417e92
Improve UX sleep only once
chong-he Dec 18, 2024
45932ed
cli
chong-he Dec 18, 2024
f00a347
make cli
chong-he Dec 18, 2024
8d4b6a4
update validator data
chong-he Dec 18, 2024
871692a
remove loop
chong-he Dec 18, 2024
872daed
Add exit status
chong-he Dec 18, 2024
a5f4d2d
Minor fix
chong-he Dec 18, 2024
f8f94b9
Add doc
chong-he Dec 18, 2024
f935b9f
mdlint
chong-he Dec 18, 2024
d1d6442
Update lighthouse/tests
chong-he Dec 18, 2024
02f09d5
Simplify by remove print in test
chong-he Dec 18, 2024
2ee5498
remove debug
chong-he Dec 18, 2024
584c682
Merge branch 'unstable' into vm-voluntary-exit
chong-he Dec 19, 2024
47419b3
Add dependency
chong-he Dec 19, 2024
5a4f928
save exit message to a file
chong-he Dec 19, 2024
6cdc908
Add merge messages to one file
chong-he Dec 20, 2024
2b0163f
fmt
chong-he Dec 20, 2024
6a70f4e
Update doc
chong-he Dec 20, 2024
d327c59
Add more assert in test
chong-he Jan 6, 2025
6fb971e
Merge branch 'unstable' into vm-voluntary-exit
chong-he Jan 6, 2025
2f6ebe1
Merge branch 'unstable' into vm-voluntary-exit
chong-he Jan 22, 2025
419eda7
Remove --merge flag
chong-he Jan 22, 2025
b579d40
revise
chong-he Jan 22, 2025
2407965
Move check status to list_validator
chong-he Jan 23, 2025
6f41a19
Fix test
chong-he Jan 23, 2025
2c96cad
Update doc
chong-he Jan 23, 2025
4fb9f53
mdlint
chong-he Jan 23, 2025
683259d
udpate doc and flags
chong-he Jan 24, 2025
681feeb
Add presign in wordlist.txt
chong-he Jan 24, 2025
929335a
Add cli test
chong-he Jan 25, 2025
49e30fd
rename a bit
chong-he Jan 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Display signature only when the flag is used
  • Loading branch information
chong-he committed Dec 16, 2024
commit 04f85e275cb16d1d9387f08748a9b50286167501
154 changes: 90 additions & 64 deletions validator_manager/src/exit_validators.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::{common::vc_http_client, DumpConfig};

use clap::{Arg, ArgAction, ArgMatches, Command};
use clap_utils::FLAG_HEADER;
use eth2::types::{ConfigAndPreset, Epoch, StateId, ValidatorId, ValidatorStatus};
use eth2::{BeaconNodeHttpClient, SensitiveUrl, Timeouts};
use serde::{Deserialize, Serialize};
Expand All @@ -13,11 +14,11 @@ use types::{ChainSpec, EthSpec, PublicKeyBytes};

pub const CMD: &str = "exit";
pub const BEACON_URL_FLAG: &str = "beacon-node";
pub const VALIDATORS_FILE_FLAG: &str = "validators-file";
pub const VC_URL_FLAG: &str = "vc-url";
pub const VC_TOKEN_FLAG: &str = "vc-token";
pub const VALIDATOR_FLAG: &str = "validators";
pub const EXIT_EPOCH_FLAG: &str = "exit-epoch";
pub const SIGNATURE_FLAG: &str = "signature";

pub fn cli_app() -> Command {
Command::new(CMD)
Expand Down Expand Up @@ -64,6 +65,14 @@ pub fn cli_app() -> Command {
.action(ArgAction::Set)
.display_order(0),
)
.arg(
Arg::new(SIGNATURE_FLAG)
.long(SIGNATURE_FLAG)
.help("Display the signature of the voluntary exit.")
.help_heading(FLAG_HEADER)
.action(ArgAction::Set)
.display_order(0),
)
}

#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
Expand All @@ -73,6 +82,7 @@ pub struct ExitConfig {
pub validators_to_exit: Vec<PublicKeyBytes>,
pub beacon_url: Option<SensitiveUrl>,
pub exit_epoch: Option<Epoch>,
pub signature: Option<bool>,
}

impl ExitConfig {
Expand All @@ -94,6 +104,7 @@ impl ExitConfig {
validators_to_exit,
beacon_url: clap_utils::parse_optional(matches, BEACON_URL_FLAG)?,
exit_epoch: clap_utils::parse_optional(matches, EXIT_EPOCH_FLAG)?,
signature: clap_utils::parse_optional(matches, SIGNATURE_FLAG)?,
})
}
}
Expand All @@ -118,13 +129,16 @@ async fn run<E: EthSpec>(config: ExitConfig) -> Result<(), String> {
mut validators_to_exit,
beacon_url,
exit_epoch,
signature,
} = config;

let (http_client, validators) = vc_http_client(vc_url.clone(), &vc_token_path).await?;

// Exit all validators on the VC
if validators_to_exit.is_empty() {
validators_to_exit = validators.iter().map(|v| v.validating_pubkey).collect();
}

// Check that the validators_to_exit is in the validator client
for validator_to_exit in &validators_to_exit {
if !validators
Expand All @@ -141,11 +155,12 @@ async fn run<E: EthSpec>(config: ExitConfig) -> Result<(), String> {
.await
.map_err(|e| format!("Failed to generate voluntary exit message: {}", e))?;

let exit_message_json = serde_json::to_string(&exit_message.data);

match exit_message_json {
Ok(json) => println!("{}", json),
Err(e) => eprintln!("Failed to serialize voluntary exit message: {}", e),
if signature.is_some() {
let exit_message_json = serde_json::to_string(&exit_message.data);
match exit_message_json {
Ok(json) => println!("{}", json),
Err(e) => eprintln!("Failed to serialize voluntary exit message: {}", e),
}
}

// only publish the voluntary exit if the --beacon-node flag is present
Expand All @@ -160,6 +175,22 @@ async fn run<E: EthSpec>(config: ExitConfig) -> Result<(), String> {
return Err("Beacon URL is not provided".into());
};

// Get beacon node spec to be used later
let genesis_data = beacon_node
.get_beacon_genesis()
.await
.map_err(|e| format!("Failed to get genesis data: {}", e))?
.data;

let config_and_preset = beacon_node
.get_config_spec::<ConfigAndPreset>()
.await
.map_err(|e| format!("Failed to get config spec: {}", e))?
.data;

let spec = ChainSpec::from_config::<E>(config_and_preset.config())
.ok_or_else(|| "Failed to create chain spec".to_string())?;

if beacon_node
.get_node_syncing()
.await
Expand All @@ -173,6 +204,40 @@ async fn run<E: EthSpec>(config: ExitConfig) -> Result<(), String> {
);
}

let validator_data = beacon_node
.get_beacon_states_validator_id(
StateId::Head,
&ValidatorId::PublicKey(validator_to_exit),
)
.await
.map_err(|e| format!("Failed to get validator details: {:?}", e))?
.ok_or_else(|| {
format!(
"Validator {} is not present in the beacon state. \
Please ensure that your beacon node is synced and the validator has been deposited.",
validator_to_exit)})?
.data;

let activation_epoch = validator_data.validator.activation_epoch;
let current_epoch = get_current_epoch::<E>(genesis_data.genesis_time, &spec)
.ok_or("Failed to get current epoch. Please check your system time")?;

// Check if validator is eligible for exit
if validator_data.status == ValidatorStatus::ActiveOngoing
&& current_epoch < activation_epoch + spec.shard_committee_period
{
eprintln!(
"Validator {} is not eligible for exit. It will become eligible on epoch {}",
validator_to_exit,
activation_epoch + spec.shard_committee_period
)
} else if validator_data.status != ValidatorStatus::ActiveOngoing {
eprintln!(
"Validator {} is not eligible for exit. Validator status is: {:?}",
validator_to_exit, validator_data.status
)
};

beacon_node
.post_beacon_pool_voluntary_exits(&exit_message.data)
.await
Expand All @@ -183,41 +248,12 @@ async fn run<E: EthSpec>(config: ExitConfig) -> Result<(), String> {
validator_to_exit
);

// check validator status
let validator_data = beacon_node
.get_beacon_states_validator_id(
StateId::Head,
&ValidatorId::PublicKey(validator_to_exit),
)
.await
.map_err(|e| format!("Failed to get validator details: {:?}", e))?
.ok_or_else(|| {
format!(
"Validator {} is not present in the beacon state. \
Please ensure that your beacon node is synced and the validator has been deposited.",
validator_to_exit)})?
.data;

// Check validator status after publishing voluntary exit
match validator_data.status {
ValidatorStatus::ActiveExiting => {
let exit_epoch = validator_data.validator.exit_epoch;
let withdrawal_epoch = validator_data.validator.withdrawable_epoch;

let genesis_data = beacon_node
.get_beacon_genesis()
.await
.map_err(|e| format!("Failed to get genesis data: {}", e))?
.data;

let spec = beacon_node
.get_config_spec::<ConfigAndPreset>()
.await
.map_err(|e| format!("Failed to get config spec: {}", e))?
.data;

let chain_spec = ChainSpec::from_config::<E>(spec.config())
.ok_or_else(|| "Failed to create chain spec".to_string())?;

// let slot_clock = SystemTimeSlotClock::new(
// spec.genesis_slot,
// Duration::from_secs(genesis_data.genesis_time),
Expand All @@ -227,21 +263,6 @@ async fn run<E: EthSpec>(config: ExitConfig) -> Result<(), String> {
// let current_epoch = get_current_epoch::<SystemTimeSlotClock, E>(slot_clock)
// .ok_or_else(|| "Unable to determine current epoch".to_string())?;

fn get_current_epoch<E: EthSpec>(
genesis_time: u64,
spec: &ChainSpec,
) -> Option<Epoch> {
let slot_clock = SystemTimeSlotClock::new(
spec.genesis_slot,
Duration::from_secs(genesis_time),
Duration::from_secs(spec.seconds_per_slot),
);
slot_clock.now().map(|s| s.epoch(E::slots_per_epoch()))
}

let current_epoch =
get_current_epoch::<E>(genesis_data.genesis_time, &chain_spec)
.ok_or("Failed to get current epoch. Please check your system time")?;
eprintln!("Voluntary exit 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.");
Expand All @@ -252,32 +273,37 @@ async fn run<E: EthSpec>(config: ExitConfig) -> Result<(), String> {
eprintln!("Please keep your validator running till exit epoch");
eprintln!(
"Exit epoch in approximately {} secs",
(exit_epoch - current_epoch)
* chain_spec.seconds_per_slot
* E::slots_per_epoch()
(exit_epoch - current_epoch) * spec.seconds_per_slot * E::slots_per_epoch()
);
}

_ => {
eprintln!("Waiting for voluntary exit to be accepted into the beacon chain...")
}
// fn get_current_epoch<T: 'static + SlotClock + Clone, E: EthSpec>(
// slot_clock: T,
// ) -> Option<Epoch> {
// slot_clock.now().map(|s| s.epoch(E::slots_per_epoch()))
// }
} // fn get_current_epoch<T: 'static + SlotClock + Clone, E: EthSpec>(
// slot_clock: T,
// ) -> Option<Epoch> {
// slot_clock.now().map(|s| s.epoch(E::slots_per_epoch()))
// }

// let spec = ChainSpec::mainnet();
// let spec = ChainSpec::mainnet();

// let current_epoch =
// get_current_epoch::<E>(genesis_time, &spec).ok_or("Failed to get current epoch")?;
//let current_epoch = get_current_epoch::<E>(genesis_data.genesis_time, spec);
// let current_epoch =
// get_current_epoch::<E>(genesis_time, &spec).ok_or("Failed to get current epoch")?;
//let current_epoch = get_current_epoch::<E>(genesis_data.genesis_time, spec);
}
}
}
Ok(())
}

fn get_current_epoch<E: EthSpec>(genesis_time: u64, spec: &ChainSpec) -> Option<Epoch> {
let slot_clock = SystemTimeSlotClock::new(
spec.genesis_slot,
Duration::from_secs(genesis_time),
Duration::from_secs(spec.seconds_per_slot),
);
slot_clock.now().map(|s| s.epoch(E::slots_per_epoch()))
}
#[cfg(not(debug_assertions))]
#[cfg(test)]
mod test {
Expand Down