Refactor beacon node CLI

This commit is contained in:
Paul Hauner
2019-11-28 10:31:51 +11:00
parent 9bd0e13d9d
commit 73ead55235
14 changed files with 455 additions and 835 deletions

View File

@@ -35,3 +35,4 @@ futures = "0.1.29"
environment = { path = "../lighthouse/environment" } environment = { path = "../lighthouse/environment" }
genesis = { path = "genesis" } genesis = { path = "genesis" }
eth2_testnet = { path = "../eth2/utils/eth2_testnet" } eth2_testnet = { path = "../eth2/utils/eth2_testnet" }
eth2-libp2p = { path = "./eth2-libp2p" }

View File

@@ -41,8 +41,8 @@ impl Default for ClientGenesis {
pub struct Config { pub struct Config {
pub data_dir: PathBuf, pub data_dir: PathBuf,
pub db_type: String, pub db_type: String,
db_name: String, pub db_name: String,
freezer_db_path: Option<PathBuf>, pub freezer_db_path: Option<PathBuf>,
pub log_file: PathBuf, pub log_file: PathBuf,
pub spec_constants: String, pub spec_constants: String,
/// If true, the node will use co-ordinated junk for eth1 values. /// If true, the node will use co-ordinated junk for eth1 values.
@@ -69,7 +69,7 @@ impl Default for Config {
db_name: "chain_db".to_string(), db_name: "chain_db".to_string(),
freezer_db_path: None, freezer_db_path: None,
genesis: <_>::default(), genesis: <_>::default(),
network: NetworkConfig::new(), network: NetworkConfig::default(),
rest_api: <_>::default(), rest_api: <_>::default(),
websocket_server: <_>::default(), websocket_server: <_>::default(),
spec_constants: TESTNET_SPEC_CONSTANTS.into(), spec_constants: TESTNET_SPEC_CONSTANTS.into(),
@@ -135,26 +135,6 @@ impl Config {
.ok_or_else(|| "Unable to locate user home directory".to_string())?; .ok_or_else(|| "Unable to locate user home directory".to_string())?;
ensure_dir_exists(path) ensure_dir_exists(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<(), String> {
if let Some(dir) = args.value_of("datadir") {
self.data_dir = PathBuf::from(dir);
};
if let Some(freezer_dir) = args.value_of("freezer-dir") {
self.freezer_db_path = Some(PathBuf::from(freezer_dir));
}
self.network.apply_cli_args(args)?;
self.rest_api.apply_cli_args(args)?;
self.websocket_server.apply_cli_args(args)?;
Ok(())
}
} }
/// Ensure that the directory at `path` exists, by creating it and all parents if necessary. /// Ensure that the directory at `path` exists, by creating it and all parents if necessary.

View File

@@ -5,7 +5,6 @@ authors = ["Age Manning <Age@AgeManning.com>"]
edition = "2018" edition = "2018"
[dependencies] [dependencies]
clap = "2.33.0"
hex = "0.3" hex = "0.3"
# rust-libp2p is presently being sourced from a Sigma Prime fork of the # rust-libp2p is presently being sourced from a Sigma Prime fork of the
# `libp2p/rust-libp2p` repository. # `libp2p/rust-libp2p` repository.

View File

@@ -1,4 +1,3 @@
use clap::ArgMatches;
use enr::Enr; use enr::Enr;
use libp2p::gossipsub::{GossipsubConfig, GossipsubConfigBuilder}; use libp2p::gossipsub::{GossipsubConfig, GossipsubConfigBuilder};
use libp2p::Multiaddr; use libp2p::Multiaddr;
@@ -97,84 +96,3 @@ impl Default for Config {
} }
} }
} }
/// Generates a default Config.
impl Config {
pub fn new() -> Self {
Config::default()
}
pub fn apply_cli_args(&mut self, args: &ArgMatches) -> Result<(), String> {
// If a `datadir` has been specified, set the network dir to be inside it.
if let Some(dir) = args.value_of("datadir") {
self.network_dir = PathBuf::from(dir).join("network");
};
// If a network dir has been specified, override the `datadir` definition.
if let Some(dir) = args.value_of("network-dir") {
self.network_dir = PathBuf::from(dir);
};
if let Some(listen_address_str) = args.value_of("listen-address") {
let listen_address = listen_address_str
.parse()
.map_err(|_| format!("Invalid listen address: {:?}", listen_address_str))?;
self.listen_address = listen_address;
self.discovery_address = listen_address;
}
if let Some(max_peers_str) = args.value_of("maxpeers") {
self.max_peers = max_peers_str
.parse::<usize>()
.map_err(|_| format!("Invalid number of max peers: {}", max_peers_str))?;
}
if let Some(port_str) = args.value_of("port") {
let port = port_str
.parse::<u16>()
.map_err(|_| format!("Invalid port: {}", port_str))?;
self.libp2p_port = port;
self.discovery_port = port;
}
if let Some(boot_enr_str) = args.value_of("boot-nodes") {
self.boot_nodes = boot_enr_str
.split(',')
.map(|enr| enr.parse().map_err(|_| format!("Invalid ENR: {}", enr)))
.collect::<Result<Vec<Enr>, _>>()?;
}
if let Some(libp2p_addresses_str) = args.value_of("libp2p-addresses") {
self.libp2p_nodes = libp2p_addresses_str
.split(',')
.map(|multiaddr| {
multiaddr
.parse()
.map_err(|_| format!("Invalid Multiaddr: {}", multiaddr))
})
.collect::<Result<Vec<Multiaddr>, _>>()?;
}
if let Some(topics_str) = args.value_of("topics") {
self.topics = topics_str.split(',').map(|s| s.into()).collect();
}
if let Some(discovery_address_str) = args.value_of("discovery-address") {
self.discovery_address = discovery_address_str
.parse()
.map_err(|_| format!("Invalid discovery address: {:?}", discovery_address_str))?
}
if let Some(disc_port_str) = args.value_of("disc-port") {
self.discovery_port = disc_port_str
.parse::<u16>()
.map_err(|_| format!("Invalid discovery port: {}", disc_port_str))?;
}
if let Some(p2p_priv_key) = args.value_of("p2p-priv-key") {
self.secret_key_hex = Some(p2p_priv_key.to_string());
}
Ok(())
}
}

