use client::{ error, notifier, BeaconChainTypes, Client, ClientConfig, ClientType, Eth2Config, InitialiseBeaconChain, }; use futures::sync::oneshot; use futures::Future; use slog::{error, info, warn}; use std::cell::RefCell; use std::path::Path; use std::path::PathBuf; use store::{DiskStore, MemoryStore}; use tokio::runtime::Builder; use tokio::runtime::Runtime; use tokio::runtime::TaskExecutor; use tokio_timer::clock::Clock; use types::{InteropEthSpec, MainnetEthSpec, MinimalEthSpec}; /// Reads the configuration and initializes a `BeaconChain` with the required types and parameters. /// /// Spawns an executor which performs syncing, networking, block production, etc. /// /// Blocks the current thread, returning after the `BeaconChain` has exited or a `Ctrl+C` /// signal. pub fn run_beacon_node( client_config: ClientConfig, eth2_config: Eth2Config, log: &slog::Logger, ) -> error::Result<()> { let runtime = Builder::new() .name_prefix("main-") .clock(Clock::system()) .build() .map_err(|e| format!("{:?}", e))?; let executor = runtime.executor(); let db_path: PathBuf = client_config .db_path() .ok_or_else::(|| "Unable to access database path".into())?; let db_type = &client_config.db_type; let spec_constants = eth2_config.spec_constants.clone(); let other_client_config = client_config.clone(); warn!( log, "Ethereum 2.0 is pre-release. This software is experimental." ); info!( log, "BeaconNode init"; "p2p_listen_address" => format!("{:?}", &other_client_config.network.listen_address), "data_dir" => format!("{:?}", other_client_config.data_dir()), "network_dir" => format!("{:?}", other_client_config.network.network_dir), "spec_constants" => &spec_constants, "db_type" => &other_client_config.db_type, ); match (db_type.as_str(), spec_constants.as_str()) { ("disk", "minimal") => run::>( &db_path, client_config, eth2_config, executor, runtime, log, ), ("memory", "minimal") => run::>( &db_path, client_config, eth2_config, executor, runtime, log, ), ("disk", "mainnet") => run::>( &db_path, client_config, eth2_config, executor, runtime, log, ), ("memory", "mainnet") => run::>( &db_path, client_config, eth2_config, executor, runtime, log, ), ("disk", "interop") => run::>( &db_path, client_config, eth2_config, executor, runtime, log, ), ("memory", "interop") => run::>( &db_path, client_config, eth2_config, executor, runtime, log, ), (db_type, spec) => { error!(log, "Unknown runtime configuration"; "spec_constants" => spec, "db_type" => db_type); Err("Unknown specification and/or db_type.".into()) } } } /// Performs the type-generic parts of launching a `BeaconChain`. fn run( db_path: &Path, client_config: ClientConfig, eth2_config: Eth2Config, executor: TaskExecutor, mut runtime: Runtime, log: &slog::Logger, ) -> error::Result<()> where T: BeaconChainTypes + InitialiseBeaconChain + Clone + Send + Sync + 'static, T::Store: OpenDatabase, { let store = T::Store::open_database(&db_path)?; let client: Client = Client::new(client_config, eth2_config, store, log.clone(), &executor)?; // run service until ctrl-c let (ctrlc_send, ctrlc_oneshot) = 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 ctrl-c message"); } }) .map_err(|e| format!("Could not set ctrlc handler: {:?}", e))?; let (exit_signal, exit) = exit_future::signal(); notifier::run(&client, executor, exit); runtime .block_on(ctrlc_oneshot) .map_err(|e| format!("Ctrlc oneshot failed: {:?}", e))?; // perform global shutdown operations. info!(log, "Shutting down.."); exit_signal.fire(); // shutdown the client // client.exit_signal.fire(); drop(client); runtime.shutdown_on_idle().wait().unwrap(); 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; } impl OpenDatabase for MemoryStore { fn open_database(_path: &Path) -> error::Result { Ok(MemoryStore::open()) } } impl OpenDatabase for DiskStore { fn open_database(path: &Path) -> error::Result { DiskStore::open(path).map_err(|e| format!("Unable to open database: {:?}", e).into()) } }