diff --git a/bin/client/src/lib.rs b/bin/client/src/lib.rs index 9364c458e..f95e3c0a8 100644 --- a/bin/client/src/lib.rs +++ b/bin/client/src/lib.rs @@ -7,18 +7,21 @@ 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; -use kona_preimage::{HintWriterClient, PreimageOracleClient}; +use kona_executor::{ExecutorError, 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, + BootInfo, CachingOracle, HintType, }; use thiserror::Error; use tracing::{error, info, warn}; @@ -64,35 +67,34 @@ where return Err(e.into()); } }; - let l1_provider = OracleL1ChainProvider::new(boot.clone(), oracle.clone()); - let l2_provider = OracleL2ChainProvider::new(boot.clone(), oracle.clone()); + 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 genesis block is claimed, we can exit early. - // The agreed upon prestate is consented to by all parties, and there is no state - // transition, so the claim is valid if the claimed output root matches the agreed - // upon output root. - if boot.claimed_l2_block_number == 0 { - warn!("Genesis block claimed. Exiting early."); - if boot.agreed_l2_output_root == boot.claimed_l2_output_root { - info!( - target: "client", - "Successfully validated genesis block with output root {output_root}", - output_root = boot.agreed_l2_output_root - ); - return Ok(()); - } else { - error!( - target: "client", - "Failed to validate genesis block. Expected {genesis_root}, actual {claimed_root}", - genesis_root = boot.agreed_l2_output_root, - claimed_root = boot.claimed_l2_output_root - ); - return Err(FaultProofProgramError::InvalidClaim( - boot.agreed_l2_output_root, - boot.claimed_l2_output_root, - )); - }; + // 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(()); } //////////////////////////////////////////////////////////////// @@ -100,13 +102,7 @@ where //////////////////////////////////////////////////////////////// // Create a new derivation driver with the given boot information and oracle. - let cursor = new_pipeline_cursor( - oracle.clone(), - &boot, - &mut l1_provider.clone(), - &mut l2_provider.clone(), - ) - .await?; + 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(), @@ -147,3 +143,33 @@ where 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)) +} diff --git a/crates/proof-sdk/proof/src/sync.rs b/crates/proof-sdk/proof/src/sync.rs index 3398cf9f0..0cb804f05 100644 --- a/crates/proof-sdk/proof/src/sync.rs +++ b/crates/proof-sdk/proof/src/sync.rs @@ -2,44 +2,25 @@ use crate::{ errors::OracleProviderError, l1::OracleL1ChainProvider, l2::OracleL2ChainProvider, BootInfo, - FlushableCache, HintType, + FlushableCache, }; -use alloc::sync::Arc; -use alloy_consensus::Sealed; +use alloy_consensus::{Header, Sealed}; use core::fmt::Debug; use kona_derive::traits::ChainProvider; use kona_driver::{PipelineCursor, TipCursor}; -use kona_executor::TrieDBProvider; -use kona_preimage::{CommsClient, PreimageKey, PreimageKeyType}; +use kona_preimage::CommsClient; use op_alloy_protocol::BatchValidationProvider; /// Constructs a [`PipelineCursor`] from the caching oracle, boot info, and providers. pub async fn new_pipeline_cursor( - caching_oracle: Arc, boot_info: &BootInfo, + safe_header: Sealed
, chain_provider: &mut OracleL1ChainProvider, l2_chain_provider: &mut OracleL2ChainProvider, ) -> Result where O: CommsClient + FlushableCache + FlushableCache + Send + Sync + Debug, { - // Find the initial safe head, based off of the starting L2 block number in the boot info. - 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)?; - let safe_header = l2_chain_provider.header_by_hash(safe_hash)?; let safe_head_info = l2_chain_provider.l2_block_info_by_number(safe_header.number).await?; let l1_origin = chain_provider.block_info_by_number(safe_head_info.l1_origin.number).await?; @@ -54,7 +35,6 @@ where let origin = chain_provider.block_info_by_number(l1_origin_number).await?; // Construct the cursor. - let safe_header = Sealed::new_unchecked(safe_header, safe_hash); let mut cursor = PipelineCursor::new(channel_timeout, origin); let tip = TipCursor::new(safe_head_info, safe_header, boot_info.agreed_l2_output_root); cursor.advance(origin, tip);