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"
|
dirs = "1.0.3"
|
||||||
exit-future = "0.1.3"
|
exit-future = "0.1.3"
|
||||||
futures = "0.1.25"
|
futures = "0.1.25"
|
||||||
|
reqwest = "0.9"
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
|
use crate::local_bootstrap::BootstrapParams;
|
||||||
use crate::{config::GenesisState, ClientConfig};
|
use crate::{config::GenesisState, ClientConfig};
|
||||||
use beacon_chain::{
|
use beacon_chain::{
|
||||||
lmd_ghost::{LmdGhost, ThreadSafeReducedTree},
|
lmd_ghost::{LmdGhost, ThreadSafeReducedTree},
|
||||||
@ -6,6 +7,7 @@ use beacon_chain::{
|
|||||||
store::Store,
|
store::Store,
|
||||||
BeaconChain, BeaconChainTypes,
|
BeaconChain, BeaconChainTypes,
|
||||||
};
|
};
|
||||||
|
use reqwest::Url;
|
||||||
use slog::{crit, info, Logger};
|
use slog::{crit, info, Logger};
|
||||||
use slot_clock::SlotClock;
|
use slot_clock::SlotClock;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
@ -74,6 +76,15 @@ where
|
|||||||
serde_yaml::from_reader(file)
|
serde_yaml::from_reader(file)
|
||||||
.map_err(|e| format!("Unable to parse YAML genesis state file: {:?}", e))?
|
.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);
|
let mut genesis_block = BeaconBlock::empty(&spec);
|
||||||
|
@ -48,6 +48,8 @@ pub enum GenesisState {
|
|||||||
},
|
},
|
||||||
/// Load a YAML-encoded genesis state from a file.
|
/// Load a YAML-encoded genesis state from a file.
|
||||||
Yaml { file: PathBuf },
|
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 {
|
impl Default for Config {
|
||||||
|
@ -2,6 +2,7 @@ extern crate slog;
|
|||||||
|
|
||||||
mod beacon_chain_types;
|
mod beacon_chain_types;
|
||||||
mod config;
|
mod config;
|
||||||
|
mod local_bootstrap;
|
||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod notifier;
|
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)))
|
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 config;
|
||||||
mod helpers;
|
mod helpers;
|
||||||
mod node;
|
mod node;
|
||||||
|
mod spec;
|
||||||
mod url_query;
|
mod url_query;
|
||||||
|
|
||||||
use beacon_chain::{BeaconChain, BeaconChainTypes};
|
use beacon_chain::{BeaconChain, BeaconChainTypes};
|
||||||
@ -101,10 +102,15 @@ pub fn start_server<T: BeaconChainTypes + Clone + 'static>(
|
|||||||
|
|
||||||
// Route the request to the correct handler.
|
// Route the request to the correct handler.
|
||||||
let result = match (req.method(), path.as_ref()) {
|
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") => beacon::get_state::<T>(req),
|
||||||
(&Method::GET, "/beacon/state_root") => beacon::get_state_root::<T>(req),
|
(&Method::GET, "/beacon/state_root") => beacon::get_state_root::<T>(req),
|
||||||
(&Method::GET, "/node/version") => node::get_version(req),
|
(&Method::GET, "/node/version") => node::get_version(req),
|
||||||
(&Method::GET, "/node/genesis_time") => node::get_genesis_time::<T>(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())),
|
_ => 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