From bf91cfe731abfe3c858b8fba565725d0e81c867b Mon Sep 17 00:00:00 2001 From: clabby Date: Tue, 14 Jan 2025 17:11:52 -0500 Subject: [PATCH] feat(client): Interop binary (#903) --- Cargo.lock | 1 + bin/client/Cargo.toml | 5 + bin/client/src/interop.rs | 249 ++++++++++++++++++ bin/client/src/kona.rs | 2 +- bin/client/src/kona_interop.rs | 47 ++++ bin/client/src/lib.rs | 172 +----------- bin/client/src/single.rs | 176 +++++++++++++ bin/host/src/lib.rs | 2 +- crates/driver/src/core.rs | 3 +- .../proof-sdk/proof/src/l1/chain_provider.rs | 12 +- .../proof-sdk/proof/src/l2/chain_provider.rs | 37 +-- crates/proof-sdk/proof/src/sync.rs | 15 +- 12 files changed, 507 insertions(+), 214 deletions(-) create mode 100644 bin/client/src/interop.rs create mode 100644 bin/client/src/kona_interop.rs create mode 100644 bin/client/src/single.rs diff --git a/Cargo.lock b/Cargo.lock index 438c6f74f..de4124469 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2355,6 +2355,7 @@ dependencies = [ "kona-mpt", "kona-preimage", "kona-proof", + "kona-proof-interop", "kona-std-fpvm", "kona-std-fpvm-proc", "lru", diff --git a/bin/client/Cargo.toml b/bin/client/Cargo.toml index abcd81dcf..6b76b72d6 100644 --- a/bin/client/Cargo.toml +++ b/bin/client/Cargo.toml @@ -16,6 +16,7 @@ kona-driver.workspace = true kona-preimage.workspace = true kona-executor.workspace = true kona-proof.workspace = true +kona-proof-interop.workspace = true kona-std-fpvm.workspace = true kona-std-fpvm-proc.workspace = true @@ -54,3 +55,7 @@ client-tracing = ["kona-std-fpvm/tracing"] [[bin]] name = "kona" path = "src/kona.rs" + +[[bin]] +name = "kona-int" +path = "src/kona_interop.rs" diff --git a/bin/client/src/interop.rs b/bin/client/src/interop.rs new file mode 100644 index 000000000..fbac5d12d --- /dev/null +++ b/bin/client/src/interop.rs @@ -0,0 +1,249 @@ +//! Multi-chain, interoperable fault proof program entrypoint. + +use alloc::{string::ToString, sync::Arc}; +use alloy_consensus::Sealed; +use alloy_primitives::{Bytes, B256}; +use alloy_rlp::Decodable; +use core::fmt::Debug; +use kona_driver::{Driver, DriverError}; +use kona_executor::{ExecutorError, KonaHandleRegister, TrieDBProvider}; +use kona_preimage::{ + errors::PreimageOracleError, CommsClient, HintWriterClient, PreimageKey, PreimageKeyType, + PreimageOracleClient, +}; +use kona_proof::{ + errors::OracleProviderError, + executor::KonaExecutor, + l1::{OracleBlobProvider, OracleL1ChainProvider, OraclePipeline}, + l2::OracleL2ChainProvider, + sync::new_pipeline_cursor, + CachingOracle, +}; +use kona_proof_interop::{ + pre_state::{OptimisticBlock, PreState, TransitionState}, + BootInfo, HintType, +}; +use thiserror::Error; +use tracing::{error, info}; + +/// An error that can occur when running the fault proof program. +#[derive(Error, Debug)] +pub enum FaultProofProgramError { + /// The claim is invalid. + #[error("Invalid claim. Expected {0}, actual {1}")] + InvalidClaim(B256, B256), + /// An error occurred in the Oracle provider. + #[error(transparent)] + OracleProviderError(#[from] OracleProviderError), + /// An error occurred in the driver. + #[error(transparent)] + Driver(#[from] DriverError), + /// An error occurred during RLP decoding. + #[error("RLP decoding error: {0}")] + RLPDecodingError(alloy_rlp::Error), +} + +/// Executes the fault proof program with the given [PreimageOracleClient] and [HintWriterClient]. +#[inline] +pub async fn run( + oracle_client: P, + hint_client: H, + handle_register: Option< + KonaHandleRegister< + OracleL2ChainProvider>, + OracleL2ChainProvider>, + >, + >, +) -> Result<(), FaultProofProgramError> +where + P: PreimageOracleClient + Send + Sync + Debug + Clone, + H: HintWriterClient + Send + Sync + Debug + Clone, +{ + const ORACLE_LRU_SIZE: usize = 1024; + + //////////////////////////////////////////////////////////////// + // PROLOGUE // + //////////////////////////////////////////////////////////////// + + let oracle = Arc::new(CachingOracle::new(ORACLE_LRU_SIZE, oracle_client, hint_client)); + let boot = match BootInfo::load(oracle.as_ref()).await { + Ok(boot) => Arc::new(boot), + Err(e) => { + error!(target: "client", "Failed to load boot info: {:?}", e); + return Err(e.into()); + } + }; + + // Load in the pre-state from the preimage oracle and fetch the L2 safe head block hash. + let pre = + PreState::decode(&mut read_raw_pre_state(oracle.as_ref(), boot.as_ref()).await?.as_ref()) + .map_err(FaultProofProgramError::RLPDecodingError)?; + let safe_head_hash = fetch_l2_safe_head_hash(oracle.as_ref(), &pre).await?; + + // Instantiate the L1 EL + CL provider and the L2 EL provider. + let mut l1_provider = OracleL1ChainProvider::new(boot.l1_head, oracle.clone()); + let mut l2_provider = + OracleL2ChainProvider::new(safe_head_hash, boot.rollup_config.clone(), oracle.clone()); + let beacon = OracleBlobProvider::new(oracle.clone()); + + // Fetch the safe head's block header. + let safe_head = l2_provider + .header_by_hash(safe_head_hash) + .map(|header| Sealed::new_unchecked(header, safe_head_hash))?; + + // Translate the claimed timestamp to an L2 block number. + let claimed_l2_block_number = (boot.claimed_l2_timestamp - boot.rollup_config.genesis.l2_time) / + boot.rollup_config.block_time; + + // If the claimed L2 block number is less than the safe head of the L2 chain, the claim is + // invalid. + if claimed_l2_block_number < safe_head.number { + error!( + target: "client", + "Claimed L2 block number {claimed} is less than the safe head {safe}", + claimed = claimed_l2_block_number, + safe = safe_head.number + ); + return Err(FaultProofProgramError::InvalidClaim( + boot.agreed_pre_state, + boot.claimed_post_state, + )); + } + + // In the case where the agreed upon L2 pre-state is the same as the claimed L2 post-state, + // trace extension is detected and we can skip the derivation and execution steps. + if boot.agreed_pre_state == boot.claimed_post_state { + info!( + target: "client", + "Trace extension detected. State transition is already agreed upon.", + ); + return Ok(()); + } + + //////////////////////////////////////////////////////////////// + // DERIVATION & EXECUTION // + //////////////////////////////////////////////////////////////// + + // Create a new derivation driver with the given boot information and oracle. + let cursor = + new_pipeline_cursor(&boot.rollup_config, safe_head, &mut l1_provider, &mut l2_provider) + .await?; + let cfg = Arc::new(boot.rollup_config.clone()); + let pipeline = OraclePipeline::new( + cfg.clone(), + cursor.clone(), + oracle.clone(), + beacon, + l1_provider.clone(), + l2_provider.clone(), + ); + let executor = KonaExecutor::new(&cfg, l2_provider.clone(), l2_provider, handle_register, None); + let mut driver = Driver::new(cursor, executor, pipeline); + + // Run the derivation pipeline until we are able to produce the output root of the claimed + // L2 block. + let (number, block_hash, output_root) = + driver.advance_to_target(&boot.rollup_config, Some(claimed_l2_block_number)).await?; + + //////////////////////////////////////////////////////////////// + // EPILOGUE // + //////////////////////////////////////////////////////////////// + + let optimistic_block = OptimisticBlock::new(block_hash, output_root); + let transition_state = match pre { + PreState::SuperRoot(super_root) => { + TransitionState::new(super_root, alloc::vec![optimistic_block], 1) + } + PreState::TransitionState(mut ts) => { + ts.pending_progress.push(optimistic_block); + ts.step += 1; + ts + } + }; + + if transition_state.hash() != boot.claimed_post_state { + error!( + target: "client", + "Failed to validate L2 block #{number} with output root {output_root}", + number = number, + output_root = output_root + ); + return Err(FaultProofProgramError::InvalidClaim( + transition_state.hash(), + boot.claimed_post_state, + )); + } + + info!( + target: "client", + "Successfully validated L2 block #{number} with output root {output_root}", + number = number, + output_root = output_root + ); + + Ok(()) +} + +/// Reads the raw pre-state from the preimage oracle. +async fn read_raw_pre_state( + caching_oracle: &O, + boot_info: &BootInfo, +) -> Result +where + O: CommsClient, +{ + caching_oracle + .write(&HintType::AgreedPreState.encode_with(&[boot_info.agreed_pre_state.as_ref()])) + .await + .map_err(OracleProviderError::Preimage)?; + let pre = caching_oracle + .get(PreimageKey::new(*boot_info.agreed_pre_state, PreimageKeyType::Keccak256)) + .await + .map_err(OracleProviderError::Preimage)?; + + if pre.is_empty() { + return Err(OracleProviderError::Preimage(PreimageOracleError::Other( + "Invalid pre-state preimage".to_string(), + ))); + } + + Ok(Bytes::from(pre)) +} + +/// Fetches the safe head hash of the L2 chain based on the agreed upon L2 output root in the +/// [BootInfo]. +async fn fetch_l2_safe_head_hash( + caching_oracle: &O, + pre: &PreState, +) -> Result +where + O: CommsClient, +{ + // Fetch the output root of the safe head block for the current L2 chain. + let rich_output = match pre { + PreState::SuperRoot(super_root) => { + super_root.output_roots.first().ok_or(OracleProviderError::Preimage( + PreimageOracleError::Other("No output roots in super root".to_string()), + ))? + } + PreState::TransitionState(transition_state) => { + transition_state.pre_state.output_roots.get(transition_state.step as usize).ok_or( + OracleProviderError::Preimage(PreimageOracleError::Other( + "No output roots in transition state's pending progress".to_string(), + )), + )? + } + }; + + caching_oracle + .write( + &HintType::L2OutputRoot.encode_with(&[rich_output.chain_id.to_be_bytes().as_slice()]), + ) + .await + .map_err(OracleProviderError::Preimage)?; + let output_preimage = caching_oracle + .get(PreimageKey::new(*rich_output.output_root, PreimageKeyType::Keccak256)) + .await + .map_err(OracleProviderError::Preimage)?; + output_preimage[96..128].try_into().map_err(OracleProviderError::SliceConversion) +} diff --git a/bin/client/src/kona.rs b/bin/client/src/kona.rs index cf166c142..2e91cd30e 100644 --- a/bin/client/src/kona.rs +++ b/bin/client/src/kona.rs @@ -39,7 +39,7 @@ fn main() -> Result<(), String> { .expect("Failed to set tracing subscriber"); } - kona_proof::block_on(kona_client::run( + kona_proof::block_on(kona_client::single::run( ORACLE_READER, HINT_WRITER, Some(precompiles::fpvm_handle_register), diff --git a/bin/client/src/kona_interop.rs b/bin/client/src/kona_interop.rs new file mode 100644 index 000000000..a0b33e0e1 --- /dev/null +++ b/bin/client/src/kona_interop.rs @@ -0,0 +1,47 @@ +#![doc = include_str!("../README.md")] +#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)] +#![deny(unused_must_use, rust_2018_idioms)] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +#![no_std] +#![cfg_attr(any(target_arch = "mips", target_arch = "riscv64"), no_main)] + +extern crate alloc; + +use alloc::string::String; +use kona_preimage::{HintWriter, OracleReader}; +use kona_std_fpvm::{FileChannel, FileDescriptor}; +use kona_std_fpvm_proc::client_entry; + +mod precompiles; + +/// The global preimage oracle reader pipe. +static ORACLE_READER_PIPE: FileChannel = + FileChannel::new(FileDescriptor::PreimageRead, FileDescriptor::PreimageWrite); + +/// The global hint writer pipe. +static HINT_WRITER_PIPE: FileChannel = + FileChannel::new(FileDescriptor::HintRead, FileDescriptor::HintWrite); + +/// The global preimage oracle reader. +static ORACLE_READER: OracleReader = OracleReader::new(ORACLE_READER_PIPE); + +/// The global hint writer. +static HINT_WRITER: HintWriter = HintWriter::new(HINT_WRITER_PIPE); + +#[client_entry(100_000_000)] +fn main() -> Result<(), String> { + #[cfg(feature = "client-tracing")] + { + use kona_std_fpvm::tracing::FpvmTracingSubscriber; + + let subscriber = FpvmTracingSubscriber::new(tracing::Level::INFO); + tracing::subscriber::set_global_default(subscriber) + .expect("Failed to set tracing subscriber"); + } + + kona_proof::block_on(kona_client::interop::run( + ORACLE_READER, + HINT_WRITER, + Some(precompiles::fpvm_handle_register), + )) +} diff --git a/bin/client/src/lib.rs b/bin/client/src/lib.rs index 83c4c70cd..ae62ca068 100644 --- a/bin/client/src/lib.rs +++ b/bin/client/src/lib.rs @@ -7,173 +7,5 @@ extern crate alloc; -use alloc::sync::Arc; -use alloy_consensus::{Header, Sealed}; -use alloy_primitives::B256; -use core::fmt::Debug; -use kona_driver::{Driver, DriverError}; -use kona_executor::{ExecutorError, KonaHandleRegister, TrieDBProvider}; -use kona_preimage::{ - CommsClient, HintWriterClient, PreimageKey, PreimageKeyType, PreimageOracleClient, -}; -use kona_proof::{ - errors::OracleProviderError, - executor::KonaExecutor, - l1::{OracleBlobProvider, OracleL1ChainProvider, OraclePipeline}, - l2::OracleL2ChainProvider, - sync::new_pipeline_cursor, - BootInfo, CachingOracle, HintType, -}; -use thiserror::Error; -use tracing::{error, info, warn}; - -/// An error that can occur when running the fault proof program. -#[derive(Error, Debug)] -pub enum FaultProofProgramError { - /// The claim is invalid. - #[error("Invalid claim. Expected {0}, actual {1}")] - InvalidClaim(B256, B256), - /// An error occurred in the Oracle provider. - #[error(transparent)] - OracleProviderError(#[from] OracleProviderError), - /// An error occurred in the driver. - #[error(transparent)] - Driver(#[from] DriverError), -} - -/// Executes the fault proof program with the given [PreimageOracleClient] and [HintWriterClient]. -#[inline] -pub async fn run( - oracle_client: P, - hint_client: H, - handle_register: Option< - KonaHandleRegister< - OracleL2ChainProvider>, - OracleL2ChainProvider>, - >, - >, -) -> Result<(), FaultProofProgramError> -where - P: PreimageOracleClient + Send + Sync + Debug + Clone, - H: HintWriterClient + Send + Sync + Debug + Clone, -{ - const ORACLE_LRU_SIZE: usize = 1024; - - //////////////////////////////////////////////////////////////// - // PROLOGUE // - //////////////////////////////////////////////////////////////// - - let oracle = Arc::new(CachingOracle::new(ORACLE_LRU_SIZE, oracle_client, hint_client)); - let boot = match BootInfo::load(oracle.as_ref()).await { - Ok(boot) => Arc::new(boot), - Err(e) => { - error!(target: "client", "Failed to load boot info: {:?}", e); - return Err(e.into()); - } - }; - let mut l1_provider = OracleL1ChainProvider::new(boot.clone(), oracle.clone()); - let mut l2_provider = OracleL2ChainProvider::new(boot.clone(), oracle.clone()); - let beacon = OracleBlobProvider::new(oracle.clone()); - - // If the claimed L2 block number is less than the safe head of the L2 chain, the claim is - // invalid. - let safe_head = fetch_safe_head(oracle.as_ref(), boot.as_ref(), &mut l2_provider).await?; - if boot.claimed_l2_block_number < safe_head.number { - error!( - target: "client", - "Claimed L2 block number {claimed} is less than the safe head {safe}", - claimed = boot.claimed_l2_block_number, - safe = safe_head.number - ); - return Err(FaultProofProgramError::InvalidClaim( - boot.agreed_l2_output_root, - boot.claimed_l2_output_root, - )); - } - - // In the case where the agreed upon L2 output root is the same as the claimed L2 output root, - // trace extension is detected and we can skip the derivation and execution steps. - if boot.agreed_l2_output_root == boot.claimed_l2_output_root { - info!( - target: "client", - "Trace extension detected. State transition is already agreed upon.", - ); - return Ok(()); - } - - //////////////////////////////////////////////////////////////// - // DERIVATION & EXECUTION // - //////////////////////////////////////////////////////////////// - - // Create a new derivation driver with the given boot information and oracle. - let cursor = new_pipeline_cursor(&boot, safe_head, &mut l1_provider, &mut l2_provider).await?; - let cfg = Arc::new(boot.rollup_config.clone()); - let pipeline = OraclePipeline::new( - cfg.clone(), - cursor.clone(), - oracle.clone(), - beacon, - l1_provider.clone(), - l2_provider.clone(), - ); - let executor = KonaExecutor::new(&cfg, l2_provider.clone(), l2_provider, handle_register, None); - let mut driver = Driver::new(cursor, executor, pipeline); - - // Run the derivation pipeline until we are able to produce the output root of the claimed - // L2 block. - let (number, output_root) = - driver.advance_to_target(&boot.rollup_config, Some(boot.claimed_l2_block_number)).await?; - - //////////////////////////////////////////////////////////////// - // EPILOGUE // - //////////////////////////////////////////////////////////////// - - if output_root != boot.claimed_l2_output_root { - error!( - target: "client", - "Failed to validate L2 block #{number} with output root {output_root}", - number = number, - output_root = output_root - ); - return Err(FaultProofProgramError::InvalidClaim(output_root, boot.claimed_l2_output_root)); - } - - info!( - target: "client", - "Successfully validated L2 block #{number} with output root {output_root}", - number = number, - output_root = output_root - ); - - Ok(()) -} - -/// Fetches the safe head of the L2 chain based on the agreed upon L2 output root in the -/// [BootInfo]. -async fn fetch_safe_head( - caching_oracle: &O, - boot_info: &BootInfo, - l2_chain_provider: &mut OracleL2ChainProvider, -) -> Result, OracleProviderError> -where - O: CommsClient, -{ - caching_oracle - .write(&HintType::StartingL2Output.encode_with(&[boot_info.agreed_l2_output_root.as_ref()])) - .await - .map_err(OracleProviderError::Preimage)?; - let mut output_preimage = [0u8; 128]; - caching_oracle - .get_exact( - PreimageKey::new(*boot_info.agreed_l2_output_root, PreimageKeyType::Keccak256), - &mut output_preimage, - ) - .await - .map_err(OracleProviderError::Preimage)?; - - let safe_hash = - output_preimage[96..128].try_into().map_err(OracleProviderError::SliceConversion)?; - l2_chain_provider - .header_by_hash(safe_hash) - .map(|header| Sealed::new_unchecked(header, safe_hash)) -} +pub mod interop; +pub mod single; diff --git a/bin/client/src/single.rs b/bin/client/src/single.rs new file mode 100644 index 000000000..d63a9a2c1 --- /dev/null +++ b/bin/client/src/single.rs @@ -0,0 +1,176 @@ +//! Single-chain fault proof program entrypoint. + +use alloc::sync::Arc; +use alloy_consensus::Sealed; +use alloy_primitives::B256; +use core::fmt::Debug; +use kona_driver::{Driver, DriverError}; +use kona_executor::{ExecutorError, KonaHandleRegister, TrieDBProvider}; +use kona_preimage::{ + CommsClient, HintWriterClient, PreimageKey, PreimageKeyType, PreimageOracleClient, +}; +use kona_proof::{ + errors::OracleProviderError, + executor::KonaExecutor, + l1::{OracleBlobProvider, OracleL1ChainProvider, OraclePipeline}, + l2::OracleL2ChainProvider, + sync::new_pipeline_cursor, + BootInfo, CachingOracle, HintType, +}; +use thiserror::Error; +use tracing::{error, info}; + +/// An error that can occur when running the fault proof program. +#[derive(Error, Debug)] +pub enum FaultProofProgramError { + /// The claim is invalid. + #[error("Invalid claim. Expected {0}, actual {1}")] + InvalidClaim(B256, B256), + /// An error occurred in the Oracle provider. + #[error(transparent)] + OracleProviderError(#[from] OracleProviderError), + /// An error occurred in the driver. + #[error(transparent)] + Driver(#[from] DriverError), +} + +/// Executes the fault proof program with the given [PreimageOracleClient] and [HintWriterClient]. +#[inline] +pub async fn run( + oracle_client: P, + hint_client: H, + handle_register: Option< + KonaHandleRegister< + OracleL2ChainProvider>, + OracleL2ChainProvider>, + >, + >, +) -> Result<(), FaultProofProgramError> +where + P: PreimageOracleClient + Send + Sync + Debug + Clone, + H: HintWriterClient + Send + Sync + Debug + Clone, +{ + const ORACLE_LRU_SIZE: usize = 1024; + + //////////////////////////////////////////////////////////////// + // PROLOGUE // + //////////////////////////////////////////////////////////////// + + let oracle = Arc::new(CachingOracle::new(ORACLE_LRU_SIZE, oracle_client, hint_client)); + let boot = match BootInfo::load(oracle.as_ref()).await { + Ok(boot) => Arc::new(boot), + Err(e) => { + error!(target: "client", "Failed to load boot info: {:?}", e); + return Err(e.into()); + } + }; + let safe_head_hash = fetch_safe_head_hash(oracle.as_ref(), boot.as_ref()).await?; + + let mut l1_provider = OracleL1ChainProvider::new(boot.l1_head, oracle.clone()); + let mut l2_provider = + OracleL2ChainProvider::new(safe_head_hash, boot.rollup_config.clone(), oracle.clone()); + let beacon = OracleBlobProvider::new(oracle.clone()); + + // Fetch the safe head's block header. + let safe_head = l2_provider + .header_by_hash(safe_head_hash) + .map(|header| Sealed::new_unchecked(header, safe_head_hash))?; + + // If the claimed L2 block number is less than the safe head of the L2 chain, the claim is + // invalid. + if boot.claimed_l2_block_number < safe_head.number { + error!( + target: "client", + "Claimed L2 block number {claimed} is less than the safe head {safe}", + claimed = boot.claimed_l2_block_number, + safe = safe_head.number + ); + return Err(FaultProofProgramError::InvalidClaim( + boot.agreed_l2_output_root, + boot.claimed_l2_output_root, + )); + } + + // In the case where the agreed upon L2 output root is the same as the claimed L2 output root, + // trace extension is detected and we can skip the derivation and execution steps. + if boot.agreed_l2_output_root == boot.claimed_l2_output_root { + info!( + target: "client", + "Trace extension detected. State transition is already agreed upon.", + ); + return Ok(()); + } + + //////////////////////////////////////////////////////////////// + // DERIVATION & EXECUTION // + //////////////////////////////////////////////////////////////// + + // Create a new derivation driver with the given boot information and oracle. + let cursor = + new_pipeline_cursor(&boot.rollup_config, safe_head, &mut l1_provider, &mut l2_provider) + .await?; + let cfg = Arc::new(boot.rollup_config.clone()); + let pipeline = OraclePipeline::new( + cfg.clone(), + cursor.clone(), + oracle.clone(), + beacon, + l1_provider.clone(), + l2_provider.clone(), + ); + let executor = KonaExecutor::new(&cfg, l2_provider.clone(), l2_provider, handle_register, None); + let mut driver = Driver::new(cursor, executor, pipeline); + + // Run the derivation pipeline until we are able to produce the output root of the claimed + // L2 block. + let (number, _, output_root) = + driver.advance_to_target(&boot.rollup_config, Some(boot.claimed_l2_block_number)).await?; + + //////////////////////////////////////////////////////////////// + // EPILOGUE // + //////////////////////////////////////////////////////////////// + + if output_root != boot.claimed_l2_output_root { + error!( + target: "client", + "Failed to validate L2 block #{number} with output root {output_root}", + number = number, + output_root = output_root + ); + return Err(FaultProofProgramError::InvalidClaim(output_root, boot.claimed_l2_output_root)); + } + + info!( + target: "client", + "Successfully validated L2 block #{number} with output root {output_root}", + number = number, + output_root = output_root + ); + + Ok(()) +} + +/// Fetches the safe head hash of the L2 chain based on the agreed upon L2 output root in the +/// [BootInfo]. +async fn fetch_safe_head_hash( + caching_oracle: &O, + boot_info: &BootInfo, +) -> Result +where + O: CommsClient, +{ + caching_oracle + .write(&HintType::StartingL2Output.encode_with(&[boot_info.agreed_l2_output_root.as_ref()])) + .await + .map_err(OracleProviderError::Preimage)?; + let mut output_preimage = [0u8; 128]; + caching_oracle + .get_exact( + PreimageKey::new(*boot_info.agreed_l2_output_root, PreimageKeyType::Keccak256), + &mut output_preimage, + ) + .await + .map_err(OracleProviderError::Preimage)?; + + output_preimage[96..128].try_into().map_err(OracleProviderError::SliceConversion) +} diff --git a/bin/host/src/lib.rs b/bin/host/src/lib.rs index 0337777c9..560e020b1 100644 --- a/bin/host/src/lib.rs +++ b/bin/host/src/lib.rs @@ -90,7 +90,7 @@ pub async fn start_server_and_native_client(cfg: HostCli) -> Result { )); // Start the client program in a separate child process. - let program_task = task::spawn(kona_client::run( + let program_task = task::spawn(kona_client::single::run( OracleReader::new(preimage_chan.client), HintWriter::new(hint_chan.client), None, diff --git a/crates/driver/src/core.rs b/crates/driver/src/core.rs index 3e190e8cf..d1542fb47 100644 --- a/crates/driver/src/core.rs +++ b/crates/driver/src/core.rs @@ -73,7 +73,7 @@ where &mut self, cfg: &RollupConfig, mut target: Option, - ) -> DriverResult<(u64, B256), E::Error> { + ) -> DriverResult<(u64, B256, B256), E::Error> { loop { // Check if we have reached the target block number. if let Some(tb) = target { @@ -81,6 +81,7 @@ where info!(target: "client", "Derivation complete, reached L2 safe head."); return Ok(( self.cursor.l2_safe_head().block_info.number, + self.cursor.l2_safe_head().block_info.hash, *self.cursor.l2_safe_head_output_root(), )); } diff --git a/crates/proof-sdk/proof/src/l1/chain_provider.rs b/crates/proof-sdk/proof/src/l1/chain_provider.rs index 0cf9c8196..de07a5e38 100644 --- a/crates/proof-sdk/proof/src/l1/chain_provider.rs +++ b/crates/proof-sdk/proof/src/l1/chain_provider.rs @@ -1,6 +1,6 @@ //! Contains the concrete implementation of the [ChainProvider] trait for the proof. -use crate::{errors::OracleProviderError, BootInfo, HintType}; +use crate::{errors::OracleProviderError, HintType}; use alloc::{boxed::Box, sync::Arc, vec::Vec}; use alloy_consensus::{Header, Receipt, ReceiptEnvelope, TxEnvelope}; use alloy_eips::eip2718::Decodable2718; @@ -15,16 +15,16 @@ use maili_protocol::BlockInfo; /// The oracle-backed L1 chain provider for the client program. #[derive(Debug, Clone)] pub struct OracleL1ChainProvider { - /// The boot information - boot_info: Arc, + /// The L1 head hash. + pub l1_head: B256, /// The preimage oracle client. pub oracle: Arc, } impl OracleL1ChainProvider { /// Creates a new [OracleL1ChainProvider] with the given boot information and oracle client. - pub const fn new(boot_info: Arc, oracle: Arc) -> Self { - Self { boot_info, oracle } + pub const fn new(l1_head: B256, oracle: Arc) -> Self { + Self { l1_head, oracle } } } @@ -52,7 +52,7 @@ impl ChainProvider for OracleL1ChainProvider { async fn block_info_by_number(&mut self, block_number: u64) -> Result { // Fetch the starting block header. - let mut header = self.header_by_hash(self.boot_info.l1_head).await?; + let mut header = self.header_by_hash(self.l1_head).await?; // Check if the block number is in range. If not, we can fail early. if block_number > header.number { diff --git a/crates/proof-sdk/proof/src/l2/chain_provider.rs b/crates/proof-sdk/proof/src/l2/chain_provider.rs index a6b1eecb1..2bd3e53a4 100644 --- a/crates/proof-sdk/proof/src/l2/chain_provider.rs +++ b/crates/proof-sdk/proof/src/l2/chain_provider.rs @@ -1,6 +1,6 @@ //! Contains the concrete implementation of the [L2ChainProvider] trait for the client program. -use crate::{errors::OracleProviderError, BootInfo, HintType}; +use crate::{errors::OracleProviderError, HintType}; use alloc::{boxed::Box, sync::Arc, vec::Vec}; use alloy_consensus::{BlockBody, Header}; use alloy_eips::eip2718::Decodable2718; @@ -18,16 +18,18 @@ use op_alloy_genesis::{RollupConfig, SystemConfig}; /// The oracle-backed L2 chain provider for the client program. #[derive(Debug, Clone)] pub struct OracleL2ChainProvider { - /// The boot information - boot_info: Arc, + /// The L2 safe head block hash. + l2_head: B256, + /// The rollup configuration. + rollup_config: RollupConfig, /// The preimage oracle client. oracle: Arc, } impl OracleL2ChainProvider { /// Creates a new [OracleL2ChainProvider] with the given boot information and oracle client. - pub const fn new(boot_info: Arc, oracle: Arc) -> Self { - Self { boot_info, oracle } + pub const fn new(l2_head: B256, rollup_config: RollupConfig, oracle: Arc) -> Self { + Self { l2_head, rollup_config, oracle } } } @@ -35,27 +37,7 @@ impl OracleL2ChainProvider { /// Returns a [Header] corresponding to the given L2 block number, by walking back from the /// L2 safe head. async fn header_by_number(&mut self, block_number: u64) -> Result { - // Fetch the starting L2 output preimage. - self.oracle - .write( - &HintType::StartingL2Output - .encode_with(&[self.boot_info.agreed_l2_output_root.as_ref()]), - ) - .await - .map_err(OracleProviderError::Preimage)?; - let output_preimage = self - .oracle - .get(PreimageKey::new( - *self.boot_info.agreed_l2_output_root, - PreimageKeyType::Keccak256, - )) - .await - .map_err(OracleProviderError::Preimage)?; - - // Fetch the starting block header. - let block_hash = - output_preimage[96..128].try_into().map_err(OracleProviderError::SliceConversion)?; - let mut header = self.header_by_hash(block_hash)?; + let mut header = self.header_by_hash(self.l2_head)?; // Check if the block number is in range. If not, we can fail early. if block_number > header.number { @@ -80,7 +62,7 @@ impl BatchValidationProvider for OracleL2ChainProv let block = self.block_by_number(number).await?; // Construct the system config from the payload. - L2BlockInfo::from_block_and_genesis(&block, &self.boot_info.rollup_config.genesis) + L2BlockInfo::from_block_and_genesis(&block, &self.rollup_config.genesis) .map_err(OracleProviderError::BlockInfo) } @@ -114,7 +96,6 @@ impl BatchValidationProvider for OracleL2ChainProv transactions, ommers: Vec::new(), withdrawals: self - .boot_info .rollup_config .is_canyon_active(timestamp) .then(|| alloy_eips::eip4895::Withdrawals::new(Vec::new())), diff --git a/crates/proof-sdk/proof/src/sync.rs b/crates/proof-sdk/proof/src/sync.rs index 2cc63844b..9865c207c 100644 --- a/crates/proof-sdk/proof/src/sync.rs +++ b/crates/proof-sdk/proof/src/sync.rs @@ -1,19 +1,21 @@ //! Sync Start use crate::{ - errors::OracleProviderError, l1::OracleL1ChainProvider, l2::OracleL2ChainProvider, BootInfo, + errors::OracleProviderError, l1::OracleL1ChainProvider, l2::OracleL2ChainProvider, FlushableCache, }; use alloy_consensus::{Header, Sealed}; +use alloy_primitives::B256; use core::fmt::Debug; use kona_derive::traits::ChainProvider; use kona_driver::{PipelineCursor, TipCursor}; use kona_preimage::CommsClient; use maili_protocol::BatchValidationProvider; +use maili_registry::RollupConfig; /// Constructs a [`PipelineCursor`] from the caching oracle, boot info, and providers. pub async fn new_pipeline_cursor( - boot_info: &BootInfo, + rollup_config: &RollupConfig, safe_header: Sealed
, chain_provider: &mut OracleL1ChainProvider, l2_chain_provider: &mut OracleL2ChainProvider, @@ -26,17 +28,16 @@ where // Walk back the starting L1 block by `channel_timeout` to ensure that the full channel is // captured. - let channel_timeout = - boot_info.rollup_config.channel_timeout(safe_head_info.block_info.timestamp); + let channel_timeout = rollup_config.channel_timeout(safe_head_info.block_info.timestamp); let mut l1_origin_number = l1_origin.number.saturating_sub(channel_timeout); - if l1_origin_number < boot_info.rollup_config.genesis.l1.number { - l1_origin_number = boot_info.rollup_config.genesis.l1.number; + if l1_origin_number < rollup_config.genesis.l1.number { + l1_origin_number = rollup_config.genesis.l1.number; } let origin = chain_provider.block_info_by_number(l1_origin_number).await?; // Construct the cursor. let mut cursor = PipelineCursor::new(channel_timeout, origin); - let tip = TipCursor::new(safe_head_info, safe_header, boot_info.agreed_l2_output_root); + let tip = TipCursor::new(safe_head_info, safe_header, B256::ZERO); cursor.advance(origin, tip); Ok(cursor) }