Skip to content

Commit

Permalink
Add first attempt at warp server
Browse files Browse the repository at this point in the history
  • Loading branch information
paulhauner committed Sep 5, 2020
1 parent 2627463 commit 49ac075
Show file tree
Hide file tree
Showing 11 changed files with 572 additions and 4 deletions.
323 changes: 319 additions & 4 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ members = [
"beacon_node/client",
"beacon_node/eth1",
"beacon_node/eth2_libp2p",
"beacon_node/http_api",
"beacon_node/network",
"beacon_node/rest_api",
"beacon_node/store",
Expand Down
14 changes: 14 additions & 0 deletions beacon_node/beacon_chain/src/beacon_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,8 @@ pub struct BeaconChain<T: BeaconChainTypes> {
pub(crate) canonical_head: TimeoutRwLock<BeaconSnapshot<T::EthSpec>>,
/// The root of the genesis block.
pub genesis_block_root: Hash256,
/// The root of the genesis state.
pub genesis_state_root: Hash256,
/// The root of the list of genesis validators, used during syncing.
pub genesis_validators_root: Hash256,

Expand Down Expand Up @@ -458,6 +460,18 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
}
}

/// Returns the block root at the given slot, if any. Only returns roots in the canonical chain.
///
/// ## Errors
///
/// May return a database error.
pub fn block_root_at_slot(&self, slot: Slot) -> Result<Option<Hash256>, Error> {
process_results(self.rev_iter_block_roots()?, |mut iter| {
iter.find(|(_, this_slot)| *this_slot == slot)
.map(|(root, _)| root)
})
}

/// Returns the block at the given root, if any.
///
/// ## Errors
Expand Down
1 change: 1 addition & 0 deletions beacon_node/beacon_chain/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,7 @@ where
observed_attester_slashings: <_>::default(),
eth1_chain: self.eth1_chain,
genesis_validators_root: canonical_head.beacon_state.genesis_validators_root,
genesis_state_root: canonical_head.beacon_state_root,
canonical_head: TimeoutRwLock::new(canonical_head.clone()),
genesis_block_root: self
.genesis_block_root
Expand Down
1 change: 1 addition & 0 deletions beacon_node/client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,4 @@ lazy_static = "1.4.0"
lighthouse_metrics = { path = "../../common/lighthouse_metrics" }
time = "0.2.16"
bus = "2.2.3"
http_api = { path = "../http_api" }
9 changes: 9 additions & 0 deletions beacon_node/client/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,15 @@ where
TColdStore,
>,
> {
let http_api_context = Arc::new(http_api::Context {
chain: self.beacon_chain.clone(),
});
let ctx = http_api_context.clone();
self.runtime_context
.unwrap()
.executor
.spawn_without_exit(async move { http_api::serve(ctx).await }, "cats");

Client {
beacon_chain: self.beacon_chain,
network_globals: self.network_globals,
Expand Down
16 changes: 16 additions & 0 deletions beacon_node/http_api/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "http_api"
version = "0.1.0"
authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
warp = "0.2.4"
serde = { version = "1.0.110", features = ["derive"] }
tokio = { version = "0.2.21", features = ["sync"] }
parking_lot = "0.11.0"
types = { path = "../../consensus/types" }
hex = "0.4.2"
beacon_chain = { path = "../beacon_chain" }
66 changes: 66 additions & 0 deletions beacon_node/http_api/src/block_id.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use beacon_chain::{BeaconChain, BeaconChainTypes};
use std::str::FromStr;
use types::{Hash256, Slot};

#[derive(Debug)]
pub enum BlockId {
Head,
Genesis,
Finalized,
Justified,
Slot(Slot),
Root(Hash256),
}

impl BlockId {
pub fn root<T: BeaconChainTypes>(
&self,
chain: &BeaconChain<T>,
) -> Result<Hash256, warp::Rejection> {
match self {
BlockId::Head => chain
.head_info()
.map(|head| head.block_root)
.map_err(crate::reject::beacon_chain_error),
BlockId::Genesis => Ok(chain.genesis_block_root),
BlockId::Finalized => chain
.head_info()
.map(|head| head.finalized_checkpoint.root)
.map_err(crate::reject::beacon_chain_error),
BlockId::Justified => chain
.head_info()
.map(|head| head.current_justified_checkpoint.root)
.map_err(crate::reject::beacon_chain_error),
BlockId::Slot(slot) => chain
.block_root_at_slot(*slot)
.map_err(crate::reject::beacon_chain_error)
.and_then(|root_opt| root_opt.ok_or_else(|| warp::reject::not_found())),
BlockId::Root(root) => Ok(*root),
}
}
}

impl FromStr for BlockId {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"head" => Ok(BlockId::Head),
"genesis" => Ok(BlockId::Genesis),
"finalized" => Ok(BlockId::Finalized),
"justified" => Ok(BlockId::Justified),
other => {
if other.starts_with("0x") {
Hash256::from_str(s)
.map(BlockId::Root)
.map_err(|e| format!("{} cannot be parsed as a root", e))
} else {
u64::from_str(s)
.map(Slot::new)
.map(BlockId::Slot)
.map_err(|_| format!("{} cannot be parsed as a parameter", s))
}
}
}
}
}
78 changes: 78 additions & 0 deletions beacon_node/http_api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
mod block_id;
mod reject;
mod state_id;

