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/backend/online.rs b/bin/host/src/backend/online.rs index bf71bb78b3..584aad9b6b 100644 --- a/bin/host/src/backend/online.rs +++ b/bin/host/src/backend/online.rs @@ -7,7 +7,7 @@ use kona_preimage::{ errors::{PreimageOracleError, PreimageOracleResult}, HintRouter, PreimageFetcher, PreimageKey, }; -use kona_proof::{errors::HintParsingError, Hint}; +use kona_proof::{errors::HintParsingError, EncodableHint, Hint}; use std::{collections::HashSet, hash::Hash, str::FromStr, sync::Arc}; use tokio::sync::RwLock; use tracing::{debug, error, trace}; @@ -16,7 +16,7 @@ use tracing::{debug, error, trace}; /// [OnlineHostBackend]. pub trait OnlineHostBackendCfg { /// The hint type describing the range of hints that can be received. - type HintType: FromStr + Hash + Eq + PartialEq + Clone + Send + Sync; + type HintType: FromStr + EncodableHint + Hash + Eq + PartialEq + Clone + Send + Sync; /// The providers that are used to fetch data in response to hints. type Providers: Send + Sync; diff --git a/bin/host/src/single/cfg.rs b/bin/host/src/single/cfg.rs index 5cfc946ffe..4294ab01b6 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..54cdc0ee08 100644 --- a/bin/host/src/single/handler.rs +++ b/bin/host/src/single/handler.rs @@ -16,7 +16,7 @@ use alloy_rpc_types::{debug::ExecutionWitness, Block, BlockTransactionsKind}; use anyhow::{anyhow, ensure, Result}; use async_trait::async_trait; use kona_preimage::{PreimageKey, PreimageKeyType}; -use kona_proof::{Hint, HintType}; +use kona_proof::{Hint, HintType, PayloadWitnessHint}; use maili_protocol::BlockInfo; use op_alloy_rpc_types_engine::OpPayloadAttributes; use std::collections::HashMap; @@ -320,16 +320,15 @@ impl HintHandler for SingleChainHintHandler { HintType::L2PayloadWitness => { ensure!(hint.data.len() >= 32, "Invalid hint data length"); - let parent_block_hash = B256::from_slice(&hint.data.as_ref()[..32]); - let payload_attributes: OpPayloadAttributes = - serde_json::from_slice(&hint.data[32..])?; + let hint_payload: PayloadWitnessHint = + serde_json::from_slice(&hint.data)?; let execute_payload_response: ExecutionWitness = providers .l2 .client() .request::<(B256, OpPayloadAttributes), ExecutionWitness>( "debug_executePayload", - (parent_block_hash, payload_attributes), + (hint_payload.parent_block_hash, hint_payload.payload_attributes), ) .await .map_err(|e| anyhow!("Failed to fetch preimage: {e}"))?; diff --git a/crates/proof/executor/src/db/mod.rs b/crates/proof/executor/src/db/mod.rs index 81d43ea586..d3f908df63 100644 --- a/crates/proof/executor/src/db/mod.rs +++ b/crates/proof/executor/src/db/mod.rs @@ -88,7 +88,7 @@ where /// The [TrieDBProvider] 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..4c86d31342 100644 --- a/crates/proof/executor/src/executor/mod.rs +++ b/crates/proof/executor/src/executor/mod.rs @@ -79,6 +79,7 @@ where db: &mut TrieDB, block_number: u64, ) -> Result { + db.hinter.hint_account_proof(L2_TO_L1_BRIDGE, block_number).map_err(|_err| TrieDBError::MissingAccountInfo)?; match db.storage_roots().get(&L2_TO_L1_BRIDGE) { Some(storage_root) => Ok(storage_root.blind()), None => Ok(db @@ -137,6 +138,8 @@ where let parent_block_hash: B256 = self.trie_db.parent_block_header().seal(); + self.trie_db.hinter.hint_payload_execution(parent_block_hash, &payload).map_err(|_err| TrieDBError::MissingAccountInfo)?; + 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..e1dd3ddc19 100644 --- a/crates/proof/mpt/Cargo.toml +++ b/crates/proof/mpt/Cargo.toml @@ -20,6 +20,7 @@ serde = { workspace = true, optional = true, features = ["derive", "alloc"] } alloy-rlp.workspace = true alloy-trie.workspace = true alloy-primitives = { workspace = true, features = ["rlp"] } +op-alloy-rpc-types-engine.workspace = true [dev-dependencies] # Alloy diff --git a/crates/proof/mpt/src/noop.rs b/crates/proof/mpt/src/noop.rs index 33567fbac5..c73f1cb321 100644 --- a/crates/proof/mpt/src/noop.rs +++ b/crates/proof/mpt/src/noop.rs @@ -4,6 +4,7 @@ use crate::{TrieHinter, TrieNode, TrieProvider}; use alloc::string::String; use alloy_primitives::{Address, B256, U256}; +use op_alloy_rpc_types_engine::OpPayloadAttributes; /// The default, no-op implementation of the [TrieProvider] trait, used for testing. #[derive(Debug, Clone, Copy)] @@ -40,4 +41,12 @@ impl TrieHinter for NoopTrieHinter { ) -> Result<(), Self::Error> { Ok(()) } + + fn hint_payload_execution( + &self, + _parent_block_hash: B256, + _payload_attributes: &OpPayloadAttributes, + ) -> Result<(), Self::Error> { + Ok(()) + } } diff --git a/crates/proof/mpt/src/traits.rs b/crates/proof/mpt/src/traits.rs index 75736c36b0..91d4e0cd01 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 all trie nodes required for the execution of the given payload + /// attributes. + /// + /// ## Takes + /// - `parent_block_hash` - the block hash to build the payload on + /// - `payload_attributes` - payload data including transactions + /// + /// ## Returns + /// - Ok(()): If the hint was successful. + /// - Err(Self::Error): If the hint was unsuccessful. + fn hint_payload_execution( + &self, + parent_block_hash: B256, + 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..c76741aaab 100644 --- a/crates/proof/proof-interop/src/hint.rs +++ b/crates/proof/proof-interop/src/hint.rs @@ -2,7 +2,7 @@ use alloc::{string::ToString, vec::Vec}; use core::{fmt::Display, str::FromStr}; -use kona_proof::{errors::HintParsingError, Hint}; +use kona_proof::{errors::HintParsingError, EncodableHint, Hint, HintEncodingType}; /// The [HintType] enum is used to specify the type of hint that was received. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -107,3 +107,9 @@ impl Display for HintType { write!(f, "{}", s) } } + +impl EncodableHint for HintType { + fn encoding_type(&self) -> HintEncodingType { + HintEncodingType::Hex + } +} \ No newline at end of file diff --git a/crates/proof/proof/src/hint.rs b/crates/proof/proof/src/hint.rs index 8e134f2ee6..1712a92bd5 100644 --- a/crates/proof/proof/src/hint.rs +++ b/crates/proof/proof/src/hint.rs @@ -5,10 +5,41 @@ use alloc::{ string::{String, ToString}, vec::Vec, }; -use alloy_primitives::{hex, Bytes}; -use core::{fmt::Display, str::FromStr}; +use alloy_primitives::{hex, Bytes, B256}; +use op_alloy_rpc_types_engine::OpPayloadAttributes; +use serde::{Deserialize, Serialize}; +use core::{fmt::Display, str::FromStr, str::from_utf8}; use kona_preimage::HintWriterClient; +/// JSON-serialization of hint data for payload witness hints. +#[derive(Serialize, Deserialize, Debug)] +pub struct PayloadWitnessHint { + /// Block hash to build payload on. + pub parent_block_hash: B256, + + /// Payload attributes needed to build payload. + pub payload_attributes: OpPayloadAttributes, + + /// Chain ID of hint. + pub chain_id: Option, +} + +/// Type of encoding used for hint data +#[derive(Debug)] +pub enum HintEncodingType { + /// Data is encoded as a UTF-8 string (used for JSON encoding) + UTF8, + + /// Data is encoded as a hex string (used for binary encoding) + Hex, +} + +/// Allows fetching data encoding type for a specific hint type. +pub trait EncodableHint { + /// Get data encoding type for hint type. + fn encoding_type(&self) -> HintEncodingType; +} + /// A [Hint] is parsed in the format ` `, where `` is a string that /// represents the type of hint, and `` is the data associated with the hint (bytes /// encoded as hex UTF-8). @@ -22,7 +53,7 @@ pub struct Hint { impl Hint where - HT: Display, + HT: Display + EncodableHint, { /// Creates a new [Hint] with the specified type and data. pub fn new>(ty: HT, data: T) -> Self { @@ -55,26 +86,37 @@ where /// Encodes the hint as a string. pub fn encode(&self) -> String { - alloc::format!("{} {}", self.ty, self.data) + let encoded_data = match self.ty.encoding_type() { + HintEncodingType::Hex => self.data.to_string(), + HintEncodingType::UTF8 => from_utf8(&self.data).map_err(|e| { + info!("got error encoding {}: {}", self.data, e); + e + }).unwrap().to_string(), + }; + alloc::format!("{} {}", self.ty, encoded_data) } } impl FromStr for Hint where - HT: FromStr, + HT: FromStr + EncodableHint, { type Err = HintParsingError; fn from_str(s: &str) -> Result { - let mut parts = s.split(' ').collect::>(); + let parts = s + .split_once(' ') + .ok_or(HintParsingError(alloc::format!("Invalid hint format: {}", s)))?; - if parts.len() != 2 { - return Err(HintParsingError(alloc::format!("Invalid hint format: {}", s))); - } + let hint_string = parts.0.to_string(); + let hint_type = HT::from_str(&hint_string)?; - let hint_type = parts.remove(0).parse::()?; - let hint_data = - hex::decode(parts.remove(0)).map_err(|e| HintParsingError(e.to_string()))?.into(); + let data_string = parts.1.to_string(); + let hint_data = match hint_type.encoding_type() { + HintEncodingType::UTF8 => data_string.into(), + HintEncodingType::Hex => hex::decode(data_string).map_err(|e| HintParsingError(e.to_string()))?.into(), + }; + Ok(Self { ty: hint_type, data: hint_data }) } @@ -113,6 +155,15 @@ pub enum HintType { L2PayloadWitness, } +impl EncodableHint for HintType { + fn encoding_type(&self) -> HintEncodingType { + match *self { + HintType::L2PayloadWitness => HintEncodingType::UTF8, + _ => HintEncodingType::Hex, + } + } +} + impl HintType { /// Creates a new [Hint] from `self` and the specified data. The data passed will be /// concatenated into a single byte array before being stored in the resulting [Hint]. diff --git a/crates/proof/proof/src/l2/chain_provider.rs b/crates/proof/proof/src/l2/chain_provider.rs index 3756b9c85c..13ae4eb403 100644 --- a/crates/proof/proof/src/l2/chain_provider.rs +++ b/crates/proof/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, HintType}; +use crate::{errors::OracleProviderError, HintType, PayloadWitnessHint}; use alloc::{boxed::Box, sync::Arc, vec::Vec}; use alloy_consensus::{BlockBody, Header}; use alloy_eips::eip2718::Decodable2718; @@ -15,6 +15,7 @@ use kona_preimage::{CommsClient, PreimageKey, PreimageKeyType}; use maili_genesis::{RollupConfig, SystemConfig}; use maili_protocol::{to_system_config, BatchValidationProvider, L2BlockInfo}; use op_alloy_consensus::{OpBlock, OpTxEnvelope}; +use op_alloy_rpc_types_engine::OpPayloadAttributes; use spin::RwLock; /// The oracle-backed L2 chain provider for the client program. @@ -242,4 +243,25 @@ impl TrieHinter for OracleL2ChainProvider { .await }) } + + fn hint_payload_execution( + &self, + parent_block_hash: B256, + payload_attributes: &OpPayloadAttributes, + ) -> Result<(), Self::Error> { + crate::block_on(async move { + HintType::L2PayloadWitness + .with_data(&[ + &serde_json::to_vec(&PayloadWitnessHint{ + parent_block_hash: parent_block_hash, + payload_attributes: payload_attributes.clone(), + chain_id: None, + }).map_err(OracleProviderError::Serde)? + + ]) + .with_data(self.chain_id.map_or_else(Vec::new, |id| id.to_be_bytes().to_vec())) + .send(self.oracle.as_ref()) + .await + }) + } } diff --git a/crates/proof/proof/src/lib.rs b/crates/proof/proof/src/lib.rs index b3bfa1796c..5668a5e51e 100644 --- a/crates/proof/proof/src/lib.rs +++ b/crates/proof/proof/src/lib.rs @@ -21,7 +21,7 @@ pub mod errors; pub mod executor; mod hint; -pub use hint::{Hint, HintType}; +pub use hint::{Hint, HintType, HintEncodingType, EncodableHint, PayloadWitnessHint}; pub mod boot; pub use boot::BootInfo;