use crate::helpers::*; use crate::response_builder::ResponseBuilder; use crate::{ApiError, ApiResult, BoxFut, NetworkService, UrlQuery}; use beacon_chain::{BeaconChain, BeaconChainTypes}; use hyper::{Body, Request}; use serde::Serialize; use ssz_derive::Encode; use std::sync::Arc; use store::Store; use types::{BeaconBlock, BeaconState, Epoch, EthSpec, Hash256, Slot, Validator}; #[derive(Serialize, Encode)] pub struct HeadResponse { pub slot: Slot, pub block_root: Hash256, pub state_root: Hash256, pub finalized_slot: Slot, pub finalized_block_root: Hash256, pub justified_slot: Slot, pub justified_block_root: Hash256, pub previous_justified_slot: Slot, pub previous_justified_block_root: Hash256, } /// 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()))?; let chain_head = beacon_chain.head(); let head = HeadResponse { slot: chain_head.beacon_state.slot, block_root: chain_head.beacon_block_root, state_root: chain_head.beacon_state_root, finalized_slot: chain_head .beacon_state .finalized_checkpoint .epoch .start_slot(T::EthSpec::slots_per_epoch()), finalized_block_root: chain_head.beacon_state.finalized_checkpoint.root, justified_slot: chain_head .beacon_state .current_justified_checkpoint .epoch .start_slot(T::EthSpec::slots_per_epoch()), justified_block_root: chain_head.beacon_state.current_justified_checkpoint.root, previous_justified_slot: chain_head .beacon_state .previous_justified_checkpoint .epoch .start_slot(T::EthSpec::slots_per_epoch()), 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))) } #[derive(Serialize, Encode)] #[serde(bound = "T: EthSpec")] pub struct BlockResponse { pub root: Hash256, pub beacon_block: BeaconBlock, } /// 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)?; block_root_at_slot(&beacon_chain, target).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 response = BlockResponse { root: block_root, beacon_block: block, }; ResponseBuilder::new(&req).body(&response) } /// 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)?; let slot_string = UrlQuery::from_request(&req)?.only_one("slot")?; let target = parse_slot(&slot_string)?; let root = 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))) } /// 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)?; ResponseBuilder::new(&req).body(&beacon_chain.head().beacon_state) } /// 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 = 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")? .parse::() .map(Epoch::from) .map_err(|e| { ApiError::InvalidQueryParams(format!( "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| { ApiError::ServerError(format!("Unable to determine current epoch: {:?}", e)) })?, }; let all_validators = &beacon_chain.head().beacon_state.validators; let active_vals: Vec = all_validators .iter() .filter(|v| v.is_active_at(epoch)) .cloned() .collect(); ResponseBuilder::new(&req).body(&active_vals) } #[derive(Serialize, Encode)] #[serde(bound = "T: EthSpec")] pub struct StateResponse { pub root: Hash256, pub beacon_state: BeaconState, } /// HTTP handler to return a `BeaconState` at a given `root` or `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(req: Request) -> ApiResult { let beacon_chain = get_beacon_chain_from_request::(&req)?; let head_state = beacon_chain.head().beacon_state; let (key, value) = match UrlQuery::from_request(&req) { Ok(query) => { // We have *some* parameters, just check them. let query_params = ["root", "slot"]; 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); } }; let (root, state): (Hash256, BeaconState) = match (key.as_ref(), value) { ("slot", value) => state_at_slot(&beacon_chain, parse_slot(&value)?)?, ("root", value) => { let root = &parse_root(&value)?; let state = 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())), }; let response = StateResponse { root, beacon_state: state, }; ResponseBuilder::new(&req).body(&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 = get_beacon_chain_from_request::(&req)?; let slot_string = UrlQuery::from_request(&req)?.only_one("slot")?; let slot = parse_slot(&slot_string)?; let root = 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))) } /// HTTP handler to return the highest finalized slot. pub fn get_current_finalized_checkpoint( req: Request, ) -> 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(); 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))) } /// 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)?; let (_root, state) = state_at_slot(&beacon_chain, Slot::new(0))?; ResponseBuilder::new(&req).body(&state) }