mirror of
https://github.com/sigp/lighthouse.git
synced 2026-07-05 05:44:30 +00:00
Add function to check against beacon node
This commit is contained in:
@@ -2,11 +2,16 @@ use super::common::*;
|
|||||||
use account_utils::{random_password_string, read_mnemonic_from_cli, read_password_from_user};
|
use account_utils::{random_password_string, read_mnemonic_from_cli, read_password_from_user};
|
||||||
use clap::{App, Arg, ArgMatches};
|
use clap::{App, Arg, ArgMatches};
|
||||||
use environment::Environment;
|
use environment::Environment;
|
||||||
use eth2::lighthouse_vc::std_types::KeystoreJsonStr;
|
use eth2::{
|
||||||
|
lighthouse_vc::std_types::KeystoreJsonStr,
|
||||||
|
types::{StateId, ValidatorId},
|
||||||
|
BeaconNodeHttpClient, SensitiveUrl, Timeouts,
|
||||||
|
};
|
||||||
use eth2_wallet::WalletBuilder;
|
use eth2_wallet::WalletBuilder;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::time::Duration;
|
||||||
use types::*;
|
use types::*;
|
||||||
|
|
||||||
pub const CMD: &str = "create";
|
pub const CMD: &str = "create";
|
||||||
@@ -22,10 +27,13 @@ pub const ETH1_WITHDRAWAL_ADDRESS_FLAG: &str = "eth1-withdrawal-address";
|
|||||||
pub const GAS_LIMIT_FLAG: &str = "gas-limit";
|
pub const GAS_LIMIT_FLAG: &str = "gas-limit";
|
||||||
pub const FEE_RECIPIENT_FLAG: &str = "suggested-fee-recipient";
|
pub const FEE_RECIPIENT_FLAG: &str = "suggested-fee-recipient";
|
||||||
pub const BUILDER_PROPOSALS_FLAG: &str = "builder-proposals";
|
pub const BUILDER_PROPOSALS_FLAG: &str = "builder-proposals";
|
||||||
|
pub const BEACON_NODE_FLAG: &str = "beacon-node";
|
||||||
|
|
||||||
pub const VALIDATORS_FILENAME: &str = "validators.json";
|
pub const VALIDATORS_FILENAME: &str = "validators.json";
|
||||||
pub const DEPOSITS_FILENAME: &str = "deposits.json";
|
pub const DEPOSITS_FILENAME: &str = "deposits.json";
|
||||||
|
|
||||||
|
const BEACON_NODE_HTTP_TIMEOUT: Duration = Duration::from_secs(2);
|
||||||
|
|
||||||
struct ValidatorsAndDeposits {
|
struct ValidatorsAndDeposits {
|
||||||
validators: Vec<ValidatorSpecification>,
|
validators: Vec<ValidatorSpecification>,
|
||||||
deposits: Option<Vec<StandardDepositDataJson>>,
|
deposits: Option<Vec<StandardDepositDataJson>>,
|
||||||
@@ -161,6 +169,19 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
|
|||||||
)
|
)
|
||||||
.required(false),
|
.required(false),
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(BEACON_NODE_FLAG)
|
||||||
|
.long(BEACON_NODE_FLAG)
|
||||||
|
.value_name("HTTP_ADDRESS")
|
||||||
|
.help(
|
||||||
|
"A HTTP(S) address of a beacon node using the beacon-API. \
|
||||||
|
If this value is provided, an error will be raised if any validator \
|
||||||
|
key here is already known as a validator by that beacon node. This helps \
|
||||||
|
prevent the same validator being created twice and therefore slashable \
|
||||||
|
conditions.",
|
||||||
|
)
|
||||||
|
.takes_value(true),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn cli_run<'a, T: EthSpec>(
|
pub async fn cli_run<'a, T: EthSpec>(
|
||||||
@@ -193,7 +214,7 @@ pub async fn cli_run<'a, T: EthSpec>(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let validators_and_deposits = build_validator_spec_from_cli(matches, spec)?;
|
let validators_and_deposits = build_validator_spec_from_cli(matches, spec).await?;
|
||||||
|
|
||||||
write_to_json_file(&validators_path, &validators_and_deposits.validators)?;
|
write_to_json_file(&validators_path, &validators_and_deposits.validators)?;
|
||||||
|
|
||||||
@@ -214,7 +235,7 @@ fn write_to_json_file<P: AsRef<Path>, S: Serialize>(path: P, contents: &S) -> Re
|
|||||||
.map_err(|e| format!("Failed to write JSON to {:?}: {:?}", path.as_ref(), e))
|
.map_err(|e| format!("Failed to write JSON to {:?}: {:?}", path.as_ref(), e))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_validator_spec_from_cli<'a>(
|
async fn build_validator_spec_from_cli<'a>(
|
||||||
matches: &'a ArgMatches<'a>,
|
matches: &'a ArgMatches<'a>,
|
||||||
spec: &ChainSpec,
|
spec: &ChainSpec,
|
||||||
) -> Result<ValidatorsAndDeposits, String> {
|
) -> Result<ValidatorsAndDeposits, String> {
|
||||||
@@ -232,6 +253,25 @@ fn build_validator_spec_from_cli<'a>(
|
|||||||
let builder_proposals = matches.is_present(BUILDER_PROPOSALS_FLAG);
|
let builder_proposals = matches.is_present(BUILDER_PROPOSALS_FLAG);
|
||||||
let fee_recipient: Option<Address> = clap_utils::parse_optional(matches, FEE_RECIPIENT_FLAG)?;
|
let fee_recipient: Option<Address> = clap_utils::parse_optional(matches, FEE_RECIPIENT_FLAG)?;
|
||||||
let gas_limit: Option<u64> = clap_utils::parse_optional(matches, GAS_LIMIT_FLAG)?;
|
let gas_limit: Option<u64> = clap_utils::parse_optional(matches, GAS_LIMIT_FLAG)?;
|
||||||
|
let bn_url: Option<SensitiveUrl> = clap_utils::parse_optional(matches, BEACON_NODE_FLAG)?;
|
||||||
|
|
||||||
|
let bn_http_client = if let Some(bn_url) = bn_url {
|
||||||
|
let bn_http_client =
|
||||||
|
BeaconNodeHttpClient::new(bn_url, Timeouts::set_all(BEACON_NODE_HTTP_TIMEOUT));
|
||||||
|
|
||||||
|
let version = bn_http_client
|
||||||
|
.get_node_version()
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Failed to test connection to beacon node: {:?}", e))?
|
||||||
|
.data
|
||||||
|
.version;
|
||||||
|
|
||||||
|
eprintln!("Connected to beacon node running version {}", version);
|
||||||
|
|
||||||
|
Some(bn_http_client)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let mnemonic = read_mnemonic_from_cli(mnemonic_path, stdin_inputs)?;
|
let mnemonic = read_mnemonic_from_cli(mnemonic_path, stdin_inputs)?;
|
||||||
let voting_keystore_password = if specify_voting_keystore_password {
|
let voting_keystore_password = if specify_voting_keystore_password {
|
||||||
@@ -285,6 +325,44 @@ fn build_validator_spec_from_cli<'a>(
|
|||||||
.map_err(|e| format!("Failed to derive keystore {}: {:?}", i, e))?;
|
.map_err(|e| format!("Failed to derive keystore {}: {:?}", i, e))?;
|
||||||
let voting_keystore = keystores.voting;
|
let voting_keystore = keystores.voting;
|
||||||
|
|
||||||
|
// If the user has provided a beacon node URL, check that the validator doesn't already
|
||||||
|
// exist in the beacon chain.
|
||||||
|
if let Some(bn_http_client) = &bn_http_client {
|
||||||
|
let voting_public_key = voting_keystore
|
||||||
|
.public_key()
|
||||||
|
.ok_or_else(|| {
|
||||||
|
format!("Validator keystore at index {} is missing a public key", i)
|
||||||
|
})?
|
||||||
|
.into();
|
||||||
|
|
||||||
|
match bn_http_client
|
||||||
|
.get_beacon_states_validator_id(
|
||||||
|
StateId::Head,
|
||||||
|
&ValidatorId::PublicKey(voting_public_key),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(Some(_)) => {
|
||||||
|
return Err(format!(
|
||||||
|
"Validator {:?} at derivation index {} already exists in the beacon chain. \
|
||||||
|
This indicates a slashing risk, be sure to never run the same validator on two \
|
||||||
|
different validator clients",
|
||||||
|
voting_public_key, derivation_index
|
||||||
|
))?
|
||||||
|
}
|
||||||
|
Ok(None) => eprintln!(
|
||||||
|
"Validator {:?} was not found in the beacon chain",
|
||||||
|
voting_public_key
|
||||||
|
),
|
||||||
|
Err(e) => {
|
||||||
|
return Err(format!(
|
||||||
|
"Error checking if validator exists in beacon chain: {:?}",
|
||||||
|
e
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(deposits) = &mut deposits {
|
if let Some(deposits) = &mut deposits {
|
||||||
// Decrypt the voting keystore so a deposit message can be signed.
|
// Decrypt the voting keystore so a deposit message can be signed.
|
||||||
let voting_keypair = voting_keystore
|
let voting_keypair = voting_keystore
|
||||||
|
|||||||
Reference in New Issue
Block a user