Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 6 additions & 3 deletions bin/ethlambda/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use std::{

use clap::Parser;
use ethlambda_p2p::{Bootnode, parse_enrs, start_p2p};
use ethlambda_rpc::metrics::start_prometheus_metrics_api;
use ethlambda_types::primitives::H256;
use ethlambda_types::{
genesis::Genesis,
Expand All @@ -20,6 +19,7 @@ use tracing::{error, info};
use tracing_subscriber::{EnvFilter, Layer, Registry, layer::SubscriberExt};

use ethlambda_blockchain::BlockChain;
use ethlambda_storage::Store;

const ASCII_ART: &str = r#"
_ _ _ _ _
Expand Down Expand Up @@ -91,9 +91,10 @@ async fn main() {
read_validator_keys(&validators_path, &validator_keys_dir, &options.node_id);

let genesis_state = State::from_genesis(&genesis, validators);
let store = Store::from_genesis(genesis_state);

let (p2p_tx, p2p_rx) = tokio::sync::mpsc::unbounded_channel();
let blockchain = BlockChain::spawn(genesis_state, p2p_tx, validator_keys);
let blockchain = BlockChain::spawn(store.clone(), p2p_tx, validator_keys);

let p2p_handle = tokio::spawn(start_p2p(
node_p2p_key,
Expand All @@ -103,7 +104,9 @@ async fn main() {
p2p_rx,
));

start_prometheus_metrics_api(metrics_socket).await.unwrap();
ethlambda_rpc::start_rpc_server(metrics_socket, store)
.await
.unwrap();

info!("Node initialized");

Expand Down
7 changes: 3 additions & 4 deletions crates/blockchain/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use ethlambda_types::{
block::{BlockSignatures, BlockWithAttestation, SignedBlockWithAttestation},
primitives::TreeHash,
signature::ValidatorSecretKey,
state::{Checkpoint, State},
state::Checkpoint,
};
use spawned_concurrency::tasks::{
CallResponse, CastResponse, GenServer, GenServerHandle, send_after,
Expand Down Expand Up @@ -40,12 +40,11 @@ pub const SECONDS_PER_SLOT: u64 = 4;

impl BlockChain {
pub fn spawn(
genesis_state: State,
store: Store,
p2p_tx: mpsc::UnboundedSender<OutboundGossip>,
validator_keys: HashMap<u64, ValidatorSecretKey>,
) -> BlockChain {
let genesis_time = genesis_state.config.genesis_time;
let store = Store::from_genesis(genesis_state);
let genesis_time = store.config().genesis_time;
let key_manager = key_manager::KeyManager::new(validator_keys);
let handle = BlockChainServer {
store,
Expand Down
1 change: 1 addition & 0 deletions crates/common/types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ version.workspace = true
[dependencies]
thiserror.workspace = true
serde.workspace = true
hex.workspace = true
ethereum-types.workspace = true

leansig.workspace = true
Expand Down
3 changes: 2 additions & 1 deletion crates/common/types/src/block.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use serde::Serialize;
use ssz_derive::{Decode, Encode};
use ssz_types::typenum::U1048576;
use tree_hash_derive::TreeHash;
Expand Down Expand Up @@ -136,7 +137,7 @@ pub struct BlockWithAttestation {
///
/// Headers are smaller than full blocks. They're useful for tracking the chain
/// without storing everything.
#[derive(Debug, Clone, Encode, Decode, TreeHash)]
#[derive(Debug, Clone, Serialize, Encode, Decode, TreeHash)]
pub struct BlockHeader {
/// The slot in which the block was proposed
pub slot: u64,
Expand Down
12 changes: 10 additions & 2 deletions crates/common/types/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::{
pub type ValidatorRegistryLimit = U4096;

/// The main consensus state object
#[derive(Debug, Clone, Encode, Decode, TreeHash)]
#[derive(Debug, Clone, Serialize, Encode, Decode, TreeHash)]
pub struct State {
/// The chain's configuration parameters
pub config: ChainConfig,
Expand Down Expand Up @@ -62,14 +62,22 @@ pub type JustificationValidators =
ssz_types::BitList<ssz_types::typenum::Prod<HistoricalRootsLimit, ValidatorRegistryLimit>>;

/// Represents a validator's static metadata and operational interface.
#[derive(Debug, Clone, Encode, Decode, TreeHash)]
#[derive(Debug, Clone, Serialize, Encode, Decode, TreeHash)]
pub struct Validator {
/// XMSS one-time signature public key.
#[serde(serialize_with = "serialize_pubkey_hex")]
pub pubkey: ValidatorPubkeyBytes,
/// Validator index in the registry.
pub index: u64,
}

fn serialize_pubkey_hex<S>(pubkey: &ValidatorPubkeyBytes, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&hex::encode(pubkey))
}

impl Validator {
pub fn get_pubkey(&self) -> Result<ValidatorPublicKey, DecodeError> {
// TODO: make this unfallible by moving check to the constructor
Expand Down
3 changes: 3 additions & 0 deletions crates/net/rpc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,6 @@ tokio.workspace = true
prometheus.workspace = true
thiserror.workspace = true
tracing.workspace = true
ethlambda-storage.workspace = true
serde.workspace = true
serde_json.workspace = true
44 changes: 44 additions & 0 deletions crates/net/rpc/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1 +1,45 @@
use std::net::SocketAddr;

use axum::{Json, Router, response::IntoResponse, routing::get};
use ethlambda_storage::Store;

pub mod metrics;

pub async fn start_rpc_server(address: SocketAddr, store: Store) -> Result<(), std::io::Error> {
let metrics_router = metrics::start_prometheus_metrics_api();

// Create stateful routes first, then convert to stateless by applying state
let api_routes = Router::new()
.route("/lean/v0/states/finalized", get(get_latest_finalized_state))
.route(
"/lean/v0/checkpoints/justified",
get(get_latest_justified_state),
)
.with_state(store);

// Merge stateless routers
let app = Router::new().merge(metrics_router).merge(api_routes);

// Start the axum app
let listener = tokio::net::TcpListener::bind(address).await?;
axum::serve(listener, app).await?;

Ok(())
}

async fn get_latest_finalized_state(
axum::extract::State(store): axum::extract::State<Store>,
) -> impl IntoResponse {
let finalized = store.latest_finalized();
let state = store
.get_state(&finalized.root)
.expect("finalized state exists");
Json(state)
}

async fn get_latest_justified_state(
axum::extract::State(store): axum::extract::State<Store>,
) -> impl IntoResponse {
let checkpoint = store.latest_justified();
Json(checkpoint)
}
14 changes: 3 additions & 11 deletions crates/net/rpc/src/metrics.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,11 @@
use std::net::SocketAddr;

use axum::{Router, http::HeaderValue, response::IntoResponse, routing::get};
use thiserror::Error;
use tracing::warn;

pub async fn start_prometheus_metrics_api(address: SocketAddr) -> Result<(), std::io::Error> {
let app = Router::new()
pub fn start_prometheus_metrics_api() -> Router {
Router::new()
.route("/metrics", get(get_metrics))
.route("/health", get(get_health));

// Start the axum app
let listener = tokio::net::TcpListener::bind(address).await?;
axum::serve(listener, app).await?;

Ok(())
.route("/lean/v0/health", get(get_health))
}

pub(crate) async fn get_health() -> impl IntoResponse {
Expand Down