Refactor ClientConfig, add serde to it

This commit is contained in:
Paul Hauner 2019-06-07 19:44:27 -04:00
parent 39ec96ad82
commit e73a31c37f
No known key found for this signature in database
GPG Key ID: 303E4494BB28068C
15 changed files with 223 additions and 229 deletions

View File

@ -8,6 +8,7 @@ edition = "2018"
types = { path = "../eth2/types" } types = { path = "../eth2/types" }
store = { path = "./store" } store = { path = "./store" }
client = { path = "client" } client = { path = "client" }
fork_choice = { path = "../eth2/fork_choice" }
version = { path = "version" } version = { path = "version" }
clap = "2.32.0" clap = "2.32.0"
slog = { version = "^2.2.3" , features = ["max_level_trace", "release_max_level_debug"] } slog = { version = "^2.2.3" , features = ["max_level_trace", "release_max_level_debug"] }

View File

@ -15,6 +15,8 @@ prometheus = "^0.6"
types = { path = "../../eth2/types" } types = { path = "../../eth2/types" }
tree_hash = { path = "../../eth2/utils/tree_hash" } tree_hash = { path = "../../eth2/utils/tree_hash" }
slot_clock = { path = "../../eth2/utils/slot_clock" } slot_clock = { path = "../../eth2/utils/slot_clock" }
serde = "1.0"
serde_derive = "1.0"
error-chain = "0.12.0" error-chain = "0.12.0"
slog = "^2.2.3" slog = "^2.2.3"
ssz = { path = "../../eth2/utils/ssz" } ssz = { path = "../../eth2/utils/ssz" }

View File