View File

@@ -22,7 +22,6 @@ eth2_ssz = { path = "../../eth2/utils/ssz" }
eth2_ssz_derive = { path = "../../eth2/utils/ssz_derive" } eth2_ssz_derive = { path = "../../eth2/utils/ssz_derive" }
state_processing = { path = "../../eth2/state_processing" } state_processing = { path = "../../eth2/state_processing" }
types = { path = "../../eth2/types" } types = { path = "../../eth2/types" }
clap = "2.33"
http = "0.1" http = "0.1"
hyper = "0.12" hyper = "0.12"
exit-future = "0.1.4" exit-future = "0.1.4"

View File

@@ -1,4 +1,3 @@
use clap::ArgMatches;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::net::Ipv4Addr; use std::net::Ipv4Addr;
@@ -50,25 +49,3 @@ impl Default for Config {
} }
} }
} }
impl Config {
pub fn apply_cli_args(&mut self, args: &ArgMatches) -> Result<(), &'static str> {
if args.is_present("no-api") {
self.enabled = false;
}
if let Some(rpc_address) = args.value_of("api-address") {
self.listen_address = rpc_address
.parse::<Ipv4Addr>()
.map_err(|_| "api-address is not a valid IPv4 address.")?;
}
if let Some(rpc_port) = args.value_of("api-port") {
self.port = rpc_port
.parse::<u16>()
.map_err(|_| "api-port is not a valid u16.")?;
}
Ok(())
}
}

View File

