mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-03 00:31:50 +00:00
Add validator-manager (#3502)
## Issue Addressed Addresses #2557 ## Proposed Changes Adds the `lighthouse validator-manager` command, which provides: - `lighthouse validator-manager create` - Creates a `validators.json` file and a `deposits.json` (same format as https://github.com/ethereum/staking-deposit-cli) - `lighthouse validator-manager import` - Imports validators from a `validators.json` file to the VC via the HTTP API. - `lighthouse validator-manager move` - Moves validators from one VC to the other, utilizing only the VC API. ## Additional Info In98bcb947cI've reduced some VC `ERRO` and `CRIT` warnings to `WARN` or `DEBG` for the case where a pubkey is missing from the validator store. These were being triggered when we removed a validator but still had it in caches. It seems to me that `UnknownPubkey` will only happen in the case where we've removed a validator, so downgrading the logs is prudent. All the logs are `DEBG` apart from attestations and blocks which are `WARN`. I thought having *some* logging about this condition might help us down the track. In856cd7e37dI've made the VC delete the corresponding password file when it's deleting a keystore. This seemed like nice hygiene. Notably, it'll only delete that password file after it scans the validator definitions and finds that no other validator is also using that password file.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
use crate::{Error as DirError, ValidatorDir};
|
||||
use bls::get_withdrawal_credentials;
|
||||
use deposit_contract::{encode_eth1_tx_data, Error as DepositError};
|
||||
use directory::ensure_dir_exists;
|
||||
use eth2_keystore::{Error as KeystoreError, Keystore, KeystoreBuilder, PlainText};
|
||||
use filesystem::create_with_600_perms;
|
||||
use rand::{distributions::Alphanumeric, Rng};
|
||||
@@ -41,6 +42,7 @@ pub enum Error {
|
||||
#[cfg(feature = "insecure_keys")]
|
||||
InsecureKeysError(String),
|
||||
MissingPasswordDir,
|
||||
UnableToCreatePasswordDir(String),
|
||||
}
|
||||
|
||||
impl From<KeystoreError> for Error {
|
||||
@@ -78,6 +80,13 @@ impl<'a> Builder<'a> {
|
||||
self
|
||||
}
|
||||
|
||||
/// Optionally supply a directory in which to store the passwords for the validator keystores.
|
||||
/// If `None` is provided, do not store the password.
|
||||
pub fn password_dir_opt(mut self, password_dir_opt: Option<PathBuf>) -> Self {
|
||||
self.password_dir = password_dir_opt;
|
||||
self
|
||||
}
|
||||
|
||||
/// Build the `ValidatorDir` use the given `keystore` which can be unlocked with `password`.
|
||||
///
|
||||
/// The builder will not necessarily check that `password` can unlock `keystore`.
|
||||
@@ -153,6 +162,10 @@ impl<'a> Builder<'a> {
|
||||
create_dir_all(&dir).map_err(Error::UnableToCreateDir)?;
|
||||
}
|
||||
|
||||
if let Some(password_dir) = &self.password_dir {
|
||||
ensure_dir_exists(password_dir).map_err(Error::UnableToCreatePasswordDir)?;
|
||||
}
|
||||
|
||||
// The withdrawal keystore must be initialized in order to store it or create an eth1
|
||||
// deposit.
|
||||
if (self.store_withdrawal_keystore || self.deposit_info.is_some())
|
||||
@@ -234,7 +247,7 @@ impl<'a> Builder<'a> {
|
||||
if self.store_withdrawal_keystore {
|
||||
// Write the withdrawal password to file.
|
||||
write_password_to_file(
|
||||
password_dir.join(withdrawal_keypair.pk.as_hex_string()),
|
||||
keystore_password_path(password_dir, &withdrawal_keystore),
|
||||
withdrawal_password.as_bytes(),
|
||||
)?;
|
||||
|
||||
@@ -250,7 +263,7 @@ impl<'a> Builder<'a> {
|
||||
if let Some(password_dir) = self.password_dir.as_ref() {
|
||||
// Write the voting password to file.
|
||||
write_password_to_file(
|
||||
password_dir.join(format!("0x{}", voting_keystore.pubkey())),
|
||||
keystore_password_path(password_dir, &voting_keystore),
|
||||
voting_password.as_bytes(),
|
||||
)?;
|
||||
}
|
||||
@@ -262,6 +275,12 @@ impl<'a> Builder<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn keystore_password_path<P: AsRef<Path>>(password_dir: P, keystore: &Keystore) -> PathBuf {
|
||||
password_dir
|
||||
.as_ref()
|
||||
.join(format!("0x{}", keystore.pubkey()))
|
||||
}
|
||||
|
||||
/// Writes a JSON keystore to file.
|
||||
fn write_keystore_to_file(path: PathBuf, keystore: &Keystore) -> Result<(), Error> {
|
||||
if path.exists() {
|
||||
|
||||
@@ -15,6 +15,6 @@ pub use crate::validator_dir::{
|
||||
ETH1_DEPOSIT_TX_HASH_FILE,
|
||||
};
|
||||
pub use builder::{
|
||||
Builder, Error as BuilderError, ETH1_DEPOSIT_DATA_FILE, VOTING_KEYSTORE_FILE,
|
||||
WITHDRAWAL_KEYSTORE_FILE,
|
||||
keystore_password_path, Builder, Error as BuilderError, ETH1_DEPOSIT_DATA_FILE,
|
||||
VOTING_KEYSTORE_FILE, WITHDRAWAL_KEYSTORE_FILE,
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::builder::{
|
||||
ETH1_DEPOSIT_AMOUNT_FILE, ETH1_DEPOSIT_DATA_FILE, VOTING_KEYSTORE_FILE,
|
||||
keystore_password_path, ETH1_DEPOSIT_AMOUNT_FILE, ETH1_DEPOSIT_DATA_FILE, VOTING_KEYSTORE_FILE,
|
||||
WITHDRAWAL_KEYSTORE_FILE,
|
||||
};
|
||||
use deposit_contract::decode_eth1_tx_data;
|
||||
@@ -219,9 +219,7 @@ pub fn unlock_keypair<P: AsRef<Path>>(
|
||||
)
|
||||
.map_err(Error::UnableToReadKeystore)?;
|
||||
|
||||
let password_path = password_dir
|
||||
.as_ref()
|
||||
.join(format!("0x{}", keystore.pubkey()));
|
||||
let password_path = keystore_password_path(password_dir, &keystore);
|
||||
let password: PlainText = read(&password_path)
|
||||
.map_err(|_| Error::UnableToReadPassword(password_path))?
|
||||
.into();
|
||||
|
||||
Reference in New Issue
Block a user