Shift HTTP server heavy-lifting to blocking executor (#1518)
## Issue Addressed NA ## Proposed Changes Shift practically all HTTP endpoint handlers to the blocking executor (some very light tasks are left on the core executor). ## Additional Info This PR covers the `rest_api` which will soon be refactored to suit the standard API. As such, I've cut a few corners and left some existing issues open in this patch. What I have done here should leave the API in state that is not necessary *exactly* the same, but good enough for us to run validators with. Specifically, the number of blocking workers that can be spawned is unbounded and I have not implemented a queue; this will need to be fixed when we implement the standard API.
This commit is contained in:
parent
2bc9115a94
commit
c895dc8971
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -4308,15 +4308,22 @@ dependencies = [
|
|||||||
name = "rest_types"
|
name = "rest_types"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"beacon_chain",
|
||||||
"bls",
|
"bls",
|
||||||
|
"environment",
|
||||||
"eth2_hashing",
|
"eth2_hashing",
|
||||||
"eth2_ssz",
|
"eth2_ssz",
|
||||||
"eth2_ssz_derive",
|
"eth2_ssz_derive",
|
||||||
|
"hyper 0.13.7",
|
||||||
"procinfo",
|
"procinfo",
|
||||||
"psutil",
|
"psutil",
|
||||||
"rayon",
|
"rayon",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde_yaml",
|
||||||
"state_processing",
|
"state_processing",
|
||||||
|
"store",
|
||||||
|
"tokio 0.2.22",
|
||||||
"tree_hash",
|
"tree_hash",
|
||||||
"types",
|
"types",
|
||||||
]
|
]
|
||||||
|
@ -1,34 +0,0 @@
|
|||||||
use crate::response_builder::ResponseBuilder;
|
|
||||||
use crate::ApiResult;
|
|
||||||
use beacon_chain::{BeaconChain, BeaconChainTypes};
|
|
||||||
use hyper::{Body, Request};
|
|
||||||
use operation_pool::PersistedOperationPool;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
/// Returns the `proto_array` fork choice struct, encoded as JSON.
|
|
||||||
///
|
|
||||||
/// Useful for debugging or advanced inspection of the chain.
|
|
||||||
pub fn get_fork_choice<T: BeaconChainTypes>(
|
|
||||||
req: Request<Body>,
|
|
||||||
beacon_chain: Arc<BeaconChain<T>>,
|
|
||||||
) -> ApiResult {
|
|
||||||
ResponseBuilder::new(&req)?.body_no_ssz(
|
|
||||||
&*beacon_chain
|
|
||||||
.fork_choice
|
|
||||||
.read()
|
|
||||||
.proto_array()
|
|
||||||
.core_proto_array(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the `PersistedOperationPool` struct.
|
|
||||||
///
|
|
||||||
/// Useful for debugging or advanced inspection of the stored operations.
|
|
||||||
pub fn get_operation_pool<T: BeaconChainTypes>(
|
|
||||||
req: Request<Body>,
|
|
||||||
beacon_chain: Arc<BeaconChain<T>>,
|
|
||||||
) -> ApiResult {
|
|
||||||
ResponseBuilder::new(&req)?.body(&PersistedOperationPool::from_operation_pool(
|
|
||||||
&beacon_chain.op_pool,
|
|
||||||
))
|
|
||||||
}
|
|
@ -1,14 +1,13 @@
|
|||||||
use crate::helpers::*;
|
use crate::helpers::*;
|
||||||
use crate::response_builder::ResponseBuilder;
|
|
||||||
use crate::validator::get_state_for_epoch;
|
use crate::validator::get_state_for_epoch;
|
||||||
use crate::{ApiError, ApiResult, UrlQuery};
|
use crate::Context;
|
||||||
|
use crate::{ApiError, UrlQuery};
|
||||||
use beacon_chain::{
|
use beacon_chain::{
|
||||||
observed_operations::ObservationOutcome, BeaconChain, BeaconChainTypes, StateSkipConfig,
|
observed_operations::ObservationOutcome, BeaconChain, BeaconChainTypes, StateSkipConfig,
|
||||||
};
|
};
|
||||||
use bus::BusReader;
|
|
||||||
use futures::executor::block_on;
|
use futures::executor::block_on;
|
||||||
use hyper::body::Bytes;
|
use hyper::body::Bytes;
|
||||||
use hyper::{Body, Request, Response};
|
use hyper::{Body, Request};
|
||||||
use rest_types::{
|
use rest_types::{
|
||||||
BlockResponse, CanonicalHeadResponse, Committee, HeadBeaconBlock, StateResponse,
|
BlockResponse, CanonicalHeadResponse, Committee, HeadBeaconBlock, StateResponse,
|
||||||
ValidatorRequest, ValidatorResponse,
|
ValidatorRequest, ValidatorResponse,
|
||||||
@ -16,20 +15,20 @@ use rest_types::{
|
|||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use slog::{error, Logger};
|
use slog::error;
|
||||||
use types::{
|
use types::{
|
||||||
AttesterSlashing, BeaconState, EthSpec, Hash256, ProposerSlashing, PublicKeyBytes,
|
AttesterSlashing, BeaconState, EthSpec, Hash256, ProposerSlashing, PublicKeyBytes,
|
||||||
RelativeEpoch, SignedBeaconBlockHash, Slot,
|
RelativeEpoch, SignedBeaconBlockHash, Slot,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// HTTP handler to return a `BeaconBlock` at a given `root` or `slot`.
|
/// Returns a summary of the head of the beacon chain.
|
||||||
pub fn get_head<T: BeaconChainTypes>(
|
pub fn get_head<T: BeaconChainTypes>(
|
||||||
req: Request<Body>,
|
ctx: Arc<Context<T>>,
|
||||||
beacon_chain: Arc<BeaconChain<T>>,
|
) -> Result<CanonicalHeadResponse, ApiError> {
|
||||||
) -> ApiResult {
|
let beacon_chain = &ctx.beacon_chain;
|
||||||
let chain_head = beacon_chain.head()?;
|
let chain_head = beacon_chain.head()?;
|
||||||
|
|
||||||
let head = CanonicalHeadResponse {
|
Ok(CanonicalHeadResponse {
|
||||||
slot: chain_head.beacon_state.slot,
|
slot: chain_head.beacon_state.slot,
|
||||||
block_root: chain_head.beacon_block_root,
|
block_root: chain_head.beacon_block_root,
|
||||||
state_root: chain_head.beacon_state_root,
|
state_root: chain_head.beacon_state_root,
|
||||||
@ -51,33 +50,27 @@ pub fn get_head<T: BeaconChainTypes>(
|
|||||||
.epoch
|
.epoch
|
||||||
.start_slot(T::EthSpec::slots_per_epoch()),
|
.start_slot(T::EthSpec::slots_per_epoch()),
|
||||||
previous_justified_block_root: chain_head.beacon_state.previous_justified_checkpoint.root,
|
previous_justified_block_root: chain_head.beacon_state.previous_justified_checkpoint.root,
|
||||||
};
|
})
|
||||||
|
|
||||||
ResponseBuilder::new(&req)?.body(&head)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// HTTP handler to return a list of head BeaconBlocks.
|
/// Return the list of heads of the beacon chain.
|
||||||
pub fn get_heads<T: BeaconChainTypes>(
|
pub fn get_heads<T: BeaconChainTypes>(ctx: Arc<Context<T>>) -> Vec<HeadBeaconBlock> {
|
||||||
req: Request<Body>,
|
ctx.beacon_chain
|
||||||
beacon_chain: Arc<BeaconChain<T>>,
|
|
||||||
) -> ApiResult {
|
|
||||||
let heads = beacon_chain
|
|
||||||
.heads()
|
.heads()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(beacon_block_root, beacon_block_slot)| HeadBeaconBlock {
|
.map(|(beacon_block_root, beacon_block_slot)| HeadBeaconBlock {
|
||||||
beacon_block_root,
|
beacon_block_root,
|
||||||
beacon_block_slot,
|
beacon_block_slot,
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect()
|
||||||
|
|
||||||
ResponseBuilder::new(&req)?.body(&heads)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// HTTP handler to return a `BeaconBlock` at a given `root` or `slot`.
|
/// HTTP handler to return a `BeaconBlock` at a given `root` or `slot`.
|
||||||
pub fn get_block<T: BeaconChainTypes>(
|
pub fn get_block<T: BeaconChainTypes>(
|
||||||
req: Request<Body>,
|
req: Request<Vec<u8>>,
|
||||||
beacon_chain: Arc<BeaconChain<T>>,
|
ctx: Arc<Context<T>>,
|
||||||
) -> ApiResult {
|
) -> Result<BlockResponse<T::EthSpec>, ApiError> {
|
||||||
|
let beacon_chain = &ctx.beacon_chain;
|
||||||
let query_params = ["root", "slot"];
|
let query_params = ["root", "slot"];
|
||||||
let (key, value) = UrlQuery::from_request(&req)?.first_of(&query_params)?;
|
let (key, value) = UrlQuery::from_request(&req)?.first_of(&query_params)?;
|
||||||
|
|
||||||
@ -85,7 +78,7 @@ pub fn get_block<T: BeaconChainTypes>(
|
|||||||
("slot", value) => {
|
("slot", value) => {
|
||||||
let target = parse_slot(&value)?;
|
let target = parse_slot(&value)?;
|
||||||
|
|
||||||
block_root_at_slot(&beacon_chain, target)?.ok_or_else(|| {
|
block_root_at_slot(beacon_chain, target)?.ok_or_else(|| {
|
||||||
ApiError::NotFound(format!(
|
ApiError::NotFound(format!(
|
||||||
"Unable to find SignedBeaconBlock for slot {:?}",
|
"Unable to find SignedBeaconBlock for slot {:?}",
|
||||||
target
|
target
|
||||||
@ -103,30 +96,26 @@ pub fn get_block<T: BeaconChainTypes>(
|
|||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let response = BlockResponse {
|
Ok(BlockResponse {
|
||||||
root: block_root,
|
root: block_root,
|
||||||
beacon_block: block,
|
beacon_block: block,
|
||||||
};
|
})
|
||||||
|
|
||||||
ResponseBuilder::new(&req)?.body(&response)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// HTTP handler to return a `SignedBeaconBlock` root at a given `slot`.
|
/// HTTP handler to return a `SignedBeaconBlock` root at a given `slot`.
|
||||||
pub fn get_block_root<T: BeaconChainTypes>(
|
pub fn get_block_root<T: BeaconChainTypes>(
|
||||||
req: Request<Body>,
|
req: Request<Vec<u8>>,
|
||||||
beacon_chain: Arc<BeaconChain<T>>,
|
ctx: Arc<Context<T>>,
|
||||||
) -> ApiResult {
|
) -> Result<Hash256, ApiError> {
|
||||||
let slot_string = UrlQuery::from_request(&req)?.only_one("slot")?;
|
let slot_string = UrlQuery::from_request(&req)?.only_one("slot")?;
|
||||||
let target = parse_slot(&slot_string)?;
|
let target = parse_slot(&slot_string)?;
|
||||||
|
|
||||||
let root = block_root_at_slot(&beacon_chain, target)?.ok_or_else(|| {
|
block_root_at_slot(&ctx.beacon_chain, target)?.ok_or_else(|| {
|
||||||
ApiError::NotFound(format!(
|
ApiError::NotFound(format!(
|
||||||
"Unable to find SignedBeaconBlock for slot {:?}",
|
"Unable to find SignedBeaconBlock for slot {:?}",
|
||||||
target
|
target
|
||||||
))
|
))
|
||||||
})?;
|
})
|
||||||
|
|
||||||
ResponseBuilder::new(&req)?.body(&root)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_sse_response_chunk(new_head_hash: SignedBeaconBlockHash) -> std::io::Result<Bytes> {
|
fn make_sse_response_chunk(new_head_hash: SignedBeaconBlockHash) -> std::io::Result<Bytes> {
|
||||||
@ -140,45 +129,27 @@ fn make_sse_response_chunk(new_head_hash: SignedBeaconBlockHash) -> std::io::Res
|
|||||||
Ok(bytes)
|
Ok(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stream_forks<T: BeaconChainTypes>(
|
pub fn stream_forks<T: BeaconChainTypes>(ctx: Arc<Context<T>>) -> Result<Body, ApiError> {
|
||||||
log: Logger,
|
let mut events = ctx.events.lock().add_rx();
|
||||||
mut events: BusReader<SignedBeaconBlockHash>,
|
|
||||||
) -> ApiResult {
|
|
||||||
let (mut sender, body) = Body::channel();
|
let (mut sender, body) = Body::channel();
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
while let Ok(new_head_hash) = events.recv() {
|
while let Ok(new_head_hash) = events.recv() {
|
||||||
let chunk = match make_sse_response_chunk(new_head_hash) {
|
let chunk = match make_sse_response_chunk(new_head_hash) {
|
||||||
Ok(chunk) => chunk,
|
Ok(chunk) => chunk,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!(log, "Failed to make SSE chunk"; "error" => e.to_string());
|
error!(ctx.log, "Failed to make SSE chunk"; "error" => e.to_string());
|
||||||
sender.abort();
|
sender.abort();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
match block_on(sender.send_data(chunk)) {
|
match block_on(sender.send_data(chunk)) {
|
||||||
Err(e) if e.is_closed() => break,
|
Err(e) if e.is_closed() => break,
|
||||||
Err(e) => error!(log, "Couldn't stream piece {:?}", e),
|
Err(e) => error!(ctx.log, "Couldn't stream piece {:?}", e),
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let response = Response::builder()
|
Ok(body)
|
||||||
.status(200)
|
|
||||||
.header("Content-Type", "text/event-stream")
|
|
||||||
.header("Connection", "Keep-Alive")
|
|
||||||
.header("Cache-Control", "no-cache")
|
|
||||||
.header("Access-Control-Allow-Origin", "*")
|
|
||||||
.body(body)
|
|
||||||
.map_err(|e| ApiError::ServerError(format!("Failed to build response: {:?}", e)))?;
|
|
||||||
Ok(response)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// HTTP handler to return the `Fork` of the current head.
|
|
||||||
pub fn get_fork<T: BeaconChainTypes>(
|
|
||||||
req: Request<Body>,
|
|
||||||
beacon_chain: Arc<BeaconChain<T>>,
|
|
||||||
) -> ApiResult {
|
|
||||||
ResponseBuilder::new(&req)?.body(&beacon_chain.head()?.beacon_state.fork)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// HTTP handler to which accepts a query string of a list of validator pubkeys and maps it to a
|
/// HTTP handler to which accepts a query string of a list of validator pubkeys and maps it to a
|
||||||
@ -187,9 +158,9 @@ pub fn get_fork<T: BeaconChainTypes>(
|
|||||||
/// This method is limited to as many `pubkeys` that can fit in a URL. See `post_validators` for
|
/// This method is limited to as many `pubkeys` that can fit in a URL. See `post_validators` for
|
||||||
/// doing bulk requests.
|
/// doing bulk requests.
|
||||||
pub fn get_validators<T: BeaconChainTypes>(
|
pub fn get_validators<T: BeaconChainTypes>(
|
||||||
req: Request<Body>,
|
req: Request<Vec<u8>>,
|
||||||
beacon_chain: Arc<BeaconChain<T>>,
|
ctx: Arc<Context<T>>,
|
||||||
) -> ApiResult {
|
) -> Result<Vec<ValidatorResponse>, ApiError> {
|
||||||
let query = UrlQuery::from_request(&req)?;
|
let query = UrlQuery::from_request(&req)?;
|
||||||
|
|
||||||
let validator_pubkeys = query
|
let validator_pubkeys = query
|
||||||
@ -204,17 +175,14 @@ pub fn get_validators<T: BeaconChainTypes>(
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let validators =
|
validator_responses_by_pubkey(&ctx.beacon_chain, state_root_opt, validator_pubkeys)
|
||||||
validator_responses_by_pubkey(beacon_chain, state_root_opt, validator_pubkeys)?;
|
|
||||||
|
|
||||||
ResponseBuilder::new(&req)?.body(&validators)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// HTTP handler to return all validators, each as a `ValidatorResponse`.
|
/// HTTP handler to return all validators, each as a `ValidatorResponse`.
|
||||||
pub fn get_all_validators<T: BeaconChainTypes>(
|
pub fn get_all_validators<T: BeaconChainTypes>(
|
||||||
req: Request<Body>,
|
req: Request<Vec<u8>>,
|
||||||
beacon_chain: Arc<BeaconChain<T>>,
|
ctx: Arc<Context<T>>,
|
||||||
) -> ApiResult {
|
) -> Result<Vec<ValidatorResponse>, ApiError> {
|
||||||
let query = UrlQuery::from_request(&req)?;
|
let query = UrlQuery::from_request(&req)?;
|
||||||
|
|
||||||
let state_root_opt = if let Some((_key, value)) = query.first_of_opt(&["state_root"]) {
|
let state_root_opt = if let Some((_key, value)) = query.first_of_opt(&["state_root"]) {
|
||||||
@ -223,23 +191,21 @@ pub fn get_all_validators<T: BeaconChainTypes>(
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut state = get_state_from_root_opt(&beacon_chain, state_root_opt)?;
|
let mut state = get_state_from_root_opt(&ctx.beacon_chain, state_root_opt)?;
|
||||||
state.update_pubkey_cache()?;
|
state.update_pubkey_cache()?;
|
||||||
|
|
||||||
let validators = state
|
state
|
||||||
.validators
|
.validators
|
||||||
.iter()
|
.iter()
|
||||||
.map(|validator| validator_response_by_pubkey(&state, validator.pubkey.clone()))
|
.map(|validator| validator_response_by_pubkey(&state, validator.pubkey.clone()))
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()
|
||||||
|
|
||||||
ResponseBuilder::new(&req)?.body(&validators)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// HTTP handler to return all active validators, each as a `ValidatorResponse`.
|
/// HTTP handler to return all active validators, each as a `ValidatorResponse`.
|
||||||
pub fn get_active_validators<T: BeaconChainTypes>(
|
pub fn get_active_validators<T: BeaconChainTypes>(
|
||||||
req: Request<Body>,
|
req: Request<Vec<u8>>,
|
||||||
beacon_chain: Arc<BeaconChain<T>>,
|
ctx: Arc<Context<T>>,
|
||||||
) -> ApiResult {
|
) -> Result<Vec<ValidatorResponse>, ApiError> {
|
||||||
let query = UrlQuery::from_request(&req)?;
|
let query = UrlQuery::from_request(&req)?;
|
||||||
|
|
||||||
let state_root_opt = if let Some((_key, value)) = query.first_of_opt(&["state_root"]) {
|
let state_root_opt = if let Some((_key, value)) = query.first_of_opt(&["state_root"]) {
|
||||||
@ -248,17 +214,15 @@ pub fn get_active_validators<T: BeaconChainTypes>(
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut state = get_state_from_root_opt(&beacon_chain, state_root_opt)?;
|
let mut state = get_state_from_root_opt(&ctx.beacon_chain, state_root_opt)?;
|
||||||
state.update_pubkey_cache()?;
|
state.update_pubkey_cache()?;
|
||||||
|
|
||||||
let validators = state
|
state
|
||||||
.validators
|
.validators
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|validator| validator.is_active_at(state.current_epoch()))
|
.filter(|validator| validator.is_active_at(state.current_epoch()))
|
||||||
.map(|validator| validator_response_by_pubkey(&state, validator.pubkey.clone()))
|
.map(|validator| validator_response_by_pubkey(&state, validator.pubkey.clone()))
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()
|
||||||
|
|
||||||
ResponseBuilder::new(&req)?.body(&validators)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// HTTP handler to which accepts a `ValidatorRequest` and returns a `ValidatorResponse` for
|
/// HTTP handler to which accepts a `ValidatorRequest` and returns a `ValidatorResponse` for
|
||||||
@ -266,17 +230,11 @@ pub fn get_active_validators<T: BeaconChainTypes>(
|
|||||||
///
|
///
|
||||||
/// This method allows for a basically unbounded list of `pubkeys`, where as the `get_validators`
|
/// This method allows for a basically unbounded list of `pubkeys`, where as the `get_validators`
|
||||||
/// request is limited by the max number of pubkeys you can fit in a URL.
|
/// request is limited by the max number of pubkeys you can fit in a URL.
|
||||||
pub async fn post_validators<T: BeaconChainTypes>(
|
pub fn post_validators<T: BeaconChainTypes>(
|
||||||
req: Request<Body>,
|
req: Request<Vec<u8>>,
|
||||||
beacon_chain: Arc<BeaconChain<T>>,
|
ctx: Arc<Context<T>>,
|
||||||
) -> ApiResult {
|
) -> Result<Vec<ValidatorResponse>, ApiError> {
|
||||||
let response_builder = ResponseBuilder::new(&req);
|
serde_json::from_slice::<ValidatorRequest>(&req.into_body())
|
||||||
|
|
||||||
let body = req.into_body();
|
|
||||||
let chunks = hyper::body::to_bytes(body)
|
|
||||||
.await
|
|
||||||
.map_err(|e| ApiError::ServerError(format!("Unable to get request body: {:?}", e)))?;
|
|
||||||
serde_json::from_slice::<ValidatorRequest>(&chunks)
|
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
ApiError::BadRequest(format!(
|
ApiError::BadRequest(format!(
|
||||||
"Unable to parse JSON into ValidatorRequest: {:?}",
|
"Unable to parse JSON into ValidatorRequest: {:?}",
|
||||||
@ -285,12 +243,11 @@ pub async fn post_validators<T: BeaconChainTypes>(
|
|||||||
})
|
})
|
||||||
.and_then(|bulk_request| {
|
.and_then(|bulk_request| {
|
||||||
validator_responses_by_pubkey(
|
validator_responses_by_pubkey(
|
||||||
beacon_chain,
|
&ctx.beacon_chain,
|
||||||
bulk_request.state_root,
|
bulk_request.state_root,
|
||||||
bulk_request.pubkeys,
|
bulk_request.pubkeys,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.and_then(|validators| response_builder?.body(&validators))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns either the state given by `state_root_opt`, or the canonical head state if it is
|
/// Returns either the state given by `state_root_opt`, or the canonical head state if it is
|
||||||
@ -317,11 +274,11 @@ fn get_state_from_root_opt<T: BeaconChainTypes>(
|
|||||||
/// Maps a vec of `validator_pubkey` to a vec of `ValidatorResponse`, using the state at the given
|
/// Maps a vec of `validator_pubkey` to a vec of `ValidatorResponse`, using the state at the given
|
||||||
/// `state_root`. If `state_root.is_none()`, uses the canonial head state.
|
/// `state_root`. If `state_root.is_none()`, uses the canonial head state.
|
||||||
fn validator_responses_by_pubkey<T: BeaconChainTypes>(
|
fn validator_responses_by_pubkey<T: BeaconChainTypes>(
|
||||||
beacon_chain: Arc<BeaconChain<T>>,
|
beacon_chain: &BeaconChain<T>,
|
||||||
state_root_opt: Option<Hash256>,
|
state_root_opt: Option<Hash256>,
|
||||||
validator_pubkeys: Vec<PublicKeyBytes>,
|
validator_pubkeys: Vec<PublicKeyBytes>,
|
||||||
) -> Result<Vec<ValidatorResponse>, ApiError> {
|
) -> Result<Vec<ValidatorResponse>, ApiError> {
|
||||||
let mut state = get_state_from_root_opt(&beacon_chain, state_root_opt)?;
|
let mut state = get_state_from_root_opt(beacon_chain, state_root_opt)?;
|
||||||
state.update_pubkey_cache()?;
|
state.update_pubkey_cache()?;
|
||||||
|
|
||||||
validator_pubkeys
|
validator_pubkeys
|
||||||
@ -372,24 +329,25 @@ fn validator_response_by_pubkey<E: EthSpec>(
|
|||||||
|
|
||||||
/// HTTP handler
|
/// HTTP handler
|
||||||
pub fn get_committees<T: BeaconChainTypes>(
|
pub fn get_committees<T: BeaconChainTypes>(
|
||||||
req: Request<Body>,
|
req: Request<Vec<u8>>,
|
||||||
beacon_chain: Arc<BeaconChain<T>>,
|
ctx: Arc<Context<T>>,
|
||||||
) -> ApiResult {
|
) -> Result<Vec<Committee>, ApiError> {
|
||||||
let query = UrlQuery::from_request(&req)?;
|
let query = UrlQuery::from_request(&req)?;
|
||||||
|
|
||||||
let epoch = query.epoch()?;
|
let epoch = query.epoch()?;
|
||||||
|
|
||||||
let mut state = get_state_for_epoch(&beacon_chain, epoch, StateSkipConfig::WithoutStateRoots)?;
|
let mut state =
|
||||||
|
get_state_for_epoch(&ctx.beacon_chain, epoch, StateSkipConfig::WithoutStateRoots)?;
|
||||||
|
|
||||||
let relative_epoch = RelativeEpoch::from_epoch(state.current_epoch(), epoch).map_err(|e| {
|
let relative_epoch = RelativeEpoch::from_epoch(state.current_epoch(), epoch).map_err(|e| {
|
||||||
ApiError::ServerError(format!("Failed to get state suitable for epoch: {:?}", e))
|
ApiError::ServerError(format!("Failed to get state suitable for epoch: {:?}", e))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
state
|
state
|
||||||
.build_committee_cache(relative_epoch, &beacon_chain.spec)
|
.build_committee_cache(relative_epoch, &ctx.beacon_chain.spec)
|
||||||
.map_err(|e| ApiError::ServerError(format!("Unable to build committee cache: {:?}", e)))?;
|
.map_err(|e| ApiError::ServerError(format!("Unable to build committee cache: {:?}", e)))?;
|
||||||
|
|
||||||
let committees = state
|
Ok(state
|
||||||
.get_beacon_committees_at_epoch(relative_epoch)
|
.get_beacon_committees_at_epoch(relative_epoch)
|
||||||
.map_err(|e| ApiError::ServerError(format!("Unable to get all committees: {:?}", e)))?
|
.map_err(|e| ApiError::ServerError(format!("Unable to get all committees: {:?}", e)))?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -398,9 +356,7 @@ pub fn get_committees<T: BeaconChainTypes>(
|
|||||||
index: c.index,
|
index: c.index,
|
||||||
committee: c.committee.to_vec(),
|
committee: c.committee.to_vec(),
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>())
|
||||||
|
|
||||||
ResponseBuilder::new(&req)?.body(&committees)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// HTTP handler to return a `BeaconState` at a given `root` or `slot`.
|
/// HTTP handler to return a `BeaconState` at a given `root` or `slot`.
|
||||||
@ -408,10 +364,10 @@ pub fn get_committees<T: BeaconChainTypes>(
|
|||||||
/// Will not return a state if the request slot is in the future. Will return states higher than
|
/// Will not return a state if the request slot is in the future. Will return states higher than
|
||||||
/// the current head by skipping slots.
|
/// the current head by skipping slots.
|
||||||
pub fn get_state<T: BeaconChainTypes>(
|
pub fn get_state<T: BeaconChainTypes>(
|
||||||
req: Request<Body>,
|
req: Request<Vec<u8>>,
|
||||||
beacon_chain: Arc<BeaconChain<T>>,
|
ctx: Arc<Context<T>>,
|
||||||
) -> ApiResult {
|
) -> Result<StateResponse<T::EthSpec>, ApiError> {
|
||||||
let head_state = beacon_chain.head()?.beacon_state;
|
let head_state = ctx.beacon_chain.head()?.beacon_state;
|
||||||
|
|
||||||
let (key, value) = match UrlQuery::from_request(&req) {
|
let (key, value) = match UrlQuery::from_request(&req) {
|
||||||
Ok(query) => {
|
Ok(query) => {
|
||||||
@ -429,11 +385,12 @@ pub fn get_state<T: BeaconChainTypes>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let (root, state): (Hash256, BeaconState<T::EthSpec>) = match (key.as_ref(), value) {
|
let (root, state): (Hash256, BeaconState<T::EthSpec>) = match (key.as_ref(), value) {
|
||||||
("slot", value) => state_at_slot(&beacon_chain, parse_slot(&value)?)?,
|
("slot", value) => state_at_slot(&ctx.beacon_chain, parse_slot(&value)?)?,
|
||||||
("root", value) => {
|
("root", value) => {
|
||||||
let root = &parse_root(&value)?;
|
let root = &parse_root(&value)?;
|
||||||
|
|
||||||
let state = beacon_chain
|
let state = ctx
|
||||||
|
.beacon_chain
|
||||||
.store
|
.store
|
||||||
.get_state(root, None)?
|
.get_state(root, None)?
|
||||||
.ok_or_else(|| ApiError::NotFound(format!("No state for root: {:?}", root)))?;
|
.ok_or_else(|| ApiError::NotFound(format!("No state for root: {:?}", root)))?;
|
||||||
@ -443,12 +400,10 @@ pub fn get_state<T: BeaconChainTypes>(
|
|||||||
_ => return Err(ApiError::ServerError("Unexpected query parameter".into())),
|
_ => return Err(ApiError::ServerError("Unexpected query parameter".into())),
|
||||||
};
|
};
|
||||||
|
|
||||||
let response = StateResponse {
|
Ok(StateResponse {
|
||||||
root,
|
root,
|
||||||
beacon_state: state,
|
beacon_state: state,
|
||||||
};
|
})
|
||||||
|
|
||||||
ResponseBuilder::new(&req)?.body(&response)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// HTTP handler to return a `BeaconState` root at a given `slot`.
|
/// HTTP handler to return a `BeaconState` root at a given `slot`.
|
||||||
@ -456,15 +411,13 @@ pub fn get_state<T: BeaconChainTypes>(
|
|||||||
/// Will not return a state if the request slot is in the future. Will return states higher than
|
/// Will not return a state if the request slot is in the future. Will return states higher than
|
||||||
/// the current head by skipping slots.
|
/// the current head by skipping slots.
|
||||||
pub fn get_state_root<T: BeaconChainTypes>(
|
pub fn get_state_root<T: BeaconChainTypes>(
|
||||||
req: Request<Body>,
|
req: Request<Vec<u8>>,
|
||||||
beacon_chain: Arc<BeaconChain<T>>,
|
ctx: Arc<Context<T>>,
|
||||||
) -> ApiResult {
|
) -> Result<Hash256, ApiError> {
|
||||||
let slot_string = UrlQuery::from_request(&req)?.only_one("slot")?;
|
let slot_string = UrlQuery::from_request(&req)?.only_one("slot")?;
|
||||||
let slot = parse_slot(&slot_string)?;
|
let slot = parse_slot(&slot_string)?;
|
||||||
|
|
||||||
let root = state_root_at_slot(&beacon_chain, slot, StateSkipConfig::WithStateRoots)?;
|
state_root_at_slot(&ctx.beacon_chain, slot, StateSkipConfig::WithStateRoots)
|
||||||
|
|
||||||
ResponseBuilder::new(&req)?.body(&root)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// HTTP handler to return a `BeaconState` at the genesis block.
|
/// HTTP handler to return a `BeaconState` at the genesis block.
|
||||||
@ -472,50 +425,28 @@ pub fn get_state_root<T: BeaconChainTypes>(
|
|||||||
/// This is an undocumented convenience method used during testing. For production, simply do a
|
/// This is an undocumented convenience method used during testing. For production, simply do a
|
||||||
/// state request at slot 0.
|
/// state request at slot 0.
|
||||||
pub fn get_genesis_state<T: BeaconChainTypes>(
|
pub fn get_genesis_state<T: BeaconChainTypes>(
|
||||||
req: Request<Body>,
|
ctx: Arc<Context<T>>,
|
||||||
beacon_chain: Arc<BeaconChain<T>>,
|
) -> Result<BeaconState<T::EthSpec>, ApiError> {
|
||||||
) -> ApiResult {
|
state_at_slot(&ctx.beacon_chain, Slot::new(0)).map(|(_root, state)| state)
|
||||||
let (_root, state) = state_at_slot(&beacon_chain, Slot::new(0))?;
|
|
||||||
|
|
||||||
ResponseBuilder::new(&req)?.body(&state)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read the genesis time from the current beacon chain state.
|
pub fn proposer_slashing<T: BeaconChainTypes>(
|
||||||
pub fn get_genesis_time<T: BeaconChainTypes>(
|
req: Request<Vec<u8>>,
|
||||||
req: Request<Body>,
|
ctx: Arc<Context<T>>,
|
||||||
beacon_chain: Arc<BeaconChain<T>>,
|
) -> Result<bool, ApiError> {
|
||||||
) -> ApiResult {
|
|
||||||
ResponseBuilder::new(&req)?.body(&beacon_chain.head_info()?.genesis_time)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read the `genesis_validators_root` from the current beacon chain state.
|
|
||||||
pub fn get_genesis_validators_root<T: BeaconChainTypes>(
|
|
||||||
req: Request<Body>,
|
|
||||||
beacon_chain: Arc<BeaconChain<T>>,
|
|
||||||
) -> ApiResult {
|
|
||||||
ResponseBuilder::new(&req)?.body(&beacon_chain.head_info()?.genesis_validators_root)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn proposer_slashing<T: BeaconChainTypes>(
|
|
||||||
req: Request<Body>,
|
|
||||||
beacon_chain: Arc<BeaconChain<T>>,
|
|
||||||
) -> ApiResult {
|
|
||||||
let response_builder = ResponseBuilder::new(&req);
|
|
||||||
|
|
||||||
let body = req.into_body();
|
let body = req.into_body();
|
||||||
let chunks = hyper::body::to_bytes(body)
|
|
||||||
.await
|
|
||||||
.map_err(|e| ApiError::ServerError(format!("Unable to get request body: {:?}", e)))?;
|
|
||||||
|
|
||||||
serde_json::from_slice::<ProposerSlashing>(&chunks)
|
serde_json::from_slice::<ProposerSlashing>(&body)
|
||||||
.map_err(|e| format!("Unable to parse JSON into ProposerSlashing: {:?}", e))
|
.map_err(|e| format!("Unable to parse JSON into ProposerSlashing: {:?}", e))
|
||||||
.and_then(move |proposer_slashing| {
|
.and_then(move |proposer_slashing| {
|
||||||
if beacon_chain.eth1_chain.is_some() {
|
if ctx.beacon_chain.eth1_chain.is_some() {
|
||||||
let obs_outcome = beacon_chain
|
let obs_outcome = ctx
|
||||||
|
.beacon_chain
|
||||||
.verify_proposer_slashing_for_gossip(proposer_slashing)
|
.verify_proposer_slashing_for_gossip(proposer_slashing)
|
||||||
.map_err(|e| format!("Error while verifying proposer slashing: {:?}", e))?;
|
.map_err(|e| format!("Error while verifying proposer slashing: {:?}", e))?;
|
||||||
if let ObservationOutcome::New(verified_proposer_slashing) = obs_outcome {
|
if let ObservationOutcome::New(verified_proposer_slashing) = obs_outcome {
|
||||||
beacon_chain.import_proposer_slashing(verified_proposer_slashing);
|
ctx.beacon_chain
|
||||||
|
.import_proposer_slashing(verified_proposer_slashing);
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err("Proposer slashing for that validator index already known".into())
|
Err("Proposer slashing for that validator index already known".into())
|
||||||
@ -524,22 +455,17 @@ pub async fn proposer_slashing<T: BeaconChainTypes>(
|
|||||||
Err("Cannot insert proposer slashing on node without Eth1 connection.".to_string())
|
Err("Cannot insert proposer slashing on node without Eth1 connection.".to_string())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.map_err(ApiError::BadRequest)
|
.map_err(ApiError::BadRequest)?;
|
||||||
.and_then(|_| response_builder?.body(&true))
|
|
||||||
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn attester_slashing<T: BeaconChainTypes>(
|
pub fn attester_slashing<T: BeaconChainTypes>(
|
||||||
req: Request<Body>,
|
req: Request<Vec<u8>>,
|
||||||
beacon_chain: Arc<BeaconChain<T>>,
|
ctx: Arc<Context<T>>,
|
||||||
) -> ApiResult {
|
) -> Result<bool, ApiError> {
|
||||||
let response_builder = ResponseBuilder::new(&req);
|
|
||||||
|
|
||||||
let body = req.into_body();
|
let body = req.into_body();
|
||||||
let chunks = hyper::body::to_bytes(body)
|
serde_json::from_slice::<AttesterSlashing<T::EthSpec>>(&body)
|
||||||
.await
|
|
||||||
.map_err(|e| ApiError::ServerError(format!("Unable to get request body: {:?}", e)))?;
|
|
||||||
|
|
||||||
serde_json::from_slice::<AttesterSlashing<T::EthSpec>>(&chunks)
|
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
ApiError::BadRequest(format!(
|
ApiError::BadRequest(format!(
|
||||||
"Unable to parse JSON into AttesterSlashing: {:?}",
|
"Unable to parse JSON into AttesterSlashing: {:?}",
|
||||||
@ -547,13 +473,13 @@ pub async fn attester_slashing<T: BeaconChainTypes>(
|
|||||||
))
|
))
|
||||||
})
|
})
|
||||||
.and_then(move |attester_slashing| {
|
.and_then(move |attester_slashing| {
|
||||||
if beacon_chain.eth1_chain.is_some() {
|
if ctx.beacon_chain.eth1_chain.is_some() {
|
||||||
beacon_chain
|
ctx.beacon_chain
|
||||||
.verify_attester_slashing_for_gossip(attester_slashing)
|
.verify_attester_slashing_for_gossip(attester_slashing)
|
||||||
.map_err(|e| format!("Error while verifying attester slashing: {:?}", e))
|
.map_err(|e| format!("Error while verifying attester slashing: {:?}", e))
|
||||||
.and_then(|outcome| {
|
.and_then(|outcome| {
|
||||||
if let ObservationOutcome::New(verified_attester_slashing) = outcome {
|
if let ObservationOutcome::New(verified_attester_slashing) = outcome {
|
||||||
beacon_chain
|
ctx.beacon_chain
|
||||||
.import_attester_slashing(verified_attester_slashing)
|
.import_attester_slashing(verified_attester_slashing)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
format!("Error while importing attester slashing: {:?}", e)
|
format!("Error while importing attester slashing: {:?}", e)
|
||||||
@ -568,6 +494,7 @@ pub async fn attester_slashing<T: BeaconChainTypes>(
|
|||||||
"Cannot insert attester slashing on node without Eth1 connection.".to_string(),
|
"Cannot insert attester slashing on node without Eth1 connection.".to_string(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
})
|
})?;
|
||||||
.and_then(|_| response_builder?.body(&true))
|
|
||||||
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
use crate::helpers::*;
|
use crate::helpers::*;
|
||||||
use crate::response_builder::ResponseBuilder;
|
use crate::{ApiError, Context, UrlQuery};
|
||||||
use crate::{ApiError, ApiResult, UrlQuery};
|
use beacon_chain::BeaconChainTypes;
|
||||||
use beacon_chain::{BeaconChain, BeaconChainTypes};
|
use hyper::Request;
|
||||||
use hyper::{Body, Request};
|
|
||||||
use rest_types::{IndividualVotesRequest, IndividualVotesResponse};
|
use rest_types::{IndividualVotesRequest, IndividualVotesResponse};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use ssz_derive::{Decode, Encode};
|
use ssz_derive::{Decode, Encode};
|
||||||
@ -50,38 +49,31 @@ impl Into<VoteCount> for TotalBalances {
|
|||||||
|
|
||||||
/// HTTP handler return a `VoteCount` for some given `Epoch`.
|
/// HTTP handler return a `VoteCount` for some given `Epoch`.
|
||||||
pub fn get_vote_count<T: BeaconChainTypes>(
|
pub fn get_vote_count<T: BeaconChainTypes>(
|
||||||
req: Request<Body>,
|
req: Request<Vec<u8>>,
|
||||||
beacon_chain: Arc<BeaconChain<T>>,
|
ctx: Arc<Context<T>>,
|
||||||
) -> ApiResult {
|
) -> Result<VoteCount, ApiError> {
|
||||||
let query = UrlQuery::from_request(&req)?;
|
let query = UrlQuery::from_request(&req)?;
|
||||||
|
|
||||||
let epoch = query.epoch()?;
|
let epoch = query.epoch()?;
|
||||||
// This is the last slot of the given epoch (one prior to the first slot of the next epoch).
|
// This is the last slot of the given epoch (one prior to the first slot of the next epoch).
|
||||||
let target_slot = (epoch + 1).start_slot(T::EthSpec::slots_per_epoch()) - 1;
|
let target_slot = (epoch + 1).start_slot(T::EthSpec::slots_per_epoch()) - 1;
|
||||||
|
|
||||||
let (_root, state) = state_at_slot(&beacon_chain, target_slot)?;
|
let (_root, state) = state_at_slot(&ctx.beacon_chain, target_slot)?;
|
||||||
let spec = &beacon_chain.spec;
|
let spec = &ctx.beacon_chain.spec;
|
||||||
|
|
||||||
let mut validator_statuses = ValidatorStatuses::new(&state, spec)?;
|
let mut validator_statuses = ValidatorStatuses::new(&state, spec)?;
|
||||||
validator_statuses.process_attestations(&state, spec)?;
|
validator_statuses.process_attestations(&state, spec)?;
|
||||||
|
|
||||||
let report: VoteCount = validator_statuses.total_balances.into();
|
Ok(validator_statuses.total_balances.into())
|
||||||
|
|
||||||
ResponseBuilder::new(&req)?.body(&report)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn post_individual_votes<T: BeaconChainTypes>(
|
pub fn post_individual_votes<T: BeaconChainTypes>(
|
||||||
req: Request<Body>,
|
req: Request<Vec<u8>>,
|
||||||
beacon_chain: Arc<BeaconChain<T>>,
|
ctx: Arc<Context<T>>,
|
||||||
) -> ApiResult {
|
) -> Result<Vec<IndividualVotesResponse>, ApiError> {
|
||||||
let response_builder = ResponseBuilder::new(&req);
|
|
||||||
|
|
||||||
let body = req.into_body();
|
let body = req.into_body();
|
||||||
let chunks = hyper::body::to_bytes(body)
|
|
||||||
.await
|
|
||||||
.map_err(|e| ApiError::ServerError(format!("Unable to get request body: {:?}", e)))?;
|
|
||||||
|
|
||||||
serde_json::from_slice::<IndividualVotesRequest>(&chunks)
|
serde_json::from_slice::<IndividualVotesRequest>(&body)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
ApiError::BadRequest(format!(
|
ApiError::BadRequest(format!(
|
||||||
"Unable to parse JSON into ValidatorDutiesRequest: {:?}",
|
"Unable to parse JSON into ValidatorDutiesRequest: {:?}",
|
||||||
@ -94,8 +86,8 @@ pub async fn post_individual_votes<T: BeaconChainTypes>(
|
|||||||
// This is the last slot of the given epoch (one prior to the first slot of the next epoch).
|
// This is the last slot of the given epoch (one prior to the first slot of the next epoch).
|
||||||
let target_slot = (epoch + 1).start_slot(T::EthSpec::slots_per_epoch()) - 1;
|
let target_slot = (epoch + 1).start_slot(T::EthSpec::slots_per_epoch()) - 1;
|
||||||
|
|
||||||
let (_root, mut state) = state_at_slot(&beacon_chain, target_slot)?;
|
let (_root, mut state) = state_at_slot(&ctx.beacon_chain, target_slot)?;
|
||||||
let spec = &beacon_chain.spec;
|
let spec = &ctx.beacon_chain.spec;
|
||||||
|
|
||||||
let mut validator_statuses = ValidatorStatuses::new(&state, spec)?;
|
let mut validator_statuses = ValidatorStatuses::new(&state, spec)?;
|
||||||
validator_statuses.process_attestations(&state, spec)?;
|
validator_statuses.process_attestations(&state, spec)?;
|
||||||
@ -135,5 +127,4 @@ pub async fn post_individual_votes<T: BeaconChainTypes>(
|
|||||||
})
|
})
|
||||||
.collect::<Result<Vec<_>, _>>()
|
.collect::<Result<Vec<_>, _>>()
|
||||||
})
|
})
|
||||||
.and_then(|votes| response_builder?.body_no_ssz(&votes))
|
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
use crate::{ApiError, ApiResult, NetworkChannel};
|
use crate::{ApiError, NetworkChannel};
|
||||||
use beacon_chain::{BeaconChain, BeaconChainTypes, StateSkipConfig};
|
use beacon_chain::{BeaconChain, BeaconChainTypes, StateSkipConfig};
|
||||||
use bls::PublicKeyBytes;
|
use bls::PublicKeyBytes;
|
||||||
use eth2_libp2p::PubsubMessage;
|
use eth2_libp2p::PubsubMessage;
|
||||||
use http::header;
|
|
||||||
use hyper::{Body, Request};
|
|
||||||
use itertools::process_results;
|
use itertools::process_results;
|
||||||
use network::NetworkMessage;
|
use network::NetworkMessage;
|
||||||
use ssz::Decode;
|
use ssz::Decode;
|
||||||
@ -41,21 +39,6 @@ pub fn parse_committee_index(string: &str) -> Result<CommitteeIndex, ApiError> {
|
|||||||
.map_err(|e| ApiError::BadRequest(format!("Unable to parse committee index: {:?}", e)))
|
.map_err(|e| ApiError::BadRequest(format!("Unable to parse committee index: {:?}", e)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks the provided request to ensure that the `content-type` header.
|
|
||||||
///
|
|
||||||
/// The content-type header should either be omitted, in which case JSON is assumed, or it should
|
|
||||||
/// explicitly specify `application/json`. If anything else is provided, an error is returned.
|
|
||||||
pub fn check_content_type_for_json(req: &Request<Body>) -> Result<(), ApiError> {
|
|
||||||
match req.headers().get(header::CONTENT_TYPE) {
|
|
||||||
Some(h) if h == "application/json" => Ok(()),
|
|
||||||
Some(h) => Err(ApiError::BadRequest(format!(
|
|
||||||
"The provided content-type {:?} is not available, this endpoint only supports json.",
|
|
||||||
h
|
|
||||||
))),
|
|
||||||
_ => Ok(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse an SSZ object from some hex-encoded bytes.
|
/// Parse an SSZ object from some hex-encoded bytes.
|
||||||
///
|
///
|
||||||
/// E.g., A signature is `"0x0000000000000000000000000000000000000000000000000000000000000000"`
|
/// E.g., A signature is `"0x0000000000000000000000000000000000000000000000000000000000000000"`
|
||||||
@ -228,14 +211,8 @@ pub fn state_root_at_slot<T: BeaconChainTypes>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn implementation_pending_response(_req: Request<Body>) -> ApiResult {
|
|
||||||
Err(ApiError::NotImplemented(
|
|
||||||
"API endpoint has not yet been implemented, but is planned to be soon.".to_owned(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn publish_beacon_block_to_network<T: BeaconChainTypes + 'static>(
|
pub fn publish_beacon_block_to_network<T: BeaconChainTypes + 'static>(
|
||||||
chan: NetworkChannel<T::EthSpec>,
|
chan: &NetworkChannel<T::EthSpec>,
|
||||||
block: SignedBeaconBlock<T::EthSpec>,
|
block: SignedBeaconBlock<T::EthSpec>,
|
||||||
) -> Result<(), ApiError> {
|
) -> Result<(), ApiError> {
|
||||||
// send the block via SSZ encoding
|
// send the block via SSZ encoding
|
||||||
|
@ -1,22 +1,15 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod macros;
|
|
||||||
#[macro_use]
|
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
|
mod router;
|
||||||
extern crate network as client_network;
|
extern crate network as client_network;
|
||||||
|
|
||||||
mod advanced;
|
|
||||||
mod beacon;
|
mod beacon;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
mod consensus;
|
mod consensus;
|
||||||
mod error;
|
|
||||||
mod helpers;
|
mod helpers;
|
||||||
mod lighthouse;
|
mod lighthouse;
|
||||||
mod metrics;
|
mod metrics;
|
||||||
mod network;
|
|
||||||
mod node;
|
mod node;
|
||||||
mod response_builder;
|
|
||||||
mod router;
|
|
||||||
mod spec;
|
|
||||||
mod url_query;
|
mod url_query;
|
||||||
mod validator;
|
mod validator;
|
||||||
|
|
||||||
@ -24,7 +17,6 @@ use beacon_chain::{BeaconChain, BeaconChainTypes};
|
|||||||
use bus::Bus;
|
use bus::Bus;
|
||||||
use client_network::NetworkMessage;
|
use client_network::NetworkMessage;
|
||||||
pub use config::ApiEncodingFormat;
|
pub use config::ApiEncodingFormat;
|
||||||
use error::{ApiError, ApiResult};
|
|
||||||
use eth2_config::Eth2Config;
|
use eth2_config::Eth2Config;
|
||||||
use eth2_libp2p::NetworkGlobals;
|
use eth2_libp2p::NetworkGlobals;
|
||||||
use futures::future::TryFutureExt;
|
use futures::future::TryFutureExt;
|
||||||
@ -32,6 +24,7 @@ use hyper::server::conn::AddrStream;
|
|||||||
use hyper::service::{make_service_fn, service_fn};
|
use hyper::service::{make_service_fn, service_fn};
|
||||||
use hyper::{Body, Request, Server};
|
use hyper::{Body, Request, Server};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
use rest_types::ApiError;
|
||||||
use slog::{info, warn};
|
use slog::{info, warn};
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
@ -42,6 +35,7 @@ use url_query::UrlQuery;
|
|||||||
|
|
||||||
pub use crate::helpers::parse_pubkey_bytes;
|
pub use crate::helpers::parse_pubkey_bytes;
|
||||||
pub use config::Config;
|
pub use config::Config;
|
||||||
|
pub use router::Context;
|
||||||
|
|
||||||
pub type NetworkChannel<T> = mpsc::UnboundedSender<NetworkMessage<T>>;
|
pub type NetworkChannel<T> = mpsc::UnboundedSender<NetworkMessage<T>>;
|
||||||
|
|
||||||
@ -63,36 +57,28 @@ pub fn start_server<T: BeaconChainTypes>(
|
|||||||
events: Arc<Mutex<Bus<SignedBeaconBlockHash>>>,
|
events: Arc<Mutex<Bus<SignedBeaconBlockHash>>>,
|
||||||
) -> Result<SocketAddr, hyper::Error> {
|
) -> Result<SocketAddr, hyper::Error> {
|
||||||
let log = executor.log();
|
let log = executor.log();
|
||||||
let inner_log = log.clone();
|
|
||||||
let rest_api_config = Arc::new(config.clone());
|
|
||||||
let eth2_config = Arc::new(eth2_config);
|
let eth2_config = Arc::new(eth2_config);
|
||||||
|
|
||||||
|
let context = Arc::new(Context {
|
||||||
|
executor: executor.clone(),
|
||||||
|
config: config.clone(),
|
||||||
|
beacon_chain,
|
||||||
|
network_globals: network_info.network_globals.clone(),
|
||||||
|
network_chan: network_info.network_chan,
|
||||||
|
eth2_config,
|
||||||
|
log: log.clone(),
|
||||||
|
db_path,
|
||||||
|
freezer_db_path,
|
||||||
|
events,
|
||||||
|
});
|
||||||
|
|
||||||
// Define the function that will build the request handler.
|
// Define the function that will build the request handler.
|
||||||
let make_service = make_service_fn(move |_socket: &AddrStream| {
|
let make_service = make_service_fn(move |_socket: &AddrStream| {
|
||||||
let beacon_chain = beacon_chain.clone();
|
let ctx = context.clone();
|
||||||
let log = inner_log.clone();
|
|
||||||
let rest_api_config = rest_api_config.clone();
|
|
||||||
let eth2_config = eth2_config.clone();
|
|
||||||
let network_globals = network_info.network_globals.clone();
|
|
||||||
let network_channel = network_info.network_chan.clone();
|
|
||||||
let db_path = db_path.clone();
|
|
||||||
let freezer_db_path = freezer_db_path.clone();
|
|
||||||
let events = events.clone();
|
|
||||||
|
|
||||||
async move {
|
async move {
|
||||||
Ok::<_, hyper::Error>(service_fn(move |req: Request<Body>| {
|
Ok::<_, hyper::Error>(service_fn(move |req: Request<Body>| {
|
||||||
router::route(
|
router::on_http_request(req, ctx.clone())
|
||||||
req,
|
|
||||||
beacon_chain.clone(),
|
|
||||||
network_globals.clone(),
|
|
||||||
network_channel.clone(),
|
|
||||||
rest_api_config.clone(),
|
|
||||||
eth2_config.clone(),
|
|
||||||
log.clone(),
|
|
||||||
db_path.clone(),
|
|
||||||
freezer_db_path.clone(),
|
|
||||||
events.clone(),
|
|
||||||
)
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,24 +1,16 @@
|
|||||||
//! This contains a collection of lighthouse specific HTTP endpoints.
|
//! This contains a collection of lighthouse specific HTTP endpoints.
|
||||||
|
|
||||||
use crate::response_builder::ResponseBuilder;
|
use crate::{ApiError, Context};
|
||||||
use crate::ApiResult;
|
use beacon_chain::BeaconChainTypes;
|
||||||
use eth2_libp2p::{NetworkGlobals, PeerInfo};
|
use eth2_libp2p::PeerInfo;
|
||||||
use hyper::{Body, Request};
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use types::EthSpec;
|
use types::EthSpec;
|
||||||
|
|
||||||
/// The syncing state of the beacon node.
|
|
||||||
pub fn syncing<T: EthSpec>(
|
|
||||||
req: Request<Body>,
|
|
||||||
network_globals: Arc<NetworkGlobals<T>>,
|
|
||||||
) -> ApiResult {
|
|
||||||
ResponseBuilder::new(&req)?.body_no_ssz(&network_globals.sync_state())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns all known peers and corresponding information
|
/// Returns all known peers and corresponding information
|
||||||
pub fn peers<T: EthSpec>(req: Request<Body>, network_globals: Arc<NetworkGlobals<T>>) -> ApiResult {
|
pub fn peers<T: BeaconChainTypes>(ctx: Arc<Context<T>>) -> Result<Vec<Peer<T::EthSpec>>, ApiError> {
|
||||||
let peers: Vec<Peer<T>> = network_globals
|
Ok(ctx
|
||||||
|
.network_globals
|
||||||
.peers
|
.peers
|
||||||
.read()
|
.read()
|
||||||
.peers()
|
.peers()
|
||||||
@ -26,16 +18,15 @@ pub fn peers<T: EthSpec>(req: Request<Body>, network_globals: Arc<NetworkGlobals
|
|||||||
peer_id: peer_id.to_string(),
|
peer_id: peer_id.to_string(),
|
||||||
peer_info: peer_info.clone(),
|
peer_info: peer_info.clone(),
|
||||||
})
|
})
|
||||||
.collect();
|
.collect())
|
||||||
ResponseBuilder::new(&req)?.body_no_ssz(&peers)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns all known connected peers and their corresponding information
|
/// Returns all known connected peers and their corresponding information
|
||||||
pub fn connected_peers<T: EthSpec>(
|
pub fn connected_peers<T: BeaconChainTypes>(
|
||||||
req: Request<Body>,
|
ctx: Arc<Context<T>>,
|
||||||
network_globals: Arc<NetworkGlobals<T>>,
|
) -> Result<Vec<Peer<T::EthSpec>>, ApiError> {
|
||||||
) -> ApiResult {
|
Ok(ctx
|
||||||
let peers: Vec<Peer<T>> = network_globals
|
.network_globals
|
||||||
.peers
|
.peers
|
||||||
.read()
|
.read()
|
||||||
.connected_peers()
|
.connected_peers()
|
||||||
@ -43,14 +34,13 @@ pub fn connected_peers<T: EthSpec>(
|
|||||||
peer_id: peer_id.to_string(),
|
peer_id: peer_id.to_string(),
|
||||||
peer_info: peer_info.clone(),
|
peer_info: peer_info.clone(),
|
||||||
})
|
})
|
||||||
.collect();
|
.collect())
|
||||||
ResponseBuilder::new(&req)?.body_no_ssz(&peers)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Information returned by `peers` and `connected_peers`.
|
/// Information returned by `peers` and `connected_peers`.
|
||||||
#[derive(Clone, Debug, Serialize)]
|
#[derive(Clone, Debug, Serialize)]
|
||||||
#[serde(bound = "T: EthSpec")]
|
#[serde(bound = "T: EthSpec")]
|
||||||
struct Peer<T: EthSpec> {
|
pub struct Peer<T: EthSpec> {
|
||||||
/// The Peer's ID
|
/// The Peer's ID
|
||||||
peer_id: String,
|
peer_id: String,
|
||||||
/// The PeerInfo associated with the peer.
|
/// The PeerInfo associated with the peer.
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
macro_rules! try_future {
|
|
||||||
($expr:expr) => {
|
|
||||||
match $expr {
|
|
||||||
core::result::Result::Ok(val) => val,
|
|
||||||
core::result::Result::Err(err) => return Err(std::convert::From::from(err)),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
($expr:expr,) => {
|
|
||||||
$crate::try_future!($expr)
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,42 +1,38 @@
|
|||||||
use crate::response_builder::ResponseBuilder;
|
use crate::{ApiError, Context};
|
||||||
use crate::{ApiError, ApiResult};
|
use beacon_chain::BeaconChainTypes;
|
||||||
use beacon_chain::{BeaconChain, BeaconChainTypes};
|
|
||||||
use hyper::{Body, Request};
|
|
||||||
use lighthouse_metrics::{Encoder, TextEncoder};
|
use lighthouse_metrics::{Encoder, TextEncoder};
|
||||||
use rest_types::Health;
|
use rest_types::Health;
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub use lighthouse_metrics::*;
|
pub use lighthouse_metrics::*;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
|
pub static ref BEACON_HTTP_API_REQUESTS_TOTAL: Result<IntCounterVec> =
|
||||||
|
try_create_int_counter_vec(
|
||||||
|
"beacon_http_api_requests_total",
|
||||||
|
"Count of HTTP requests received",
|
||||||
|
&["endpoint"]
|
||||||
|
);
|
||||||
|
pub static ref BEACON_HTTP_API_SUCCESS_TOTAL: Result<IntCounterVec> =
|
||||||
|
try_create_int_counter_vec(
|
||||||
|
"beacon_http_api_success_total",
|
||||||
|
"Count of HTTP requests that returned 200 OK",
|
||||||
|
&["endpoint"]
|
||||||
|
);
|
||||||
|
pub static ref BEACON_HTTP_API_ERROR_TOTAL: Result<IntCounterVec> = try_create_int_counter_vec(
|
||||||
|
"beacon_http_api_error_total",
|
||||||
|
"Count of HTTP that did not return 200 OK",
|
||||||
|
&["endpoint"]
|
||||||
|
);
|
||||||
|
pub static ref BEACON_HTTP_API_TIMES_TOTAL: Result<HistogramVec> = try_create_histogram_vec(
|
||||||
|
"beacon_http_api_times_total",
|
||||||
|
"Duration to process HTTP requests",
|
||||||
|
&["endpoint"]
|
||||||
|
);
|
||||||
pub static ref REQUEST_RESPONSE_TIME: Result<Histogram> = try_create_histogram(
|
pub static ref REQUEST_RESPONSE_TIME: Result<Histogram> = try_create_histogram(
|
||||||
"http_server_request_duration_seconds",
|
"http_server_request_duration_seconds",
|
||||||
"Time taken to build a response to a HTTP request"
|
"Time taken to build a response to a HTTP request"
|
||||||
);
|
);
|
||||||
pub static ref REQUEST_COUNT: Result<IntCounter> = try_create_int_counter(
|
|
||||||
"http_server_request_total",
|
|
||||||
"Total count of HTTP requests received"
|
|
||||||
);
|
|
||||||
pub static ref SUCCESS_COUNT: Result<IntCounter> = try_create_int_counter(
|
|
||||||
"http_server_success_total",
|
|
||||||
"Total count of HTTP 200 responses sent"
|
|
||||||
);
|
|
||||||
pub static ref VALIDATOR_GET_BLOCK_REQUEST_RESPONSE_TIME: Result<Histogram> =
|
|
||||||
try_create_histogram(
|
|
||||||
"http_server_validator_block_get_request_duration_seconds",
|
|
||||||
"Time taken to respond to GET /validator/block"
|
|
||||||
);
|
|
||||||
pub static ref VALIDATOR_GET_ATTESTATION_REQUEST_RESPONSE_TIME: Result<Histogram> =
|
|
||||||
try_create_histogram(
|
|
||||||
"http_server_validator_attestation_get_request_duration_seconds",
|
|
||||||
"Time taken to respond to GET /validator/attestation"
|
|
||||||
);
|
|
||||||
pub static ref VALIDATOR_GET_DUTIES_REQUEST_RESPONSE_TIME: Result<Histogram> =
|
|
||||||
try_create_histogram(
|
|
||||||
"http_server_validator_duties_get_request_duration_seconds",
|
|
||||||
"Time taken to respond to GET /validator/duties"
|
|
||||||
);
|
|
||||||
pub static ref PROCESS_NUM_THREADS: Result<IntGauge> = try_create_int_gauge(
|
pub static ref PROCESS_NUM_THREADS: Result<IntGauge> = try_create_int_gauge(
|
||||||
"process_num_threads",
|
"process_num_threads",
|
||||||
"Number of threads used by the current process"
|
"Number of threads used by the current process"
|
||||||
@ -77,11 +73,8 @@ lazy_static! {
|
|||||||
///
|
///
|
||||||
/// This is a HTTP handler method.
|
/// This is a HTTP handler method.
|
||||||
pub fn get_prometheus<T: BeaconChainTypes>(
|
pub fn get_prometheus<T: BeaconChainTypes>(
|
||||||
req: Request<Body>,
|
ctx: Arc<Context<T>>,
|
||||||
beacon_chain: Arc<BeaconChain<T>>,
|
) -> std::result::Result<String, ApiError> {
|
||||||
db_path: PathBuf,
|
|
||||||
freezer_db_path: PathBuf,
|
|
||||||
) -> ApiResult {
|
|
||||||
let mut buffer = vec![];
|
let mut buffer = vec![];
|
||||||
let encoder = TextEncoder::new();
|
let encoder = TextEncoder::new();
|
||||||
|
|
||||||
@ -101,9 +94,9 @@ pub fn get_prometheus<T: BeaconChainTypes>(
|
|||||||
// using `lighthouse_metrics::gather(..)` to collect the global `DEFAULT_REGISTRY` metrics into
|
// using `lighthouse_metrics::gather(..)` to collect the global `DEFAULT_REGISTRY` metrics into
|
||||||
// a string that can be returned via HTTP.
|
// a string that can be returned via HTTP.
|
||||||
|
|
||||||
slot_clock::scrape_for_metrics::<T::EthSpec, T::SlotClock>(&beacon_chain.slot_clock);
|
slot_clock::scrape_for_metrics::<T::EthSpec, T::SlotClock>(&ctx.beacon_chain.slot_clock);
|
||||||
store::scrape_for_metrics(&db_path, &freezer_db_path);
|
store::scrape_for_metrics(&ctx.db_path, &ctx.freezer_db_path);
|
||||||
beacon_chain::scrape_for_metrics(&beacon_chain);
|
beacon_chain::scrape_for_metrics(&ctx.beacon_chain);
|
||||||
eth2_libp2p::scrape_discovery_metrics();
|
eth2_libp2p::scrape_discovery_metrics();
|
||||||
|
|
||||||
// This will silently fail if we are unable to observe the health. This is desired behaviour
|
// This will silently fail if we are unable to observe the health. This is desired behaviour
|
||||||
@ -133,6 +126,5 @@ pub fn get_prometheus<T: BeaconChainTypes>(
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
String::from_utf8(buffer)
|
String::from_utf8(buffer)
|
||||||
.map(|string| ResponseBuilder::new(&req)?.body_text(string))
|
.map_err(|e| ApiError::ServerError(format!("Failed to encode prometheus info: {:?}", e)))
|
||||||
.map_err(|e| ApiError::ServerError(format!("Failed to encode prometheus info: {:?}", e)))?
|
|
||||||
}
|
}
|
||||||
|
@ -1,72 +0,0 @@
|
|||||||
use crate::error::ApiResult;
|
|
||||||
use crate::response_builder::ResponseBuilder;
|
|
||||||
use crate::NetworkGlobals;
|
|
||||||
use beacon_chain::BeaconChainTypes;
|
|
||||||
use eth2_libp2p::{Multiaddr, PeerId};
|
|
||||||
use hyper::{Body, Request};
|
|
||||||
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<T: BeaconChainTypes>(
|
|
||||||
req: Request<Body>,
|
|
||||||
network: Arc<NetworkGlobals<T::EthSpec>>,
|
|
||||||
) -> ApiResult {
|
|
||||||
let multiaddresses: Vec<Multiaddr> = network.listen_multiaddrs();
|
|
||||||
ResponseBuilder::new(&req)?.body_no_ssz(&multiaddresses)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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<T: BeaconChainTypes>(
|
|
||||||
req: Request<Body>,
|
|
||||||
network: Arc<NetworkGlobals<T::EthSpec>>,
|
|
||||||
) -> ApiResult {
|
|
||||||
ResponseBuilder::new(&req)?.body(&network.listen_port_tcp())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// HTTP handler to return the Discv5 ENR from the client's libp2p service.
|
|
||||||
///
|
|
||||||
/// ENR is encoded as base64 string.
|
|
||||||
pub fn get_enr<T: BeaconChainTypes>(
|
|
||||||
req: Request<Body>,
|
|
||||||
network: Arc<NetworkGlobals<T::EthSpec>>,
|
|
||||||
) -> 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<T: BeaconChainTypes>(
|
|
||||||
req: Request<Body>,
|
|
||||||
network: Arc<NetworkGlobals<T::EthSpec>>,
|
|
||||||
) -> 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<T: BeaconChainTypes>(
|
|
||||||
req: Request<Body>,
|
|
||||||
network: Arc<NetworkGlobals<T::EthSpec>>,
|
|
||||||
) -> 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<T: BeaconChainTypes>(
|
|
||||||
req: Request<Body>,
|
|
||||||
network: Arc<NetworkGlobals<T::EthSpec>>,
|
|
||||||
) -> ApiResult {
|
|
||||||
let connected_peers: Vec<String> = network
|
|
||||||
.peers
|
|
||||||
.read()
|
|
||||||
.connected_peer_ids()
|
|
||||||
.map(PeerId::to_string)
|
|
||||||
.collect();
|
|
||||||
ResponseBuilder::new(&req)?.body_no_ssz(&connected_peers)
|
|
||||||
}
|
|
@ -1,23 +1,19 @@
|
|||||||
use crate::response_builder::ResponseBuilder;
|
use crate::{ApiError, Context};
|
||||||
use crate::{ApiError, ApiResult};
|
use beacon_chain::BeaconChainTypes;
|
||||||
use eth2_libp2p::{types::SyncState, NetworkGlobals};
|
use eth2_libp2p::types::SyncState;
|
||||||
use hyper::{Body, Request};
|
use rest_types::{SyncingResponse, SyncingStatus};
|
||||||
use lighthouse_version::version_with_platform;
|
|
||||||
use rest_types::{Health, SyncingResponse, SyncingStatus};
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use types::{EthSpec, Slot};
|
use types::Slot;
|
||||||
|
|
||||||
/// Read the version string from the current Lighthouse build.
|
/// Returns a syncing status.
|
||||||
pub fn get_version(req: Request<Body>) -> ApiResult {
|
pub fn syncing<T: BeaconChainTypes>(ctx: Arc<Context<T>>) -> Result<SyncingResponse, ApiError> {
|
||||||
ResponseBuilder::new(&req)?.body_no_ssz(&version_with_platform())
|
let current_slot = ctx
|
||||||
}
|
.beacon_chain
|
||||||
|
.head_info()
|
||||||
|
.map_err(|e| ApiError::ServerError(format!("Unable to read head slot: {:?}", e)))?
|
||||||
|
.slot;
|
||||||
|
|
||||||
pub fn syncing<T: EthSpec>(
|
let (starting_slot, highest_slot) = match ctx.network_globals.sync_state() {
|
||||||
req: Request<Body>,
|
|
||||||
network: Arc<NetworkGlobals<T>>,
|
|
||||||
current_slot: Slot,
|
|
||||||
) -> ApiResult {
|
|
||||||
let (starting_slot, highest_slot) = match network.sync_state() {
|
|
||||||
SyncState::SyncingFinalized {
|
SyncState::SyncingFinalized {
|
||||||
start_slot,
|
start_slot,
|
||||||
head_slot,
|
head_slot,
|
||||||
@ -36,14 +32,8 @@ pub fn syncing<T: EthSpec>(
|
|||||||
highest_slot,
|
highest_slot,
|
||||||
};
|
};
|
||||||
|
|
||||||
ResponseBuilder::new(&req)?.body(&SyncingResponse {
|
Ok(SyncingResponse {
|
||||||
is_syncing: network.is_syncing(),
|
is_syncing: ctx.network_globals.is_syncing(),
|
||||||
sync_status,
|
sync_status,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_health(req: Request<Body>) -> ApiResult {
|
|
||||||
let health = Health::observe().map_err(ApiError::ServerError)?;
|
|
||||||
|
|
||||||
ResponseBuilder::new(&req)?.body_no_ssz(&health)
|
|
||||||
}
|
|
||||||
|
@ -1,83 +0,0 @@
|
|||||||
use super::{ApiError, ApiResult};
|
|
||||||
use crate::config::ApiEncodingFormat;
|
|
||||||
use hyper::header;
|
|
||||||
use hyper::{Body, Request, Response, StatusCode};
|
|
||||||
use serde::Serialize;
|
|
||||||
use ssz::Encode;
|
|
||||||
|
|
||||||
pub struct ResponseBuilder {
|
|
||||||
encoding: ApiEncodingFormat,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ResponseBuilder {
|
|
||||||
pub fn new(req: &Request<Body>) -> Result<Self, ApiError> {
|
|
||||||
let accept_header: String = req
|
|
||||||
.headers()
|
|
||||||
.get(header::ACCEPT)
|
|
||||||
.map_or(Ok(""), |h| h.to_str())
|
|
||||||
.map_err(|e| {
|
|
||||||
ApiError::BadRequest(format!(
|
|
||||||
"The Accept header contains invalid characters: {:?}",
|
|
||||||
e
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.map(String::from)?;
|
|
||||||
|
|
||||||
// JSON is our default encoding, unless something else is requested.
|
|
||||||
let encoding = ApiEncodingFormat::from(accept_header.as_str());
|
|
||||||
Ok(Self { encoding })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn body<T: Serialize + Encode>(self, item: &T) -> ApiResult {
|
|
||||||
match self.encoding {
|
|
||||||
ApiEncodingFormat::SSZ => Response::builder()
|
|
||||||
.status(StatusCode::OK)
|
|
||||||
.header("content-type", "application/ssz")
|
|
||||||
.body(Body::from(item.as_ssz_bytes()))
|
|
||||||
.map_err(|e| ApiError::ServerError(format!("Failed to build response: {:?}", e))),
|
|
||||||
_ => self.body_no_ssz(item),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn body_no_ssz<T: Serialize>(self, item: &T) -> ApiResult {
|
|
||||||
let (body, content_type) = match self.encoding {
|
|
||||||
ApiEncodingFormat::JSON => (
|
|
||||||
Body::from(serde_json::to_string(&item).map_err(|e| {
|
|
||||||
ApiError::ServerError(format!(
|
|
||||||
"Unable to serialize response body as JSON: {:?}",
|
|
||||||
e
|
|
||||||
))
|
|
||||||
})?),
|
|
||||||
"application/json",
|
|
||||||
),
|
|
||||||
ApiEncodingFormat::SSZ => {
|
|
||||||
return Err(ApiError::UnsupportedType(
|
|
||||||
"Response cannot be encoded as SSZ.".into(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
ApiEncodingFormat::YAML => (
|
|
||||||
Body::from(serde_yaml::to_string(&item).map_err(|e| {
|
|
||||||
ApiError::ServerError(format!(
|
|
||||||
"Unable to serialize response body as YAML: {:?}",
|
|
||||||
e
|
|
||||||
))
|
|
||||||
})?),
|
|
||||||
"application/yaml",
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
Response::builder()
|
|
||||||
.status(StatusCode::OK)
|
|
||||||
.header("content-type", content_type)
|
|
||||||
.body(body)
|
|
||||||
.map_err(|e| ApiError::ServerError(format!("Failed to build response: {:?}", e)))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn body_text(self, text: String) -> ApiResult {
|
|
||||||
Response::builder()
|
|
||||||
.status(StatusCode::OK)
|
|
||||||
.header("content-type", "text/plain; charset=utf-8")
|
|
||||||
.body(Body::from(text))
|
|
||||||
.map_err(|e| ApiError::ServerError(format!("Failed to build response: {:?}", e)))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,241 +1,322 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
advanced, beacon, config::Config, consensus, error::ApiError, helpers, lighthouse, metrics,
|
beacon, config::Config, consensus, lighthouse, metrics, node, validator, NetworkChannel,
|
||||||
network, node, spec, validator, NetworkChannel,
|
|
||||||
};
|
};
|
||||||
use beacon_chain::{BeaconChain, BeaconChainTypes};
|
use beacon_chain::{BeaconChain, BeaconChainTypes};
|
||||||
use bus::Bus;
|
use bus::Bus;
|
||||||
|
use environment::TaskExecutor;
|
||||||
use eth2_config::Eth2Config;
|
use eth2_config::Eth2Config;
|
||||||
use eth2_libp2p::NetworkGlobals;
|
use eth2_libp2p::{NetworkGlobals, PeerId};
|
||||||
use hyper::header::HeaderValue;
|
use hyper::header::HeaderValue;
|
||||||
use hyper::{Body, Method, Request, Response};
|
use hyper::{Body, Method, Request, Response};
|
||||||
|
use lighthouse_version::version_with_platform;
|
||||||
|
use operation_pool::PersistedOperationPool;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
use rest_types::{ApiError, Handler, Health};
|
||||||
use slog::debug;
|
use slog::debug;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use types::{SignedBeaconBlockHash, Slot};
|
use types::{EthSpec, SignedBeaconBlockHash};
|
||||||
|
|
||||||
// Allowing more than 7 arguments.
|
pub struct Context<T: BeaconChainTypes> {
|
||||||
#[allow(clippy::too_many_arguments)]
|
pub executor: TaskExecutor,
|
||||||
pub async fn route<T: BeaconChainTypes>(
|
pub config: Config,
|
||||||
|
pub beacon_chain: Arc<BeaconChain<T>>,
|
||||||
|
pub network_globals: Arc<NetworkGlobals<T::EthSpec>>,
|
||||||
|
pub network_chan: NetworkChannel<T::EthSpec>,
|
||||||
|
pub eth2_config: Arc<Eth2Config>,
|
||||||
|
pub log: slog::Logger,
|
||||||
|
pub db_path: PathBuf,
|
||||||
|
pub freezer_db_path: PathBuf,
|
||||||
|
pub events: Arc<Mutex<Bus<SignedBeaconBlockHash>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn on_http_request<T: BeaconChainTypes>(
|
||||||
req: Request<Body>,
|
req: Request<Body>,
|
||||||
beacon_chain: Arc<BeaconChain<T>>,
|
ctx: Arc<Context<T>>,
|
||||||
network_globals: Arc<NetworkGlobals<T::EthSpec>>,
|
|
||||||
network_channel: NetworkChannel<T::EthSpec>,
|
|
||||||
rest_api_config: Arc<Config>,
|
|
||||||
eth2_config: Arc<Eth2Config>,
|
|
||||||
local_log: slog::Logger,
|
|
||||||
db_path: PathBuf,
|
|
||||||
freezer_db_path: PathBuf,
|
|
||||||
events: Arc<Mutex<Bus<SignedBeaconBlockHash>>>,
|
|
||||||
) -> Result<Response<Body>, ApiError> {
|
) -> Result<Response<Body>, ApiError> {
|
||||||
metrics::inc_counter(&metrics::REQUEST_COUNT);
|
|
||||||
let received_instant = Instant::now();
|
|
||||||
|
|
||||||
let path = req.uri().path().to_string();
|
let path = req.uri().path().to_string();
|
||||||
|
|
||||||
let log = local_log.clone();
|
let _timer = metrics::start_timer_vec(&metrics::BEACON_HTTP_API_TIMES_TOTAL, &[&path]);
|
||||||
let result = {
|
metrics::inc_counter_vec(&metrics::BEACON_HTTP_API_REQUESTS_TOTAL, &[&path]);
|
||||||
let _timer = metrics::start_timer(&metrics::REQUEST_RESPONSE_TIME);
|
|
||||||
|
|
||||||
match (req.method(), path.as_ref()) {
|
let received_instant = Instant::now();
|
||||||
// Methods for Client
|
let log = ctx.log.clone();
|
||||||
(&Method::GET, "/node/health") => node::get_health(req),
|
let allow_origin = ctx.config.allow_origin.clone();
|
||||||
(&Method::GET, "/node/version") => node::get_version(req),
|
|
||||||
(&Method::GET, "/node/syncing") => {
|
|
||||||
// inform the current slot, or set to 0
|
|
||||||
let current_slot = beacon_chain
|
|
||||||
.head_info()
|
|
||||||
.map(|info| info.slot)
|
|
||||||
.unwrap_or_else(|_| Slot::from(0u64));
|
|
||||||
|
|
||||||
node::syncing::<T::EthSpec>(req, network_globals, current_slot)
|
match route(req, ctx).await {
|
||||||
}
|
|
||||||
|
|
||||||
// Methods for Network
|
|
||||||
(&Method::GET, "/network/enr") => network::get_enr::<T>(req, network_globals),
|
|
||||||
(&Method::GET, "/network/peer_count") => {
|
|
||||||
network::get_peer_count::<T>(req, network_globals)
|
|
||||||
}
|
|
||||||
(&Method::GET, "/network/peer_id") => network::get_peer_id::<T>(req, network_globals),
|
|
||||||
(&Method::GET, "/network/peers") => network::get_peer_list::<T>(req, network_globals),
|
|
||||||
(&Method::GET, "/network/listen_port") => {
|
|
||||||
network::get_listen_port::<T>(req, network_globals)
|
|
||||||
}
|
|
||||||
(&Method::GET, "/network/listen_addresses") => {
|
|
||||||
network::get_listen_addresses::<T>(req, network_globals)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Methods for Beacon Node
|
|
||||||
(&Method::GET, "/beacon/head") => beacon::get_head::<T>(req, beacon_chain),
|
|
||||||
(&Method::GET, "/beacon/heads") => beacon::get_heads::<T>(req, beacon_chain),
|
|
||||||
(&Method::GET, "/beacon/block") => beacon::get_block::<T>(req, beacon_chain),
|
|
||||||
(&Method::GET, "/beacon/block_root") => beacon::get_block_root::<T>(req, beacon_chain),
|
|
||||||
(&Method::GET, "/beacon/fork") => beacon::get_fork::<T>(req, beacon_chain),
|
|
||||||
(&Method::GET, "/beacon/fork/stream") => {
|
|
||||||
let reader = events.lock().add_rx();
|
|
||||||
beacon::stream_forks::<T>(log, reader)
|
|
||||||
}
|
|
||||||
(&Method::GET, "/beacon/genesis_time") => {
|
|
||||||
beacon::get_genesis_time::<T>(req, beacon_chain)
|
|
||||||
}
|
|
||||||
(&Method::GET, "/beacon/genesis_validators_root") => {
|
|
||||||
beacon::get_genesis_validators_root::<T>(req, beacon_chain)
|
|
||||||
}
|
|
||||||
(&Method::GET, "/beacon/validators") => beacon::get_validators::<T>(req, beacon_chain),
|
|
||||||
(&Method::POST, "/beacon/validators") => {
|
|
||||||
beacon::post_validators::<T>(req, beacon_chain).await
|
|
||||||
}
|
|
||||||
(&Method::GET, "/beacon/validators/all") => {
|
|
||||||
beacon::get_all_validators::<T>(req, beacon_chain)
|
|
||||||
}
|
|
||||||
(&Method::GET, "/beacon/validators/active") => {
|
|
||||||
beacon::get_active_validators::<T>(req, beacon_chain)
|
|
||||||
}
|
|
||||||
(&Method::GET, "/beacon/state") => beacon::get_state::<T>(req, beacon_chain),
|
|
||||||
(&Method::GET, "/beacon/state_root") => beacon::get_state_root::<T>(req, beacon_chain),
|
|
||||||
(&Method::GET, "/beacon/state/genesis") => {
|
|
||||||
beacon::get_genesis_state::<T>(req, beacon_chain)
|
|
||||||
}
|
|
||||||
(&Method::GET, "/beacon/committees") => beacon::get_committees::<T>(req, beacon_chain),
|
|
||||||
(&Method::POST, "/beacon/proposer_slashing") => {
|
|
||||||
beacon::proposer_slashing::<T>(req, beacon_chain).await
|
|
||||||
}
|
|
||||||
(&Method::POST, "/beacon/attester_slashing") => {
|
|
||||||
beacon::attester_slashing::<T>(req, beacon_chain).await
|
|
||||||
}
|
|
||||||
|
|
||||||
// Methods for Validator
|
|
||||||
(&Method::POST, "/validator/duties") => {
|
|
||||||
let timer =
|
|
||||||
metrics::start_timer(&metrics::VALIDATOR_GET_DUTIES_REQUEST_RESPONSE_TIME);
|
|
||||||
let response = validator::post_validator_duties::<T>(req, beacon_chain);
|
|
||||||
drop(timer);
|
|
||||||
response.await
|
|
||||||
}
|
|
||||||
(&Method::POST, "/validator/subscribe") => {
|
|
||||||
validator::post_validator_subscriptions::<T>(req, network_channel).await
|
|
||||||
}
|
|
||||||
(&Method::GET, "/validator/duties/all") => {
|
|
||||||
validator::get_all_validator_duties::<T>(req, beacon_chain)
|
|
||||||
}
|
|
||||||
(&Method::GET, "/validator/duties/active") => {
|
|
||||||
validator::get_active_validator_duties::<T>(req, beacon_chain)
|
|
||||||
}
|
|
||||||
(&Method::GET, "/validator/block") => {
|
|
||||||
let timer =
|
|
||||||
metrics::start_timer(&metrics::VALIDATOR_GET_BLOCK_REQUEST_RESPONSE_TIME);
|
|
||||||
let response = validator::get_new_beacon_block::<T>(req, beacon_chain, log);
|
|
||||||
drop(timer);
|
|
||||||
response
|
|
||||||
}
|
|
||||||
(&Method::POST, "/validator/block") => {
|
|
||||||
validator::publish_beacon_block::<T>(req, beacon_chain, network_channel, log).await
|
|
||||||
}
|
|
||||||
(&Method::GET, "/validator/attestation") => {
|
|
||||||
let timer =
|
|
||||||
metrics::start_timer(&metrics::VALIDATOR_GET_ATTESTATION_REQUEST_RESPONSE_TIME);
|
|
||||||
let response = validator::get_new_attestation::<T>(req, beacon_chain);
|
|
||||||
drop(timer);
|
|
||||||
response
|
|
||||||
}
|
|
||||||
(&Method::GET, "/validator/aggregate_attestation") => {
|
|
||||||
validator::get_aggregate_attestation::<T>(req, beacon_chain)
|
|
||||||
}
|
|
||||||
(&Method::POST, "/validator/attestations") => {
|
|
||||||
validator::publish_attestations::<T>(req, beacon_chain, network_channel, log).await
|
|
||||||
}
|
|
||||||
(&Method::POST, "/validator/aggregate_and_proofs") => {
|
|
||||||
validator::publish_aggregate_and_proofs::<T>(
|
|
||||||
req,
|
|
||||||
beacon_chain,
|
|
||||||
network_channel,
|
|
||||||
log,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
// Methods for consensus
|
|
||||||
(&Method::GET, "/consensus/global_votes") => {
|
|
||||||
consensus::get_vote_count::<T>(req, beacon_chain)
|
|
||||||
}
|
|
||||||
(&Method::POST, "/consensus/individual_votes") => {
|
|
||||||
consensus::post_individual_votes::<T>(req, beacon_chain).await
|
|
||||||
}
|
|
||||||
|
|
||||||
// Methods for bootstrap and checking configuration
|
|
||||||
(&Method::GET, "/spec") => spec::get_spec::<T>(req, beacon_chain),
|
|
||||||
(&Method::GET, "/spec/slots_per_epoch") => spec::get_slots_per_epoch::<T>(req),
|
|
||||||
(&Method::GET, "/spec/deposit_contract") => {
|
|
||||||
helpers::implementation_pending_response(req)
|
|
||||||
}
|
|
||||||
(&Method::GET, "/spec/eth2_config") => spec::get_eth2_config::<T>(req, eth2_config),
|
|
||||||
|
|
||||||
// Methods for advanced parameters
|
|
||||||
(&Method::GET, "/advanced/fork_choice") => {
|
|
||||||
advanced::get_fork_choice::<T>(req, beacon_chain)
|
|
||||||
}
|
|
||||||
(&Method::GET, "/advanced/operation_pool") => {
|
|
||||||
advanced::get_operation_pool::<T>(req, beacon_chain)
|
|
||||||
}
|
|
||||||
|
|
||||||
(&Method::GET, "/metrics") => {
|
|
||||||
metrics::get_prometheus::<T>(req, beacon_chain, db_path, freezer_db_path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lighthouse specific
|
|
||||||
(&Method::GET, "/lighthouse/syncing") => {
|
|
||||||
lighthouse::syncing::<T::EthSpec>(req, network_globals)
|
|
||||||
}
|
|
||||||
|
|
||||||
(&Method::GET, "/lighthouse/peers") => {
|
|
||||||
lighthouse::peers::<T::EthSpec>(req, network_globals)
|
|
||||||
}
|
|
||||||
|
|
||||||
(&Method::GET, "/lighthouse/connected_peers") => {
|
|
||||||
lighthouse::connected_peers::<T::EthSpec>(req, network_globals)
|
|
||||||
}
|
|
||||||
_ => Err(ApiError::NotFound(
|
|
||||||
"Request path and/or method not found.".to_owned(),
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let request_processing_duration = Instant::now().duration_since(received_instant);
|
|
||||||
|
|
||||||
// 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).
|
|
||||||
|
|
||||||
match result {
|
|
||||||
Ok(mut response) => {
|
Ok(mut response) => {
|
||||||
if rest_api_config.allow_origin != "" {
|
metrics::inc_counter_vec(&metrics::BEACON_HTTP_API_SUCCESS_TOTAL, &[&path]);
|
||||||
|
|
||||||
|
if allow_origin != "" {
|
||||||
let headers = response.headers_mut();
|
let headers = response.headers_mut();
|
||||||
headers.insert(
|
headers.insert(
|
||||||
hyper::header::ACCESS_CONTROL_ALLOW_ORIGIN,
|
hyper::header::ACCESS_CONTROL_ALLOW_ORIGIN,
|
||||||
HeaderValue::from_str(&rest_api_config.allow_origin)?,
|
HeaderValue::from_str(&allow_origin)?,
|
||||||
);
|
);
|
||||||
headers.insert(hyper::header::VARY, HeaderValue::from_static("Origin"));
|
headers.insert(hyper::header::VARY, HeaderValue::from_static("Origin"));
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
local_log,
|
log,
|
||||||
"HTTP API request successful";
|
"HTTP API request successful";
|
||||||
"path" => path,
|
"path" => path,
|
||||||
"duration_ms" => request_processing_duration.as_millis()
|
"duration_ms" => Instant::now().duration_since(received_instant).as_millis()
|
||||||
);
|
);
|
||||||
metrics::inc_counter(&metrics::SUCCESS_COUNT);
|
|
||||||
Ok(response)
|
Ok(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
|
metrics::inc_counter_vec(&metrics::BEACON_HTTP_API_ERROR_TOTAL, &[&path]);
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
local_log,
|
log,
|
||||||
"HTTP API request failure";
|
"HTTP API request failure";
|
||||||
"path" => path,
|
"path" => path,
|
||||||
"duration_ms" => request_processing_duration.as_millis()
|
"duration_ms" => Instant::now().duration_since(received_instant).as_millis()
|
||||||
);
|
);
|
||||||
Ok(error.into())
|
Ok(error.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn route<T: BeaconChainTypes>(
|
||||||
|
req: Request<Body>,
|
||||||
|
ctx: Arc<Context<T>>,
|
||||||
|
) -> Result<Response<Body>, ApiError> {
|
||||||
|
let path = req.uri().path().to_string();
|
||||||
|
let ctx = ctx.clone();
|
||||||
|
let method = req.method().clone();
|
||||||
|
let executor = ctx.executor.clone();
|
||||||
|
let handler = Handler::new(req, ctx, executor)?;
|
||||||
|
|
||||||
|
match (method, path.as_ref()) {
|
||||||
|
(Method::GET, "/node/version") => handler
|
||||||
|
.static_value(version_with_platform())
|
||||||
|
.await?
|
||||||
|
.serde_encodings(),
|
||||||
|
(Method::GET, "/node/health") => handler
|
||||||
|
.static_value(Health::observe().map_err(ApiError::ServerError)?)
|
||||||
|
.await?
|
||||||
|
.serde_encodings(),
|
||||||
|
(Method::GET, "/node/syncing") => handler
|
||||||
|
.allow_body()
|
||||||
|
.in_blocking_task(|_, ctx| node::syncing(ctx))
|
||||||
|
.await?
|
||||||
|
.serde_encodings(),
|
||||||
|
(Method::GET, "/network/enr") => handler
|
||||||
|
.in_core_task(|_, ctx| Ok(ctx.network_globals.local_enr().to_base64()))
|
||||||
|
.await?
|
||||||
|
.serde_encodings(),
|
||||||
|
(Method::GET, "/network/peer_count") => handler
|
||||||
|
.in_core_task(|_, ctx| Ok(ctx.network_globals.connected_peers()))
|
||||||
|
.await?
|
||||||
|
.serde_encodings(),
|
||||||
|
(Method::GET, "/network/peer_id") => handler
|
||||||
|
.in_core_task(|_, ctx| Ok(ctx.network_globals.local_peer_id().to_base58()))
|
||||||
|
.await?
|
||||||
|
.serde_encodings(),
|
||||||
|
(Method::GET, "/network/peers") => handler
|
||||||
|
.in_blocking_task(|_, ctx| {
|
||||||
|
Ok(ctx
|
||||||
|
.network_globals
|
||||||
|
.peers
|
||||||
|
.read()
|
||||||
|
.connected_peer_ids()
|
||||||
|
.map(PeerId::to_string)
|
||||||
|
.collect::<Vec<_>>())
|
||||||
|
})
|
||||||
|
.await?
|
||||||
|
.serde_encodings(),
|
||||||
|
(Method::GET, "/network/listen_port") => handler
|
||||||
|
.in_core_task(|_, ctx| Ok(ctx.network_globals.listen_port_tcp()))
|
||||||
|
.await?
|
||||||
|
.serde_encodings(),
|
||||||
|
(Method::GET, "/network/listen_addresses") => handler
|
||||||
|
.in_blocking_task(|_, ctx| Ok(ctx.network_globals.listen_multiaddrs()))
|
||||||
|
.await?
|
||||||
|
.serde_encodings(),
|
||||||
|
(Method::GET, "/beacon/head") => handler
|
||||||
|
.in_blocking_task(|_, ctx| beacon::get_head(ctx))
|
||||||
|
.await?
|
||||||
|
.all_encodings(),
|
||||||
|
(Method::GET, "/beacon/heads") => handler
|
||||||
|
.in_blocking_task(|_, ctx| Ok(beacon::get_heads(ctx)))
|
||||||
|
.await?
|
||||||
|
.all_encodings(),
|
||||||
|
(Method::GET, "/beacon/block") => handler
|
||||||
|
.in_blocking_task(beacon::get_block)
|
||||||
|
.await?
|
||||||
|
.all_encodings(),
|
||||||
|
(Method::GET, "/beacon/block_root") => handler
|
||||||
|
.in_blocking_task(beacon::get_block_root)
|
||||||
|
.await?
|
||||||
|
.all_encodings(),
|
||||||
|
(Method::GET, "/beacon/fork") => handler
|
||||||
|
.in_blocking_task(|_, ctx| Ok(ctx.beacon_chain.head_info()?.fork))
|
||||||
|
.await?
|
||||||
|
.all_encodings(),
|
||||||
|
(Method::GET, "/beacon/fork/stream") => {
|
||||||
|
handler.sse_stream(|_, ctx| beacon::stream_forks(ctx)).await
|
||||||
|
}
|
||||||
|
(Method::GET, "/beacon/genesis_time") => handler
|
||||||
|
.in_blocking_task(|_, ctx| Ok(ctx.beacon_chain.head_info()?.genesis_time))
|
||||||
|
.await?
|
||||||
|
.all_encodings(),
|
||||||
|
(Method::GET, "/beacon/genesis_validators_root") => handler
|
||||||
|
.in_blocking_task(|_, ctx| Ok(ctx.beacon_chain.head_info()?.genesis_validators_root))
|
||||||
|
.await?
|
||||||
|
.all_encodings(),
|
||||||
|
(Method::GET, "/beacon/validators") => handler
|
||||||
|
.in_blocking_task(beacon::get_validators)
|
||||||
|
.await?
|
||||||
|
.all_encodings(),
|
||||||
|
(Method::POST, "/beacon/validators") => handler
|
||||||
|
.allow_body()
|
||||||
|
.in_blocking_task(beacon::post_validators)
|
||||||
|
.await?
|
||||||
|
.all_encodings(),
|
||||||
|
(Method::GET, "/beacon/validators/all") => handler
|
||||||
|
.in_blocking_task(beacon::get_all_validators)
|
||||||
|
.await?
|
||||||
|
.all_encodings(),
|
||||||
|
(Method::GET, "/beacon/validators/active") => handler
|
||||||
|
.in_blocking_task(beacon::get_active_validators)
|
||||||
|
.await?
|
||||||
|
.all_encodings(),
|
||||||
|
(Method::GET, "/beacon/state") => handler
|
||||||
|
.in_blocking_task(beacon::get_state)
|
||||||
|
.await?
|
||||||
|
.all_encodings(),
|
||||||
|
(Method::GET, "/beacon/state_root") => handler
|
||||||
|
.in_blocking_task(beacon::get_state_root)
|
||||||
|
.await?
|
||||||
|
.all_encodings(),
|
||||||
|
(Method::GET, "/beacon/state/genesis") => handler
|
||||||
|
.in_blocking_task(|_, ctx| beacon::get_genesis_state(ctx))
|
||||||
|
.await?
|
||||||
|
.all_encodings(),
|
||||||
|
(Method::GET, "/beacon/committees") => handler
|
||||||
|
.in_blocking_task(beacon::get_committees)
|
||||||
|
.await?
|
||||||
|
.all_encodings(),
|
||||||
|
(Method::POST, "/beacon/proposer_slashing") => handler
|
||||||
|
.allow_body()
|
||||||
|
.in_blocking_task(beacon::proposer_slashing)
|
||||||
|
.await?
|
||||||
|
.serde_encodings(),
|
||||||
|
(Method::POST, "/beacon/attester_slashing") => handler
|
||||||
|
.allow_body()
|
||||||
|
.in_blocking_task(beacon::attester_slashing)
|
||||||
|
.await?
|
||||||
|
.serde_encodings(),
|
||||||
|
(Method::POST, "/validator/duties") => handler
|
||||||
|
.allow_body()
|
||||||
|
.in_blocking_task(validator::post_validator_duties)
|
||||||
|
.await?
|
||||||
|
.serde_encodings(),
|
||||||
|
(Method::POST, "/validator/subscribe") => handler
|
||||||
|
.allow_body()
|
||||||
|
.in_blocking_task(validator::post_validator_subscriptions)
|
||||||
|
.await?
|
||||||
|
.serde_encodings(),
|
||||||
|
(Method::GET, "/validator/duties/all") => handler
|
||||||
|
.in_blocking_task(validator::get_all_validator_duties)
|
||||||
|
.await?
|
||||||
|
.serde_encodings(),
|
||||||
|
(Method::GET, "/validator/duties/active") => handler
|
||||||
|
.in_blocking_task(validator::get_active_validator_duties)
|
||||||
|
.await?
|
||||||
|
.serde_encodings(),
|
||||||
|
(Method::GET, "/validator/block") => handler
|
||||||
|
.in_blocking_task(validator::get_new_beacon_block)
|
||||||
|
.await?
|
||||||
|
.serde_encodings(),
|
||||||
|
(Method::POST, "/validator/block") => handler
|
||||||
|
.allow_body()
|
||||||
|
.in_blocking_task(validator::publish_beacon_block)
|
||||||
|
.await?
|
||||||
|
.serde_encodings(),
|
||||||
|
(Method::GET, "/validator/attestation") => handler
|
||||||
|
.in_blocking_task(validator::get_new_attestation)
|
||||||
|
.await?
|
||||||
|
.serde_encodings(),
|
||||||
|
(Method::GET, "/validator/aggregate_attestation") => handler
|
||||||
|
.in_blocking_task(validator::get_aggregate_attestation)
|
||||||
|
.await?
|
||||||
|
.serde_encodings(),
|
||||||
|
(Method::POST, "/validator/attestations") => handler
|
||||||
|
.allow_body()
|
||||||
|
.in_blocking_task(validator::publish_attestations)
|
||||||
|
.await?
|
||||||
|
.serde_encodings(),
|
||||||
|
(Method::POST, "/validator/aggregate_and_proofs") => handler
|
||||||
|
.allow_body()
|
||||||
|
.in_blocking_task(validator::publish_aggregate_and_proofs)
|
||||||
|
.await?
|
||||||
|
.serde_encodings(),
|
||||||
|
(Method::GET, "/consensus/global_votes") => handler
|
||||||
|
.allow_body()
|
||||||
|
.in_blocking_task(consensus::get_vote_count)
|
||||||
|
.await?
|
||||||
|
.serde_encodings(),
|
||||||
|
(Method::POST, "/consensus/individual_votes") => handler
|
||||||
|
.allow_body()
|
||||||
|
.in_blocking_task(consensus::post_individual_votes)
|
||||||
|
.await?
|
||||||
|
.serde_encodings(),
|
||||||
|
(Method::GET, "/spec") => handler
|
||||||
|
// TODO: this clone is not ideal.
|
||||||
|
.in_blocking_task(|_, ctx| Ok(ctx.beacon_chain.spec.clone()))
|
||||||
|
.await?
|
||||||
|
.serde_encodings(),
|
||||||
|
(Method::GET, "/spec/slots_per_epoch") => handler
|
||||||
|
.static_value(T::EthSpec::slots_per_epoch())
|
||||||
|
.await?
|
||||||
|
.serde_encodings(),
|
||||||
|
(Method::GET, "/spec/eth2_config") => handler
|
||||||
|
// TODO: this clone is not ideal.
|
||||||
|
.in_blocking_task(|_, ctx| Ok(ctx.eth2_config.as_ref().clone()))
|
||||||
|
.await?
|
||||||
|
.serde_encodings(),
|
||||||
|
(Method::GET, "/advanced/fork_choice") => handler
|
||||||
|
.in_blocking_task(|_, ctx| {
|
||||||
|
Ok(ctx
|
||||||
|
.beacon_chain
|
||||||
|
.fork_choice
|
||||||
|
.read()
|
||||||
|
.proto_array()
|
||||||
|
.core_proto_array()
|
||||||
|
.clone())
|
||||||
|
})
|
||||||
|
.await?
|
||||||
|
.serde_encodings(),
|
||||||
|
(Method::GET, "/advanced/operation_pool") => handler
|
||||||
|
.in_blocking_task(|_, ctx| {
|
||||||
|
Ok(PersistedOperationPool::from_operation_pool(
|
||||||
|
&ctx.beacon_chain.op_pool,
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.await?
|
||||||
|
.serde_encodings(),
|
||||||
|
(Method::GET, "/metrics") => handler
|
||||||
|
.in_blocking_task(|_, ctx| metrics::get_prometheus(ctx))
|
||||||
|
.await?
|
||||||
|
.text_encoding(),
|
||||||
|
(Method::GET, "/lighthouse/syncing") => handler
|
||||||
|
.in_blocking_task(|_, ctx| Ok(ctx.network_globals.sync_state()))
|
||||||
|
.await?
|
||||||
|
.serde_encodings(),
|
||||||
|
(Method::GET, "/lighthouse/peers") => handler
|
||||||
|
.in_blocking_task(|_, ctx| lighthouse::peers(ctx))
|
||||||
|
.await?
|
||||||
|
.serde_encodings(),
|
||||||
|
(Method::GET, "/lighthouse/connected_peers") => handler
|
||||||
|
.in_blocking_task(|_, ctx| lighthouse::connected_peers(ctx))
|
||||||
|
.await?
|
||||||
|
.serde_encodings(),
|
||||||
|
_ => Err(ApiError::NotFound(
|
||||||
|
"Request path and/or method not found.".to_owned(),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
use super::ApiResult;
|
|
||||||
use crate::response_builder::ResponseBuilder;
|
|
||||||
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<T: BeaconChainTypes>(
|
|
||||||
req: Request<Body>,
|
|
||||||
beacon_chain: Arc<BeaconChain<T>>,
|
|
||||||
) -> ApiResult {
|
|
||||||
ResponseBuilder::new(&req)?.body_no_ssz(&beacon_chain.spec)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// HTTP handler to return the full Eth2Config object.
|
|
||||||
pub fn get_eth2_config<T: BeaconChainTypes>(
|
|
||||||
req: Request<Body>,
|
|
||||||
eth2_config: Arc<Eth2Config>,
|
|
||||||
) -> ApiResult {
|
|
||||||
ResponseBuilder::new(&req)?.body_no_ssz(eth2_config.as_ref())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// HTTP handler to return the full spec object.
|
|
||||||
pub fn get_slots_per_epoch<T: BeaconChainTypes>(req: Request<Body>) -> ApiResult {
|
|
||||||
ResponseBuilder::new(&req)?.body(&T::EthSpec::slots_per_epoch())
|
|
||||||
}
|
|
@ -1,15 +1,12 @@
|
|||||||
use crate::helpers::{
|
use crate::helpers::{parse_hex_ssz_bytes, publish_beacon_block_to_network};
|
||||||
check_content_type_for_json, parse_hex_ssz_bytes, publish_beacon_block_to_network,
|
use crate::{ApiError, Context, NetworkChannel, UrlQuery};
|
||||||
};
|
|
||||||
use crate::response_builder::ResponseBuilder;
|
|
||||||
use crate::{ApiError, ApiResult, NetworkChannel, UrlQuery};
|
|
||||||
use beacon_chain::{
|
use beacon_chain::{
|
||||||
attestation_verification::Error as AttnError, BeaconChain, BeaconChainError, BeaconChainTypes,
|
attestation_verification::Error as AttnError, BeaconChain, BeaconChainError, BeaconChainTypes,
|
||||||
BlockError, ForkChoiceError, StateSkipConfig,
|
BlockError, ForkChoiceError, StateSkipConfig,
|
||||||
};
|
};
|
||||||
use bls::PublicKeyBytes;
|
use bls::PublicKeyBytes;
|
||||||
use eth2_libp2p::PubsubMessage;
|
use eth2_libp2p::PubsubMessage;
|
||||||
use hyper::{Body, Request};
|
use hyper::Request;
|
||||||
use network::NetworkMessage;
|
use network::NetworkMessage;
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use rest_types::{ValidatorDutiesRequest, ValidatorDutyBytes, ValidatorSubscription};
|
use rest_types::{ValidatorDutiesRequest, ValidatorDutyBytes, ValidatorSubscription};
|
||||||
@ -17,25 +14,20 @@ use slog::{error, info, trace, warn, Logger};
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use types::beacon_state::EthSpec;
|
use types::beacon_state::EthSpec;
|
||||||
use types::{
|
use types::{
|
||||||
Attestation, AttestationData, BeaconState, Epoch, RelativeEpoch, SelectionProof,
|
Attestation, AttestationData, BeaconBlock, BeaconState, Epoch, RelativeEpoch, SelectionProof,
|
||||||
SignedAggregateAndProof, SignedBeaconBlock, SubnetId,
|
SignedAggregateAndProof, SignedBeaconBlock, SubnetId,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// HTTP Handler to retrieve the duties for a set of validators during a particular epoch. This
|
/// HTTP Handler to retrieve the duties for a set of validators during a particular epoch. This
|
||||||
/// method allows for collecting bulk sets of validator duties without risking exceeding the max
|
/// method allows for collecting bulk sets of validator duties without risking exceeding the max
|
||||||
/// URL length with query pairs.
|
/// URL length with query pairs.
|
||||||
pub async fn post_validator_duties<T: BeaconChainTypes>(
|
pub fn post_validator_duties<T: BeaconChainTypes>(
|
||||||
req: Request<Body>,
|
req: Request<Vec<u8>>,
|
||||||
beacon_chain: Arc<BeaconChain<T>>,
|
ctx: Arc<Context<T>>,
|
||||||
) -> ApiResult {
|
) -> Result<Vec<ValidatorDutyBytes>, ApiError> {
|
||||||
let response_builder = ResponseBuilder::new(&req);
|
|
||||||
|
|
||||||
let body = req.into_body();
|
let body = req.into_body();
|
||||||
let chunks = hyper::body::to_bytes(body)
|
|
||||||
.await
|
|
||||||
.map_err(|e| ApiError::ServerError(format!("Unable to get request body: {:?}", e)))?;
|
|
||||||
|
|
||||||
serde_json::from_slice::<ValidatorDutiesRequest>(&chunks)
|
serde_json::from_slice::<ValidatorDutiesRequest>(&body)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
ApiError::BadRequest(format!(
|
ApiError::BadRequest(format!(
|
||||||
"Unable to parse JSON into ValidatorDutiesRequest: {:?}",
|
"Unable to parse JSON into ValidatorDutiesRequest: {:?}",
|
||||||
@ -44,29 +36,22 @@ pub async fn post_validator_duties<T: BeaconChainTypes>(
|
|||||||
})
|
})
|
||||||
.and_then(|bulk_request| {
|
.and_then(|bulk_request| {
|
||||||
return_validator_duties(
|
return_validator_duties(
|
||||||
beacon_chain,
|
&ctx.beacon_chain.clone(),
|
||||||
bulk_request.epoch,
|
bulk_request.epoch,
|
||||||
bulk_request.pubkeys.into_iter().map(Into::into).collect(),
|
bulk_request.pubkeys.into_iter().map(Into::into).collect(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.and_then(|duties| response_builder?.body_no_ssz(&duties))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// HTTP Handler to retrieve subscriptions for a set of validators. This allows the node to
|
/// HTTP Handler to retrieve subscriptions for a set of validators. This allows the node to
|
||||||
/// organise peer discovery and topic subscription for known validators.
|
/// organise peer discovery and topic subscription for known validators.
|
||||||
pub async fn post_validator_subscriptions<T: BeaconChainTypes>(
|
pub fn post_validator_subscriptions<T: BeaconChainTypes>(
|
||||||
req: Request<Body>,
|
req: Request<Vec<u8>>,
|
||||||
network_chan: NetworkChannel<T::EthSpec>,
|
ctx: Arc<Context<T>>,
|
||||||
) -> ApiResult {
|
) -> Result<(), ApiError> {
|
||||||
try_future!(check_content_type_for_json(&req));
|
|
||||||
let response_builder = ResponseBuilder::new(&req);
|
|
||||||
|
|
||||||
let body = req.into_body();
|
let body = req.into_body();
|
||||||
let chunks = hyper::body::to_bytes(body)
|
|
||||||
.await
|
|
||||||
.map_err(|e| ApiError::ServerError(format!("Unable to get request body: {:?}", e)))?;
|
|
||||||
|
|
||||||
serde_json::from_slice(&chunks)
|
serde_json::from_slice(&body)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
ApiError::BadRequest(format!(
|
ApiError::BadRequest(format!(
|
||||||
"Unable to parse JSON into ValidatorSubscriptions: {:?}",
|
"Unable to parse JSON into ValidatorSubscriptions: {:?}",
|
||||||
@ -74,7 +59,7 @@ pub async fn post_validator_subscriptions<T: BeaconChainTypes>(
|
|||||||
))
|
))
|
||||||
})
|
})
|
||||||
.and_then(move |subscriptions: Vec<ValidatorSubscription>| {
|
.and_then(move |subscriptions: Vec<ValidatorSubscription>| {
|
||||||
network_chan
|
ctx.network_chan
|
||||||
.send(NetworkMessage::Subscribe { subscriptions })
|
.send(NetworkMessage::Subscribe { subscriptions })
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
ApiError::ServerError(format!(
|
ApiError::ServerError(format!(
|
||||||
@ -84,19 +69,18 @@ pub async fn post_validator_subscriptions<T: BeaconChainTypes>(
|
|||||||
})?;
|
})?;
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
.and_then(|_| response_builder?.body_no_ssz(&()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// HTTP Handler to retrieve all validator duties for the given epoch.
|
/// HTTP Handler to retrieve all validator duties for the given epoch.
|
||||||
pub fn get_all_validator_duties<T: BeaconChainTypes>(
|
pub fn get_all_validator_duties<T: BeaconChainTypes>(
|
||||||
req: Request<Body>,
|
req: Request<Vec<u8>>,
|
||||||
beacon_chain: Arc<BeaconChain<T>>,
|
ctx: Arc<Context<T>>,
|
||||||
) -> ApiResult {
|
) -> Result<Vec<ValidatorDutyBytes>, ApiError> {
|
||||||
let query = UrlQuery::from_request(&req)?;
|
let query = UrlQuery::from_request(&req)?;
|
||||||
|
|
||||||
let epoch = query.epoch()?;
|
let epoch = query.epoch()?;
|
||||||
|
|
||||||
let state = get_state_for_epoch(&beacon_chain, epoch, StateSkipConfig::WithoutStateRoots)?;
|
let state = get_state_for_epoch(&ctx.beacon_chain, epoch, StateSkipConfig::WithoutStateRoots)?;
|
||||||
|
|
||||||
let validator_pubkeys = state
|
let validator_pubkeys = state
|
||||||
.validators
|
.validators
|
||||||
@ -104,21 +88,19 @@ pub fn get_all_validator_duties<T: BeaconChainTypes>(
|
|||||||
.map(|validator| validator.pubkey.clone())
|
.map(|validator| validator.pubkey.clone())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let duties = return_validator_duties(beacon_chain, epoch, validator_pubkeys)?;
|
return_validator_duties(&ctx.beacon_chain, epoch, validator_pubkeys)
|
||||||
|
|
||||||
ResponseBuilder::new(&req)?.body_no_ssz(&duties)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// HTTP Handler to retrieve all active validator duties for the given epoch.
|
/// HTTP Handler to retrieve all active validator duties for the given epoch.
|
||||||
pub fn get_active_validator_duties<T: BeaconChainTypes>(
|
pub fn get_active_validator_duties<T: BeaconChainTypes>(
|
||||||
req: Request<Body>,
|
req: Request<Vec<u8>>,
|
||||||
beacon_chain: Arc<BeaconChain<T>>,
|
ctx: Arc<Context<T>>,
|
||||||
) -> ApiResult {
|
) -> Result<Vec<ValidatorDutyBytes>, ApiError> {
|
||||||
let query = UrlQuery::from_request(&req)?;
|
let query = UrlQuery::from_request(&req)?;
|
||||||
|
|
||||||
let epoch = query.epoch()?;
|
let epoch = query.epoch()?;
|
||||||
|
|
||||||
let state = get_state_for_epoch(&beacon_chain, epoch, StateSkipConfig::WithoutStateRoots)?;
|
let state = get_state_for_epoch(&ctx.beacon_chain, epoch, StateSkipConfig::WithoutStateRoots)?;
|
||||||
|
|
||||||
let validator_pubkeys = state
|
let validator_pubkeys = state
|
||||||
.validators
|
.validators
|
||||||
@ -127,9 +109,7 @@ pub fn get_active_validator_duties<T: BeaconChainTypes>(
|
|||||||
.map(|validator| validator.pubkey.clone())
|
.map(|validator| validator.pubkey.clone())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let duties = return_validator_duties(beacon_chain, epoch, validator_pubkeys)?;
|
return_validator_duties(&ctx.beacon_chain, epoch, validator_pubkeys)
|
||||||
|
|
||||||
ResponseBuilder::new(&req)?.body_no_ssz(&duties)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper function to return the state that can be used to determine the duties for some `epoch`.
|
/// Helper function to return the state that can be used to determine the duties for some `epoch`.
|
||||||
@ -165,7 +145,7 @@ pub fn get_state_for_epoch<T: BeaconChainTypes>(
|
|||||||
|
|
||||||
/// Helper function to get the duties for some `validator_pubkeys` in some `epoch`.
|
/// Helper function to get the duties for some `validator_pubkeys` in some `epoch`.
|
||||||
fn return_validator_duties<T: BeaconChainTypes>(
|
fn return_validator_duties<T: BeaconChainTypes>(
|
||||||
beacon_chain: Arc<BeaconChain<T>>,
|
beacon_chain: &BeaconChain<T>,
|
||||||
epoch: Epoch,
|
epoch: Epoch,
|
||||||
validator_pubkeys: Vec<PublicKeyBytes>,
|
validator_pubkeys: Vec<PublicKeyBytes>,
|
||||||
) -> Result<Vec<ValidatorDutyBytes>, ApiError> {
|
) -> Result<Vec<ValidatorDutyBytes>, ApiError> {
|
||||||
@ -281,10 +261,9 @@ fn return_validator_duties<T: BeaconChainTypes>(
|
|||||||
|
|
||||||
/// HTTP Handler to produce a new BeaconBlock from the current state, ready to be signed by a validator.
|
/// HTTP Handler to produce a new BeaconBlock from the current state, ready to be signed by a validator.
|
||||||
pub fn get_new_beacon_block<T: BeaconChainTypes>(
|
pub fn get_new_beacon_block<T: BeaconChainTypes>(
|
||||||
req: Request<Body>,
|
req: Request<Vec<u8>>,
|
||||||
beacon_chain: Arc<BeaconChain<T>>,
|
ctx: Arc<Context<T>>,
|
||||||
log: Logger,
|
) -> Result<BeaconBlock<T::EthSpec>, ApiError> {
|
||||||
) -> ApiResult {
|
|
||||||
let query = UrlQuery::from_request(&req)?;
|
let query = UrlQuery::from_request(&req)?;
|
||||||
|
|
||||||
let slot = query.slot()?;
|
let slot = query.slot()?;
|
||||||
@ -296,11 +275,12 @@ pub fn get_new_beacon_block<T: BeaconChainTypes>(
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let (new_block, _state) = beacon_chain
|
let (new_block, _state) = ctx
|
||||||
|
.beacon_chain
|
||||||
.produce_block(randao_reveal, slot, validator_graffiti)
|
.produce_block(randao_reveal, slot, validator_graffiti)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
error!(
|
error!(
|
||||||
log,
|
ctx.log,
|
||||||
"Error whilst producing block";
|
"Error whilst producing block";
|
||||||
"error" => format!("{:?}", e)
|
"error" => format!("{:?}", e)
|
||||||
);
|
);
|
||||||
@ -311,48 +291,40 @@ pub fn get_new_beacon_block<T: BeaconChainTypes>(
|
|||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
ResponseBuilder::new(&req)?.body(&new_block)
|
Ok(new_block)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// HTTP Handler to publish a SignedBeaconBlock, which has been signed by a validator.
|
/// HTTP Handler to publish a SignedBeaconBlock, which has been signed by a validator.
|
||||||
pub async fn publish_beacon_block<T: BeaconChainTypes>(
|
pub fn publish_beacon_block<T: BeaconChainTypes>(
|
||||||
req: Request<Body>,
|
req: Request<Vec<u8>>,
|
||||||
beacon_chain: Arc<BeaconChain<T>>,
|
ctx: Arc<Context<T>>,
|
||||||
network_chan: NetworkChannel<T::EthSpec>,
|
) -> Result<(), ApiError> {
|
||||||
log: Logger,
|
|
||||||
) -> ApiResult {
|
|
||||||
try_future!(check_content_type_for_json(&req));
|
|
||||||
let response_builder = ResponseBuilder::new(&req);
|
|
||||||
|
|
||||||
let body = req.into_body();
|
let body = req.into_body();
|
||||||
let chunks = hyper::body::to_bytes(body)
|
|
||||||
.await
|
|
||||||
.map_err(|e| ApiError::ServerError(format!("Unable to get request body: {:?}", e)))?;
|
|
||||||
|
|
||||||
serde_json::from_slice(&chunks).map_err(|e| {
|
serde_json::from_slice(&body).map_err(|e| {
|
||||||
ApiError::BadRequest(format!("Unable to parse JSON into SignedBeaconBlock: {:?}", e))
|
ApiError::BadRequest(format!("Unable to parse JSON into SignedBeaconBlock: {:?}", e))
|
||||||
})
|
})
|
||||||
.and_then(move |block: SignedBeaconBlock<T::EthSpec>| {
|
.and_then(move |block: SignedBeaconBlock<T::EthSpec>| {
|
||||||
let slot = block.slot();
|
let slot = block.slot();
|
||||||
match beacon_chain.process_block(block.clone()) {
|
match ctx.beacon_chain.process_block(block.clone()) {
|
||||||
Ok(block_root) => {
|
Ok(block_root) => {
|
||||||
// Block was processed, publish via gossipsub
|
// Block was processed, publish via gossipsub
|
||||||
info!(
|
info!(
|
||||||
log,
|
ctx.log,
|
||||||
"Block from local validator";
|
"Block from local validator";
|
||||||
"block_root" => format!("{}", block_root),
|
"block_root" => format!("{}", block_root),
|
||||||
"block_slot" => slot,
|
"block_slot" => slot,
|
||||||
);
|
);
|
||||||
|
|
||||||
publish_beacon_block_to_network::<T>(network_chan, block)?;
|
publish_beacon_block_to_network::<T>(&ctx.network_chan, block)?;
|
||||||
|
|
||||||
// Run the fork choice algorithm and enshrine a new canonical head, if
|
// Run the fork choice algorithm and enshrine a new canonical head, if
|
||||||
// found.
|
// found.
|
||||||
//
|
//
|
||||||
// The new head may or may not be the block we just received.
|
// The new head may or may not be the block we just received.
|
||||||
if let Err(e) = beacon_chain.fork_choice() {
|
if let Err(e) = ctx.beacon_chain.fork_choice() {
|
||||||
error!(
|
error!(
|
||||||
log,
|
ctx.log,
|
||||||
"Failed to find beacon chain head";
|
"Failed to find beacon chain head";
|
||||||
"error" => format!("{:?}", e)
|
"error" => format!("{:?}", e)
|
||||||
);
|
);
|
||||||
@ -366,9 +338,9 @@ pub async fn publish_beacon_block<T: BeaconChainTypes>(
|
|||||||
// - Excessive time between block produce and publish.
|
// - Excessive time between block produce and publish.
|
||||||
// - A validator is using another beacon node to produce blocks and
|
// - A validator is using another beacon node to produce blocks and
|
||||||
// submitting them here.
|
// submitting them here.
|
||||||
if beacon_chain.head()?.beacon_block_root != block_root {
|
if ctx.beacon_chain.head()?.beacon_block_root != block_root {
|
||||||
warn!(
|
warn!(
|
||||||
log,
|
ctx.log,
|
||||||
"Block from validator is not head";
|
"Block from validator is not head";
|
||||||
"desc" => "potential re-org",
|
"desc" => "potential re-org",
|
||||||
);
|
);
|
||||||
@ -380,7 +352,7 @@ pub async fn publish_beacon_block<T: BeaconChainTypes>(
|
|||||||
}
|
}
|
||||||
Err(BlockError::BeaconChainError(e)) => {
|
Err(BlockError::BeaconChainError(e)) => {
|
||||||
error!(
|
error!(
|
||||||
log,
|
ctx.log,
|
||||||
"Error whilst processing block";
|
"Error whilst processing block";
|
||||||
"error" => format!("{:?}", e)
|
"error" => format!("{:?}", e)
|
||||||
);
|
);
|
||||||
@ -392,7 +364,7 @@ pub async fn publish_beacon_block<T: BeaconChainTypes>(
|
|||||||
}
|
}
|
||||||
Err(other) => {
|
Err(other) => {
|
||||||
warn!(
|
warn!(
|
||||||
log,
|
ctx.log,
|
||||||
"Invalid block from local validator";
|
"Invalid block from local validator";
|
||||||
"outcome" => format!("{:?}", other)
|
"outcome" => format!("{:?}", other)
|
||||||
);
|
);
|
||||||
@ -404,41 +376,41 @@ pub async fn publish_beacon_block<T: BeaconChainTypes>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.and_then(|_| response_builder?.body_no_ssz(&()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// HTTP Handler to produce a new Attestation from the current state, ready to be signed by a validator.
|
/// HTTP Handler to produce a new Attestation from the current state, ready to be signed by a validator.
|
||||||
pub fn get_new_attestation<T: BeaconChainTypes>(
|
pub fn get_new_attestation<T: BeaconChainTypes>(
|
||||||
req: Request<Body>,
|
req: Request<Vec<u8>>,
|
||||||
beacon_chain: Arc<BeaconChain<T>>,
|
ctx: Arc<Context<T>>,
|
||||||
) -> ApiResult {
|
) -> Result<Attestation<T::EthSpec>, ApiError> {
|
||||||
let query = UrlQuery::from_request(&req)?;
|
let query = UrlQuery::from_request(&req)?;
|
||||||
|
|
||||||
let slot = query.slot()?;
|
let slot = query.slot()?;
|
||||||
let index = query.committee_index()?;
|
let index = query.committee_index()?;
|
||||||
|
|
||||||
let attestation = beacon_chain
|
ctx.beacon_chain
|
||||||
.produce_unaggregated_attestation(slot, index)
|
.produce_unaggregated_attestation(slot, index)
|
||||||
.map_err(|e| ApiError::BadRequest(format!("Unable to produce attestation: {:?}", e)))?;
|
.map_err(|e| ApiError::BadRequest(format!("Unable to produce attestation: {:?}", e)))
|
||||||
|
|
||||||
ResponseBuilder::new(&req)?.body(&attestation)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// HTTP Handler to retrieve the aggregate attestation for a slot
|
/// HTTP Handler to retrieve the aggregate attestation for a slot
|
||||||
pub fn get_aggregate_attestation<T: BeaconChainTypes>(
|
pub fn get_aggregate_attestation<T: BeaconChainTypes>(
|
||||||
req: Request<Body>,
|
req: Request<Vec<u8>>,
|
||||||
beacon_chain: Arc<BeaconChain<T>>,
|
ctx: Arc<Context<T>>,
|
||||||
) -> ApiResult {
|
) -> Result<Attestation<T::EthSpec>, ApiError> {
|
||||||
let query = UrlQuery::from_request(&req)?;
|
let query = UrlQuery::from_request(&req)?;
|
||||||
|
|
||||||
let attestation_data = query.attestation_data()?;
|
let attestation_data = query.attestation_data()?;
|
||||||
|
|
||||||
match beacon_chain.get_aggregated_attestation(&attestation_data) {
|
match ctx
|
||||||
Ok(Some(attestation)) => ResponseBuilder::new(&req)?.body(&attestation),
|
.beacon_chain
|
||||||
|
.get_aggregated_attestation(&attestation_data)
|
||||||
|
{
|
||||||
|
Ok(Some(attestation)) => Ok(attestation),
|
||||||
Ok(None) => Err(ApiError::NotFound(format!(
|
Ok(None) => Err(ApiError::NotFound(format!(
|
||||||
"No matching aggregate attestation for slot {:?} is known in slot {:?}",
|
"No matching aggregate attestation for slot {:?} is known in slot {:?}",
|
||||||
attestation_data.slot,
|
attestation_data.slot,
|
||||||
beacon_chain.slot()
|
ctx.beacon_chain.slot()
|
||||||
))),
|
))),
|
||||||
Err(e) => Err(ApiError::ServerError(format!(
|
Err(e) => Err(ApiError::ServerError(format!(
|
||||||
"Unable to obtain attestation: {:?}",
|
"Unable to obtain attestation: {:?}",
|
||||||
@ -448,22 +420,13 @@ pub fn get_aggregate_attestation<T: BeaconChainTypes>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// HTTP Handler to publish a list of Attestations, which have been signed by a number of validators.
|
/// HTTP Handler to publish a list of Attestations, which have been signed by a number of validators.
|
||||||
pub async fn publish_attestations<T: BeaconChainTypes>(
|
pub fn publish_attestations<T: BeaconChainTypes>(
|
||||||
req: Request<Body>,
|
req: Request<Vec<u8>>,
|
||||||
beacon_chain: Arc<BeaconChain<T>>,
|
ctx: Arc<Context<T>>,
|
||||||
network_chan: NetworkChannel<T::EthSpec>,
|
) -> Result<(), ApiError> {
|
||||||
log: Logger,
|
let bytes = req.into_body();
|
||||||
) -> ApiResult {
|
|
||||||
try_future!(check_content_type_for_json(&req));
|
|
||||||
let response_builder = ResponseBuilder::new(&req);
|
|
||||||
|
|
||||||
let body = req.into_body();
|
serde_json::from_slice(&bytes)
|
||||||
let chunk = hyper::body::to_bytes(body)
|
|
||||||
.await
|
|
||||||
.map_err(|e| ApiError::ServerError(format!("Unable to get request body: {:?}", e)))?;
|
|
||||||
|
|
||||||
let chunks = chunk.iter().cloned().collect::<Vec<u8>>();
|
|
||||||
serde_json::from_slice(&chunks.as_slice())
|
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
ApiError::BadRequest(format!(
|
ApiError::BadRequest(format!(
|
||||||
"Unable to deserialize JSON into a list of attestations: {:?}",
|
"Unable to deserialize JSON into a list of attestations: {:?}",
|
||||||
@ -478,12 +441,12 @@ pub async fn publish_attestations<T: BeaconChainTypes>(
|
|||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, (attestation, subnet_id))| {
|
.map(|(i, (attestation, subnet_id))| {
|
||||||
process_unaggregated_attestation(
|
process_unaggregated_attestation(
|
||||||
&beacon_chain,
|
&ctx.beacon_chain,
|
||||||
network_chan.clone(),
|
ctx.network_chan.clone(),
|
||||||
attestation,
|
attestation,
|
||||||
subnet_id,
|
subnet_id,
|
||||||
i,
|
i,
|
||||||
&log,
|
&ctx.log,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect::<Vec<Result<_, _>>>()
|
.collect::<Vec<Result<_, _>>>()
|
||||||
@ -493,7 +456,7 @@ pub async fn publish_attestations<T: BeaconChainTypes>(
|
|||||||
//
|
//
|
||||||
// Note: this will only provide info about the _first_ failure, not all failures.
|
// Note: this will only provide info about the _first_ failure, not all failures.
|
||||||
.and_then(|processing_results| processing_results.into_iter().try_for_each(|result| result))
|
.and_then(|processing_results| processing_results.into_iter().try_for_each(|result| result))
|
||||||
.and_then(|_| response_builder?.body_no_ssz(&()))
|
.map(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Processes an unaggregrated attestation that was included in a list of attestations with the
|
/// Processes an unaggregrated attestation that was included in a list of attestations with the
|
||||||
@ -566,21 +529,13 @@ fn process_unaggregated_attestation<T: BeaconChainTypes>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// HTTP Handler to publish an Attestation, which has been signed by a validator.
|
/// HTTP Handler to publish an Attestation, which has been signed by a validator.
|
||||||
#[allow(clippy::redundant_clone)] // false positives in this function.
|
pub fn publish_aggregate_and_proofs<T: BeaconChainTypes>(
|
||||||
pub async fn publish_aggregate_and_proofs<T: BeaconChainTypes>(
|
req: Request<Vec<u8>>,
|
||||||
req: Request<Body>,
|
ctx: Arc<Context<T>>,
|
||||||
beacon_chain: Arc<BeaconChain<T>>,
|
) -> Result<(), ApiError> {
|
||||||
network_chan: NetworkChannel<T::EthSpec>,
|
|
||||||
log: Logger,
|
|
||||||
) -> ApiResult {
|
|
||||||
try_future!(check_content_type_for_json(&req));
|
|
||||||
let response_builder = ResponseBuilder::new(&req);
|
|
||||||
let body = req.into_body();
|
let body = req.into_body();
|
||||||
let chunk = hyper::body::to_bytes(body)
|
|
||||||
.await
|
serde_json::from_slice(&body)
|
||||||
.map_err(|e| ApiError::ServerError(format!("Unable to get request body: {:?}", e)))?;
|
|
||||||
let chunks = chunk.iter().cloned().collect::<Vec<u8>>();
|
|
||||||
serde_json::from_slice(&chunks.as_slice())
|
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
ApiError::BadRequest(format!(
|
ApiError::BadRequest(format!(
|
||||||
"Unable to deserialize JSON into a list of SignedAggregateAndProof: {:?}",
|
"Unable to deserialize JSON into a list of SignedAggregateAndProof: {:?}",
|
||||||
@ -595,11 +550,11 @@ pub async fn publish_aggregate_and_proofs<T: BeaconChainTypes>(
|
|||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, signed_aggregate)| {
|
.map(|(i, signed_aggregate)| {
|
||||||
process_aggregated_attestation(
|
process_aggregated_attestation(
|
||||||
&beacon_chain,
|
&ctx.beacon_chain,
|
||||||
network_chan.clone(),
|
ctx.network_chan.clone(),
|
||||||
signed_aggregate,
|
signed_aggregate,
|
||||||
i,
|
i,
|
||||||
&log,
|
&ctx.log,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect::<Vec<Result<_, _>>>()
|
.collect::<Vec<Result<_, _>>>()
|
||||||
@ -609,7 +564,6 @@ pub async fn publish_aggregate_and_proofs<T: BeaconChainTypes>(
|
|||||||
//
|
//
|
||||||
// Note: this will only provide info about the _first_ failure, not all failures.
|
// Note: this will only provide info about the _first_ failure, not all failures.
|
||||||
.and_then(|processing_results| processing_results.into_iter().try_for_each(|result| result))
|
.and_then(|processing_results| processing_results.into_iter().try_for_each(|result| result))
|
||||||
.and_then(|_| response_builder?.body_no_ssz(&()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Processes an aggregrated attestation that was included in a list of attestations with the index
|
/// Processes an aggregrated attestation that was included in a list of attestations with the index
|
||||||
|
@ -804,7 +804,7 @@ fn get_version() {
|
|||||||
let version = env
|
let version = env
|
||||||
.runtime()
|
.runtime()
|
||||||
.block_on(remote_node.http.node().get_version())
|
.block_on(remote_node.http.node().get_version())
|
||||||
.expect("should fetch eth2 config from http api");
|
.expect("should fetch version from http api");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
lighthouse_version::version_with_platform(),
|
lighthouse_version::version_with_platform(),
|
||||||
|
@ -14,6 +14,13 @@ state_processing = { path = "../../consensus/state_processing" }
|
|||||||
bls = { path = "../../crypto/bls" }
|
bls = { path = "../../crypto/bls" }
|
||||||
serde = { version = "1.0.110", features = ["derive"] }
|
serde = { version = "1.0.110", features = ["derive"] }
|
||||||
rayon = "1.3.0"
|
rayon = "1.3.0"
|
||||||
|
hyper = "0.13.5"
|
||||||
|
tokio = { version = "0.2.21", features = ["sync"] }
|
||||||
|
environment = { path = "../../lighthouse/environment" }
|
||||||
|
store = { path = "../../beacon_node/store" }
|
||||||
|
beacon_chain = { path = "../../beacon_node/beacon_chain" }
|
||||||
|
serde_json = "1.0.52"
|
||||||
|
serde_yaml = "0.8.11"
|
||||||
|
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
psutil = "3.1.0"
|
psutil = "3.1.0"
|
||||||
|
247
common/rest_types/src/handler.rs
Normal file
247
common/rest_types/src/handler.rs
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
use crate::{ApiError, ApiResult};
|
||||||
|
use environment::TaskExecutor;
|
||||||
|
use hyper::header;
|
||||||
|
use hyper::{Body, Request, Response, StatusCode};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
use ssz::Encode;
|
||||||
|
|
||||||
|
/// Defines the encoding for the API.
|
||||||
|
#[derive(Clone, Serialize, Deserialize, Copy)]
|
||||||
|
pub enum ApiEncodingFormat {
|
||||||
|
JSON,
|
||||||
|
YAML,
|
||||||
|
SSZ,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ApiEncodingFormat {
|
||||||
|
pub fn get_content_type(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
ApiEncodingFormat::JSON => "application/json",
|
||||||
|
ApiEncodingFormat::YAML => "application/yaml",
|
||||||
|
ApiEncodingFormat::SSZ => "application/ssz",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&str> for ApiEncodingFormat {
|
||||||
|
fn from(f: &str) -> ApiEncodingFormat {
|
||||||
|
match f {
|
||||||
|
"application/yaml" => ApiEncodingFormat::YAML,
|
||||||
|
"application/ssz" => ApiEncodingFormat::SSZ,
|
||||||
|
_ => ApiEncodingFormat::JSON,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provides a HTTP request handler with Lighthouse-specific functionality.
|
||||||
|
pub struct Handler<T> {
|
||||||
|
executor: TaskExecutor,
|
||||||
|
req: Request<()>,
|
||||||
|
body: Body,
|
||||||
|
ctx: T,
|
||||||
|
encoding: ApiEncodingFormat,
|
||||||
|
allow_body: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Clone + Send + Sync + 'static> Handler<T> {
|
||||||
|
/// Start handling a new request.
|
||||||
|
pub fn new(req: Request<Body>, ctx: T, executor: TaskExecutor) -> Result<Self, ApiError> {
|
||||||
|
let (req_parts, body) = req.into_parts();
|
||||||
|
let req = Request::from_parts(req_parts, ());
|
||||||
|
|
||||||
|
let accept_header: String = req
|
||||||
|
.headers()
|
||||||
|
.get(header::ACCEPT)
|
||||||
|
.map_or(Ok(""), |h| h.to_str())
|
||||||
|
.map_err(|e| {
|
||||||
|
ApiError::BadRequest(format!(
|
||||||
|
"The Accept header contains invalid characters: {:?}",
|
||||||
|
e
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.map(String::from)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
executor,
|
||||||
|
req,
|
||||||
|
body,
|
||||||
|
ctx,
|
||||||
|
allow_body: false,
|
||||||
|
encoding: ApiEncodingFormat::from(accept_header.as_str()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The default behaviour is to return an error if any body is supplied in the request. Calling
|
||||||
|
/// this function disables that error.
|
||||||
|
pub fn allow_body(mut self) -> Self {
|
||||||
|
self.allow_body = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a simple static value.
|
||||||
|
///
|
||||||
|
/// Does not use the blocking executor.
|
||||||
|
pub async fn static_value<V>(self, value: V) -> Result<HandledRequest<V>, ApiError> {
|
||||||
|
// Always check and disallow a body for a static value.
|
||||||
|
let _ = Self::get_body(self.body, false).await?;
|
||||||
|
|
||||||
|
Ok(HandledRequest {
|
||||||
|
value,
|
||||||
|
encoding: self.encoding,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calls `func` in-line, on the core executor.
|
||||||
|
///
|
||||||
|
/// This should only be used for very fast tasks.
|
||||||
|
pub async fn in_core_task<F, V>(self, func: F) -> Result<HandledRequest<V>, ApiError>
|
||||||
|
where
|
||||||
|
V: Send + Sync + 'static,
|
||||||
|
F: Fn(Request<Vec<u8>>, T) -> Result<V, ApiError> + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
let body = Self::get_body(self.body, self.allow_body).await?;
|
||||||
|
let (req_parts, _) = self.req.into_parts();
|
||||||
|
let req = Request::from_parts(req_parts, body);
|
||||||
|
|
||||||
|
let value = func(req, self.ctx)?;
|
||||||
|
|
||||||
|
Ok(HandledRequest {
|
||||||
|
value,
|
||||||
|
encoding: self.encoding,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Spawns `func` on the blocking executor.
|
||||||
|
///
|
||||||
|
/// This method is suitable for handling long-running or intensive tasks.
|
||||||
|
pub async fn in_blocking_task<F, V>(self, func: F) -> Result<HandledRequest<V>, ApiError>
|
||||||
|
where
|
||||||
|
V: Send + Sync + 'static,
|
||||||
|
F: Fn(Request<Vec<u8>>, T) -> Result<V, ApiError> + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
let ctx = self.ctx;
|
||||||
|
let body = Self::get_body(self.body, self.allow_body).await?;
|
||||||
|
let (req_parts, _) = self.req.into_parts();
|
||||||
|
let req = Request::from_parts(req_parts, body);
|
||||||
|
|
||||||
|
let value = self
|
||||||
|
.executor
|
||||||
|
.clone()
|
||||||
|
.handle
|
||||||
|
.spawn_blocking(move || func(req, ctx))
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
ApiError::ServerError(format!(
|
||||||
|
"Failed to get blocking join handle: {}",
|
||||||
|
e.to_string()
|
||||||
|
))
|
||||||
|
})??;
|
||||||
|
|
||||||
|
Ok(HandledRequest {
|
||||||
|
value,
|
||||||
|
encoding: self.encoding,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call `func`, then return a response that is suitable for an SSE stream.
|
||||||
|
pub async fn sse_stream<F>(self, func: F) -> ApiResult
|
||||||
|
where
|
||||||
|
F: Fn(Request<()>, T) -> Result<Body, ApiError>,
|
||||||
|
{
|
||||||
|
let body = func(self.req, self.ctx)?;
|
||||||
|
|
||||||
|
Response::builder()
|
||||||
|
.status(200)
|
||||||
|
.header("Content-Type", "text/event-stream")
|
||||||
|
.header("Connection", "Keep-Alive")
|
||||||
|
.header("Cache-Control", "no-cache")
|
||||||
|
.header("Access-Control-Allow-Origin", "*")
|
||||||
|
.body(body)
|
||||||
|
.map_err(|e| ApiError::ServerError(format!("Failed to build response: {:?}", e)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Downloads the bytes for `body`.
|
||||||
|
async fn get_body(body: Body, allow_body: bool) -> Result<Vec<u8>, ApiError> {
|
||||||
|
let bytes = hyper::body::to_bytes(body)
|
||||||
|
.await
|
||||||
|
.map_err(|e| ApiError::ServerError(format!("Unable to get request body: {:?}", e)))?;
|
||||||
|
|
||||||
|
if !allow_body && !bytes[..].is_empty() {
|
||||||
|
Err(ApiError::BadRequest(
|
||||||
|
"The request body must be empty".to_string(),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Ok(bytes.into_iter().collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A request that has been "handled" and now a result (`value`) needs to be serialize and
|
||||||
|
/// returned.
|
||||||
|
pub struct HandledRequest<V> {
|
||||||
|
encoding: ApiEncodingFormat,
|
||||||
|
value: V,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HandledRequest<String> {
|
||||||
|
/// Simple encode a string as utf-8.
|
||||||
|
pub fn text_encoding(self) -> ApiResult {
|
||||||
|
Response::builder()
|
||||||
|
.status(StatusCode::OK)
|
||||||
|
.header("content-type", "text/plain; charset=utf-8")
|
||||||
|
.body(Body::from(self.value))
|
||||||
|
.map_err(|e| ApiError::ServerError(format!("Failed to build response: {:?}", e)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: Serialize + Encode> HandledRequest<V> {
|
||||||
|
/// Suitable for all items which implement `serde` and `ssz`.
|
||||||
|
pub fn all_encodings(self) -> ApiResult {
|
||||||
|
match self.encoding {
|
||||||
|
ApiEncodingFormat::SSZ => Response::builder()
|
||||||
|
.status(StatusCode::OK)
|
||||||
|
.header("content-type", "application/ssz")
|
||||||
|
.body(Body::from(self.value.as_ssz_bytes()))
|
||||||
|
.map_err(|e| ApiError::ServerError(format!("Failed to build response: {:?}", e))),
|
||||||
|
_ => self.serde_encodings(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: Serialize> HandledRequest<V> {
|
||||||
|
/// Suitable for items which only implement `serde`.
|
||||||
|
pub fn serde_encodings(self) -> ApiResult {
|
||||||
|
let (body, content_type) = match self.encoding {
|
||||||
|
ApiEncodingFormat::JSON => (
|
||||||
|
Body::from(serde_json::to_string(&self.value).map_err(|e| {
|
||||||
|
ApiError::ServerError(format!(
|
||||||
|
"Unable to serialize response body as JSON: {:?}",
|
||||||
|
e
|
||||||
|
))
|
||||||
|
})?),
|
||||||
|
"application/json",
|
||||||
|
),
|
||||||
|
ApiEncodingFormat::SSZ => {
|
||||||
|
return Err(ApiError::UnsupportedType(
|
||||||
|
"Response cannot be encoded as SSZ.".into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
ApiEncodingFormat::YAML => (
|
||||||
|
Body::from(serde_yaml::to_string(&self.value).map_err(|e| {
|
||||||
|
ApiError::ServerError(format!(
|
||||||
|
"Unable to serialize response body as YAML: {:?}",
|
||||||
|
e
|
||||||
|
))
|
||||||
|
})?),
|
||||||
|
"application/yaml",
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
Response::builder()
|
||||||
|
.status(StatusCode::OK)
|
||||||
|
.header("content-type", content_type)
|
||||||
|
.body(body)
|
||||||
|
.map_err(|e| ApiError::ServerError(format!("Failed to build response: {:?}", e)))
|
||||||
|
}
|
||||||
|
}
|
@ -2,20 +2,21 @@
|
|||||||
//!
|
//!
|
||||||
//! This is primarily used by the validator client and the beacon node rest API.
|
//! This is primarily used by the validator client and the beacon node rest API.
|
||||||
|
|
||||||
|
mod api_error;
|
||||||
mod beacon;
|
mod beacon;
|
||||||
mod consensus;
|
mod consensus;
|
||||||
|
mod handler;
|
||||||
mod node;
|
mod node;
|
||||||
mod validator;
|
mod validator;
|
||||||
|
|
||||||
|
pub use api_error::{ApiError, ApiResult};
|
||||||
pub use beacon::{
|
pub use beacon::{
|
||||||
BlockResponse, CanonicalHeadResponse, Committee, HeadBeaconBlock, StateResponse,
|
BlockResponse, CanonicalHeadResponse, Committee, HeadBeaconBlock, StateResponse,
|
||||||
ValidatorRequest, ValidatorResponse,
|
ValidatorRequest, ValidatorResponse,
|
||||||
};
|
};
|
||||||
|
pub use consensus::{IndividualVote, IndividualVotesRequest, IndividualVotesResponse};
|
||||||
|
pub use handler::{ApiEncodingFormat, Handler};
|
||||||
|
pub use node::{Health, SyncingResponse, SyncingStatus};
|
||||||
pub use validator::{
|
pub use validator::{
|
||||||
ValidatorDutiesRequest, ValidatorDuty, ValidatorDutyBytes, ValidatorSubscription,
|
ValidatorDutiesRequest, ValidatorDuty, ValidatorDutyBytes, ValidatorSubscription,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use consensus::{IndividualVote, IndividualVotesRequest, IndividualVotesResponse};
|
|
||||||
|
|
||||||
pub use node::{Health, SyncingResponse, SyncingStatus};
|
|
||||||
|
@ -27,7 +27,7 @@ pub struct ProtoNode {
|
|||||||
best_descendant: Option<usize>,
|
best_descendant: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Serialize, Deserialize)]
|
#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct ProtoArray {
|
pub struct ProtoArray {
|
||||||
/// Do not attempt to prune the tree unless it has at least this many nodes. Small prunes
|
/// Do not attempt to prune the tree unless it has at least this many nodes. Small prunes
|
||||||
/// simply waste time.
|
/// simply waste time.
|
||||||
|
@ -8,7 +8,7 @@ use tokio::runtime::Handle;
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct TaskExecutor {
|
pub struct TaskExecutor {
|
||||||
/// The handle to the runtime on which tasks are spawned
|
/// The handle to the runtime on which tasks are spawned
|
||||||
pub(crate) handle: Handle,
|
pub handle: Handle,
|
||||||
/// The receiver exit future which on receiving shuts down the task
|
/// The receiver exit future which on receiving shuts down the task
|
||||||
pub(crate) exit: exit_future::Exit,
|
pub(crate) exit: exit_future::Exit,
|
||||||
/// Sender given to tasks, so that if they encounter a state in which execution cannot
|
/// Sender given to tasks, so that if they encounter a state in which execution cannot
|
||||||
|
Loading…
Reference in New Issue
Block a user