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
41 changes: 41 additions & 0 deletions Cargo.lock

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

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ members = [
"crates/catalog",
"crates/template",
"crates/babel",
"crates/babel-api",
"crates/fetcher",
"crates/fetcher-api",
"crates/cosmos-keys"
Expand All @@ -18,6 +19,7 @@ runtime-docker-compose = { path = "crates/runtime-docker-compose" }
runtime-trait = { path = "crates/runtime-trait" }
catalog = { path = "crates/catalog" }
template = { path = "crates/template" }
babel-api = { path = "crates/babel-api" }
fetcher-api = { path = "crates/fetcher-api" }
cosmos-keys = { path = "crates/cosmos-keys" }

Expand All @@ -34,6 +36,6 @@ tinytemplate = "1.2"
askama = "0.14.0"
clap = { version = "4.5", features = ["derive"] }
reqwest = { version = "0.12", default-features = false }
jsonrpsee = { version = "0.24", features = ["server", "ws-client", "macros"] }
jsonrpsee = { version = "0.24", features = ["server", "ws-client", "http-client", "macros"] }
tracing = "0.1"
tracing-subscriber = "0.3"
12 changes: 12 additions & 0 deletions crates/babel-api/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "babel-api"
version = "0.1.0"
edition = "2021"

[dependencies]
jsonrpsee = { workspace = true }
tokio = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
eyre = { workspace = true }
tracing = "0.1"
21 changes: 21 additions & 0 deletions crates/babel-api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use jsonrpsee::proc_macros::rpc;
use serde::{Deserialize, Serialize};

/// Status response
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Status {
pub peers: u64,
pub current_block_number: u64,
pub is_syncing: bool,
pub latest_block_number: Option<u64>,
pub is_ready: bool,
pub is_healthy: bool,
}

/// JSON-RPC API for Babel status
#[rpc(server, client)]
pub trait BabelApi {
/// Get comprehensive status
#[method(name = "status")]
async fn status(&self) -> Result<Status, jsonrpsee::types::ErrorObjectOwned>;
}
2 changes: 2 additions & 0 deletions crates/babel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ serde = { workspace = true }
serde_json = { workspace = true }
eyre = { workspace = true }
async-trait = { workspace = true }
babel-api = { workspace = true }

url = "2.5.7"
jsonrpsee = { workspace = true }

# HTTP server
axum = "0.8"
Expand Down
26 changes: 17 additions & 9 deletions crates/babel/bin/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,14 @@ struct Cli {
#[arg(long)]
rpc_url: String,

/// Server bind address
/// REST server bind address
#[arg(long, default_value = "127.0.0.1:3000")]
addr: String,

/// RPC server bind address
#[arg(long, default_value = "127.0.0.1:3001")]
rpc_addr: String,

/// Node name for metrics labels (defaults to hostname/IP from rpc_url)
#[arg(long)]
nodename: Option<String>,
Expand Down Expand Up @@ -56,29 +60,33 @@ async fn main() -> eyre::Result<()> {
nodename,
);

match cli.node_type.as_str() {
let server = match cli.node_type.as_str() {
"ethereum" => {
let babel = EthereumBabel::new(cli.rpc_url).await?;
let server = BabelServer::new(babel, nodename);
server.serve(&cli.addr).await?;
BabelServer::new(babel, nodename)
}
"ethereum_beacon" => {
let babel = EthereumBeaconBabel::new(cli.rpc_url);
let server = BabelServer::new(babel, nodename);
server.serve(&cli.addr).await?;
BabelServer::new(babel, nodename)
}
"cosmos" => {
let babel = CosmosBabel::new(cli.rpc_url);
let server = BabelServer::new(babel, nodename);
server.serve(&cli.addr).await?;
BabelServer::new(babel, nodename)
}
_ => {
return Err(eyre::eyre!(
"Unknown node type: {}. Supported types: ethereum, ethereum_beacon, cosmos",
cli.node_type
));
}
}
};

// Start RPC server in background
let rpc_addr = cli.rpc_addr.parse()?;
let (_rpc_handle, _rpc_local_addr) = babel::rpc::start_rpc_server(rpc_addr, server.cached_status()).await?;

// Start REST server
server.serve(&cli.addr).await?;

Ok(())
}
16 changes: 4 additions & 12 deletions crates/babel/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use async_trait::async_trait;
use serde::{Deserialize, Serialize};

// Re-export Status from babel-api
pub use babel_api::Status;

/// Core trait for blockchain node health checks
#[async_trait]
Expand All @@ -8,21 +10,11 @@ pub trait Babel: Send + Sync {
async fn status(&self) -> eyre::Result<Status>;
}

/// Status response
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Status {
pub peers: u64,
pub current_block_number: u64,
pub is_syncing: bool,
pub latest_block_number: Option<u64>,
pub is_ready: bool,
pub is_healthy: bool,
}

pub mod cosmos;
pub mod ethereum;
pub mod ethereum_beacon;
pub mod metrics;
pub mod rpc;
pub mod server;
mod utils;

Expand Down
49 changes: 49 additions & 0 deletions crates/babel/src/rpc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use babel_api::{BabelApiServer, Status};
use std::sync::Arc;
use tokio::sync::RwLock;

/// Implementation of the Babel RPC server
pub struct BabelRpcServer {
cached_status: Arc<RwLock<Option<Status>>>,
}

impl BabelRpcServer {
pub fn new(cached_status: Arc<RwLock<Option<Status>>>) -> Self {
Self { cached_status }
}
}

#[jsonrpsee::core::async_trait]
impl BabelApiServer for BabelRpcServer {
async fn status(&self) -> Result<Status, jsonrpsee::types::ErrorObjectOwned> {
self.cached_status
.read()
.await
.clone()
.ok_or_else(|| {
jsonrpsee::types::ErrorObjectOwned::owned(
1,
"Status not yet available",
None::<()>,
)
})
}
}

/// Start the RPC server
pub async fn start_rpc_server(
addr: std::net::SocketAddr,
cached_status: Arc<RwLock<Option<Status>>>,
) -> eyre::Result<(jsonrpsee::server::ServerHandle, std::net::SocketAddr)> {
use jsonrpsee::server::Server;

let server = Server::builder().build(addr).await?;
let local_addr = server.local_addr()?;

let rpc_server = BabelRpcServer::new(cached_status);
let handle = server.start(rpc_server.into_rpc());

tracing::info!("Babel RPC server listening on {}", local_addr);

Ok((handle, local_addr))
}
Loading
Loading