Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(host): Modular components #915

Merged
merged 6 commits into from
Jan 22, 2025
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
11 changes: 1 addition & 10 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion bin/host/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ tracing.workspace = true
reqwest.workspace = true
serde_json.workspace = true
async-trait.workspace = true
rocksdb = { workspace = true, features = ["snappy"] }
tokio = { workspace = true, features = ["full"] }
serde = { workspace = true, features = ["derive"] }
rocksdb = { workspace = true, features = ["snappy"] }
clap = { workspace = true, features = ["derive", "env"] }
tracing-subscriber = { workspace = true, features = ["fmt"] }

Expand Down
2 changes: 1 addition & 1 deletion bin/host/src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ pub enum HostMode {
}

/// Styles for the CLI application.
const fn cli_styles() -> clap::builder::Styles {
pub(crate) const fn cli_styles() -> clap::builder::Styles {
clap::builder::Styles::styled()
.usage(Style::new().bold().underline().fg_color(Some(Color::Ansi(AnsiColor::Yellow))))
.header(Style::new().bold().underline().fg_color(Some(Color::Ansi(AnsiColor::Yellow))))
Expand Down
12 changes: 12 additions & 0 deletions bin/host/src/eth/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
//! Ethereum utilities for the host binary.

use alloy_provider::ReqwestProvider;
use alloy_rpc_client::RpcClient;
use alloy_transport_http::Http;
use reqwest::Client;

mod blobs;
pub use blobs::{
APIConfigResponse, APIGenesisResponse, OnlineBlobProvider, ReducedConfigData,
Expand All @@ -8,3 +13,10 @@ pub use blobs::{

mod precompiles;
pub(crate) use precompiles::execute;

/// Returns an HTTP provider for the given URL.
pub fn http_provider(url: &str) -> ReqwestProvider {
let url = url.parse().unwrap();
let http = Http::<Client>::new(url);
ReqwestProvider::new(RpcClient::new(http, true))
}
2 changes: 1 addition & 1 deletion bin/host/src/fetcher.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! Fetcher trait definition.
//! [Fetcher] trait definition.

use kona_preimage::{HintRouter, PreimageFetcher};

Expand Down
113 changes: 4 additions & 109 deletions bin/host/src/interop/cli.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,15 @@
//! This module contains all CLI-specific code for the interop entrypoint.

use super::{
local_kv::DEFAULT_CHAIN_ID, start_server, start_server_and_native_client, LocalKeyValueStore,
};
use crate::{
cli::{parse_b256, parse_bytes},
eth::OnlineBlobProvider,
kv::{DiskKeyValueStore, MemoryKeyValueStore, SharedKeyValueStore, SplitKeyValueStore},
};
use super::local_kv::DEFAULT_CHAIN_ID;
use crate::cli::{cli_styles, parse_b256, parse_bytes};
use alloy_primitives::{Bytes, B256};
use alloy_provider::{Provider, ReqwestProvider};
use alloy_rlp::Decodable;
use alloy_rpc_client::RpcClient;
use alloy_transport_http::Http;
use anyhow::{anyhow, Result};
use clap::{
builder::styling::{AnsiColor, Color, Style},
Parser,
};
use clap::Parser;
use kona_proof_interop::PreState;
use maili_genesis::RollupConfig;
use reqwest::Client;
use serde::Serialize;
use std::{collections::HashMap, path::PathBuf, sync::Arc};
use tokio::sync::RwLock;
use tracing::error;
use std::{collections::HashMap, path::PathBuf};

/// The host binary CLI application arguments.
#[derive(Default, Parser, Serialize, Clone, Debug)]
Expand Down Expand Up @@ -97,26 +82,6 @@ pub struct InteropHostCli {
}

impl InteropHostCli {
/// Runs the host binary in single-chain mode.
pub async fn run(self) -> Result<()> {
if self.server {
start_server(self).await?;
} else {
let status = match start_server_and_native_client(self).await {
Ok(status) => status,
Err(e) => {
error!(target: "kona_host", "Exited with an error: {:?}", e);
panic!("{e}");
}
};

// Bubble up the exit status of the client program.
std::process::exit(status as i32);
}

Ok(())
}

/// Returns `true` if the host is running in offline mode.
pub const fn is_offline(&self) -> bool {
self.l1_node_address.is_none() &&
Expand Down Expand Up @@ -150,57 +115,6 @@ impl InteropHostCli {
}
}

/// Creates the providers associated with the [InteropHostCli] configuration.
///
/// ## Returns
/// - A [ReqwestProvider] for the L1 node.
/// - An [OnlineBlobProvider] for the L1 beacon node.
/// - A hash map of chain ID -> [ReqwestProvider] for the L2 nodes.
pub async fn create_providers(
&self,
) -> Result<(ReqwestProvider, OnlineBlobProvider, HashMap<u64, ReqwestProvider>)> {
let l1_provider = Self::http_provider(
self.l1_node_address.as_ref().ok_or(anyhow!("Provider must be set"))?,
);

let blob_provider = OnlineBlobProvider::new_http(
self.l1_beacon_address.clone().ok_or(anyhow!("Beacon API URL must be set"))?,
)
.await
.map_err(|e| anyhow!("Failed to load blob provider configuration: {e}"))?;

// 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 mut l2_providers = HashMap::with_capacity(l2_node_addresses.len());
for l2_node_address in l2_node_addresses {
let l2_provider = Self::http_provider(l2_node_address);
let chain_id = l2_provider.get_chain_id().await?;

l2_providers.insert(chain_id, l2_provider);
}

Ok((l1_provider, blob_provider, l2_providers))
}

/// Parses the CLI arguments and returns a new instance of a [SharedKeyValueStore], as it is
/// configured to be created.
pub fn construct_kv_store(&self) -> SharedKeyValueStore {
let local_kv_store = LocalKeyValueStore::new(self.clone());

let kv_store: SharedKeyValueStore = if let Some(ref data_dir) = self.data_dir {
let disk_kv_store = DiskKeyValueStore::new(data_dir.clone());
let split_kv_store = SplitKeyValueStore::new(local_kv_store, disk_kv_store);
Arc::new(RwLock::new(split_kv_store))
} else {
let mem_kv_store = MemoryKeyValueStore::new();
let split_kv_store = SplitKeyValueStore::new(local_kv_store, mem_kv_store);
Arc::new(RwLock::new(split_kv_store))
};

kv_store
}

/// 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>> {
Expand All @@ -226,23 +140,4 @@ impl InteropHostCli {
},
)
}

/// Returns an HTTP provider for the given URL.
fn http_provider(url: &str) -> ReqwestProvider {
let url = url.parse().unwrap();
let http = Http::<Client>::new(url);
ReqwestProvider::new(RpcClient::new(http, true))
}
}

/// Styles for the CLI application.
const fn cli_styles() -> clap::builder::Styles {
clap::builder::Styles::styled()
.usage(Style::new().bold().underline().fg_color(Some(Color::Ansi(AnsiColor::Yellow))))
.header(Style::new().bold().underline().fg_color(Some(Color::Ansi(AnsiColor::Yellow))))
.literal(Style::new().fg_color(Some(Color::Ansi(AnsiColor::Green))))
.invalid(Style::new().bold().fg_color(Some(Color::Ansi(AnsiColor::Red))))
.error(Style::new().bold().fg_color(Some(Color::Ansi(AnsiColor::Red))))
.valid(Style::new().bold().underline().fg_color(Some(Color::Ansi(AnsiColor::Green))))
.placeholder(Style::new().fg_color(Some(Color::Ansi(AnsiColor::White))))
}
3 changes: 2 additions & 1 deletion bin/host/src/interop/fetcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//! preimages from a remote source serving the super-chain (interop) proof mode.

use super::InteropHostCli;
use crate::{eth::OnlineBlobProvider, kv::KeyValueStore};
use crate::eth::OnlineBlobProvider;
use alloy_consensus::{Header, TxEnvelope, EMPTY_ROOT_HASH};
use alloy_eips::{
eip2718::Encodable2718,
Expand All @@ -18,6 +18,7 @@ use alloy_rpc_types::{
};
use anyhow::{anyhow, Result};
use async_trait::async_trait;
use kona_host::KeyValueStore;
use kona_preimage::{
errors::{PreimageOracleError, PreimageOracleResult},
HintRouter, PreimageFetcher, PreimageKey, PreimageKeyType,
Expand Down
2 changes: 1 addition & 1 deletion bin/host/src/interop/local_kv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
//! using the [InteropHostCli] config.

use super::InteropHostCli;
use crate::kv::KeyValueStore;
use alloy_primitives::{keccak256, B256};
use anyhow::Result;
use kona_host::KeyValueStore;
use kona_preimage::PreimageKey;
use kona_proof_interop::boot::{
L1_HEAD_KEY, L2_AGREED_PRE_STATE_KEY, L2_CHAIN_ID_KEY, L2_CLAIMED_POST_STATE_KEY,
Expand Down
109 changes: 2 additions & 107 deletions bin/host/src/interop/mod.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,5 @@
//! This module contains the super-chain (interop) mode for the host.

use crate::{kv::KeyValueStore, server::PreimageServer};
use anyhow::Result;
use kona_preimage::{
BidirectionalChannel, HintReader, HintWriter, NativeChannel, OracleReader, OracleServer,
};
use kona_std_fpvm::{FileChannel, FileDescriptor};
use std::sync::Arc;
use tokio::{sync::RwLock, task};
use tracing::info;

mod cli;
pub use cli::InteropHostCli;

Expand All @@ -19,100 +9,5 @@ pub use local_kv::LocalKeyValueStore;
mod fetcher;
pub use fetcher::InteropFetcher;

/// Starts the [PreimageServer] in the primary thread. In this mode, the host program has been
/// invoked by the Fault Proof VM and the client program is running in the parent process.
pub async fn start_server(cfg: InteropHostCli) -> Result<()> {
let (preimage_chan, hint_chan) = (
FileChannel::new(FileDescriptor::PreimageRead, FileDescriptor::PreimageWrite),
FileChannel::new(FileDescriptor::HintRead, FileDescriptor::HintWrite),
);
let oracle_server = OracleServer::new(preimage_chan);
let hint_reader = HintReader::new(hint_chan);
let kv_store = cfg.construct_kv_store();
let fetcher = if !cfg.is_offline() {
let (l1_provider, blob_provider, l2_providers) = cfg.create_providers().await?;
Some(Arc::new(RwLock::new(InteropFetcher::new(
cfg,
kv_store.clone(),
l1_provider,
blob_provider,
l2_providers,
))))
} else {
None
};

// Start the server and wait for it to complete.
info!("Starting preimage server.");
PreimageServer::new(oracle_server, hint_reader, kv_store, fetcher).start().await?;
info!("Preimage server has exited.");

Ok(())
}

/// Starts the [PreimageServer] and the client program in separate threads. The client program is
/// ran natively in this mode.
///
/// ## Takes
/// - `cfg`: The host configuration.
///
/// ## Returns
/// - `Ok(exit_code)` if the client program exits successfully.
/// - `Err(_)` if the client program failed to execute, was killed by a signal, or the host program
/// exited first.
pub async fn start_server_and_native_client(cfg: InteropHostCli) -> Result<i32> {
let hint_chan = BidirectionalChannel::new()?;
let preimage_chan = BidirectionalChannel::new()?;
let kv_store = cfg.construct_kv_store();
let fetcher = if !cfg.is_offline() {
let (l1_provider, blob_provider, l2_providers) = cfg.create_providers().await?;
Some(Arc::new(RwLock::new(InteropFetcher::new(
cfg,
kv_store.clone(),
l1_provider,
blob_provider,
l2_providers,
))))
} else {
None
};

// Create the server and start it.
let server_task = task::spawn(start_native_preimage_server(
kv_store,
fetcher,
hint_chan.host,
preimage_chan.host,
));

// Start the client program in a separate child process.
let program_task = task::spawn(kona_client::interop::run(
OracleReader::new(preimage_chan.client),
HintWriter::new(hint_chan.client),
None,
));

// Execute both tasks and wait for them to complete.
info!("Starting preimage server and client program.");
let (_, client_result) = tokio::try_join!(server_task, program_task,)?;
info!(target: "kona_host", "Preimage server and client program have joined.");

Ok(client_result.is_err() as i32)
}

/// Starts the preimage server in a separate thread. The client program is ran natively in this
/// mode.
pub async fn start_native_preimage_server<KV>(
kv_store: Arc<RwLock<KV>>,
fetcher: Option<Arc<RwLock<InteropFetcher<KV>>>>,
hint_chan: NativeChannel,
preimage_chan: NativeChannel,
) -> Result<()>
where
KV: KeyValueStore + Send + Sync + ?Sized + 'static,
{
let hint_reader = HintReader::new(hint_chan);
let oracle_server = OracleServer::new(preimage_chan);

PreimageServer::new(oracle_server, hint_reader, kv_store, fetcher).start().await
}
mod orchestrator;
pub use orchestrator::InteropProviders;
Loading
Loading