Add validator changes from validator-to-rest

This commit is contained in:
Paul Hauner
2019-11-12 16:50:12 +11:00
parent f229bbba1c
commit 41805611d0
5 changed files with 86 additions and 76 deletions

View File

@@ -2,6 +2,37 @@ use clap::ArgMatches;
use serde::{Deserialize, Serialize};
use std::net::Ipv4Addr;
/// Defines the encoding for the API
///
/// The validator client can speak to the beacon node in a number of encodings. Currently both JSON
/// and YAML are supported.
#[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,
}
}
}
/// HTTP REST API Configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config {

View File

@@ -5,9 +5,9 @@ extern crate lazy_static;
extern crate network as client_network;
mod beacon;
mod config;
pub mod config;
mod error;
mod helpers;
pub mod helpers;
mod metrics;
mod network;
mod node;
@@ -19,6 +19,7 @@ mod validator;
use beacon_chain::{BeaconChain, BeaconChainTypes};
use client_network::NetworkMessage;
use client_network::Service as NetworkService;
pub use config::ApiEncodingFormat;
use error::{ApiError, ApiResult};
use eth2_config::Eth2Config;
use futures::future::IntoFuture;
@@ -26,8 +27,7 @@ use hyper::rt::Future;
use hyper::service::Service;
use hyper::{Body, Method, Request, Response, Server};
use parking_lot::RwLock;
use slog::{info, warn};
use std::net::SocketAddr;
use slog::{info, o, warn};
use std::ops::Deref;
use std::path::PathBuf;
use std::sync::Arc;
@@ -35,8 +35,10 @@ use tokio::runtime::TaskExecutor;
use tokio::sync::mpsc;
use url_query::UrlQuery;
pub use crate::helpers::parse_pubkey;
pub use beacon::{BlockResponse, HeadResponse, StateResponse};
pub use config::Config;
pub use config::Config as ApiConfig;
pub use validator::ValidatorDuty;
type BoxFut = Box<dyn Future<Item = Response<Body>, Error = ApiError> + Send>;
@@ -197,14 +199,16 @@ impl<T: BeaconChainTypes> Service for ApiService<T> {
}
pub fn start_server<T: BeaconChainTypes>(
config: &Config,
config: &ApiConfig,
executor: &TaskExecutor,
beacon_chain: Arc<BeaconChain<T>>,
network_info: NetworkInfo<T>,
db_path: PathBuf,
eth2_config: Eth2Config,
log: slog::Logger,
) -> Result<(exit_future::Signal, SocketAddr), hyper::Error> {
log: &slog::Logger,
) -> Result<exit_future::Signal, hyper::Error> {
let log = log.new(o!("Service" => "Api"));
// build a channel to kill the HTTP server
let (exit_signal, exit) = exit_future::signal();
@@ -236,11 +240,8 @@ pub fn start_server<T: BeaconChainTypes>(
};
let log_clone = log.clone();
let server = Server::bind(&bind_addr).serve(service);
let actual_listen_addr = server.local_addr();
let server_future = server
let server = Server::bind(&bind_addr)
.serve(service)
.with_graceful_shutdown(server_exit)
.map_err(move |e| {
warn!(
@@ -250,15 +251,15 @@ pub fn start_server<T: BeaconChainTypes>(
});
info!(
log,
"REST API started";
"address" => format!("{}", actual_listen_addr.ip()),
"port" => actual_listen_addr.port(),
log,
"REST API started";
"address" => format!("{}", config.listen_address),
"port" => config.port,
);
executor.spawn(server_future);
executor.spawn(server);
Ok((exit_signal, actual_listen_addr))
Ok(exit_signal)
}
#[derive(Clone)]

View File

@@ -1,47 +1,36 @@
use super::{ApiError, ApiResult};
use crate::config::ApiEncodingFormat;
use http::header;
use hyper::{Body, Request, Response, StatusCode};
use serde::Serialize;
use ssz::Encode;
pub enum Encoding {
JSON,
SSZ,
YAML,
TEXT,
}
pub struct ResponseBuilder {
encoding: Encoding,
encoding: ApiEncodingFormat,
}
impl ResponseBuilder {
pub fn new(req: &Request<Body>) -> Result<Self, ApiError> {
let content_header: String = req
let accept_header: String = req
.headers()
.get(header::CONTENT_TYPE)
.get(header::ACCEPT)
.map_or(Ok(""), |h| h.to_str())
.map_err(|e| {
ApiError::BadRequest(format!(
"The content-type header contains invalid characters: {:?}",
"The Accept header contains invalid characters: {:?}",
e
))
})
.map(String::from)?;
// JSON is our default encoding, unless something else is requested.
let encoding = match content_header {
ref h if h.starts_with("application/ssz") => Encoding::SSZ,
ref h if h.starts_with("application/yaml") => Encoding::YAML,
ref h if h.starts_with("text/") => Encoding::TEXT,
_ => Encoding::JSON,
};
let encoding = ApiEncodingFormat::from(accept_header.as_str());
Ok(Self { encoding })
}
pub fn body<T: Serialize + Encode>(self, item: &T) -> ApiResult {
match self.encoding {
Encoding::SSZ => Response::builder()
ApiEncodingFormat::SSZ => Response::builder()
.status(StatusCode::OK)
.header("content-type", "application/ssz")
.body(Body::from(item.as_ssz_bytes()))
@@ -52,7 +41,7 @@ impl ResponseBuilder {
pub fn body_no_ssz<T: Serialize>(self, item: &T) -> ApiResult {
let (body, content_type) = match self.encoding {
Encoding::JSON => (
ApiEncodingFormat::JSON => (
Body::from(serde_json::to_string(&item).map_err(|e| {
ApiError::ServerError(format!(
"Unable to serialize response body as JSON: {:?}",
@@ -61,12 +50,12 @@ impl ResponseBuilder {
})?),
"application/json",
),
Encoding::SSZ => {
ApiEncodingFormat::SSZ => {
return Err(ApiError::UnsupportedType(
"Response cannot be encoded as SSZ.".into(),
));
}
Encoding::YAML => (
ApiEncodingFormat::YAML => (
Body::from(serde_yaml::to_string(&item).map_err(|e| {
ApiError::ServerError(format!(
"Unable to serialize response body as YAML: {:?}",
@@ -75,11 +64,6 @@ impl ResponseBuilder {
})?),
"application/yaml",
),
Encoding::TEXT => {
return Err(ApiError::UnsupportedType(
"Response cannot be encoded as plain text.".into(),
));
}
};
Response::builder()

View File

@@ -5,7 +5,7 @@ use crate::helpers::{
use crate::response_builder::ResponseBuilder;
use crate::{ApiError, ApiResult, BoxFut, UrlQuery};
use beacon_chain::{AttestationProcessingOutcome, BeaconChainTypes, BlockProcessingOutcome};
use bls::{AggregateSignature, PublicKey, Signature};
use bls::{AggregateSignature, PublicKey, Signature, BLS_PUBLIC_KEY_BYTE_SIZE};
use futures::future::Future;
use futures::stream::Stream;
use hyper::{Body, Request};
@@ -22,7 +22,7 @@ use types::{Attestation, BeaconBlock, BitList, Epoch, RelativeEpoch, Shard, Slot
#[derive(Debug, Serialize, Deserialize)]
pub struct ValidatorDuty {
/// The validator's BLS public key, uniquely identifying them. _48-bytes, hex encoded with 0x prefix, case insensitive._
pub validator_pubkey: String,
pub validator_pubkey: PublicKey,
/// The slot at which the validator must attest.
pub attestation_slot: Option<Slot>,
/// The shard in which the validator must attest.
@@ -34,7 +34,8 @@ pub struct ValidatorDuty {
impl ValidatorDuty {
pub fn new() -> ValidatorDuty {
ValidatorDuty {
validator_pubkey: "".to_string(),
validator_pubkey: PublicKey::from_bytes(vec![0; BLS_PUBLIC_KEY_BYTE_SIZE].as_slice())
.expect("Should always be able to create a 'zero' BLS public key."),
attestation_slot: None,
attestation_shard: None,
block_proposal_slot: None,
@@ -103,7 +104,7 @@ pub fn get_validator_duties<T: BeaconChainTypes + 'static>(req: Request<Body>) -
// Look up duties for each validator
for val_pk in validators {
let mut duty = ValidatorDuty::new();
duty.validator_pubkey = val_pk.as_hex_string();
duty.validator_pubkey = val_pk.clone();
// Get the validator index
// If it does not exist in the index, just add a null duty and move on.
@@ -210,14 +211,8 @@ pub fn publish_beacon_block<T: BeaconChainTypes + 'static>(req: Request<Body>) -
Box::new(body
.concat2()
.map_err(|e| ApiError::ServerError(format!("Unable to get request body: {:?}",e)))
.map(|chunk| chunk.iter().cloned().collect::<Vec<u8>>())
.and_then(|chunks| {
serde_json::from_slice(&chunks.as_slice()).map_err(|e| {
ApiError::BadRequest(format!(
"Unable to deserialize JSON into a BeaconBlock: {:?}",
e
))
})
serde_json::from_slice(&chunks).map_err(|e| ApiError::BadRequest(format!("Unable to parse JSON into BeaconBlock: {:?}",e)))
})
.and_then(move |block: BeaconBlock<T::EthSpec>| {
let slot = block.slot;