diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index 5feefd8417..500f6411f1 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -983,20 +983,34 @@ impl BeaconChain { Ok(BlockProcessingOutcome::Processed { block_root }) } - /// Produce a new block at the present slot. + /// Produce a new block at the specified slot. /// /// The produced block will not be inherently valid, it must be signed by a block producer. /// Block signing is out of the scope of this function and should be done by a separate program. pub fn produce_block( &self, randao_reveal: Signature, + slot: Slot, ) -> Result<(BeaconBlock, BeaconState), BlockProductionError> { let state = self.state.read().clone(); + + self.produce_block_on_state(state, slot, randao_reveal) + } + + /// Produce a new block at the current slot + /// + /// Calls `produce_block`, with the slot parameter set as the current. + /// + /// ** This function is probably obsolete (was for previous RPC), and can probably be removed ** + pub fn produce_current_block( + &self, + randao_reveal: Signature, + ) -> Result<(BeaconBlock, BeaconState), BlockProductionError> { let slot = self .read_slot_clock() .ok_or_else(|| BlockProductionError::UnableToReadSlot)?; - self.produce_block_on_state(state, slot, randao_reveal) + self.produce_block(randao_reveal, slot) } /// Produce a block for some `slot` upon the given `state`. diff --git a/beacon_node/rest_api/src/beacon.rs b/beacon_node/rest_api/src/beacon.rs index b489f1fe76..5dcbc728a2 100644 --- a/beacon_node/rest_api/src/beacon.rs +++ b/beacon_node/rest_api/src/beacon.rs @@ -130,6 +130,22 @@ pub fn get_block_root(req: Request) -> ApiR Ok(success_response(Body::from(json))) } +/// HTTP handler to return the `Fork` of the current head. +pub fn get_fork(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 json: String = serde_json::to_string(&chain_head.beacon_state.fork).map_err(|e| { + ApiError::ServerError(format!("Unable to serialize BeaconState::Fork: {:?}", e)) + })?; + + Ok(success_response(Body::from(json))) +} + #[derive(Serialize)] #[serde(bound = "T: EthSpec")] pub struct StateResponse { diff --git a/beacon_node/rest_api/src/helpers.rs b/beacon_node/rest_api/src/helpers.rs index 88755fcde0..2477884c47 100644 --- a/beacon_node/rest_api/src/helpers.rs +++ b/beacon_node/rest_api/src/helpers.rs @@ -2,9 +2,7 @@ use crate::{ApiError, ApiResult}; use beacon_chain::{BeaconChain, BeaconChainTypes}; use bls::PublicKey; use hex; -use hyper::{Body, Request, StatusCode}; -use serde::de::value::StringDeserializer; -use serde_json::Deserializer; +use hyper::{Body, Request}; use store::{iter::AncestorIter, Store}; use types::{BeaconState, EthSpec, Hash256, RelativeEpoch, Slot}; diff --git a/beacon_node/rest_api/src/lib.rs b/beacon_node/rest_api/src/lib.rs index 99a8c63438..a6ee948ae6 100644 --- a/beacon_node/rest_api/src/lib.rs +++ b/beacon_node/rest_api/src/lib.rs @@ -139,16 +139,13 @@ pub fn start_server( (&Method::GET, "/network/listen_addresses") => { network::get_listen_addresses::(req) } - (&Method::GET, "/network/block_discovery") => { - helpers::implementation_pending_response(req) - } // Methods for Beacon Node (&Method::GET, "/beacon/head") => beacon::get_head::(req), (&Method::GET, "/beacon/block") => beacon::get_block::(req), (&Method::GET, "/beacon/block_root") => beacon::get_block_root::(req), (&Method::GET, "/beacon/blocks") => helpers::implementation_pending_response(req), - (&Method::GET, "/beacon/fork") => helpers::implementation_pending_response(req), + (&Method::GET, "/beacon/fork") => beacon::get_fork::(req), (&Method::GET, "/beacon/attestations") => { helpers::implementation_pending_response(req) } @@ -171,7 +168,7 @@ pub fn start_server( validator::get_validator_duties::(req) } (&Method::GET, "/beacon/validator/block") => { - helpers::implementation_pending_response(req) + validator::get_new_beacon_block::(req) } (&Method::POST, "/beacon/validator/block") => { helpers::implementation_pending_response(req) diff --git a/beacon_node/rest_api/src/url_query.rs b/beacon_node/rest_api/src/url_query.rs index e39a9a4499..3802ff8317 100644 --- a/beacon_node/rest_api/src/url_query.rs +++ b/beacon_node/rest_api/src/url_query.rs @@ -64,7 +64,7 @@ impl<'a> UrlQuery<'a> { /// Returns a vector of all values present where `key` is in `keys /// /// If no match is found, an `InvalidQueryParams` error is returned. - pub fn all_of(mut self, key: &str) -> Result, ApiError> { + pub fn all_of(self, key: &str) -> Result, ApiError> { let queries: Vec<_> = self .0 .filter_map(|(k, v)| { diff --git a/beacon_node/rest_api/src/validator.rs b/beacon_node/rest_api/src/validator.rs index 4294f9c20b..645a358373 100644 --- a/beacon_node/rest_api/src/validator.rs +++ b/beacon_node/rest_api/src/validator.rs @@ -1,13 +1,12 @@ use super::{success_response, ApiResult}; use crate::{helpers::*, ApiError, UrlQuery}; use beacon_chain::{BeaconChain, BeaconChainTypes}; -use bls::PublicKey; +use bls::{PublicKey, Signature}; use hyper::{Body, Request}; use serde::{Deserialize, Serialize}; use std::sync::Arc; -use store::Store; use types::beacon_state::EthSpec; -use types::{BeaconBlock, BeaconState, Epoch, RelativeEpoch, Shard, Slot}; +use types::{Epoch, RelativeEpoch, Shard, Slot}; #[derive(Debug, Serialize, Deserialize)] pub struct ValidatorDuty { @@ -39,16 +38,14 @@ pub fn get_validator_duties(req: Request) - .extensions() .get::>>() .ok_or_else(|| ApiError::ServerError("Beacon chain extension missing".to_string()))?; + //TODO Surely this state_cache thing is not necessary? let _ = beacon_chain .ensure_state_caches_are_built() .map_err(|e| ApiError::ServerError(format!("Unable to build state caches: {:?}", e)))?; - let head_state = beacon_chain - .speculative_state() - .expect("This is legacy code and should be removed."); + let head_state = &beacon_chain.head().beacon_state; // Parse and check query parameters let query = UrlQuery::from_request(&req)?; - let current_epoch = head_state.current_epoch(); let epoch = match query.first_of(&["epoch"]) { Ok((_, v)) => Epoch::new(v.parse::().map_err(|e| { @@ -66,7 +63,7 @@ pub fn get_validator_duties(req: Request) - )) })?; //TODO: Handle an array of validators, currently only takes one - let mut validators: Vec = match query.all_of("validator_pubkeys") { + let validators: Vec = match query.all_of("validator_pubkeys") { Ok(v) => v .iter() .map(|pk| parse_pubkey(pk)) @@ -147,3 +144,60 @@ pub fn get_validator_duties(req: Request) - ); Ok(success_response(body)) } + +/// 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 { + // Get beacon state + let beacon_chain = req + .extensions() + .get::>>() + .ok_or_else(|| ApiError::ServerError("Beacon chain extension missing".to_string()))?; + //TODO Surely this state_cache thing is not necessary? + let _ = beacon_chain + .ensure_state_caches_are_built() + .map_err(|e| ApiError::ServerError(format!("Unable to build state caches: {:?}", e)))?; + + let query = UrlQuery::from_request(&req)?; + let slot = match query.first_of(&["slot"]) { + Ok((_, v)) => Slot::new(v.parse::().map_err(|e| { + ApiError::InvalidQueryParams(format!("Invalid slot parameter, must be a u64. {:?}", e)) + })?), + Err(e) => { + return Err(e); + } + }; + let randao_reveal = match query.first_of(&["randao_reveal"]) { + Ok((_, v)) => Signature::from_bytes( + hex::decode(&v) + .map_err(|e| { + ApiError::InvalidQueryParams(format!( + "Invalid hex string for randao_reveal: {:?}", + e + )) + })? + .as_slice(), + ) + .map_err(|e| { + ApiError::InvalidQueryParams(format!("randao_reveal is not a valid signature: {:?}", e)) + })?, + Err(e) => { + return Err(e); + } + }; + + let new_block = match beacon_chain.produce_block(randao_reveal, slot) { + Ok((block, _state)) => block, + Err(e) => { + return Err(ApiError::ServerError(format!( + "Beacon node is not able to produce a block: {:?}", + e + ))); + } + }; + + let body = Body::from( + serde_json::to_string(&new_block) + .expect("We should always be able to serialize a new block that we created."), + ); + Ok(success_response(body)) +} diff --git a/beacon_node/rpc/src/beacon_block.rs b/beacon_node/rpc/src/beacon_block.rs index b1a67399e2..012fcb678d 100644 --- a/beacon_node/rpc/src/beacon_block.rs +++ b/beacon_node/rpc/src/beacon_block.rs @@ -51,7 +51,7 @@ impl BeaconBlockService for BeaconBlockServiceInstance { } }; - let produced_block = match self.chain.produce_block(randao_reveal) { + let produced_block = match self.chain.produce_current_block(randao_reveal) { Ok((block, _state)) => block, Err(e) => { // could not produce a block