Add first attempts at HTTP bootstrap
This commit is contained in:
parent
82e8aafb01
commit
4f98a3985f
@ -27,3 +27,4 @@ clap = "2.32.0"
|
||||
dirs = "1.0.3"
|
||||
exit-future = "0.1.3"
|
||||
futures = "0.1.25"
|
||||
reqwest = "0.9"
|
||||
|
@ -1,4 +1,5 @@
|
||||
use crate::error::Result;
|
||||
use crate::local_bootstrap::BootstrapParams;
|
||||
use crate::{config::GenesisState, ClientConfig};
|
||||
use beacon_chain::{
|
||||
lmd_ghost::{LmdGhost, ThreadSafeReducedTree},
|
||||
@ -6,6 +7,7 @@ use beacon_chain::{
|
||||
store::Store,
|
||||
BeaconChain, BeaconChainTypes,
|
||||
};
|
||||
use reqwest::Url;
|
||||
use slog::{crit, info, Logger};
|
||||
use slot_clock::SlotClock;
|
||||
use std::fs::File;
|
||||
@ -74,6 +76,15 @@ where
|
||||
serde_yaml::from_reader(file)
|
||||
.map_err(|e| format!("Unable to parse YAML genesis state file: {:?}", e))?
|
||||
}
|
||||
GenesisState::HttpBootstrap { server } => {
|
||||
let url: Url =
|
||||
Url::parse(&server).map_err(|e| format!("Invalid bootstrap server url: {}", e))?;
|
||||
|
||||
let params = BootstrapParams::from_http_api(url)
|
||||
.map_err(|e| format!("Failed to bootstrap from HTTP server: {:?}", e))?;
|
||||
|
||||
params.genesis_state
|
||||
}
|
||||
};
|
||||
|
||||
let mut genesis_block = BeaconBlock::empty(&spec);
|
||||
|
@ -48,6 +48,8 @@ pub enum GenesisState {
|
||||
},
|
||||
/// Load a YAML-encoded genesis state from a file.
|
||||
Yaml { file: PathBuf },
|
||||
/// Use a HTTP server (running our REST-API) to load genesis and finalized states and blocks.
|
||||
HttpBootstrap { server: String },
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
|
@ -2,6 +2,7 @@ extern crate slog;
|
||||
|
||||
mod beacon_chain_types;
|
||||
mod config;
|
||||
mod local_bootstrap;
|
||||
|
||||
pub mod error;
|
||||
pub mod notifier;
|
||||
|
93
beacon_node/client/src/local_bootstrap.rs
Normal file
93
beacon_node/client/src/local_bootstrap.rs
Normal file
@ -0,0 +1,93 @@
|
||||
use reqwest::{Error as HttpError, Url};
|
||||
use types::{BeaconBlock, BeaconState, Checkpoint, EthSpec, Slot};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
UrlCannotBeBase,
|
||||
HttpError(HttpError),
|
||||
}
|
||||
|
||||
impl From<HttpError> for Error {
|
||||
fn from(e: HttpError) -> Error {
|
||||
Error::HttpError(e)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BootstrapParams<T: EthSpec> {
|
||||
pub finalized_block: BeaconBlock<T>,
|
||||
pub finalized_state: BeaconState<T>,
|
||||
pub genesis_block: BeaconBlock<T>,
|
||||
pub genesis_state: BeaconState<T>,
|
||||
}
|
||||
|
||||
impl<T: EthSpec> BootstrapParams<T> {
|
||||
pub fn from_http_api(url: Url) -> Result<Self, Error> {
|
||||
let slots_per_epoch = get_slots_per_epoch(url.clone())?;
|
||||
let genesis_slot = Slot::new(0);
|
||||
let finalized_slot = get_finalized_slot(url.clone(), slots_per_epoch.as_u64())?;
|
||||
|
||||
Ok(Self {
|
||||
finalized_block: get_block(url.clone(), finalized_slot)?,
|
||||
finalized_state: get_state(url.clone(), finalized_slot)?,
|
||||
genesis_block: get_block(url.clone(), genesis_slot)?,
|
||||
genesis_state: get_state(url.clone(), genesis_slot)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn get_slots_per_epoch(mut url: Url) -> Result<Slot, Error> {
|
||||
url.path_segments_mut()
|
||||
.map(|mut url| {
|
||||
url.push("spec").push("slots_per_epoch");
|
||||
})
|
||||
.map_err(|_| Error::UrlCannotBeBase)?;
|
||||
|
||||
reqwest::get(url)?
|
||||
.error_for_status()?
|
||||
.json()
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
fn get_finalized_slot(mut url: Url, slots_per_epoch: u64) -> Result<Slot, Error> {
|
||||
url.path_segments_mut()
|
||||
.map(|mut url| {
|
||||
url.push("beacon").push("latest_finalized_checkpoint");
|
||||
})
|
||||
.map_err(|_| Error::UrlCannotBeBase)?;
|
||||
|
||||
let checkpoint: Checkpoint = reqwest::get(url)?.error_for_status()?.json()?;
|
||||
|
||||
Ok(checkpoint.epoch.start_slot(slots_per_epoch))
|
||||
}
|
||||
|
||||
fn get_state<T: EthSpec>(mut url: Url, slot: Slot) -> Result<BeaconState<T>, Error> {
|
||||
url.path_segments_mut()
|
||||
.map(|mut url| {
|
||||
url.push("beacon").push("state");
|
||||
})
|
||||
.map_err(|_| Error::UrlCannotBeBase)?;
|
||||
|
||||
url.query_pairs_mut()
|
||||
.append_pair("slot", &format!("{}", slot.as_u64()));
|
||||
|
||||
reqwest::get(url)?
|
||||
.error_for_status()?
|
||||
.json()
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
fn get_block<T: EthSpec>(mut url: Url, slot: Slot) -> Result<BeaconBlock<T>, Error> {
|
||||
url.path_segments_mut()
|
||||
.map(|mut url| {
|
||||
url.push("beacon").push("block");
|
||||
})
|
||||
.map_err(|_| Error::UrlCannotBeBase)?;
|
||||
|
||||
url.query_pairs_mut()
|
||||
.append_pair("slot", &format!("{}", slot.as_u64()));
|
||||
|
||||
reqwest::get(url)?
|
||||
.error_for_status()?
|
||||
.json()
|
||||
.map_err(Into::into)
|
||||
}
|
@ -58,3 +58,24 @@ pub fn get_state_root<T: BeaconChainTypes + 'static>(req: Request<Body>) -> ApiR
|
||||
|
||||
Ok(success_response(Body::from(json)))
|
||||
}
|
||||
|
||||
/// HTTP handler to return the highest finalized slot.
|
||||
pub fn get_latest_finalized_checkpoint<T: BeaconChainTypes + 'static>(
|
||||
req: Request<Body>,
|
||||
) -> ApiResult {
|
||||
let beacon_chain = req
|
||||
.extensions()
|
||||
.get::<Arc<BeaconChain<T>>>()
|
||||
.ok_or_else(|| ApiError::ServerError("Beacon chain extension missing".to_string()))?;
|
||||
|
||||
let checkpoint = beacon_chain
|
||||
.head()
|
||||
.beacon_state
|
||||
.finalized_checkpoint
|
||||
.clone();
|
||||
|
||||
let json: String = serde_json::to_string(&checkpoint)
|
||||
.map_err(|e| ApiError::ServerError(format!("Unable to serialize checkpoint: {:?}", e)))?;
|
||||
|
||||
Ok(success_response(Body::from(json)))
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ mod beacon;
|
||||
mod config;
|
||||
mod helpers;
|
||||
mod node;
|
||||
mod spec;
|
||||
mod url_query;
|
||||
|
||||
use beacon_chain::{BeaconChain, BeaconChainTypes};
|
||||
@ -101,10 +102,15 @@ pub fn start_server<T: BeaconChainTypes + Clone + 'static>(
|
||||
|
||||
// Route the request to the correct handler.
|
||||
let result = match (req.method(), path.as_ref()) {
|
||||
(&Method::GET, "/beacon/latest_finalized_checkpoint") => {
|
||||
beacon::get_latest_finalized_checkpoint::<T>(req)
|
||||
}
|
||||
(&Method::GET, "/beacon/state") => beacon::get_state::<T>(req),
|
||||
(&Method::GET, "/beacon/state_root") => beacon::get_state_root::<T>(req),
|
||||
(&Method::GET, "/node/version") => node::get_version(req),
|
||||
(&Method::GET, "/node/genesis_time") => node::get_genesis_time::<T>(req),
|
||||
(&Method::GET, "/spec") => spec::get_spec::<T>(req),
|
||||
(&Method::GET, "/spec/slots_per_epoch") => spec::get_slots_per_epoch::<T>(req),
|
||||
_ => Err(ApiError::MethodNotAllowed(path.clone())),
|
||||
};
|
||||
|
||||
|
27
beacon_node/rest_api/src/spec.rs
Normal file
27
beacon_node/rest_api/src/spec.rs
Normal file
@ -0,0 +1,27 @@
|
||||
use super::{success_response, ApiResult};
|
||||
use crate::ApiError;
|
||||
use beacon_chain::{BeaconChain, BeaconChainTypes};
|
||||
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 + 'static>(req: Request<Body>) -> ApiResult {
|
||||
let beacon_chain = req
|
||||
.extensions()
|
||||
.get::<Arc<BeaconChain<T>>>()
|
||||
.ok_or_else(|| ApiError::ServerError("Beacon chain extension missing".to_string()))?;
|
||||
|
||||
let json: String = serde_json::to_string(&beacon_chain.spec)
|
||||
.map_err(|e| ApiError::ServerError(format!("Unable to serialize spec: {:?}", e)))?;
|
||||
|
||||
Ok(success_response(Body::from(json)))
|
||||
}
|
||||
|
||||
/// HTTP handler to return the full spec object.
|
||||
pub fn get_slots_per_epoch<T: BeaconChainTypes + 'static>(_req: Request<Body>) -> ApiResult {
|
||||
let json: String = serde_json::to_string(&T::EthSpec::slots_per_epoch())
|
||||
.map_err(|e| ApiError::ServerError(format!("Unable to serialize epoch: {:?}", e)))?;
|
||||
|
||||
Ok(success_response(Body::from(json)))
|
||||
}
|
Loading…
Reference in New Issue
Block a user