mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-14 10:22:38 +00:00
Add interop keys and startup wait
This commit is contained in:
@@ -93,10 +93,10 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
|
||||
.value_name("VALIDATOR_INDEX")
|
||||
.required(true)
|
||||
.help("The first validator public key to be generated for this client."))
|
||||
.arg(Arg::with_name("validator_count")
|
||||
.value_name("COUNT")
|
||||
.arg(Arg::with_name("last_validator")
|
||||
.value_name("VALIDATOR_INDEX")
|
||||
.required(true)
|
||||
.help("The number of validators."))
|
||||
.help("The end of the range of keys to generate. This index is not generated."))
|
||||
)
|
||||
.subcommand(SubCommand::with_name("interop-yaml")
|
||||
.about("Loads plain-text secret keys from YAML files. Expects the interop format defined
|
||||
|
||||
@@ -1,16 +1,8 @@
|
||||
use crate::validator_directory::ValidatorDirectory;
|
||||
use bincode;
|
||||
use clap::ArgMatches;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use slog::{error, warn};
|
||||
use std::fs::{self, File};
|
||||
use std::io::{Error, ErrorKind};
|
||||
use std::ops::Range;
|
||||
use std::path::PathBuf;
|
||||
use types::{
|
||||
test_utils::{generate_deterministic_keypair, load_keypairs_from_yaml},
|
||||
EthSpec, Keypair, MainnetEthSpec,
|
||||
};
|
||||
use types::{EthSpec, MainnetEthSpec};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum KeySource {
|
||||
@@ -18,8 +10,6 @@ pub enum KeySource {
|
||||
Disk,
|
||||
/// Generate the keypairs (insecure, generates predictable keys).
|
||||
TestingKeypairRange(Range<usize>),
|
||||
/// Load testing keypairs from YAML
|
||||
YamlKeypairs(PathBuf),
|
||||
}
|
||||
|
||||
impl Default for KeySource {
|
||||
@@ -48,8 +38,6 @@ pub struct Config {
|
||||
pub slots_per_epoch: u64,
|
||||
}
|
||||
|
||||
const DEFAULT_PRIVATE_KEY_FILENAME: &str = "private.key";
|
||||
|
||||
impl Default for Config {
|
||||
/// Build a new configuration from defaults.
|
||||
fn default() -> Self {
|
||||
@@ -66,137 +54,74 @@ impl Default for Config {
|
||||
}
|
||||
|
||||
impl Config {
|
||||
/// Returns the full path for the client data directory (not just the name of the directory).
|
||||
pub fn full_data_dir(&self) -> Option<PathBuf> {
|
||||
dirs::home_dir().map(|path| path.join(&self.data_dir))
|
||||
}
|
||||
/// Parses the CLI arguments and attempts to load the client configuration.
|
||||
pub fn from_cli(cli_args: &ArgMatches) -> Result<Config, String> {
|
||||
let mut client_config = Config::default();
|
||||
|
||||
/// Creates the data directory (and any non-existing parent directories).
|
||||
pub fn create_data_dir(&self) -> Option<PathBuf> {
|
||||
let path = dirs::home_dir()?.join(&self.data_dir);
|
||||
fs::create_dir_all(&path).ok()?;
|
||||
Some(path)
|
||||
}
|
||||
|
||||
/// Apply the following arguments to `self`, replacing values if they are specified in `args`.
|
||||
///
|
||||
/// Returns an error if arguments are obviously invalid. May succeed even if some values are
|
||||
/// invalid.
|
||||
pub fn apply_cli_args(
|
||||
&mut self,
|
||||
args: &ArgMatches,
|
||||
_log: &slog::Logger,
|
||||
) -> Result<(), &'static str> {
|
||||
if let Some(datadir) = args.value_of("datadir") {
|
||||
self.data_dir = PathBuf::from(datadir);
|
||||
if let Some(datadir) = cli_args.value_of("datadir") {
|
||||
client_config.data_dir = PathBuf::from(datadir);
|
||||
};
|
||||
|
||||
if let Some(srv) = args.value_of("server") {
|
||||
self.server = srv.to_string();
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Loads the validator keys from disk.
|
||||
///
|
||||
/// ## Errors
|
||||
///
|
||||
/// Returns an error if the base directory does not exist, however it does not return for any
|
||||
/// invalid directories/files. Instead, it just filters out failures and logs errors. This
|
||||
/// behaviour is intended to avoid the scenario where a single invalid file can stop all
|
||||
/// validators.
|
||||
pub fn fetch_keys_from_disk(&self, log: &slog::Logger) -> Result<Vec<Keypair>, String> {
|
||||
let base_dir = self
|
||||
.full_data_dir()
|
||||
.ok_or_else(|| format!("Base directory does not exist: {:?}", self.full_data_dir()))?;
|
||||
|
||||
let keypairs = fs::read_dir(&base_dir)
|
||||
.map_err(|e| format!("Failed to read base directory: {:?}", e))?
|
||||
.filter_map(|validator_dir| {
|
||||
let path = validator_dir.ok()?.path();
|
||||
|
||||
if path.is_dir() {
|
||||
match ValidatorDirectory::load_for_signing(path.clone()) {
|
||||
Ok(validator_directory) => validator_directory.voting_keypair,
|
||||
Err(e) => {
|
||||
error!(
|
||||
log,
|
||||
"Failed to load a validator directory";
|
||||
"error" => e,
|
||||
"path" => path.to_str(),
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(keypairs)
|
||||
}
|
||||
|
||||
pub fn fetch_testing_keypairs(
|
||||
&self,
|
||||
range: std::ops::Range<usize>,
|
||||
) -> Result<Vec<Keypair>, String> {
|
||||
Ok(range.map(generate_deterministic_keypair).collect())
|
||||
}
|
||||
|
||||
/// Loads the keypairs according to `self.key_source`. Will return one or more keypairs, or an
|
||||
/// error.
|
||||
#[allow(dead_code)]
|
||||
pub fn fetch_keys(&self, log: &slog::Logger) -> Result<Vec<Keypair>, String> {
|
||||
let keypairs = match &self.key_source {
|
||||
KeySource::Disk => self.fetch_keys_from_disk(log)?,
|
||||
KeySource::TestingKeypairRange(range) => {
|
||||
warn!(
|
||||
log,
|
||||
"Using insecure interop private keys";
|
||||
"range" => format!("{:?}", range)
|
||||
);
|
||||
self.fetch_testing_keypairs(range.clone())?
|
||||
}
|
||||
KeySource::YamlKeypairs(path) => {
|
||||
warn!(
|
||||
log,
|
||||
"Private keys are stored insecurely (plain text). Testing use only."
|
||||
);
|
||||
|
||||
load_keypairs_from_yaml(path.to_path_buf())?
|
||||
}
|
||||
};
|
||||
|
||||
// Check if it's an empty vector, and return none.
|
||||
if keypairs.is_empty() {
|
||||
Err(
|
||||
"No validator keypairs were found, unable to proceed. To generate \
|
||||
testing keypairs, see 'testnet range --help'."
|
||||
.into(),
|
||||
)
|
||||
} else {
|
||||
Ok(keypairs)
|
||||
if let Some(server) = cli_args.value_of("server") {
|
||||
client_config.server = server.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
/// Saves a keypair to a file inside the appropriate validator directory. Returns the saved path filename.
|
||||
#[allow(dead_code)]
|
||||
pub fn save_key(&self, key: &Keypair) -> Result<PathBuf, Error> {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
let validator_config_path = self.data_dir.join(key.identifier());
|
||||
let key_path = validator_config_path.join(DEFAULT_PRIVATE_KEY_FILENAME);
|
||||
if let Some(port) = cli_args.value_of("server-http-port") {
|
||||
client_config.server_http_port = port
|
||||
.parse::<u16>()
|
||||
.map_err(|e| format!("Unable to parse HTTP port: {:?}", e))?;
|
||||
}
|
||||
|
||||
fs::create_dir_all(&validator_config_path)?;
|
||||
if let Some(port) = cli_args.value_of("server-grpc-port") {
|
||||
client_config.server_grpc_port = port
|
||||
.parse::<u16>()
|
||||
.map_err(|e| format!("Unable to parse gRPC port: {:?}", e))?;
|
||||
}
|
||||
|
||||
let mut key_file = File::create(&key_path)?;
|
||||
let mut perm = key_file.metadata()?.permissions();
|
||||
perm.set_mode((libc::S_IWUSR | libc::S_IRUSR) as u32);
|
||||
key_file.set_permissions(perm)?;
|
||||
let client_config = match cli_args.subcommand() {
|
||||
("testnet", Some(sub_cli_args)) => {
|
||||
if cli_args.is_present("eth2-config") && sub_cli_args.is_present("bootstrap") {
|
||||
return Err(
|
||||
"Cannot specify --eth2-config and --bootstrap as it may result \
|
||||
in ambiguity."
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
process_testnet_subcommand(sub_cli_args, client_config)
|
||||
}
|
||||
_ => return Err("You must use the testnet command. See '--help'.".into()),
|
||||
}?;
|
||||
|
||||
bincode::serialize_into(&mut key_file, &key)
|
||||
.map_err(|e| Error::new(ErrorKind::InvalidData, e))?;
|
||||
Ok(key_path)
|
||||
Ok(client_config)
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses the `testnet` CLI subcommand.
|
||||
fn process_testnet_subcommand(
|
||||
cli_args: &ArgMatches,
|
||||
mut client_config: Config,
|
||||
) -> Result<Config, String> {
|
||||
client_config.key_source = match cli_args.subcommand() {
|
||||
("insecure", Some(sub_cli_args)) => {
|
||||
let first = sub_cli_args
|
||||
.value_of("first_validator")
|
||||
.ok_or_else(|| "No first validator supplied")?
|
||||
.parse::<usize>()
|
||||
.map_err(|e| format!("Unable to parse first validator: {:?}", e))?;
|
||||
let last = sub_cli_args
|
||||
.value_of("last_validator")
|
||||
.ok_or_else(|| "No last validator supplied")?
|
||||
.parse::<usize>()
|
||||
.map_err(|e| format!("Unable to parse last validator: {:?}", e))?;
|
||||
|
||||
if last < first {
|
||||
return Err("Cannot supply a last validator less than the first".to_string());
|
||||
}
|
||||
|
||||
KeySource::TestingKeypairRange(first..last)
|
||||
}
|
||||
_ => KeySource::Disk,
|
||||
};
|
||||
|
||||
Ok(client_config)
|
||||
}
|
||||
|
||||
@@ -17,22 +17,25 @@ use clap::ArgMatches;
|
||||
use config::{Config as ClientConfig, KeySource};
|
||||
use duties_service::{DutiesService, DutiesServiceBuilder};
|
||||
use environment::RuntimeContext;
|
||||
use eth2_config::Eth2Config;
|
||||
use exit_future::Signal;
|
||||
use fork_service::{ForkService, ForkServiceBuilder};
|
||||
use futures::{Future, IntoFuture};
|
||||
use lighthouse_bootstrap::Bootstrapper;
|
||||
use futures::{
|
||||
future::{self, loop_fn, Loop},
|
||||
Future, IntoFuture,
|
||||
};
|
||||
use parking_lot::RwLock;
|
||||
use remote_beacon_node::RemoteBeaconNode;
|
||||
use slog::{info, Logger};
|
||||
use slog::{error, info, Logger};
|
||||
use slot_clock::SlotClock;
|
||||
use slot_clock::SystemTimeSlotClock;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use std::time::{Duration, Instant};
|
||||
use tokio::timer::Delay;
|
||||
use types::EthSpec;
|
||||
use validator_store::ValidatorStore;
|
||||
|
||||
const RETRY_DELAY: Duration = Duration::from_secs(2);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ProductionValidatorClient<T: EthSpec> {
|
||||
context: RuntimeContext<T>,
|
||||
@@ -47,22 +50,13 @@ impl<T: EthSpec> ProductionValidatorClient<T> {
|
||||
/// Instantiates the validator client, _without_ starting the timers to trigger block
|
||||
/// and attestation production.
|
||||
pub fn new_from_cli(
|
||||
mut context: RuntimeContext<T>,
|
||||
matches: &ArgMatches,
|
||||
context: RuntimeContext<T>,
|
||||
cli_args: &ArgMatches,
|
||||
) -> impl Future<Item = Self, Error = String> {
|
||||
let mut log = context.log.clone();
|
||||
|
||||
get_configs(&matches, &mut log)
|
||||
ClientConfig::from_cli(&cli_args)
|
||||
.into_future()
|
||||
.map_err(|e| format!("Unable to initialize config: {}", e))
|
||||
.and_then(|(client_config, eth2_config)| {
|
||||
// TODO: the eth2 config in the env is being completely ignored.
|
||||
//
|
||||
// See https://github.com/sigp/lighthouse/issues/602
|
||||
context.eth2_config = eth2_config;
|
||||
|
||||
Self::new(context, client_config)
|
||||
})
|
||||
.and_then(|client_config| Self::new(context, client_config))
|
||||
}
|
||||
|
||||
/// Instantiates the validator client, _without_ starting the timers to trigger block
|
||||
@@ -71,12 +65,14 @@ impl<T: EthSpec> ProductionValidatorClient<T> {
|
||||
mut context: RuntimeContext<T>,
|
||||
client_config: ClientConfig,
|
||||
) -> impl Future<Item = Self, Error = String> {
|
||||
let log = context.log.clone();
|
||||
let log_1 = context.log.clone();
|
||||
let log_2 = context.log.clone();
|
||||
let log_3 = context.log.clone();
|
||||
|
||||
info!(
|
||||
log,
|
||||
log_1,
|
||||
"Starting validator client";
|
||||
"datadir" => client_config.full_data_dir().expect("Unable to find datadir").to_str(),
|
||||
"datadir" => format!("{:?}", client_config.data_dir),
|
||||
);
|
||||
|
||||
format!(
|
||||
@@ -86,12 +82,18 @@ impl<T: EthSpec> ProductionValidatorClient<T> {
|
||||
.parse()
|
||||
.map_err(|e| format!("Unable to parse server address: {:?}", e))
|
||||
.into_future()
|
||||
.and_then(|http_server_addr| {
|
||||
.and_then(move |http_server_addr| {
|
||||
info!(
|
||||
log_1,
|
||||
"Beacon node connection info";
|
||||
"http_server" => format!("{}", http_server_addr),
|
||||
);
|
||||
|
||||
RemoteBeaconNode::new(http_server_addr)
|
||||
.map_err(|e| format!("Unable to init beacon node http client: {}", e))
|
||||
})
|
||||
.and_then(move |beacon_node| wait_for_node(beacon_node, log_2))
|
||||
.and_then(|beacon_node| {
|
||||
// TODO: add loop function to retry if node not online.
|
||||
beacon_node
|
||||
.http
|
||||
.spec()
|
||||
@@ -131,17 +133,29 @@ impl<T: EthSpec> ProductionValidatorClient<T> {
|
||||
Duration::from_millis(context.eth2_config.spec.milliseconds_per_slot),
|
||||
);
|
||||
|
||||
dbg!(context.eth2_config.spec.milliseconds_per_slot);
|
||||
|
||||
// TODO: fix expect.
|
||||
let validator_store = ValidatorStore::load_from_disk(
|
||||
client_config.full_data_dir().expect("Get rid of this."),
|
||||
context.eth2_config.spec.clone(),
|
||||
log.clone(),
|
||||
)?;
|
||||
let validator_store: ValidatorStore<T> = match &client_config.key_source {
|
||||
// Load pre-existing validators from the data dir.
|
||||
//
|
||||
// Use the `account_manager` to generate these files.
|
||||
KeySource::Disk => ValidatorStore::load_from_disk(
|
||||
client_config.data_dir.clone(),
|
||||
context.eth2_config.spec.clone(),
|
||||
log_3.clone(),
|
||||
)?,
|
||||
// Generate ephemeral insecure keypairs for testing purposes.
|
||||
//
|
||||
// Do not use in production.
|
||||
KeySource::TestingKeypairRange(range) => {
|
||||
ValidatorStore::insecure_ephemeral_validators(
|
||||
range.clone(),
|
||||
context.eth2_config.spec.clone(),
|
||||
log_3.clone(),
|
||||
)?
|
||||
}
|
||||
};
|
||||
|
||||
info!(
|
||||
log,
|
||||
log_3,
|
||||
"Loaded validator keypair store";
|
||||
"voting_validators" => validator_store.num_voting_validators()
|
||||
);
|
||||
@@ -221,134 +235,50 @@ impl<T: EthSpec> ProductionValidatorClient<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses the CLI arguments and attempts to load the client and eth2 configuration.
|
||||
///
|
||||
/// This is not a pure function, it reads from disk and may contact network servers.
|
||||
fn get_configs(
|
||||
cli_args: &ArgMatches,
|
||||
mut log: &mut Logger,
|
||||
) -> Result<(ClientConfig, Eth2Config), String> {
|
||||
let mut client_config = ClientConfig::default();
|
||||
/// Request the version from the node, looping back and trying again on failure. Exit once the node
|
||||
/// has been contacted.
|
||||
fn wait_for_node<E: EthSpec>(
|
||||
beacon_node: RemoteBeaconNode<E>,
|
||||
log: Logger,
|
||||
) -> impl Future<Item = RemoteBeaconNode<E>, Error = String> {
|
||||
// Try to get the version string from the node, looping until success is returned.
|
||||
loop_fn(beacon_node.clone(), move |beacon_node| {
|
||||
let log = log.clone();
|
||||
beacon_node
|
||||
.clone()
|
||||
.http
|
||||
.node()
|
||||
.get_version()
|
||||
.map_err(|e| format!("{:?}", e))
|
||||
.then(move |result| {
|
||||
let future: Box<dyn Future<Item = Loop<_, _>, Error = String> + Send> = match result
|
||||
{
|
||||
Ok(version) => {
|
||||
info!(
|
||||
log,
|
||||
"Connected to beacon node";
|
||||
"version" => version,
|
||||
);
|
||||
|
||||
client_config.apply_cli_args(&cli_args, &mut log)?;
|
||||
Box::new(future::ok(Loop::Break(beacon_node)))
|
||||
}
|
||||
Err(e) => {
|
||||
error!(
|
||||
log,
|
||||
"Unable to connect to beacon node";
|
||||
"error" => format!("{:?}", e),
|
||||
);
|
||||
|
||||
if let Some(server) = cli_args.value_of("server") {
|
||||
client_config.server = server.to_string();
|
||||
}
|
||||
Box::new(
|
||||
Delay::new(Instant::now() + RETRY_DELAY)
|
||||
.map_err(|e| format!("Failed to trigger delay: {:?}", e))
|
||||
.and_then(|_| future::ok(Loop::Continue(beacon_node))),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(port) = cli_args.value_of("server-http-port") {
|
||||
client_config.server_http_port = port
|
||||
.parse::<u16>()
|
||||
.map_err(|e| format!("Unable to parse HTTP port: {:?}", e))?;
|
||||
}
|
||||
|
||||
if let Some(port) = cli_args.value_of("server-grpc-port") {
|
||||
client_config.server_grpc_port = port
|
||||
.parse::<u16>()
|
||||
.map_err(|e| format!("Unable to parse gRPC port: {:?}", e))?;
|
||||
}
|
||||
|
||||
info!(
|
||||
*log,
|
||||
"Beacon node connection info";
|
||||
"grpc_port" => client_config.server_grpc_port,
|
||||
"http_port" => client_config.server_http_port,
|
||||
"server" => &client_config.server,
|
||||
);
|
||||
|
||||
let (client_config, eth2_config) = match cli_args.subcommand() {
|
||||
("testnet", Some(sub_cli_args)) => {
|
||||
if cli_args.is_present("eth2-config") && sub_cli_args.is_present("bootstrap") {
|
||||
return Err(
|
||||
"Cannot specify --eth2-config and --bootstrap as it may result \
|
||||
in ambiguity."
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
process_testnet_subcommand(sub_cli_args, client_config, log)
|
||||
}
|
||||
_ => return Err("You must use the testnet command. See '--help'.".into()),
|
||||
}?;
|
||||
|
||||
Ok((client_config, eth2_config))
|
||||
}
|
||||
|
||||
/// Parses the `testnet` CLI subcommand.
|
||||
///
|
||||
/// This is not a pure function, it reads from disk and may contact network servers.
|
||||
fn process_testnet_subcommand(
|
||||
cli_args: &ArgMatches,
|
||||
mut client_config: ClientConfig,
|
||||
log: &Logger,
|
||||
) -> Result<(ClientConfig, Eth2Config), String> {
|
||||
let eth2_config = if cli_args.is_present("bootstrap") {
|
||||
info!(log, "Connecting to bootstrap server");
|
||||
let bootstrapper = Bootstrapper::connect(
|
||||
format!(
|
||||
"http://{}:{}",
|
||||
client_config.server, client_config.server_http_port
|
||||
),
|
||||
&log,
|
||||
)?;
|
||||
|
||||
let eth2_config = bootstrapper.eth2_config()?;
|
||||
|
||||
info!(
|
||||
log,
|
||||
"Bootstrapped eth2 config via HTTP";
|
||||
"slot_time_millis" => eth2_config.spec.milliseconds_per_slot,
|
||||
"spec" => ð2_config.spec_constants,
|
||||
);
|
||||
|
||||
eth2_config
|
||||
} else {
|
||||
match cli_args.value_of("spec") {
|
||||
Some("mainnet") => Eth2Config::mainnet(),
|
||||
Some("minimal") => Eth2Config::minimal(),
|
||||
Some("interop") => Eth2Config::interop(),
|
||||
_ => return Err("No --spec flag provided. See '--help'.".into()),
|
||||
}
|
||||
};
|
||||
|
||||
client_config.key_source = match cli_args.subcommand() {
|
||||
("insecure", Some(sub_cli_args)) => {
|
||||
let first = sub_cli_args
|
||||
.value_of("first_validator")
|
||||
.ok_or_else(|| "No first validator supplied")?
|
||||
.parse::<usize>()
|
||||
.map_err(|e| format!("Unable to parse first validator: {:?}", e))?;
|
||||
let count = sub_cli_args
|
||||
.value_of("validator_count")
|
||||
.ok_or_else(|| "No validator count supplied")?
|
||||
.parse::<usize>()
|
||||
.map_err(|e| format!("Unable to parse validator count: {:?}", e))?;
|
||||
|
||||
info!(
|
||||
log,
|
||||
"Generating unsafe testing keys";
|
||||
"first_validator" => first,
|
||||
"count" => count
|
||||
);
|
||||
|
||||
KeySource::TestingKeypairRange(first..first + count)
|
||||
}
|
||||
("interop-yaml", Some(sub_cli_args)) => {
|
||||
let path = sub_cli_args
|
||||
.value_of("path")
|
||||
.ok_or_else(|| "No yaml path supplied")?
|
||||
.parse::<PathBuf>()
|
||||
.map_err(|e| format!("Unable to parse yaml path: {:?}", e))?;
|
||||
|
||||
info!(
|
||||
log,
|
||||
"Loading keypairs from interop YAML format";
|
||||
"path" => format!("{:?}", path),
|
||||
);
|
||||
|
||||
KeySource::YamlKeypairs(path)
|
||||
}
|
||||
_ => KeySource::Disk,
|
||||
};
|
||||
|
||||
Ok((client_config, eth2_config))
|
||||
future
|
||||
})
|
||||
})
|
||||
.map(|_| beacon_node)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
use crate::validator_directory::ValidatorDirectory;
|
||||
use crate::validator_directory::{ValidatorDirectory, ValidatorDirectoryBuilder};
|
||||
use parking_lot::RwLock;
|
||||
use rayon::prelude::*;
|
||||
use slog::{error, Logger};
|
||||
use std::collections::HashMap;
|
||||
use std::fs::read_dir;
|
||||
use std::iter::FromIterator;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::Range;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use tempdir::TempDir;
|
||||
use tree_hash::{SignedRoot, TreeHash};
|
||||
use types::{
|
||||
Attestation, BeaconBlock, ChainSpec, Domain, Epoch, EthSpec, Fork, PublicKey, Signature,
|
||||
@@ -17,6 +20,7 @@ pub struct ValidatorStore<E> {
|
||||
validators: Arc<RwLock<HashMap<PublicKey, ValidatorDirectory>>>,
|
||||
spec: Arc<ChainSpec>,
|
||||
log: Logger,
|
||||
temp_dir: Option<Arc<TempDir>>,
|
||||
_phantom: PhantomData<E>,
|
||||
}
|
||||
|
||||
@@ -55,6 +59,47 @@ impl<E: EthSpec> ValidatorStore<E> {
|
||||
validators: Arc::new(RwLock::new(HashMap::from_iter(validator_iter))),
|
||||
spec: Arc::new(spec),
|
||||
log,
|
||||
temp_dir: None,
|
||||
_phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn insecure_ephemeral_validators(
|
||||
range: Range<usize>,
|
||||
spec: ChainSpec,
|
||||
log: Logger,
|
||||
) -> Result<Self, String> {
|
||||
let temp_dir = TempDir::new("insecure_validator")
|
||||
.map_err(|e| format!("Unable to create temp dir: {:?}", e))?;
|
||||
let data_dir = PathBuf::from(temp_dir.path());
|
||||
|
||||
let validators = range
|
||||
.collect::<Vec<_>>()
|
||||
.par_iter()
|
||||
.map(|index| {
|
||||
ValidatorDirectoryBuilder::default()
|
||||
.spec(spec.clone())
|
||||
.full_deposit_amount()?
|
||||
.insecure_keypairs(*index)
|
||||
.create_directory(data_dir.clone())?
|
||||
.write_keypair_files()?
|
||||
.write_eth1_data_file()?
|
||||
.build()
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?
|
||||
.into_iter()
|
||||
.filter_map(|validator_directory| {
|
||||
validator_directory
|
||||
.voting_keypair
|
||||
.clone()
|
||||
.map(|voting_keypair| (voting_keypair.pk, validator_directory))
|
||||
});
|
||||
|
||||
Ok(Self {
|
||||
validators: Arc::new(RwLock::new(HashMap::from_iter(validators))),
|
||||
spec: Arc::new(spec),
|
||||
log,
|
||||
temp_dir: Some(Arc::new(temp_dir)),
|
||||
_phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user