diff --git a/beacon_node/beacon_chain/Cargo.toml b/beacon_node/beacon_chain/Cargo.toml index 018ea1976..f6763d167 100644 --- a/beacon_node/beacon_chain/Cargo.toml +++ b/beacon_node/beacon_chain/Cargo.toml @@ -5,6 +5,7 @@ authors = ["Paul Hauner ", "Age Manning Result { + get_eth2_config(self.url.clone()).map_err(|e| format!("Unable to get Eth2Config: {:?}", e)) + } + /// Returns the servers ENR address. pub fn enr(&self) -> Result { get_enr(self.url.clone()).map_err(|e| format!("Unable to get ENR: {:?}", e)) @@ -129,6 +135,19 @@ fn get_slots_per_epoch(mut url: Url) -> Result { .map_err(Into::into) } +fn get_eth2_config(mut url: Url) -> Result { + url.path_segments_mut() + .map(|mut url| { + url.push("spec").push("eth2_config"); + }) + .map_err(|_| Error::InvalidUrl)?; + + reqwest::get(url)? + .error_for_status()? + .json() + .map_err(Into::into) +} + fn get_finalized_slot(mut url: Url, slots_per_epoch: u64) -> Result { url.path_segments_mut() .map(|mut url| { diff --git a/beacon_node/client/src/lib.rs b/beacon_node/client/src/lib.rs index 0bb30d0af..2612fd648 100644 --- a/beacon_node/client/src/lib.rs +++ b/beacon_node/client/src/lib.rs @@ -162,6 +162,7 @@ where beacon_chain.clone(), network.clone(), client_config.db_path().expect("unable to read datadir"), + eth2_config.clone(), &log, ) { Ok(s) => Some(s), diff --git a/beacon_node/rest_api/Cargo.toml b/beacon_node/rest_api/Cargo.toml index cac196d9c..5303dc8bd 100644 --- a/beacon_node/rest_api/Cargo.toml +++ b/beacon_node/rest_api/Cargo.toml @@ -27,5 +27,6 @@ exit-future = "0.1.3" tokio = "0.1.17" url = "2.0" lazy_static = "1.3.0" +eth2_config = { path = "../../eth2/utils/eth2_config" } lighthouse_metrics = { path = "../../eth2/utils/lighthouse_metrics" } slot_clock = { path = "../../eth2/utils/slot_clock" } diff --git a/beacon_node/rest_api/src/lib.rs b/beacon_node/rest_api/src/lib.rs index 964dd7998..b1137c249 100644 --- a/beacon_node/rest_api/src/lib.rs +++ b/beacon_node/rest_api/src/lib.rs @@ -13,6 +13,7 @@ mod url_query; use beacon_chain::{BeaconChain, BeaconChainTypes}; use client_network::Service as NetworkService; +use eth2_config::Eth2Config; use hyper::rt::Future; use hyper::service::service_fn_ok; use hyper::{Body, Method, Response, Server, StatusCode}; @@ -79,6 +80,7 @@ pub fn start_server( beacon_chain: Arc>, network_service: Arc>, db_path: PathBuf, + eth2_config: Eth2Config, log: &slog::Logger, ) -> Result { let log = log.new(o!("Service" => "Api")); @@ -100,12 +102,14 @@ pub fn start_server( // Clone our stateful objects, for use in service closure. let server_log = log.clone(); let server_bc = beacon_chain.clone(); + let eth2_config = Arc::new(eth2_config); let service = move || { let log = server_log.clone(); let beacon_chain = server_bc.clone(); let db_path = db_path.clone(); let network_service = network_service.clone(); + let eth2_config = eth2_config.clone(); // Create a simple handler for the router, inject our stateful objects into the request. service_fn_ok(move |mut req| { @@ -118,6 +122,8 @@ pub fn start_server( req.extensions_mut().insert::(db_path.clone()); req.extensions_mut() .insert::>>(network_service.clone()); + req.extensions_mut() + .insert::>(eth2_config.clone()); let path = req.uri().path().to_string(); @@ -144,6 +150,7 @@ pub fn start_server( (&Method::GET, "/node/genesis_time") => node::get_genesis_time::(req), (&Method::GET, "/spec") => spec::get_spec::(req), (&Method::GET, "/spec/slots_per_epoch") => spec::get_slots_per_epoch::(req), + (&Method::GET, "/spec/eth2_config") => spec::get_eth2_config::(req), _ => Err(ApiError::MethodNotAllowed(path.clone())), }; diff --git a/beacon_node/rest_api/src/spec.rs b/beacon_node/rest_api/src/spec.rs index d0c8e4368..86d1c227d 100644 --- a/beacon_node/rest_api/src/spec.rs +++ b/beacon_node/rest_api/src/spec.rs @@ -1,6 +1,7 @@ use super::{success_response, ApiResult}; use crate::ApiError; use beacon_chain::{BeaconChain, BeaconChainTypes}; +use eth2_config::Eth2Config; use hyper::{Body, Request}; use std::sync::Arc; use types::EthSpec; @@ -18,6 +19,19 @@ pub fn get_spec(req: Request) -> ApiResult Ok(success_response(Body::from(json))) } +/// HTTP handler to return the full Eth2Config object. +pub fn get_eth2_config(req: Request) -> ApiResult { + let eth2_config = req + .extensions() + .get::>() + .ok_or_else(|| ApiError::ServerError("Eth2Config extension missing".to_string()))?; + + let json: String = serde_json::to_string(eth2_config.as_ref()) + .map_err(|e| ApiError::ServerError(format!("Unable to serialize Eth2Config: {:?}", e)))?; + + Ok(success_response(Body::from(json))) +} + /// HTTP handler to return the full spec object. pub fn get_slots_per_epoch(_req: Request) -> ApiResult { let json: String = serde_json::to_string(&T::EthSpec::slots_per_epoch()) diff --git a/beacon_node/src/config.rs b/beacon_node/src/config.rs index f47a2ddb0..e76bd48fa 100644 --- a/beacon_node/src/config.rs +++ b/beacon_node/src/config.rs @@ -63,7 +63,13 @@ fn process_testnet_subcommand( builder.set_random_datadir()?; } + let is_bootstrap = cli_args.subcommand_name() == Some("bootstrap"); + if let Some(path_string) = cli_args.value_of("eth2-config") { + if is_bootstrap { + return Err("Cannot supply --eth2-config when using bootsrap".to_string()); + } + let path = path_string .parse::() .map_err(|e| format!("Unable to parse eth2-config path: {:?}", e))?; @@ -100,6 +106,7 @@ fn process_testnet_subcommand( .and_then(|s| s.parse::().ok()); builder.import_bootstrap_libp2p_address(server, port)?; + builder.import_bootstrap_eth2_config(server)?; builder.set_beacon_chain_start_method(BeaconChainStartMethod::HttpBootstrap { server: server.to_string(), @@ -252,6 +259,19 @@ impl<'a> ConfigBuilder<'a> { Ok(()) } + /// Imports an `Eth2Config` from `server`, returning an error if this fails. + pub fn import_bootstrap_eth2_config(&mut self, server: &str) -> Result<()> { + let bootstrapper = Bootstrapper::from_server_string(server.to_string())?; + + self.update_eth2_config(bootstrapper.eth2_config()?); + + Ok(()) + } + + fn update_eth2_config(&mut self, eth2_config: Eth2Config) { + self.eth2_config = eth2_config; + } + /// Reads the subcommand and tries to update `self.eth2_config` based up on the `--spec` flag. /// /// Returns an error if the `--spec` flag is not present in the given `cli_args`.