Progress with move implementation

This commit is contained in:
Paul Hauner
2022-08-24 15:54:52 +10:00
parent 71409e4897
commit e98a0155bd
5 changed files with 286 additions and 121 deletions

View File

@@ -1,7 +1,11 @@
use account_utils::ZeroizeString;
use eth2::lighthouse_vc::std_types::{InterchangeJsonStr, KeystoreJsonStr};
use eth2::{
lighthouse_vc::{http_client::ValidatorClientHttpClient, std_types::SingleKeystoreResponse},
lighthouse_vc::{
http_client::ValidatorClientHttpClient,
std_types::{ImportKeystoresRequest, SingleKeystoreResponse},
types::UpdateFeeRecipientRequest,
},
SensitiveUrl,
};
use serde::{Deserialize, Serialize};
@@ -10,6 +14,8 @@ use std::path::{Path, PathBuf};
use tree_hash::TreeHash;
use types::*;
pub const IGNORE_DUPLICATES_FLAG: &str = "ignore-duplicates";
/// When the `ethereum/staking-deposit-cli` tool generates deposit data JSON, it adds a
/// `deposit_cli_version` to protect the web-based "Launchpad" tool against a breaking change that
/// was introduced in `ethereum/staking-deposit-cli`. Lighthouse don't really have a version that it
@@ -19,6 +25,16 @@ use types::*;
/// 2. Weird enough to identify Lighthouse.
const LIGHTHOUSE_DEPOSIT_CLI_VERSION: &str = "20.18.20";
#[derive(Debug)]
pub enum UploadError {
InvalidPublicKey,
DuplicateValidator(PublicKeyBytes),
FailedToListKeys(eth2::Error),
KeyUploadFailed(eth2::Error),
FeeRecipientUpdateFailed(eth2::Error),
PatchValidatorFailed(eth2::Error),
}
#[derive(Serialize, Deserialize)]
pub struct ValidatorSpecification {
pub voting_keystore: KeystoreJsonStr,
@@ -30,6 +46,97 @@ pub struct ValidatorSpecification {
pub enabled: Option<bool>,
}
impl ValidatorSpecification {
/// Upload the validator to a validator client via HTTP.
pub async fn upload(
self,
http_client: &ValidatorClientHttpClient,
ignore_duplicates: bool,
) -> Result<(), UploadError> {
let ValidatorSpecification {
voting_keystore,
voting_keystore_password,
slashing_protection,
fee_recipient,
gas_limit,
builder_proposals,
enabled,
} = self;
let voting_public_key = voting_keystore
.public_key()
.ok_or(UploadError::InvalidPublicKey)?
.into();
let request = ImportKeystoresRequest {
keystores: vec![voting_keystore],
passwords: vec![voting_keystore_password],
slashing_protection,
};
// Check to see if this validator already exists on the remote validator.
match http_client.get_keystores().await {
Ok(response) => {
if response
.data
.iter()
.find(|validator| validator.validating_pubkey == voting_public_key)
.is_some()
{
if ignore_duplicates {
eprintln!(
"Duplicate validators are ignored, ignoring {:?} which exists \
on the destination validator client",
voting_public_key
);
} else {
return Err(UploadError::DuplicateValidator(voting_public_key));
}
}
}
Err(e) => {
return Err(UploadError::FailedToListKeys(e));
}
};
if let Err(e) = http_client.post_keystores(&request).await {
// Return here *without* writing the deposit JSON file. This might help prevent
// users from submitting duplicate deposits or deposits for validators that weren't
// initialized on a VC.
//
// Next the the user runs with the --ignore-duplicates flag there should be a new,
// complete deposit JSON file created.
return Err(UploadError::KeyUploadFailed(e));
}
if let Some(fee_recipient) = fee_recipient {
http_client
.post_fee_recipient(
&voting_public_key,
&UpdateFeeRecipientRequest {
ethaddress: fee_recipient,
},
)
.await
.map_err(UploadError::FeeRecipientUpdateFailed)?;
}
if gas_limit.is_some() || builder_proposals.is_some() || enabled.is_some() {
http_client
.patch_lighthouse_validators(
&voting_public_key,
enabled,
gas_limit,
builder_proposals,
)
.await
.map_err(UploadError::PatchValidatorFailed)?;
}
Ok(())
}
}
#[derive(Serialize, Deserialize)]
pub struct CreateSpec {
pub mnemonic: String,
@@ -225,3 +332,20 @@ pub async fn vc_http_client<P: AsRef<Path>>(
Ok((http_client, remote_keystores))
}
/// Write some object to a file as JSON.
///
/// The file must be created new, it must not already exist.
pub fn write_to_json_file<P: AsRef<Path>, S: Serialize>(
path: P,
contents: &S,
) -> Result<(), String> {
eprintln!("Writing {:?}", path.as_ref());
let mut file = fs::OpenOptions::new()
.write(true)
.create_new(true)
.open(&path)
.map_err(|e| format!("Failed to open {:?}: {:?}", path.as_ref(), e))?;
serde_json::to_writer(&mut file, contents)
.map_err(|e| format!("Failed to write JSON to {:?}: {:?}", path.as_ref(), e))
}