diff --git a/beacon_node/rest_api/src/beacon.rs b/beacon_node/rest_api/src/beacon.rs index c1a9da6eea..908fc2a573 100644 --- a/beacon_node/rest_api/src/beacon.rs +++ b/beacon_node/rest_api/src/beacon.rs @@ -23,12 +23,10 @@ pub struct HeadResponse { } /// HTTP handler to return a `BeaconBlock` at a given `root` or `slot`. -pub fn get_head(req: Request) -> ApiResult { - let beacon_chain = req - .extensions() - .get::>>() - .ok_or_else(|| ApiError::ServerError("Beacon chain extension missing".to_string()))?; - +pub fn get_head( + req: Request, + beacon_chain: Arc>, +) -> ApiResult { let chain_head = beacon_chain.head(); let head = HeadResponse { @@ -66,12 +64,10 @@ pub struct BlockResponse { } /// HTTP handler to return a `BeaconBlock` at a given `root` or `slot`. -pub fn get_block(req: Request) -> ApiResult { - let beacon_chain = req - .extensions() - .get::>>() - .ok_or_else(|| ApiError::ServerError("Beacon chain extension missing".to_string()))?; - +pub fn get_block( + req: Request, + beacon_chain: Arc>, +) -> ApiResult { let query_params = ["root", "slot"]; let (key, value) = UrlQuery::from_request(&req)?.first_of(&query_params)?; @@ -106,9 +102,10 @@ pub fn get_block(req: Request) -> ApiResult } /// HTTP handler to return a `BeaconBlock` root at a given `slot`. -pub fn get_block_root(req: Request) -> ApiResult { - let beacon_chain = get_beacon_chain_from_request::(&req)?; - +pub fn get_block_root( + req: Request, + beacon_chain: Arc>, +) -> ApiResult { let slot_string = UrlQuery::from_request(&req)?.only_one("slot")?; let target = parse_slot(&slot_string)?; @@ -120,8 +117,10 @@ pub fn get_block_root(req: Request) -> ApiR } /// HTTP handler to return the `Fork` of the current head. -pub fn get_fork(req: Request) -> ApiResult { - let beacon_chain = get_beacon_chain_from_request::(&req)?; +pub fn get_fork( + req: Request, + beacon_chain: Arc>, +) -> ApiResult { ResponseBuilder::new(&req)?.body(&beacon_chain.head().beacon_state.fork) } @@ -129,9 +128,10 @@ pub fn get_fork(req: Request) -> ApiResult /// /// The `Epoch` parameter can be any epoch number. If it is not specified, /// the current epoch is assumed. -pub fn get_validators(req: Request) -> ApiResult { - let beacon_chain = get_beacon_chain_from_request::(&req)?; - +pub fn get_validators( + req: Request, + beacon_chain: Arc>, +) -> ApiResult { let epoch = match UrlQuery::from_request(&req) { // We have some parameters, so make sure it's the epoch one and parse it Ok(query) => query @@ -168,8 +168,10 @@ pub struct StateResponse { /// /// Will not return a state if the request slot is in the future. Will return states higher than /// the current head by skipping slots. -pub fn get_state(req: Request) -> ApiResult { - let beacon_chain = get_beacon_chain_from_request::(&req)?; +pub fn get_state( + req: Request, + beacon_chain: Arc>, +) -> ApiResult { let head_state = beacon_chain.head().beacon_state; let (key, value) = match UrlQuery::from_request(&req) { @@ -214,9 +216,10 @@ pub fn get_state(req: Request) -> ApiResult /// /// Will not return a state if the request slot is in the future. Will return states higher than /// the current head by skipping slots. -pub fn get_state_root(req: Request) -> ApiResult { - let beacon_chain = get_beacon_chain_from_request::(&req)?; - +pub fn get_state_root( + req: Request, + beacon_chain: Arc>, +) -> ApiResult { let slot_string = UrlQuery::from_request(&req)?.only_one("slot")?; let slot = parse_slot(&slot_string)?; @@ -228,8 +231,8 @@ pub fn get_state_root(req: Request) -> ApiR /// HTTP handler to return the highest finalized slot. pub fn get_current_finalized_checkpoint( req: Request, + beacon_chain: Arc>, ) -> ApiResult { - let beacon_chain = get_beacon_chain_from_request::(&req)?; let head_state = beacon_chain.head().beacon_state; let checkpoint = head_state.finalized_checkpoint.clone(); @@ -238,9 +241,10 @@ pub fn get_current_finalized_checkpoint( } /// HTTP handler to return a `BeaconState` at the genesis block. -pub fn get_genesis_state(req: Request) -> ApiResult { - let beacon_chain = get_beacon_chain_from_request::(&req)?; - +pub fn get_genesis_state( + req: Request, + beacon_chain: Arc>, +) -> ApiResult { let (_root, state) = state_at_slot(&beacon_chain, Slot::new(0))?; ResponseBuilder::new(&req)?.body(&state) diff --git a/beacon_node/rest_api/src/helpers.rs b/beacon_node/rest_api/src/helpers.rs index eba54e8670..2a8e7db428 100644 --- a/beacon_node/rest_api/src/helpers.rs +++ b/beacon_node/rest_api/src/helpers.rs @@ -213,26 +213,6 @@ pub fn implementation_pending_response(_req: Request) -> ApiResult { )) } -pub fn get_beacon_chain_from_request( - req: &Request, -) -> Result<(Arc>), ApiError> { - // Get beacon state - let beacon_chain = req - .extensions() - .get::>>() - .ok_or_else(|| ApiError::ServerError("Beacon chain extension missing".into()))?; - - Ok(beacon_chain.clone()) -} - -pub fn get_logger_from_request(req: &Request) -> slog::Logger { - let log = req - .extensions() - .get::() - .expect("Should always get the logger from the request, since we put it in there."); - log.to_owned() -} - pub fn publish_beacon_block_to_network( chan: Arc>>, block: BeaconBlock, diff --git a/beacon_node/rest_api/src/lib.rs b/beacon_node/rest_api/src/lib.rs index ad27cde3e7..a5d434367b 100644 --- a/beacon_node/rest_api/src/lib.rs +++ b/beacon_node/rest_api/src/lib.rs @@ -12,6 +12,7 @@ mod metrics; mod network; mod node; mod response_builder; +mod router; mod spec; mod url_query; mod validator; @@ -22,10 +23,10 @@ use client_network::Service as NetworkService; pub use config::ApiEncodingFormat; use error::{ApiError, ApiResult}; use eth2_config::Eth2Config; -use futures::future::IntoFuture; use hyper::rt::Future; -use hyper::service::Service; -use hyper::{Body, Method, Request, Response, Server}; +use hyper::server::conn::AddrStream; +use hyper::service::{make_service_fn, service_fn}; +use hyper::{Body, Request, Response, Server}; use parking_lot::RwLock; use slog::{info, warn}; use std::net::SocketAddr; @@ -41,162 +42,14 @@ pub use beacon::{BlockResponse, HeadResponse, StateResponse}; pub use config::Config; pub use validator::ValidatorDuty; -type BoxFut = Box, Error = ApiError> + Send>; - -pub struct ApiService { - log: slog::Logger, - beacon_chain: Arc>, - db_path: DBPath, - network_service: Arc>, - network_channel: Arc>>, - eth2_config: Arc, -} +pub type BoxFut = Box, Error = ApiError> + Send>; +pub type NetworkChannel = Arc>>; pub struct NetworkInfo { pub network_service: Arc>, pub network_chan: mpsc::UnboundedSender, } -fn into_boxfut(item: F) -> BoxFut -where - F: IntoFuture, Error = ApiError>, - F::Future: Send, -{ - Box::new(item.into_future()) -} - -impl Service for ApiService { - type ReqBody = Body; - type ResBody = Body; - type Error = ApiError; - type Future = BoxFut; - - fn call(&mut self, mut req: Request) -> Self::Future { - metrics::inc_counter(&metrics::REQUEST_COUNT); - let timer = metrics::start_timer(&metrics::REQUEST_RESPONSE_TIME); - - // Add all the useful bits into the request, so that we can pull them out in the individual - // functions. - req.extensions_mut() - .insert::(self.log.clone()); - req.extensions_mut() - .insert::>>(self.beacon_chain.clone()); - req.extensions_mut().insert::(self.db_path.clone()); - req.extensions_mut() - .insert::>>(self.network_service.clone()); - req.extensions_mut() - .insert::>>>( - self.network_channel.clone(), - ); - req.extensions_mut() - .insert::>(self.eth2_config.clone()); - - let path = req.uri().path().to_string(); - - // Route the request to the correct handler. - let result = match (req.method(), path.as_ref()) { - // Methods for Client - (&Method::GET, "/node/version") => into_boxfut(node::get_version(req)), - (&Method::GET, "/node/genesis_time") => into_boxfut(node::get_genesis_time::(req)), - (&Method::GET, "/node/syncing") => { - into_boxfut(helpers::implementation_pending_response(req)) - } - - // Methods for Network - (&Method::GET, "/network/enr") => into_boxfut(network::get_enr::(req)), - (&Method::GET, "/network/peer_count") => into_boxfut(network::get_peer_count::(req)), - (&Method::GET, "/network/peer_id") => into_boxfut(network::get_peer_id::(req)), - (&Method::GET, "/network/peers") => into_boxfut(network::get_peer_list::(req)), - (&Method::GET, "/network/listen_port") => { - into_boxfut(network::get_listen_port::(req)) - } - (&Method::GET, "/network/listen_addresses") => { - into_boxfut(network::get_listen_addresses::(req)) - } - - // Methods for Beacon Node - (&Method::GET, "/beacon/head") => into_boxfut(beacon::get_head::(req)), - (&Method::GET, "/beacon/block") => into_boxfut(beacon::get_block::(req)), - (&Method::GET, "/beacon/block_root") => into_boxfut(beacon::get_block_root::(req)), - (&Method::GET, "/beacon/blocks") => { - into_boxfut(helpers::implementation_pending_response(req)) - } - (&Method::GET, "/beacon/fork") => into_boxfut(beacon::get_fork::(req)), - (&Method::GET, "/beacon/attestations") => { - into_boxfut(helpers::implementation_pending_response(req)) - } - (&Method::GET, "/beacon/attestations/pending") => { - into_boxfut(helpers::implementation_pending_response(req)) - } - - (&Method::GET, "/beacon/validators") => into_boxfut(beacon::get_validators::(req)), - (&Method::GET, "/beacon/validators/indicies") => { - into_boxfut(helpers::implementation_pending_response(req)) - } - (&Method::GET, "/beacon/validators/pubkeys") => { - into_boxfut(helpers::implementation_pending_response(req)) - } - - // Methods for Validator - (&Method::GET, "/validator/duties") => { - into_boxfut(validator::get_validator_duties::(req)) - } - (&Method::GET, "/validator/block") => { - into_boxfut(validator::get_new_beacon_block::(req)) - } - (&Method::POST, "/validator/block") => validator::publish_beacon_block::(req), - (&Method::GET, "/validator/attestation") => { - into_boxfut(validator::get_new_attestation::(req)) - } - (&Method::POST, "/validator/attestation") => validator::publish_attestation::(req), - - (&Method::GET, "/beacon/state") => into_boxfut(beacon::get_state::(req)), - (&Method::GET, "/beacon/state_root") => into_boxfut(beacon::get_state_root::(req)), - (&Method::GET, "/beacon/state/current_finalized_checkpoint") => { - into_boxfut(beacon::get_current_finalized_checkpoint::(req)) - } - (&Method::GET, "/beacon/state/genesis") => { - into_boxfut(beacon::get_genesis_state::(req)) - } - //TODO: Add aggreggate/filtered state lookups here, e.g. /beacon/validators/balances - - // Methods for bootstrap and checking configuration - (&Method::GET, "/spec") => into_boxfut(spec::get_spec::(req)), - (&Method::GET, "/spec/slots_per_epoch") => { - into_boxfut(spec::get_slots_per_epoch::(req)) - } - (&Method::GET, "/spec/deposit_contract") => { - into_boxfut(helpers::implementation_pending_response(req)) - } - (&Method::GET, "/spec/eth2_config") => into_boxfut(spec::get_eth2_config::(req)), - - (&Method::GET, "/metrics") => into_boxfut(metrics::get_prometheus::(req)), - - _ => Box::new(futures::future::err(ApiError::NotFound( - "Request path and/or method not found.".to_owned(), - ))), - }; - - let response = match result.wait() { - // Return the `hyper::Response`. - Ok(response) => { - metrics::inc_counter(&metrics::SUCCESS_COUNT); - slog::debug!(self.log, "Request successful: {:?}", path); - response - } - // Map the `ApiError` into `hyper::Response`. - Err(e) => { - slog::debug!(self.log, "Request failure: {:?}", path); - e.into() - } - }; - - metrics::stop_timer(timer); - - Box::new(futures::future::ok(response)) - } -} - pub fn start_server( config: &Config, executor: &TaskExecutor, @@ -206,46 +59,53 @@ pub fn start_server( eth2_config: Eth2Config, log: slog::Logger, ) -> Result<(exit_future::Signal, SocketAddr), hyper::Error> { - // build a channel to kill the HTTP server - let (exit_signal, exit) = exit_future::signal(); + // Define the function that will build the request handler. + let inner_log = log.clone(); + let eth2_config = Arc::new(eth2_config); + let make_service = make_service_fn(move |_socket: &AddrStream| { + let beacon_chain = beacon_chain.clone(); + let log = inner_log.clone(); + let eth2_config = eth2_config.clone(); + let network_service = network_info.network_service.clone(); + let network_channel = Arc::new(RwLock::new(network_info.network_chan.clone())); + let db_path = db_path.clone(); - let exit_log = log.clone(); - let server_exit = exit.and_then(move |_| { - info!(exit_log, "API service shutdown"); - Ok(()) + service_fn(move |req: Request| { + router::route( + req, + beacon_chain.clone(), + network_service.clone(), + network_channel.clone(), + eth2_config.clone(), + log.clone(), + db_path.clone(), + ) + }) }); - let db_path = DBPath(db_path); - - // Get the address to bind to let bind_addr = (config.listen_address, config.port).into(); + let server = Server::bind(&bind_addr).serve(make_service); - // 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 || -> futures::future::FutureResult, String> { - futures::future::ok(ApiService { - log: server_log.clone(), - beacon_chain: server_bc.clone(), - db_path: db_path.clone(), - network_service: network_info.network_service.clone(), - network_channel: Arc::new(RwLock::new(network_info.network_chan.clone())), - eth2_config: eth2_config.clone(), - }) - }; - - let log_clone = log.clone(); - let server = Server::bind(&bind_addr).serve(service); - + // Determine the address the server is actually listening on. + // + // This may be different to `bind_addr` if bind port was 0 (this allows the OS to choose a free + // port). let actual_listen_addr = server.local_addr(); + // Build a channel to kill the HTTP server. + let (exit_signal, exit) = exit_future::signal(); + let inner_log = log.clone(); + let server_exit = exit.and_then(move |_| { + info!(inner_log, "API service shutdown"); + Ok(()) + }); + // Configure the `hyper` server to gracefully shutdown when the shutdown channel is triggered. + let inner_log = log.clone(); let server_future = server .with_graceful_shutdown(server_exit) .map_err(move |e| { warn!( - log_clone, + inner_log, "API failed to start, Unable to bind"; "address" => format!("{:?}", e) ) }); diff --git a/beacon_node/rest_api/src/metrics.rs b/beacon_node/rest_api/src/metrics.rs index 966d4deacf..77447a479d 100644 --- a/beacon_node/rest_api/src/metrics.rs +++ b/beacon_node/rest_api/src/metrics.rs @@ -1,9 +1,10 @@ -use crate::helpers::get_beacon_chain_from_request; use crate::response_builder::ResponseBuilder; -use crate::{ApiError, ApiResult, DBPath}; -use beacon_chain::BeaconChainTypes; +use crate::{ApiError, ApiResult}; +use beacon_chain::{BeaconChain, BeaconChainTypes}; use hyper::{Body, Request}; use lighthouse_metrics::{Encoder, TextEncoder}; +use std::path::PathBuf; +use std::sync::Arc; pub use lighthouse_metrics::*; @@ -27,16 +28,14 @@ lazy_static! { /// # Note /// /// This is a HTTP handler method. -pub fn get_prometheus(req: Request) -> ApiResult { +pub fn get_prometheus( + req: Request, + beacon_chain: Arc>, + db_path: PathBuf, +) -> ApiResult { let mut buffer = vec![]; let encoder = TextEncoder::new(); - let beacon_chain = get_beacon_chain_from_request::(&req)?; - let db_path = req - .extensions() - .get::() - .ok_or_else(|| ApiError::ServerError("DBPath extension missing".to_string()))?; - // There are two categories of metrics: // // - Dynamically updated: things like histograms and event counters that are updated on the diff --git a/beacon_node/rest_api/src/network.rs b/beacon_node/rest_api/src/network.rs index f193ef8eab..171d59beda 100644 --- a/beacon_node/rest_api/src/network.rs +++ b/beacon_node/rest_api/src/network.rs @@ -9,11 +9,10 @@ use std::sync::Arc; /// HTTP handler to return the list of libp2p multiaddr the client is listening on. /// /// Returns a list of `Multiaddr`, serialized according to their `serde` impl. -pub fn get_listen_addresses(req: Request) -> ApiResult { - let network = req - .extensions() - .get::>>() - .expect("The network service should always be there, we put it there"); +pub fn get_listen_addresses( + req: Request, + network: Arc>, +) -> ApiResult { let multiaddresses: Vec = network.listen_multiaddrs(); ResponseBuilder::new(&req)?.body_no_ssz(&multiaddresses) } @@ -21,54 +20,48 @@ pub fn get_listen_addresses(req: Request) -> ApiResul /// HTTP handler to return the network port the client is listening on. /// /// Returns the TCP port number in its plain form (which is also valid JSON serialization) -pub fn get_listen_port(req: Request) -> ApiResult { - let network = req - .extensions() - .get::>>() - .expect("The network service should always be there, we put it there") - .clone(); +pub fn get_listen_port( + req: Request, + network: Arc>, +) -> ApiResult { ResponseBuilder::new(&req)?.body(&network.listen_port()) } /// HTTP handler to return the Discv5 ENR from the client's libp2p service. /// /// ENR is encoded as base64 string. -pub fn get_enr(req: Request) -> ApiResult { - let network = req - .extensions() - .get::>>() - .expect("The network service should always be there, we put it there"); +pub fn get_enr( + req: Request, + network: Arc>, +) -> ApiResult { ResponseBuilder::new(&req)?.body_no_ssz(&network.local_enr().to_base64()) } /// HTTP handler to return the `PeerId` from the client's libp2p service. /// /// PeerId is encoded as base58 string. -pub fn get_peer_id(req: Request) -> ApiResult { - let network = req - .extensions() - .get::>>() - .expect("The network service should always be there, we put it there"); +pub fn get_peer_id( + req: Request, + network: Arc>, +) -> ApiResult { ResponseBuilder::new(&req)?.body_no_ssz(&network.local_peer_id().to_base58()) } /// HTTP handler to return the number of peers connected in the client's libp2p service. -pub fn get_peer_count(req: Request) -> ApiResult { - let network = req - .extensions() - .get::>>() - .expect("The network service should always be there, we put it there"); +pub fn get_peer_count( + req: Request, + network: Arc>, +) -> ApiResult { ResponseBuilder::new(&req)?.body(&network.connected_peers()) } /// HTTP handler to return the list of peers connected to the client's libp2p service. /// /// Peers are presented as a list of `PeerId::to_string()`. -pub fn get_peer_list(req: Request) -> ApiResult { - let network = req - .extensions() - .get::>>() - .expect("The network service should always be there, we put it there"); +pub fn get_peer_list( + req: Request, + network: Arc>, +) -> ApiResult { let connected_peers: Vec = network .connected_peer_set() .iter() diff --git a/beacon_node/rest_api/src/node.rs b/beacon_node/rest_api/src/node.rs index 882edcfd56..bce99e50c6 100644 --- a/beacon_node/rest_api/src/node.rs +++ b/beacon_node/rest_api/src/node.rs @@ -1,8 +1,8 @@ -use crate::helpers::get_beacon_chain_from_request; use crate::response_builder::ResponseBuilder; use crate::ApiResult; -use beacon_chain::BeaconChainTypes; +use beacon_chain::{BeaconChain, BeaconChainTypes}; use hyper::{Body, Request}; +use std::sync::Arc; use version; /// Read the version string from the current Lighthouse build. @@ -11,7 +11,9 @@ pub fn get_version(req: Request) -> ApiResult { } /// Read the genesis time from the current beacon chain state. -pub fn get_genesis_time(req: Request) -> ApiResult { - let beacon_chain = get_beacon_chain_from_request::(&req)?; +pub fn get_genesis_time( + req: Request, + beacon_chain: Arc>, +) -> ApiResult { ResponseBuilder::new(&req)?.body(&beacon_chain.head().beacon_state.genesis_time) } diff --git a/beacon_node/rest_api/src/router.rs b/beacon_node/rest_api/src/router.rs new file mode 100644 index 0000000000..9f323fbe3f --- /dev/null +++ b/beacon_node/rest_api/src/router.rs @@ -0,0 +1,169 @@ +use crate::{ + beacon, error::ApiError, helpers, metrics, network, node, spec, validator, BoxFut, + NetworkChannel, +}; +use beacon_chain::{BeaconChain, BeaconChainTypes}; +use client_network::Service as NetworkService; +use eth2_config::Eth2Config; +use futures::{Future, IntoFuture}; +use hyper::{Body, Error, Method, Request, Response}; +use slog::debug; +use std::path::PathBuf; +use std::sync::Arc; + +fn into_boxfut(item: F) -> BoxFut +where + F: IntoFuture, Error = ApiError>, + F::Future: Send, +{ + Box::new(item.into_future()) +} + +pub fn route( + req: Request, + beacon_chain: Arc>, + network_service: Arc>, + network_channel: NetworkChannel, + eth2_config: Arc, + local_log: slog::Logger, + db_path: PathBuf, +) -> impl Future, Error = Error> { + metrics::inc_counter(&metrics::REQUEST_COUNT); + let timer = metrics::start_timer(&metrics::REQUEST_RESPONSE_TIME); + + let path = req.uri().path().to_string(); + + let log = local_log.clone(); + let request_result: Box, Error = _> + Send> = + match (req.method(), path.as_ref()) { + // Methods for Client + (&Method::GET, "/node/version") => into_boxfut(node::get_version(req)), + (&Method::GET, "/node/genesis_time") => { + into_boxfut(node::get_genesis_time::(req, beacon_chain)) + } + (&Method::GET, "/node/syncing") => { + into_boxfut(helpers::implementation_pending_response(req)) + } + + // Methods for Network + (&Method::GET, "/network/enr") => { + into_boxfut(network::get_enr::(req, network_service)) + } + (&Method::GET, "/network/peer_count") => { + into_boxfut(network::get_peer_count::(req, network_service)) + } + (&Method::GET, "/network/peer_id") => { + into_boxfut(network::get_peer_id::(req, network_service)) + } + (&Method::GET, "/network/peers") => { + into_boxfut(network::get_peer_list::(req, network_service)) + } + (&Method::GET, "/network/listen_port") => { + into_boxfut(network::get_listen_port::(req, network_service)) + } + (&Method::GET, "/network/listen_addresses") => { + into_boxfut(network::get_listen_addresses::(req, network_service)) + } + + // Methods for Beacon Node + (&Method::GET, "/beacon/head") => into_boxfut(beacon::get_head::(req, beacon_chain)), + (&Method::GET, "/beacon/block") => { + into_boxfut(beacon::get_block::(req, beacon_chain)) + } + (&Method::GET, "/beacon/block_root") => { + into_boxfut(beacon::get_block_root::(req, beacon_chain)) + } + (&Method::GET, "/beacon/blocks") => { + into_boxfut(helpers::implementation_pending_response(req)) + } + (&Method::GET, "/beacon/fork") => into_boxfut(beacon::get_fork::(req, beacon_chain)), + (&Method::GET, "/beacon/attestations") => { + into_boxfut(helpers::implementation_pending_response(req)) + } + (&Method::GET, "/beacon/attestations/pending") => { + into_boxfut(helpers::implementation_pending_response(req)) + } + + (&Method::GET, "/beacon/validators") => { + into_boxfut(beacon::get_validators::(req, beacon_chain)) + } + (&Method::GET, "/beacon/validators/indicies") => { + into_boxfut(helpers::implementation_pending_response(req)) + } + (&Method::GET, "/beacon/validators/pubkeys") => { + into_boxfut(helpers::implementation_pending_response(req)) + } + + // Methods for Validator + (&Method::GET, "/validator/duties") => { + into_boxfut(validator::get_validator_duties::(req, beacon_chain, log)) + } + (&Method::GET, "/validator/block") => { + into_boxfut(validator::get_new_beacon_block::(req, beacon_chain)) + } + (&Method::POST, "/validator/block") => { + validator::publish_beacon_block::(req, beacon_chain, network_channel, log) + } + (&Method::GET, "/validator/attestation") => { + into_boxfut(validator::get_new_attestation::(req, beacon_chain)) + } + (&Method::POST, "/validator/attestation") => { + validator::publish_attestation::(req, beacon_chain, network_channel, log) + } + + (&Method::GET, "/beacon/state") => { + into_boxfut(beacon::get_state::(req, beacon_chain)) + } + (&Method::GET, "/beacon/state_root") => { + into_boxfut(beacon::get_state_root::(req, beacon_chain)) + } + (&Method::GET, "/beacon/state/current_finalized_checkpoint") => into_boxfut( + beacon::get_current_finalized_checkpoint::(req, beacon_chain), + ), + (&Method::GET, "/beacon/state/genesis") => { + into_boxfut(beacon::get_genesis_state::(req, beacon_chain)) + } + //TODO: Add aggreggate/filtered state lookups here, e.g. /beacon/validators/balances + + // Methods for bootstrap and checking configuration + (&Method::GET, "/spec") => into_boxfut(spec::get_spec::(req, beacon_chain)), + (&Method::GET, "/spec/slots_per_epoch") => { + into_boxfut(spec::get_slots_per_epoch::(req)) + } + (&Method::GET, "/spec/deposit_contract") => { + into_boxfut(helpers::implementation_pending_response(req)) + } + (&Method::GET, "/spec/eth2_config") => { + into_boxfut(spec::get_eth2_config::(req, eth2_config)) + } + + (&Method::GET, "/metrics") => { + into_boxfut(metrics::get_prometheus::(req, beacon_chain, db_path)) + } + + _ => Box::new(futures::future::err(ApiError::NotFound( + "Request path and/or method not found.".to_owned(), + ))), + }; + + // Map the Rust-friendly `Result` in to a http-friendly response. In effect, this ensures that + // any `Err` returned from our response handlers becomes a valid http response to the client + // (e.g., a response with a 404 or 500 status). + request_result.then(move |result| match result { + Ok(response) => { + debug!(local_log, "Request successful: {:?}", path); + metrics::inc_counter(&metrics::SUCCESS_COUNT); + metrics::stop_timer(timer); + + Ok(response) + } + Err(e) => { + let error_response = e.into(); + + debug!(local_log, "Request failure: {:?}", path); + metrics::stop_timer(timer); + + Ok(error_response) + } + }) +} diff --git a/beacon_node/rest_api/src/spec.rs b/beacon_node/rest_api/src/spec.rs index 083ff5ad48..529ed830fe 100644 --- a/beacon_node/rest_api/src/spec.rs +++ b/beacon_node/rest_api/src/spec.rs @@ -1,26 +1,24 @@ use super::ApiResult; -use crate::helpers::get_beacon_chain_from_request; use crate::response_builder::ResponseBuilder; -use crate::ApiError; -use beacon_chain::BeaconChainTypes; +use beacon_chain::{BeaconChain, BeaconChainTypes}; use eth2_config::Eth2Config; use hyper::{Body, Request}; use std::sync::Arc; use types::EthSpec; /// HTTP handler to return the full spec object. -pub fn get_spec(req: Request) -> ApiResult { - let beacon_chain = get_beacon_chain_from_request::(&req)?; +pub fn get_spec( + req: Request, + beacon_chain: Arc>, +) -> ApiResult { ResponseBuilder::new(&req)?.body_no_ssz(&beacon_chain.spec) } /// 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()))?; - +pub fn get_eth2_config( + req: Request, + eth2_config: Arc, +) -> ApiResult { ResponseBuilder::new(&req)?.body_no_ssz(eth2_config.as_ref()) } diff --git a/beacon_node/rest_api/src/validator.rs b/beacon_node/rest_api/src/validator.rs index 9d0899b951..392338831a 100644 --- a/beacon_node/rest_api/src/validator.rs +++ b/beacon_node/rest_api/src/validator.rs @@ -1,21 +1,19 @@ use crate::helpers::{ - check_content_type_for_json, get_beacon_chain_from_request, get_logger_from_request, - parse_pubkey, parse_signature, publish_attestation_to_network, publish_beacon_block_to_network, + check_content_type_for_json, parse_pubkey, parse_signature, publish_attestation_to_network, + publish_beacon_block_to_network, }; use crate::response_builder::ResponseBuilder; -use crate::{ApiError, ApiResult, BoxFut, UrlQuery}; -use beacon_chain::{AttestationProcessingOutcome, BeaconChainTypes, BlockProcessingOutcome}; +use crate::{ApiError, ApiResult, BoxFut, NetworkChannel, UrlQuery}; +use beacon_chain::{ + AttestationProcessingOutcome, BeaconChain, BeaconChainTypes, BlockProcessingOutcome, +}; use bls::{AggregateSignature, PublicKey, BLS_PUBLIC_KEY_BYTE_SIZE}; use futures::future::Future; use futures::stream::Stream; use hyper::{Body, Request}; -use network::NetworkMessage; -use parking_lot::RwLock; use serde::{Deserialize, Serialize}; -use slog::{info, trace, warn}; +use slog::{info, trace, warn, Logger}; use std::sync::Arc; -use tokio; -use tokio::sync::mpsc; use types::beacon_state::EthSpec; use types::{Attestation, BeaconBlock, BitList, Epoch, RelativeEpoch, Shard, Slot}; @@ -44,10 +42,12 @@ impl ValidatorDuty { } /// HTTP Handler to retrieve a the duties for a set of validators during a particular epoch -pub fn get_validator_duties(req: Request) -> ApiResult { - let log = get_logger_from_request(&req); +pub fn get_validator_duties( + req: Request, + beacon_chain: Arc>, + log: Logger, +) -> ApiResult { slog::trace!(log, "Validator duties requested of API: {:?}", &req); - let beacon_chain = get_beacon_chain_from_request::(&req)?; let mut head_state = beacon_chain.head().beacon_state; slog::trace!(log, "Got head state from request."); @@ -154,9 +154,10 @@ pub fn get_validator_duties(req: Request) - } /// HTTP Handler to produce a new BeaconBlock from the current state, ready to be signed by a validator. -pub fn get_new_beacon_block(req: Request) -> ApiResult { - let beacon_chain = get_beacon_chain_from_request::(&req)?; - +pub fn get_new_beacon_block( + req: Request, + beacon_chain: Arc>, +) -> ApiResult { let query = UrlQuery::from_request(&req)?; let slot = query .first_of(&["slot"]) @@ -186,17 +187,13 @@ pub fn get_new_beacon_block(req: Request) - } /// HTTP Handler to publish a BeaconBlock, which has been signed by a validator. -pub fn publish_beacon_block(req: Request) -> BoxFut { +pub fn publish_beacon_block( + req: Request, + beacon_chain: Arc>, + network_chan: NetworkChannel, + log: Logger, +) -> BoxFut { try_future!(check_content_type_for_json(&req)); - let log = get_logger_from_request(&req); - let beacon_chain = try_future!(get_beacon_chain_from_request::(&req)); - // Get the network sending channel from the request, for later transmission - let network_chan = req - .extensions() - .get::>>>() - .expect("Should always get the network channel from the request, since we put it in there.") - .clone(); - let response_builder = ResponseBuilder::new(&req); let body = req.into_body(); @@ -239,8 +236,10 @@ pub fn publish_beacon_block(req: Request) - } /// HTTP Handler to produce a new Attestation from the current state, ready to be signed by a validator. -pub fn get_new_attestation(req: Request) -> ApiResult { - let beacon_chain = get_beacon_chain_from_request::(&req)?; +pub fn get_new_attestation( + req: Request, + beacon_chain: Arc>, +) -> ApiResult { let mut head_state = beacon_chain.head().beacon_state; let query = UrlQuery::from_request(&req)?; @@ -347,17 +346,13 @@ pub fn get_new_attestation(req: Request) -> } /// HTTP Handler to publish an Attestation, which has been signed by a validator. -pub fn publish_attestation(req: Request) -> BoxFut { +pub fn publish_attestation( + req: Request, + beacon_chain: Arc>, + network_chan: NetworkChannel, + log: Logger, +) -> BoxFut { try_future!(check_content_type_for_json(&req)); - let log = get_logger_from_request(&req); - let beacon_chain = try_future!(get_beacon_chain_from_request::(&req)); - // Get the network sending channel from the request, for later transmission - let network_chan = req - .extensions() - .get::>>>() - .expect("Should always get the network channel from the request, since we put it in there.") - .clone(); - let response_builder = ResponseBuilder::new(&req); let body = req.into_body();