@@ -29,20 +29,19 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
* Network parameters. * Network parameters.
*/ */
.arg( .arg(
Arg::with_name("port-bump") Arg::with_name("zero-ports")
.long("port-bump") .long("zero-ports")
.short("b") .short("z")
.value_name("INCREMENT") .help("Sets all listening TCP/UDP ports to 0, allowing the OS to choose some \
.help("Sets all listening TCP/UDP ports to default values, but with each port increased by \ arbitrary free port number.")
INCREMENT. Useful when starting multiple nodes on a single machine. Using increments \ .takes_value(false),
in multiples of 10 is recommended.")
.takes_value(true),
) )
.arg( .arg(
Arg::with_name("listen-address") Arg::with_name("listen-address")
.long("listen-address") .long("listen-address")
.value_name("ADDRESS") .value_name("ADDRESS")
.help("The address lighthouse will listen for UDP and TCP connections. (default 127.0.0.1).") .help("The address lighthouse will listen for UDP and TCP connections.")
.default_value("127.0.0.1")
.takes_value(true) .takes_value(true)
) )
.arg( .arg(
@@ -50,13 +49,14 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.long("port") .long("port")
.value_name("PORT") .value_name("PORT")
.help("The TCP/UDP port to listen on. The UDP port can be modified by the --discovery-port flag.") .help("The TCP/UDP port to listen on. The UDP port can be modified by the --discovery-port flag.")
.conflicts_with("port-bump") .default_value("9000")
.takes_value(true), .takes_value(true),
) )
.arg( .arg(
Arg::with_name("maxpeers") Arg::with_name("maxpeers")
.long("maxpeers") .long("maxpeers")
.help("The maximum number of peers (default 10).") .help("The maximum number of peers.")
.default_value("10")
.takes_value(true), .takes_value(true),
) )
.arg( .arg(
@@ -72,64 +72,70 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.long("disc-port") .long("disc-port")
.value_name("PORT") .value_name("PORT")
.help("The discovery UDP port.") .help("The discovery UDP port.")
.conflicts_with("port-bump") .default_value("9000")
.takes_value(true), .takes_value(true),
) )
.arg( .arg(
Arg::with_name("discovery-address") Arg::with_name("discovery-address")
.long("discovery-address") .long("discovery-address")
.value_name("ADDRESS") .value_name("ADDRESS")
.help("The IP address to broadcast to other peers on how to reach this node.") .help("The IP address to broadcast to other peers on how to reach this node. \
Default is determined automatically.")
.takes_value(true), .takes_value(true),
) )
.arg( .arg(
Arg::with_name("topics") Arg::with_name("topics")
.long("topics") .long("topics")
.value_name("STRING") .value_name("STRING")
.help("One or more comma-delimited gossipsub topic strings to subscribe to.") .help("One or more comma-delimited gossipsub topic strings to subscribe to. Default \
is determined automatically.")
.takes_value(true), .takes_value(true),
) )
.arg( .arg(
Arg::with_name("libp2p-addresses") Arg::with_name("libp2p-addresses")
.long("libp2p-addresses") .long("libp2p-addresses")
.value_name("MULTIADDR") .value_name("MULTIADDR")
.help("One or more comma-delimited multiaddrs to manually connect to a libp2p peer without an ENR.") .help("One or more comma-delimited multiaddrs to manually connect to a libp2p peer \
without an ENR.")
.takes_value(true), .takes_value(true),
) )
.arg( .arg(
Arg::with_name("p2p-priv-key") Arg::with_name("p2p-priv-key")
.long("p2p-priv-key") .long("p2p-priv-key")
.value_name("HEX") .value_name("HEX")
.help("A secp256k1 secret key, represented as ASCII-encoded hex bytes (with or without 0x prefix).") .help("A secp256k1 secret key, represented as ASCII-encoded hex bytes (with or \
without 0x prefix). Default is either loaded from disk or generated \
automatically.")
.takes_value(true), .takes_value(true),
) )
/* REST API related arguments */ /* REST API related arguments */
.arg( .arg(
Arg::with_name("no-api") Arg::with_name("http")
.long("no-api") .long("http")
.help("Disable RESTful HTTP API server.") .help("Enable RESTful HTTP API server. Disabled by default.")
.takes_value(false), .takes_value(false),
) )
.arg( .arg(
Arg::with_name("api-address") Arg::with_name("http-address")
.long("api-address") .long("http-address")
.value_name("ADDRESS") .value_name("ADDRESS")
.help("Set the listen address for the RESTful HTTP API server.") .help("Set the listen address for the RESTful HTTP API server.")
.default_value("127.0.0.1")
.takes_value(true), .takes_value(true),
) )
.arg( .arg(
Arg::with_name("api-port") Arg::with_name("http-port")
.long("api-port") .long("http-port")
.value_name("PORT") .value_name("PORT")
.help("Set the listen TCP port for the RESTful HTTP API server.") .help("Set the listen TCP port for the RESTful HTTP API server.")
.conflicts_with("port-bump") .default_value("5052")
.takes_value(true), .takes_value(true),
) )
/* Websocket related arguments */ /* Websocket related arguments */
.arg( .arg(
Arg::with_name("no-ws") Arg::with_name("ws")
.long("no-ws") .long("ws")
.help("Disable websocket server.") .help("Enable the websocket server. Disabled by default.")
.takes_value(false), .takes_value(false),
) )
.arg( .arg(
@@ -137,7 +143,7 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.long("ws-address") .long("ws-address")
.value_name("ADDRESS") .value_name("ADDRESS")
.help("Set the listen address for the websocket server.") .help("Set the listen address for the websocket server.")
.conflicts_with_all(&["no-ws"]) .default_value("127.0.0.1")
.takes_value(true), .takes_value(true),
) )
.arg( .arg(
@@ -145,16 +151,24 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.long("ws-port") .long("ws-port")
.value_name("PORT") .value_name("PORT")
.help("Set the listen TCP port for the websocket server.") .help("Set the listen TCP port for the websocket server.")
.conflicts_with_all(&["no-ws", "port-bump"]) .default_value("5053")
.takes_value(true), .takes_value(true),
) )
/* /*
* Eth1 Integration * Eth1 Integration
*/ */
.arg(
Arg::with_name("eth1")
.long("eth1")
.help("If present the node will connect to an eth1 node. This is required for \
block production, you must use this flag if you wish to serve a validator.")
.takes_value(false),
)
.arg( .arg(
Arg::with_name("dummy-eth1") Arg::with_name("dummy-eth1")
.long("dummy-eth1") .long("dummy-eth1")
.conflicts_with("eth1")
.help("If present, uses an eth1 backend that generates static dummy data.\ .help("If present, uses an eth1 backend that generates static dummy data.\
Identical to the method used at the 2019 Canada interop.") Identical to the method used at the 2019 Canada interop.")
) )
@@ -166,32 +180,6 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.takes_value(true) .takes_value(true)
.default_value("http://localhost:8545") .default_value("http://localhost:8545")
) )
.arg(
Arg::with_name("eth1-follow")
.long("eth1-follow")
.value_name("BLOCK_COUNT")
.help("Specifies how many blocks we should cache behind the eth1 head. A larger number means a smaller cache.")
.takes_value(true)
// TODO: set this higher once we're not using testnets all the time.
.default_value("0")
)
.arg(
Arg::with_name("deposit-contract")
.long("deposit-contract")
.short("e")
.value_name("DEPOSIT-CONTRACT")
.help("Specifies the deposit contract address on the Eth1 chain.")
.takes_value(true)
)
.arg(
Arg::with_name("deposit-contract-deploy")
.long("deposit-contract-deploy")
.value_name("BLOCK_NUMBER")
.help("Specifies the block number that the deposit contract was deployed at.")
.takes_value(true)
// TODO: set this higher once we're not using testnets all the time.
.default_value("0")
)
/* /*
* The "testnet" sub-command. * The "testnet" sub-command.
* *
@@ -199,21 +187,6 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
*/ */
.subcommand(SubCommand::with_name("testnet") .subcommand(SubCommand::with_name("testnet")
.about("Create a new Lighthouse datadir using a testnet strategy.") .about("Create a new Lighthouse datadir using a testnet strategy.")
.arg(
Arg::with_name("eth2-config")
.long("eth2-config")
.value_name("TOML_FILE")
.help("A existing eth2_spec TOML file (e.g., eth2_spec.toml).")
.takes_value(true)
.conflicts_with("spec")
)
.arg(
Arg::with_name("client-config")
.long("client-config")
.value_name("TOML_FILE")
.help("An existing beacon_node TOML file (e.g., beacon_node.toml).")
.takes_value(true)
)
.arg( .arg(
Arg::with_name("random-datadir") Arg::with_name("random-datadir")
.long("random-datadir") .long("random-datadir")
@@ -221,13 +194,6 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.help("If present, append a random string to the datadir path. Useful for fast development \ .help("If present, append a random string to the datadir path. Useful for fast development \
iteration.") iteration.")
) )
.arg(
Arg::with_name("random-propagation")
.long("random-propagation")
.value_name("INTEGER")
.takes_value(true)
.help("Specifies (as a percentage) the likelihood of propagating blocks and attestations. This should only be used for testing networking elements. The value must like in the range 1-100.")
)
.arg( .arg(
Arg::with_name("force") Arg::with_name("force")
.long("force") .long("force")
@@ -236,33 +202,22 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
backup directory.") backup directory.")
.conflicts_with("random-datadir") .conflicts_with("random-datadir")
) )
.arg(
Arg::with_name("random-propagation")
.long("random-propagation")
.value_name("INTEGER")
.takes_value(true)
.help("Specifies (as a percentage) the likelihood of propagating blocks and \
attestations. This should only be used for testing networking elements. The \
value must like in the range 1-100. Default is 100.")
)
.arg( .arg(
Arg::with_name("slot-time") Arg::with_name("slot-time")
.long("slot-time") .long("slot-time")
.short("t") .short("t")
.value_name("MILLISECONDS") .value_name("MILLISECONDS")
.help("Defines the slot time when creating a new testnet.") .help("Defines the slot time when creating a new testnet. The default is \
) specified by the spec.")
/*
* `boostrap`
*
* Start a new node by downloading genesis and network info from another node via the
* HTTP API.
*/
.subcommand(SubCommand::with_name("bootstrap")
.about("Connects to the given HTTP server, downloads a genesis state and attempts to peer with it.")
.arg(Arg::with_name("server")
.value_name("HTTP_SERVER")
.required(true)
.default_value("http://localhost:5052")
.help("A HTTP server, with a http:// prefix"))
.arg(Arg::with_name("libp2p-port")
.short("p")
.long("port")
.value_name("TCP_PORT")
.help("A libp2p listen port used to peer with the bootstrap server. This flag is useful \
when port-fowarding is used: you may connect using a different port than \
the one the server is immediately listening on."))
) )
/* /*
* `recent` * `recent`
@@ -282,7 +237,7 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.short("m") .short("m")
.value_name("MINUTES") .value_name("MINUTES")
.required(true) .required(true)
.default_value("0") .default_value("30")
.help("The maximum number of minutes that will have elapsed before genesis")) .help("The maximum number of minutes that will have elapsed before genesis"))
) )
/* /*
@@ -320,25 +275,14 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.required(true) .required(true)
.help("A file from which to read the state")) .help("A file from which to read the state"))
) )
/*
* `eth1`
*
* Connect to a Sigma Prime testnet.
*/
.subcommand(SubCommand::with_name("eth1")
.about("Connect to a testnet defined by an eth2_testnet directory.")
.arg(Arg::with_name("directory")
.value_name("DIRECTORY")
.index(1)
.help("A directory generated by lcli. Defaults to ~/.lighthouse/testnet. See lcli testnet --help"))
)
/* /*
* `prysm` * `prysm`
* *
* Connect to the Prysmatic Labs testnet. * Connect to the Prysmatic Labs testnet.
*/ */
.subcommand(SubCommand::with_name("prysm") .subcommand(SubCommand::with_name("prysm")
.about("Connect to the Prysmatic Labs testnet on Goerli.") .about("Connect to the Prysmatic Labs testnet on Goerli. Not guaranteed to be \
up-to-date or functioning.")
) )
) )
} }

