From ebd97730d572a80343f1cd8779d19e1b56e8a5eb Mon Sep 17 00:00:00 2001 From: Luke Anderson Date: Wed, 11 Sep 2019 01:43:49 +1000 Subject: [PATCH] Converted the Beacon API service to Futures - Added SSZ encode for HeadResponse - Converted all of the /beacon/ endpoints to return BoxFut instead of ApiResult - Wrapped all of the '?'s in a new macro try_future!() - Copied the try macro to try_future, so that a boxed future can easily be returned. - Replaced all of the response serializations to use the new success_response --- beacon_node/rest_api/src/beacon.rs | 164 +++++++++++++--------------- beacon_node/rest_api/src/helpers.rs | 2 +- beacon_node/rest_api/src/lib.rs | 4 +- beacon_node/rest_api/src/macros.rs | 13 +++ 4 files changed, 94 insertions(+), 89 deletions(-) create mode 100644 beacon_node/rest_api/src/macros.rs diff --git a/beacon_node/rest_api/src/beacon.rs b/beacon_node/rest_api/src/beacon.rs index 4c57e4770d..f5abc51af2 100644 --- a/beacon_node/rest_api/src/beacon.rs +++ b/beacon_node/rest_api/src/beacon.rs @@ -9,7 +9,7 @@ use std::sync::Arc; use store::Store; use types::{BeaconBlock, BeaconState, Epoch, EthSpec, Hash256, Slot, Validator}; -#[derive(Serialize)] +#[derive(Serialize, Encode)] pub struct HeadResponse { pub slot: Slot, pub block_root: Hash256, @@ -23,11 +23,12 @@ pub struct HeadResponse { } /// HTTP handler to return a `BeaconBlock` at a given `root` or `slot`. -pub fn get_head(req: Request) -> ApiResult { +pub fn get_head(req: Request) -> BoxFut { let beacon_chain = req .extensions() .get::>>() - .ok_or_else(|| ApiError::ServerError("Beacon chain extension missing".to_string()))?; + .expect("BeaconChain extension must be there, because we put it there.") + .clone(); let chain_head = beacon_chain.head(); @@ -55,10 +56,7 @@ pub fn get_head(req: Request) -> ApiResult previous_justified_block_root: chain_head.beacon_state.previous_justified_checkpoint.root, }; - let json: String = serde_json::to_string(&head) - .map_err(|e| ApiError::ServerError(format!("Unable to serialize HeadResponse: {:?}", e)))?; - - Ok(success_response_old(Body::from(json))) + success_response(req, &head) } #[derive(Serialize, Encode)] @@ -69,84 +67,83 @@ pub struct BlockResponse { } /// HTTP handler to return a `BeaconBlock` at a given `root` or `slot`. -pub fn get_block(req: Request) -> ApiResult { +pub fn get_block(req: Request) -> BoxFut { let beacon_chain = req .extensions() .get::>>() - .ok_or_else(|| ApiError::ServerError("Beacon chain extension missing".to_string()))?; + .expect("BeaconChain extension must be there, because we put it there.") + .clone(); let query_params = ["root", "slot"]; - let (key, value) = UrlQuery::from_request(&req)?.first_of(&query_params)?; + let query = try_future!(UrlQuery::from_request(&req)); + let (key, value) = try_future!(query.first_of(&query_params)); let block_root = match (key.as_ref(), value) { ("slot", value) => { - let target = parse_slot(&value)?; + let target = try_future!(parse_slot(&value)); - block_root_at_slot(&beacon_chain, target).ok_or_else(|| { + try_future!(block_root_at_slot(&beacon_chain, target).ok_or_else(|| { ApiError::NotFound(format!("Unable to find BeaconBlock for slot {:?}", target)) - })? + })) + } + ("root", value) => try_future!(parse_root(&value)), + _ => { + return Box::new(futures::future::err(ApiError::ServerError( + "Unexpected query parameter".into(), + ))) } - ("root", value) => parse_root(&value)?, - _ => return Err(ApiError::ServerError("Unexpected query parameter".into())), }; - let block = beacon_chain + let block = try_future!(try_future!(beacon_chain .store - .get::>(&block_root)? - .ok_or_else(|| { - ApiError::NotFound(format!( - "Unable to find BeaconBlock for root {:?}", - block_root - )) - })?; + .get::>(&block_root)) + .ok_or_else(|| { + ApiError::NotFound(format!( + "Unable to find BeaconBlock for root {:?}", + block_root + )) + })); let response = BlockResponse { root: block_root, beacon_block: block, }; - ResponseBuilder::new(&req).body(&response) + success_response(req, &response) } /// HTTP handler to return a `BeaconBlock` root at a given `slot`. -pub fn get_block_root(req: Request) -> ApiResult { - let (beacon_chain, _head_state) = get_beacon_chain_from_request::(&req)?; +pub fn get_block_root(req: Request) -> BoxFut { + let (beacon_chain, _head_state) = try_future!(get_beacon_chain_from_request::(&req)); - let slot_string = UrlQuery::from_request(&req)?.only_one("slot")?; - let target = parse_slot(&slot_string)?; + let slot_string: String = + try_future!(try_future!(UrlQuery::from_request(&req)).only_one("slot")); + let target: Slot = try_future!(parse_slot(&slot_string)); - let root = block_root_at_slot(&beacon_chain, target).ok_or_else(|| { + let root = try_future!(block_root_at_slot(&beacon_chain, target).ok_or_else(|| { ApiError::NotFound(format!("Unable to find BeaconBlock for slot {:?}", target)) - })?; + })); - let json: String = serde_json::to_string(&root) - .map_err(|e| ApiError::ServerError(format!("Unable to serialize root: {:?}", e)))?; - - Ok(success_response_old(Body::from(json))) + success_response(req, &root) } /// HTTP handler to return the `Fork` of the current head. -pub fn get_fork(req: Request) -> ApiResult { - let (_beacon_chain, head_state) = get_beacon_chain_from_request::(&req)?; +pub fn get_fork(req: Request) -> BoxFut { + let (_beacon_chain, head_state) = try_future!(get_beacon_chain_from_request::(&req)); - let json: String = serde_json::to_string(&head_state.fork).map_err(|e| { - ApiError::ServerError(format!("Unable to serialize BeaconState::Fork: {:?}", e)) - })?; - - Ok(success_response_old(Body::from(json))) + success_response(req, &head_state.fork) } /// HTTP handler to return the set of validators for an `Epoch` /// /// 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, _head_state) = get_beacon_chain_from_request::(&req)?; +pub fn get_validators(req: Request) -> BoxFut { + let (beacon_chain, _head_state) = try_future!(get_beacon_chain_from_request::(&req)); 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 - .only_one("epoch")? + Ok(query) => try_future!(try_future!(query.only_one("epoch")) .parse::() .map(Epoch::from) .map_err(|e| { @@ -154,11 +151,11 @@ pub fn get_validators(req: Request) -> ApiR "Invalid epoch parameter, must be a u64. {:?}", e )) - })?, + })), // In this case, our url query did not contain any parameters, so we take the default - Err(_) => beacon_chain.epoch().map_err(|e| { + Err(_) => try_future!(beacon_chain.epoch().map_err(|e| { ApiError::ServerError(format!("Unable to determine current epoch: {:?}", e)) - })?, + })), }; let all_validators = &beacon_chain.head().beacon_state.validators; @@ -168,7 +165,7 @@ pub fn get_validators(req: Request) -> ApiR .cloned() .collect(); - ResponseBuilder::new(&req).body(&active_vals) + success_response(req, &active_vals) } #[derive(Serialize, Encode)] @@ -182,43 +179,42 @@ 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, head_state) = get_beacon_chain_from_request::(&req)?; +pub fn get_state(req: Request) -> BoxFut { + let (beacon_chain, head_state) = try_future!(get_beacon_chain_from_request::(&req)); let (key, value) = match UrlQuery::from_request(&req) { Ok(query) => { - // We have *some* parameters, check them. + // We have *some* parameters, just check them. let query_params = ["root", "slot"]; - match query.first_of(&query_params) { - Ok((k, v)) => (k, v), - Err(e) => { - // Wrong parameters provided, or another error, return the error. - return Err(e); - } - } + try_future!(query.first_of(&query_params)) } Err(ApiError::InvalidQueryParams(_)) => { // No parameters provided at all, use current slot. (String::from("slot"), head_state.slot.to_string()) } Err(e) => { - return Err(e); + return Box::new(futures::future::err(e)); } }; let (root, state): (Hash256, BeaconState) = match (key.as_ref(), value) { - ("slot", value) => state_at_slot(&beacon_chain, parse_slot(&value)?)?, + ("slot", value) => try_future!(state_at_slot( + &beacon_chain, + try_future!(parse_slot(&value)) + )), ("root", value) => { - let root = &parse_root(&value)?; + let root = &try_future!(parse_root(&value)); - let state = beacon_chain - .store - .get(root)? - .ok_or_else(|| ApiError::NotFound(format!("No state for root: {:?}", root)))?; + let state = try_future!(try_future!(beacon_chain.store.get(root)) + .ok_or_else(|| ApiError::NotFound(format!("No state for root: {:?}", root)))); (*root, state) } - _ => return Err(ApiError::ServerError("Unexpected query parameter".into())), + _ => { + return Box::new(futures::future::err(ApiError::ServerError( + "Unexpected query parameter".into(), + ))) + } }; let response = StateResponse { @@ -226,32 +222,29 @@ pub fn get_state(req: Request) -> ApiResult beacon_state: state, }; - ResponseBuilder::new(&req).body(&response) + success_response(req, &response) } /// HTTP handler to return a `BeaconState` root at a given `slot`. /// /// 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, _head_state) = get_beacon_chain_from_request::(&req)?; +pub fn get_state_root(req: Request) -> BoxFut { + let (beacon_chain, _head_state) = try_future!(get_beacon_chain_from_request::(&req)); - let slot_string = UrlQuery::from_request(&req)?.only_one("slot")?; - let slot = parse_slot(&slot_string)?; + let slot_string = try_future!(try_future!(UrlQuery::from_request(&req)).only_one("slot")); + let slot = try_future!(parse_slot(&slot_string)); - let root = state_root_at_slot(&beacon_chain, slot)?; + let root = try_future!(state_root_at_slot(&beacon_chain, slot)); - let json: String = serde_json::to_string(&root) - .map_err(|e| ApiError::ServerError(format!("Unable to serialize root: {:?}", e)))?; - - Ok(success_response_old(Body::from(json))) + success_response(req, &root) } /// HTTP handler to return the highest finalized slot. pub fn get_current_finalized_checkpoint( req: Request, -) -> ApiResult { - let (beacon_chain, _head_state) = get_beacon_chain_from_request::(&req)?; +) -> BoxFut { + let (beacon_chain, _head_state) = try_future!(get_beacon_chain_from_request::(&req)); let checkpoint = beacon_chain .head() @@ -259,17 +252,14 @@ pub fn get_current_finalized_checkpoint( .finalized_checkpoint .clone(); - let json: String = serde_json::to_string(&checkpoint) - .map_err(|e| ApiError::ServerError(format!("Unable to serialize checkpoint: {:?}", e)))?; - - Ok(success_response_old(Body::from(json))) + success_response(req, &checkpoint) } /// HTTP handler to return a `BeaconState` at the genesis block. -pub fn get_genesis_state(req: Request) -> ApiResult { - let (beacon_chain, _head_state) = get_beacon_chain_from_request::(&req)?; +pub fn get_genesis_state(req: Request) -> BoxFut { + let (beacon_chain, _head_state) = try_future!(get_beacon_chain_from_request::(&req)); - let (_root, state) = state_at_slot(&beacon_chain, Slot::new(0))?; + let (_root, state) = try_future!(state_at_slot(&beacon_chain, Slot::new(0))); - ResponseBuilder::new(&req).body(&state) + success_response(req, &state) } diff --git a/beacon_node/rest_api/src/helpers.rs b/beacon_node/rest_api/src/helpers.rs index 006deb2681..9eae4a00a6 100644 --- a/beacon_node/rest_api/src/helpers.rs +++ b/beacon_node/rest_api/src/helpers.rs @@ -227,7 +227,7 @@ pub fn get_beacon_chain_from_request( let beacon_chain = req .extensions() .get::>>() - .ok_or_else(|| ApiError::ServerError("Beacon chain extension missing".into()))?; + .expect("BeaconChain extension must be there, because we put it there."); let mut head_state = beacon_chain .state_now() .map_err(|e| ApiError::ServerError(format!("Unable to get current BeaconState {:?}", e)))?; diff --git a/beacon_node/rest_api/src/lib.rs b/beacon_node/rest_api/src/lib.rs index dc4abc2bf0..e4f88caf38 100644 --- a/beacon_node/rest_api/src/lib.rs +++ b/beacon_node/rest_api/src/lib.rs @@ -1,4 +1,6 @@ #[macro_use] +mod macros; +#[macro_use] extern crate lazy_static; extern crate network as client_network; @@ -91,7 +93,6 @@ impl Service for ApiService { (&Method::GET, "/network/listen_port") => network::get_listen_port::(req), (&Method::GET, "/network/listen_addresses") => network::get_listen_addresses::(req), - /* // Methods for Beacon Node (&Method::GET, "/beacon/head") => beacon::get_head::(req), (&Method::GET, "/beacon/block") => beacon::get_block::(req), @@ -111,6 +112,7 @@ impl Service for ApiService { helpers::implementation_pending_response(req) } + /* // Methods for Validator (&Method::GET, "/beacon/validator/duties") => validator::get_validator_duties::(req), (&Method::GET, "/beacon/validator/block") => validator::get_new_beacon_block::(req), diff --git a/beacon_node/rest_api/src/macros.rs b/beacon_node/rest_api/src/macros.rs new file mode 100644 index 0000000000..e95cfb8aed --- /dev/null +++ b/beacon_node/rest_api/src/macros.rs @@ -0,0 +1,13 @@ +macro_rules! try_future { + ($expr:expr) => { + match $expr { + core::result::Result::Ok(val) => val, + core::result::Result::Err(err) => { + return Box::new(futures::future::err(std::convert::From::from(err))) + } + } + }; + ($expr:expr,) => { + $crate::try_future!($expr) + }; +}