diff --git a/Cargo.lock b/Cargo.lock index e81ad35249..b210d06963 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2453,6 +2453,7 @@ dependencies = [ "rand", "reqwest", "revm", + "thiserror 1.0.63 (git+https://github.com/quartiq/thiserror?branch=no-std)", "tokio", "tracing", "tracing-subscriber", diff --git a/bin/client/src/fault/handler/mod.rs b/bin/client/src/fault/handler/mod.rs index 5ce0494eaf..af262610d2 100644 --- a/bin/client/src/fault/handler/mod.rs +++ b/bin/client/src/fault/handler/mod.rs @@ -3,7 +3,7 @@ //! [KonaHandleRegister]: kona_executor::KonaHandleRegister use alloc::sync::Arc; -use kona_mpt::{TrieDB, TrieDBFetcher, TrieDBHinter}; +use kona_mpt::{TrieDB, TrieHinter, TrieProvider}; use revm::{ handler::register::EvmHandler, primitives::{spec_to_generic, SpecId}, @@ -20,8 +20,8 @@ mod kzg_point_eval; pub(crate) fn fpvm_handle_register( handler: &mut EvmHandler<'_, (), &mut State<&mut TrieDB>>, ) where - F: TrieDBFetcher, - H: TrieDBHinter, + F: TrieProvider, + H: TrieHinter, { let spec_id = handler.cfg.spec_id; diff --git a/bin/client/src/kona.rs b/bin/client/src/kona.rs index 8f9804a079..1386bd67a2 100644 --- a/bin/client/src/kona.rs +++ b/bin/client/src/kona.rs @@ -63,7 +63,7 @@ fn main() -> Result<()> { let mut executor = StatelessL2BlockExecutor::builder(&boot.rollup_config) .with_parent_header(driver.take_l2_safe_head_header()) - .with_fetcher(l2_provider.clone()) + .with_provider(l2_provider.clone()) .with_hinter(l2_provider) .with_handle_register(fpvm_handle_register) .build()?; diff --git a/bin/client/src/l1/chain_provider.rs b/bin/client/src/l1/chain_provider.rs index dd0d0e7bf8..f9668851f3 100644 --- a/bin/client/src/l1/chain_provider.rs +++ b/bin/client/src/l1/chain_provider.rs @@ -9,7 +9,7 @@ use alloy_rlp::Decodable; use anyhow::{anyhow, Result}; use async_trait::async_trait; use kona_derive::traits::ChainProvider; -use kona_mpt::{OrderedListWalker, TrieDBFetcher}; +use kona_mpt::{OrderedListWalker, TrieProvider}; use kona_preimage::{CommsClient, PreimageKey, PreimageKeyType}; use op_alloy_protocol::BlockInfo; @@ -121,7 +121,9 @@ impl ChainProvider for OracleL1ChainProvider { } } -impl TrieDBFetcher for OracleL1ChainProvider { +impl TrieProvider for OracleL1ChainProvider { + type Error = anyhow::Error; + fn trie_node_preimage(&self, key: B256) -> Result { // On L1, trie node preimages are stored as keccak preimage types in the oracle. We assume // that a hint for these preimages has already been sent, prior to this call. @@ -134,10 +136,10 @@ impl TrieDBFetcher for OracleL1ChainProvider { } fn bytecode_by_hash(&self, _: B256) -> Result { - unimplemented!("TrieDBFetcher::bytecode_by_hash unimplemented for OracleL1ChainProvider") + unimplemented!("TrieProvider::bytecode_by_hash unimplemented for OracleL1ChainProvider") } fn header_by_hash(&self, _: B256) -> Result
{ - unimplemented!("TrieDBFetcher::header_by_hash unimplemented for OracleL1ChainProvider") + unimplemented!("TrieProvider::header_by_hash unimplemented for OracleL1ChainProvider") } } diff --git a/bin/client/src/l1/driver.rs b/bin/client/src/l1/driver.rs index fbe5c0ccc1..01e493a7cf 100644 --- a/bin/client/src/l1/driver.rs +++ b/bin/client/src/l1/driver.rs @@ -19,7 +19,7 @@ use kona_derive::{ }, traits::{BlobProvider, ChainProvider, L2ChainProvider, OriginProvider}, }; -use kona_mpt::TrieDBFetcher; +use kona_mpt::TrieProvider; use kona_preimage::{CommsClient, PreimageKey, PreimageKeyType}; use op_alloy_protocol::{BlockInfo, L2BlockInfo}; use op_alloy_rpc_types_engine::OptimismAttributesWithParent; diff --git a/bin/client/src/l2/chain_provider.rs b/bin/client/src/l2/chain_provider.rs index 7969453931..9a604e52f0 100644 --- a/bin/client/src/l2/chain_provider.rs +++ b/bin/client/src/l2/chain_provider.rs @@ -9,7 +9,7 @@ use alloy_rlp::Decodable; use anyhow::{anyhow, Result}; use async_trait::async_trait; use kona_derive::traits::L2ChainProvider; -use kona_mpt::{OrderedListWalker, TrieDBFetcher, TrieDBHinter}; +use kona_mpt::{OrderedListWalker, TrieHinter, TrieProvider}; use kona_preimage::{CommsClient, PreimageKey, PreimageKeyType}; use kona_primitives::{L2ExecutionPayloadEnvelope, OpBlock}; use op_alloy_consensus::OpTxEnvelope; @@ -120,7 +120,9 @@ impl L2ChainProvider for OracleL2ChainProvider } } -impl TrieDBFetcher for OracleL2ChainProvider { +impl TrieProvider for OracleL2ChainProvider { + type Error = anyhow::Error; + fn trie_node_preimage(&self, key: B256) -> Result { // On L2, trie node preimages are stored as keccak preimage types in the oracle. We assume // that a hint for these preimages has already been sent, prior to this call. @@ -157,7 +159,9 @@ impl TrieDBFetcher for OracleL2ChainProvider { } } -impl TrieDBHinter for OracleL2ChainProvider { +impl TrieHinter for OracleL2ChainProvider { + type Error = anyhow::Error; + fn hint_trie_node(&self, hash: B256) -> Result<()> { kona_common::block_on(async move { self.oracle.write(&HintType::L2StateNode.encode_with(&[hash.as_slice()])).await diff --git a/crates/executor/benches/execution.rs b/crates/executor/benches/execution.rs index 0ad03e7d7f..06c4c0fe6c 100644 --- a/crates/executor/benches/execution.rs +++ b/crates/executor/benches/execution.rs @@ -7,22 +7,22 @@ use alloy_rpc_types_engine::PayloadAttributes; use anyhow::{anyhow, Result}; use criterion::{criterion_group, criterion_main, Bencher, Criterion}; use kona_executor::StatelessL2BlockExecutor; -use kona_mpt::{NoopTrieDBHinter, TrieDBFetcher}; +use kona_mpt::{NoopTrieHinter, TrieProvider}; use op_alloy_genesis::{RollupConfig, OP_BASE_FEE_PARAMS, OP_CANYON_BASE_FEE_PARAMS}; use op_alloy_rpc_types_engine::OptimismPayloadAttributes; use pprof::criterion::{Output, PProfProfiler}; use serde::Deserialize; use std::collections::HashMap; -/// A [TrieDBFetcher] implementation that fetches trie nodes and bytecode from the local +/// A [TrieProvider] implementation that fetches trie nodes and bytecode from the local /// testdata folder. #[derive(Deserialize)] -struct TestdataTrieDBFetcher { +struct TestdataTrieProvider { preimages: HashMap, } -impl TestdataTrieDBFetcher { - /// Constructs a new [TestdataTrieDBFetcher] with the given testdata folder. +impl TestdataTrieProvider { + /// Constructs a new [TestdataTrieProvider] with the given testdata folder. pub(crate) fn new(testdata_folder: &str) -> Self { let file_name = format!("testdata/{}/output.json", testdata_folder); let preimages = serde_json::from_str::>( @@ -33,7 +33,9 @@ impl TestdataTrieDBFetcher { } } -impl TrieDBFetcher for TestdataTrieDBFetcher { +impl TrieProvider for TestdataTrieProvider { + type Error = anyhow::Error; + fn trie_node_preimage(&self, key: B256) -> Result { self.preimages .get(&key) @@ -79,8 +81,8 @@ fn op_mainnet_exec_bench( bencher.iter(|| { let mut l2_block_executor = StatelessL2BlockExecutor::builder(&rollup_config) .with_parent_header(pre_state_header.clone().seal_slow()) - .with_fetcher(TestdataTrieDBFetcher::new(data_folder)) - .with_hinter(NoopTrieDBHinter) + .with_provider(TestdataTrieProvider::new(data_folder)) + .with_hinter(NoopTrieHinter) .build() .unwrap(); l2_block_executor.execute_payload(payload_attrs.clone()).unwrap(); diff --git a/crates/executor/src/builder.rs b/crates/executor/src/builder.rs index 0388115d2a..ca3cffb447 100644 --- a/crates/executor/src/builder.rs +++ b/crates/executor/src/builder.rs @@ -3,7 +3,7 @@ use crate::StatelessL2BlockExecutor; use alloy_consensus::{Header, Sealable, Sealed}; use anyhow::Result; -use kona_mpt::{TrieDB, TrieDBFetcher, TrieDBHinter}; +use kona_mpt::{TrieDB, TrieHinter, TrieProvider}; use op_alloy_genesis::RollupConfig; use revm::{db::State, handler::register::EvmHandler}; @@ -15,8 +15,8 @@ pub type KonaHandleRegister = #[derive(Debug)] pub struct StatelessL2BlockExecutorBuilder<'a, F, H> where - F: TrieDBFetcher, - H: TrieDBHinter, + F: TrieProvider, + H: TrieHinter, { /// The [RollupConfig]. config: &'a RollupConfig, @@ -24,16 +24,16 @@ where parent_header: Option>, /// The [KonaHandleRegister] to use during execution. handler_register: Option>, - /// The [TrieDBFetcher] to fetch the state trie preimages. + /// The [TrieProvider] to fetch the state trie preimages. fetcher: Option, - /// The [TrieDBHinter] to hint the state trie preimages. + /// The [TrieHinter] to hint the state trie preimages. hinter: Option, } impl<'a, F, H> StatelessL2BlockExecutorBuilder<'a, F, H> where - F: TrieDBFetcher, - H: TrieDBHinter, + F: TrieProvider, + H: TrieHinter, { /// Instantiate a new builder with the given [RollupConfig]. pub fn with_config(config: &'a RollupConfig) -> Self { @@ -46,13 +46,13 @@ where self } - /// Set the [TrieDBFetcher] to fetch the state trie preimages. - pub fn with_fetcher(mut self, fetcher: F) -> Self { + /// Set the [TrieProvider] to fetch the state trie preimages. + pub fn with_provider(mut self, fetcher: F) -> Self { self.fetcher = Some(fetcher); self } - /// Set the [TrieDBHinter] to hint the state trie preimages. + /// Set the [TrieHinter] to hint the state trie preimages. pub fn with_hinter(mut self, hinter: H) -> Self { self.hinter = Some(hinter); self diff --git a/crates/executor/src/canyon.rs b/crates/executor/src/canyon.rs index 7e082d10aa..0687eb0a36 100644 --- a/crates/executor/src/canyon.rs +++ b/crates/executor/src/canyon.rs @@ -2,7 +2,7 @@ use alloy_primitives::{address, b256, hex, Address, Bytes, B256}; use anyhow::Result; -use kona_mpt::{TrieDB, TrieDBFetcher, TrieDBHinter}; +use kona_mpt::{TrieDB, TrieHinter, TrieProvider}; use op_alloy_genesis::RollupConfig; use revm::{ primitives::{Account, Bytecode, HashMap}, @@ -28,8 +28,8 @@ pub(crate) fn ensure_create2_deployer_canyon( timestamp: u64, ) -> Result<()> where - F: TrieDBFetcher, - H: TrieDBHinter, + F: TrieProvider, + H: TrieHinter, { // If the canyon hardfork is active at the current timestamp, and it was not active at the // previous block timestamp, then we need to force-deploy the create2 deployer contract. diff --git a/crates/executor/src/lib.rs b/crates/executor/src/lib.rs index 2df912a10d..f1bc21d93e 100644 --- a/crates/executor/src/lib.rs +++ b/crates/executor/src/lib.rs @@ -12,7 +12,7 @@ use alloy_consensus::{Header, Sealable, EMPTY_OMMER_ROOT_HASH, EMPTY_ROOT_HASH}; use alloy_eips::eip2718::{Decodable2718, Encodable2718}; use alloy_primitives::{address, keccak256, Address, Bytes, TxKind, B256, U256}; use anyhow::{anyhow, Result}; -use kona_mpt::{ordered_trie_with_encoder, TrieDB, TrieDBFetcher, TrieDBHinter}; +use kona_mpt::{ordered_trie_with_encoder, TrieDB, TrieHinter, TrieProvider}; use op_alloy_consensus::{OpReceiptEnvelope, OpTxEnvelope}; use op_alloy_genesis::RollupConfig; use op_alloy_rpc_types_engine::OptimismPayloadAttributes; @@ -43,8 +43,8 @@ use util::{extract_tx_gas_limit, is_system_transaction, logs_bloom, receipt_enve #[derive(Debug)] pub struct StatelessL2BlockExecutor<'a, F, H> where - F: TrieDBFetcher, - H: TrieDBHinter, + F: TrieProvider, + H: TrieHinter, { /// The [RollupConfig]. config: &'a RollupConfig, @@ -56,8 +56,8 @@ where impl<'a, F, H> StatelessL2BlockExecutor<'a, F, H> where - F: TrieDBFetcher, - H: TrieDBHinter, + F: TrieProvider, + H: TrieHinter, { /// Constructs a new [StatelessL2BlockExecutorBuilder] with the given [RollupConfig]. pub fn builder(config: &'a RollupConfig) -> StatelessL2BlockExecutorBuilder<'a, F, H> { @@ -647,20 +647,20 @@ mod test { use alloy_primitives::{address, b256, hex}; use alloy_rlp::Decodable; use alloy_rpc_types_engine::PayloadAttributes; - use kona_mpt::NoopTrieDBHinter; + use kona_mpt::NoopTrieHinter; use op_alloy_genesis::{OP_BASE_FEE_PARAMS, OP_CANYON_BASE_FEE_PARAMS}; use serde::Deserialize; use std::{collections::HashMap, format}; - /// A [TrieDBFetcher] implementation that fetches trie nodes and bytecode from the local + /// A [TrieProvider] implementation that fetches trie nodes and bytecode from the local /// testdata folder. #[derive(Deserialize)] - struct TestdataTrieDBFetcher { + struct TestdataTrieProvider { preimages: HashMap, } - impl TestdataTrieDBFetcher { - /// Constructs a new [TestdataTrieDBFetcher] with the given testdata folder. + impl TestdataTrieProvider { + /// Constructs a new [TestdataTrieProvider] with the given testdata folder. pub(crate) fn new(testdata_folder: &str) -> Self { let file_name = format!("testdata/{}/output.json", testdata_folder); let preimages = serde_json::from_str::>( @@ -671,7 +671,9 @@ mod test { } } - impl TrieDBFetcher for TestdataTrieDBFetcher { + impl TrieProvider for TestdataTrieProvider { + type Error = anyhow::Error; + fn trie_node_preimage(&self, key: B256) -> Result { self.preimages .get(&key) @@ -721,8 +723,8 @@ mod test { // Initialize the block executor on block #120794431's post-state. let mut l2_block_executor = StatelessL2BlockExecutor::builder(&rollup_config) .with_parent_header(header.seal_slow()) - .with_fetcher(TestdataTrieDBFetcher::new("block_120794432_exec")) - .with_hinter(NoopTrieDBHinter) + .with_provider(TestdataTrieProvider::new("block_120794432_exec")) + .with_hinter(NoopTrieHinter) .build() .unwrap(); @@ -778,8 +780,8 @@ mod test { // Initialize the block executor on block #121049888's post-state. let mut l2_block_executor = StatelessL2BlockExecutor::builder(&rollup_config) .with_parent_header(parent_header.seal_slow()) - .with_fetcher(TestdataTrieDBFetcher::new("block_121049889_exec")) - .with_hinter(NoopTrieDBHinter) + .with_provider(TestdataTrieProvider::new("block_121049889_exec")) + .with_hinter(NoopTrieHinter) .build() .unwrap(); @@ -839,8 +841,8 @@ mod test { // Initialize the block executor on block #121003240's post-state. let mut l2_block_executor = StatelessL2BlockExecutor::builder(&rollup_config) .with_parent_header(parent_header.seal_slow()) - .with_fetcher(TestdataTrieDBFetcher::new("block_121003241_exec")) - .with_hinter(NoopTrieDBHinter) + .with_provider(TestdataTrieProvider::new("block_121003241_exec")) + .with_hinter(NoopTrieHinter) .build() .unwrap(); @@ -907,8 +909,8 @@ mod test { // Initialize the block executor on block #121057302's post-state. let mut l2_block_executor = StatelessL2BlockExecutor::builder(&rollup_config) .with_parent_header(parent_header.seal_slow()) - .with_fetcher(TestdataTrieDBFetcher::new("block_121057303_exec")) - .with_hinter(NoopTrieDBHinter) + .with_provider(TestdataTrieProvider::new("block_121057303_exec")) + .with_hinter(NoopTrieHinter) .build() .unwrap(); @@ -969,8 +971,8 @@ mod test { // Initialize the block executor on block #121057302's post-state. let mut l2_block_executor = StatelessL2BlockExecutor::builder(&rollup_config) .with_parent_header(parent_header.seal_slow()) - .with_fetcher(TestdataTrieDBFetcher::new("block_121065789_exec")) - .with_hinter(NoopTrieDBHinter) + .with_provider(TestdataTrieProvider::new("block_121065789_exec")) + .with_hinter(NoopTrieHinter) .build() .unwrap(); @@ -1040,8 +1042,8 @@ mod test { // Initialize the block executor on block #121135703's post-state. let mut l2_block_executor = StatelessL2BlockExecutor::builder(&rollup_config) .with_parent_header(parent_header.seal_slow()) - .with_fetcher(TestdataTrieDBFetcher::new("block_121135704_exec")) - .with_hinter(NoopTrieDBHinter) + .with_provider(TestdataTrieProvider::new("block_121135704_exec")) + .with_hinter(NoopTrieHinter) .build() .unwrap(); diff --git a/crates/mpt/Cargo.toml b/crates/mpt/Cargo.toml index 8ad8fc2936..a4f7ff67db 100644 --- a/crates/mpt/Cargo.toml +++ b/crates/mpt/Cargo.toml @@ -10,8 +10,8 @@ homepage.workspace = true [dependencies] # General -anyhow.workspace = true tracing.workspace = true +thiserror.workspace = true # Revm + Alloy revm.workspace = true @@ -22,6 +22,7 @@ alloy-trie.workspace = true [dev-dependencies] tokio.workspace = true +anyhow.workspace = true reqwest.workspace = true futures.workspace = true tracing-subscriber.workspace = true diff --git a/crates/mpt/benches/trie_node.rs b/crates/mpt/benches/trie_node.rs index c20f05ca86..922d83d510 100644 --- a/crates/mpt/benches/trie_node.rs +++ b/crates/mpt/benches/trie_node.rs @@ -2,7 +2,7 @@ use alloy_trie::Nibbles; use criterion::{criterion_group, criterion_main, Criterion}; -use kona_mpt::{NoopTrieDBFetcher, NoopTrieDBHinter, TrieNode}; +use kona_mpt::{NoopTrieHinter, NoopTrieProvider, TrieNode}; use pprof::criterion::{Output, PProfProfiler}; use rand::{rngs::StdRng, seq::SliceRandom, Rng, SeedableRng}; @@ -20,7 +20,7 @@ fn trie(c: &mut Criterion) { b.iter(|| { let mut trie = TrieNode::Empty; for key in &keys { - trie.insert(key, key.to_vec().into(), &NoopTrieDBFetcher).unwrap(); + trie.insert(key, key.to_vec().into(), &NoopTrieProvider).unwrap(); } }); }); @@ -32,7 +32,7 @@ fn trie(c: &mut Criterion) { b.iter(|| { let mut trie = TrieNode::Empty; for key in &keys { - trie.insert(key, key.to_vec().into(), &NoopTrieDBFetcher).unwrap(); + trie.insert(key, key.to_vec().into(), &NoopTrieProvider).unwrap(); } }); }); @@ -46,13 +46,13 @@ fn trie(c: &mut Criterion) { let keys_to_delete = keys.choose_multiple(rng, 16).cloned().collect::>(); for key in &keys { - trie.insert(key, key.to_vec().into(), &NoopTrieDBFetcher).unwrap(); + trie.insert(key, key.to_vec().into(), &NoopTrieProvider).unwrap(); } b.iter(|| { let trie = &mut trie.clone(); for key in &keys_to_delete { - trie.delete(key, &NoopTrieDBFetcher, &NoopTrieDBHinter).unwrap(); + trie.delete(key, &NoopTrieProvider, &NoopTrieHinter).unwrap(); } }); }); @@ -62,7 +62,7 @@ fn trie(c: &mut Criterion) { (0..2usize.pow(16)).map(|_| Nibbles::unpack(rng.gen::<[u8; 32]>())).collect::>(); let mut trie = TrieNode::Empty; for key in &keys { - trie.insert(key, key.to_vec().into(), &NoopTrieDBFetcher).unwrap(); + trie.insert(key, key.to_vec().into(), &NoopTrieProvider).unwrap(); } let rng = &mut rand::thread_rng(); @@ -71,7 +71,7 @@ fn trie(c: &mut Criterion) { b.iter(|| { let trie = &mut trie.clone(); for key in &keys_to_delete { - trie.delete(key, &NoopTrieDBFetcher, &NoopTrieDBHinter).unwrap(); + trie.delete(key, &NoopTrieProvider, &NoopTrieHinter).unwrap(); } }); }); @@ -81,7 +81,7 @@ fn trie(c: &mut Criterion) { (0..2usize.pow(12)).map(|_| Nibbles::unpack(rng.gen::<[u8; 32]>())).collect::>(); let mut trie = TrieNode::Empty; for key in &keys { - trie.insert(key, key.to_vec().into(), &NoopTrieDBFetcher).unwrap(); + trie.insert(key, key.to_vec().into(), &NoopTrieProvider).unwrap(); } let rng = &mut rand::thread_rng(); @@ -89,7 +89,7 @@ fn trie(c: &mut Criterion) { b.iter(|| { for key in &keys_to_retrieve { - trie.open(key, &NoopTrieDBFetcher).unwrap(); + trie.open(key, &NoopTrieProvider).unwrap(); } }); }); @@ -99,7 +99,7 @@ fn trie(c: &mut Criterion) { (0..2usize.pow(16)).map(|_| Nibbles::unpack(rng.gen::<[u8; 32]>())).collect::>(); let mut trie = TrieNode::Empty; for key in &keys { - trie.insert(key, key.to_vec().into(), &NoopTrieDBFetcher).unwrap(); + trie.insert(key, key.to_vec().into(), &NoopTrieProvider).unwrap(); } let rng = &mut rand::thread_rng(); @@ -107,7 +107,7 @@ fn trie(c: &mut Criterion) { b.iter(|| { for key in &keys_to_retrieve { - trie.open(key, &NoopTrieDBFetcher).unwrap(); + trie.open(key, &NoopTrieProvider).unwrap(); } }); }); @@ -117,7 +117,7 @@ fn trie(c: &mut Criterion) { (0..2usize.pow(12)).map(|_| Nibbles::unpack(rng.gen::<[u8; 32]>())).collect::>(); let mut trie = TrieNode::Empty; for key in &keys { - trie.insert(key, key.to_vec().into(), &NoopTrieDBFetcher).unwrap(); + trie.insert(key, key.to_vec().into(), &NoopTrieProvider).unwrap(); } b.iter(|| { @@ -131,7 +131,7 @@ fn trie(c: &mut Criterion) { (0..2usize.pow(16)).map(|_| Nibbles::unpack(rng.gen::<[u8; 32]>())).collect::>(); let mut trie = TrieNode::Empty; for key in &keys { - trie.insert(key, key.to_vec().into(), &NoopTrieDBFetcher).unwrap(); + trie.insert(key, key.to_vec().into(), &NoopTrieProvider).unwrap(); } b.iter(|| { diff --git a/crates/mpt/src/db/mod.rs b/crates/mpt/src/db/mod.rs index c825f37415..1f39dcc58a 100644 --- a/crates/mpt/src/db/mod.rs +++ b/crates/mpt/src/db/mod.rs @@ -1,31 +1,31 @@ //! This module contains an implementation of an in-memory Trie DB for [revm], that allows for //! incremental updates through fetching node preimages on the fly during execution. -use crate::{TrieDBFetcher, TrieDBHinter, TrieNode}; -use alloc::vec::Vec; +use crate::{ + errors::{TrieDBError, TrieDBResult}, + TrieHinter, TrieNode, TrieNodeError, TrieProvider, +}; +use alloc::{string::ToString, vec::Vec}; use alloy_consensus::{Header, Sealed, EMPTY_ROOT_HASH}; use alloy_primitives::{keccak256, Address, B256, U256}; use alloy_rlp::{Decodable, Encodable}; use alloy_trie::Nibbles; -use anyhow::{anyhow, Result}; use revm::{ db::{states::StorageSlot, BundleState}, primitives::{AccountInfo, Bytecode, HashMap, BLOCK_HASH_HISTORY}, Database, }; +use tracing::debug; mod account; pub use account::TrieAccount; -use tracing::debug; /// A Trie DB that caches open state in-memory. /// /// When accounts that don't already exist within the cached [TrieNode] are queried, the database /// fetches the preimages of the trie nodes on the path to the account using the `PreimageFetcher` -/// (`PF` generic) and `CodeHashFetcher` (`CHF` generic). This allows for data to be fetched in a -/// verifiable manner given an initial trusted state root as it is needed during execution. In -/// addition, the `HeaderFetcher` (`HF` generic) is used to fetch block headers, relative to the -/// DB's current block hash, for block hash lookups. +/// (`F` generic). This allows for data to be fetched in a verifiable manner given an initial +/// trusted state root as it is needed during execution. /// /// The [TrieDB] is intended to be wrapped by a [State], which is then used by the [revm::Evm] to /// capture state transitions during block execution. @@ -50,7 +50,7 @@ use tracing::debug; /// use alloy_consensus::{Header, Sealable}; /// use alloy_primitives::{Bytes, B256}; /// use anyhow::Result; -/// use kona_mpt::{NoopTrieDBFetcher, NoopTrieDBHinter, TrieDB}; +/// use kona_mpt::{NoopTrieHinter, NoopTrieProvider, TrieDB}; /// use revm::{db::states::bundle_state::BundleRetention, EvmBuilder, StateBuilder}; /// /// let mock_starting_root = B256::default(); @@ -59,8 +59,8 @@ use tracing::debug; /// let trie_db = TrieDB::new( /// mock_starting_root, /// mock_parent_block_header.seal_slow(), -/// NoopTrieDBFetcher, -/// NoopTrieDBHinter, +/// NoopTrieProvider, +/// NoopTrieHinter, /// ); /// let mut state = StateBuilder::new_with_database(trie_db).with_bundle_update().build(); /// let evm = EvmBuilder::default().with_db(&mut state).build(); @@ -79,8 +79,8 @@ use tracing::debug; #[derive(Debug, Clone)] pub struct TrieDB where - F: TrieDBFetcher, - H: TrieDBHinter, + F: TrieProvider, + H: TrieHinter, { /// The [TrieNode] representation of the root node. root_node: TrieNode, @@ -88,16 +88,16 @@ where storage_roots: HashMap, /// The parent block hash of the current block. parent_block_header: Sealed
, - /// The [TrieDBFetcher] + /// The [TrieProvider] fetcher: F, - /// The [TrieDBHinter] + /// The [TrieHinter] hinter: H, } impl TrieDB where - F: TrieDBFetcher, - H: TrieDBHinter, + F: TrieProvider, + H: TrieHinter, { /// Creates a new [TrieDB] with the given root node. pub fn new(root: B256, parent_block_header: Sealed
, fetcher: F, hinter: H) -> Self { @@ -155,7 +155,7 @@ where /// ## Returns /// - `Ok(B256)`: The new state root hash of the trie DB. /// - `Err(_)`: If the state root hash could not be computed. - pub fn state_root(&mut self, bundle: &BundleState) -> Result { + pub fn state_root(&mut self, bundle: &BundleState) -> TrieDBResult { debug!(target: "client_executor", "Recomputing state root"); // Update the accounts in the trie with the changeset. @@ -171,7 +171,7 @@ where ); // Extract the new state root from the root node. - self.root_node.blinded_commitment().ok_or(anyhow!("State root node is not a blinded node")) + self.root_node.blinded_commitment().ok_or(TrieDBError::RootNotBlinded) } /// Returns a reference to the current parent block header of the trie DB. @@ -197,9 +197,11 @@ where /// - `Ok(Some(TrieAccount))`: The [TrieAccount] of the account. /// - `Ok(None)`: If the account does not exist in the trie. /// - `Err(_)`: If the account could not be fetched. - pub fn get_trie_account(&mut self, address: &Address) -> Result> { + pub fn get_trie_account(&mut self, address: &Address) -> TrieDBResult> { // Send a hint to the host to fetch the account proof. - self.hinter.hint_account_proof(*address, self.parent_block_header.number)?; + self.hinter + .hint_account_proof(*address, self.parent_block_header.number) + .map_err(|e| TrieDBError::Provider(e.to_string()))?; // Fetch the account from the trie. let hashed_address_nibbles = Nibbles::unpack(keccak256(address.as_slice())); @@ -210,7 +212,8 @@ where // Decode the trie account from the RLP bytes. TrieAccount::decode(&mut trie_account_rlp.as_ref()) - .map_err(|e| anyhow!("Error decoding trie account: {e}")) + .map_err(TrieNodeError::RLPError) + .map_err(Into::into) .map(Some) } @@ -222,7 +225,7 @@ where /// ## Returns /// - `Ok(())` if the accounts were successfully updated. /// - `Err(_)` if the accounts could not be updated. - fn update_accounts(&mut self, bundle: &BundleState) -> Result<()> { + fn update_accounts(&mut self, bundle: &BundleState) -> TrieDBResult<()> { for (address, bundle_account) in bundle.state() { if bundle_account.status.is_not_modified() { continue; @@ -239,7 +242,7 @@ where } let account_info = - bundle_account.account_info().ok_or(anyhow!("Account info not found"))?; + bundle_account.account_info().ok_or(TrieDBError::MissingAccountInfo)?; let mut trie_account = TrieAccount { balance: account_info.balance, nonce: account_info.nonce, @@ -259,9 +262,8 @@ where // Recompute the account storage root. acc_storage_root.blind(); - let commitment = acc_storage_root - .blinded_commitment() - .ok_or(anyhow!("Storage root node is not a blinded node"))?; + let commitment = + acc_storage_root.blinded_commitment().ok_or(TrieDBError::RootNotBlinded)?; trie_account.storage_root = commitment; // RLP encode the trie account for insertion. @@ -291,7 +293,7 @@ where value: &StorageSlot, fetcher: &F, hinter: &H, - ) -> Result<()> { + ) -> TrieDBResult<()> { if !value.is_changed() { return Ok(()); } @@ -316,10 +318,10 @@ where impl Database for TrieDB where - F: TrieDBFetcher, - H: TrieDBHinter, + F: TrieProvider, + H: TrieHinter, { - type Error = anyhow::Error; + type Error = TrieDBError; fn basic(&mut self, address: Address) -> Result, Self::Error> { // Fetch the account from the trie. @@ -345,12 +347,14 @@ where self.fetcher .bytecode_by_hash(code_hash) .map(Bytecode::new_raw) - .map_err(|e| anyhow!("Failed to fetch code by hash: {e}")) + .map_err(|e| TrieDBError::Provider(e.to_string())) } fn storage(&mut self, address: Address, index: U256) -> Result { // Send a hint to the host to fetch the storage proof. - self.hinter.hint_storage_proof(address, index, self.parent_block_header.number)?; + self.hinter + .hint_storage_proof(address, index, self.parent_block_header.number) + .map_err(|e| TrieDBError::Provider(e.to_string()))?; // Fetch the account's storage root from the cache. If storage is being accessed, the // account should have been loaded into the cache by the `basic` method. If the account was @@ -367,7 +371,7 @@ where Some(slot_value) => { // Decode the storage slot value. let int_slot = U256::decode(&mut slot_value.as_ref()) - .map_err(|e| anyhow!("Failed to decode storage slot value: {e}"))?; + .map_err(TrieNodeError::RLPError)?; Ok(int_slot) } None => { @@ -392,7 +396,10 @@ where // Walk back the block headers to the desired block number. while header.number > block_number { - header = self.fetcher.header_by_hash(header.parent_hash)?; + header = self + .fetcher + .header_by_hash(header.parent_hash) + .map_err(|e| TrieDBError::Provider(e.to_string()))?; } Ok(header.hash_slow()) diff --git a/crates/mpt/src/errors.rs b/crates/mpt/src/errors.rs new file mode 100644 index 0000000000..a0e505cee1 --- /dev/null +++ b/crates/mpt/src/errors.rs @@ -0,0 +1,64 @@ +//! Errors for the `kona-derive` crate. + +use alloc::string::String; +use thiserror::Error; + +/// A [Result] type alias where the error is [TrieNodeError]. +pub type TrieNodeResult = Result; + +/// An error type for [TrieNode] operations. +/// +/// [TrieNode]: crate::TrieNode +#[derive(Error, Debug, PartialEq, Eq)] +pub enum TrieNodeError { + /// Invalid trie node type encountered. + #[error("Invalid trie node type encountered")] + InvalidNodeType, + /// Failed to decode trie node. + #[error("Failed to decode trie node: {0}")] + RLPError(alloy_rlp::Error), + /// Key does not exist in trie. + #[error("Key does not exist in trie. Encountered {0} node.")] + KeyNotFound(String), + /// Trie node is not a leaf node. + #[error("Trie provider error: {0}")] + Provider(String), +} + +/// A [Result] type alias where the error is [TrieDBError]. +pub type TrieDBResult = Result; + +/// An error type for [TrieDB] operations. +/// +/// [TrieDB]: crate::TrieDB +#[derive(Error, Debug, PartialEq, Eq)] +pub enum TrieDBError { + /// Trie root node has not been blinded. + #[error("Trie root node has not been blinded")] + RootNotBlinded, + /// Missing account info for bundle account. + #[error("Missing account info for bundle account.")] + MissingAccountInfo, + /// Trie node error. + #[error("Trie node error: {0}")] + TrieNode(#[from] TrieNodeError), + /// Trie provider error. + #[error("Trie provider error: {0}")] + Provider(String), +} + +/// A [Result] type alias where the error is [OrderedListWalkerError]. +pub type OrderedListWalkerResult = Result; + +/// An error type for [OrderedListWalker] operations. +/// +/// [OrderedListWalker]: crate::OrderedListWalker +#[derive(Error, Debug, PartialEq, Eq)] +pub enum OrderedListWalkerError { + /// Iterator has already been hydrated, and cannot be re-hydrated until it is exhausted. + #[error("Iterator has already been hydrated, and cannot be re-hydrated until it is exhausted")] + AlreadyHydrated, + /// Trie node error. + #[error(transparent)] + TrieNode(#[from] TrieNodeError), +} diff --git a/crates/mpt/src/fetcher.rs b/crates/mpt/src/fetcher.rs index 277cb584ca..280ac878da 100644 --- a/crates/mpt/src/fetcher.rs +++ b/crates/mpt/src/fetcher.rs @@ -1,13 +1,17 @@ -//! Contains the [TrieDBFetcher] trait for fetching trie node preimages, contract bytecode, and +//! Contains the [TrieProvider] trait for fetching trie node preimages, contract bytecode, and //! headers. +use alloc::string::{String, ToString}; use alloy_consensus::Header; use alloy_primitives::{Address, Bytes, B256, U256}; -use anyhow::Result; +use core::fmt::Display; -/// The [TrieDBFetcher] trait defines the synchronous interface for fetching trie node preimages and +/// The [TrieProvider] trait defines the synchronous interface for fetching trie node preimages and /// headers. -pub trait TrieDBFetcher { +pub trait TrieProvider { + /// The error type for fetching trie node preimages. + type Error: Display + ToString; + /// Fetches the preimage for the given trie node hash. /// /// ## Takes @@ -18,7 +22,7 @@ pub trait TrieDBFetcher { /// - Err(anyhow::Error): If the trie node preimage could not be fetched. /// /// [TrieDB]: crate::TrieDB - fn trie_node_preimage(&self, key: B256) -> Result; + fn trie_node_preimage(&self, key: B256) -> Result; /// Fetches the preimage of the bytecode hash provided. /// @@ -30,7 +34,7 @@ pub trait TrieDBFetcher { /// - Err(anyhow::Error): If the bytecode hash could not be fetched. /// /// [TrieDB]: crate::TrieDB - fn bytecode_by_hash(&self, code_hash: B256) -> Result; + fn bytecode_by_hash(&self, code_hash: B256) -> Result; /// Fetches the preimage of [Header] hash provided. /// @@ -42,12 +46,15 @@ pub trait TrieDBFetcher { /// - Err(anyhow::Error): If the [Header] could not be fetched. /// /// [TrieDB]: crate::TrieDB - fn header_by_hash(&self, hash: B256) -> Result
; + fn header_by_hash(&self, hash: B256) -> Result; } -/// The [TrieDBHinter] trait defines the synchronous interface for hinting the host to fetch trie +/// The [TrieHinter] trait defines the synchronous interface for hinting the host to fetch trie /// node preimages. -pub trait TrieDBHinter { +pub trait TrieHinter { + /// The error type for hinting trie node preimages. + type Error: Display + ToString; + /// Hints the host to fetch the trie node preimage by hash. /// /// ## Takes @@ -55,7 +62,7 @@ pub trait TrieDBHinter { /// /// ## Returns /// - Ok(()): If the hint was successful. - fn hint_trie_node(&self, hash: B256) -> Result<()>; + fn hint_trie_node(&self, hash: B256) -> Result<(), Self::Error>; /// Hints the host to fetch the trie node preimages on the path to the given address. /// @@ -66,7 +73,7 @@ pub trait TrieDBHinter { /// ## Returns /// - Ok(()): If the hint was successful. /// - Err(anyhow::Error): If the hint was unsuccessful. - fn hint_account_proof(&self, address: Address, block_number: u64) -> Result<()>; + fn hint_account_proof(&self, address: Address, block_number: u64) -> Result<(), Self::Error>; /// Hints the host to fetch the trie node preimages on the path to the storage slot within the /// given account's storage trie. @@ -79,41 +86,55 @@ pub trait TrieDBHinter { /// ## Returns /// - Ok(()): If the hint was successful. /// - Err(anyhow::Error): If the hint was unsuccessful. - fn hint_storage_proof(&self, address: Address, slot: U256, block_number: u64) -> Result<()>; + fn hint_storage_proof( + &self, + address: Address, + slot: U256, + block_number: u64, + ) -> Result<(), Self::Error>; } -/// The default, no-op implementation of the [TrieDBFetcher] trait, used for testing. +/// The default, no-op implementation of the [TrieProvider] trait, used for testing. #[derive(Debug, Clone, Copy)] -pub struct NoopTrieDBFetcher; +pub struct NoopTrieProvider; -impl TrieDBFetcher for NoopTrieDBFetcher { - fn trie_node_preimage(&self, _key: B256) -> Result { +impl TrieProvider for NoopTrieProvider { + type Error = String; + + fn trie_node_preimage(&self, _key: B256) -> Result { Ok(Bytes::new()) } - fn bytecode_by_hash(&self, _code_hash: B256) -> Result { + fn bytecode_by_hash(&self, _code_hash: B256) -> Result { Ok(Bytes::new()) } - fn header_by_hash(&self, _hash: B256) -> Result
{ + fn header_by_hash(&self, _hash: B256) -> Result { Ok(Header::default()) } } -/// The default, no-op implementation of the [TrieDBHinter] trait, used for testing. +/// The default, no-op implementation of the [TrieHinter] trait, used for testing. #[derive(Debug, Clone, Copy)] -pub struct NoopTrieDBHinter; +pub struct NoopTrieHinter; + +impl TrieHinter for NoopTrieHinter { + type Error = String; -impl TrieDBHinter for NoopTrieDBHinter { - fn hint_trie_node(&self, _hash: B256) -> Result<()> { + fn hint_trie_node(&self, _hash: B256) -> Result<(), Self::Error> { Ok(()) } - fn hint_account_proof(&self, _address: Address, _block_number: u64) -> Result<()> { + fn hint_account_proof(&self, _address: Address, _block_number: u64) -> Result<(), Self::Error> { Ok(()) } - fn hint_storage_proof(&self, _address: Address, _slot: U256, _block_number: u64) -> Result<()> { + fn hint_storage_proof( + &self, + _address: Address, + _slot: U256, + _block_number: u64, + ) -> Result<(), Self::Error> { Ok(()) } } diff --git a/crates/mpt/src/lib.rs b/crates/mpt/src/lib.rs index a8ccf4389c..3de42454e3 100644 --- a/crates/mpt/src/lib.rs +++ b/crates/mpt/src/lib.rs @@ -10,8 +10,14 @@ extern crate alloc; mod db; pub use db::{TrieAccount, TrieDB}; +mod errors; +pub use errors::{ + OrderedListWalkerError, OrderedListWalkerResult, TrieDBError, TrieDBResult, TrieNodeError, + TrieNodeResult, +}; + mod fetcher; -pub use fetcher::{NoopTrieDBFetcher, NoopTrieDBHinter, TrieDBFetcher, TrieDBHinter}; +pub use fetcher::{NoopTrieHinter, NoopTrieProvider, TrieHinter, TrieProvider}; mod node; pub use node::TrieNode; diff --git a/crates/mpt/src/list_walker.rs b/crates/mpt/src/list_walker.rs index 97f93b1051..9aa230b9ce 100644 --- a/crates/mpt/src/list_walker.rs +++ b/crates/mpt/src/list_walker.rs @@ -1,11 +1,13 @@ //! This module contains the [OrderedListWalker] struct, which allows for traversing an MPT root of //! a derivable ordered list. -use crate::{TrieDBFetcher, TrieNode}; -use alloc::{collections::VecDeque, vec}; +use crate::{ + errors::{OrderedListWalkerError, OrderedListWalkerResult}, + TrieNode, TrieNodeError, TrieProvider, +}; +use alloc::{collections::VecDeque, string::ToString, vec}; use alloy_primitives::{Bytes, B256}; use alloy_rlp::{Decodable, EMPTY_STRING_CODE}; -use anyhow::{anyhow, Result}; use core::marker::PhantomData; /// A [OrderedListWalker] allows for traversing over a Merkle Patricia Trie containing a derivable @@ -14,7 +16,7 @@ use core::marker::PhantomData; /// Once it has ben hydrated with [Self::hydrate], the elements in the derivable list can be /// iterated over using the [Iterator] implementation. #[derive(Debug, Clone, Eq, PartialEq)] -pub struct OrderedListWalker { +pub struct OrderedListWalker { /// The Merkle Patricia Trie root. root: B256, /// The leaf nodes of the derived list, in order. [None] if the tree has yet to be fully @@ -26,7 +28,7 @@ pub struct OrderedListWalker { impl OrderedListWalker where - F: TrieDBFetcher, + F: TrieProvider, { /// Creates a new [OrderedListWalker], yet to be hydrated. pub fn new(root: B256) -> Self { @@ -35,7 +37,7 @@ where /// Creates a new [OrderedListWalker] and hydrates it with [Self::hydrate] and the given fetcher /// immediately. - pub fn try_new_hydrated(root: B256, fetcher: &F) -> Result { + pub fn try_new_hydrated(root: B256, fetcher: &F) -> OrderedListWalkerResult { let mut walker = Self { root, inner: None, _phantom: PhantomData }; walker.hydrate(fetcher)?; Ok(walker) @@ -43,10 +45,10 @@ where /// Hydrates the [OrderedListWalker]'s iterator with the leaves of the derivable list. If /// `Self::inner` is [Some], this function will fail fast. - pub fn hydrate(&mut self, fetcher: &F) -> Result<()> { + pub fn hydrate(&mut self, fetcher: &F) -> OrderedListWalkerResult<()> { // Do not allow for re-hydration if `inner` is `Some` and still contains elements. if self.inner.is_some() && self.inner.as_ref().map(|s| s.len()).unwrap_or_default() > 0 { - anyhow::bail!("Iterator is already hydrated, and has not been consumed entirely.") + return Err(OrderedListWalkerError::AlreadyHydrated); } // Get the preimage to the root node. @@ -59,13 +61,12 @@ where if !ordered_list.is_empty() { if ordered_list.len() <= EMPTY_STRING_CODE as usize { // If the list length is < 0x80, the final element is the first element. - let first = ordered_list.pop_back().ok_or(anyhow!("Empty list fetched"))?; + let first = ordered_list.pop_back().expect("Cannot be empty"); ordered_list.push_front(first); } else { // If the list length is > 0x80, the element at index 0x80-1 is the first element. - let first = ordered_list - .remove((EMPTY_STRING_CODE - 1) as usize) - .ok_or(anyhow!("Empty list fetched"))?; + let first = + ordered_list.remove((EMPTY_STRING_CODE - 1) as usize).expect("Cannot be empty"); ordered_list.push_front(first); } } @@ -81,7 +82,10 @@ where } /// Traverses a [TrieNode], returning all values of child [TrieNode::Leaf] variants. - fn fetch_leaves(trie_node: &TrieNode, fetcher: &F) -> Result> { + fn fetch_leaves( + trie_node: &TrieNode, + fetcher: &F, + ) -> OrderedListWalkerResult> { match trie_node { TrieNode::Branch { stack } => { let mut leaf_values = VecDeque::with_capacity(stack.len()); @@ -118,24 +122,28 @@ where } } TrieNode::Empty => Ok(VecDeque::new()), - _ => anyhow::bail!("Invalid trie node type encountered"), + _ => Err(TrieNodeError::InvalidNodeType.into()), } } /// Grabs the preimage of `hash` using `fetcher`, and attempts to decode the preimage data into /// a [TrieNode]. Will error if the conversion of `T` into [B256] fails. - fn get_trie_node(hash: T, fetcher: &F) -> Result + fn get_trie_node(hash: T, fetcher: &F) -> OrderedListWalkerResult where T: Into, { - let preimage = fetcher.trie_node_preimage(hash.into())?; - TrieNode::decode(&mut preimage.as_ref()).map_err(|e| anyhow!(e)) + let preimage = fetcher + .trie_node_preimage(hash.into()) + .map_err(|e| TrieNodeError::Provider(e.to_string()))?; + TrieNode::decode(&mut preimage.as_ref()) + .map_err(TrieNodeError::RLPError) + .map_err(Into::into) } } impl Iterator for OrderedListWalker where - F: TrieDBFetcher, + F: TrieProvider, { type Item = (Bytes, Bytes); @@ -162,7 +170,7 @@ mod test { get_live_derivable_receipts_list, get_live_derivable_transactions_list, TrieNodeProvider, }, - NoopTrieDBFetcher, + NoopTrieProvider, }; use alloc::{collections::BTreeMap, string::String, vec::Vec}; use alloy_consensus::{ReceiptEnvelope, TxEnvelope}; @@ -226,7 +234,7 @@ mod test { #[test] fn test_empty_list_walker() { - assert!(OrderedListWalker::fetch_leaves(&TrieNode::Empty, &NoopTrieDBFetcher) + assert!(OrderedListWalker::fetch_leaves(&TrieNode::Empty, &NoopTrieProvider) .expect("Failed to traverse empty trie") .is_empty()); } diff --git a/crates/mpt/src/node.rs b/crates/mpt/src/node.rs index 63bcfd423e..d585135def 100644 --- a/crates/mpt/src/node.rs +++ b/crates/mpt/src/node.rs @@ -2,14 +2,15 @@ //! Patricia Trie. use crate::{ + errors::TrieNodeResult, util::{rlp_list_element_length, unpack_path_to_nibbles}, - TrieDBFetcher, TrieDBHinter, + TrieHinter, TrieNodeError, TrieProvider, }; -use alloc::{boxed::Box, vec, vec::Vec}; -use alloy_primitives::{keccak256, Bytes, B256}; +use alloc::{boxed::Box, string::ToString, vec, vec::Vec}; +use alloy_primitives::{hex, keccak256, Bytes, B256}; use alloy_rlp::{length_of_length, Buf, Decodable, Encodable, Header, EMPTY_STRING_CODE}; use alloy_trie::{Nibbles, EMPTY_ROOT_HASH}; -use anyhow::{anyhow, Result}; +use core::fmt::Display; /// The length of the branch list when RLP encoded const BRANCH_LIST_LENGTH: usize = 17; @@ -91,6 +92,22 @@ pub enum TrieNode { }, } +impl Display for TrieNode { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + TrieNode::Empty => write!(f, "Empty"), + TrieNode::Blinded { commitment } => write!(f, "Blinded({})", commitment), + TrieNode::Leaf { prefix, value } => { + write!(f, "Leaf({}, {})", hex::encode(prefix.as_ref()), hex::encode(value.as_ref())) + } + TrieNode::Extension { prefix, node } => { + write!(f, "Extension({}, {})", hex::encode(prefix.as_ref()), node) + } + TrieNode::Branch { .. } => write!(f, "Branch"), + } + } +} + impl TrieNode { /// Creates a new [TrieNode::Blinded] node. /// @@ -128,15 +145,17 @@ impl TrieNode { } /// Unblinds the [TrieNode] if it is a [TrieNode::Blinded] node. - pub fn unblind(&mut self, fetcher: &F) -> Result<()> { + pub fn unblind(&mut self, fetcher: &F) -> TrieNodeResult<()> { if let TrieNode::Blinded { commitment } = self { if *commitment == EMPTY_ROOT_HASH { // If the commitment is the empty root hash, the node is empty, and we don't need to // reach out to the fetcher. *self = TrieNode::Empty; } else { - *self = TrieNode::decode(&mut fetcher.trie_node_preimage(*commitment)?.as_ref()) - .map_err(|e| anyhow!(e))?; + let rlp = fetcher + .trie_node_preimage(*commitment) + .map_err(|e| TrieNodeError::Provider(e.to_string()))?; + *self = TrieNode::decode(&mut rlp.as_ref()).map_err(TrieNodeError::RLPError)?; } } Ok(()) @@ -154,11 +173,11 @@ impl TrieNode { /// ## Returns /// - `Err(_)` - Could not retrieve the node with the given key from the trie. /// - `Ok((_, _))` - The key and value of the node - pub fn open<'a, F: TrieDBFetcher>( + pub fn open<'a, F: TrieProvider>( &'a mut self, path: &Nibbles, fetcher: &F, - ) -> Result> { + ) -> TrieNodeResult> { match self { TrieNode::Branch { ref mut stack } => { let branch_nibble = path[0] as usize; @@ -198,12 +217,12 @@ impl TrieNode { /// ## Returns /// - `Err(_)` - Could not insert the node at the given path in the trie. /// - `Ok(())` - The node was successfully inserted at the given path. - pub fn insert( + pub fn insert( &mut self, path: &Nibbles, value: Bytes, fetcher: &F, - ) -> Result<()> { + ) -> TrieNodeResult<()> { match self { TrieNode::Empty => { // If the trie node is null, insert the leaf node at the current path. @@ -313,28 +332,26 @@ impl TrieNode { /// ## Returns /// - `Err(_)` - Could not delete the node at the given path in the trie. /// - `Ok(())` - The node was successfully deleted at the given path. - pub fn delete( + pub fn delete( &mut self, path: &Nibbles, fetcher: &F, hinter: &H, - ) -> Result<()> { + ) -> TrieNodeResult<()> { match self { - TrieNode::Empty => { - anyhow::bail!("Key does not exist in trie (empty node)") - } + TrieNode::Empty => Err(TrieNodeError::KeyNotFound(self.to_string())), TrieNode::Leaf { prefix, .. } => { if path == prefix { *self = TrieNode::Empty; Ok(()) } else { - anyhow::bail!("Key does not exist in trie (leaf node mismatch)") + Err(TrieNodeError::KeyNotFound(self.to_string())) } } TrieNode::Extension { prefix, node } => { let shared_nibbles = path.common_prefix_length(prefix); if shared_nibbles < prefix.len() { - anyhow::bail!("Key does not exist in trie (extension node mismatch)") + return Err(TrieNodeError::KeyNotFound(self.to_string())); } else if shared_nibbles == path.len() { *self = TrieNode::Empty; return Ok(()); @@ -405,11 +422,11 @@ impl TrieNode { /// ## Returns /// - `Ok(())` - The node was successfully collapsed /// - `Err(_)` - Could not collapse the node - fn collapse_if_possible( + fn collapse_if_possible( &mut self, fetcher: &F, hinter: &H, - ) -> Result<()> { + ) -> TrieNodeResult<()> { match self { TrieNode::Extension { prefix, node } => match node.as_mut() { TrieNode::Extension { prefix: child_prefix, node: child_node } => { @@ -472,7 +489,9 @@ impl TrieNode { // In this special case, we need to send a hint to fetch the preimage of // the blinded node, since it is outside of the paths that have been // traversed so far. - hinter.hint_trie_node(*commitment)?; + hinter + .hint_trie_node(*commitment) + .map_err(|e| TrieNodeError::Provider(e.to_string()))?; non_empty_node.unblind(fetcher)?; self.collapse_if_possible(fetcher, hinter)?; @@ -491,14 +510,14 @@ impl TrieNode { /// /// **Note:** This function assumes that the passed reader has already consumed the RLP header /// of the [TrieNode::Leaf] or [TrieNode::Extension] node. - fn try_decode_leaf_or_extension_payload(buf: &mut &[u8]) -> Result { + fn try_decode_leaf_or_extension_payload(buf: &mut &[u8]) -> TrieNodeResult { // Decode the path and value of the leaf or extension node. - let path = Bytes::decode(buf).map_err(|e| anyhow!("Failed to decode: {e}"))?; + let path = Bytes::decode(buf).map_err(TrieNodeError::RLPError)?; let first_nibble = path[0] >> NIBBLE_WIDTH; let first = match first_nibble { PREFIX_EXTENSION_ODD | PREFIX_LEAF_ODD => Some(path[0] & 0x0F), PREFIX_EXTENSION_EVEN | PREFIX_LEAF_EVEN => None, - _ => anyhow::bail!("Unexpected path identifier in high-order nibble"), + _ => return Err(TrieNodeError::InvalidNodeType), }; // Check the high-order nibble of the path to determine the type of node. @@ -506,7 +525,7 @@ impl TrieNode { PREFIX_EXTENSION_EVEN | PREFIX_EXTENSION_ODD => { // Extension node let extension_node_value = - TrieNode::decode(buf).map_err(|e| anyhow!("Failed to decode: {e}"))?; + TrieNode::decode(buf).map_err(TrieNodeError::RLPError)?; Ok(TrieNode::Extension { prefix: unpack_path_to_nibbles(first, path[1..].as_ref()), node: Box::new(extension_node_value), @@ -514,15 +533,13 @@ impl TrieNode { } PREFIX_LEAF_EVEN | PREFIX_LEAF_ODD => { // Leaf node - let value = Bytes::decode(buf).map_err(|e| anyhow!("Failed to decode: {e}"))?; + let value = Bytes::decode(buf).map_err(TrieNodeError::RLPError)?; Ok(TrieNode::Leaf { prefix: unpack_path_to_nibbles(first, path[1..].as_ref()), value, }) } - _ => { - anyhow::bail!("Unexpected path identifier in high-order nibble") - } + _ => Err(TrieNodeError::InvalidNodeType), } } @@ -671,8 +688,8 @@ impl Decodable for TrieNode { mod test { use super::*; use crate::{ - fetcher::NoopTrieDBFetcher, ordered_trie_with_encoder, test_util::TrieNodeProvider, - NoopTrieDBHinter, TrieNode, + fetcher::NoopTrieProvider, ordered_trie_with_encoder, test_util::TrieNodeProvider, + NoopTrieHinter, TrieNode, }; use alloc::{collections::BTreeMap, vec, vec::Vec}; use alloy_primitives::{b256, bytes, hex, keccak256}; @@ -806,7 +823,7 @@ mod test { #[test] fn test_insert_static() { let mut node = TrieNode::Empty; - let noop_fetcher = NoopTrieDBFetcher; + let noop_fetcher = NoopTrieProvider; node.insert(&Nibbles::unpack(hex!("012345")), bytes!("01"), &noop_fetcher).unwrap(); node.insert(&Nibbles::unpack(hex!("012346")), bytes!("02"), &noop_fetcher).unwrap(); @@ -850,7 +867,7 @@ mod test { for key in keys { hb.add_leaf(Nibbles::unpack(key), key.as_ref()); - node.insert(&Nibbles::unpack(key), key.into(), &NoopTrieDBFetcher).unwrap(); + node.insert(&Nibbles::unpack(key), key.into(), &NoopTrieProvider).unwrap(); } node.blind(); @@ -876,12 +893,12 @@ mod test { if !deleted_keys.contains(&key) { hb.add_leaf(Nibbles::unpack(key), key.as_ref()); } - node.insert(&Nibbles::unpack(key), key.into(), &NoopTrieDBFetcher).unwrap(); + node.insert(&Nibbles::unpack(key), key.into(), &NoopTrieProvider).unwrap(); } // Delete the keys that were randomly selected from the trie node. for deleted_key in deleted_keys { - node.delete(&Nibbles::unpack(deleted_key), &NoopTrieDBFetcher, &NoopTrieDBHinter) + node.delete(&Nibbles::unpack(deleted_key), &NoopTrieProvider, &NoopTrieHinter) .unwrap(); } diff --git a/crates/mpt/src/test_util.rs b/crates/mpt/src/test_util.rs index e4cf4281ed..2716a24616 100644 --- a/crates/mpt/src/test_util.rs +++ b/crates/mpt/src/test_util.rs @@ -2,7 +2,7 @@ extern crate std; -use crate::{ordered_trie_with_encoder, TrieDBFetcher}; +use crate::{ordered_trie_with_encoder, TrieProvider}; use alloc::{collections::BTreeMap, vec::Vec}; use alloy_consensus::{Receipt, ReceiptEnvelope, ReceiptWithBloom, TxEnvelope, TxType}; use alloy_primitives::{keccak256, Bytes, Log, B256}; @@ -116,7 +116,7 @@ pub(crate) async fn get_live_derivable_transactions_list( Ok((root, preimages, consensus_txs)) } -/// A mock [TrieDBFetcher] for testing that serves in-memory preimages. +/// A mock [TrieProvider] for testing that serves in-memory preimages. pub(crate) struct TrieNodeProvider { preimages: BTreeMap, bytecode: BTreeMap, @@ -133,7 +133,9 @@ impl TrieNodeProvider { } } -impl TrieDBFetcher for TrieNodeProvider { +impl TrieProvider for TrieNodeProvider { + type Error = anyhow::Error; + fn trie_node_preimage(&self, key: B256) -> Result { self.preimages.get(&key).cloned().ok_or_else(|| anyhow!("Key not found")) }