diff --git a/beacon_node/client/src/builder.rs b/beacon_node/client/src/builder.rs index af520ff8d67..62a78505021 100644 --- a/beacon_node/client/src/builder.rs +++ b/beacon_node/client/src/builder.rs @@ -62,6 +62,7 @@ pub struct ClientBuilder { network_globals: Option>>, network_send: Option>>, http_listen_addr: Option, + http_api_config: http_api::Config, websocket_listen_addr: Option, eth_spec_instance: T::EthSpec, } @@ -104,6 +105,7 @@ where network_globals: None, network_send: None, http_listen_addr: None, + http_api_config: <_>::default(), websocket_listen_addr: None, eth_spec_instance, } @@ -280,6 +282,12 @@ where Ok(self) } + /// Provides configuration for the HTTP API. + pub fn http_api_config(mut self, config: http_api::Config) -> Self { + self.http_api_config = config; + self + } + /// Immediately starts the beacon node REST API http server. pub fn http_server( mut self, @@ -380,19 +388,36 @@ where TColdStore, >, > { - let http_api_context = Arc::new(http_api::Context { - chain: self.beacon_chain.clone(), - }); - let ctx = http_api_context.clone(); - self.runtime_context - .unwrap() - .executor - .spawn_without_exit(async move { http_api::serve(ctx).await }, "cats"); + let http_api_listen_addr = if self.http_api_config.enabled { + let ctx = Arc::new(http_api::Context { + config: self.http_api_config.clone(), + chain: self.beacon_chain.clone(), + // TODO + log: self.runtime_context.as_ref().unwrap().log().clone(), + }); + + // TODO + let exit = self.runtime_context.as_ref().unwrap().executor.exit(); + + let (listen_addr, server) = http_api::serve(ctx, exit) + .map_err(|e| format!("Unable to start HTTP API server: {:?}", e)) + .unwrap(); // TODO + + self.runtime_context + .unwrap() + .executor + .spawn_without_exit(async move { server.await }, "http-api"); + + Some(listen_addr) + } else { + None + }; Client { beacon_chain: self.beacon_chain, network_globals: self.network_globals, http_listen_addr: self.http_listen_addr, + http_api_listen_addr, websocket_listen_addr: self.websocket_listen_addr, } } diff --git a/beacon_node/client/src/config.rs b/beacon_node/client/src/config.rs index 19088e785b5..feb5f1a33e2 100644 --- a/beacon_node/client/src/config.rs +++ b/beacon_node/client/src/config.rs @@ -67,6 +67,7 @@ pub struct Config { pub chain: beacon_chain::ChainConfig, pub websocket_server: websocket_server::Config, pub eth1: eth1::Config, + pub http_api: http_api::Config, } impl Default for Config { @@ -88,6 +89,7 @@ impl Default for Config { eth1: <_>::default(), disabled_forks: Vec::new(), graffiti: Graffiti::default(), + http_api: <_>::default(), } } } diff --git a/beacon_node/client/src/lib.rs b/beacon_node/client/src/lib.rs index da670ff1344..299f6882885 100644 --- a/beacon_node/client/src/lib.rs +++ b/beacon_node/client/src/lib.rs @@ -24,6 +24,8 @@ pub struct Client { beacon_chain: Option>>, network_globals: Option>>, http_listen_addr: Option, + /// Listen address for the standard eth2.0 API, if the service was started. + http_api_listen_addr: Option, websocket_listen_addr: Option, } @@ -38,6 +40,11 @@ impl Client { self.http_listen_addr } + /// Returns the address of the client's standard eth2.0 API server, if it was started. + pub fn http_api_listen_addr(&self) -> Option { + self.http_api_listen_addr + } + /// Returns the address of the client's WebSocket API server, if it was started. pub fn websocket_listen_addr(&self) -> Option { self.websocket_listen_addr diff --git a/beacon_node/http_api/Cargo.toml b/beacon_node/http_api/Cargo.toml index b8875f79df3..dba01347f1d 100644 --- a/beacon_node/http_api/Cargo.toml +++ b/beacon_node/http_api/Cargo.toml @@ -15,6 +15,7 @@ types = { path = "../../consensus/types" } hex = "0.4.2" beacon_chain = { path = "../beacon_chain" } eth2 = { path = "../../common/eth2" } +slog = "2.5.2" [dev-dependencies] diff --git a/beacon_node/http_api/src/lib.rs b/beacon_node/http_api/src/lib.rs index 9729297b111..4b1353bbe00 100644 --- a/beacon_node/http_api/src/lib.rs +++ b/beacon_node/http_api/src/lib.rs @@ -5,13 +5,13 @@ mod state_id; use beacon_chain::{BeaconChain, BeaconChainError, BeaconChainTypes}; use block_id::BlockId; use eth2::types::{self as api_types, ValidatorId}; -use serde::Serialize; +use serde::{Deserialize, Serialize}; +use slog::{crit, info, Logger}; use state_id::StateId; use std::borrow::Cow; use std::future::Future; -use std::net::SocketAddr; +use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; use std::sync::Arc; -use tokio::sync::oneshot; use types::{CommitteeCache, Epoch, EthSpec, RelativeEpoch}; use warp::Filter; @@ -19,16 +19,41 @@ const API_PREFIX: &str = "eth"; const API_VERSION: &str = "v1"; pub struct Context { + pub config: Config, pub chain: Option>>, - pub listen_address: [u8; 4], + pub log: Logger, +} + +#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)] +pub struct Config { + pub enabled: bool, + pub listen_socket_addr: SocketAddr, + pub listen_addr: Ipv4Addr, pub listen_port: u16, } +impl Default for Config { + fn default() -> Self { + Self { + enabled: false, + listen_socket_addr: SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 5054).into(), + listen_addr: Ipv4Addr::new(127, 0, 0, 1), + listen_port: 5054, + } + } +} + pub fn serve( ctx: Arc>, -) -> Result<(SocketAddr, impl Future, oneshot::Sender<()>), warp::Error> { - let listen_address = ctx.listen_address; - let listen_port = ctx.listen_port; + shutdown: impl Future + Send + Sync + 'static, +) -> Result<(SocketAddr, impl Future), warp::Error> { + let config = ctx.config.clone(); + let log = ctx.log.clone(); + + if config.enabled == false { + crit!(log, "Cannot start disabled HTTP server"); + panic!("a disabled server should not be started"); + } let base_path = warp::path(API_PREFIX).and(warp::path(API_VERSION)); let chain_filter = warp::any() @@ -423,15 +448,21 @@ pub fn serve( .or(beacon_block_root) .recover(crate::reject::handle_rejection); - let (shutdown_tx, shutdown_rx) = oneshot::channel::<()>(); + // let (shutdown_tx, shutdown_rx) = oneshot::channel::<()>(); let (listening_socket, server) = warp::serve(routes).try_bind_with_graceful_shutdown( - (listen_address, listen_port), + SocketAddrV4::new(config.listen_addr, config.listen_port), async { - shutdown_rx.await.ok(); + shutdown.await; }, )?; - Ok((listening_socket, server, shutdown_tx)) + info!( + log, + "HTTP API started"; + "listen_address" => listening_socket.to_string(), + ); + + Ok((listening_socket, server)) } async fn blocking_task(func: F) -> T diff --git a/beacon_node/src/cli.rs b/beacon_node/src/cli.rs index 6caa8acd9b2..e8c32dbca6a 100644 --- a/beacon_node/src/cli.rs +++ b/beacon_node/src/cli.rs @@ -163,6 +163,29 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> { .default_value("") .takes_value(true), ) + /* Standard eth2.0 API related arguments */ + .arg( + Arg::with_name("std-http") + .long("std-http") + .help("Enable RESTful HTTP API server. Disabled by default.") + .takes_value(false), + ) + .arg( + Arg::with_name("std-http-address") + .long("std-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("std-http-port") + .long("std-http2-port") + .value_name("PORT") + .help("Set the listen TCP port for the RESTful HTTP API server.") + .default_value("5052") + .takes_value(true), + ) /* Websocket related arguments */ .arg( Arg::with_name("ws") diff --git a/beacon_node/src/config.rs b/beacon_node/src/config.rs index f9abfca6aeb..395ec32e9cc 100644 --- a/beacon_node/src/config.rs +++ b/beacon_node/src/config.rs @@ -112,6 +112,29 @@ pub fn get_config( client_config.rest_api.allow_origin = allow_origin.to_string(); } + /* + * Standard eth2.0 API server + * + * Note: these "std-http" commands are only whilst the API is in beta. Eventually the existing + * HTTP server will be replaced and these flags will disappear. + */ + + if cli_args.is_present("std-http") { + client_config.http_api.enabled = true; + } + + if let Some(address) = cli_args.value_of("std-http-address") { + client_config.http_api.listen_addr = address + .parse::() + .map_err(|_| "std-http-address is not a valid IPv4 address.")?; + } + + if let Some(port) = cli_args.value_of("std-http-port") { + client_config.http_api.listen_port = port + .parse::() + .map_err(|_| "std-http-port is not a valid u16.")?; + } + /* * Websocket server */ diff --git a/beacon_node/src/lib.rs b/beacon_node/src/lib.rs index 19931916013..efcbb3258ea 100644 --- a/beacon_node/src/lib.rs +++ b/beacon_node/src/lib.rs @@ -130,7 +130,8 @@ impl ProductionBeaconNode { .build_beacon_chain()? .network(&client_config.network) .await? - .notifier()?; + .notifier()? + .http_api_config(client_config.http_api.clone()); let builder = if client_config.rest_api.enabled { builder.http_server(&client_config, &http_eth2_config, events)?