From 2bf0d5c071efee2f24bda10afe5f21ec6a9c4884 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Wed, 14 Aug 2019 11:22:43 +1000 Subject: [PATCH] Add beacon_block methods to rest api --- beacon_node/client/src/local_bootstrap.rs | 22 ++++--- beacon_node/rest_api/src/beacon.rs | 74 ++++++++++++++++++++++- beacon_node/rest_api/src/lib.rs | 2 + 3 files changed, 87 insertions(+), 11 deletions(-) diff --git a/beacon_node/client/src/local_bootstrap.rs b/beacon_node/client/src/local_bootstrap.rs index f38762b3be..79fad7ec2e 100644 --- a/beacon_node/client/src/local_bootstrap.rs +++ b/beacon_node/client/src/local_bootstrap.rs @@ -2,7 +2,7 @@ use reqwest::{Error as HttpError, Url}; use types::{BeaconBlock, BeaconState, Checkpoint, EthSpec, Slot}; #[derive(Debug)] -pub enum Error { +enum Error { UrlCannotBeBase, HttpError(HttpError), } @@ -21,16 +21,22 @@ pub struct BootstrapParams { } impl BootstrapParams { - pub fn from_http_api(url: Url) -> Result { - let slots_per_epoch = get_slots_per_epoch(url.clone())?; + pub fn from_http_api(url: Url) -> Result { + let slots_per_epoch = get_slots_per_epoch(url.clone()) + .map_err(|e| format!("Unable to get slots per epoch: {:?}", e))?; let genesis_slot = Slot::new(0); - let finalized_slot = get_finalized_slot(url.clone(), slots_per_epoch.as_u64())?; + let finalized_slot = get_finalized_slot(url.clone(), slots_per_epoch.as_u64()) + .map_err(|e| format!("Unable to get finalized slot: {:?}", e))?; Ok(Self { - finalized_block: get_block(url.clone(), finalized_slot)?, - finalized_state: get_state(url.clone(), finalized_slot)?, - genesis_block: get_block(url.clone(), genesis_slot)?, - genesis_state: get_state(url.clone(), genesis_slot)?, + finalized_block: get_block(url.clone(), finalized_slot) + .map_err(|e| format!("Unable to get finalized block: {:?}", e))?, + finalized_state: get_state(url.clone(), finalized_slot) + .map_err(|e| format!("Unable to get finalized state: {:?}", e))?, + genesis_block: get_block(url.clone(), genesis_slot) + .map_err(|e| format!("Unable to get genesis block: {:?}", e))?, + genesis_state: get_state(url.clone(), genesis_slot) + .map_err(|e| format!("Unable to get genesis state: {:?}", e))?, }) } } diff --git a/beacon_node/rest_api/src/beacon.rs b/beacon_node/rest_api/src/beacon.rs index 8b089f542b..a2afb10010 100644 --- a/beacon_node/rest_api/src/beacon.rs +++ b/beacon_node/rest_api/src/beacon.rs @@ -4,7 +4,75 @@ use beacon_chain::{BeaconChain, BeaconChainTypes}; use hyper::{Body, Request}; use std::sync::Arc; use store::Store; -use types::BeaconState; +use types::{BeaconBlock, BeaconState}; + +/// 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()))?; + + let query_params = ["root", "slot"]; + let (key, value) = UrlQuery::from_request(&req)?.first_of(&query_params)?; + + let block_root = match (key.as_ref(), value) { + ("slot", value) => { + let target = parse_slot(&value)?; + + beacon_chain + .rev_iter_block_roots() + .take_while(|(_root, slot)| *slot >= target) + .find(|(_root, slot)| *slot == target) + .map(|(root, _slot)| root) + .ok_or_else(|| { + ApiError::NotFound(format!("Unable to find BeaconBlock for slot {}", target)) + })? + } + ("root", value) => parse_root(&value)?, + _ => return Err(ApiError::ServerError("Unexpected query parameter".into())), + }; + + let block = beacon_chain + .store + .get::>(&block_root)? + .ok_or_else(|| { + ApiError::NotFound(format!( + "Unable to find BeaconBlock for root {}", + block_root + )) + })?; + + let json: String = serde_json::to_string(&block) + .map_err(|e| ApiError::ServerError(format!("Unable to serialize BeaconBlock: {:?}", e)))?; + + Ok(success_response(Body::from(json))) +} + +/// HTTP handler to return a `BeaconBlock` root at a given `slot`. +pub fn get_block_root(req: Request) -> ApiResult { + let beacon_chain = req + .extensions() + .get::>>() + .ok_or_else(|| ApiError::ServerError("Beacon chain extension missing".to_string()))?; + + let slot_string = UrlQuery::from_request(&req)?.only_one("slot")?; + let target = parse_slot(&slot_string)?; + + let root = beacon_chain + .rev_iter_block_roots() + .take_while(|(_root, slot)| *slot >= target) + .find(|(_root, slot)| *slot == target) + .map(|(root, _slot)| root) + .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(Body::from(json))) +} /// HTTP handler to return a `BeaconState` at a given `root` or `slot`. /// @@ -29,7 +97,7 @@ pub fn get_state(req: Request) -> ApiResult .get(root)? .ok_or_else(|| ApiError::NotFound(format!("No state for root: {}", root)))? } - _ => unreachable!("Guarded by UrlQuery::from_request()"), + _ => return Err(ApiError::ServerError("Unexpected query parameter".into())), }; let json: String = serde_json::to_string(&state) @@ -38,7 +106,7 @@ pub fn get_state(req: Request) -> ApiResult Ok(success_response(Body::from(json))) } -/// HTTP handler to return a `BeaconState` root at a given or `slot`. +/// 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. diff --git a/beacon_node/rest_api/src/lib.rs b/beacon_node/rest_api/src/lib.rs index 57c5482cd8..4f07b482a4 100644 --- a/beacon_node/rest_api/src/lib.rs +++ b/beacon_node/rest_api/src/lib.rs @@ -102,6 +102,8 @@ pub fn start_server( // Route the request to the correct handler. let result = match (req.method(), path.as_ref()) { + (&Method::GET, "/beacon/block") => beacon::get_block::(req), + (&Method::GET, "/beacon/block_root") => beacon::get_block_root::(req), (&Method::GET, "/beacon/latest_finalized_checkpoint") => { beacon::get_latest_finalized_checkpoint::(req) }