use beacon_chain::{BeaconChain, BeaconChainTypes};
use block_id::BlockId;
use state_id::StateId;
use std::sync::Arc;
use warp::Filter;

const API_PREFIX: &str = "eth";
const API_VERSION: &str = "v1";

pub struct Context<T: BeaconChainTypes> {
pub chain: Option<Arc<BeaconChain<T>>>,
}

pub async fn serve<T: BeaconChainTypes>(ctx: Arc<Context<T>>) {
let base_path = warp::path(API_PREFIX).and(warp::path(API_VERSION));
let chain_filter = warp::any()
.map(move || ctx.chain.clone())
.and_then(|chain| async move {
match chain {
Some(chain) => Ok(chain),
None => Err(warp::reject::not_found()),
}
});

/*
* beacon/states
*/

let beacon_states_path = base_path
.and(warp::path("beacon"))
.and(warp::path("states"))
.and(warp::path::param::<StateId>())
.and(chain_filter.clone());

let beacon_state_root = beacon_states_path
.clone()
.and(warp::path("root"))
.and(warp::path::end())
.and_then(|state_id: StateId, chain: Arc<BeaconChain<T>>| async move {
state_id.root(&chain).map(|resp| warp::reply::json(&resp))
});

let beacon_state_fork = beacon_states_path
.clone()
.and(warp::path("fork"))
.and(warp::path::end())
.and_then(|state_id: StateId, chain: Arc<BeaconChain<T>>| async move {
state_id.root(&chain).map(|resp| warp::reply::json(&resp))
});

/*
* beacon/blocks
*/

let beacon_blocks_path = base_path
.and(warp::path("beacon"))
.and(warp::path("blocks"))
.and(warp::path::param::<BlockId>())
.and(chain_filter.clone());

let beacon_block_root = beacon_blocks_path
.clone()
.and(warp::path("root"))
.and(warp::path::end())
.and_then(|block_id: BlockId, chain: Arc<BeaconChain<T>>| async move {
block_id.root(&chain).map(|resp| warp::reply::json(&resp))
});

let routes = beacon_state_root
.or(beacon_state_fork)
.or(beacon_block_root);

warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}
10 changes: 10 additions & 0 deletions beacon_node/http_api/src/reject.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use warp::reject::Reject;

pub fn beacon_chain_error(e: beacon_chain::BeaconChainError) -> warp::reject::Rejection {
warp::reject::custom(BeaconChainError(e))
}

#[derive(Debug)]
pub struct BeaconChainError(pub beacon_chain::BeaconChainError);

impl Reject for BeaconChainError {}
57 changes: 57 additions & 0 deletions beacon_node/http_api/src/state_id.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use beacon_chain::{BeaconChain, BeaconChainTypes};
use std::str::FromStr;
use types::{Hash256, Slot};

#[derive(Debug)]
pub enum StateId {
Head,
Genesis,
Finalized,
Justified,
Slot(Slot),
Root(Hash256),
}

impl StateId {
pub fn root<T: BeaconChainTypes>(
&self,
chain: &BeaconChain<T>,
) -> Result<Hash256, warp::Rejection> {
match self {
StateId::Head => chain
.head_info()
.map(|head| head.state_root)
.map_err(crate::reject::beacon_chain_error),
StateId::Genesis => Ok(chain.genesis_state_root),
StateId::Finalized => todo!(),
StateId::Justified => todo!(),
StateId::Slot(_) => todo!(),
StateId::Root(root) => Ok(*root),
}
}
}

impl FromStr for StateId {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"head" => Ok(StateId::Head),
"genesis" => Ok(StateId::Genesis),
"finalized" => Ok(StateId::Finalized),
"justified" => Ok(StateId::Justified),
other => {
if other.starts_with("0x") {
Hash256::from_str(s)
.map(StateId::Root)
.map_err(|e| format!("{} cannot be parsed as a root", e))
} else {
u64::from_str(s)
.map(Slot::new)
.map(StateId::Slot)
.map_err(|_| format!("{} cannot be parsed as a slot", s))
}
}
}
}
}

0 comments on commit 49ac075

Please sign in to comment.