File diff suppressed because it is too large Load Diff

View File

@@ -57,15 +57,15 @@ impl<E: EthSpec> ProductionBeaconNode<E> {
) -> impl Future<Item = Self, Error = String> + 'a { ) -> impl Future<Item = Self, Error = String> + 'a {
let log = context.log.clone(); let log = context.log.clone();
// TODO: the eth2 config in the env is being completely ignored. // TODO: the eth2 config in the env is being modified.
// //
// See https://github.com/sigp/lighthouse/issues/602 // See https://github.com/sigp/lighthouse/issues/602
get_configs(&matches, log).into_future().and_then( get_configs(&matches, context.eth2_config.clone(), log)
move |(client_config, eth2_config, _log)| { .into_future()
.and_then(move |(client_config, eth2_config, _log)| {
context.eth2_config = eth2_config; context.eth2_config = eth2_config;
Self::new(context, client_config) Self::new(context, client_config)
}, })
)
} }
/// Starts a new beacon node `Client` in the given `environment`. /// Starts a new beacon node `Client` in the given `environment`.

View File

@@ -7,7 +7,6 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
clap = "2.33.0"
exit-future = "0.1.4" exit-future = "0.1.4"
futures = "0.1.29" futures = "0.1.29"
serde = "1.0.102" serde = "1.0.102"

