From 41805611d07de01a8f9549a1f0ee38e5a93e6444 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Tue, 12 Nov 2019 16:50:12 +1100 Subject: [PATCH] Add `validator` changes from `validator-to-rest` --- beacon_node/rest_api/Cargo.toml | 39 ++++++++++---------- beacon_node/rest_api/src/config.rs | 31 ++++++++++++++++ beacon_node/rest_api/src/lib.rs | 39 ++++++++++---------- beacon_node/rest_api/src/response_builder.rs | 36 +++++------------- beacon_node/rest_api/src/validator.rs | 17 +++------ 5 files changed, 86 insertions(+), 76 deletions(-) diff --git a/beacon_node/rest_api/Cargo.toml b/beacon_node/rest_api/Cargo.toml index ac019b97c0..d19317a719 100644 --- a/beacon_node/rest_api/Cargo.toml +++ b/beacon_node/rest_api/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rest_api" version = "0.1.0" -authors = ["Luke Anderson "] +authors = ["Luke Anderson "] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -12,28 +12,27 @@ network = { path = "../network" } eth2-libp2p = { path = "../eth2-libp2p" } store = { path = "../store" } version = { path = "../version" } -serde = { version = "1.0.102", features = ["derive"] } -serde_json = "1.0.41" -serde_yaml = "0.8.11" -slog = "2.5.2" -slog-term = "2.4.2" -slog-async = "2.3.0" -eth2_ssz = "0.1.2" -eth2_ssz_derive = "0.1.0" +serde = { version = "1.0", features = ["derive"] } +serde_json = "^1.0" +serde_yaml = "0.8" +slog = "^2.2.3" +slog-term = "^2.4.0" +slog-async = "^2.3.0" +eth2_ssz = { path = "../../eth2/utils/ssz" } +eth2_ssz_derive = { path = "../../eth2/utils/ssz_derive" } state_processing = { path = "../../eth2/state_processing" } types = { path = "../../eth2/types" } -clap = "2.33.0" -http = "0.1.19" -prometheus = { version = "0.7.0", features = ["process"] } +clap = "2.32.0" +http = "^0.1.17" +prometheus = { version = "^0.6", features = ["process"] } hyper = "0.12.35" -exit-future = "0.1.4" -tokio = "0.1.22" -url = "2.1.0" -lazy_static = "1.4.0" +exit-future = "0.1.3" +tokio = "0.1.17" +url = "2.0" +lazy_static = "1.3.0" eth2_config = { path = "../../eth2/utils/eth2_config" } lighthouse_metrics = { path = "../../eth2/utils/lighthouse_metrics" } slot_clock = { path = "../../eth2/utils/slot_clock" } -hex = "0.3" -parking_lot = "0.9.0" -futures = "0.1.29" - +hex = "0.3.2" +parking_lot = "0.9" +futures = "0.1.25" diff --git a/beacon_node/rest_api/src/config.rs b/beacon_node/rest_api/src/config.rs index c262a128a1..68f23d1c7a 100644 --- a/beacon_node/rest_api/src/config.rs +++ b/beacon_node/rest_api/src/config.rs @@ -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 { diff --git a/beacon_node/rest_api/src/lib.rs b/beacon_node/rest_api/src/lib.rs index f8fbdcab78..9165ccaac1 100644 --- a/beacon_node/rest_api/src/lib.rs +++ b/beacon_node/rest_api/src/lib.rs @@ -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, Error = ApiError> + Send>; @@ -197,14 +199,16 @@ impl Service for ApiService { } pub fn start_server( - config: &Config, + config: &ApiConfig, executor: &TaskExecutor, beacon_chain: Arc>, network_info: NetworkInfo, db_path: PathBuf, eth2_config: Eth2Config, - log: slog::Logger, -) -> Result<(exit_future::Signal, SocketAddr), hyper::Error> { + log: &slog::Logger, +) -> Result { + 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( }; 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( }); 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)] diff --git a/beacon_node/rest_api/src/response_builder.rs b/beacon_node/rest_api/src/response_builder.rs index ec7d8cb8db..0c8752a113 100644 --- a/beacon_node/rest_api/src/response_builder.rs +++ b/beacon_node/rest_api/src/response_builder.rs @@ -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) -> Result { - 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(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(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() diff --git a/beacon_node/rest_api/src/validator.rs b/beacon_node/rest_api/src/validator.rs index 082966637a..ae48ad864d 100644 --- a/beacon_node/rest_api/src/validator.rs +++ b/beacon_node/rest_api/src/validator.rs @@ -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, /// 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(req: Request) - // 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(req: Request) - Box::new(body .concat2() .map_err(|e| ApiError::ServerError(format!("Unable to get request body: {:?}",e))) - .map(|chunk| chunk.iter().cloned().collect::>()) .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| { let slot = block.slot;