reth_stateless/witness_db.rs
1//! Provides the [`WitnessDatabase`] type, an implementation of [`reth_revm::Database`]
2//! specifically designed for stateless execution environments.
3
4use crate::trie::StatelessTrie;
5use alloc::{collections::btree_map::BTreeMap, format};
6use alloy_primitives::{map::B256Map, Address, B256, U256};
7use reth_errors::ProviderError;
8use reth_revm::{bytecode::Bytecode, state::AccountInfo, Database};
9
10/// An EVM database implementation backed by witness data.
11///
12/// This struct implements the [`reth_revm::Database`] trait, allowing the EVM to execute
13/// transactions using:
14/// - Account and storage slot data provided by a [`StatelessTrie`] implementation.
15/// - Bytecode and ancestor block hashes provided by in-memory maps.
16///
17/// This is designed for stateless execution scenarios where direct access to a full node's
18/// database is not available or desired.
19#[derive(Debug)]
20pub(crate) struct WitnessDatabase<'a, T>
21where
22 T: StatelessTrie,
23{
24 /// Map of block numbers to block hashes.
25 /// This is used to service the `BLOCKHASH` opcode.
26 block_hashes_by_block_number: BTreeMap<u64, B256>,
27 /// Map of code hashes to bytecode.
28 /// Used to fetch contract code needed during execution.
29 bytecode: B256Map<Bytecode>,
30 /// The sparse Merkle Patricia Trie containing account and storage state.
31 /// This is used to provide account/storage values during EVM execution.
32 trie: &'a T,
33}
34
35impl<'a, T> WitnessDatabase<'a, T>
36where
37 T: StatelessTrie,
38{
39 /// Creates a new [`WitnessDatabase`] instance.
40 ///
41 /// # Assumptions
42 ///
43 /// This function assumes:
44 /// 1. The provided `trie` has been populated with state data consistent with a known state root
45 /// (e.g., using witness data and verifying against a parent block's state root).
46 /// 2. The `bytecode` map contains all bytecode corresponding to code hashes present in the
47 /// account data within the `trie`.
48 /// 3. The `ancestor_hashes` map contains the block hashes for the relevant ancestor blocks (up
49 /// to 256 including the current block number). It assumes these hashes correspond to a
50 /// contiguous chain of blocks. The caller is responsible for verifying the contiguity and
51 /// the block limit.
52 pub(crate) const fn new(
53 trie: &'a T,
54 bytecode: B256Map<Bytecode>,
55 ancestor_hashes: BTreeMap<u64, B256>,
56 ) -> Self {
57 Self { trie, block_hashes_by_block_number: ancestor_hashes, bytecode }
58 }
59}
60
61impl<T> Database for WitnessDatabase<'_, T>
62where
63 T: StatelessTrie,
64{
65 /// The database error type.
66 type Error = ProviderError;
67
68 /// Get basic account information by hashing the address and looking up the account RLP
69 /// in the underlying [`StatelessTrie`] implementation.
70 ///
71 /// Returns `Ok(None)` if the account is not found in the trie.
72 fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
73 self.trie.account(address).map(|opt| {
74 opt.map(|account| AccountInfo {
75 balance: account.balance,
76 nonce: account.nonce,
77 code_hash: account.code_hash,
78 code: None,
79 })
80 })
81 }
82
83 /// Get storage value of an account at a specific slot.
84 ///
85 /// Returns `U256::ZERO` if the slot is not found in the trie.
86 fn storage(&mut self, address: Address, slot: U256) -> Result<U256, Self::Error> {
87 self.trie.storage(address, slot)
88 }
89
90 /// Get account code by its hash from the provided bytecode map.
91 ///
92 /// Returns an error if the bytecode for the given hash is not found in the map.
93 fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> {
94 self.bytecode.get(&code_hash).cloned().ok_or_else(|| {
95 ProviderError::TrieWitnessError(format!("bytecode for {code_hash} not found"))
96 })
97 }
98
99 /// Get block hash by block number from the provided ancestor hashes map.
100 ///
101 /// Returns an error if the hash for the given block number is not found in the map.
102 fn block_hash(&mut self, block_number: u64) -> Result<B256, Self::Error> {
103 self.block_hashes_by_block_number
104 .get(&block_number)
105 .copied()
106 .ok_or(ProviderError::StateForNumberNotFound(block_number))
107 }
108}