From 9ca353f19d81b594370b0f3375133097f01cfd84 Mon Sep 17 00:00:00 2001 From: clabby Date: Thu, 30 Jan 2025 19:51:09 -0500 Subject: [PATCH] feat(proof-interop): Support multiple `RollupConfigs` in boot --- Cargo.lock | 1 + bin/client/src/interop/consolidate.rs | 10 ++- bin/client/src/interop/mod.rs | 14 ++- bin/client/src/interop/transition.rs | 55 +++++++----- bin/client/src/interop/util.rs | 33 +------ bin/client/src/single.rs | 35 ++++---- bin/host/src/interop/fetcher.rs | 2 +- bin/host/src/interop/local_kv.rs | 14 ++- crates/interop/Cargo.toml | 4 + crates/interop/src/super_root.rs | 2 + crates/proof-sdk/proof-interop/Cargo.toml | 2 +- crates/proof-sdk/proof-interop/src/boot.rs | 87 +++++++++++++------ .../proof-sdk/proof-interop/src/pre_state.rs | 25 +++++- .../proof-sdk/proof/src/l2/chain_provider.rs | 4 +- 14 files changed, 165 insertions(+), 123 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1293ea092..680d87827 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2662,6 +2662,7 @@ dependencies = [ "async-trait", "op-alloy-consensus", "rand 0.9.0", + "serde", "thiserror 2.0.11", "tokio", "tracing", diff --git a/bin/client/src/interop/consolidate.rs b/bin/client/src/interop/consolidate.rs index 52efd8f50..7dc0ae47b 100644 --- a/bin/client/src/interop/consolidate.rs +++ b/bin/client/src/interop/consolidate.rs @@ -20,18 +20,17 @@ use tracing::info; pub(crate) async fn consolidate_dependencies( oracle: Arc>, boot: BootInfo, - pre: PreState, ) -> Result<(), FaultProofProgramError> where P: PreimageOracleClient + Send + Sync + Debug + Clone, H: HintWriterClient + Send + Sync + Debug + Clone, { - let provider = OracleInteropProvider::new(oracle, pre.clone()); + let provider = OracleInteropProvider::new(oracle, boot.agreed_pre_state.clone()); info!(target: "client_interop", "Deriving local-safe headers from prestate"); // Ensure that the pre-state is a transition state. - let PreState::TransitionState(ref transition_state) = pre else { + let PreState::TransitionState(ref transition_state) = boot.agreed_pre_state else { return Err(FaultProofProgramError::StateTransitionFailed); }; @@ -58,7 +57,10 @@ where // // TODO: This won't work if we replace blocks, `transition` doesn't allow replacement of pending // progress just yet. - let post = pre.transition(None).ok_or(FaultProofProgramError::StateTransitionFailed)?; + let post = boot + .agreed_pre_state + .transition(None) + .ok_or(FaultProofProgramError::StateTransitionFailed)?; let post_commitment = post.hash(); // Ensure that the post-state matches the claimed post-state. diff --git a/bin/client/src/interop/mod.rs b/bin/client/src/interop/mod.rs index 73a550eae..86d96bdc7 100644 --- a/bin/client/src/interop/mod.rs +++ b/bin/client/src/interop/mod.rs @@ -2,7 +2,6 @@ use alloc::sync::Arc; use alloy_primitives::B256; -use alloy_rlp::Decodable; use consolidate::consolidate_dependencies; use core::fmt::Debug; use kona_driver::DriverError; @@ -13,7 +12,6 @@ use kona_proof_interop::{BootInfo, PreState, INVALID_TRANSITION_HASH, TRANSITION use thiserror::Error; use tracing::{error, info}; use transition::sub_transition; -use util::read_raw_pre_state; pub(crate) mod consolidate; pub(crate) mod transition; @@ -69,7 +67,7 @@ where }; // If the pre state is invalid, short-circuit and check if the post-state claim is also invalid. - if boot.agreed_pre_state == INVALID_TRANSITION_HASH && + if boot.agreed_pre_state_commitment == INVALID_TRANSITION_HASH && boot.claimed_post_state == INVALID_TRANSITION_HASH { info!(target: "client_interop", "Invalid pre and post state, short-circuiting."); @@ -78,20 +76,18 @@ where // Load in the agreed pre-state from the preimage oracle in order to determine the active // sub-problem. - let pre = PreState::decode(&mut read_raw_pre_state(oracle.as_ref(), &boot).await?.as_ref()) - .map_err(FaultProofProgramError::RLPDecodingError)?; - match pre { + match boot.agreed_pre_state { PreState::SuperRoot(_) => { // If the pre-state is a super root, the first sub-problem is always selected. - sub_transition(oracle, handle_register, boot, pre).await + sub_transition(oracle, handle_register, boot).await } PreState::TransitionState(ref transition_state) => { // If the pre-state is a transition state, the sub-problem is selected based on the // current step. if transition_state.step < TRANSITION_STATE_MAX_STEPS { - sub_transition(oracle, handle_register, boot, pre).await + sub_transition(oracle, handle_register, boot).await } else { - consolidate_dependencies(oracle, boot, pre).await + consolidate_dependencies(oracle, boot).await } } } diff --git a/bin/client/src/interop/transition.rs b/bin/client/src/interop/transition.rs index 5db3f30b5..136207f0b 100644 --- a/bin/client/src/interop/transition.rs +++ b/bin/client/src/interop/transition.rs @@ -31,31 +31,42 @@ pub(crate) async fn sub_transition( >, >, boot: BootInfo, - pre: PreState, ) -> Result<(), FaultProofProgramError> where P: PreimageOracleClient + Send + Sync + Debug + Clone, H: HintWriterClient + Send + Sync + Debug + Clone, { // Check if we can short-circuit the transition, if we are within padding. - if let PreState::TransitionState(ref transition_state) = pre { + if let PreState::TransitionState(ref transition_state) = boot.agreed_pre_state { if transition_state.step >= transition_state.pre_state.output_roots.len() as u64 { info!( target: "interop_client", "No derivation/execution required, transition state is already saturated." ); - return transition_and_check(pre, None, boot.claimed_post_state, None); + return transition_and_check(boot.agreed_pre_state, None, boot.claimed_post_state, None); } } // Fetch the L2 block hash of the current safe head. - let safe_head_hash = fetch_l2_safe_head_hash(oracle.as_ref(), &pre).await?; + let safe_head_hash = fetch_l2_safe_head_hash(oracle.as_ref(), &boot.agreed_pre_state).await?; + + // Determine the active L2 chain ID and the fetch rollup configuration. + let active_l2_chain_id = boot + .agreed_pre_state + .active_l2_chain_id() + .ok_or(FaultProofProgramError::StateTransitionFailed)?; + let rollup_config = boot + .rollup_configs + .get(&active_l2_chain_id) + .cloned() + .map(Arc::new) + .ok_or(FaultProofProgramError::StateTransitionFailed)?; // 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()); + OracleL2ChainProvider::new(safe_head_hash, rollup_config.clone(), oracle.clone()); let beacon = OracleBlobProvider::new(oracle.clone()); // Fetch the safe head's block header. @@ -64,9 +75,8 @@ where .map(|header| Sealed::new_unchecked(header, safe_head_hash))?; // Translate the claimed timestamp to an L2 block number. - let claimed_l2_block_number = boot.rollup_config.genesis.l2.number + - ((boot.claimed_l2_timestamp - boot.rollup_config.genesis.l2_time) / - boot.rollup_config.block_time); + let claimed_l2_block_number = rollup_config.genesis.l2.number + + ((boot.claimed_l2_timestamp - rollup_config.genesis.l2_time) / rollup_config.block_time); // If the claimed L2 block number is less than the safe head of the L2 chain, the claim is // invalid. @@ -79,7 +89,7 @@ where safe = safe_head.number ); return Err(FaultProofProgramError::InvalidClaim( - boot.agreed_pre_state, + boot.agreed_pre_state_commitment, boot.claimed_post_state, )); } @@ -95,29 +105,34 @@ where // 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) + new_pipeline_cursor(rollup_config.as_ref(), safe_head, &mut l1_provider, &mut l2_provider) .await?; l2_provider.set_cursor(cursor.clone()); - let cfg = Arc::new(boot.rollup_config.clone()); let pipeline = OraclePipeline::new( - cfg.clone(), + rollup_config.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 executor = KonaExecutor::new( + rollup_config.as_ref(), + 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. - match driver.advance_to_target(&boot.rollup_config, Some(claimed_l2_block_number)).await { + match driver.advance_to_target(rollup_config.as_ref(), Some(claimed_l2_block_number)).await { Ok((safe_head, output_root)) => { let optimistic_block = OptimisticBlock::new(safe_head.block_info.hash, output_root); transition_and_check( - pre, + boot.agreed_pre_state, Some(optimistic_block), boot.claimed_post_state, Some((boot.claimed_l2_timestamp, safe_head.block_info.timestamp)), @@ -137,14 +152,12 @@ where "Exhausted data source; Transitioning to invalid state." ); - if boot.claimed_post_state == INVALID_TRANSITION_HASH { - Ok(()) - } else { - Err(FaultProofProgramError::InvalidClaim( + (boot.claimed_post_state == INVALID_TRANSITION_HASH).then_some(()).ok_or( + FaultProofProgramError::InvalidClaim( INVALID_TRANSITION_HASH, boot.claimed_post_state, - )) - } + ), + ) } Err(e) => { error!( diff --git a/bin/client/src/interop/util.rs b/bin/client/src/interop/util.rs index 2098fc2db..b42764350 100644 --- a/bin/client/src/interop/util.rs +++ b/bin/client/src/interop/util.rs @@ -1,39 +1,12 @@ //! Utilities for the interop proof program use alloc::string::ToString; -use alloy_primitives::{Bytes, B256}; +use alloy_primitives::B256; use kona_preimage::{errors::PreimageOracleError, CommsClient, PreimageKey, PreimageKeyType}; use kona_proof::errors::OracleProviderError; -use kona_proof_interop::{BootInfo, HintType, PreState}; +use kona_proof_interop::{HintType, PreState}; -/// Reads the raw pre-state from the preimage oracle. -pub(crate) 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]. +/// Fetches the safe head hash of the L2 chain, using the active L2 chain in the [PreState]. pub(crate) async fn fetch_l2_safe_head_hash( caching_oracle: &O, pre: &PreState, diff --git a/bin/client/src/single.rs b/bin/client/src/single.rs index 71361639e..3a00a6fc5 100644 --- a/bin/client/src/single.rs +++ b/bin/client/src/single.rs @@ -55,18 +55,13 @@ where //////////////////////////////////////////////////////////////// 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 boot = BootInfo::load(oracle.as_ref()).await?; + let rollup_config = Arc::new(boot.rollup_config); + let safe_head_hash = fetch_safe_head_hash(oracle.as_ref(), boot.agreed_l2_output_root).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()); + OracleL2ChainProvider::new(safe_head_hash, rollup_config.clone(), oracle.clone()); let beacon = OracleBlobProvider::new(oracle.clone()); // Fetch the safe head's block header. @@ -105,26 +100,32 @@ where // 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) + new_pipeline_cursor(rollup_config.as_ref(), safe_head, &mut l1_provider, &mut l2_provider) .await?; l2_provider.set_cursor(cursor.clone()); - let cfg = Arc::new(boot.rollup_config.clone()); let pipeline = OraclePipeline::new( - cfg.clone(), + rollup_config.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 executor = KonaExecutor::new( + rollup_config.as_ref(), + 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 (safe_head, output_root) = - driver.advance_to_target(&boot.rollup_config, Some(boot.claimed_l2_block_number)).await?; + let (safe_head, output_root) = driver + .advance_to_target(rollup_config.as_ref(), Some(boot.claimed_l2_block_number)) + .await?; //////////////////////////////////////////////////////////////// // EPILOGUE // @@ -154,7 +155,7 @@ where /// [BootInfo]. pub async fn fetch_safe_head_hash( caching_oracle: &O, - boot_info: &BootInfo, + agreed_l2_output_root: B256, ) -> Result where O: CommsClient, @@ -163,7 +164,7 @@ where HintType::StartingL2Output .get_exact_preimage( caching_oracle, - boot_info.agreed_l2_output_root, + agreed_l2_output_root, PreimageKeyType::Keccak256, &mut output_preimage, ) diff --git a/bin/host/src/interop/fetcher.rs b/bin/host/src/interop/fetcher.rs index 67744dc1c..306c5407c 100644 --- a/bin/host/src/interop/fetcher.rs +++ b/bin/host/src/interop/fetcher.rs @@ -687,7 +687,7 @@ where let mut l1_provider = OracleL1ChainProvider::new(l1_head, oracle.clone()); let mut l2_provider = OracleL2ChainProvider::new( agreed_block_hash, - rollup_config.as_ref().clone(), + rollup_config.clone(), oracle.clone(), ); let beacon = OracleBlobProvider::new(oracle.clone()); diff --git a/bin/host/src/interop/local_kv.rs b/bin/host/src/interop/local_kv.rs index d9bcdeb0d..57de709fc 100644 --- a/bin/host/src/interop/local_kv.rs +++ b/bin/host/src/interop/local_kv.rs @@ -7,9 +7,10 @@ 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, - L2_CLAIMED_TIMESTAMP_KEY, L2_ROLLUP_CONFIG_KEY, + L1_HEAD_KEY, L2_AGREED_PRE_STATE_KEY, L2_CLAIMED_POST_STATE_KEY, L2_CLAIMED_TIMESTAMP_KEY, + L2_ROLLUP_CONFIG_KEY, }; +use maili_registry::HashMap; /// The default chain ID to use if none is provided. pub(crate) const DEFAULT_CHAIN_ID: u64 = 0xbeef_babe; @@ -37,14 +38,11 @@ impl KeyValueStore for LocalKeyValueStore { } L2_CLAIMED_POST_STATE_KEY => Some(self.cfg.claimed_l2_post_state.to_vec()), L2_CLAIMED_TIMESTAMP_KEY => Some(self.cfg.claimed_l2_timestamp.to_be_bytes().to_vec()), - L2_CHAIN_ID_KEY => Some(self.cfg.active_l2_chain_id().ok()?.to_be_bytes().to_vec()), L2_ROLLUP_CONFIG_KEY => { let rollup_configs = self.cfg.read_rollup_configs().ok()?; - let active_rollup_config = rollup_configs - .get(&self.cfg.active_l2_chain_id().ok()?) - .cloned() - .unwrap_or_default(); - let serialized = serde_json::to_vec(&active_rollup_config).ok()?; + let serialized = + serde_json::to_vec(&rollup_configs.into_iter().collect::>()) + .ok()?; Some(serialized) } _ => None, diff --git a/crates/interop/Cargo.toml b/crates/interop/Cargo.toml index 30d2ce8dc..ea4124463 100644 --- a/crates/interop/Cargo.toml +++ b/crates/interop/Cargo.toml @@ -27,6 +27,9 @@ op-alloy-consensus.workspace = true # Arbitrary arbitrary = { version = "1.4", features = ["derive"], optional = true } +# Serde +serde = { workspace = true, optional = true } + [dev-dependencies] tokio = { workspace = true, features = ["full"] } alloy-primitives = { workspace = true, features = ["rlp", "arbitrary"] } @@ -35,3 +38,4 @@ rand.workspace = true [features] arbitrary = ["dep:arbitrary", "alloy-primitives/arbitrary"] +serde = ["dep:serde", "alloy-primitives/serde"] diff --git a/crates/interop/src/super_root.rs b/crates/interop/src/super_root.rs index 8949b6062..3ce9c60ed 100644 --- a/crates/interop/src/super_root.rs +++ b/crates/interop/src/super_root.rs @@ -13,6 +13,7 @@ use alloy_rlp::{Buf, BufMut}; /// The [SuperRoot] is the snapshot of the superchain at a given timestamp. #[derive(Debug, Clone, Eq, PartialEq)] #[cfg_attr(any(feature = "arbitrary", test), derive(arbitrary::Arbitrary))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct SuperRoot { /// The timestamp of the superchain snapshot, in seconds. pub timestamp: u64, @@ -89,6 +90,7 @@ impl SuperRoot { /// A wrapper around an output root hash with the chain ID it belongs to. #[derive(Debug, Clone, Eq, PartialEq)] #[cfg_attr(any(feature = "arbitrary", test), derive(arbitrary::Arbitrary))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct OutputRootWithChain { /// The chain ID of the output root. pub chain_id: u64, diff --git a/crates/proof-sdk/proof-interop/Cargo.toml b/crates/proof-sdk/proof-interop/Cargo.toml index 4db5c0181..cdac72cf4 100644 --- a/crates/proof-sdk/proof-interop/Cargo.toml +++ b/crates/proof-sdk/proof-interop/Cargo.toml @@ -14,7 +14,7 @@ workspace = true [dependencies] # Workspace kona-preimage.workspace = true -kona-interop.workspace = true +kona-interop = { workspace = true, features = ["serde"] } kona-proof.workspace = true kona-mpt.workspace = true diff --git a/crates/proof-sdk/proof-interop/src/boot.rs b/crates/proof-sdk/proof-interop/src/boot.rs index fc472a88d..29dbb465d 100644 --- a/crates/proof-sdk/proof-interop/src/boot.rs +++ b/crates/proof-sdk/proof-interop/src/boot.rs @@ -1,11 +1,17 @@ //! This module contains the prologue phase of the client program, pulling in the boot information //! through the `PreimageOracle` ABI as local keys. -use alloy_primitives::{B256, U256}; -use kona_preimage::{PreimageKey, PreimageOracleClient}; +use crate::{HintType, PreState}; +use alloc::{string::ToString, vec::Vec}; +use alloy_primitives::{Bytes, B256, U256}; +use alloy_rlp::Decodable; +use kona_preimage::{ + errors::PreimageOracleError, CommsClient, HintWriterClient, PreimageKey, PreimageKeyType, + PreimageOracleClient, +}; use kona_proof::errors::OracleProviderError; use maili_genesis::RollupConfig; -use maili_registry::ROLLUP_CONFIGS; +use maili_registry::{HashMap, ROLLUP_CONFIGS}; use serde::{Deserialize, Serialize}; use tracing::warn; @@ -21,9 +27,6 @@ pub const L2_CLAIMED_POST_STATE_KEY: U256 = U256::from_be_slice(&[3]); /// The local key ident for the L2 claim timestamp. pub const L2_CLAIMED_TIMESTAMP_KEY: U256 = U256::from_be_slice(&[4]); -/// The local key ident for the L2 chain ID. -pub const L2_CHAIN_ID_KEY: U256 = U256::from_be_slice(&[5]); - /// The local key ident for the L2 rollup config. pub const L2_ROLLUP_CONFIG_KEY: U256 = U256::from_be_slice(&[6]); @@ -33,15 +36,15 @@ pub struct BootInfo { /// The L1 head hash containing the safe L2 chain data that may reproduce the post-state claim. pub l1_head: B256, /// The agreed upon superchain pre-state commitment. - pub agreed_pre_state: B256, + pub agreed_pre_state_commitment: B256, + /// The agreed upon superchain pre-state. + pub agreed_pre_state: PreState, /// The claimed (disputed) superchain post-state commitment. pub claimed_post_state: B256, /// The L2 claim timestamp. pub claimed_l2_timestamp: u64, - /// The L2 chain ID. - pub chain_id: u64, /// The rollup config for the L2 chain. - pub rollup_config: RollupConfig, + pub rollup_configs: HashMap, } impl BootInfo { @@ -55,7 +58,7 @@ impl BootInfo { /// - `Err(_)`: Failed to load the boot information. pub async fn load(oracle: &O) -> Result where - O: PreimageOracleClient + Send, + O: PreimageOracleClient + HintWriterClient + Clone + Send, { let mut l1_head: B256 = B256::ZERO; oracle @@ -84,25 +87,29 @@ impl BootInfo { .try_into() .map_err(OracleProviderError::SliceConversion)?, ); - let chain_id = u64::from_be_bytes( - oracle - .get(PreimageKey::new_local(L2_CHAIN_ID_KEY.to())) - .await - .map_err(OracleProviderError::Preimage)? - .as_slice() - .try_into() - .map_err(OracleProviderError::SliceConversion)?, - ); + + let agreed_pre_state = + PreState::decode(&mut read_raw_pre_state(oracle, l2_pre).await?.as_ref()) + .map_err(OracleProviderError::Rlp)?; + + let chain_ids: Vec<_> = match agreed_pre_state { + PreState::SuperRoot(ref super_root) => { + super_root.output_roots.iter().map(|r| r.chain_id).collect() + } + PreState::TransitionState(ref transition_state) => { + transition_state.pre_state.output_roots.iter().map(|r| r.chain_id).collect() + } + }; // Attempt to load the rollup config from the chain ID. If there is no config for the chain, // fall back to loading the config from the preimage oracle. - let rollup_config = if let Some(config) = ROLLUP_CONFIGS.get(&chain_id) { - config.clone() + let rollup_configs = if chain_ids.iter().all(|id| ROLLUP_CONFIGS.contains_key(id)) { + chain_ids.iter().map(|id| (*id, ROLLUP_CONFIGS[id].clone())).collect() } else { warn!( target: "boot-loader", - "No rollup config found for chain ID {}, falling back to preimage oracle. This is insecure in production without additional validation!", - chain_id + "No rollup config found for chain IDs {:?}, falling back to preimage oracle. This is insecure in production without additional validation!", + chain_ids ); let ser_cfg = oracle .get(PreimageKey::new_local(L2_ROLLUP_CONFIG_KEY.to())) @@ -113,11 +120,37 @@ impl BootInfo { Ok(Self { l1_head, - agreed_pre_state: l2_pre, + rollup_configs, + agreed_pre_state_commitment: l2_pre, + agreed_pre_state, claimed_post_state: l2_post, claimed_l2_timestamp: l2_claim_block, - chain_id, - rollup_config, }) } } + +/// Reads the raw pre-state from the preimage oracle. +pub(crate) async fn read_raw_pre_state( + caching_oracle: &O, + agreed_pre_state_commitment: B256, +) -> Result +where + O: CommsClient, +{ + caching_oracle + .write(&HintType::AgreedPreState.encode_with(&[agreed_pre_state_commitment.as_ref()])) + .await + .map_err(OracleProviderError::Preimage)?; + let pre = caching_oracle + .get(PreimageKey::new(*agreed_pre_state_commitment, 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)) +} diff --git a/crates/proof-sdk/proof-interop/src/pre_state.rs b/crates/proof-sdk/proof-interop/src/pre_state.rs index 386fd5558..bcc920a42 100644 --- a/crates/proof-sdk/proof-interop/src/pre_state.rs +++ b/crates/proof-sdk/proof-interop/src/pre_state.rs @@ -4,6 +4,7 @@ use alloc::vec::Vec; use alloy_primitives::{b256, keccak256, Bytes, B256}; use alloy_rlp::{Buf, Decodable, Encodable, Header, RlpDecodable, RlpEncodable}; use kona_interop::{OutputRootWithChain, SuperRoot, SUPER_ROOT_VERSION}; +use serde::{Deserialize, Serialize}; /// The current [TransitionState] encoding format version. pub(crate) const TRANSITION_STATE_VERSION: u8 = 255; @@ -19,7 +20,7 @@ pub const INVALID_TRANSITION_HASH: B256 = /// [TransitionState]. The [SuperRoot] is the canonical state of the superchain, while the /// [TransitionState] is a super-structure of the [SuperRoot] that represents the progress of a /// pending superchain state transition from one [SuperRoot] to the next. -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] #[cfg_attr(any(feature = "arbitrary", test), derive(arbitrary::Arbitrary))] pub enum PreState { /// The canonical state of the superchain. @@ -36,6 +37,22 @@ impl PreState { keccak256(&rlp_buf) } + /// Returns the active L2 chain ID of the [PreState]. This is the chain ID of the output root + /// that is to be committed to in the next transition step, or `0xDEAD` if the [PreState] + /// has already been fully saturated. + pub fn active_l2_chain_id(&self) -> Option { + match self { + Self::SuperRoot(super_root) => { + super_root.output_roots.first().map(|output_root| output_root.chain_id) + } + Self::TransitionState(transition_state) => transition_state + .pre_state + .output_roots + .get(transition_state.step as usize) + .map(|output_root| output_root.chain_id), + } + } + /// Transitions to the next state, appending the [OptimisticBlock] to the pending progress. pub fn transition(self, optimistic_block: Option) -> Option { match self { @@ -117,7 +134,7 @@ impl Decodable for PreState { /// The [TransitionState] is a super-structure of the [SuperRoot] that represents the progress of a /// pending superchain state transition from one [SuperRoot] to the next. -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] #[cfg_attr(any(feature = "arbitrary", test), derive(arbitrary::Arbitrary))] pub struct TransitionState { /// The canonical pre-state super root commitment. @@ -204,7 +221,9 @@ impl Decodable for TransitionState { } /// A wrapper around a pending output root hash with the block hash it commits to. -#[derive(Default, Debug, Clone, Eq, PartialEq, RlpEncodable, RlpDecodable)] +#[derive( + Default, Debug, Clone, Eq, PartialEq, RlpEncodable, RlpDecodable, Serialize, Deserialize, +)] #[cfg_attr(any(feature = "arbitrary", test), derive(arbitrary::Arbitrary))] pub struct OptimisticBlock { /// The block hash of the output root. diff --git a/crates/proof-sdk/proof/src/l2/chain_provider.rs b/crates/proof-sdk/proof/src/l2/chain_provider.rs index 20c211db9..2185abbc6 100644 --- a/crates/proof-sdk/proof/src/l2/chain_provider.rs +++ b/crates/proof-sdk/proof/src/l2/chain_provider.rs @@ -23,7 +23,7 @@ pub struct OracleL2ChainProvider { /// The L2 safe head block hash. l2_head: B256, /// The rollup configuration. - rollup_config: RollupConfig, + rollup_config: Arc, /// The preimage oracle client. oracle: Arc, /// The derivation pipeline cursor @@ -32,7 +32,7 @@ pub struct OracleL2ChainProvider { impl OracleL2ChainProvider { /// Creates a new [OracleL2ChainProvider] with the given boot information and oracle client. - pub const fn new(l2_head: B256, rollup_config: RollupConfig, oracle: Arc) -> Self { + pub const fn new(l2_head: B256, rollup_config: Arc, oracle: Arc) -> Self { Self { l2_head, rollup_config, oracle, cursor: None } }