diff --git a/bin/host/src/bin/host.rs b/bin/host/src/bin/host.rs index f4c07f969d..03a100938d 100644 --- a/bin/host/src/bin/host.rs +++ b/bin/host/src/bin/host.rs @@ -6,10 +6,7 @@ use anyhow::Result; use clap::{ArgAction, Parser, Subcommand}; -use kona_host::{ - cli::{cli_styles, init_tracing_subscriber}, - DetachedHostOrchestrator, -}; +use kona_host::cli::{cli_styles, init_tracing_subscriber}; use serde::Serialize; use tracing::info; @@ -39,10 +36,10 @@ pub struct HostCli { pub enum HostMode { /// Run the host in single-chain mode. #[cfg(feature = "single")] - Single(kona_host::single::SingleChainHostCli), + Single(kona_host::single::SingleChainHost), /// Run the host in super-chain (interop) mode. #[cfg(feature = "interop")] - Super(kona_host::interop::InteropHostCli), + Super(kona_host::interop::InteropHost), } #[tokio::main(flavor = "multi_thread")] @@ -53,11 +50,11 @@ async fn main() -> Result<()> { match cfg.mode { #[cfg(feature = "single")] HostMode::Single(cfg) => { - cfg.run().await?; + cfg.start().await?; } #[cfg(feature = "interop")] HostMode::Super(cfg) => { - cfg.run().await?; + cfg.start().await?; } } diff --git a/bin/host/src/fetcher.rs b/bin/host/src/fetcher.rs deleted file mode 100644 index bd131db11f..0000000000 --- a/bin/host/src/fetcher.rs +++ /dev/null @@ -1,11 +0,0 @@ -//! [Fetcher] trait definition. - -use kona_preimage::{HintRouter, PreimageFetcher}; - -/// The Fetcher trait is used to define the interface for fetching data from the preimage oracle, -/// by [PreimageKey], and routing hints. -/// -/// [PreimageKey]: kona_preimage::PreimageKey -pub trait Fetcher: PreimageFetcher + HintRouter {} - -impl Fetcher for T where T: PreimageFetcher + HintRouter {} diff --git a/bin/host/src/interop/cfg.rs b/bin/host/src/interop/cfg.rs new file mode 100644 index 0000000000..19d7db7e12 --- /dev/null +++ b/bin/host/src/interop/cfg.rs @@ -0,0 +1,280 @@ +//! This module contains all CLI-specific code for the interop entrypoint. + +use super::{InteropFetcher, InteropLocalInputs}; +use crate::{ + cli::{ + cli_styles, + parser::{parse_b256, parse_bytes}, + }, + eth::http_provider, + DiskKeyValueStore, MemoryKeyValueStore, OfflineHostBackend, PreimageServer, + SharedKeyValueStore, SplitKeyValueStore, +}; +use alloy_primitives::{Bytes, B256}; +use alloy_provider::{Provider, RootProvider}; +use alloy_rlp::Decodable; +use anyhow::{anyhow, Result}; +use clap::Parser; +use kona_preimage::{ + BidirectionalChannel, Channel, HintReader, HintWriter, OracleReader, OracleServer, +}; +use kona_proof_interop::PreState; +use kona_providers_alloy::{OnlineBeaconClient, OnlineBlobProvider}; +use kona_std_fpvm::{FileChannel, FileDescriptor}; +use maili_genesis::RollupConfig; +use serde::Serialize; +use std::{collections::HashMap, path::PathBuf, sync::Arc}; +use tokio::{ + sync::RwLock, + task::{self, JoinHandle}, +}; + +/// The interop host application. +#[derive(Default, Parser, Serialize, Clone, Debug)] +#[command(styles = cli_styles())] +pub struct InteropHost { + /// Hash of the L1 head block, marking a static, trusted cutoff point for reading data from the + /// L1 chain. + #[clap(long, value_parser = parse_b256, env)] + pub l1_head: B256, + /// Agreed [PreState] to start from. Can be a [PreState::SuperRoot] or + /// [PreState::TransitionState]. + /// + /// [PreState]: kona_proof_interop::PreState + #[clap(long, visible_alias = "l2-pre-state", value_parser = parse_bytes, env)] + pub agreed_l2_pre_state: Bytes, + /// Claimed L2 post-state to validate. + #[clap(long, visible_alias = "l2-claim", value_parser = parse_b256, env)] + pub claimed_l2_post_state: B256, + /// Claimed L2 timestamp, corresponding to the L2 post-state. + #[clap(long, visible_alias = "l2-timestamp", env)] + pub claimed_l2_timestamp: u64, + /// Addresses of L2 JSON-RPC endpoints to use (eth and debug namespace required). + #[clap( + long, + visible_alias = "l2s", + requires = "l1_node_address", + requires = "l1_beacon_address", + value_delimiter = ',', + env + )] + pub l2_node_addresses: Option>, + /// Address of L1 JSON-RPC endpoint to use (eth and debug namespace required) + #[clap( + long, + visible_alias = "l1", + requires = "l2_node_address", + requires = "l1_beacon_address", + env + )] + pub l1_node_address: Option, + /// Address of the L1 Beacon API endpoint to use. + #[clap( + long, + visible_alias = "beacon", + requires = "l1_node_address", + requires = "l2_node_addresses", + env + )] + pub l1_beacon_address: Option, + /// The Data Directory for preimage data storage. Optional if running in online mode, + /// required if running in offline mode. + #[clap( + long, + visible_alias = "db", + required_unless_present_all = ["l2_node_addresses", "l1_node_address", "l1_beacon_address"], + env + )] + pub data_dir: Option, + /// Run the client program natively. + #[clap(long, conflicts_with = "server", required_unless_present = "server")] + pub native: bool, + /// Run in pre-image server mode without executing any client program. If not provided, the + /// host will run the client program in the host process. + #[clap(long, conflicts_with = "native", required_unless_present = "native")] + pub server: bool, + /// Path to rollup configs. If provided, the host will use this config instead of attempting to + /// look up the configs in the superchain registry. + #[clap(long, alias = "rollup-cfgs", value_delimiter = ',', env)] + pub rollup_config_paths: Option>, +} + +impl InteropHost { + /// Starts the [InteropHost] application. + pub async fn start(self) -> Result<()> { + if self.server { + let hint = FileChannel::new(FileDescriptor::HintRead, FileDescriptor::HintWrite); + let preimage = + FileChannel::new(FileDescriptor::PreimageRead, FileDescriptor::PreimageWrite); + + self.start_server(hint, preimage).await?.await? + } else { + self.start_native().await + } + } + + /// Starts the preimage server, communicating with the client over the provided channels. + async fn start_server(&self, hint: C, preimage: C) -> Result>> + where + C: Channel + Send + Sync + 'static, + { + let kv_store = self.create_key_value_store()?; + + let task_handle = if self.is_offline() { + task::spawn( + PreimageServer::new( + OracleServer::new(preimage), + HintReader::new(hint), + Arc::new(OfflineHostBackend::new(kv_store)), + ) + .start(), + ) + } else { + let providers = self.create_providers().await?; + let backend = InteropFetcher::new( + self.clone(), + kv_store.clone(), + providers.l1_provider, + providers.blob_provider, + providers.l2_providers, + ); + + task::spawn( + PreimageServer::new( + OracleServer::new(preimage), + HintReader::new(hint), + Arc::new(backend), + ) + .start(), + ) + }; + + 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<()> { + let hint = BidirectionalChannel::new()?; + let preimage = BidirectionalChannel::new()?; + + let server_task = self.start_server(hint.host, preimage.host).await?; + let client_task = task::spawn(kona_client::single::run( + OracleReader::new(preimage.client), + HintWriter::new(hint.client), + None, + )); + + let (_, client_result) = tokio::try_join!(server_task, client_task)?; + + // Bubble up the exit status of the client program if execution completes. + std::process::exit(client_result.is_err() as i32) + } + + /// Returns `true` if the host is running in offline mode. + pub const fn is_offline(&self) -> bool { + self.l1_node_address.is_none() && + self.l2_node_addresses.is_none() && + self.l1_beacon_address.is_none() && + self.data_dir.is_some() + } + + /// Returns the active L2 chain ID based on the agreed L2 pre-state. + pub fn active_l2_chain_id(&self) -> Result { + let pre_state = match PreState::decode(&mut self.agreed_l2_pre_state.as_ref()) { + Ok(pre_state) => pre_state, + // If the pre-state is invalid, return a dummy chain ID. + Err(_) => return Ok(0), + }; + + match pre_state { + PreState::SuperRoot(super_root) => Ok(super_root + .output_roots + .first() + .ok_or(anyhow!("output roots are empty"))? + .chain_id), + PreState::TransitionState(transition_state) => Ok(transition_state + .pre_state + .output_roots + .get( + (transition_state.step as usize) + .min(transition_state.pre_state.output_roots.len() - 1), + ) + .ok_or(anyhow!("no output root found"))? + .chain_id), + } + } + + /// 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> { + 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." + ) + })?; + + 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}"))?; + + // Deserialize the config and return it. + let cfg: RollupConfig = serde_json::from_str(&ser_config) + .map_err(|e| anyhow!("Error deserializing RollupConfig: {e}"))?; + + 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 { + let local_kv_store = InteropLocalInputs::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)) + }; + + Ok(kv_store) + } + + /// Creates the providers required for the preimage server backend. + async fn create_providers(&self) -> Result { + let l1_provider = + http_provider(self.l1_node_address.as_ref().ok_or(anyhow!("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"))?, + )) + .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 mut l2_providers = HashMap::default(); + for l2_node_address in l2_node_addresses { + let l2_provider = http_provider(l2_node_address); + let chain_id = l2_provider.get_chain_id().await?; + l2_providers.insert(chain_id, l2_provider); + } + + Ok(InteropProviders { l1_provider, blob_provider, l2_providers }) + } +} +/// The providers required for the single chain host. +#[derive(Debug)] +pub struct InteropProviders { + /// The L1 EL provider. + l1_provider: RootProvider, + /// The L1 beacon node provider. + blob_provider: OnlineBlobProvider, + /// The L2 EL providers, keyed by chain ID. + l2_providers: HashMap, +} diff --git a/bin/host/src/interop/cli.rs b/bin/host/src/interop/cli.rs deleted file mode 100644 index 9b529c9fd8..0000000000 --- a/bin/host/src/interop/cli.rs +++ /dev/null @@ -1,143 +0,0 @@ -//! This module contains all CLI-specific code for the interop entrypoint. - -use super::local_kv::DEFAULT_CHAIN_ID; -use crate::cli::{ - cli_styles, - parser::{parse_b256, parse_bytes}, -}; -use alloy_primitives::{Bytes, B256}; -use alloy_rlp::Decodable; -use anyhow::{anyhow, Result}; -use clap::Parser; -use kona_proof_interop::PreState; -use maili_genesis::RollupConfig; -use serde::Serialize; -use std::{collections::HashMap, path::PathBuf}; - -/// The host binary CLI application arguments. -#[derive(Default, Parser, Serialize, Clone, Debug)] -#[command(styles = cli_styles())] -pub struct InteropHostCli { - /// Hash of the L1 head block, marking a static, trusted cutoff point for reading data from the - /// L1 chain. - #[clap(long, value_parser = parse_b256, env)] - pub l1_head: B256, - /// Agreed [PreState] to start from. Can be a [PreState::SuperRoot] or - /// [PreState::TransitionState]. - /// - /// [PreState]: kona_proof_interop::PreState - #[clap(long, visible_alias = "l2-pre-state", value_parser = parse_bytes, env)] - pub agreed_l2_pre_state: Bytes, - /// Claimed L2 post-state to validate. - #[clap(long, visible_alias = "l2-claim", value_parser = parse_b256, env)] - pub claimed_l2_post_state: B256, - /// Claimed L2 timestamp, corresponding to the L2 post-state. - #[clap(long, visible_alias = "l2-timestamp", env)] - pub claimed_l2_timestamp: u64, - /// Addresses of L2 JSON-RPC endpoints to use (eth and debug namespace required). - #[clap( - long, - visible_alias = "l2s", - requires = "l1_node_address", - requires = "l1_beacon_address", - value_delimiter = ',', - env - )] - pub l2_node_addresses: Option>, - /// Address of L1 JSON-RPC endpoint to use (eth and debug namespace required) - #[clap( - long, - visible_alias = "l1", - requires = "l2_node_address", - requires = "l1_beacon_address", - env - )] - pub l1_node_address: Option, - /// Address of the L1 Beacon API endpoint to use. - #[clap( - long, - visible_alias = "beacon", - requires = "l1_node_address", - requires = "l2_node_addresses", - env - )] - pub l1_beacon_address: Option, - /// The Data Directory for preimage data storage. Optional if running in online mode, - /// required if running in offline mode. - #[clap( - long, - visible_alias = "db", - required_unless_present_all = ["l2_node_addresses", "l1_node_address", "l1_beacon_address"], - env - )] - pub data_dir: Option, - /// Run the client program natively. - #[clap(long, conflicts_with = "server", required_unless_present = "server")] - pub native: bool, - /// Run in pre-image server mode without executing any client program. If not provided, the - /// host will run the client program in the host process. - #[clap(long, conflicts_with = "native", required_unless_present = "native")] - pub server: bool, - /// Path to rollup configs. If provided, the host will use this config instead of attempting to - /// look up the configs in the superchain registry. - #[clap(long, alias = "rollup-cfgs", value_delimiter = ',', env)] - pub rollup_config_paths: Option>, -} - -impl InteropHostCli { - /// Returns `true` if the host is running in offline mode. - pub const fn is_offline(&self) -> bool { - self.l1_node_address.is_none() && - self.l2_node_addresses.is_none() && - self.l1_beacon_address.is_none() - } - - /// Returns the active L2 chain ID based on the agreed L2 pre-state. - pub fn active_l2_chain_id(&self) -> Result { - let pre_state = match PreState::decode(&mut self.agreed_l2_pre_state.as_ref()) { - Ok(pre_state) => pre_state, - // If the pre-state is invalid, return a dummy chain ID. - Err(_) => return Ok(DEFAULT_CHAIN_ID), - }; - - match pre_state { - PreState::SuperRoot(super_root) => Ok(super_root - .output_roots - .first() - .ok_or(anyhow!("output roots are empty"))? - .chain_id), - PreState::TransitionState(transition_state) => Ok(transition_state - .pre_state - .output_roots - .get( - (transition_state.step as usize) - .min(transition_state.pre_state.output_roots.len() - 1), - ) - .ok_or(anyhow!("no output root found"))? - .chain_id), - } - } - - /// 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> { - 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." - ) - })?; - - 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}"))?; - - // Deserialize the config and return it. - let cfg: RollupConfig = serde_json::from_str(&ser_config) - .map_err(|e| anyhow!("Error deserializing RollupConfig: {e}"))?; - - acc.insert(cfg.l2_chain_id, cfg); - Ok(acc) - }) - } -} diff --git a/bin/host/src/interop/fetcher.rs b/bin/host/src/interop/fetcher.rs index 2faf1acac9..3c1f454aa4 100644 --- a/bin/host/src/interop/fetcher.rs +++ b/bin/host/src/interop/fetcher.rs @@ -1,7 +1,7 @@ //! This module contains the [InteropFetcher] struct, which is responsible for fetching //! preimages from a remote source serving the super-chain (interop) proof mode. -use super::InteropHostCli; +use super::InteropHost; use crate::{single::SingleChainFetcher, KeyValueStore, PreimageServer}; use alloy_consensus::{Header, Sealed, TxEnvelope, EMPTY_ROOT_HASH}; use alloy_eips::{ @@ -9,7 +9,7 @@ use alloy_eips::{ eip4844::{IndexedBlobHash, FIELD_ELEMENTS_PER_BLOB}, BlockId, }; -use alloy_primitives::{address, keccak256, map::HashMap, Address, Bytes, B256}; +use alloy_primitives::{address, keccak256, Address, Bytes, B256}; use alloy_provider::{Provider, RootProvider}; use alloy_rlp::{Decodable, Encodable, EMPTY_STRING_CODE}; use alloy_rpc_types::{ @@ -35,7 +35,7 @@ use kona_proof_interop::{Hint, HintType, PreState}; use kona_providers_alloy::{OnlineBeaconClient, OnlineBlobProvider}; use maili_protocol::BlockInfo; use maili_registry::ROLLUP_CONFIGS; -use std::sync::Arc; +use std::{collections::HashMap, sync::Arc}; use tokio::{sync::RwLock, task}; use tracing::{error, trace, warn}; @@ -46,7 +46,7 @@ where KV: KeyValueStore + ?Sized, { /// Configuration - cfg: InteropHostCli, + cfg: InteropHost, /// Key-value store for preimages. kv_store: Arc>, /// L1 chain provider. @@ -67,7 +67,7 @@ where { /// Create a new [InteropFetcher] with the given [KeyValueStore]. pub fn new( - cfg: InteropHostCli, + cfg: InteropHost, kv_store: Arc>, l1_provider: RootProvider, blob_provider: OnlineBlobProvider, @@ -669,13 +669,12 @@ where PreimageServer::new( OracleServer::new(preimage.host), HintReader::new(hint.host), - self.kv_store.clone(), - Some(Arc::new(RwLock::new(fetcher))), + Arc::new(fetcher), ) .start(), ); let client_task = task::spawn({ - let InteropHostCli { l1_head, .. } = self.cfg; + let InteropHost { l1_head, .. } = self.cfg; async move { let oracle = Arc::new(CachingOracle::new( 1024, diff --git a/bin/host/src/interop/local_kv.rs b/bin/host/src/interop/local_kv.rs index b96c90990a..3c0b3465ff 100644 --- a/bin/host/src/interop/local_kv.rs +++ b/bin/host/src/interop/local_kv.rs @@ -1,7 +1,7 @@ //! Contains a concrete implementation of the [KeyValueStore] trait that stores data on disk, -//! using the [InteropHostCli] config. +//! using the [InteropHost] config. -use super::InteropHostCli; +use super::InteropHost; use crate::KeyValueStore; use alloy_primitives::{keccak256, B256}; use anyhow::Result; @@ -11,23 +11,20 @@ use kona_proof_interop::boot::{ L2_ROLLUP_CONFIG_KEY, }; -/// The default chain ID to use if none is provided. -pub(crate) const DEFAULT_CHAIN_ID: u64 = 0xbeef_babe; - -/// A simple, synchronous key-value store that returns data from a [InteropHostCli] config. +/// A simple, synchronous key-value store that returns data from a [InteropHost] config. #[derive(Debug)] -pub struct LocalKeyValueStore { - cfg: InteropHostCli, +pub struct InteropLocalInputs { + cfg: InteropHost, } -impl LocalKeyValueStore { - /// Create a new [LocalKeyValueStore] with the given [InteropHostCli] config. - pub const fn new(cfg: InteropHostCli) -> Self { +impl InteropLocalInputs { + /// Create a new [InteropLocalInputs] with the given [InteropHost] config. + pub const fn new(cfg: InteropHost) -> Self { Self { cfg } } } -impl KeyValueStore for LocalKeyValueStore { +impl KeyValueStore for InteropLocalInputs { fn get(&self, key: B256) -> Option> { let preimage_key = PreimageKey::try_from(*key).ok()?; match preimage_key.key_value() { diff --git a/bin/host/src/interop/mod.rs b/bin/host/src/interop/mod.rs index 0bba2e1ccb..79faae742c 100644 --- a/bin/host/src/interop/mod.rs +++ b/bin/host/src/interop/mod.rs @@ -1,13 +1,10 @@ //! This module contains the super-chain (interop) mode for the host. -mod cli; -pub use cli::InteropHostCli; +mod cfg; +pub use cfg::{InteropHost, InteropProviders}; mod local_kv; -pub use local_kv::LocalKeyValueStore; +pub use local_kv::InteropLocalInputs; mod fetcher; pub use fetcher::InteropFetcher; - -mod orchestrator; -pub use orchestrator::InteropProviders; diff --git a/bin/host/src/interop/orchestrator.rs b/bin/host/src/interop/orchestrator.rs deleted file mode 100644 index eaa2a7e005..0000000000 --- a/bin/host/src/interop/orchestrator.rs +++ /dev/null @@ -1,105 +0,0 @@ -//! [InteropHostCli]'s [HostOrchestrator] + [DetachedHostOrchestrator] implementations. - -use super::{InteropFetcher, InteropHostCli, LocalKeyValueStore}; -use crate::{ - eth::http_provider, DetachedHostOrchestrator, DiskKeyValueStore, Fetcher, HostOrchestrator, - MemoryKeyValueStore, SharedKeyValueStore, SplitKeyValueStore, -}; -use alloy_primitives::map::HashMap; -use alloy_provider::{Provider, RootProvider}; -use anyhow::{anyhow, Result}; -use async_trait::async_trait; -use kona_preimage::{HintWriter, NativeChannel, OracleReader}; -use kona_providers_alloy::{OnlineBeaconClient, OnlineBlobProvider}; -use std::sync::Arc; -use tokio::sync::RwLock; - -/// The providers required for the single chain host. -#[derive(Debug)] -pub struct InteropProviders { - /// The L1 EL provider. - l1_provider: RootProvider, - /// The L1 beacon node provider. - blob_provider: OnlineBlobProvider, - /// The L2 EL providers, keyed by chain ID. - l2_providers: HashMap, -} - -#[async_trait] -impl HostOrchestrator for InteropHostCli { - type Providers = InteropProviders; - - async fn create_providers(&self) -> Result> { - if self.is_offline() { - return Ok(None); - } - - let l1_provider = - http_provider(self.l1_node_address.as_ref().ok_or(anyhow!("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"))?, - )) - .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 mut l2_providers = HashMap::default(); - for l2_node_address in l2_node_addresses { - let l2_provider = http_provider(l2_node_address); - let chain_id = l2_provider.get_chain_id().await?; - - l2_providers.insert(chain_id, l2_provider); - } - - Ok(Some(InteropProviders { l1_provider, blob_provider, l2_providers })) - } - - fn create_fetcher( - &self, - providers: Option, - kv_store: SharedKeyValueStore, - ) -> Option>> { - providers.map(|providers| { - // TODO: Don't pass the whole cfg to the interop fetcher. - Arc::new(RwLock::new(InteropFetcher::new( - self.clone(), - kv_store, - providers.l1_provider, - providers.blob_provider, - providers.l2_providers, - ))) - }) - } - - fn create_key_value_store(&self) -> Result { - 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)) - }; - - Ok(kv_store) - } - - async fn run_client_native( - hint_reader: HintWriter, - oracle_reader: OracleReader, - ) -> Result<()> { - kona_client::interop::run(oracle_reader, hint_reader, None).await.map_err(Into::into) - } -} - -#[async_trait] -impl DetachedHostOrchestrator for InteropHostCli { - fn is_detached(&self) -> bool { - self.server - } -} diff --git a/bin/host/src/lib.rs b/bin/host/src/lib.rs index 69c340cfaa..a259a9c297 100644 --- a/bin/host/src/lib.rs +++ b/bin/host/src/lib.rs @@ -1,24 +1,16 @@ #![doc = include_str!("../README.md")] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] -mod orchestrator; -pub use orchestrator::{DetachedHostOrchestrator, HostOrchestrator}; - -mod fetcher; -pub use fetcher::Fetcher; +mod server; +pub use server::PreimageServer; mod kv; pub use kv::{ DiskKeyValueStore, KeyValueStore, MemoryKeyValueStore, SharedKeyValueStore, SplitKeyValueStore, }; -mod preimage; -pub use preimage::{ - OfflineHintRouter, OfflinePreimageFetcher, OnlineHintRouter, OnlinePreimageFetcher, -}; - -mod server; -pub use server::PreimageServer; +mod offline; +pub use offline::OfflineHostBackend; pub mod cli; diff --git a/bin/host/src/offline.rs b/bin/host/src/offline.rs new file mode 100644 index 0000000000..0f97d2e01b --- /dev/null +++ b/bin/host/src/offline.rs @@ -0,0 +1,50 @@ +//! Contains the implementations of the [HintRouter] and [PreimageFetcher] traits.] + +use crate::kv::KeyValueStore; +use async_trait::async_trait; +use kona_preimage::{ + errors::{PreimageOracleError, PreimageOracleResult}, + HintRouter, PreimageFetcher, PreimageKey, +}; +use std::sync::Arc; +use tokio::sync::RwLock; + +/// A [KeyValueStore]-backed implementation of the [PreimageFetcher] trait. +#[derive(Debug)] +pub struct OfflineHostBackend +where + KV: KeyValueStore + ?Sized, +{ + inner: Arc>, +} + +impl OfflineHostBackend +where + KV: KeyValueStore + ?Sized, +{ + /// Create a new [OfflineHostBackend] from the given [KeyValueStore]. + pub const fn new(kv_store: Arc>) -> Self { + Self { inner: kv_store } + } +} + +#[async_trait] +impl PreimageFetcher for OfflineHostBackend +where + KV: KeyValueStore + Send + Sync + ?Sized, +{ + async fn get_preimage(&self, key: PreimageKey) -> PreimageOracleResult> { + let kv_store = self.inner.read().await; + kv_store.get(key.into()).ok_or(PreimageOracleError::KeyNotFound) + } +} + +#[async_trait] +impl HintRouter for OfflineHostBackend +where + KV: KeyValueStore + Send + Sync + ?Sized, +{ + async fn route_hint(&self, _hint: String) -> PreimageOracleResult<()> { + Ok(()) + } +} diff --git a/bin/host/src/orchestrator.rs b/bin/host/src/orchestrator.rs deleted file mode 100644 index 0cf478db4c..0000000000 --- a/bin/host/src/orchestrator.rs +++ /dev/null @@ -1,132 +0,0 @@ -//! Contains the [HostOrchestrator] trait, which defines entry points for the host to run a given -//! module. - -use crate::{Fetcher, PreimageServer, SharedKeyValueStore}; -use anyhow::Result; -use async_trait::async_trait; -use kona_preimage::{ - BidirectionalChannel, HintReader, HintWriter, NativeChannel, OracleReader, OracleServer, -}; -use kona_std_fpvm::{FileChannel, FileDescriptor}; -use std::sync::Arc; -use tokio::{sync::RwLock, task}; - -/// The host<->client communication channels. The client channels are optional, as the client may -/// not be running in the same process as the host. -#[derive(Debug)] -struct HostComms { - /// The host<->client hint channel. - pub hint: BidirectionalChannel, - /// The host<->client preimage channel. - pub preimage: BidirectionalChannel, -} - -/// The host->client communication channels when running in detached mode. The client channels are -/// held in a separate process. -#[derive(Debug)] -struct DetachedHostComms { - /// The host->client hint channel. - pub hint: FileChannel, - /// The host->client preimage channel. - pub preimage: FileChannel, -} - -/// The orchestrator is responsible for starting the host and client program, and managing the -/// communication between them. It is the entry point for the host to run a given module. -/// -/// This trait is specific to running both the host and client program in-process. For detached -/// mode, see [DetachedHostOrchestrator]. -#[async_trait] -pub trait HostOrchestrator { - /// A collection of the providers that the host can use to reference remote resources. - type Providers; - - /// Instantiates the providers for the host's fetcher. - async fn create_providers(&self) -> Result>; - - /// Constructs the [KeyValueStore] for the host. - /// - /// [KeyValueStore]: crate::KeyValueStore - fn create_key_value_store(&self) -> Result; - - /// Creates a [Fetcher] for the host program's preimage server. - fn create_fetcher( - &self, - providers: Option, - kv_store: SharedKeyValueStore, - ) -> Option>>; - - /// Runs the client program natively and returns the exit code. - async fn run_client_native( - hint_reader: HintWriter, - oracle_reader: OracleReader, - ) -> Result<()>; - - /// Starts the host and client program in-process. - async fn start(&self) -> Result<()> { - let comms = HostComms { - hint: BidirectionalChannel::new()?, - preimage: BidirectionalChannel::new()?, - }; - let kv_store = self.create_key_value_store()?; - let providers = self.create_providers().await?; - let fetcher = self.create_fetcher(providers, kv_store.clone()); - - let server_task = task::spawn( - PreimageServer::new( - OracleServer::new(comms.preimage.host), - HintReader::new(comms.hint.host), - kv_store, - fetcher, - ) - .start(), - ); - let client_task = task::spawn(Self::run_client_native( - HintWriter::new(comms.hint.client), - OracleReader::new(comms.preimage.client), - )); - - let (_, client_result) = tokio::try_join!(server_task, client_task)?; - - // Bubble up the exit status of the client program. - std::process::exit(client_result.is_err() as i32); - } -} - -/// The orchestrator for starting the host in detached mode, with the client program running in a -/// separate process. -#[async_trait] -pub trait DetachedHostOrchestrator: HostOrchestrator { - /// Returns whether the host is running in detached mode. - fn is_detached(&self) -> bool; - - /// Starts the host in detached mode, with the client program running in a separate process. - async fn run_detached(&self) -> Result<()> { - let comms = DetachedHostComms { - hint: FileChannel::new(FileDescriptor::HintRead, FileDescriptor::HintWrite), - preimage: FileChannel::new(FileDescriptor::PreimageRead, FileDescriptor::PreimageWrite), - }; - let kv_store = self.create_key_value_store()?; - let providers = self.create_providers().await?; - let fetcher = self.create_fetcher(providers, kv_store.clone()); - - PreimageServer::new( - OracleServer::new(comms.preimage), - HintReader::new(comms.hint), - kv_store, - fetcher, - ) - .start() - .await - } - - /// Override for [HostOrchestrator::start] that starts the host in detached mode, - /// if [DetachedHostOrchestrator::is_detached] returns `true`. - async fn run(&self) -> Result<()> { - if self.is_detached() { - self.run_detached().await - } else { - HostOrchestrator::start(self).await - } - } -} diff --git a/bin/host/src/preimage.rs b/bin/host/src/preimage.rs deleted file mode 100644 index 7da932326e..0000000000 --- a/bin/host/src/preimage.rs +++ /dev/null @@ -1,111 +0,0 @@ -//! Contains the implementations of the [HintRouter] and [PreimageFetcher] traits.] - -use crate::{fetcher::Fetcher, kv::KeyValueStore}; -use async_trait::async_trait; -use kona_preimage::{ - errors::{PreimageOracleError, PreimageOracleResult}, - HintRouter, PreimageFetcher, PreimageKey, -}; -use std::sync::Arc; -use tokio::sync::RwLock; - -/// A [Fetcher]-backed implementation of the [PreimageFetcher] trait. -#[derive(Debug)] -pub struct OnlinePreimageFetcher -where - F: Fetcher, -{ - inner: Arc>, -} - -#[async_trait] -impl PreimageFetcher for OnlinePreimageFetcher -where - F: Fetcher + Send + Sync, -{ - async fn get_preimage(&self, key: PreimageKey) -> PreimageOracleResult> { - let fetcher = self.inner.read().await; - fetcher.get_preimage(key).await.map_err(|e| PreimageOracleError::Other(e.to_string())) - } -} - -impl OnlinePreimageFetcher -where - F: Fetcher, -{ - /// Create a new [OnlinePreimageFetcher] from the given [Fetcher]. - pub const fn new(fetcher: Arc>) -> Self { - Self { inner: fetcher } - } -} - -/// A [KeyValueStore]-backed implementation of the [PreimageFetcher] trait. -#[derive(Debug)] -pub struct OfflinePreimageFetcher -where - KV: KeyValueStore + ?Sized, -{ - inner: Arc>, -} - -#[async_trait] -impl PreimageFetcher for OfflinePreimageFetcher -where - KV: KeyValueStore + Send + Sync + ?Sized, -{ - async fn get_preimage(&self, key: PreimageKey) -> PreimageOracleResult> { - let kv_store = self.inner.read().await; - kv_store.get(key.into()).ok_or(PreimageOracleError::KeyNotFound) - } -} - -impl OfflinePreimageFetcher -where - KV: KeyValueStore + ?Sized, -{ - /// Create a new [OfflinePreimageFetcher] from the given [KeyValueStore]. - pub const fn new(kv_store: Arc>) -> Self { - Self { inner: kv_store } - } -} - -/// A [Fetcher]-backed implementation of the [HintRouter] trait. -#[derive(Debug)] -pub struct OnlineHintRouter -where - F: Fetcher, -{ - inner: Arc>, -} - -#[async_trait] -impl HintRouter for OnlineHintRouter -where - F: Fetcher + Send + Sync, -{ - async fn route_hint(&self, hint: String) -> PreimageOracleResult<()> { - let fetcher = self.inner.write().await; - fetcher.route_hint(hint).await - } -} - -impl OnlineHintRouter -where - F: Fetcher, -{ - /// Create a new [OnlineHintRouter] from the given [Fetcher]. - pub const fn new(fetcher: Arc>) -> Self { - Self { inner: fetcher } - } -} - -/// An [OfflineHintRouter] is a [HintRouter] that does nothing. -#[derive(Debug)] -pub struct OfflineHintRouter; - -#[async_trait] -impl HintRouter for OfflineHintRouter { - async fn route_hint(&self, _hint: String) -> PreimageOracleResult<()> { - Ok(()) - } -} diff --git a/bin/host/src/server.rs b/bin/host/src/server.rs index 9958c7a063..f1601bbd9b 100644 --- a/bin/host/src/server.rs +++ b/bin/host/src/server.rs @@ -1,73 +1,44 @@ //! This module contains the [PreimageServer] struct and its implementation. -use crate::{ - fetcher::Fetcher, - kv::KeyValueStore, - preimage::{ - OfflineHintRouter, OfflinePreimageFetcher, OnlineHintRouter, OnlinePreimageFetcher, - }, -}; use anyhow::{anyhow, Result}; use kona_preimage::{ - errors::PreimageOracleError, HintReaderServer, HintRouter, PreimageFetcher, - PreimageOracleServer, + errors::PreimageOracleError, HintReaderServer, PreimageOracleServer, PreimageServerBackend, }; use std::sync::Arc; -use tokio::{spawn, sync::RwLock}; +use tokio::spawn; use tracing::{error, info}; /// The [PreimageServer] is responsible for waiting for incoming preimage requests and /// serving them to the client. #[derive(Debug)] -pub struct PreimageServer -where - P: PreimageOracleServer, - H: HintReaderServer, - KV: KeyValueStore + ?Sized, - F: Fetcher, -{ +pub struct PreimageServer { /// The oracle server. oracle_server: P, /// The hint router. hint_reader: H, - /// Key-value store for preimages. - kv_store: Arc>, - /// The fetcher for fetching preimages from a remote source. If [None], the server will only - /// serve preimages that are already in the key-value store. - fetcher: Option>>, + /// [PreimageServerBackend] that routes hints and retrieves preimages. + backend: Arc, } -impl PreimageServer +impl PreimageServer where P: PreimageOracleServer + Send + Sync + 'static, H: HintReaderServer + Send + Sync + 'static, - KV: KeyValueStore + Send + Sync + ?Sized + 'static, - F: Fetcher + Send + Sync + 'static, + B: PreimageServerBackend + Send + Sync + 'static, { /// Create a new [PreimageServer] with the given [PreimageOracleServer], - /// [HintReaderServer], and [KeyValueStore]. Holds onto the file descriptors for the pipes - /// that are created, so that the pipes are not closed until the server is dropped. - pub const fn new( - oracle_server: P, - hint_reader: H, - kv_store: Arc>, - fetcher: Option>>, - ) -> Self { - Self { oracle_server, hint_reader, kv_store, fetcher } + /// [HintReaderServer], and [PreimageServerBackend]. + pub const fn new(oracle_server: P, hint_reader: H, backend: Arc) -> Self { + Self { oracle_server, hint_reader, backend } } /// Starts the [PreimageServer] and waits for incoming requests. pub async fn start(self) -> Result<()> { // Create the futures for the oracle server and hint router. - let server = spawn(Self::start_oracle_server( - self.kv_store.clone(), - self.fetcher.clone(), - self.oracle_server, - )); - let hint_router = spawn(Self::start_hint_router(self.hint_reader, self.fetcher)); + 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())); - // Spawn tasks for the futures and wait for them to complete. If one of the tasks closes - // before the other, cancel the other task. + // 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))?, @@ -76,67 +47,37 @@ where /// Starts the oracle server, which waits for incoming preimage requests and serves them to the /// client. - async fn start_oracle_server( - kv_store: Arc>, - fetcher: Option>>, - oracle_server: P, - ) -> Result<()> { - #[inline(always)] - async fn do_loop(fetcher: &F, server: &P) -> Result<()> - where - F: PreimageFetcher + Send + Sync, - P: PreimageOracleServer, - { - loop { - // Serve the next preimage request. This `await` will yield to the runtime - // if no progress can be made. - match server.next_preimage_request(fetcher).await { - Ok(_) => continue, - Err(PreimageOracleError::IOError(_)) => return Ok(()), - Err(e) => { - error!("Failed to serve preimage request: {e}"); - return Err(anyhow!("Failed to serve preimage request: {e}")); - } + async fn start_oracle_server(oracle_server: P, backend: Arc) -> Result<()> { + info!(target: "host-server", "Starting oracle server"); + loop { + // Serve the next preimage request. This `await` will yield to the runtime + // if no progress can be made. + match oracle_server.next_preimage_request(backend.as_ref()).await { + Ok(_) => continue, + Err(PreimageOracleError::IOError(_)) => return Ok(()), + Err(e) => { + error!("Failed to serve preimage request: {e}"); + return Err(anyhow!("Failed to serve preimage request: {e}")); } } } - - info!(target: "host-server", "Starting oracle server"); - if let Some(fetcher) = fetcher.as_ref() { - do_loop(&OnlinePreimageFetcher::new(Arc::clone(fetcher)), &oracle_server).await - } else { - do_loop(&OfflinePreimageFetcher::new(Arc::clone(&kv_store)), &oracle_server).await - } } /// Starts the hint router, which waits for incoming hints and routes them to the appropriate /// handler. - async fn start_hint_router(hint_reader: H, fetcher: Option>>) -> Result<()> { - #[inline(always)] - async fn do_loop(router: &R, server: &H) -> Result<()> - where - R: HintRouter + Send + Sync, - H: HintReaderServer, - { - loop { - // Route the next hint. This `await` will yield to the runtime if no progress can be - // made. - match server.next_hint(router).await { - Ok(_) => continue, - Err(PreimageOracleError::IOError(_)) => return Ok(()), - Err(e) => { - error!("Failed to serve route hint: {e}"); - return Err(anyhow!("Failed to route hint: {e}")); - } + async fn start_hint_router(hint_reader: H, backend: Arc) -> Result<()> { + info!(target: "host-server", "Starting hint router"); + loop { + // Route the next hint. This `await` will yield to the runtime if no progress can be + // made. + match hint_reader.next_hint(backend.as_ref()).await { + Ok(_) => continue, + Err(PreimageOracleError::IOError(_)) => return Ok(()), + Err(e) => { + error!("Failed to serve route hint: {e}"); + return Err(anyhow!("Failed to route hint: {e}")); } } } - - info!(target: "host-server", "Starting hint router"); - if let Some(fetcher) = fetcher.as_ref() { - do_loop(&OnlineHintRouter::new(Arc::clone(fetcher)), &hint_reader).await - } else { - do_loop(&OfflineHintRouter, &hint_reader).await - } } } diff --git a/bin/host/src/single/cli.rs b/bin/host/src/single/cfg.rs similarity index 56% rename from bin/host/src/single/cli.rs rename to bin/host/src/single/cfg.rs index 313c625fa5..75f0c19bc3 100644 --- a/bin/host/src/single/cli.rs +++ b/bin/host/src/single/cfg.rs @@ -1,17 +1,33 @@ //! This module contains all CLI-specific code for the single chain entrypoint. -use crate::cli::{cli_styles, parser::parse_b256}; +use super::{SingleChainFetcher, SingleChainLocalInputs}; +use crate::{ + cli::{cli_styles, parser::parse_b256}, + eth::http_provider, + DiskKeyValueStore, MemoryKeyValueStore, OfflineHostBackend, PreimageServer, + SharedKeyValueStore, SplitKeyValueStore, +}; use alloy_primitives::B256; +use alloy_provider::RootProvider; use anyhow::{anyhow, Result}; use clap::Parser; +use kona_preimage::{ + BidirectionalChannel, Channel, HintReader, HintWriter, OracleReader, OracleServer, +}; +use kona_providers_alloy::{OnlineBeaconClient, OnlineBlobProvider}; +use kona_std_fpvm::{FileChannel, FileDescriptor}; use maili_genesis::RollupConfig; use serde::Serialize; -use std::path::PathBuf; +use std::{path::PathBuf, sync::Arc}; +use tokio::{ + sync::RwLock, + task::{self, JoinHandle}, +}; /// The host binary CLI application arguments. #[derive(Default, Parser, Serialize, Clone, Debug)] #[command(styles = cli_styles())] -pub struct SingleChainHostCli { +pub struct SingleChainHost { /// Hash of the L1 head block. Derivation stops after this block is processed. #[clap(long, value_parser = parse_b256, env)] pub l1_head: B256, @@ -91,12 +107,84 @@ pub struct SingleChainHostCli { pub rollup_config_path: Option, } -impl SingleChainHostCli { +impl SingleChainHost { + /// Starts the [SingleChainHost] application. + pub async fn start(self) -> Result<()> { + if self.server { + let hint = FileChannel::new(FileDescriptor::HintRead, FileDescriptor::HintWrite); + let preimage = + FileChannel::new(FileDescriptor::PreimageRead, FileDescriptor::PreimageWrite); + + self.start_server(hint, preimage).await?.await? + } else { + self.start_native().await + } + } + + /// Starts the preimage server, communicating with the client over the provided channels. + async fn start_server(&self, hint: C, preimage: C) -> Result>> + where + C: Channel + Send + Sync + 'static, + { + let kv_store = self.create_key_value_store()?; + + let task_handle = if self.is_offline() { + task::spawn( + PreimageServer::new( + OracleServer::new(preimage), + HintReader::new(hint), + Arc::new(OfflineHostBackend::new(kv_store)), + ) + .start(), + ) + } else { + let providers = self.create_providers().await?; + let backend = SingleChainFetcher::new( + kv_store.clone(), + providers.l1_provider, + providers.blob_provider, + providers.l2_provider, + self.agreed_l2_head_hash, + ); + + task::spawn( + PreimageServer::new( + OracleServer::new(preimage), + HintReader::new(hint), + Arc::new(backend), + ) + .start(), + ) + }; + + 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<()> { + let hint = BidirectionalChannel::new()?; + let preimage = BidirectionalChannel::new()?; + + let server_task = self.start_server(hint.host, preimage.host).await?; + let client_task = task::spawn(kona_client::single::run( + OracleReader::new(preimage.client), + HintWriter::new(hint.client), + None, + )); + + let (_, client_result) = tokio::try_join!(server_task, client_task)?; + + // Bubble up the exit status of the client program if execution completes. + std::process::exit(client_result.is_err() as i32) + } + /// Returns `true` if the host is running in offline mode. pub const fn is_offline(&self) -> bool { self.l1_node_address.is_none() && self.l2_node_address.is_none() && - self.l1_beacon_address.is_none() + self.l1_beacon_address.is_none() && + self.data_dir.is_some() } /// Reads the [RollupConfig] from the file system and returns it as a string. @@ -115,11 +203,54 @@ impl SingleChainHostCli { serde_json::from_str(&ser_config) .map_err(|e| anyhow!("Error deserializing RollupConfig: {e}")) } + + /// Creates the key-value store for the host backend. + fn create_key_value_store(&self) -> Result { + let local_kv_store = SingleChainLocalInputs::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)) + }; + + Ok(kv_store) + } + + /// Creates the providers required for the host backend. + async fn create_providers(&self) -> Result { + let l1_provider = + http_provider(self.l1_node_address.as_ref().ok_or(anyhow!("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"))?, + )) + .await; + let l2_provider = http_provider( + self.l2_node_address.as_ref().ok_or(anyhow!("L2 node address must be set"))?, + ); + + Ok(SingleChainProviders { l1_provider, blob_provider, l2_provider }) + } +} + +/// The providers required for the single chain host. +#[derive(Debug, Clone)] +pub struct SingleChainProviders { + /// The L1 EL provider. + l1_provider: RootProvider, + /// The L1 beacon node provider. + blob_provider: OnlineBlobProvider, + /// The L2 EL provider. + l2_provider: RootProvider, } #[cfg(test)] mod test { - use crate::single::SingleChainHostCli; + use crate::single::SingleChainHost; use alloy_primitives::B256; use clap::Parser; @@ -177,7 +308,7 @@ mod test { for (args_ext, valid) in cases.into_iter() { let args = default_flags.iter().chain(args_ext.iter()).cloned().collect::>(); - let parsed = SingleChainHostCli::try_parse_from(args); + let parsed = SingleChainHost::try_parse_from(args); assert_eq!(parsed.is_ok(), valid); } } diff --git a/bin/host/src/single/local_kv.rs b/bin/host/src/single/local_kv.rs index 2ac660b8ac..1f565b3f16 100644 --- a/bin/host/src/single/local_kv.rs +++ b/bin/host/src/single/local_kv.rs @@ -1,7 +1,7 @@ //! Contains a concrete implementation of the [KeyValueStore] trait that stores data on disk, -//! using the [SingleChainHostCli] config. +//! using the [SingleChainHost] config. -use super::SingleChainHostCli; +use super::SingleChainHost; use crate::KeyValueStore; use alloy_primitives::B256; use anyhow::Result; @@ -11,23 +11,20 @@ use kona_proof::boot::{ L2_ROLLUP_CONFIG_KEY, }; -/// The default chain ID to use if none is provided. -const DEFAULT_CHAIN_ID: u64 = 0xbeefbabe; - -/// A simple, synchronous key-value store that returns data from a [SingleChainHostCli] config. +/// A simple, synchronous key-value store that returns data from a [SingleChainHost] config. #[derive(Debug)] -pub struct LocalKeyValueStore { - cfg: SingleChainHostCli, +pub struct SingleChainLocalInputs { + cfg: SingleChainHost, } -impl LocalKeyValueStore { - /// Create a new [LocalKeyValueStore] with the given [SingleChainHostCli] config. - pub const fn new(cfg: SingleChainHostCli) -> Self { +impl SingleChainLocalInputs { + /// Create a new [SingleChainLocalInputs] with the given [SingleChainHost] config. + pub const fn new(cfg: SingleChainHost) -> Self { Self { cfg } } } -impl KeyValueStore for LocalKeyValueStore { +impl KeyValueStore for SingleChainLocalInputs { fn get(&self, key: B256) -> Option> { let preimage_key = PreimageKey::try_from(*key).ok()?; match preimage_key.key_value() { @@ -38,7 +35,7 @@ impl KeyValueStore for LocalKeyValueStore { Some(self.cfg.claimed_l2_block_number.to_be_bytes().to_vec()) } L2_CHAIN_ID_KEY => { - Some(self.cfg.l2_chain_id.unwrap_or(DEFAULT_CHAIN_ID).to_be_bytes().to_vec()) + Some(self.cfg.l2_chain_id.unwrap_or_default().to_be_bytes().to_vec()) } L2_ROLLUP_CONFIG_KEY => { let rollup_config = self.cfg.read_rollup_config().ok()?; diff --git a/bin/host/src/single/mod.rs b/bin/host/src/single/mod.rs index b363dd4132..a58b0665b1 100644 --- a/bin/host/src/single/mod.rs +++ b/bin/host/src/single/mod.rs @@ -1,13 +1,10 @@ //! This module contains the single-chain mode for the host. -mod cli; -pub use cli::SingleChainHostCli; - -mod orchestrator; -pub use orchestrator::SingleChainProviders; +mod cfg; +pub use cfg::{SingleChainHost, SingleChainProviders}; mod local_kv; -pub use local_kv::LocalKeyValueStore; +pub use local_kv::SingleChainLocalInputs; mod fetcher; pub use fetcher::SingleChainFetcher; diff --git a/bin/host/src/single/orchestrator.rs b/bin/host/src/single/orchestrator.rs deleted file mode 100644 index 0673ea4117..0000000000 --- a/bin/host/src/single/orchestrator.rs +++ /dev/null @@ -1,94 +0,0 @@ -//! [SingleChainHostCli]'s [HostOrchestrator] + [DetachedHostOrchestrator] implementations. - -use super::{LocalKeyValueStore, SingleChainFetcher, SingleChainHostCli}; -use crate::{ - eth::http_provider, DetachedHostOrchestrator, DiskKeyValueStore, Fetcher, HostOrchestrator, - MemoryKeyValueStore, SharedKeyValueStore, SplitKeyValueStore, -}; -use alloy_provider::RootProvider; -use anyhow::{anyhow, Result}; -use async_trait::async_trait; -use kona_preimage::{HintWriter, NativeChannel, OracleReader}; -use kona_providers_alloy::{OnlineBeaconClient, OnlineBlobProvider}; -use std::sync::Arc; -use tokio::sync::RwLock; - -/// The providers required for the single chain host. -#[derive(Debug)] -pub struct SingleChainProviders { - /// The L1 EL provider. - l1_provider: RootProvider, - /// The L1 beacon node provider. - blob_provider: OnlineBlobProvider, - /// The L2 EL provider. - l2_provider: RootProvider, -} - -#[async_trait] -impl HostOrchestrator for SingleChainHostCli { - type Providers = SingleChainProviders; - - async fn create_providers(&self) -> Result> { - if self.is_offline() { - return Ok(None); - } - - let l1_provider = - http_provider(self.l1_node_address.as_ref().ok_or(anyhow!("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"))?, - )) - .await; - let l2_provider = http_provider( - self.l2_node_address.as_ref().ok_or(anyhow!("L2 node address must be set"))?, - ); - - Ok(Some(SingleChainProviders { l1_provider, blob_provider, l2_provider })) - } - - fn create_fetcher( - &self, - providers: Option, - kv_store: SharedKeyValueStore, - ) -> Option>> { - providers.map(|providers| { - Arc::new(RwLock::new(SingleChainFetcher::new( - kv_store, - providers.l1_provider, - providers.blob_provider, - providers.l2_provider, - self.agreed_l2_head_hash, - ))) - }) - } - - fn create_key_value_store(&self) -> Result { - 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)) - }; - - Ok(kv_store) - } - - async fn run_client_native( - hint_reader: HintWriter, - oracle_reader: OracleReader, - ) -> Result<()> { - kona_client::single::run(oracle_reader, hint_reader, None).await.map_err(Into::into) - } -} - -#[async_trait] -impl DetachedHostOrchestrator for SingleChainHostCli { - fn is_detached(&self) -> bool { - self.server - } -} diff --git a/crates/proof-sdk/preimage/src/lib.rs b/crates/proof-sdk/preimage/src/lib.rs index 84e52c642a..25ec7a6283 100644 --- a/crates/proof-sdk/preimage/src/lib.rs +++ b/crates/proof-sdk/preimage/src/lib.rs @@ -26,7 +26,7 @@ pub use hint::{HintReader, HintWriter}; mod traits; pub use traits::{ Channel, CommsClient, HintReaderServer, HintRouter, HintWriterClient, PreimageFetcher, - PreimageOracleClient, PreimageOracleServer, + PreimageOracleClient, PreimageOracleServer, PreimageServerBackend, }; #[cfg(any(test, feature = "std"))] diff --git a/crates/proof-sdk/preimage/src/traits.rs b/crates/proof-sdk/preimage/src/traits.rs index 5614b21259..5740210979 100644 --- a/crates/proof-sdk/preimage/src/traits.rs +++ b/crates/proof-sdk/preimage/src/traits.rs @@ -102,6 +102,13 @@ pub trait PreimageFetcher { async fn get_preimage(&self, key: PreimageKey) -> PreimageOracleResult>; } +/// A [PreimageServerBackend] is a trait that combines the [PreimageFetcher] and [HintRouter] +/// traits. +pub trait PreimageServerBackend: PreimageFetcher + HintRouter {} + +// Implement the super trait for any type that satisfies the bounds +impl PreimageServerBackend for T {} + /// A [Channel] is a high-level interface to read and write data to a counterparty. #[async_trait] pub trait Channel {