View File

@@ -1,5 +1,4 @@
use clap::ArgMatches; use serde_derive::{Deserialize, Serialize};
use serde::{Deserialize, Serialize};
use std::net::Ipv4Addr; use std::net::Ipv4Addr;
/// The core configuration of a Lighthouse beacon node. /// The core configuration of a Lighthouse beacon node.
@@ -21,25 +20,3 @@ impl Default for Config {
} }
} }
} }
impl Config {
pub fn apply_cli_args(&mut self, args: &ArgMatches) -> Result<(), &'static str> {
if args.is_present("no-ws") {
self.enabled = false;
}
if let Some(rpc_address) = args.value_of("ws-address") {
self.listen_address = rpc_address
.parse::<Ipv4Addr>()
.map_err(|_| "ws-address is not a valid IPv4 address.")?;
}
if let Some(rpc_port) = args.value_of("ws-port") {
self.port = rpc_port
.parse::<u16>()
.map_err(|_| "ws-port is not a valid u16.")?;
}
Ok(())
}
}

View File

@@ -13,3 +13,4 @@ tempdir = "0.3"
serde = "1.0" serde = "1.0"
serde_json = "^1.0" serde_json = "^1.0"
types = { path = "../../types"} types = { path = "../../types"}
eth2-libp2p = { path = "../../../beacon_node/eth2-libp2p"}

