From eb4856d2bd9f4041b97de8a41cfdbe8267bd0bf3 Mon Sep 17 00:00:00 2001 From: clabby Date: Sun, 19 Jan 2025 12:41:40 -0500 Subject: [PATCH] feat(client): Interop consolidation sub-problem --- Cargo.lock | 5 + bin/client/src/interop/consolidate.rs | 50 +++++ bin/client/src/interop/mod.rs | 10 +- bin/client/src/interop/transition.rs | 2 +- bin/host/src/interop/fetcher.rs | 49 ++++- crates/interop/src/errors.rs | 25 +-- crates/interop/src/graph.rs | 11 +- crates/interop/src/lib.rs | 5 +- crates/interop/src/test_util.rs | 28 +-- crates/interop/src/traits.rs | 14 +- crates/proof-sdk/proof-interop/Cargo.toml | 7 + crates/proof-sdk/proof-interop/src/hint.rs | 4 + crates/proof-sdk/proof-interop/src/lib.rs | 3 + .../proof-sdk/proof-interop/src/provider.rs | 179 ++++++++++++++++++ crates/proof-sdk/proof/src/errors.rs | 5 +- .../proof-sdk/proof/src/l1/chain_provider.rs | 2 +- 16 files changed, 336 insertions(+), 63 deletions(-) create mode 100644 crates/proof-sdk/proof-interop/src/provider.rs diff --git a/Cargo.lock b/Cargo.lock index c84679a3..0823dd0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2577,14 +2577,19 @@ dependencies = [ name = "kona-proof-interop" version = "0.1.1" dependencies = [ + "alloy-consensus", + "alloy-eips", "alloy-primitives", "alloy-rlp", "arbitrary", + "async-trait", "kona-interop", + "kona-mpt", "kona-preimage", "kona-proof", "maili-genesis", "maili-registry", + "op-alloy-consensus", "rand", "serde", "serde_json", diff --git a/bin/client/src/interop/consolidate.rs b/bin/client/src/interop/consolidate.rs index 89571882..14cbe282 100644 --- a/bin/client/src/interop/consolidate.rs +++ b/bin/client/src/interop/consolidate.rs @@ -1 +1,51 @@ //! Consolidation phase of the interop proof program. + +use super::FaultProofProgramError; +use alloc::{sync::Arc, vec::Vec}; +use core::fmt::Debug; +use kona_interop::MessageGraph; +use kona_preimage::{HintWriterClient, PreimageOracleClient}; +use kona_proof::CachingOracle; +use kona_proof_interop::{OracleInteropProvider, PreState}; +use revm::primitives::HashMap; + +/// Executes the consolidation phase of the interop proof with the given [PreimageOracleClient] and +/// [HintWriterClient]. +/// +/// This phase is responsible for checking the dependencies between [OptimisticBlock]s in the +/// superchain and ensuring that all dependencies are satisfied. +/// +/// [OptimisticBlock]: kona_proof_interop::OptimisticBlock +pub(crate) async fn consolidate_dependencies( + oracle: Arc>, + pre: PreState, +) -> Result<(), FaultProofProgramError> +where + P: PreimageOracleClient + Send + Sync + Debug + Clone, + H: HintWriterClient + Send + Sync + Debug + Clone, +{ + let provider = OracleInteropProvider::new(oracle, pre.clone()); + + // Ensure that the pre-state is a transition state. + let PreState::TransitionState(transition_state) = pre else { + return Err(FaultProofProgramError::StateTransitionFailed); + }; + + let block_hashes = transition_state + .pending_progress + .iter() + .zip(transition_state.pre_state.output_roots) + .map(|(optimistic_block, pre_state)| (pre_state.chain_id, optimistic_block.block_hash)) + .collect::>(); + + let mut headers = Vec::with_capacity(block_hashes.len()); + for (chain_id, block_hash) in block_hashes { + let header = provider.header_by_hash(chain_id, block_hash).await?; + headers.push((chain_id, header.seal(block_hash))); + } + + let graph = MessageGraph::derive(headers.as_slice(), provider).await.unwrap(); + graph.resolve().await.unwrap(); + + Ok(()) +} diff --git a/bin/client/src/interop/mod.rs b/bin/client/src/interop/mod.rs index 369e9eef..18b9305b 100644 --- a/bin/client/src/interop/mod.rs +++ b/bin/client/src/interop/mod.rs @@ -3,6 +3,7 @@ use alloc::sync::Arc; use alloy_primitives::B256; use alloy_rlp::Decodable; +use consolidate::consolidate_dependencies; use core::fmt::Debug; use kona_driver::DriverError; use kona_executor::{ExecutorError, KonaHandleRegister}; @@ -11,6 +12,7 @@ use kona_proof::{errors::OracleProviderError, l2::OracleL2ChainProvider, Caching use kona_proof_interop::{BootInfo, PreState, INVALID_TRANSITION_HASH, TRANSITION_STATE_MAX_STEPS}; use thiserror::Error; use tracing::{error, info}; +use transition::sub_transition; use util::read_raw_pre_state; pub(crate) mod consolidate; @@ -66,7 +68,7 @@ where } }; - // If the pre state is invalid, short-circuit and check if the post-state is also invalid. + // 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 && boot.claimed_post_state == INVALID_TRANSITION_HASH { @@ -81,15 +83,15 @@ where match pre { PreState::SuperRoot(_) => { // If the pre-state is a super root, the first sub-problem is always selected. - transition::sub_transition(oracle, handle_register, boot, pre).await + sub_transition(oracle, handle_register, boot, pre).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 { - transition::sub_transition(oracle, handle_register, boot, pre).await + sub_transition(oracle, handle_register, boot, pre).await } else { - unimplemented!("Consolidation step") + consolidate_dependencies(oracle, pre).await } } } diff --git a/bin/client/src/interop/transition.rs b/bin/client/src/interop/transition.rs index c8ad0deb..e69bca7c 100644 --- a/bin/client/src/interop/transition.rs +++ b/bin/client/src/interop/transition.rs @@ -42,7 +42,7 @@ where if transition_state.step >= transition_state.pre_state.output_roots.len() as u64 { info!( target: "interop_client", - "No state transition required, transition state is already saturated." + "No derivation/execution required, transition state is already saturated." ); return transition_and_check(pre, None, boot.claimed_post_state); diff --git a/bin/host/src/interop/fetcher.rs b/bin/host/src/interop/fetcher.rs index 34020133..4db80f4e 100644 --- a/bin/host/src/interop/fetcher.rs +++ b/bin/host/src/interop/fetcher.rs @@ -256,19 +256,31 @@ where } HintType::L2BlockHeader => { // Validate the hint data length. - if hint_data.len() != 32 { + if hint_data.len() < 32 || hint_data.len() > 40 { anyhow::bail!("Invalid hint data length: {}", hint_data.len()); } // Fetch the raw header from the L2 chain provider. - let hash: B256 = hint_data + let hash: B256 = hint_data[0..32] .as_ref() .try_into() .map_err(|e| anyhow!("Failed to convert bytes to B256: {e}"))?; + + let active_l2_chain_id = if hint_data.len() == 40 { + u64::from_be_bytes( + hint_data[32..40] + .as_ref() + .try_into() + .map_err(|e| anyhow!("Failed to convert bytes to u64: {e}"))?, + ) + } else { + self.active_l2_chain_id + }; + let raw_header: Bytes = self .l2_providers - .get(&self.active_l2_chain_id) - .ok_or(anyhow!("No active L2 chain ID"))? + .get(&active_l2_chain_id) + .ok_or(anyhow!("No provider for active L2 chain ID"))? .client() .request("debug_getRawHeader", [hash]) .await @@ -322,6 +334,35 @@ where _ => anyhow::bail!("Only BlockTransactions::Hashes are supported."), }; } + HintType::L2Receipts => { + // Validate the hint data length. + if hint_data.len() != 40 { + anyhow::bail!("Invalid hint data length: {}", hint_data.len()); + } + + // Fetch the receipts from the L1 chain provider and store the receipts within the + // key-value store. + let hash: B256 = hint_data[0..32] + .as_ref() + .try_into() + .map_err(|e| anyhow!("Failed to convert bytes to B256: {e}"))?; + let chain_id = u64::from_be_bytes( + hint_data[32..40] + .as_ref() + .try_into() + .map_err(|e| anyhow!("Failed to convert bytes to u64: {e}"))?, + ); + + let raw_receipts: Vec = self + .l2_providers + .get(&chain_id) + .ok_or(anyhow!("Provider for chain ID {chain_id} not found"))? + .client() + .request("debug_getRawReceipts", [hash]) + .await + .map_err(|e| anyhow!(e))?; + self.store_trie_nodes(raw_receipts.as_slice()).await?; + } HintType::L2Code => { // geth hashdb scheme code hash key prefix const CODE_PREFIX: u8 = b'c'; diff --git a/crates/interop/src/errors.rs b/crates/interop/src/errors.rs index 3a78f073..05e1e9c7 100644 --- a/crates/interop/src/errors.rs +++ b/crates/interop/src/errors.rs @@ -1,5 +1,6 @@ //! Error types for the `kona-interop` crate. +use crate::InteropProvider; use alloc::vec::Vec; use alloy_primitives::{Address, B256}; use thiserror::Error; @@ -8,7 +9,7 @@ use thiserror::Error; /// /// [MessageGraph]: crate::MessageGraph #[derive(Debug, Clone, PartialEq, Eq, Error)] -pub enum MessageGraphError { +pub enum MessageGraphError { /// Dependency set is impossibly empty #[error("Dependency set is impossibly empty")] EmptyDependencySet, @@ -32,27 +33,13 @@ pub enum MessageGraphError { InvalidMessages(Vec), /// Interop provider error #[error("Interop provider: {0}")] - InteropProviderError(#[from] InteropProviderError), + InteropProviderError(#[from] E), } /// A [Result] alias for the [MessageGraphError] type. -pub type MessageGraphResult = core::result::Result; - -/// An error type for the [InteropProvider] trait. -/// -/// [InteropProvider]: crate::InteropProvider -#[derive(Debug, Clone, PartialEq, Eq, Error)] -pub enum InteropProviderError { - /// Unknown Chain ID - #[error("Unknown Chain ID")] - UnknownChainId, - /// Not found - #[error("Not found")] - NotFound, -} - -/// A [Result] alias for the [InteropProviderError] type. -pub type InteropProviderResult = core::result::Result; +#[allow(type_alias_bounds)] +pub type MessageGraphResult = + core::result::Result>; /// An error type for the [SuperRoot] struct's serialization and deserialization. /// diff --git a/crates/interop/src/graph.rs b/crates/interop/src/graph.rs index 97e0648b..b6868b54 100644 --- a/crates/interop/src/graph.rs +++ b/crates/interop/src/graph.rs @@ -47,7 +47,10 @@ where /// blocks and searching for [ExecutingMessage]s. /// /// [ExecutingMessage]: crate::ExecutingMessage - pub async fn derive(blocks: &[(u64, Sealed
)], provider: P) -> MessageGraphResult { + pub async fn derive( + blocks: &[(u64, Sealed
)], + provider: P, + ) -> MessageGraphResult { info!( target: "message-graph", "Deriving message graph from {} blocks.", @@ -84,7 +87,7 @@ where } /// Checks the validity of all messages within the graph. - pub async fn resolve(mut self) -> MessageGraphResult<()> { + pub async fn resolve(mut self) -> MessageGraphResult<(), P> { info!( target: "message-graph", "Checking the message graph for invalid messages." @@ -120,7 +123,7 @@ where /// Attempts to remove as many edges from the graph as possible by resolving the dependencies /// of each message. If a message cannot be resolved, it is considered invalid. After this /// function is called, any outstanding messages are invalid. - async fn reduce(&mut self) -> MessageGraphResult<()> { + async fn reduce(&mut self) -> MessageGraphResult<(), P> { // Create a new vector to store invalid edges let mut invalid_messages = Vec::with_capacity(self.messages.len()); @@ -155,7 +158,7 @@ where async fn check_single_dependency( &self, message: &EnrichedExecutingMessage, - ) -> MessageGraphResult<()> { + ) -> MessageGraphResult<(), P> { // ChainID Invariant: The chain id of the initiating message MUST be in the dependency set // This is enforced implicitly by the graph constructor and the provider. diff --git a/crates/interop/src/lib.rs b/crates/interop/src/lib.rs index be4da320..c5fada5f 100644 --- a/crates/interop/src/lib.rs +++ b/crates/interop/src/lib.rs @@ -25,10 +25,7 @@ mod traits; pub use traits::InteropProvider; mod errors; -pub use errors::{ - InteropProviderError, InteropProviderResult, MessageGraphError, MessageGraphResult, - SuperRootError, SuperRootResult, -}; +pub use errors::{MessageGraphError, MessageGraphResult, SuperRootError, SuperRootResult}; mod super_root; pub use super_root::{OutputRootWithChain, SuperRoot}; diff --git a/crates/interop/src/test_util.rs b/crates/interop/src/test_util.rs index ab6a1627..604c7337 100644 --- a/crates/interop/src/test_util.rs +++ b/crates/interop/src/test_util.rs @@ -2,10 +2,7 @@ #![allow(missing_docs, unreachable_pub)] -use crate::{ - errors::InteropProviderResult, traits::InteropProvider, ExecutingMessage, MessageIdentifier, - CROSS_L2_INBOX_ADDRESS, -}; +use crate::{traits::InteropProvider, ExecutingMessage, MessageIdentifier, CROSS_L2_INBOX_ADDRESS}; use alloy_consensus::{Header, Receipt, ReceiptWithBloom, Sealed}; use alloy_primitives::{map::HashMap, Address, Bytes, Log, LogData, B256, U256}; use alloy_sol_types::{SolEvent, SolValue}; @@ -27,10 +24,16 @@ impl MockInteropProvider { } } +#[derive(thiserror::Error, Debug, Eq, PartialEq)] +#[error("Mock interop provider error")] +pub struct InteropProviderError; + #[async_trait] impl InteropProvider for MockInteropProvider { + type Error = InteropProviderError; + /// Fetch a [Header] by its number. - async fn header_by_number(&self, chain_id: u64, number: u64) -> InteropProviderResult
{ + async fn header_by_number(&self, chain_id: u64, number: u64) -> Result { Ok(self .headers .get(&chain_id) @@ -40,23 +43,12 @@ impl InteropProvider for MockInteropProvider { .clone()) } - /// Fetch a [Header] by its hash. - async fn header_by_hash(&self, chain_id: u64, hash: B256) -> InteropProviderResult
{ - Ok(self - .headers - .get(&chain_id) - .and_then(|headers| headers.values().find(|header| header.hash() == hash)) - .unwrap() - .inner() - .clone()) - } - /// Fetch all receipts for a given block by number. async fn receipts_by_number( &self, chain_id: u64, number: u64, - ) -> InteropProviderResult> { + ) -> Result, Self::Error> { Ok(self.receipts.get(&chain_id).and_then(|receipts| receipts.get(&number)).unwrap().clone()) } @@ -65,7 +57,7 @@ impl InteropProvider for MockInteropProvider { &self, chain_id: u64, block_hash: B256, - ) -> InteropProviderResult> { + ) -> Result, Self::Error> { Ok(self .receipts .get(&chain_id) diff --git a/crates/interop/src/traits.rs b/crates/interop/src/traits.rs index dde157ae..b6d52355 100644 --- a/crates/interop/src/traits.rs +++ b/crates/interop/src/traits.rs @@ -1,33 +1,33 @@ //! Traits for the `kona-interop` crate. -use crate::errors::InteropProviderResult; use alloc::{boxed::Box, vec::Vec}; use alloy_consensus::Header; use alloy_primitives::B256; use async_trait::async_trait; +use core::{error::Error, fmt::Display}; use op_alloy_consensus::OpReceiptEnvelope; /// Describes the interface of the interop data provider. This provider is multiplexed over several /// chains, with each method consuming a chain ID to determine the target chain. #[async_trait] pub trait InteropProvider { - /// Fetch a [Header] by its number. - async fn header_by_number(&self, chain_id: u64, number: u64) -> InteropProviderResult
; + /// The error type for the provider. + type Error: Error + Display; - /// Fetch a [Header] by its hash. - async fn header_by_hash(&self, chain_id: u64, hash: B256) -> InteropProviderResult
; + /// Fetch a [Header] by its number. + async fn header_by_number(&self, chain_id: u64, number: u64) -> Result; /// Fetch all receipts for a given block by number. async fn receipts_by_number( &self, chain_id: u64, number: u64, - ) -> InteropProviderResult>; + ) -> Result, Self::Error>; /// Fetch all receipts for a given block by hash. async fn receipts_by_hash( &self, chain_id: u64, block_hash: B256, - ) -> InteropProviderResult>; + ) -> Result, Self::Error>; } diff --git a/crates/proof-sdk/proof-interop/Cargo.toml b/crates/proof-sdk/proof-interop/Cargo.toml index 88007279..393b772c 100644 --- a/crates/proof-sdk/proof-interop/Cargo.toml +++ b/crates/proof-sdk/proof-interop/Cargo.toml @@ -16,6 +16,7 @@ workspace = true kona-preimage.workspace = true kona-interop.workspace = true kona-proof.workspace = true +kona-mpt.workspace = true # Maili maili-registry.workspace = true @@ -24,11 +25,17 @@ maili-genesis = { workspace = true, features = ["serde"] } # Alloy alloy-rlp.workspace = true alloy-primitives.workspace = true +alloy-consensus.workspace = true +alloy-eips.workspace = true + +# OP Alloy +op-alloy-consensus.workspace = true # General serde.workspace = true tracing.workspace = true serde_json.workspace = true +async-trait.workspace = true # Arbitrary arbitrary = { version = "1.4", features = ["derive"], optional = true } diff --git a/crates/proof-sdk/proof-interop/src/hint.rs b/crates/proof-sdk/proof-interop/src/hint.rs index c1a1d43f..83886030 100644 --- a/crates/proof-sdk/proof-interop/src/hint.rs +++ b/crates/proof-sdk/proof-interop/src/hint.rs @@ -58,6 +58,8 @@ pub enum HintType { L2BlockHeader, /// A hint that specifies the transactions of a layer 2 block. L2Transactions, + /// A hint that specifies the receipts of a layer 2 block. + L2Receipts, /// A hint that specifies the code of a contract on layer 2. L2Code, /// A hint that specifies the preimage of the agreed upon pre-state claim. @@ -97,6 +99,7 @@ impl TryFrom<&str> for HintType { "l1-precompile" => Ok(Self::L1Precompile), "l2-block-header" => Ok(Self::L2BlockHeader), "l2-transactions" => Ok(Self::L2Transactions), + "l2-receipts" => Ok(Self::L2Receipts), "l2-code" => Ok(Self::L2Code), "agreed-pre-state" => Ok(Self::AgreedPreState), "l2-output-root" => Ok(Self::L2OutputRoot), @@ -119,6 +122,7 @@ impl From for &str { HintType::L1Precompile => "l1-precompile", HintType::L2BlockHeader => "l2-block-header", HintType::L2Transactions => "l2-transactions", + HintType::L2Receipts => "l2-receipts", HintType::L2Code => "l2-code", HintType::AgreedPreState => "agreed-pre-state", HintType::L2OutputRoot => "l2-output-root", diff --git a/crates/proof-sdk/proof-interop/src/lib.rs b/crates/proof-sdk/proof-interop/src/lib.rs index 8a3ec5c6..96e3d1a1 100644 --- a/crates/proof-sdk/proof-interop/src/lib.rs +++ b/crates/proof-sdk/proof-interop/src/lib.rs @@ -15,5 +15,8 @@ pub use pre_state::{ mod hint; pub use hint::{Hint, HintType}; +mod provider; +pub use provider::OracleInteropProvider; + pub mod boot; pub use boot::BootInfo; diff --git a/crates/proof-sdk/proof-interop/src/provider.rs b/crates/proof-sdk/proof-interop/src/provider.rs new file mode 100644 index 00000000..5f9e7a06 --- /dev/null +++ b/crates/proof-sdk/proof-interop/src/provider.rs @@ -0,0 +1,179 @@ +//! [InteropProvider] trait implementation using a [CommsClient] data source. + +use crate::{HintType, PreState}; +use alloc::{boxed::Box, sync::Arc, vec::Vec}; +use alloy_consensus::Header; +use alloy_eips::eip2718::Decodable2718; +use alloy_primitives::B256; +use alloy_rlp::Decodable; +use async_trait::async_trait; +use kona_interop::InteropProvider; +use kona_mpt::{OrderedListWalker, TrieNode, TrieProvider}; +use kona_preimage::{CommsClient, PreimageKey, PreimageKeyType}; +use kona_proof::errors::OracleProviderError; +use op_alloy_consensus::OpReceiptEnvelope; + +/// A [CommsClient] backed [InteropProvider] implementation. +#[derive(Debug, Clone)] +pub struct OracleInteropProvider { + /// The oracle client. + oracle: Arc, + /// The [PreState] for the current program execution. + pre_state: PreState, +} + +impl OracleInteropProvider +where + T: CommsClient + Send + Sync, +{ + /// Creates a new [OracleInteropProvider] with the given oracle client and [PreState]. + pub const fn new(oracle: Arc, pre_state: PreState) -> Self { + Self { oracle, pre_state } + } + + /// Fetch the [Header] for the block with the given hash. + pub async fn header_by_hash( + &self, + chain_id: u64, + block_hash: B256, + ) -> Result::Error> { + self.oracle + .write( + &HintType::L2BlockHeader + .encode_with(&[block_hash.as_slice(), chain_id.to_be_bytes().as_ref()]), + ) + .await + .map_err(OracleProviderError::Preimage)?; + + let header_rlp = self + .oracle + .get(PreimageKey::new(*block_hash, PreimageKeyType::Keccak256)) + .await + .map_err(OracleProviderError::Preimage)?; + + Header::decode(&mut header_rlp.as_ref()).map_err(OracleProviderError::Rlp) + } + + /// Fetch the [OpReceiptEnvelope]s for the block with the given hash. + async fn derive_receipts( + &self, + chain_id: u64, + block_hash: B256, + header: &Header, + ) -> Result, ::Error> { + // Send a hint for the block's receipts, and walk through the receipts trie in the header to + // verify them. + self.oracle + .write( + &HintType::L2Receipts + .encode_with(&[block_hash.as_ref(), chain_id.to_be_bytes().as_slice()]), + ) + .await + .map_err(OracleProviderError::Preimage)?; + let trie_walker = OrderedListWalker::try_new_hydrated(header.receipts_root, self) + .map_err(OracleProviderError::TrieWalker)?; + + // Decode the receipts within the receipts trie. + let receipts = trie_walker + .into_iter() + .map(|(_, rlp)| { + let envelope = OpReceiptEnvelope::decode_2718(&mut rlp.as_ref())?; + Ok(envelope) + }) + .collect::, _>>() + .map_err(OracleProviderError::Rlp)?; + + Ok(receipts) + } +} + +#[async_trait] +impl InteropProvider for OracleInteropProvider +where + T: CommsClient + Send + Sync, +{ + type Error = OracleProviderError; + + /// Fetch a [Header] by its number. + async fn header_by_number(&self, chain_id: u64, number: u64) -> Result { + // Find the safe head for the given chain ID. + // + // TODO: Deduplicate + cache safe head lookups. + let pre_state = match &self.pre_state { + PreState::SuperRoot(super_root) => super_root, + PreState::TransitionState(transition_state) => &transition_state.pre_state, + }; + let output = pre_state + .output_roots + .iter() + .find(|o| o.chain_id == chain_id) + .ok_or(OracleProviderError::UnknownChainId(chain_id))?; + self.oracle + .write(&HintType::L2OutputRoot.encode_with(&[ + output.output_root.as_slice(), + output.chain_id.to_be_bytes().as_slice(), + ])) + .await + .map_err(OracleProviderError::Preimage)?; + let output_preimage = self + .oracle + .get(PreimageKey::new(*output.output_root, PreimageKeyType::Keccak256)) + .await + .map_err(OracleProviderError::Preimage)?; + let safe_head_hash = + output_preimage[96..128].try_into().map_err(OracleProviderError::SliceConversion)?; + + // Fetch the starting block header. + let mut header = self.header_by_hash(chain_id, safe_head_hash).await?; + + // Check if the block number is in range. If not, we can fail early. + if number > header.number { + return Err(OracleProviderError::BlockNumberPastHead(number, header.number)); + } + + // Walk back the block headers to the desired block number. + while header.number > number { + header = self.header_by_hash(chain_id, header.parent_hash).await?; + } + + Ok(header) + } + + /// Fetch all receipts for a given block by number. + async fn receipts_by_number( + &self, + chain_id: u64, + number: u64, + ) -> Result, Self::Error> { + let header = self.header_by_number(chain_id, number).await?; + self.derive_receipts(chain_id, header.hash_slow(), &header).await + } + + /// Fetch all receipts for a given block by hash. + async fn receipts_by_hash( + &self, + chain_id: u64, + block_hash: B256, + ) -> Result, Self::Error> { + let header = self.header_by_hash(chain_id, block_hash).await?; + self.derive_receipts(chain_id, block_hash, &header).await + } +} + +impl TrieProvider for OracleInteropProvider +where + T: CommsClient + Send + Sync + Clone, +{ + type Error = OracleProviderError; + + fn trie_node_by_hash(&self, key: B256) -> Result { + kona_proof::block_on(async move { + let trie_node_rlp = self + .oracle + .get(PreimageKey::new(*key, PreimageKeyType::Keccak256)) + .await + .map_err(OracleProviderError::Preimage)?; + TrieNode::decode(&mut trie_node_rlp.as_ref()).map_err(OracleProviderError::Rlp) + }) + } +} diff --git a/crates/proof-sdk/proof/src/errors.rs b/crates/proof-sdk/proof/src/errors.rs index bc734ab8..3b013acf 100644 --- a/crates/proof-sdk/proof/src/errors.rs +++ b/crates/proof-sdk/proof/src/errors.rs @@ -15,7 +15,7 @@ pub enum OracleProviderError { BlockNumberPastHead(u64, u64), /// Preimage oracle error. #[error("Preimage oracle error: {0}")] - Preimage(PreimageOracleError), + Preimage(#[from] PreimageOracleError), /// List walker error. #[error("Trie walker error: {0}")] TrieWalker(OrderedListWalkerError), @@ -34,6 +34,9 @@ pub enum OracleProviderError { /// Serde error. #[error("Serde error: {0}")] Serde(serde_json::Error), + /// Unknown Chain ID + #[error("Unknown chain ID: {0}")] + UnknownChainId(u64), } impl From for PipelineErrorKind { diff --git a/crates/proof-sdk/proof/src/l1/chain_provider.rs b/crates/proof-sdk/proof/src/l1/chain_provider.rs index 15842ff8..f9166982 100644 --- a/crates/proof-sdk/proof/src/l1/chain_provider.rs +++ b/crates/proof-sdk/proof/src/l1/chain_provider.rs @@ -77,7 +77,7 @@ impl ChainProvider for OracleL1ChainProvider { let trie_walker = OrderedListWalker::try_new_hydrated(header.receipts_root, self) .map_err(OracleProviderError::TrieWalker)?; - // Decode the receipts within the transactions trie. + // Decode the receipts within the receipts trie. let receipts = trie_walker .into_iter() .map(|(_, rlp)| {