mirror of
https://github.com/sigp/lighthouse.git
synced 2026-05-07 16:55:46 +00:00
Add validator changes from validator-to-rest
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rest_api"
|
name = "rest_api"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Luke Anderson <luke@lukeanderson.com.au>"]
|
authors = ["Luke Anderson <luke@sigmaprime.io>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# 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" }
|
eth2-libp2p = { path = "../eth2-libp2p" }
|
||||||
store = { path = "../store" }
|
store = { path = "../store" }
|
||||||
version = { path = "../version" }
|
version = { path = "../version" }
|
||||||
serde = { version = "1.0.102", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0.41"
|
serde_json = "^1.0"
|
||||||
serde_yaml = "0.8.11"
|
serde_yaml = "0.8"
|
||||||
slog = "2.5.2"
|
slog = "^2.2.3"
|
||||||
slog-term = "2.4.2"
|
slog-term = "^2.4.0"
|
||||||
slog-async = "2.3.0"
|
slog-async = "^2.3.0"
|
||||||
eth2_ssz = "0.1.2"
|
eth2_ssz = { path = "../../eth2/utils/ssz" }
|
||||||
eth2_ssz_derive = "0.1.0"
|
eth2_ssz_derive = { path = "../../eth2/utils/ssz_derive" }
|
||||||
state_processing = { path = "../../eth2/state_processing" }
|
state_processing = { path = "../../eth2/state_processing" }
|
||||||
types = { path = "../../eth2/types" }
|
types = { path = "../../eth2/types" }
|
||||||
clap = "2.33.0"
|
clap = "2.32.0"
|
||||||
http = "0.1.19"
|
http = "^0.1.17"
|
||||||
prometheus = { version = "0.7.0", features = ["process"] }
|
prometheus = { version = "^0.6", features = ["process"] }
|
||||||
hyper = "0.12.35"
|
hyper = "0.12.35"
|
||||||
exit-future = "0.1.4"
|
exit-future = "0.1.3"
|
||||||
tokio = "0.1.22"
|
tokio = "0.1.17"
|
||||||
url = "2.1.0"
|
url = "2.0"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.3.0"
|
||||||
eth2_config = { path = "../../eth2/utils/eth2_config" }
|
eth2_config = { path = "../../eth2/utils/eth2_config" }
|
||||||
lighthouse_metrics = { path = "../../eth2/utils/lighthouse_metrics" }
|
lighthouse_metrics = { path = "../../eth2/utils/lighthouse_metrics" }
|
||||||
slot_clock = { path = "../../eth2/utils/slot_clock" }
|
slot_clock = { path = "../../eth2/utils/slot_clock" }
|
||||||
hex = "0.3"
|
hex = "0.3.2"
|
||||||
parking_lot = "0.9.0"
|
parking_lot = "0.9"
|
||||||
futures = "0.1.29"
|
futures = "0.1.25"
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,37 @@ use clap::ArgMatches;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::net::Ipv4Addr;
|
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
|
/// HTTP REST API Configuration
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ extern crate lazy_static;
|
|||||||
extern crate network as client_network;
|
extern crate network as client_network;
|
||||||
|
|
||||||
mod beacon;
|
mod beacon;
|
||||||
mod config;
|
pub mod config;
|
||||||
mod error;
|
mod error;
|
||||||
mod helpers;
|
pub mod helpers;
|
||||||
mod metrics;
|
mod metrics;
|
||||||
mod network;
|
mod network;
|
||||||
mod node;
|
mod node;
|
||||||
@@ -19,6 +19,7 @@ mod validator;
|
|||||||
use beacon_chain::{BeaconChain, BeaconChainTypes};
|
use beacon_chain::{BeaconChain, BeaconChainTypes};
|
||||||
use client_network::NetworkMessage;
|
use client_network::NetworkMessage;
|
||||||
use client_network::Service as NetworkService;
|
use client_network::Service as NetworkService;
|
||||||
|
pub use config::ApiEncodingFormat;
|
||||||
use error::{ApiError, ApiResult};
|
use error::{ApiError, ApiResult};
|
||||||
use eth2_config::Eth2Config;
|
use eth2_config::Eth2Config;
|
||||||
use futures::future::IntoFuture;
|
use futures::future::IntoFuture;
|
||||||
@@ -26,8 +27,7 @@ use hyper::rt::Future;
|
|||||||
use hyper::service::Service;
|
use hyper::service::Service;
|
||||||
use hyper::{Body, Method, Request, Response, Server};
|
use hyper::{Body, Method, Request, Response, Server};
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use slog::{info, warn};
|
use slog::{info, o, warn};
|
||||||
use std::net::SocketAddr;
|
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -35,8 +35,10 @@ use tokio::runtime::TaskExecutor;
|
|||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use url_query::UrlQuery;
|
use url_query::UrlQuery;
|
||||||
|
|
||||||
|
pub use crate::helpers::parse_pubkey;
|
||||||
pub use beacon::{BlockResponse, HeadResponse, StateResponse};
|
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>;
|
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>(
|
pub fn start_server<T: BeaconChainTypes>(
|
||||||
config: &Config,
|
config: &ApiConfig,
|
||||||
executor: &TaskExecutor,
|
executor: &TaskExecutor,
|
||||||
beacon_chain: Arc<BeaconChain<T>>,
|
beacon_chain: Arc<BeaconChain<T>>,
|
||||||
network_info: NetworkInfo<T>,
|
network_info: NetworkInfo<T>,
|
||||||
db_path: PathBuf,
|
db_path: PathBuf,
|
||||||
eth2_config: Eth2Config,
|
eth2_config: Eth2Config,
|
||||||
log: slog::Logger,
|
log: &slog::Logger,
|
||||||
) -> Result<(exit_future::Signal, SocketAddr), hyper::Error> {
|
) -> Result<exit_future::Signal, hyper::Error> {
|
||||||
|
let log = log.new(o!("Service" => "Api"));
|
||||||
|
|
||||||
// build a channel to kill the HTTP server
|
// build a channel to kill the HTTP server
|
||||||
let (exit_signal, exit) = exit_future::signal();
|
let (exit_signal, exit) = exit_future::signal();
|
||||||
|
|
||||||
@@ -236,11 +240,8 @@ pub fn start_server<T: BeaconChainTypes>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let log_clone = log.clone();
|
let log_clone = log.clone();
|
||||||
let server = Server::bind(&bind_addr).serve(service);
|
let server = Server::bind(&bind_addr)
|
||||||
|
.serve(service)
|
||||||
let actual_listen_addr = server.local_addr();
|
|
||||||
|
|
||||||
let server_future = server
|
|
||||||
.with_graceful_shutdown(server_exit)
|
.with_graceful_shutdown(server_exit)
|
||||||
.map_err(move |e| {
|
.map_err(move |e| {
|
||||||
warn!(
|
warn!(
|
||||||
@@ -250,15 +251,15 @@ pub fn start_server<T: BeaconChainTypes>(
|
|||||||
});
|
});
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
log,
|
log,
|
||||||
"REST API started";
|
"REST API started";
|
||||||
"address" => format!("{}", actual_listen_addr.ip()),
|
"address" => format!("{}", config.listen_address),
|
||||||
"port" => actual_listen_addr.port(),
|
"port" => config.port,
|
||||||
);
|
);
|
||||||
|
|
||||||
executor.spawn(server_future);
|
executor.spawn(server);
|
||||||
|
|
||||||
Ok((exit_signal, actual_listen_addr))
|
Ok(exit_signal)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|||||||
@@ -1,47 +1,36 @@
|
|||||||
use super::{ApiError, ApiResult};
|
use super::{ApiError, ApiResult};
|
||||||
|
use crate::config::ApiEncodingFormat;
|
||||||
use http::header;
|
use http::header;
|
||||||
use hyper::{Body, Request, Response, StatusCode};
|
use hyper::{Body, Request, Response, StatusCode};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use ssz::Encode;
|
use ssz::Encode;
|
||||||
|
|
||||||
pub enum Encoding {
|
|
||||||
JSON,
|
|
||||||
SSZ,
|
|
||||||
YAML,
|
|
||||||
TEXT,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ResponseBuilder {
|
pub struct ResponseBuilder {
|
||||||
encoding: Encoding,
|
encoding: ApiEncodingFormat,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResponseBuilder {
|
impl ResponseBuilder {
|
||||||
pub fn new(req: &Request<Body>) -> Result<Self, ApiError> {
|
pub fn new(req: &Request<Body>) -> Result<Self, ApiError> {
|
||||||
let content_header: String = req
|
let accept_header: String = req
|
||||||
.headers()
|
.headers()
|
||||||
.get(header::CONTENT_TYPE)
|
.get(header::ACCEPT)
|
||||||
.map_or(Ok(""), |h| h.to_str())
|
.map_or(Ok(""), |h| h.to_str())
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
ApiError::BadRequest(format!(
|
ApiError::BadRequest(format!(
|
||||||
"The content-type header contains invalid characters: {:?}",
|
"The Accept header contains invalid characters: {:?}",
|
||||||
e
|
e
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
.map(String::from)?;
|
.map(String::from)?;
|
||||||
|
|
||||||
// JSON is our default encoding, unless something else is requested.
|
// JSON is our default encoding, unless something else is requested.
|
||||||
let encoding = match content_header {
|
let encoding = ApiEncodingFormat::from(accept_header.as_str());
|
||||||
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,
|
|
||||||
};
|
|
||||||
Ok(Self { encoding })
|
Ok(Self { encoding })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn body<T: Serialize + Encode>(self, item: &T) -> ApiResult {
|
pub fn body<T: Serialize + Encode>(self, item: &T) -> ApiResult {
|
||||||
match self.encoding {
|
match self.encoding {
|
||||||
Encoding::SSZ => Response::builder()
|
ApiEncodingFormat::SSZ => Response::builder()
|
||||||
.status(StatusCode::OK)
|
.status(StatusCode::OK)
|
||||||
.header("content-type", "application/ssz")
|
.header("content-type", "application/ssz")
|
||||||
.body(Body::from(item.as_ssz_bytes()))
|
.body(Body::from(item.as_ssz_bytes()))
|
||||||
@@ -52,7 +41,7 @@ impl ResponseBuilder {
|
|||||||
|
|
||||||
pub fn body_no_ssz<T: Serialize>(self, item: &T) -> ApiResult {
|
pub fn body_no_ssz<T: Serialize>(self, item: &T) -> ApiResult {
|
||||||
let (body, content_type) = match self.encoding {
|
let (body, content_type) = match self.encoding {
|
||||||
Encoding::JSON => (
|
ApiEncodingFormat::JSON => (
|
||||||
Body::from(serde_json::to_string(&item).map_err(|e| {
|
Body::from(serde_json::to_string(&item).map_err(|e| {
|
||||||
ApiError::ServerError(format!(
|
ApiError::ServerError(format!(
|
||||||
"Unable to serialize response body as JSON: {:?}",
|
"Unable to serialize response body as JSON: {:?}",
|
||||||
@@ -61,12 +50,12 @@ impl ResponseBuilder {
|
|||||||
})?),
|
})?),
|
||||||
"application/json",
|
"application/json",
|
||||||
),
|
),
|
||||||
Encoding::SSZ => {
|
ApiEncodingFormat::SSZ => {
|
||||||
return Err(ApiError::UnsupportedType(
|
return Err(ApiError::UnsupportedType(
|
||||||
"Response cannot be encoded as SSZ.".into(),
|
"Response cannot be encoded as SSZ.".into(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
Encoding::YAML => (
|
ApiEncodingFormat::YAML => (
|
||||||
Body::from(serde_yaml::to_string(&item).map_err(|e| {
|
Body::from(serde_yaml::to_string(&item).map_err(|e| {
|
||||||
ApiError::ServerError(format!(
|
ApiError::ServerError(format!(
|
||||||
"Unable to serialize response body as YAML: {:?}",
|
"Unable to serialize response body as YAML: {:?}",
|
||||||
@@ -75,11 +64,6 @@ impl ResponseBuilder {
|
|||||||
})?),
|
})?),
|
||||||
"application/yaml",
|
"application/yaml",
|
||||||
),
|
),
|
||||||
Encoding::TEXT => {
|
|
||||||
return Err(ApiError::UnsupportedType(
|
|
||||||
"Response cannot be encoded as plain text.".into(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Response::builder()
|
Response::builder()
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use crate::helpers::{
|
|||||||
use crate::response_builder::ResponseBuilder;
|
use crate::response_builder::ResponseBuilder;
|
||||||
use crate::{ApiError, ApiResult, BoxFut, UrlQuery};
|
use crate::{ApiError, ApiResult, BoxFut, UrlQuery};
|
||||||
use beacon_chain::{AttestationProcessingOutcome, BeaconChainTypes, BlockProcessingOutcome};
|
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::future::Future;
|
||||||
use futures::stream::Stream;
|
use futures::stream::Stream;
|
||||||
use hyper::{Body, Request};
|
use hyper::{Body, Request};
|
||||||
@@ -22,7 +22,7 @@ use types::{Attestation, BeaconBlock, BitList, Epoch, RelativeEpoch, Shard, Slot
|
|||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct ValidatorDuty {
|
pub struct ValidatorDuty {
|
||||||
/// The validator's BLS public key, uniquely identifying them. _48-bytes, hex encoded with 0x prefix, case insensitive._
|
/// 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.
|
/// The slot at which the validator must attest.
|
||||||
pub attestation_slot: Option<Slot>,
|
pub attestation_slot: Option<Slot>,
|
||||||
/// The shard in which the validator must attest.
|
/// The shard in which the validator must attest.
|
||||||
@@ -34,7 +34,8 @@ pub struct ValidatorDuty {
|
|||||||
impl ValidatorDuty {
|
impl ValidatorDuty {
|
||||||
pub fn new() -> ValidatorDuty {
|
pub fn new() -> ValidatorDuty {
|
||||||
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_slot: None,
|
||||||
attestation_shard: None,
|
attestation_shard: None,
|
||||||
block_proposal_slot: 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
|
// Look up duties for each validator
|
||||||
for val_pk in validators {
|
for val_pk in validators {
|
||||||
let mut duty = ValidatorDuty::new();
|
let mut duty = ValidatorDuty::new();
|
||||||
duty.validator_pubkey = val_pk.as_hex_string();
|
duty.validator_pubkey = val_pk.clone();
|
||||||
|
|
||||||
// Get the validator index
|
// Get the validator index
|
||||||
// If it does not exist in the index, just add a null duty and move on.
|
// 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
|
Box::new(body
|
||||||
.concat2()
|
.concat2()
|
||||||
.map_err(|e| ApiError::ServerError(format!("Unable to get request body: {:?}",e)))
|
.map_err(|e| ApiError::ServerError(format!("Unable to get request body: {:?}",e)))
|
||||||
.map(|chunk| chunk.iter().cloned().collect::<Vec<u8>>())
|
|
||||||
.and_then(|chunks| {
|
.and_then(|chunks| {
|
||||||
serde_json::from_slice(&chunks.as_slice()).map_err(|e| {
|
serde_json::from_slice(&chunks).map_err(|e| ApiError::BadRequest(format!("Unable to parse JSON into BeaconBlock: {:?}",e)))
|
||||||
ApiError::BadRequest(format!(
|
|
||||||
"Unable to deserialize JSON into a BeaconBlock: {:?}",
|
|
||||||
e
|
|
||||||
))
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.and_then(move |block: BeaconBlock<T::EthSpec>| {
|
.and_then(move |block: BeaconBlock<T::EthSpec>| {
|
||||||
let slot = block.slot;
|
let slot = block.slot;
|
||||||
|
|||||||
Reference in New Issue
Block a user