Skip to content
This repository was archived by the owner on Jan 16, 2026. It is now read-only.
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
2 changes: 2 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions bin/host/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ kona-providers-alloy.workspace = true

# Alloy
alloy-rlp.workspace = true
alloy-transport.workspace = true
alloy-eips = { workspace = true, features = ["kzg"] }
alloy-serde.workspace = true
alloy-provider = { workspace = true, features = ["reqwest"] }
Expand Down Expand Up @@ -63,6 +64,7 @@ tokio = { workspace = true, features = ["full"] }
serde = { workspace = true, features = ["derive"] }
clap = { workspace = true, features = ["derive", "env"] }
tracing-subscriber = { workspace = true, features = ["fmt"] }
thiserror.workspace = true

[dev-dependencies]
proptest.workspace = true
Expand Down
92 changes: 63 additions & 29 deletions bin/host/src/interop/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ use super::{InteropHintHandler, InteropLocalInputs};
use crate::{
DiskKeyValueStore, MemoryKeyValueStore, OfflineHostBackend, OnlineHostBackend,
OnlineHostBackendCfg, PreimageServer, SharedKeyValueStore, SplitKeyValueStore,
eth::http_provider,
eth::http_provider, server::PreimageServerError,
};
use alloy_primitives::{B256, Bytes};
use alloy_provider::{Provider, RootProvider};
use anyhow::{Result, anyhow};
use clap::Parser;
use kona_cli::{
cli_parsers::{parse_b256, parse_bytes},
Expand Down Expand Up @@ -98,9 +97,35 @@ pub struct InteropHost {
pub rollup_config_paths: Option<Vec<PathBuf>>,
}

/// An error that can occur when handling interop hosts
#[derive(Debug, thiserror::Error)]
pub enum InteropHostError {
/// An error when handling preimage requests.
#[error("Error handling preimage request: {0}")]
PreimageServerError(#[from] PreimageServerError),
/// An IO error.
#[error("IO error: {0}")]
IOError(#[from] std::io::Error),
/// A JSON parse error.
#[error("Failed deserializing RollupConfig: {0}")]
ParseError(#[from] serde_json::Error),
/// Task failed to execute to completion.
#[error("Join error: {0}")]
ExecutionError(#[from] tokio::task::JoinError),
/// A RPC error.
#[error("Rpc Error: {0}")]
RcpError(#[from] alloy_transport::RpcError<alloy_transport::TransportErrorKind>),
/// An error when no provider found for chain ID.
#[error("No provider found for chain ID: {0}")]
RootProviderError(u64),
/// Any other error.
#[error("Error: {0}")]
Other(&'static str),
}

impl InteropHost {
/// Starts the [InteropHost] application.
pub async fn start(self) -> Result<()> {
pub async fn start(self) -> Result<(), InteropHostError> {
if self.server {
let hint = FileChannel::new(FileDescriptor::HintRead, FileDescriptor::HintWrite);
let preimage =
Expand All @@ -113,21 +138,27 @@ impl InteropHost {
}

/// Starts the preimage server, communicating with the client over the provided channels.
async fn start_server<C>(&self, hint: C, preimage: C) -> Result<JoinHandle<Result<()>>>
async fn start_server<C>(
&self,
hint: C,
preimage: C,
) -> Result<JoinHandle<Result<(), InteropHostError>>, InteropHostError>
where
C: Channel + Send + Sync + 'static,
{
let kv_store = self.create_key_value_store()?;

let task_handle = if self.is_offline() {
task::spawn(
task::spawn(async {
PreimageServer::new(
OracleServer::new(preimage),
HintReader::new(hint),
Arc::new(OfflineHostBackend::new(kv_store)),
)
.start(),
)
.start()
.await
.map_err(InteropHostError::from)
})
} else {
let providers = self.create_providers().await?;
let backend = OnlineHostBackend::new(
Expand All @@ -138,22 +169,24 @@ impl InteropHost {
)
.with_proactive_hint(HintType::L2BlockData);

task::spawn(
task::spawn(async {
PreimageServer::new(
OracleServer::new(preimage),
HintReader::new(hint),
Arc::new(backend),
)
.start(),
)
.start()
.await
.map_err(InteropHostError::from)
})
};

Ok(task_handle)
}

/// Starts the host in native mode, running both the client and preimage server in the same
/// process.
async fn start_native(&self) -> Result<()> {
async fn start_native(&self) -> Result<(), InteropHostError> {
let hint = BidirectionalChannel::new()?;
let preimage = BidirectionalChannel::new()?;

Expand All @@ -180,29 +213,27 @@ impl InteropHost {

/// Reads the [RollupConfig]s from the file system and returns a map of L2 chain ID ->
/// [RollupConfig]s.
pub fn read_rollup_configs(&self) -> Result<HashMap<u64, RollupConfig>> {
pub fn read_rollup_configs(&self) -> Result<HashMap<u64, RollupConfig>, InteropHostError> {
let rollup_config_paths = self.rollup_config_paths.as_ref().ok_or_else(|| {
anyhow::anyhow!(
"No rollup config paths provided. Please provide a path to the rollup configs."
InteropHostError::Other(
"No rollup config paths provided. Please provide a path to the rollup configs.",
)
})?;

rollup_config_paths.iter().try_fold(HashMap::default(), |mut acc, path| {
// Read the serialized config from the file system.
let ser_config = std::fs::read_to_string(path)
.map_err(|e| anyhow!("Error reading RollupConfig file: {e}"))?;
let ser_config = std::fs::read_to_string(path)?;

// Deserialize the config and return it.
let cfg: RollupConfig = serde_json::from_str(&ser_config)
.map_err(|e| anyhow!("Error deserializing RollupConfig: {e}"))?;
let cfg: RollupConfig = serde_json::from_str(&ser_config)?;

acc.insert(cfg.l2_chain_id, cfg);
Ok(acc)
})
}

/// Creates the key-value store for the host backend.
fn create_key_value_store(&self) -> Result<SharedKeyValueStore> {
fn create_key_value_store(&self) -> Result<SharedKeyValueStore, InteropHostError> {
let local_kv_store = InteropLocalInputs::new(self.clone());

let kv_store: SharedKeyValueStore = if let Some(ref data_dir) = self.data_dir {
Expand All @@ -219,18 +250,23 @@ impl InteropHost {
}

/// Creates the providers required for the preimage server backend.
async fn create_providers(&self) -> Result<InteropProviders> {
let l1_provider =
http_provider(self.l1_node_address.as_ref().ok_or(anyhow!("Provider must be set"))?);
async fn create_providers(&self) -> Result<InteropProviders, InteropHostError> {
let l1_provider = http_provider(
self.l1_node_address.as_ref().ok_or(InteropHostError::Other("Provider must be set"))?,
);

let blob_provider = OnlineBlobProvider::init(OnlineBeaconClient::new_http(
self.l1_beacon_address.clone().ok_or(anyhow!("Beacon API URL must be set"))?,
self.l1_beacon_address
.clone()
.ok_or(InteropHostError::Other("Beacon API URL must be set"))?,
))
.await;

// Resolve all chain IDs to their corresponding providers.
let l2_node_addresses =
self.l2_node_addresses.as_ref().ok_or(anyhow!("L2 node addresses must be set"))?;
let l2_node_addresses = self
.l2_node_addresses
.as_ref()
.ok_or(InteropHostError::Other("L2 node addresses must be set"))?;
let mut l2_providers = HashMap::default();
for l2_node_address in l2_node_addresses {
let l2_provider = http_provider::<Optimism>(l2_node_address);
Expand Down Expand Up @@ -260,9 +296,7 @@ pub struct InteropProviders {

impl InteropProviders {
/// Returns the L2 [RootProvider] for the given chain ID.
pub fn l2(&self, chain_id: &u64) -> Result<&RootProvider<Optimism>> {
self.l2s
.get(chain_id)
.ok_or_else(|| anyhow!("No provider found for chain ID: {}", chain_id))
pub fn l2(&self, chain_id: &u64) -> Result<&RootProvider<Optimism>, InteropHostError> {
self.l2s.get(chain_id).ok_or_else(|| InteropHostError::RootProviderError(*chain_id))
}
}
2 changes: 1 addition & 1 deletion bin/host/src/interop/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! This module contains the super-chain (interop) mode for the host.

mod cfg;
pub use cfg::{InteropHost, InteropProviders};
pub use cfg::{InteropHost, InteropHostError, InteropProviders};

mod local_kv;
pub use local_kv::InteropLocalInputs;
Expand Down
2 changes: 1 addition & 1 deletion bin/host/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]

mod server;
pub use server::PreimageServer;
pub use server::{PreimageServer, PreimageServerError};

mod kv;
pub use kv::{
Expand Down
32 changes: 24 additions & 8 deletions bin/host/src/server.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
//! This module contains the [PreimageServer] struct and its implementation.

use anyhow::{Result, anyhow};
use kona_preimage::{
HintReaderServer, PreimageOracleServer, PreimageServerBackend, errors::PreimageOracleError,
};
Expand All @@ -20,6 +19,20 @@ pub struct PreimageServer<P, H, B> {
backend: Arc<B>,
}

/// An error that can occur when handling preimage requests
#[derive(Debug, thiserror::Error)]
pub enum PreimageServerError {
/// A preimage request error.
#[error("Failed to serve preimage request: {0}")]
PreimageRequestFailed(PreimageOracleError),
/// An error when failed to serve route hint.
#[error("Failed to route hint: {0}")]
RouteHintFailed(PreimageOracleError),
/// Task failed to execute to completion.
#[error("Join error: {0}")]
ExecutionError(#[from] tokio::task::JoinError),
}

impl<P, H, B> PreimageServer<P, H, B>
where
P: PreimageOracleServer + Send + Sync + 'static,
Expand All @@ -33,21 +46,24 @@ where
}

/// Starts the [PreimageServer] and waits for incoming requests.
pub async fn start(self) -> Result<()> {
pub async fn start(self) -> Result<(), PreimageServerError> {
// Create the futures for the oracle server and hint router.
let server = spawn(Self::start_oracle_server(self.oracle_server, self.backend.clone()));
let hint_router = spawn(Self::start_hint_router(self.hint_reader, self.backend.clone()));

// Race the two futures to completion, returning the result of the first one to finish.
tokio::select! {
s = server => s.map_err(|e| anyhow!(e))?,
h = hint_router => h.map_err(|e| anyhow!(e))?,
s = server => s?,
h = hint_router => h?,
}
}

/// Starts the oracle server, which waits for incoming preimage requests and serves them to the
/// client.
async fn start_oracle_server(oracle_server: P, backend: Arc<B>) -> Result<()> {
async fn start_oracle_server(
oracle_server: P,
backend: Arc<B>,
) -> Result<(), PreimageServerError> {
info!(target: "host-server", "Starting oracle server");
loop {
// Serve the next preimage request. This `await` will yield to the runtime
Expand All @@ -57,15 +73,15 @@ where
Err(PreimageOracleError::IOError(_)) => return Ok(()),
Err(e) => {
error!("Failed to serve preimage request: {e}");
return Err(anyhow!("Failed to serve preimage request: {e}"));
return Err(PreimageServerError::PreimageRequestFailed(e));
}
}
}
}

/// Starts the hint router, which waits for incoming hints and routes them to the appropriate
/// handler.
async fn start_hint_router(hint_reader: H, backend: Arc<B>) -> Result<()> {
async fn start_hint_router(hint_reader: H, backend: Arc<B>) -> Result<(), PreimageServerError> {
info!(target: "host-server", "Starting hint router");
loop {
// Route the next hint. This `await` will yield to the runtime if no progress can be
Expand All @@ -75,7 +91,7 @@ where
Err(PreimageOracleError::IOError(_)) => return Ok(()),
Err(e) => {
error!("Failed to serve route hint: {e}");
return Err(anyhow!("Failed to route hint: {e}"));
return Err(PreimageServerError::RouteHintFailed(e));
}
}
}
Expand Down
Loading
Loading