diff --git a/Cargo.lock b/Cargo.lock index 0630b18217..e9e892a96e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2708,6 +2708,7 @@ dependencies = [ "alloy-transport-http", "alloy-trie", "criterion", + "op-alloy-rpc-types-engine", "pprof", "proptest", "rand 0.9.0", diff --git a/bin/host/src/interop/handler.rs b/bin/host/src/interop/handler.rs index 5b00a212bf..3ab804b839 100644 --- a/bin/host/src/interop/handler.rs +++ b/bin/host/src/interop/handler.rs @@ -34,6 +34,7 @@ use maili_protocol::BlockInfo; use maili_registry::ROLLUP_CONFIGS; use std::sync::Arc; use tokio::task; +use tracing::warn; /// The [HintHandler] for the [InteropHost]. #[derive(Debug, Clone, Copy)] @@ -531,6 +532,12 @@ impl HintHandler for InteropHintHandler { // Store tx root preimages. store_ordered_trie(kv.as_ref(), raw_transactions.as_slice()).await?; } + HintType::L2PayloadWitness => { + warn!( + target: "interop_hint_handler", + "L2PayloadWitness hint not implemented for interop hint handler, ignoring hint" + ); + } } Ok(()) diff --git a/bin/host/src/single/cfg.rs b/bin/host/src/single/cfg.rs index 5cfc946ffe..105d397505 100644 --- a/bin/host/src/single/cfg.rs +++ b/bin/host/src/single/cfg.rs @@ -146,7 +146,8 @@ impl SingleChainHost { kv_store.clone(), providers, SingleChainHintHandler, - ); + ) + .with_proactive_hint(HintType::L2PayloadWitness); task::spawn( PreimageServer::new( diff --git a/bin/host/src/single/handler.rs b/bin/host/src/single/handler.rs index 80d0b873b5..6f7b0abfc5 100644 --- a/bin/host/src/single/handler.rs +++ b/bin/host/src/single/handler.rs @@ -20,6 +20,7 @@ use kona_proof::{Hint, HintType}; use maili_protocol::BlockInfo; use op_alloy_rpc_types_engine::OpPayloadAttributes; use std::collections::HashMap; +use tracing::warn; /// The [HintHandler] for the [SingleChainHost]. #[derive(Debug, Clone, Copy)] @@ -258,6 +259,12 @@ impl HintHandler for SingleChainHintHandler { let hash: B256 = hint.data.as_ref().try_into()?; + warn!(target: "single_hint_handler", "L2StateNode hint was sent for node hash: {}", hash); + warn!( + target: "single_hint_handler", + "`debug_executePayload` failed to return a complete witness." + ); + // Fetch the preimage from the L2 chain provider. let preimage: Bytes = providers.l2.client().request("debug_dbGet", &[hash]).await?; @@ -324,7 +331,7 @@ impl HintHandler for SingleChainHintHandler { let payload_attributes: OpPayloadAttributes = serde_json::from_slice(&hint.data[32..])?; - let execute_payload_response: ExecutionWitness = providers + let Ok(execute_payload_response) = providers .l2 .client() .request::<(B256, OpPayloadAttributes), ExecutionWitness>( @@ -332,7 +339,11 @@ impl HintHandler for SingleChainHintHandler { (parent_block_hash, payload_attributes), ) .await - .map_err(|e| anyhow!("Failed to fetch preimage: {e}"))?; + else { + // Allow this hint to fail silently, as not all execution clients support + // the `debug_executePayload` method. + return Ok(()); + }; let mut merged = HashMap::::default(); merged.extend(execute_payload_response.state); diff --git a/crates/proof/executor/src/db/mod.rs b/crates/proof/executor/src/db/mod.rs index 81d43ea586..a346da2b7b 100644 --- a/crates/proof/executor/src/db/mod.rs +++ b/crates/proof/executor/src/db/mod.rs @@ -86,9 +86,9 @@ where /// The parent block hash of the current block. parent_block_header: Sealed
, /// The [TrieDBProvider] - fetcher: F, + pub fetcher: F, /// The [TrieHinter] - hinter: H, + pub hinter: H, } impl TrieDB diff --git a/crates/proof/executor/src/executor/mod.rs b/crates/proof/executor/src/executor/mod.rs index 025de58e3f..2b28db86e2 100644 --- a/crates/proof/executor/src/executor/mod.rs +++ b/crates/proof/executor/src/executor/mod.rs @@ -10,7 +10,7 @@ use crate::{ }, ExecutorError, ExecutorResult, TrieDBProvider, }; -use alloc::vec::Vec; +use alloc::{string::ToString, vec::Vec}; use alloy_consensus::{ Header, Sealable, Sealed, Transaction, EMPTY_OMMER_ROOT_HASH, EMPTY_ROOT_HASH, }; @@ -137,6 +137,15 @@ where let parent_block_hash: B256 = self.trie_db.parent_block_header().seal(); + // Attempt to send a payload witness hint to the host. This hint instructs the host to + // populate its preimage store with the preimages required to statelessly execute + // this payload. This feature is experimental, so if the hint fails, we continue + // without it and fall back on on-demand preimage fetching for execution. + self.trie_db + .hinter + .hint_execution_witness(parent_block_hash, &payload) + .map_err(|e| TrieDBError::Provider(e.to_string()))?; + let mut state = State::builder().with_database(&mut self.trie_db).with_bundle_update().build(); diff --git a/crates/proof/mpt/Cargo.toml b/crates/proof/mpt/Cargo.toml index 8931b5e890..1209c77672 100644 --- a/crates/proof/mpt/Cargo.toml +++ b/crates/proof/mpt/Cargo.toml @@ -21,6 +21,9 @@ alloy-rlp.workspace = true alloy-trie.workspace = true alloy-primitives = { workspace = true, features = ["rlp"] } +# Op-alloy +op-alloy-rpc-types-engine.workspace = true + [dev-dependencies] # Alloy alloy-provider = { workspace = true, features = ["reqwest"] } diff --git a/crates/proof/mpt/src/noop.rs b/crates/proof/mpt/src/noop.rs index 33567fbac5..5fd6367962 100644 --- a/crates/proof/mpt/src/noop.rs +++ b/crates/proof/mpt/src/noop.rs @@ -40,4 +40,12 @@ impl TrieHinter for NoopTrieHinter { ) -> Result<(), Self::Error> { Ok(()) } + + fn hint_execution_witness( + &self, + _parent_hash: B256, + _op_payload_attributes: &op_alloy_rpc_types_engine::OpPayloadAttributes, + ) -> Result<(), Self::Error> { + Ok(()) + } } diff --git a/crates/proof/mpt/src/traits.rs b/crates/proof/mpt/src/traits.rs index 75736c36b0..b61f509749 100644 --- a/crates/proof/mpt/src/traits.rs +++ b/crates/proof/mpt/src/traits.rs @@ -5,6 +5,7 @@ use crate::TrieNode; use alloc::string::ToString; use alloy_primitives::{Address, B256, U256}; use core::fmt::Display; +use op_alloy_rpc_types_engine::OpPayloadAttributes; /// The [TrieProvider] trait defines the synchronous interface for fetching trie node preimages. pub trait TrieProvider { @@ -65,4 +66,20 @@ pub trait TrieHinter { slot: U256, block_number: u64, ) -> Result<(), Self::Error>; + + /// Hints the host to fetch the execution witness for the [OpPayloadAttributes] applied on top + /// of the parent block's state. + /// + /// ## Takes + /// - `parent_hash` - The hash of the parent block. + /// - `op_payload_attributes` - The attributes of the operation payload. + /// + /// ## Returns + /// - Ok(()): If the hint was successful. + /// - Err(Self::Error): If the hint was unsuccessful. + fn hint_execution_witness( + &self, + parent_hash: B256, + op_payload_attributes: &OpPayloadAttributes, + ) -> Result<(), Self::Error>; } diff --git a/crates/proof/proof-interop/src/hint.rs b/crates/proof/proof-interop/src/hint.rs index 0f3b9b09ec..e89aa30b07 100644 --- a/crates/proof/proof-interop/src/hint.rs +++ b/crates/proof/proof-interop/src/hint.rs @@ -39,6 +39,9 @@ pub enum HintType { L2AccountStorageProof, /// A hint that specifies loading the payload witness for an optimistic block. L2BlockData, + /// A hint that specifies bulk storage of all the code, state and keys generated by an + /// execution witness. + L2PayloadWitness, } impl HintType { @@ -74,6 +77,7 @@ impl FromStr for HintType { "l2-account-proof" => Ok(Self::L2AccountProof), "l2-account-storage-proof" => Ok(Self::L2AccountStorageProof), "l2-block-data" => Ok(Self::L2BlockData), + "l2-payload-witness" => Ok(Self::L2PayloadWitness), _ => Err(HintParsingError(value.to_string())), } } @@ -97,6 +101,7 @@ impl From for &str { HintType::L2AccountProof => "l2-account-proof", HintType::L2AccountStorageProof => "l2-account-storage-proof", HintType::L2BlockData => "l2-block-data", + HintType::L2PayloadWitness => "l2-payload-witness", } } } diff --git a/crates/proof/proof/src/l2/chain_provider.rs b/crates/proof/proof/src/l2/chain_provider.rs index 3756b9c85c..78bac798ec 100644 --- a/crates/proof/proof/src/l2/chain_provider.rs +++ b/crates/proof/proof/src/l2/chain_provider.rs @@ -242,4 +242,21 @@ impl TrieHinter for OracleL2ChainProvider { .await }) } + + fn hint_execution_witness( + &self, + parent_hash: B256, + op_payload_attributes: &op_alloy_rpc_types_engine::OpPayloadAttributes, + ) -> Result<(), Self::Error> { + crate::block_on(async move { + let encoded_attributes = + serde_json::to_vec(op_payload_attributes).map_err(OracleProviderError::Serde)?; + + HintType::L2PayloadWitness + .with_data(&[parent_hash.as_slice(), &encoded_attributes]) + .with_data(self.chain_id.map_or_else(Vec::new, |id| id.to_be_bytes().to_vec())) + .send(self.oracle.as_ref()) + .await + }) + } }