diff --git a/Cargo.toml b/Cargo.toml index 42d69489b..1090a9b6f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,9 @@ members = [ "eth2/utils/test_random_derive", "beacon_node", "beacon_node/db", + "beacon_node/network", + "beacon_node/sync", + "beacon_node/version", "beacon_node/beacon_chain", "beacon_node/beacon_chain/test_harness", "protos", diff --git a/beacon_node/Cargo.toml b/beacon_node/Cargo.toml index a4804e07e..f909d5103 100644 --- a/beacon_node/Cargo.toml +++ b/beacon_node/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "beacon_node" version = "0.1.0" -authors = ["Paul Hauner "] +authors = ["Paul Hauner ", "Age Manning BeaconBlockStore { } } - /// Retuns an object implementing `BeaconBlockReader`, or `None` (if hash not known). + /// Returns an object implementing `BeaconBlockReader`, or `None` (if hash not known). /// /// Note: Presently, this function fully deserializes a `BeaconBlock` and returns that. In the /// future, it would be ideal to return an object capable of reading directly from serialized diff --git a/beacon_node/network/Cargo.toml b/beacon_node/network/Cargo.toml new file mode 100644 index 000000000..57f75e273 --- /dev/null +++ b/beacon_node/network/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "network" +version = "0.1.0" +authors = ["Age Manning "] +edition = "2018" + +[dependencies] +# SigP repository until PR is merged +libp2p = { git = "https://github.com/SigP/rust-libp2p", branch = "gossipsub" } +version = { path = "../version" } diff --git a/beacon_node/network/src/lib.rs b/beacon_node/network/src/lib.rs new file mode 100644 index 000000000..1dc56ec4f --- /dev/null +++ b/beacon_node/network/src/lib.rs @@ -0,0 +1,4 @@ +/// This crate provides the network server for Lighthouse. +mod network_configuration; + +pub use network_configuration::NetworkConfiguration; diff --git a/beacon_node/network/src/network_configuration.rs b/beacon_node/network/src/network_configuration.rs new file mode 100644 index 000000000..64d763287 --- /dev/null +++ b/beacon_node/network/src/network_configuration.rs @@ -0,0 +1,39 @@ +use libp2p::gossipsub::{GossipsubConfig, GossipsubConfigBuilder}; +use std::net::IpAddr; +use version; + +#[derive(Debug, Clone)] +/// Network configuration for lighthouse. +pub struct NetworkConfiguration { + //TODO: stubbing networking initial params, change in the future + /// IP address to listen on. + pub listen_address: Option, + /// Listen port UDP/TCP. + pub listen_port: Option, + /// Gossipsub configuration parameters. + pub gs_config: GossipsubConfig, + /// List of nodes to initially connect to. + pub boot_nodes: Vec, + /// Client version + pub client_version: String, + //TODO: more to be added +} + +impl Default for NetworkConfiguration { + /// Generate a default network configuration. + fn default() -> Self { + NetworkConfiguration { + listen_address: None, + listen_port: None, + gs_config: GossipsubConfigBuilder::new().build(), + boot_nodes: Vec::new(), + client_version: version::version(), + } + } +} + +impl NetworkConfiguration { + pub fn new() -> Self { + NetworkConfiguration::default() + } +} diff --git a/beacon_node/src/config.rs b/beacon_node/src/config.rs new file mode 100644 index 000000000..c7fa57909 --- /dev/null +++ b/beacon_node/src/config.rs @@ -0,0 +1,85 @@ +use clap::ArgMatches; +use db::DBType; +use fork_choice::ForkChoiceAlgorithm; +use network::NetworkConfiguration; +use slog::error; +use std::fs; +use std::net::IpAddr; +use std::path::PathBuf; +use types::ChainSpec; + +/// Stores the core configuration for this Lighthouse instance. +/// This struct is general, other components may implement more +/// specialized configuration structs. +#[derive(Debug, Clone)] +pub struct Config { + pub data_dir: PathBuf, + pub spec: ChainSpec, + pub net_conf: network::NetworkConfiguration, + pub fork_choice: ForkChoiceAlgorithm, + pub db_type: DBType, + pub db_name: PathBuf, + //pub rpc_conf: + //pub ipc_conf: +} + +impl Default for Config { + /// Build a new lighthouse configuration from defaults. + 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)); + Self { + data_dir: data_dir.clone(), + // default to foundation for chain specs + spec: ChainSpec::foundation(), + net_conf: NetworkConfiguration::default(), + // default to bitwise LMD Ghost + fork_choice: ForkChoiceAlgorithm::BitwiseLMDGhost, + // default to memory db for now + db_type: DBType::Memory, + // default db name for disk-based dbs + db_name: data_dir.join("chain.db"), + } + } +} + +impl Config { + /// Parses the CLI arguments into a `Config` struct. + pub fn parse_args(args: ArgMatches, log: &slog::Logger) -> Result { + let mut config = Config::default(); + + // Network related args + + // Custom listening address ipv4/ipv6 + if let Some(listen_address_str) = args.value_of("listen_address") { + if let Ok(listen_address) = listen_address_str.parse::() { + config.net_conf.listen_address = Some(listen_address); + } else { + error!(log, "Invalid Ip Address"; "Address" => listen_address_str); + return Err("Invalid Ip Address"); + } + } + // Custom p2p listen port + if let Some(port_str) = args.value_of("port") { + if let Ok(port) = port_str.parse::() { + config.net_conf.listen_port = Some(port); + } else { + error!(log, "Invalid port"; "port" => port_str); + return Err("Invalid port"); + } + } + + // filesystem args + + // Custom datadir + if let Some(dir) = args.value_of("datadir") { + config.data_dir = PathBuf::from(dir.to_string()); + }; + + Ok(config) + } +} diff --git a/beacon_node/src/config/mod.rs b/beacon_node/src/config/mod.rs deleted file mode 100644 index 5c94e300c..000000000 --- a/beacon_node/src/config/mod.rs +++ /dev/null @@ -1,30 +0,0 @@ -use std::fs; -use std::path::PathBuf; - -/// Stores the core configuration for this Lighthouse instance. -/// This struct is general, other components may implement more -/// specialized config structs. -#[derive(Clone)] -pub struct LighthouseConfig { - pub data_dir: PathBuf, - pub p2p_listen_port: u16, -} - -const DEFAULT_LIGHTHOUSE_DIR: &str = ".lighthouse"; - -impl LighthouseConfig { - /// Build a new lighthouse configuration from defaults. - pub fn default() -> Self { - let data_dir = { - let home = dirs::home_dir().expect("Unable to determine home dir."); - home.join(DEFAULT_LIGHTHOUSE_DIR) - }; - fs::create_dir_all(&data_dir) - .unwrap_or_else(|_| panic!("Unable to create {:?}", &data_dir)); - let p2p_listen_port = 0; - Self { - data_dir, - p2p_listen_port, - } - } -} diff --git a/beacon_node/src/error.rs b/beacon_node/src/error.rs new file mode 100644 index 000000000..163fe575d --- /dev/null +++ b/beacon_node/src/error.rs @@ -0,0 +1,8 @@ +// generates error types + +use error_chain::{ + error_chain, error_chain_processing, impl_error_chain_kind, impl_error_chain_processed, + impl_extract_backtrace, +}; + +error_chain! {} diff --git a/beacon_node/src/main.rs b/beacon_node/src/main.rs index b9ef2c8a7..ed26a55fe 100644 --- a/beacon_node/src/main.rs +++ b/beacon_node/src/main.rs @@ -1,34 +1,23 @@ extern crate slog; mod config; +mod error; mod rpc; +mod run; -use std::path::PathBuf; - -use crate::config::LighthouseConfig; -use crate::rpc::start_server; -use beacon_chain::BeaconChain; -use bls::create_proof_of_possession; use clap::{App, Arg}; -use db::{ - stores::{BeaconBlockStore, BeaconStateStore}, - MemoryDB, -}; -use fork_choice::BitwiseLMDGhost; -use slog::{error, info, o, Drain}; -use slot_clock::SystemTimeSlotClock; -use std::sync::Arc; -use types::{ChainSpec, Deposit, DepositData, DepositInput, Eth1Data, Hash256, Keypair}; +use config::Config; +use slog::{o, Drain}; fn main() { let decorator = slog_term::TermDecorator::new().build(); let drain = slog_term::CompactFormat::new(decorator).build().fuse(); let drain = slog_async::Async::new(drain).build().fuse(); - let log = slog::Logger::root(drain, o!()); + let logger = slog::Logger::root(drain, o!()); let matches = App::new("Lighthouse") - .version("0.0.1") - .author("Sigma Prime ") + .version(version::version().as_str()) + .author("Sigma Prime ") .about("Eth 2.0 Client") .arg( Arg::with_name("datadir") @@ -37,6 +26,13 @@ fn main() { .help("Data directory for keys and databases.") .takes_value(true), ) + .arg( + Arg::with_name("listen_address") + .long("listen_address") + .value_name("Listen Address") + .help("The Network address to listen for p2p connections.") + .takes_value(true), + ) .arg( Arg::with_name("port") .long("port") @@ -46,89 +42,8 @@ fn main() { ) .get_matches(); - let mut config = LighthouseConfig::default(); + // invalid arguments, panic + let config = Config::parse_args(matches, &logger).unwrap(); - // Custom datadir - if let Some(dir) = matches.value_of("datadir") { - config.data_dir = PathBuf::from(dir.to_string()); - } - - // Custom p2p listen port - if let Some(port_str) = matches.value_of("port") { - if let Ok(port) = port_str.parse::() { - config.p2p_listen_port = port; - } else { - error!(log, "Invalid port"; "port" => port_str); - return; - } - } - - // Log configuration - info!(log, ""; - "data_dir" => &config.data_dir.to_str(), - "port" => &config.p2p_listen_port); - - // Specification (presently fixed to foundation). - let spec = ChainSpec::foundation(); - - // Database (presently in-memory) - let db = Arc::new(MemoryDB::open()); - let block_store = Arc::new(BeaconBlockStore::new(db.clone())); - let state_store = Arc::new(BeaconStateStore::new(db.clone())); - - // Slot clock - let genesis_time = 1_549_935_547; // 12th Feb 2018 (arbitrary value in the past). - let slot_clock = SystemTimeSlotClock::new(genesis_time, spec.slot_duration) - .expect("Unable to load SystemTimeSlotClock"); - // Choose the fork choice - let fork_choice = BitwiseLMDGhost::new(block_store.clone(), state_store.clone()); - - /* - * Generate some random data to start a chain with. - * - * This is will need to be replace for production usage. - */ - let latest_eth1_data = Eth1Data { - deposit_root: Hash256::zero(), - block_hash: Hash256::zero(), - }; - let keypairs: Vec = (0..10) - .collect::>() - .iter() - .map(|_| Keypair::random()) - .collect(); - let initial_validator_deposits = keypairs - .iter() - .map(|keypair| Deposit { - branch: vec![], // branch verification is not specified. - index: 0, // index verification is not specified. - deposit_data: DepositData { - amount: 32_000_000_000, // 32 ETH (in Gwei) - timestamp: genesis_time - 1, - deposit_input: DepositInput { - pubkey: keypair.pk.clone(), - withdrawal_credentials: Hash256::zero(), // Withdrawal not possible. - proof_of_possession: create_proof_of_possession(&keypair), - }, - }, - }) - .collect(); - - // Genesis chain - let _chain_result = BeaconChain::genesis( - state_store.clone(), - block_store.clone(), - slot_clock, - genesis_time, - latest_eth1_data, - initial_validator_deposits, - spec, - fork_choice, - ); - - let _server = start_server(log.clone()); - - loop { - std::thread::sleep(std::time::Duration::from_secs(1)); - } + run::run_beacon_node(config, &logger); } diff --git a/beacon_node/src/run.rs b/beacon_node/src/run.rs new file mode 100644 index 000000000..18c4c3fe0 --- /dev/null +++ b/beacon_node/src/run.rs @@ -0,0 +1,54 @@ +use crate::config::Config; +use crate::error; +use crate::rpc::start_server; +use beacon_chain::BeaconChain; +use bls::create_proof_of_possession; +use db::{ + stores::{BeaconBlockStore, BeaconStateStore}, + ClientDB, DBType, DiskDB, MemoryDB, +}; +use fork_choice::{BitwiseLMDGhost, ForkChoiceAlgorithm}; +use futures::sync::oneshot; +use network::NetworkConfiguration; +use slog::{error, info}; +use slot_clock::SystemTimeSlotClock; +use std::cell::RefCell; +use std::sync::Arc; +use tokio::runtime::{Builder, Runtime, TaskExecutor}; +use types::{ChainSpec, Deposit, DepositData, DepositInput, Eth1Data, Hash256, Keypair}; + +pub fn run_beacon_node(config: Config, log: &slog::Logger) -> error::Result<()> { + let mut runtime = Builder::new() + .name_prefix("main-") + .build() + .map_err(|e| format!("{:?}", e))?; + + // Log configuration + info!(log, ""; + "data_dir" => &config.data_dir.to_str(), + "port" => &config.net_conf.listen_port); + + // run service until ctrl-c + let (ctrlc_send, ctrlc) = oneshot::channel(); + let ctrlc_send_c = RefCell::new(Some(ctrlc_send)); + ctrlc::set_handler(move || { + if let Some(ctrlc_send) = ctrlc_send_c.try_borrow_mut().unwrap().take() { + ctrlc_send + .send(()) + .expect("Error sending termination message"); + } + }); + + let executor = runtime.executor(); + + start(config, log, executor); + + runtime.block_on(ctrlc); + + info!(log, "Shutting down."); + //TODO: handle shutdown of processes gracefully + + Ok(()) +} + +fn start(config: Config, log: &slog::Logger, executor: TaskExecutor) {} diff --git a/beacon_node/sync/Cargo.toml b/beacon_node/sync/Cargo.toml new file mode 100644 index 000000000..347506bf0 --- /dev/null +++ b/beacon_node/sync/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "sync" +version = "0.1.0" +authors = ["Age Manning "] +edition = "2018" + +[dependencies] + diff --git a/beacon_node/sync/src/lib.rs b/beacon_node/sync/src/lib.rs new file mode 100644 index 000000000..f520e9e09 --- /dev/null +++ b/beacon_node/sync/src/lib.rs @@ -0,0 +1,68 @@ +// /// Syncing for lighthouse. + +/* +// for initial testing and setup, to be replaced. +pub fn sync_server(config: Config) { + // Set up database + let db = match config.db_type { + _ => Arc::new(MemoryDB::open()), + //TODO: Box db + //DBType::Memory => Arc::new(Box::new(MemoryDB::open())), + //DBType::RocksDB => Arc::new(Box::new(DiskDB::open(&config.db_name, None))), + }; + + // build block + let block_store = Arc::new(BeaconBlockStore::new(db.clone())); + let state_store = Arc::new(BeaconStateStore::new(db.clone())); + + // Slot clock + let genesis_time = 1_549_935_547; // 12th Feb 2018 (arbitrary value in the past). + let slot_clock = SystemTimeSlotClock::new(genesis_time, spec.slot_duration) + .expect("Unable to load SystemTimeSlotClock"); + // Choose the fork choice + let fork_choice = BitwiseLMDGhost::new(block_store.clone(), state_store.clone()); + + /* + * Generate some random data to start a chain with. + * + * This is will need to be replace for production usage. + */ +let latest_eth1_data = Eth1Data { +deposit_root: Hash256::zero(), +block_hash: Hash256::zero(), +}; +let keypairs: Vec = (0..10) +.collect::>() +.iter() +.map(|_| Keypair::random()) +.collect(); +let initial_validator_deposits = keypairs +.iter() +.map(|keypair| Deposit { +branch: vec![], // branch verification is not specified. +index: 0, // index verification is not specified. +deposit_data: DepositData { +amount: 32_000_000_000, // 32 ETH (in Gwei) +timestamp: genesis_time - 1, +deposit_input: DepositInput { +pubkey: keypair.pk.clone(), +withdrawal_credentials: Hash256::zero(), // Withdrawal not possible. +proof_of_possession: create_proof_of_possession(&keypair), +}, +}, +}) +.collect(); + +// Genesis chain +let _chain_result = BeaconChain::genesis( +state_store.clone(), +block_store.clone(), +slot_clock, +genesis_time, +latest_eth1_data, +initial_validator_deposits, +spec, +fork_choice, +); +} +*/ diff --git a/beacon_node/version/Cargo.toml b/beacon_node/version/Cargo.toml new file mode 100644 index 000000000..0497408f1 --- /dev/null +++ b/beacon_node/version/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "version" +version = "0.1.0" +authors = ["Age Manning "] +edition = "2018" + +[dependencies] +target_info = "0.1.0" diff --git a/beacon_node/version/src/lib.rs b/beacon_node/version/src/lib.rs new file mode 100644 index 000000000..628186aa0 --- /dev/null +++ b/beacon_node/version/src/lib.rs @@ -0,0 +1,25 @@ +//TODO: Build the version and hash of the built lighthouse binary + +/// Version information for the Lighthouse beacon node. +// currently only supports unstable release +extern crate target_info; + +use target_info::Target; + +const TRACK: &'static str = "unstable"; + +/// Provides the current platform +pub fn platform() -> String { + format!("{}-{}", Target::arch(), Target::os()) +} + +/// Version of the beacon node. +// TODO: Find the sha3 hash, date and rust version used to build the beacon_node binary +pub fn version() -> String { + format!( + "Lighthouse/v{}-{}/{}", + env!("CARGO_PKG_VERSION"), + TRACK, + platform() + ) +} diff --git a/eth2/fork_choice/src/lib.rs b/eth2/fork_choice/src/lib.rs index 6062c19b1..318ac99a8 100644 --- a/eth2/fork_choice/src/lib.rs +++ b/eth2/fork_choice/src/lib.rs @@ -94,6 +94,7 @@ impl From for ForkChoiceError { } /// Fork choice options that are currently implemented. +#[derive(Debug, Clone)] pub enum ForkChoiceAlgorithm { /// Chooses the longest chain becomes the head. Not for production. LongestChain,