@ -18,42 +18,32 @@ const TESTNET_VALIDATOR_COUNT: usize = 16;
/// Provides a new, initialized `BeaconChain` /// Provides a new, initialized `BeaconChain`
pub trait InitialiseBeaconChain<T: BeaconChainTypes> { pub trait InitialiseBeaconChain<T: BeaconChainTypes> {
fn initialise_beacon_chain(store: Arc<T::Store>, log: Logger) -> BeaconChain<T>; fn initialise_beacon_chain(store: Arc<T::Store>, log: Logger) -> BeaconChain<T> {
maybe_load_from_store_for_testnet::<_, T::Store, T::EthSpec>(store, log)
}
} }
/// A testnet-suitable BeaconChainType, using `MemoryStore`. /// A testnet-suitable BeaconChainType, using `MemoryStore`.
#[derive(Clone)] #[derive(Clone)]
pub struct TestnetMemoryBeaconChainTypes; pub struct TestnetMemoryBeaconChainTypes;
impl BeaconChainTypes for TestnetMemoryBeaconChainTypes { impl BeaconChainTypes for TestnetMemoryBeaconChainTypes {
type Store = MemoryStore; type Store = MemoryStore;
type SlotClock = SystemTimeSlotClock; type SlotClock = SystemTimeSlotClock;
type ForkChoice = OptimizedLMDGhost<Self::Store, Self::EthSpec>; type ForkChoice = OptimizedLMDGhost<Self::Store, Self::EthSpec>;
type EthSpec = LighthouseTestnetEthSpec; type EthSpec = LighthouseTestnetEthSpec;
} }
impl<T: BeaconChainTypes> InitialiseBeaconChain<T> for TestnetMemoryBeaconChainTypes {}
impl<T: BeaconChainTypes> InitialiseBeaconChain<T> for TestnetMemoryBeaconChainTypes {
fn initialise_beacon_chain(store: Arc<T::Store>, log: Logger) -> BeaconChain<T> {
maybe_load_from_store_for_testnet::<_, T::Store, T::EthSpec>(store, log)
}
}
/// A testnet-suitable BeaconChainType, using `DiskStore`. /// A testnet-suitable BeaconChainType, using `DiskStore`.
#[derive(Clone)] #[derive(Clone)]
pub struct TestnetDiskBeaconChainTypes; pub struct TestnetDiskBeaconChainTypes;
impl BeaconChainTypes for TestnetDiskBeaconChainTypes { impl BeaconChainTypes for TestnetDiskBeaconChainTypes {
type Store = DiskStore; type Store = DiskStore;
type SlotClock = SystemTimeSlotClock; type SlotClock = SystemTimeSlotClock;
type ForkChoice = OptimizedLMDGhost<Self::Store, Self::EthSpec>; type ForkChoice = OptimizedLMDGhost<Self::Store, Self::EthSpec>;
type EthSpec = LighthouseTestnetEthSpec; type EthSpec = LighthouseTestnetEthSpec;
} }
impl<T: BeaconChainTypes> InitialiseBeaconChain<T> for TestnetDiskBeaconChainTypes {}
impl<T: BeaconChainTypes> InitialiseBeaconChain<T> for TestnetDiskBeaconChainTypes {
fn initialise_beacon_chain(store: Arc<T::Store>, log: Logger) -> BeaconChain<T> {
maybe_load_from_store_for_testnet::<_, T::Store, T::EthSpec>(store, log)
}
}
/// Loads a `BeaconChain` from `store`, if it exists. Otherwise, create a new chain from genesis. /// Loads a `BeaconChain` from `store`, if it exists. Otherwise, create a new chain from genesis.
fn maybe_load_from_store_for_testnet<T, U: Store, V: EthSpec>( fn maybe_load_from_store_for_testnet<T, U: Store, V: EthSpec>(

View File

@ -1,164 +1,69 @@
use clap::ArgMatches; use clap::ArgMatches;
use fork_choice::ForkChoiceAlgorithm;
use http_server::HttpServerConfig; use http_server::HttpServerConfig;
use network::NetworkConfig; use network::NetworkConfig;
use slog::error; use serde_derive::{Deserialize, Serialize};
use std::fs; use std::fs;
use std::net::SocketAddr;
use std::net::{IpAddr, Ipv4Addr};
use std::path::PathBuf; use std::path::PathBuf;
use types::multiaddr::Protocol;
use types::multiaddr::ToMultiaddr;
use types::Multiaddr;
use types::{ChainSpec, EthSpec, LighthouseTestnetEthSpec};
#[derive(Debug, Clone)] /// The core configuration of a Lighthouse beacon node.
pub enum DBType { #[derive(Debug, Clone, Serialize, Deserialize)]
Memory,
Disk,
}
/// Stores the client configuration for this Lighthouse instance.
#[derive(Debug, Clone)]
pub struct ClientConfig { pub struct ClientConfig {
pub data_dir: PathBuf, data_dir: String,
pub spec: ChainSpec, pub spec: String,
pub net_conf: network::NetworkConfig, pub db_type: String,
pub fork_choice: ForkChoiceAlgorithm, db_name: String,
pub db_type: DBType, pub network: network::NetworkConfig,
pub db_name: PathBuf, pub rpc: rpc::RPCConfig,
pub rpc_conf: rpc::RPCConfig, pub http: HttpServerConfig, //pub ipc_conf:
pub http_conf: HttpServerConfig, //pub ipc_conf:
} }
impl Default for ClientConfig { impl Default for ClientConfig {
/// Build a new lighthouse configuration from defaults.
fn default() -> Self { fn default() -> Self {
let data_dir = {
let home = dirs::home_dir().expect("Unable to determine home dir.");
home.join(".lighthouse/")
};
fs::create_dir_all(&data_dir)
.unwrap_or_else(|_| panic!("Unable to create {:?}", &data_dir));
let default_spec = LighthouseTestnetEthSpec::spec();
let default_net_conf = NetworkConfig::new(default_spec.boot_nodes.clone());
Self { Self {
data_dir: data_dir.clone(), data_dir: ".lighthouse".to_string(),
// default to foundation for chain specs spec: "testnet".to_string(),
spec: default_spec, db_type: "disk".to_string(),
net_conf: default_net_conf, db_name: "chain_db".to_string(),
// default to bitwise LMD Ghost // Note: there are no default bootnodes specified.
fork_choice: ForkChoiceAlgorithm::BitwiseLMDGhost, // Once bootnodes are established, add them here.
// default to memory db for now network: NetworkConfig::new(vec![]),
db_type: DBType::Memory, rpc: rpc::RPCConfig::default(),
// default db name for disk-based dbs http: HttpServerConfig::default(),
db_name: data_dir.join("chain_db"),
rpc_conf: rpc::RPCConfig::default(),
http_conf: HttpServerConfig::default(),
} }
} }
} }
impl ClientConfig { impl ClientConfig {
/// Parses the CLI arguments into a `Config` struct. /// Returns the path to which the client may initialize an on-disk database.
pub fn parse_args(args: ArgMatches, log: &slog::Logger) -> Result<Self, &'static str> { pub fn db_path(&self) -> Option<PathBuf> {
let mut config = ClientConfig::default(); self.data_dir()
.and_then(|path| Some(path.join(&self.db_name)))
}
/* Network related arguments */ /// Returns the core path for the client.
pub fn data_dir(&self) -> Option<PathBuf> {
let path = dirs::home_dir()?.join(&self.data_dir);
fs::create_dir_all(&path).ok()?;
Some(path)
}
// Custom p2p listen port /// Apply the following arguments to `self`, replacing values if they are specified in `args`.
if let Some(port_str) = args.value_of("port") { ///
if let Ok(port) = port_str.parse::<u16>() { /// Returns an error if arguments are obviously invalid. May succeed even if some values are
config.net_conf.listen_port = port; /// invalid.
// update the listening multiaddrs pub fn apply_cli_args(&mut self, args: &ArgMatches) -> Result<(), &'static str> {
for address in &mut config.net_conf.listen_addresses {
address.pop();
address.append(Protocol::Tcp(port));
}
} else {
error!(log, "Invalid port"; "port" => port_str);
return Err("Invalid port");
}
}
// Custom listening address ipv4/ipv6
// TODO: Handle list of addresses
if let Some(listen_address_str) = args.value_of("listen-address") {
if let Ok(listen_address) = listen_address_str.parse::<IpAddr>() {
let multiaddr = SocketAddr::new(listen_address, config.net_conf.listen_port)
.to_multiaddr()
.expect("Invalid listen address format");
config.net_conf.listen_addresses = vec![multiaddr];
} else {
error!(log, "Invalid IP Address"; "Address" => listen_address_str);
return Err("Invalid IP Address");
}
}
// Custom bootnodes
if let Some(boot_addresses_str) = args.value_of("boot-nodes") {
let boot_addresses_split = boot_addresses_str.split(',');
for boot_address in boot_addresses_split {
if let Ok(boot_address) = boot_address.parse::<Multiaddr>() {
config.net_conf.boot_nodes.append(&mut vec![boot_address]);
} else {
error!(log, "Invalid Bootnode multiaddress"; "Multiaddr" => boot_addresses_str);
return Err("Invalid IP Address");
}
}
}
/* Filesystem related arguments */
// Custom datadir
if let Some(dir) = args.value_of("datadir") { if let Some(dir) = args.value_of("datadir") {
config.data_dir = PathBuf::from(dir.to_string()); self.data_dir = dir.to_string();
}; };
/* RPC related arguments */ if let Some(dir) = args.value_of("db") {
self.db_type = dir.to_string();
if args.is_present("rpc") {
config.rpc_conf.enabled = true;
} }
if let Some(rpc_address) = args.value_of("rpc-address") { self.network.apply_cli_args(args)?;
if let Ok(listen_address) = rpc_address.parse::<Ipv4Addr>() { self.rpc.apply_cli_args(args)?;
config.rpc_conf.listen_address = listen_address; self.http.apply_cli_args(args)?;
} else {
error!(log, "Invalid RPC listen address"; "Address" => rpc_address);
return Err("Invalid RPC listen address");
}
}
if let Some(rpc_port) = args.value_of("rpc-port") { Ok(())
if let Ok(port) = rpc_port.parse::<u16>() {
config.rpc_conf.port = port;
} else {
error!(log, "Invalid RPC port"; "port" => rpc_port);
return Err("Invalid RPC port");
}
}
/* HTTP related arguments */
if args.is_present("http") {
config.http_conf.enabled = true;
}
if let Some(listen_address) = args.value_of("http-address") {
config.http_conf.listen_address = listen_address.to_string();
}
if let Some(listen_port) = args.value_of("http-port") {
config.http_conf.listen_port = listen_port.to_string();
}
match args.value_of("db") {
Some("disk") => config.db_type = DBType::Disk,
Some("memory") => config.db_type = DBType::Memory,
_ => unreachable!(), // clap prevents this.
};
Ok(config)
} }
} }

