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" }
genesis = { path = "genesis" }
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 data_dir: PathBuf,
pub db_type: String,
db_name: String,
freezer_db_path: Option<PathBuf>,
pub db_name: String,
pub freezer_db_path: Option<PathBuf>,
pub log_file: PathBuf,
pub spec_constants: String,
/// 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(),
freezer_db_path: None,
genesis: <_>::default(),
network: NetworkConfig::new(),
network: NetworkConfig::default(),
rest_api: <_>::default(),
websocket_server: <_>::default(),
spec_constants: TESTNET_SPEC_CONSTANTS.into(),
@@ -135,26 +135,6 @@ impl Config {
.ok_or_else(|| "Unable to locate user home directory".to_string())?;
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.

View File

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

View File

@@ -1,4 +1,3 @@
use clap::ArgMatches;
use enr::Enr;
use libp2p::gossipsub::{GossipsubConfig, GossipsubConfigBuilder};
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" }
state_processing = { path = "../../eth2/state_processing" }
types = { path = "../../eth2/types" }
clap = "2.33"
http = "0.1"
hyper = "0.12"
exit-future = "0.1.4"

View File

@@ -1,4 +1,3 @@
use clap::ArgMatches;
use serde::{Deserialize, Serialize};
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.
*/
.arg(
Arg::with_name("port-bump")
.long("port-bump")
.short("b")
.value_name("INCREMENT")
.help("Sets all listening TCP/UDP ports to default values, but with each port increased by \
INCREMENT. Useful when starting multiple nodes on a single machine. Using increments \
in multiples of 10 is recommended.")
.takes_value(true),
Arg::with_name("zero-ports")
.long("zero-ports")
.short("z")
.help("Sets all listening TCP/UDP ports to 0, allowing the OS to choose some \
arbitrary free port number.")
.takes_value(false),
)
.arg(
Arg::with_name("listen-address")
.long("listen-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)
)
.arg(
@@ -50,13 +49,14 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.long("port")
.value_name("PORT")
.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),
)
.arg(
Arg::with_name("maxpeers")
.long("maxpeers")
.help("The maximum number of peers (default 10).")
.help("The maximum number of peers.")
.default_value("10")
.takes_value(true),
)
.arg(
@@ -72,64 +72,70 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.long("disc-port")
.value_name("PORT")
.help("The discovery UDP port.")
.conflicts_with("port-bump")
.default_value("9000")
.takes_value(true),
)
.arg(
Arg::with_name("discovery-address")
.long("discovery-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),
)
.arg(
Arg::with_name("topics")
.long("topics")
.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),
)
.arg(
Arg::with_name("libp2p-addresses")
.long("libp2p-addresses")
.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),
)
.arg(
Arg::with_name("p2p-priv-key")
.long("p2p-priv-key")
.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),
)
/* REST API related arguments */
.arg(
Arg::with_name("no-api")
.long("no-api")
.help("Disable RESTful HTTP API server.")
Arg::with_name("http")
.long("http")
.help("Enable RESTful HTTP API server. Disabled by default.")
.takes_value(false),
)
.arg(
Arg::with_name("api-address")
.long("api-address")
Arg::with_name("http-address")
.long("http-address")
.value_name("ADDRESS")
.help("Set the listen address for the RESTful HTTP API server.")
.default_value("127.0.0.1")
.takes_value(true),
)
.arg(
Arg::with_name("api-port")
.long("api-port")
Arg::with_name("http-port")
.long("http-port")
.value_name("PORT")
.help("Set the listen TCP port for the RESTful HTTP API server.")
.conflicts_with("port-bump")
.default_value("5052")
.takes_value(true),
)
/* Websocket related arguments */
.arg(
Arg::with_name("no-ws")
.long("no-ws")
.help("Disable websocket server.")
Arg::with_name("ws")
.long("ws")
.help("Enable the websocket server. Disabled by default.")
.takes_value(false),
)
.arg(
@@ -137,7 +143,7 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.long("ws-address")
.value_name("ADDRESS")
.help("Set the listen address for the websocket server.")
.conflicts_with_all(&["no-ws"])
.default_value("127.0.0.1")
.takes_value(true),
)
.arg(
@@ -145,16 +151,24 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.long("ws-port")
.value_name("PORT")
.help("Set the listen TCP port for the websocket server.")
.conflicts_with_all(&["no-ws", "port-bump"])
.default_value("5053")
.takes_value(true),
)
/*
* 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::with_name("dummy-eth1")
.long("dummy-eth1")
.conflicts_with("eth1")
.help("If present, uses an eth1 backend that generates static dummy data.\
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)
.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.
*
@@ -199,21 +187,6 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
*/
.subcommand(SubCommand::with_name("testnet")
.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::with_name("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 \
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::with_name("force")
.long("force")
@@ -236,33 +202,22 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
backup directory.")
.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::with_name("slot-time")
.long("slot-time")
.short("t")
.value_name("MILLISECONDS")
.help("Defines the slot time when creating a new testnet.")
)
/*
* `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."))
.help("Defines the slot time when creating a new testnet. The default is \
specified by the spec.")
)
/*
* `recent`
@@ -282,7 +237,7 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.short("m")
.value_name("MINUTES")
.required(true)
.default_value("0")
.default_value("30")
.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)
.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`
*
* Connect to the Prysmatic Labs testnet.
*/
.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 {
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
get_configs(&matches, log).into_future().and_then(
move |(client_config, eth2_config, _log)| {
get_configs(&matches, context.eth2_config.clone(), log)
.into_future()
.and_then(move |(client_config, eth2_config, _log)| {
context.eth2_config = eth2_config;
Self::new(context, client_config)
},
)
})
}
/// 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
[dependencies]
clap = "2.33.0"
exit-future = "0.1.4"
futures = "0.1.29"
serde = "1.0.102"

View File

@@ -1,5 +1,4 @@
use clap::ArgMatches;
use serde::{Deserialize, Serialize};
use serde_derive::{Deserialize, Serialize};
use std::net::Ipv4Addr;
/// 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_json = "^1.0"
types = { path = "../../types"}
eth2-libp2p = { path = "../../../beacon_node/eth2-libp2p"}

View File

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

View File

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