diff --git a/Cargo.lock b/Cargo.lock index 65825e254e4..8972d77a4c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9616,7 +9616,10 @@ dependencies = [ "reth-codecs", "reth-db", "reth-db-api", + "reth-execution-errors", "reth-primitives-traits", + "reth-provider", + "reth-revm", "reth-trie", "serde", "test-case", diff --git a/crates/optimism/trie/Cargo.toml b/crates/optimism/trie/Cargo.toml index 6f828b35367..82543cdc6cb 100644 --- a/crates/optimism/trie/Cargo.toml +++ b/crates/optimism/trie/Cargo.toml @@ -14,8 +14,12 @@ workspace = true [dependencies] # reth reth-trie = { workspace = true, features = ["serde"] } -reth-primitives-traits = { workspace = true, features = ["op"] } -reth-db = { workspace = true, features = ["mdbx", "op"] } +reth-primitives-traits = { workspace = true } +reth-db = { workspace = true, features = ["mdbx"] } +reth-revm = { workspace = true } +reth-provider = { workspace = true } +reth-db-api = { workspace = true } +reth-execution-errors = { workspace = true } # ethereum alloy-primitives.workspace = true diff --git a/crates/optimism/trie/src/api.rs b/crates/optimism/trie/src/api.rs index b0392f6e3f2..89e734bf6c1 100644 --- a/crates/optimism/trie/src/api.rs +++ b/crates/optimism/trie/src/api.rs @@ -52,6 +52,11 @@ pub trait OpProofsHashedCursor: Send + Sync { /// Move the cursor to the next entry and return it. fn next(&mut self) -> OpProofsStorageResult>; + + /// Returns `true` if there are no entries for a given key. + fn is_storage_empty(&mut self) -> OpProofsStorageResult { + Ok(self.seek(B256::ZERO)?.is_none()) + } } /// Diff of trie updates and post state for a block. diff --git a/crates/optimism/trie/src/lib.rs b/crates/optimism/trie/src/lib.rs index bee84060218..a7a4bcf57be 100644 --- a/crates/optimism/trie/src/lib.rs +++ b/crates/optimism/trie/src/lib.rs @@ -27,3 +27,7 @@ pub use in_memory::{ }; pub mod db; + +pub mod proof; + +pub mod provider; diff --git a/crates/optimism/trie/src/proof.rs b/crates/optimism/trie/src/proof.rs new file mode 100644 index 00000000000..7fd8d52f9e7 --- /dev/null +++ b/crates/optimism/trie/src/proof.rs @@ -0,0 +1,563 @@ +//! Provides proof operation implementations for [`OpProofsStorage`]. + +use crate::api::{ + OpProofsHashedCursor, OpProofsStorage, OpProofsStorageError, + OpProofsTrieCursor as OpProofsDBTrieCursor, +}; +use alloy_primitives::{ + keccak256, + map::{B256Map, HashMap}, + Address, Bytes, B256, U256, +}; +use derive_more::Constructor; +use reth_db_api::DatabaseError; +use reth_execution_errors::{StateProofError, StateRootError, StorageRootError, TrieWitnessError}; +use reth_primitives_traits::Account; +use reth_trie::{ + hashed_cursor::{ + HashedCursor, HashedCursorFactory, HashedPostStateCursorFactory, HashedStorageCursor, + }, + metrics::TrieRootMetrics, + proof::{Proof, StorageProof}, + trie_cursor::{InMemoryTrieCursorFactory, TrieCursor, TrieCursorFactory}, + updates::TrieUpdates, + witness::TrieWitness, + AccountProof, BranchNodeCompact, HashedPostState, HashedPostStateSorted, HashedStorage, + MultiProof, MultiProofTargets, Nibbles, StateRoot, StorageMultiProof, StorageRoot, TrieInput, + TrieType, +}; + +/// Manages reading storage or account trie nodes from [`OpProofsDBTrieCursor`]. +#[derive(Debug, Clone)] +pub struct OpProofsTrieCursor(pub C); + +impl OpProofsTrieCursor { + /// Creates a new `OpProofsTrieCursor` instance. + pub const fn new(cursor: C) -> Self { + Self(cursor) + } +} + +impl From for DatabaseError { + fn from(error: OpProofsStorageError) -> Self { + Self::Other(error.to_string()) + } +} + +impl TrieCursor for OpProofsTrieCursor +where + C: OpProofsDBTrieCursor + Send + Sync, +{ + fn seek_exact( + &mut self, + key: Nibbles, + ) -> Result, DatabaseError> { + Ok(self.0.seek_exact(key)?) + } + + fn seek( + &mut self, + key: Nibbles, + ) -> Result, DatabaseError> { + Ok(self.0.seek(key)?) + } + + fn next(&mut self) -> Result, DatabaseError> { + Ok(self.0.next()?) + } + + fn current(&mut self) -> Result, DatabaseError> { + Ok(self.0.current()?) + } +} + +/// Factory for creating trie cursors for [`OpProofsStorage`]. +#[derive(Debug, Clone, Constructor)] +pub struct OpProofsTrieCursorFactory { + storage: Storage, + block_number: u64, +} + +impl TrieCursorFactory for OpProofsTrieCursorFactory { + type AccountTrieCursor = OpProofsTrieCursor; + type StorageTrieCursor = OpProofsTrieCursor; + + fn account_trie_cursor(&self) -> Result { + Ok(OpProofsTrieCursor::new( + self.storage + .account_trie_cursor(self.block_number) + .map_err(Into::::into)?, + )) + } + + fn storage_trie_cursor( + &self, + hashed_address: B256, + ) -> Result { + Ok(OpProofsTrieCursor::new( + self.storage + .storage_trie_cursor(hashed_address, self.block_number) + .map_err(Into::::into)?, + )) + } +} + +/// Manages reading hashed account nodes from external storage. +#[derive(Debug, Clone)] +pub struct OpProofsHashedAccountCursor(pub C); + +impl OpProofsHashedAccountCursor { + /// Creates a new `OpProofsHashedAccountCursor` instance. + pub const fn new(cursor: C) -> Self { + Self(cursor) + } +} + +impl + Send + Sync> HashedCursor + for OpProofsHashedAccountCursor +{ + type Value = Account; + + fn seek(&mut self, key: B256) -> Result, DatabaseError> { + Ok(self.0.seek(key)?) + } + + fn next(&mut self) -> Result, DatabaseError> { + Ok(self.0.next()?) + } +} + +/// Manages reading hashed storage nodes from [`OpProofsHashedCursor`]. +#[derive(Debug, Clone)] +pub struct OpProofsHashedStorageCursor>(pub C); + +impl> OpProofsHashedStorageCursor { + /// Creates a new `OpProofsHashedStorageCursor` instance. + pub const fn new(cursor: C) -> Self { + Self(cursor) + } +} + +impl + Send + Sync> HashedCursor + for OpProofsHashedStorageCursor +{ + type Value = U256; + + fn seek(&mut self, key: B256) -> Result, DatabaseError> { + Ok(self.0.seek(key)?) + } + + fn next(&mut self) -> Result, DatabaseError> { + Ok(self.0.next()?) + } +} + +impl + Send + Sync> HashedStorageCursor + for OpProofsHashedStorageCursor +{ + fn is_storage_empty(&mut self) -> Result { + Ok(self.0.is_storage_empty()?) + } +} + +/// Factory for creating hashed account cursors for [`OpProofsStorage`]. +#[derive(Debug, Clone)] +pub struct OpProofsHashedAccountCursorFactory { + storage: Storage, + block_number: u64, +} + +impl OpProofsHashedAccountCursorFactory { + /// Creates a new `OpProofsHashedAccountCursorFactory` instance. + pub const fn new(storage: Storage, block_number: u64) -> Self { + Self { storage, block_number } + } +} + +impl HashedCursorFactory for OpProofsHashedAccountCursorFactory { + type AccountCursor = OpProofsHashedAccountCursor; + type StorageCursor = OpProofsHashedStorageCursor; + + fn hashed_account_cursor(&self) -> Result { + Ok(OpProofsHashedAccountCursor::new(self.storage.account_hashed_cursor(self.block_number)?)) + } + + fn hashed_storage_cursor( + &self, + hashed_address: B256, + ) -> Result { + Ok(OpProofsHashedStorageCursor::new( + self.storage.storage_hashed_cursor(hashed_address, self.block_number)?, + )) + } +} + +/// Extends [`Proof`] with operations specific for working with [`OpProofsStorage`]. +pub trait DatabaseProof { + /// Creates a new `DatabaseProof` instance from external storage. + fn from_tx(storage: Storage, block_number: u64) -> Self; + + /// Generates the state proof for target account based on [`TrieInput`]. + fn overlay_account_proof( + storage: Storage, + block_number: u64, + input: TrieInput, + address: Address, + slots: &[B256], + ) -> Result; + + /// Generates the state [`MultiProof`] for target hashed account and storage keys. + fn overlay_multiproof( + storage: Storage, + block_number: u64, + input: TrieInput, + targets: MultiProofTargets, + ) -> Result; +} + +impl DatabaseProof + for Proof, OpProofsHashedAccountCursorFactory> +{ + /// Create a new [`Proof`] instance from [`OpProofsStorage`]. + fn from_tx(storage: Storage, block_number: u64) -> Self { + Self::new( + OpProofsTrieCursorFactory::new(storage.clone(), block_number), + OpProofsHashedAccountCursorFactory::new(storage, block_number), + ) + } + + /// Generates the state proof for target account based on [`TrieInput`]. + fn overlay_account_proof( + storage: Storage, + block_number: u64, + input: TrieInput, + address: Address, + slots: &[B256], + ) -> Result { + let nodes_sorted = input.nodes.into_sorted(); + let state_sorted = input.state.into_sorted(); + Self::from_tx(storage.clone(), block_number) + .with_trie_cursor_factory(InMemoryTrieCursorFactory::new( + OpProofsTrieCursorFactory::new(storage.clone(), block_number), + &nodes_sorted, + )) + .with_hashed_cursor_factory(HashedPostStateCursorFactory::new( + OpProofsHashedAccountCursorFactory::new(storage, block_number), + &state_sorted, + )) + .with_prefix_sets_mut(input.prefix_sets) + .account_proof(address, slots) + } + + /// Generates the state [`MultiProof`] for target hashed account and storage keys. + fn overlay_multiproof( + storage: Storage, + block_number: u64, + input: TrieInput, + targets: MultiProofTargets, + ) -> Result { + let nodes_sorted = input.nodes.into_sorted(); + let state_sorted = input.state.into_sorted(); + Self::from_tx(storage.clone(), block_number) + .with_trie_cursor_factory(InMemoryTrieCursorFactory::new( + OpProofsTrieCursorFactory::new(storage.clone(), block_number), + &nodes_sorted, + )) + .with_hashed_cursor_factory(HashedPostStateCursorFactory::new( + OpProofsHashedAccountCursorFactory::new(storage, block_number), + &state_sorted, + )) + .with_prefix_sets_mut(input.prefix_sets) + .multiproof(targets) + } +} + +/// Extends [`StorageProof`] with operations specific for working with [`OpProofsStorage`]. +pub trait DatabaseStorageProof { + /// Create a new [`StorageProof`] from [`OpProofsStorage`] and account address. + fn from_tx(storage: Storage, block_number: u64, address: Address) -> Self; + + /// Generates the storage proof for target slot based on [`TrieInput`]. + fn overlay_storage_proof( + storage: Storage, + block_number: u64, + address: Address, + slot: B256, + storage: HashedStorage, + ) -> Result; + + /// Generates the storage multiproof for target slots based on [`TrieInput`]. + fn overlay_storage_multiproof( + storage: Storage, + block_number: u64, + address: Address, + slots: &[B256], + storage: HashedStorage, + ) -> Result; +} + +impl DatabaseStorageProof + for StorageProof< + OpProofsTrieCursorFactory, + OpProofsHashedAccountCursorFactory, + > +{ + /// Create a new [`StorageProof`] from [`OpProofsStorage`] and account address. + fn from_tx(storage: Storage, block_number: u64, address: Address) -> Self { + Self::new( + OpProofsTrieCursorFactory::new(storage.clone(), block_number), + OpProofsHashedAccountCursorFactory::new(storage, block_number), + address, + ) + } + + fn overlay_storage_proof( + storage: Storage, + block_number: u64, + address: Address, + slot: B256, + hashed_storage: HashedStorage, + ) -> Result { + let hashed_address = keccak256(address); + let prefix_set = hashed_storage.construct_prefix_set(); + let state_sorted = HashedPostStateSorted::new( + Default::default(), + HashMap::from_iter([(hashed_address, hashed_storage.into_sorted())]), + ); + Self::from_tx(storage.clone(), block_number, address) + .with_hashed_cursor_factory(HashedPostStateCursorFactory::new( + OpProofsHashedAccountCursorFactory::new(storage, block_number), + &state_sorted, + )) + .with_prefix_set_mut(prefix_set) + .storage_proof(slot) + } + + fn overlay_storage_multiproof( + storage: Storage, + block_number: u64, + address: Address, + slots: &[B256], + hashed_storage: HashedStorage, + ) -> Result { + let hashed_address = keccak256(address); + let targets = slots.iter().map(keccak256).collect(); + let prefix_set = hashed_storage.construct_prefix_set(); + let state_sorted = HashedPostStateSorted::new( + Default::default(), + HashMap::from_iter([(hashed_address, hashed_storage.into_sorted())]), + ); + Self::from_tx(storage.clone(), block_number, address) + .with_hashed_cursor_factory(HashedPostStateCursorFactory::new( + OpProofsHashedAccountCursorFactory::new(storage, block_number), + &state_sorted, + )) + .with_prefix_set_mut(prefix_set) + .storage_multiproof(targets) + } +} + +/// Extends [`StateRoot`] with operations specific for working with [`OpProofsStorage`]. +pub trait DatabaseStateRoot: Sized { + /// Calculate the state root for this [`HashedPostState`]. + /// Internally, this method retrieves prefixsets and uses them + /// to calculate incremental state root. + /// + /// # Returns + /// + /// The state root for this [`HashedPostState`]. + fn overlay_root( + storage: Storage, + block_number: u64, + post_state: HashedPostState, + ) -> Result; + + /// Calculates the state root for this [`HashedPostState`] and returns it alongside trie + /// updates. See [`Self::overlay_root`] for more info. + fn overlay_root_with_updates( + storage: Storage, + block_number: u64, + post_state: HashedPostState, + ) -> Result<(B256, TrieUpdates), StateRootError>; + + /// Calculates the state root for provided [`HashedPostState`] using cached intermediate nodes. + fn overlay_root_from_nodes( + storage: Storage, + block_number: u64, + input: TrieInput, + ) -> Result; + + /// Calculates the state root and trie updates for provided [`HashedPostState`] using + /// cached intermediate nodes. + fn overlay_root_from_nodes_with_updates( + storage: Storage, + block_number: u64, + input: TrieInput, + ) -> Result<(B256, TrieUpdates), StateRootError>; +} + +impl DatabaseStateRoot + for StateRoot, OpProofsHashedAccountCursorFactory> +{ + fn overlay_root( + storage: Storage, + block_number: u64, + post_state: HashedPostState, + ) -> Result { + let prefix_sets = post_state.construct_prefix_sets().freeze(); + let state_sorted = post_state.into_sorted(); + StateRoot::new( + OpProofsTrieCursorFactory::new(storage.clone(), block_number), + HashedPostStateCursorFactory::new( + OpProofsHashedAccountCursorFactory::new(storage, block_number), + &state_sorted, + ), + ) + .with_prefix_sets(prefix_sets) + .root() + } + + fn overlay_root_with_updates( + storage: Storage, + block_number: u64, + post_state: HashedPostState, + ) -> Result<(B256, TrieUpdates), StateRootError> { + let prefix_sets = post_state.construct_prefix_sets().freeze(); + let state_sorted = post_state.into_sorted(); + StateRoot::new( + OpProofsTrieCursorFactory::new(storage.clone(), block_number), + HashedPostStateCursorFactory::new( + OpProofsHashedAccountCursorFactory::new(storage, block_number), + &state_sorted, + ), + ) + .with_prefix_sets(prefix_sets) + .root_with_updates() + } + + fn overlay_root_from_nodes( + storage: Storage, + block_number: u64, + input: TrieInput, + ) -> Result { + let state_sorted = input.state.into_sorted(); + let nodes_sorted = input.nodes.into_sorted(); + StateRoot::new( + InMemoryTrieCursorFactory::new( + OpProofsTrieCursorFactory::new(storage.clone(), block_number), + &nodes_sorted, + ), + HashedPostStateCursorFactory::new( + OpProofsHashedAccountCursorFactory::new(storage, block_number), + &state_sorted, + ), + ) + .with_prefix_sets(input.prefix_sets.freeze()) + .root() + } + + fn overlay_root_from_nodes_with_updates( + storage: Storage, + block_number: u64, + input: TrieInput, + ) -> Result<(B256, TrieUpdates), StateRootError> { + let state_sorted = input.state.into_sorted(); + let nodes_sorted = input.nodes.into_sorted(); + StateRoot::new( + InMemoryTrieCursorFactory::new( + OpProofsTrieCursorFactory::new(storage.clone(), block_number), + &nodes_sorted, + ), + HashedPostStateCursorFactory::new( + OpProofsHashedAccountCursorFactory::new(storage, block_number), + &state_sorted, + ), + ) + .with_prefix_sets(input.prefix_sets.freeze()) + .root_with_updates() + } +} + +/// Extends [`StorageRoot`] with operations specific for working with [`OpProofsStorage`]. +pub trait DatabaseStorageRoot { + /// Calculates the storage root for provided [`HashedStorage`]. + fn overlay_root( + storage: Storage, + block_number: u64, + address: Address, + hashed_storage: HashedStorage, + ) -> Result; +} + +impl DatabaseStorageRoot + for StorageRoot, OpProofsHashedAccountCursorFactory> +{ + fn overlay_root( + storage: Storage, + block_number: u64, + address: Address, + hashed_storage: HashedStorage, + ) -> Result { + let prefix_set = hashed_storage.construct_prefix_set().freeze(); + let state_sorted = + HashedPostState::from_hashed_storage(keccak256(address), hashed_storage).into_sorted(); + StorageRoot::new( + OpProofsTrieCursorFactory::new(storage.clone(), block_number), + HashedPostStateCursorFactory::new( + OpProofsHashedAccountCursorFactory::new(storage, block_number), + &state_sorted, + ), + address, + prefix_set, + TrieRootMetrics::new(TrieType::Storage), + ) + .root() + } +} + +/// Extends [`TrieWitness`] with operations specific for working with [`OpProofsStorage`]. +pub trait DatabaseTrieWitness { + /// Creates a new [`TrieWitness`] instance from [`OpProofsStorage`]. + fn from_tx(storage: Storage, block_number: u64) -> Self; + + /// Generates the trie witness for the target state based on [`TrieInput`]. + fn overlay_witness( + storage: Storage, + block_number: u64, + input: TrieInput, + target: HashedPostState, + ) -> Result, TrieWitnessError>; +} + +impl DatabaseTrieWitness + for TrieWitness, OpProofsHashedAccountCursorFactory> +{ + fn from_tx(storage: Storage, block_number: u64) -> Self { + Self::new( + OpProofsTrieCursorFactory::new(storage.clone(), block_number), + OpProofsHashedAccountCursorFactory::new(storage, block_number), + ) + } + + fn overlay_witness( + storage: Storage, + block_number: u64, + input: TrieInput, + target: HashedPostState, + ) -> Result, TrieWitnessError> { + let nodes_sorted = input.nodes.into_sorted(); + let state_sorted = input.state.into_sorted(); + Self::from_tx(storage.clone(), block_number) + .with_trie_cursor_factory(InMemoryTrieCursorFactory::new( + OpProofsTrieCursorFactory::new(storage.clone(), block_number), + &nodes_sorted, + )) + .with_hashed_cursor_factory(HashedPostStateCursorFactory::new( + OpProofsHashedAccountCursorFactory::new(storage, block_number), + &state_sorted, + )) + .with_prefix_sets_mut(input.prefix_sets) + .always_include_root_node() + .compute(target) + } +} diff --git a/crates/optimism/trie/src/provider.rs b/crates/optimism/trie/src/provider.rs new file mode 100644 index 00000000000..f793cf359c7 --- /dev/null +++ b/crates/optimism/trie/src/provider.rs @@ -0,0 +1,220 @@ +//! Provider for external proofs storage + +use crate::{ + api::{OpProofsHashedCursor, OpProofsStorage, OpProofsStorageError}, + proof::{ + DatabaseProof, DatabaseStateRoot, DatabaseStorageProof, DatabaseStorageRoot, + DatabaseTrieWitness, + }, +}; +use alloy_primitives::keccak256; +use derive_more::Constructor; +use reth_primitives_traits::{Account, Bytecode}; +use reth_provider::{ + AccountReader, BlockHashReader, BytecodeReader, HashedPostStateProvider, ProviderError, + ProviderResult, StateProofProvider, StateProvider, StateRootProvider, StorageRootProvider, +}; +use reth_revm::{ + db::BundleState, + primitives::{alloy_primitives::BlockNumber, Address, Bytes, StorageValue, B256}, +}; +use reth_trie::{ + proof::{Proof, StorageProof}, + updates::TrieUpdates, + witness::TrieWitness, + AccountProof, HashedPostState, HashedStorage, KeccakKeyHasher, MultiProof, MultiProofTargets, + StateRoot, StorageMultiProof, StorageRoot, TrieInput, +}; +use std::fmt::Debug; + +/// State provider for external proofs storage. +#[derive(Constructor)] +pub struct OpProofsStateProviderRef<'a, Storage: OpProofsStorage> { + /// Historical state provider for non-state related tasks. + latest: Box, + + /// Storage provider for state lookups. + storage: Storage, + + /// Max block number that can be used for state lookups. + block_number: BlockNumber, +} + +impl<'a, Storage> Debug for OpProofsStateProviderRef<'a, Storage> +where + Storage: OpProofsStorage + Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("OpProofsStateProviderRef") + .field("storage", &self.storage) + .field("block_number", &self.block_number) + .finish() + } +} + +impl From for ProviderError { + fn from(error: OpProofsStorageError) -> Self { + Self::other(error) + } +} + +impl<'a, Storage: OpProofsStorage> BlockHashReader for OpProofsStateProviderRef<'a, Storage> { + fn block_hash(&self, number: BlockNumber) -> ProviderResult> { + self.latest.block_hash(number) + } + + fn canonical_hashes_range( + &self, + start: BlockNumber, + end: BlockNumber, + ) -> ProviderResult> { + self.latest.canonical_hashes_range(start, end) + } +} + +impl<'a, Storage: OpProofsStorage + Clone> StateRootProvider + for OpProofsStateProviderRef<'a, Storage> +{ + fn state_root(&self, state: HashedPostState) -> ProviderResult { + StateRoot::overlay_root(self.storage.clone(), self.block_number, state) + .map_err(|err| ProviderError::Database(err.into())) + } + + fn state_root_from_nodes(&self, input: TrieInput) -> ProviderResult { + StateRoot::overlay_root_from_nodes(self.storage.clone(), self.block_number, input) + .map_err(|err| ProviderError::Database(err.into())) + } + + fn state_root_with_updates( + &self, + state: HashedPostState, + ) -> ProviderResult<(B256, TrieUpdates)> { + StateRoot::overlay_root_with_updates(self.storage.clone(), self.block_number, state) + .map_err(|err| ProviderError::Database(err.into())) + } + + fn state_root_from_nodes_with_updates( + &self, + input: TrieInput, + ) -> ProviderResult<(B256, TrieUpdates)> { + StateRoot::overlay_root_from_nodes_with_updates( + self.storage.clone(), + self.block_number, + input, + ) + .map_err(|err| ProviderError::Database(err.into())) + } +} + +impl<'a, Storage: OpProofsStorage + Clone> StorageRootProvider + for OpProofsStateProviderRef<'a, Storage> +{ + fn storage_root(&self, address: Address, storage: HashedStorage) -> ProviderResult { + StorageRoot::overlay_root(self.storage.clone(), self.block_number, address, storage) + .map_err(|err| ProviderError::Database(err.into())) + } + + fn storage_proof( + &self, + address: Address, + slot: B256, + storage: HashedStorage, + ) -> ProviderResult { + StorageProof::overlay_storage_proof( + self.storage.clone(), + self.block_number, + address, + slot, + storage, + ) + .map_err(ProviderError::from) + } + + fn storage_multiproof( + &self, + address: Address, + slots: &[B256], + storage: HashedStorage, + ) -> ProviderResult { + StorageProof::overlay_storage_multiproof( + self.storage.clone(), + self.block_number, + address, + slots, + storage, + ) + .map_err(ProviderError::from) + } +} + +impl<'a, Storage: OpProofsStorage + Clone> StateProofProvider + for OpProofsStateProviderRef<'a, Storage> +{ + fn proof( + &self, + input: TrieInput, + address: Address, + slots: &[B256], + ) -> ProviderResult { + Proof::overlay_account_proof(self.storage.clone(), self.block_number, input, address, slots) + .map_err(ProviderError::from) + } + + fn multiproof( + &self, + input: TrieInput, + targets: MultiProofTargets, + ) -> ProviderResult { + Proof::overlay_multiproof(self.storage.clone(), self.block_number, input, targets) + .map_err(ProviderError::from) + } + + fn witness(&self, input: TrieInput, target: HashedPostState) -> ProviderResult> { + TrieWitness::overlay_witness(self.storage.clone(), self.block_number, input, target) + .map_err(ProviderError::from) + .map(|hm| hm.into_values().collect()) + } +} + +impl<'a, Storage: OpProofsStorage> HashedPostStateProvider + for OpProofsStateProviderRef<'a, Storage> +{ + fn hashed_post_state(&self, bundle_state: &BundleState) -> HashedPostState { + HashedPostState::from_bundle_state::(bundle_state.state()) + } +} + +impl<'a, Storage: OpProofsStorage> AccountReader for OpProofsStateProviderRef<'a, Storage> { + fn basic_account(&self, address: &Address) -> ProviderResult> { + let hashed_key = keccak256(address.0); + Ok(self + .storage + .account_hashed_cursor(self.block_number) + .map_err(Into::::into)? + .seek(hashed_key) + .map_err(Into::::into)? + .and_then(|(key, account)| (key == hashed_key).then_some(account))) + } +} + +impl<'a, Storage> StateProvider for OpProofsStateProviderRef<'a, Storage> +where + Storage: OpProofsStorage + Clone, +{ + fn storage(&self, address: Address, storage_key: B256) -> ProviderResult> { + let hashed_key = keccak256(storage_key); + Ok(self + .storage + .storage_hashed_cursor(keccak256(address.0), self.block_number) + .map_err(Into::::into)? + .seek(hashed_key) + .map_err(Into::::into)? + .and_then(|(key, storage)| (key == hashed_key).then_some(storage))) + } +} + +impl<'a, Storage: OpProofsStorage> BytecodeReader for OpProofsStateProviderRef<'a, Storage> { + fn bytecode_by_hash(&self, code_hash: &B256) -> ProviderResult> { + self.latest.bytecode_by_hash(code_hash) + } +}