View File

@ -6,5 +6,4 @@ error_chain! {
links { links {
Network(network::error::Error, network::error::ErrorKind); Network(network::error::Error, network::error::ErrorKind);
} }
} }

View File

@ -6,7 +6,6 @@ pub mod error;
pub mod notifier; pub mod notifier;
use beacon_chain::BeaconChain; use beacon_chain::BeaconChain;
use beacon_chain_types::InitialiseBeaconChain;
use exit_future::Signal; use exit_future::Signal;
use futures::{future::Future, Stream}; use futures::{future::Future, Stream};
use network::Service as NetworkService; use network::Service as NetworkService;
@ -18,10 +17,12 @@ use std::sync::Arc;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use tokio::runtime::TaskExecutor; use tokio::runtime::TaskExecutor;
use tokio::timer::Interval; use tokio::timer::Interval;
use types::EthSpec;
pub use beacon_chain::BeaconChainTypes; pub use beacon_chain::BeaconChainTypes;
pub use beacon_chain_types::InitialiseBeaconChain;
pub use beacon_chain_types::{TestnetDiskBeaconChainTypes, TestnetMemoryBeaconChainTypes}; pub use beacon_chain_types::{TestnetDiskBeaconChainTypes, TestnetMemoryBeaconChainTypes};
pub use client_config::{ClientConfig, DBType}; pub use client_config::ClientConfig;
/// Main beacon node client service. This provides the connection and initialisation of the clients /// Main beacon node client service. This provides the connection and initialisation of the clients
/// sub-services in multiple threads. /// sub-services in multiple threads.
@ -57,6 +58,7 @@ where
) -> error::Result<Self> { ) -> error::Result<Self> {
let metrics_registry = Registry::new(); let metrics_registry = Registry::new();
let store = Arc::new(store); let store = Arc::new(store);
let spec = T::EthSpec::spec();
// Load a `BeaconChain` from the store, or create a new one if it does not exist. // Load a `BeaconChain` from the store, or create a new one if it does not exist.
let beacon_chain = Arc::new(T::initialise_beacon_chain(store, log.clone())); let beacon_chain = Arc::new(T::initialise_beacon_chain(store, log.clone()));
@ -97,7 +99,7 @@ where
// Start the network service, libp2p and syncing threads // Start the network service, libp2p and syncing threads
// TODO: Add beacon_chain reference to network parameters // TODO: Add beacon_chain reference to network parameters
let network_config = &config.net_conf; let network_config = &config.network;
let network_logger = log.new(o!("Service" => "Network")); let network_logger = log.new(o!("Service" => "Network"));
let (network, network_send) = NetworkService::new( let (network, network_send) = NetworkService::new(
beacon_chain.clone(), beacon_chain.clone(),
@ -107,9 +109,9 @@ where
)?; )?;
// spawn the RPC server // spawn the RPC server
let rpc_exit_signal = if config.rpc_conf.enabled { let rpc_exit_signal = if config.rpc.enabled {
Some(rpc::start_server( Some(rpc::start_server(
&config.rpc_conf, &config.rpc,
executor, executor,
network_send.clone(), network_send.clone(),
beacon_chain.clone(), beacon_chain.clone(),
@ -122,13 +124,13 @@ where
// Start the `http_server` service. // Start the `http_server` service.
// //
// Note: presently we are ignoring the config and _always_ starting a HTTP server. // Note: presently we are ignoring the config and _always_ starting a HTTP server.
let http_exit_signal = if config.http_conf.enabled { let http_exit_signal = if config.http.enabled {
Some(http_server::start_service( Some(http_server::start_service(
&config.http_conf, &config.http,
executor, executor,
network_send, network_send,
beacon_chain.clone(), beacon_chain.clone(),
config.db_name.clone(), config.db_path().expect("unable to read datadir"),
metrics_registry, metrics_registry,
&log, &log,
)) ))
@ -141,7 +143,7 @@ where
// set up the validator work interval - start at next slot and proceed every slot // set up the validator work interval - start at next slot and proceed every slot
let interval = { let interval = {
// Set the interval to start at the next slot, and every slot after // Set the interval to start at the next slot, and every slot after
let slot_duration = Duration::from_secs(config.spec.seconds_per_slot); let slot_duration = Duration::from_secs(spec.seconds_per_slot);
//TODO: Handle checked add correctly //TODO: Handle checked add correctly
Interval::new(Instant::now() + duration_to_next_slot, slot_duration) Interval::new(Instant::now() + duration_to_next_slot, slot_duration)
}; };

View File

@ -6,9 +6,12 @@ edition = "2018"
[dependencies] [dependencies]
beacon_chain = { path = "../beacon_chain" } beacon_chain = { path = "../beacon_chain" }
clap = "2.32.0"
# SigP repository until PR is merged # SigP repository until PR is merged
libp2p = { git = "https://github.com/SigP/rust-libp2p", rev = "b3c32d9a821ae6cc89079499cc6e8a6bab0bffc3" } libp2p = { git = "https://github.com/SigP/rust-libp2p", rev = "b3c32d9a821ae6cc89079499cc6e8a6bab0bffc3" }
types = { path = "../../eth2/types" } types = { path = "../../eth2/types" }
serde = "1.0"
serde_derive = "1.0"
ssz = { path = "../../eth2/utils/ssz" } ssz = { path = "../../eth2/utils/ssz" }
ssz_derive = { path = "../../eth2/utils/ssz_derive" } ssz_derive = { path = "../../eth2/utils/ssz_derive" }
slog = "2.4.1" slog = "2.4.1"

View File

@ -1,20 +1,22 @@
use crate::Multiaddr; use clap::ArgMatches;
use libp2p::gossipsub::{GossipsubConfig, GossipsubConfigBuilder}; use libp2p::gossipsub::{GossipsubConfig, GossipsubConfigBuilder};
use serde_derive::{Deserialize, Serialize};
use types::multiaddr::{Error as MultiaddrError, Multiaddr};
#[derive(Clone, Debug)] #[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(default)]
/// Network configuration for lighthouse. /// Network configuration for lighthouse.
pub struct Config { pub struct Config {
//TODO: stubbing networking initial params, change in the future
/// IP address to listen on. /// IP address to listen on.
pub listen_addresses: Vec<Multiaddr>, listen_addresses: Vec<String>,
/// Listen port UDP/TCP.
pub listen_port: u16,
/// Gossipsub configuration parameters. /// Gossipsub configuration parameters.
#[serde(skip)]
pub gs_config: GossipsubConfig, pub gs_config: GossipsubConfig,
/// Configuration parameters for node identification protocol. /// Configuration parameters for node identification protocol.
#[serde(skip)]
pub identify_config: IdentifyConfig, pub identify_config: IdentifyConfig,
/// List of nodes to initially connect to. /// List of nodes to initially connect to.
pub boot_nodes: Vec<Multiaddr>, boot_nodes: Vec<String>,
/// Client version /// Client version
pub client_version: String, pub client_version: String,
/// List of topics to subscribe to as strings /// List of topics to subscribe to as strings
@ -25,15 +27,12 @@ impl Default for Config {
/// Generate a default network configuration. /// Generate a default network configuration.
fn default() -> Self { fn default() -> Self {
Config { Config {
listen_addresses: vec!["/ip4/127.0.0.1/tcp/9000" listen_addresses: vec!["/ip4/127.0.0.1/tcp/9000".to_string()],
.parse()
.expect("is a correct multi-address")],
listen_port: 9000,
gs_config: GossipsubConfigBuilder::new() gs_config: GossipsubConfigBuilder::new()
.max_gossip_size(4_000_000) .max_gossip_size(4_000_000)
.build(), .build(),
identify_config: IdentifyConfig::default(), identify_config: IdentifyConfig::default(),
boot_nodes: Vec::new(), boot_nodes: vec![],
client_version: version::version(), client_version: version::version(),
topics: vec![String::from("beacon_chain")], topics: vec![String::from("beacon_chain")],
} }
@ -41,12 +40,34 @@ impl Default for Config {
} }
impl Config { impl Config {
pub fn new(boot_nodes: Vec<Multiaddr>) -> Self { pub fn new(boot_nodes: Vec<String>) -> Self {
let mut conf = Config::default(); let mut conf = Config::default();
conf.boot_nodes = boot_nodes; conf.boot_nodes = boot_nodes;
conf conf
} }
pub fn listen_addresses(&self) -> Result<Vec<Multiaddr>, MultiaddrError> {
self.listen_addresses.iter().map(|s| s.parse()).collect()
}
pub fn boot_nodes(&self) -> Result<Vec<Multiaddr>, MultiaddrError> {
self.boot_nodes.iter().map(|s| s.parse()).collect()
}
pub fn apply_cli_args(&mut self, args: &ArgMatches) -> Result<(), &'static str> {
if let Some(listen_address_str) = args.value_of("listen-address") {
let listen_addresses = listen_address_str.split(",").map(Into::into).collect();
self.listen_addresses = listen_addresses;
}
if let Some(boot_addresses_str) = args.value_of("boot-nodes") {
let boot_addresses = boot_addresses_str.split(",").map(Into::into).collect();
self.boot_nodes = boot_addresses;
}
Ok(())
}
} }
/// The configuration parameters for the Identify protocol /// The configuration parameters for the Identify protocol

View File

@ -57,7 +57,7 @@ impl Service {
}; };
// listen on all addresses // listen on all addresses
for address in &config.listen_addresses { for address in config.listen_addresses().expect("invalid listen multiaddr") {
match Swarm::listen_on(&mut swarm, address.clone()) { match Swarm::listen_on(&mut swarm, address.clone()) {
Ok(mut listen_addr) => { Ok(mut listen_addr) => {
listen_addr.append(Protocol::P2p(local_peer_id.clone().into())); listen_addr.append(Protocol::P2p(local_peer_id.clone().into()));
@ -68,7 +68,7 @@ impl Service {
} }
// connect to boot nodes - these are currently stored as multiaddrs // connect to boot nodes - these are currently stored as multiaddrs
// Once we have discovery, can set to peerId // Once we have discovery, can set to peerId
for bootnode in config.boot_nodes { for bootnode in config.boot_nodes().expect("invalid boot node multiaddr") {
match Swarm::dial_addr(&mut swarm, bootnode.clone()) { match Swarm::dial_addr(&mut swarm, bootnode.clone()) {
Ok(()) => debug!(log, "Dialing bootnode: {}", bootnode), Ok(()) => debug!(log, "Dialing bootnode: {}", bootnode),
Err(err) => debug!( Err(err) => debug!(

View File

@ -3,17 +3,19 @@ mod key;
mod metrics; mod metrics;
use beacon_chain::{BeaconChain, BeaconChainTypes}; use beacon_chain::{BeaconChain, BeaconChainTypes};
use clap::ArgMatches;
use futures::Future; use futures::Future;
use iron::prelude::*; use iron::prelude::*;
use network::NetworkMessage; use network::NetworkMessage;
use prometheus::Registry; use prometheus::Registry;
use router::Router; use router::Router;
use serde_derive::{Deserialize, Serialize};
use slog::{info, o, warn}; use slog::{info, o, warn};
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
use tokio::runtime::TaskExecutor; use tokio::runtime::TaskExecutor;
#[derive(PartialEq, Clone, Debug)] #[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
pub struct HttpServerConfig { pub struct HttpServerConfig {
pub enabled: bool, pub enabled: bool,
pub listen_address: String, pub listen_address: String,
@ -30,6 +32,24 @@ impl Default for HttpServerConfig {
} }
} }
impl HttpServerConfig {
pub fn apply_cli_args(&mut self, args: &ArgMatches) -> Result<(), &'static str> {
if args.is_present("http") {
self.enabled = true;
}
if let Some(listen_address) = args.value_of("http-address") {
self.listen_address = listen_address.to_string();
}
if let Some(listen_port) = args.value_of("http-port") {
self.listen_port = listen_port.to_string();
}
Ok(())
}
}
/// Build the `iron` HTTP server, defining the core routes. /// Build the `iron` HTTP server, defining the core routes.
pub fn create_iron_http_server<T: BeaconChainTypes + 'static>( pub fn create_iron_http_server<T: BeaconChainTypes + 'static>(
beacon_chain: Arc<BeaconChain<T>>, beacon_chain: Arc<BeaconChain<T>>,

View File

@ -20,6 +20,8 @@ clap = "2.32.0"
store = { path = "../store" } store = { path = "../store" }
dirs = "1.0.3" dirs = "1.0.3"
futures = "0.1.23" futures = "0.1.23"
serde = "1.0"
serde_derive = "1.0"
slog = "^2.2.3" slog = "^2.2.3"
slog-term = "^2.4.0" slog-term = "^2.4.0"
slog-async = "^2.3.0" slog-async = "^2.3.0"

View File

@ -1,7 +1,9 @@
use clap::ArgMatches;
use serde_derive::{Deserialize, Serialize};
use std::net::Ipv4Addr; use std::net::Ipv4Addr;
/// RPC Configuration /// RPC Configuration
#[derive(Debug, Clone)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config { pub struct Config {
/// Enable the RPC server. /// Enable the RPC server.
pub enabled: bool, pub enabled: bool,
@ -20,3 +22,23 @@ impl Default for Config {
} }
} }
} }
impl Config {
pub fn apply_cli_args(&mut self, args: &ArgMatches) -> Result<(), &'static str> {
if args.is_present("rpc") {
self.enabled = true;
}
if let Some(rpc_address) = args.value_of("rpc-address") {
self.listen_address = rpc_address
.parse::<Ipv4Addr>()
.map_err(|_| "rpc-address is not IPv4 address")?;
}
if let Some(rpc_port) = args.value_of("rpc-port") {
self.port = rpc_port.parse::<u16>().map_err(|_| "rpc-port is not u16")?;
}
Ok(())
}
}

View File

@ -4,7 +4,7 @@ mod run;
use clap::{App, Arg}; use clap::{App, Arg};
use client::ClientConfig; use client::ClientConfig;
use slog::{error, o, Drain}; use slog::{crit, o, Drain};
fn main() { fn main() {
let decorator = slog_term::TermDecorator::new().build(); let decorator = slog_term::TermDecorator::new().build();
@ -29,21 +29,14 @@ fn main() {
Arg::with_name("listen-address") Arg::with_name("listen-address")
.long("listen-address") .long("listen-address")
.value_name("Listen Address") .value_name("Listen Address")
.help("The Network address to listen for p2p connections.") .help("One or more comma-delimited multi-addresses to listen for p2p connections.")
.takes_value(true),
)
.arg(
Arg::with_name("port")
.long("port")
.value_name("PORT")
.help("Network listen port for p2p connections.")
.takes_value(true), .takes_value(true),
) )
.arg( .arg(
Arg::with_name("boot-nodes") Arg::with_name("boot-nodes")
.long("boot-nodes") .long("boot-nodes")
.value_name("BOOTNODES") .value_name("BOOTNODES")
.help("A list of comma separated multi addresses representing bootnodes to connect to.") .help("One or more comma-delimited multi-addresses to bootstrap the p2p network.")
.takes_value(true), .takes_value(true),
) )
// rpc related arguments // rpc related arguments
@ -101,11 +94,18 @@ fn main() {
) )
.get_matches(); .get_matches();
// invalid arguments, panic let mut config = ClientConfig::default();
let config = ClientConfig::parse_args(matches, &logger).unwrap();
match config.apply_cli_args(&matches) {
Ok(()) => (),
Err(s) => {
crit!(logger, "Failed to parse CLI arguments"; "error" => s);
return;
}
};
match run::run_beacon_node(config, &logger) { match run::run_beacon_node(config, &logger) {
Ok(_) => {} Ok(_) => {}
Err(e) => error!(logger, "Beacon node failed because {:?}", e), Err(e) => crit!(logger, "Beacon node failed to start"; "reason" => format!("{:}", e)),
} }
} }

View File

@ -1,11 +1,13 @@
use client::{ use client::{
error, notifier, BeaconChainTypes, Client, ClientConfig, DBType, TestnetDiskBeaconChainTypes, error, notifier, BeaconChainTypes, Client, ClientConfig, InitialiseBeaconChain,
TestnetMemoryBeaconChainTypes, TestnetDiskBeaconChainTypes, TestnetMemoryBeaconChainTypes,
}; };
use futures::sync::oneshot; use futures::sync::oneshot;
use futures::Future; use futures::Future;
use slog::info; use slog::{error, info};
use std::cell::RefCell; use std::cell::RefCell;
use std::path::Path;
use std::path::PathBuf;
use store::{DiskStore, MemoryStore}; use store::{DiskStore, MemoryStore};
use tokio::runtime::Builder; use tokio::runtime::Builder;
use tokio::runtime::Runtime; use tokio::runtime::Runtime;
@ -19,51 +21,58 @@ pub fn run_beacon_node(config: ClientConfig, log: &slog::Logger) -> error::Resul
.build() .build()
.map_err(|e| format!("{:?}", e))?; .map_err(|e| format!("{:?}", e))?;
// Log configuration
info!(log, "Listening on {:?}", &config.net_conf.listen_addresses;
"data_dir" => &config.data_dir.to_str(),
"port" => &config.net_conf.listen_port);
let executor = runtime.executor(); let executor = runtime.executor();
match config.db_type { let db_path: PathBuf = config
DBType::Disk => { .db_path()
info!( .ok_or_else::<error::Error, _>(|| "Unable to access database path".into())?;
log, let db_type = &config.db_type;
"BeaconNode starting"; let spec = &config.spec;
"type" => "TestnetDiskBeaconChainTypes"
);
let store = DiskStore::open(&config.db_name).expect("Unable to open DB."); let other_config = config.clone();
let client: Client<TestnetDiskBeaconChainTypes> = let result = match (db_type.as_str(), spec.as_str()) {
Client::new(config, store, log.clone(), &executor)?; ("disk", "testnet") => {
run::<TestnetDiskBeaconChainTypes>(&db_path, config, executor, runtime, log)
run(client, executor, runtime, log)
} }
DBType::Memory => { ("memory", "testnet") => {
info!( run::<TestnetMemoryBeaconChainTypes>(&db_path, config, executor, runtime, log)
log,
"BeaconNode starting";
"type" => "TestnetMemoryBeaconChainTypes"
);
let store = MemoryStore::open();
let client: Client<TestnetMemoryBeaconChainTypes> =
Client::new(config, store, log.clone(), &executor)?;
run(client, executor, runtime, log)
} }
(db_type, spec) => {
error!(log, "Unknown runtime configuration"; "spec" => spec, "db_type" => db_type);
Err("Unknown specification and/or db_type.".into())
}
};
if result.is_ok() {
info!(
log,
"Started beacon node";
"p2p_listen_addresses" => format!("{:?}", &other_config.network.listen_addresses()),
"data_dir" => format!("{:?}", other_config.data_dir()),
"spec" => &other_config.spec,
"db_type" => &other_config.db_type,
);
} }
result
} }
pub fn run<T: BeaconChainTypes + Send + Sync + 'static>( pub fn run<T>(
client: Client<T>, db_path: &Path,
config: ClientConfig,
executor: TaskExecutor, executor: TaskExecutor,
mut runtime: Runtime, mut runtime: Runtime,
log: &slog::Logger, log: &slog::Logger,
) -> error::Result<()> { ) -> error::Result<()>
where
T: BeaconChainTypes + InitialiseBeaconChain<T> + Send + Sync + 'static + Clone,
T::Store: OpenDatabase,
{
let store = T::Store::open_database(&db_path)?;
let client: Client<T> = Client::new(config, store, log.clone(), &executor)?;
// run service until ctrl-c // run service until ctrl-c
let (ctrlc_send, ctrlc_oneshot) = oneshot::channel(); let (ctrlc_send, ctrlc_oneshot) = oneshot::channel();
let ctrlc_send_c = RefCell::new(Some(ctrlc_send)); let ctrlc_send_c = RefCell::new(Some(ctrlc_send));
@ -91,3 +100,22 @@ pub fn run<T: BeaconChainTypes + Send + Sync + 'static>(
runtime.shutdown_on_idle().wait().unwrap(); runtime.shutdown_on_idle().wait().unwrap();
Ok(()) Ok(())
} }
/// A convenience trait, providing a method to open a database.
///
/// Panics if unable to open the database.
pub trait OpenDatabase: Sized {
fn open_database(path: &Path) -> error::Result<Self>;
}
impl OpenDatabase for MemoryStore {
fn open_database(_path: &Path) -> error::Result<Self> {
Ok(MemoryStore::open())
}
}
impl OpenDatabase for DiskStore {
fn open_database(path: &Path) -> error::Result<Self> {
DiskStore::open(path).map_err(|e| format!("Unable to open database: {:?}", e).into())
}
}

View File

@ -1,6 +1,5 @@
extern crate rocksdb; extern crate rocksdb;
// use super::stores::COLUMNS;
use super::{ClientDB, DBError, DBValue}; use super::{ClientDB, DBError, DBValue};
use rocksdb::Error as RocksError; use rocksdb::Error as RocksError;
use rocksdb::{Options, DB}; use rocksdb::{Options, DB};