mirror of
https://github.com/sigp/lighthouse.git
synced 2026-04-19 05:48:31 +00:00
Add initial (failing) REST api tests
This commit is contained in:
@@ -175,7 +175,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
) -> Result<Vec<BeaconBlockBody<T::EthSpec>>, Error> {
|
) -> Result<Vec<BeaconBlockBody<T::EthSpec>>, Error> {
|
||||||
let bodies: Result<Vec<_>, _> = roots
|
let bodies: Result<Vec<_>, _> = roots
|
||||||
.iter()
|
.iter()
|
||||||
.map(|root| match self.get_block(root)? {
|
.map(|root| match self.block_at_root(*root)? {
|
||||||
Some(block) => Ok(block.body),
|
Some(block) => Ok(block.body),
|
||||||
None => Err(Error::DBInconsistent(format!("Missing block: {}", root))),
|
None => Err(Error::DBInconsistent(format!("Missing block: {}", root))),
|
||||||
})
|
})
|
||||||
@@ -190,7 +190,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
pub fn get_block_headers(&self, roots: &[Hash256]) -> Result<Vec<BeaconBlockHeader>, Error> {
|
pub fn get_block_headers(&self, roots: &[Hash256]) -> Result<Vec<BeaconBlockHeader>, Error> {
|
||||||
let headers: Result<Vec<BeaconBlockHeader>, _> = roots
|
let headers: Result<Vec<BeaconBlockHeader>, _> = roots
|
||||||
.iter()
|
.iter()
|
||||||
.map(|root| match self.get_block(root)? {
|
.map(|root| match self.block_at_root(*root)? {
|
||||||
Some(block) => Ok(block.block_header()),
|
Some(block) => Ok(block.block_header()),
|
||||||
None => Err(Error::DBInconsistent("Missing block".into())),
|
None => Err(Error::DBInconsistent("Missing block".into())),
|
||||||
})
|
})
|
||||||
@@ -241,11 +241,29 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
/// ## Errors
|
/// ## Errors
|
||||||
///
|
///
|
||||||
/// May return a database error.
|
/// May return a database error.
|
||||||
pub fn get_block(
|
pub fn block_at_root(
|
||||||
&self,
|
&self,
|
||||||
block_root: &Hash256,
|
block_root: Hash256,
|
||||||
) -> Result<Option<BeaconBlock<T::EthSpec>>, Error> {
|
) -> Result<Option<BeaconBlock<T::EthSpec>>, Error> {
|
||||||
Ok(self.store.get(block_root)?)
|
Ok(self.store.get(&block_root)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the block at the given slot, if any. Only returns blocks in the canonical chain.
|
||||||
|
///
|
||||||
|
/// ## Errors
|
||||||
|
///
|
||||||
|
/// May return a database error.
|
||||||
|
pub fn block_at_slot(&self, slot: Slot) -> Result<Option<BeaconBlock<T::EthSpec>>, Error> {
|
||||||
|
let root = self
|
||||||
|
.rev_iter_block_roots()
|
||||||
|
.find(|(_, this_slot)| *this_slot == slot)
|
||||||
|
.map(|(root, _)| root);
|
||||||
|
|
||||||
|
if let Some(block_root) = root {
|
||||||
|
Ok(self.store.get(&block_root)?)
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a `Checkpoint` representing the head block and state. Contains the "best block";
|
/// Returns a `Checkpoint` representing the head block and state. Contains the "best block";
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ state_processing = { path = "../../eth2/state_processing" }
|
|||||||
types = { path = "../../eth2/types" }
|
types = { path = "../../eth2/types" }
|
||||||
clap = "2.32.0"
|
clap = "2.32.0"
|
||||||
http = "^0.1.17"
|
http = "^0.1.17"
|
||||||
prometheus = { version = "^0.6", features = ["process"] }
|
|
||||||
hyper = "0.12.35"
|
hyper = "0.12.35"
|
||||||
exit-future = "0.1.3"
|
exit-future = "0.1.3"
|
||||||
tokio = "0.1.17"
|
tokio = "0.1.17"
|
||||||
@@ -36,3 +35,8 @@ slot_clock = { path = "../../eth2/utils/slot_clock" }
|
|||||||
hex = "0.3.2"
|
hex = "0.3.2"
|
||||||
parking_lot = "0.9"
|
parking_lot = "0.9"
|
||||||
futures = "0.1.25"
|
futures = "0.1.25"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
remote_beacon_node = { path = "../../eth2/utils/remote_beacon_node" }
|
||||||
|
node_test_rig = { path = "../../tests/node_test_rig" }
|
||||||
|
tree_hash = { path = "../../eth2/utils/tree_hash" }
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ 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, o, warn};
|
use slog::{info, 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;
|
||||||
@@ -37,7 +38,7 @@ use url_query::UrlQuery;
|
|||||||
|
|
||||||
pub use crate::helpers::parse_pubkey;
|
pub use crate::helpers::parse_pubkey;
|
||||||
pub use beacon::{BlockResponse, HeadResponse, StateResponse};
|
pub use beacon::{BlockResponse, HeadResponse, StateResponse};
|
||||||
pub use config::Config as ApiConfig;
|
pub use config::Config;
|
||||||
pub use validator::ValidatorDuty;
|
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>;
|
||||||
@@ -137,19 +138,17 @@ impl<T: BeaconChainTypes> Service for ApiService<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Methods for Validator
|
// Methods for Validator
|
||||||
(&Method::GET, "/beacon/validator/duties") => {
|
(&Method::GET, "/validator/duties") => {
|
||||||
into_boxfut(validator::get_validator_duties::<T>(req))
|
into_boxfut(validator::get_validator_duties::<T>(req))
|
||||||
}
|
}
|
||||||
(&Method::GET, "/beacon/validator/block") => {
|
(&Method::GET, "/validator/block") => {
|
||||||
into_boxfut(validator::get_new_beacon_block::<T>(req))
|
into_boxfut(validator::get_new_beacon_block::<T>(req))
|
||||||
}
|
}
|
||||||
(&Method::POST, "/beacon/validator/block") => validator::publish_beacon_block::<T>(req),
|
(&Method::POST, "/validator/block") => validator::publish_beacon_block::<T>(req),
|
||||||
(&Method::GET, "/beacon/validator/attestation") => {
|
(&Method::GET, "/validator/attestation") => {
|
||||||
into_boxfut(validator::get_new_attestation::<T>(req))
|
into_boxfut(validator::get_new_attestation::<T>(req))
|
||||||
}
|
}
|
||||||
(&Method::POST, "/beacon/validator/attestation") => {
|
(&Method::POST, "/validator/attestation") => validator::publish_attestation::<T>(req),
|
||||||
validator::publish_attestation::<T>(req)
|
|
||||||
}
|
|
||||||
|
|
||||||
(&Method::GET, "/beacon/state") => into_boxfut(beacon::get_state::<T>(req)),
|
(&Method::GET, "/beacon/state") => into_boxfut(beacon::get_state::<T>(req)),
|
||||||
(&Method::GET, "/beacon/state_root") => into_boxfut(beacon::get_state_root::<T>(req)),
|
(&Method::GET, "/beacon/state_root") => into_boxfut(beacon::get_state_root::<T>(req)),
|
||||||
@@ -199,16 +198,14 @@ impl<T: BeaconChainTypes> Service for ApiService<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn start_server<T: BeaconChainTypes>(
|
pub fn start_server<T: BeaconChainTypes>(
|
||||||
config: &ApiConfig,
|
config: &Config,
|
||||||
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, hyper::Error> {
|
) -> Result<(exit_future::Signal, SocketAddr), 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();
|
||||||
|
|
||||||
@@ -240,8 +237,11 @@ pub fn start_server<T: BeaconChainTypes>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let log_clone = log.clone();
|
let log_clone = log.clone();
|
||||||
let server = Server::bind(&bind_addr)
|
let server = Server::bind(&bind_addr).serve(service);
|
||||||
.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!(
|
||||||
@@ -251,15 +251,15 @@ pub fn start_server<T: BeaconChainTypes>(
|
|||||||
});
|
});
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
log,
|
log,
|
||||||
"REST API started";
|
"REST API started";
|
||||||
"address" => format!("{}", config.listen_address),
|
"address" => format!("{}", actual_listen_addr.ip()),
|
||||||
"port" => config.port,
|
"port" => actual_listen_addr.port(),
|
||||||
);
|
);
|
||||||
|
|
||||||
executor.spawn(server);
|
executor.spawn(server_future);
|
||||||
|
|
||||||
Ok(exit_signal)
|
Ok((exit_signal, actual_listen_addr))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use crate::response_builder::ResponseBuilder;
|
|||||||
use crate::{ApiError, ApiResult, DBPath};
|
use crate::{ApiError, ApiResult, DBPath};
|
||||||
use beacon_chain::BeaconChainTypes;
|
use beacon_chain::BeaconChainTypes;
|
||||||
use hyper::{Body, Request};
|
use hyper::{Body, Request};
|
||||||
use prometheus::{Encoder, TextEncoder};
|
use lighthouse_metrics::{Encoder, TextEncoder};
|
||||||
|
|
||||||
pub use lighthouse_metrics::*;
|
pub use lighthouse_metrics::*;
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ pub fn get_prometheus<T: BeaconChainTypes + 'static>(req: Request<Body>) -> ApiR
|
|||||||
beacon_chain::scrape_for_metrics(&beacon_chain);
|
beacon_chain::scrape_for_metrics(&beacon_chain);
|
||||||
|
|
||||||
encoder
|
encoder
|
||||||
.encode(&lighthouse_metrics::gather(), &mut buffer)
|
.encode(&lighthouse_metrics::gather()[..], &mut buffer)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
String::from_utf8(buffer)
|
String::from_utf8(buffer)
|
||||||
|
|||||||
161
beacon_node/rest_api/tests/test.rs
Normal file
161
beacon_node/rest_api/tests/test.rs
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
#![cfg(test)]
|
||||||
|
|
||||||
|
use node_test_rig::{
|
||||||
|
environment::{Environment, EnvironmentBuilder},
|
||||||
|
LocalBeaconNode,
|
||||||
|
};
|
||||||
|
use tree_hash::TreeHash;
|
||||||
|
use types::{
|
||||||
|
test_utils::generate_deterministic_keypair, Domain, EthSpec, MinimalEthSpec, Signature, Slot,
|
||||||
|
};
|
||||||
|
|
||||||
|
type E = MinimalEthSpec;
|
||||||
|
|
||||||
|
fn build_env() -> Environment<E> {
|
||||||
|
EnvironmentBuilder::minimal()
|
||||||
|
.null_logger()
|
||||||
|
.expect("should build env logger")
|
||||||
|
.single_thread_tokio_runtime()
|
||||||
|
.expect("should start tokio runtime")
|
||||||
|
.build()
|
||||||
|
.expect("environment should build")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn validator_block() {
|
||||||
|
let mut env = build_env();
|
||||||
|
|
||||||
|
let spec = E::default_spec();
|
||||||
|
|
||||||
|
let node = LocalBeaconNode::production(env.core_context());
|
||||||
|
let remote_node = node.remote_node().expect("should produce remote node");
|
||||||
|
|
||||||
|
let beacon_chain = node
|
||||||
|
.client
|
||||||
|
.beacon_chain()
|
||||||
|
.expect("client should have beacon chain");
|
||||||
|
|
||||||
|
let fork = beacon_chain.head().beacon_state.fork.clone();
|
||||||
|
|
||||||
|
let slot = Slot::new(1);
|
||||||
|
let randao_reveal = {
|
||||||
|
let proposer_index = beacon_chain
|
||||||
|
.block_proposer(slot)
|
||||||
|
.expect("should get proposer index");
|
||||||
|
let keypair = generate_deterministic_keypair(proposer_index);
|
||||||
|
let epoch = slot.epoch(E::slots_per_epoch());
|
||||||
|
let message = epoch.tree_hash_root();
|
||||||
|
let domain = spec.get_domain(epoch, Domain::Randao, &fork);
|
||||||
|
Signature::new(&message, domain, &keypair.sk)
|
||||||
|
};
|
||||||
|
|
||||||
|
let block = env
|
||||||
|
.runtime()
|
||||||
|
.block_on(
|
||||||
|
remote_node
|
||||||
|
.http
|
||||||
|
.validator()
|
||||||
|
.block(slot, randao_reveal.clone()),
|
||||||
|
)
|
||||||
|
.expect("should fetch block from http api");
|
||||||
|
|
||||||
|
let (expected_block, _state) = node
|
||||||
|
.client
|
||||||
|
.beacon_chain()
|
||||||
|
.expect("client should have beacon chain")
|
||||||
|
.produce_block(randao_reveal, slot)
|
||||||
|
.expect("should produce block");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
block, expected_block,
|
||||||
|
"the block returned from the API should be as expected"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn beacon_state() {
|
||||||
|
let mut env = build_env();
|
||||||
|
|
||||||
|
let node = LocalBeaconNode::production(env.core_context());
|
||||||
|
let remote_node = node.remote_node().expect("should produce remote node");
|
||||||
|
|
||||||
|
let (state_by_slot, root) = env
|
||||||
|
.runtime()
|
||||||
|
.block_on(remote_node.http.beacon().state_by_slot(Slot::new(0)))
|
||||||
|
.expect("should fetch state from http api");
|
||||||
|
|
||||||
|
let (state_by_root, root_2) = env
|
||||||
|
.runtime()
|
||||||
|
.block_on(remote_node.http.beacon().state_by_root(root))
|
||||||
|
.expect("should fetch state from http api");
|
||||||
|
|
||||||
|
let mut db_state = node
|
||||||
|
.client
|
||||||
|
.beacon_chain()
|
||||||
|
.expect("client should have beacon chain")
|
||||||
|
.state_at_slot(Slot::new(0))
|
||||||
|
.expect("should find state");
|
||||||
|
db_state.drop_all_caches();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
root, root_2,
|
||||||
|
"the two roots returned from the api should be identical"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
root,
|
||||||
|
db_state.canonical_root(),
|
||||||
|
"root from database should match that from the API"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
state_by_slot, db_state,
|
||||||
|
"genesis state by slot from api should match that from the DB"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
state_by_root, db_state,
|
||||||
|
"genesis state by root from api should match that from the DB"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn beacon_block() {
|
||||||
|
let mut env = build_env();
|
||||||
|
|
||||||
|
let node = LocalBeaconNode::production(env.core_context());
|
||||||
|
let remote_node = node.remote_node().expect("should produce remote node");
|
||||||
|
|
||||||
|
let (block_by_slot, root) = env
|
||||||
|
.runtime()
|
||||||
|
.block_on(remote_node.http.beacon().block_by_slot(Slot::new(0)))
|
||||||
|
.expect("should fetch block from http api");
|
||||||
|
|
||||||
|
let (block_by_root, root_2) = env
|
||||||
|
.runtime()
|
||||||
|
.block_on(remote_node.http.beacon().block_by_root(root))
|
||||||
|
.expect("should fetch block from http api");
|
||||||
|
|
||||||
|
let db_block = node
|
||||||
|
.client
|
||||||
|
.beacon_chain()
|
||||||
|
.expect("client should have beacon chain")
|
||||||
|
.block_at_slot(Slot::new(0))
|
||||||
|
.expect("should find block")
|
||||||
|
.expect("block should not be none");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
root, root_2,
|
||||||
|
"the two roots returned from the api should be identical"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
root,
|
||||||
|
db_block.canonical_root(),
|
||||||
|
"root from database should match that from the API"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
block_by_slot, db_block,
|
||||||
|
"genesis block by slot from api should match that from the DB"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
block_by_root, db_block,
|
||||||
|
"genesis block by root from api should match that from the DB"
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -55,7 +55,7 @@
|
|||||||
|
|
||||||
use prometheus::{HistogramOpts, HistogramTimer, Opts};
|
use prometheus::{HistogramOpts, HistogramTimer, Opts};
|
||||||
|
|
||||||
pub use prometheus::{Histogram, IntCounter, IntGauge, Result};
|
pub use prometheus::{Encoder, Histogram, IntCounter, IntGauge, Result, TextEncoder};
|
||||||
|
|
||||||
/// Collect all the metrics for reporting.
|
/// Collect all the metrics for reporting.
|
||||||
pub fn gather() -> Vec<prometheus::proto::MetricFamily> {
|
pub fn gather() -> Vec<prometheus::proto::MetricFamily> {
|
||||||
|
|||||||
@@ -12,3 +12,6 @@ url = "1.2"
|
|||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
futures = "0.1.25"
|
futures = "0.1.25"
|
||||||
types = { path = "../../../eth2/types" }
|
types = { path = "../../../eth2/types" }
|
||||||
|
rest_api = { path = "../../../beacon_node/rest_api" }
|
||||||
|
hex = "0.3"
|
||||||
|
eth2_ssz = { path = "../../../eth2/utils/ssz" }
|
||||||
|
|||||||
@@ -6,9 +6,10 @@
|
|||||||
use futures::{Future, IntoFuture};
|
use futures::{Future, IntoFuture};
|
||||||
use reqwest::r#async::{Client, RequestBuilder};
|
use reqwest::r#async::{Client, RequestBuilder};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
use ssz::Encode;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use types::{BeaconBlock, BeaconState, EthSpec};
|
use types::{BeaconBlock, BeaconState, EthSpec, Signature};
|
||||||
use types::{Hash256, Slot};
|
use types::{Hash256, Slot};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
@@ -53,16 +54,55 @@ impl<E: EthSpec> HttpClient<E> {
|
|||||||
Beacon(self.clone())
|
Beacon(self.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn validator(&self) -> Validator<E> {
|
||||||
|
Validator(self.clone())
|
||||||
|
}
|
||||||
|
|
||||||
fn url(&self, path: &str) -> Result<Url, Error> {
|
fn url(&self, path: &str) -> Result<Url, Error> {
|
||||||
self.url.join(path).map_err(|e| e.into())
|
self.url.join(path).map_err(|e| e.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&self, path: &str) -> Result<RequestBuilder, Error> {
|
pub fn get(&self, path: &str) -> Result<RequestBuilder, Error> {
|
||||||
|
// TODO: add timeout
|
||||||
self.url(path)
|
self.url(path)
|
||||||
.map(|url| Client::new().get(&url.to_string()))
|
.map(|url| Client::new().get(&url.to_string()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Provides the functions on the `/beacon` endpoint of the node.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Validator<E>(HttpClient<E>);
|
||||||
|
|
||||||
|
impl<E: EthSpec> Validator<E> {
|
||||||
|
fn url(&self, path: &str) -> Result<Url, Error> {
|
||||||
|
self.0
|
||||||
|
.url("validator/")
|
||||||
|
.and_then(move |url| url.join(path).map_err(Error::from))
|
||||||
|
.map_err(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Requests a new (unsigned) block from the beacon node.
|
||||||
|
pub fn block(
|
||||||
|
&self,
|
||||||
|
slot: Slot,
|
||||||
|
randao_reveal: Signature,
|
||||||
|
) -> impl Future<Item = BeaconBlock<E>, Error = Error> {
|
||||||
|
let client = self.0.clone();
|
||||||
|
self.url("block")
|
||||||
|
.into_future()
|
||||||
|
.and_then(move |mut url| {
|
||||||
|
url.query_pairs_mut()
|
||||||
|
.append_pair("slot", &format!("{}", slot.as_u64()));
|
||||||
|
url.query_pairs_mut()
|
||||||
|
.append_pair("randao_reveal", &signature_as_string(&randao_reveal));
|
||||||
|
client.get(&url.to_string())
|
||||||
|
})
|
||||||
|
.and_then(|builder| builder.send().map_err(Error::from))
|
||||||
|
.and_then(|response| response.error_for_status().map_err(Error::from))
|
||||||
|
.and_then(|mut success| success.json::<BeaconBlock<E>>().map_err(Error::from))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Provides the functions on the `/beacon` endpoint of the node.
|
/// Provides the functions on the `/beacon` endpoint of the node.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Beacon<E>(HttpClient<E>);
|
pub struct Beacon<E>(HttpClient<E>);
|
||||||
@@ -76,16 +116,32 @@ impl<E: EthSpec> Beacon<E> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the block and block root at the given slot.
|
/// Returns the block and block root at the given slot.
|
||||||
pub fn block_at_slot(
|
pub fn block_by_slot(
|
||||||
&self,
|
&self,
|
||||||
slot: Slot,
|
slot: Slot,
|
||||||
|
) -> impl Future<Item = (BeaconBlock<E>, Hash256), Error = Error> {
|
||||||
|
self.block("slot", format!("{}", slot.as_u64()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the block and block root at the given root.
|
||||||
|
pub fn block_by_root(
|
||||||
|
&self,
|
||||||
|
root: Hash256,
|
||||||
|
) -> impl Future<Item = (BeaconBlock<E>, Hash256), Error = Error> {
|
||||||
|
self.block("root", root_as_string(root))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the block and block root at the given slot.
|
||||||
|
fn block(
|
||||||
|
&self,
|
||||||
|
query_key: &'static str,
|
||||||
|
query_param: String,
|
||||||
) -> impl Future<Item = (BeaconBlock<E>, Hash256), Error = Error> {
|
) -> impl Future<Item = (BeaconBlock<E>, Hash256), Error = Error> {
|
||||||
let client = self.0.clone();
|
let client = self.0.clone();
|
||||||
self.url("block")
|
self.url("block")
|
||||||
.into_future()
|
.into_future()
|
||||||
.and_then(move |mut url| {
|
.and_then(move |mut url| {
|
||||||
url.query_pairs_mut()
|
url.query_pairs_mut().append_pair(query_key, &query_param);
|
||||||
.append_pair("slot", &format!("{}", slot.as_u64()));
|
|
||||||
client.get(&url.to_string())
|
client.get(&url.to_string())
|
||||||
})
|
})
|
||||||
.and_then(|builder| builder.send().map_err(Error::from))
|
.and_then(|builder| builder.send().map_err(Error::from))
|
||||||
@@ -95,16 +151,32 @@ impl<E: EthSpec> Beacon<E> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the state and state root at the given slot.
|
/// Returns the state and state root at the given slot.
|
||||||
pub fn state_at_slot(
|
pub fn state_by_slot(
|
||||||
&self,
|
&self,
|
||||||
slot: Slot,
|
slot: Slot,
|
||||||
|
) -> impl Future<Item = (BeaconState<E>, Hash256), Error = Error> {
|
||||||
|
self.state("slot", format!("{}", slot.as_u64()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the state and state root at the given root.
|
||||||
|
pub fn state_by_root(
|
||||||
|
&self,
|
||||||
|
root: Hash256,
|
||||||
|
) -> impl Future<Item = (BeaconState<E>, Hash256), Error = Error> {
|
||||||
|
self.state("root", root_as_string(root))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the state and state root at the given slot.
|
||||||
|
fn state(
|
||||||
|
&self,
|
||||||
|
query_key: &'static str,
|
||||||
|
query_param: String,
|
||||||
) -> impl Future<Item = (BeaconState<E>, Hash256), Error = Error> {
|
) -> impl Future<Item = (BeaconState<E>, Hash256), Error = Error> {
|
||||||
let client = self.0.clone();
|
let client = self.0.clone();
|
||||||
self.url("state")
|
self.url("state")
|
||||||
.into_future()
|
.into_future()
|
||||||
.and_then(move |mut url| {
|
.and_then(move |mut url| {
|
||||||
url.query_pairs_mut()
|
url.query_pairs_mut().append_pair(query_key, &query_param);
|
||||||
.append_pair("slot", &format!("{}", slot.as_u64()));
|
|
||||||
client.get(&url.to_string())
|
client.get(&url.to_string())
|
||||||
})
|
})
|
||||||
.and_then(|builder| builder.send().map_err(Error::from))
|
.and_then(|builder| builder.send().map_err(Error::from))
|
||||||
@@ -128,6 +200,14 @@ pub struct StateResponse<T: EthSpec> {
|
|||||||
pub root: Hash256,
|
pub root: Hash256,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn root_as_string(root: Hash256) -> String {
|
||||||
|
format!("0x{:?}", root)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature_as_string(signature: &Signature) -> String {
|
||||||
|
format!("0x{}", hex::encode(signature.as_ssz_bytes()))
|
||||||
|
}
|
||||||
|
|
||||||
impl From<reqwest::Error> for Error {
|
impl From<reqwest::Error> for Error {
|
||||||
fn from(e: reqwest::Error) -> Error {
|
fn from(e: reqwest::Error) -> Error {
|
||||||
Error::ReqwestError(e)
|
Error::ReqwestError(e)
|
||||||
|
|||||||
Reference in New Issue
Block a user