2877c29ca3
## Issue Addressed #3068 ## Proposed Changes Adds support for remote key API. ## Additional Info Needed to add `is_local_keystore` argument to `delete_definition_and_keystore` to know if we want to delete local or remote key. Previously this wasn't necessary because remotekeys(web3signers) could be deleted.
184 lines
6.5 KiB
Rust
184 lines
6.5 KiB
Rust
use crate::ValidatorStore;
|
|
use account_utils::validator_definitions::ValidatorDefinition;
|
|
use account_utils::{
|
|
eth2_wallet::{bip39::Mnemonic, WalletBuilder},
|
|
random_mnemonic, random_password, ZeroizeString,
|
|
};
|
|
use eth2::lighthouse_vc::types::{self as api_types};
|
|
use slot_clock::SlotClock;
|
|
use std::path::Path;
|
|
use types::ChainSpec;
|
|
use types::EthSpec;
|
|
use validator_dir::Builder as ValidatorDirBuilder;
|
|
|
|
/// Create some validator EIP-2335 keystores and store them on disk. Then, enroll the validators in
|
|
/// this validator client.
|
|
///
|
|
/// Returns the list of created validators and the mnemonic used to derive them via EIP-2334.
|
|
///
|
|
/// ## Detail
|
|
///
|
|
/// If `mnemonic_opt` is not supplied it will be randomly generated and returned in the response.
|
|
///
|
|
/// If `key_derivation_path_offset` is supplied then the EIP-2334 validator index will start at
|
|
/// this point.
|
|
pub async fn create_validators_mnemonic<P: AsRef<Path>, T: 'static + SlotClock, E: EthSpec>(
|
|
mnemonic_opt: Option<Mnemonic>,
|
|
key_derivation_path_offset: Option<u32>,
|
|
validator_requests: &[api_types::ValidatorRequest],
|
|
validator_dir: P,
|
|
validator_store: &ValidatorStore<T, E>,
|
|
spec: &ChainSpec,
|
|
) -> Result<(Vec<api_types::CreatedValidator>, Mnemonic), warp::Rejection> {
|
|
let mnemonic = mnemonic_opt.unwrap_or_else(random_mnemonic);
|
|
|
|
let wallet_password = random_password();
|
|
let mut wallet =
|
|
WalletBuilder::from_mnemonic(&mnemonic, wallet_password.as_bytes(), String::new())
|
|
.and_then(|builder| builder.build())
|
|
.map_err(|e| {
|
|
warp_utils::reject::custom_server_error(format!(
|
|
"unable to create EIP-2386 wallet: {:?}",
|
|
e
|
|
))
|
|
})?;
|
|
|
|
if let Some(nextaccount) = key_derivation_path_offset {
|
|
wallet.set_nextaccount(nextaccount).map_err(|e| {
|
|
warp_utils::reject::custom_server_error(format!(
|
|
"unable to set wallet nextaccount: {:?}",
|
|
e
|
|
))
|
|
})?;
|
|
}
|
|
|
|
let mut validators = Vec::with_capacity(validator_requests.len());
|
|
|
|
for request in validator_requests {
|
|
let voting_password = random_password();
|
|
let withdrawal_password = random_password();
|
|
let voting_password_string = ZeroizeString::from(
|
|
String::from_utf8(voting_password.as_bytes().to_vec()).map_err(|e| {
|
|
warp_utils::reject::custom_server_error(format!(
|
|
"locally generated password is not utf8: {:?}",
|
|
e
|
|
))
|
|
})?,
|
|
);
|
|
|
|
let mut keystores = wallet
|
|
.next_validator(
|
|
wallet_password.as_bytes(),
|
|
voting_password.as_bytes(),
|
|
withdrawal_password.as_bytes(),
|
|
)
|
|
.map_err(|e| {
|
|
warp_utils::reject::custom_server_error(format!(
|
|
"unable to create validator keys: {:?}",
|
|
e
|
|
))
|
|
})?;
|
|
|
|
keystores
|
|
.voting
|
|
.set_description(request.description.clone());
|
|
keystores
|
|
.withdrawal
|
|
.set_description(request.description.clone());
|
|
|
|
let voting_pubkey = format!("0x{}", keystores.voting.pubkey())
|
|
.parse()
|
|
.map_err(|e| {
|
|
warp_utils::reject::custom_server_error(format!(
|
|
"created invalid public key: {:?}",
|
|
e
|
|
))
|
|
})?;
|
|
|
|
let validator_dir = ValidatorDirBuilder::new(validator_dir.as_ref().into())
|
|
.voting_keystore(keystores.voting, voting_password.as_bytes())
|
|
.withdrawal_keystore(keystores.withdrawal, withdrawal_password.as_bytes())
|
|
.create_eth1_tx_data(request.deposit_gwei, spec)
|
|
.store_withdrawal_keystore(false)
|
|
.build()
|
|
.map_err(|e| {
|
|
warp_utils::reject::custom_server_error(format!(
|
|
"failed to build validator directory: {:?}",
|
|
e
|
|
))
|
|
})?;
|
|
|
|
let eth1_deposit_data = validator_dir
|
|
.eth1_deposit_data()
|
|
.map_err(|e| {
|
|
warp_utils::reject::custom_server_error(format!(
|
|
"failed to read local deposit data: {:?}",
|
|
e
|
|
))
|
|
})?
|
|
.ok_or_else(|| {
|
|
warp_utils::reject::custom_server_error(
|
|
"failed to create local deposit data: {:?}".to_string(),
|
|
)
|
|
})?;
|
|
|
|
if eth1_deposit_data.deposit_data.amount != request.deposit_gwei {
|
|
return Err(warp_utils::reject::custom_server_error(format!(
|
|
"invalid deposit_gwei {}, expected {}",
|
|
eth1_deposit_data.deposit_data.amount, request.deposit_gwei
|
|
)));
|
|
}
|
|
|
|
// Drop validator dir so that `add_validator_keystore` can re-lock the keystore.
|
|
let voting_keystore_path = validator_dir.voting_keystore_path();
|
|
drop(validator_dir);
|
|
|
|
validator_store
|
|
.add_validator_keystore(
|
|
voting_keystore_path,
|
|
voting_password_string,
|
|
request.enable,
|
|
request.graffiti.clone(),
|
|
request.suggested_fee_recipient,
|
|
)
|
|
.await
|
|
.map_err(|e| {
|
|
warp_utils::reject::custom_server_error(format!(
|
|
"failed to initialize validator: {:?}",
|
|
e
|
|
))
|
|
})?;
|
|
|
|
validators.push(api_types::CreatedValidator {
|
|
enabled: request.enable,
|
|
description: request.description.clone(),
|
|
graffiti: request.graffiti.clone(),
|
|
suggested_fee_recipient: request.suggested_fee_recipient,
|
|
voting_pubkey,
|
|
eth1_deposit_tx_data: eth2_serde_utils::hex::encode(ð1_deposit_data.rlp),
|
|
deposit_gwei: request.deposit_gwei,
|
|
});
|
|
}
|
|
|
|
Ok((validators, mnemonic))
|
|
}
|
|
|
|
pub async fn create_validators_web3signer<T: 'static + SlotClock, E: EthSpec>(
|
|
validators: Vec<ValidatorDefinition>,
|
|
validator_store: &ValidatorStore<T, E>,
|
|
) -> Result<(), warp::Rejection> {
|
|
for validator in validators {
|
|
validator_store
|
|
.add_validator(validator)
|
|
.await
|
|
.map_err(|e| {
|
|
warp_utils::reject::custom_server_error(format!(
|
|
"failed to initialize validator: {:?}",
|
|
e
|
|
))
|
|
})?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|