View File

@@ -7,6 +7,7 @@
//! //!
//! https://github.com/sigp/lighthouse/pull/605 //! https://github.com/sigp/lighthouse/pull/605
use eth2_libp2p::Enr;
use std::fs::{create_dir_all, File}; use std::fs::{create_dir_all, File};
use std::path::PathBuf; use std::path::PathBuf;
use types::Address; use types::Address;
@@ -14,12 +15,14 @@ use types::Address;
pub const ADDRESS_FILE: &str = "deposit_contract.txt"; pub const ADDRESS_FILE: &str = "deposit_contract.txt";
pub const DEPLOY_BLOCK_FILE: &str = "deploy_block.txt"; pub const DEPLOY_BLOCK_FILE: &str = "deploy_block.txt";
pub const MIN_GENESIS_TIME_FILE: &str = "min_genesis_time.txt"; pub const MIN_GENESIS_TIME_FILE: &str = "min_genesis_time.txt";
pub const BOOT_NODES_FILE: &str = "boot_nodes.json";
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug)]
pub struct Eth2TestnetDir { pub struct Eth2TestnetDir {
deposit_contract_address: String, deposit_contract_address: String,
pub deposit_contract_deploy_block: u64, pub deposit_contract_deploy_block: u64,
pub min_genesis_time: u64, pub min_genesis_time: u64,
pub boot_nodes: Vec<Enr>,
} }
impl Eth2TestnetDir { impl Eth2TestnetDir {
@@ -28,6 +31,7 @@ impl Eth2TestnetDir {
deposit_contract_address: String, deposit_contract_address: String,
deposit_contract_deploy_block: u64, deposit_contract_deploy_block: u64,
min_genesis_time: u64, min_genesis_time: u64,
boot_nodes: Vec<Enr>,
) -> Result<Self, String> { ) -> Result<Self, String> {
if base_dir.exists() { if base_dir.exists() {
return Err("Testnet directory already exists".to_string()); return Err("Testnet directory already exists".to_string());
@@ -36,60 +40,52 @@ impl Eth2TestnetDir {
create_dir_all(&base_dir) create_dir_all(&base_dir)
.map_err(|e| format!("Unable to create testnet directory: {:?}", e))?; .map_err(|e| format!("Unable to create testnet directory: {:?}", e))?;
File::create(base_dir.join(ADDRESS_FILE)) macro_rules! write_to_file {
.map_err(|e| format!("Unable to create {}: {:?}", ADDRESS_FILE, e)) ($file: ident, $variable: ident) => {
File::create(base_dir.join($file))
.map_err(|e| format!("Unable to create {}: {:?}", $file, e))
.and_then(|file| { .and_then(|file| {
serde_json::to_writer(file, &deposit_contract_address) serde_json::to_writer(file, &$variable)
.map_err(|e| format!("Unable to write {}: {:?}", ADDRESS_FILE, e)) .map_err(|e| format!("Unable to write {}: {:?}", $file, e))
})?; })?;
};
}
File::create(base_dir.join(DEPLOY_BLOCK_FILE)) write_to_file!(ADDRESS_FILE, deposit_contract_address);
.map_err(|e| format!("Unable to create {}: {:?}", DEPLOY_BLOCK_FILE, e)) write_to_file!(DEPLOY_BLOCK_FILE, deposit_contract_deploy_block);
.and_then(|file| { write_to_file!(MIN_GENESIS_TIME_FILE, min_genesis_time);
serde_json::to_writer(file, &deposit_contract_deploy_block) write_to_file!(BOOT_NODES_FILE, boot_nodes);
.map_err(|e| format!("Unable to write {}: {:?}", DEPLOY_BLOCK_FILE, e))
})?;
File::create(base_dir.join(MIN_GENESIS_TIME_FILE))
.map_err(|e| format!("Unable to create {}: {:?}", MIN_GENESIS_TIME_FILE, e))
.and_then(|file| {
serde_json::to_writer(file, &min_genesis_time)
.map_err(|e| format!("Unable to write {}: {:?}", MIN_GENESIS_TIME_FILE, e))
})?;
Ok(Self { Ok(Self {
deposit_contract_address, deposit_contract_address,
deposit_contract_deploy_block, deposit_contract_deploy_block,
min_genesis_time, min_genesis_time,
boot_nodes,
}) })
} }
pub fn load(base_dir: PathBuf) -> Result<Self, String> { pub fn load(base_dir: PathBuf) -> Result<Self, String> {
let deposit_contract_address = File::open(base_dir.join(ADDRESS_FILE)) macro_rules! load_from_file {
.map_err(|e| format!("Unable to open {}: {:?}", ADDRESS_FILE, e)) ($file: ident) => {
File::open(base_dir.join($file))
.map_err(|e| format!("Unable to open {}: {:?}", $file, e))
.and_then(|file| { .and_then(|file| {
serde_json::from_reader(file) serde_json::from_reader(file)
.map_err(|e| format!("Unable to parse {}: {:?}", ADDRESS_FILE, e)) .map_err(|e| format!("Unable to parse {}: {:?}", $file, e))
})?; })?;
};
}
let deposit_contract_deploy_block = File::open(base_dir.join(DEPLOY_BLOCK_FILE)) let deposit_contract_address = load_from_file!(ADDRESS_FILE);
.map_err(|e| format!("Unable to open {}: {:?}", DEPLOY_BLOCK_FILE, e)) let deposit_contract_deploy_block = load_from_file!(DEPLOY_BLOCK_FILE);
.and_then(|file| { let min_genesis_time = load_from_file!(MIN_GENESIS_TIME_FILE);
serde_json::from_reader(file) let boot_nodes = load_from_file!(BOOT_NODES_FILE);
.map_err(|e| format!("Unable to parse {}: {:?}", DEPLOY_BLOCK_FILE, e))
})?;
let min_genesis_time = File::open(base_dir.join(MIN_GENESIS_TIME_FILE))
.map_err(|e| format!("Unable to open {}: {:?}", MIN_GENESIS_TIME_FILE, e))
.and_then(|file| {
serde_json::from_reader(file)
.map_err(|e| format!("Unable to parse {}: {:?}", MIN_GENESIS_TIME_FILE, e))
})?;
Ok(Self { Ok(Self {
deposit_contract_address, deposit_contract_address,
deposit_contract_deploy_block, deposit_contract_deploy_block,
min_genesis_time, min_genesis_time,
boot_nodes,
}) })
} }
@@ -122,6 +118,8 @@ mod tests {
deposit_contract_address.clone(), deposit_contract_address.clone(),
deposit_contract_deploy_block, deposit_contract_deploy_block,
min_genesis_time, min_genesis_time,
// TODO: add some Enr for testing.
vec![],
) )
.expect("should create struct"); .expect("should create struct");

View File

@@ -57,7 +57,7 @@ fn main() {
.short("d") .short("d")
.value_name("DIR") .value_name("DIR")
.global(true) .global(true)
.help("Data directory for keys and databases.") .help("Data directory for lighthouse keys and databases.")
.takes_value(true), .takes_value(true),
) )
.subcommand(beacon_node::cli_app()) .subcommand(beacon_node::cli_app())