|
1 | | -use sbv_kv::{HashMap, KeyValueStoreGet}; |
2 | | -pub use sbv_primitives::types::revm::database::DatabaseRef; |
| 1 | +//! Most copied from <https://github.com/paradigmxyz/reth/blob/5c18df9889941837e61929be4b51abb75f07f152/crates/stateless/src/witness_db.rs> |
| 2 | +//! Under MIT license |
| 3 | +
|
| 4 | +use reth_stateless::StatelessTrie; |
| 5 | +pub use sbv_primitives::types::revm::database::Database; |
3 | 6 | use sbv_primitives::{ |
4 | | - Address, B256, Bytes, U256, |
5 | | - types::revm::{ |
6 | | - AccountInfo, Bytecode, |
7 | | - database::{BundleAccount, DBErrorMarker}, |
| 7 | + Address, B256, U256, |
| 8 | + alloy_primitives::map::B256Map, |
| 9 | + types::{ |
| 10 | + reth::evm::execute::ProviderError, |
| 11 | + revm::{AccountInfo, Bytecode}, |
8 | 12 | }, |
9 | 13 | }; |
10 | | -use sbv_trie::PartialStateTrie; |
11 | | -use std::{cell::RefCell, collections::BTreeMap, fmt}; |
| 14 | +use sbv_trie::r0::SparseState; |
| 15 | +use std::collections::BTreeMap; |
12 | 16 |
|
13 | 17 | /// A database that consists of account and storage information. |
14 | | -pub struct EvmDatabase<CodeDb, BlockHashProvider> { |
15 | | - /// Map of code hash to bytecode. |
16 | | - pub(crate) code_db: CodeDb, |
17 | | - /// Cache of analyzed code |
18 | | - analyzed_code_cache: RefCell<HashMap<B256, Option<Bytecode>>>, |
19 | | - /// partial merkle patricia trie |
20 | | - pub(crate) state: PartialStateTrie, |
21 | | - /// Provider of block hashes |
22 | | - block_hashes: BlockHashProvider, |
23 | | -} |
24 | | - |
25 | | -/// Database error. |
26 | | -#[derive(Debug, thiserror::Error)] |
27 | | -pub enum DatabaseError { |
28 | | - /// Missing L2 message queue witness |
29 | | - #[cfg(feature = "scroll")] |
30 | | - #[error("missing L2 message queue witness")] |
31 | | - MissingL2MessageQueueWitness, |
32 | | - /// Partial state trie error |
33 | | - #[error(transparent)] |
34 | | - PartialStateTrie(#[from] sbv_trie::PartialStateTrieError), |
35 | | - /// Requested code not loaded |
36 | | - #[error("requested code({0}) not loaded")] |
37 | | - CodeNotLoaded(B256), |
38 | | -} |
39 | | - |
40 | | -type Result<T, E = DatabaseError> = std::result::Result<T, E>; |
41 | | - |
42 | | -impl<CodeDb, BlockHashProvider> fmt::Debug for EvmDatabase<CodeDb, BlockHashProvider> { |
43 | | - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
44 | | - f.debug_struct("EvmDatabase").finish() |
45 | | - } |
| 18 | +#[derive(Debug)] |
| 19 | +pub struct WitnessDatabase<'a> { |
| 20 | + /// Map of block numbers to block hashes. |
| 21 | + /// This is used to service the `BLOCKHASH` opcode. |
| 22 | + block_hashes_by_block_number: &'a BTreeMap<u64, B256>, |
| 23 | + /// Map of code hashes to bytecode. |
| 24 | + /// Used to fetch contract code needed during execution. |
| 25 | + bytecode: &'a B256Map<Bytecode>, |
| 26 | + /// The sparse Merkle Patricia Trie containing account and storage state. |
| 27 | + /// This is used to provide account/storage values during EVM execution. |
| 28 | + pub(crate) trie: &'a SparseState, |
46 | 29 | } |
47 | 30 |
|
48 | | -impl<CodeDb: KeyValueStoreGet<B256, Bytes>, BlockHashProvider: KeyValueStoreGet<u64, B256>> |
49 | | - EvmDatabase<CodeDb, BlockHashProvider> |
50 | | -{ |
51 | | - /// Initialize an EVM database from a zkTrie root. |
52 | | - pub fn new(code_db: CodeDb, state: PartialStateTrie, block_hashes: BlockHashProvider) -> Self { |
53 | | - EvmDatabase { |
54 | | - code_db, |
55 | | - analyzed_code_cache: Default::default(), |
56 | | - block_hashes, |
57 | | - state, |
58 | | - } |
59 | | - } |
60 | | - |
61 | | - /// Update changes to the database. |
62 | | - pub fn commit(&mut self, post_state: BTreeMap<Address, BundleAccount>) -> Result<B256> { |
63 | | - Ok(self.state.update(post_state)?) |
64 | | - } |
65 | | - |
66 | | - /// Get the withdrawal trie root of scroll. |
| 31 | +impl<'a> WitnessDatabase<'a> { |
| 32 | + /// Creates a new [`WitnessDatabase`] instance. |
67 | 33 | /// |
68 | | - /// Note: this should not be confused with the withdrawal of the beacon chain. |
69 | | - #[cfg(feature = "scroll")] |
70 | | - pub fn withdraw_root(&self) -> Result<B256, DatabaseError> { |
71 | | - /// L2MessageQueue pre-deployed address |
72 | | - pub const ADDRESS: Address = |
73 | | - sbv_primitives::address!("5300000000000000000000000000000000000000"); |
74 | | - /// the slot of withdraw root in L2MessageQueue |
75 | | - pub const WITHDRAW_TRIE_ROOT_SLOT: U256 = U256::ZERO; |
76 | | - |
77 | | - self.basic_ref(ADDRESS)? |
78 | | - .ok_or(DatabaseError::MissingL2MessageQueueWitness)?; |
79 | | - let withdraw_root = self.storage_ref(ADDRESS, WITHDRAW_TRIE_ROOT_SLOT)?; |
80 | | - Ok(withdraw_root.into()) |
81 | | - } |
82 | | - |
83 | | - fn load_code(&self, hash: B256) -> Option<Bytecode> { |
84 | | - let mut code_cache = self.analyzed_code_cache.borrow_mut(); |
85 | | - if let Some(code) = code_cache.get(&hash) { |
86 | | - code.clone() |
87 | | - } else { |
88 | | - let code = self.code_db.get(&hash).cloned().map(Bytecode::new_raw); |
89 | | - code_cache.insert(hash, code.clone()); |
90 | | - code |
| 34 | + /// # Assumptions |
| 35 | + /// |
| 36 | + /// This function assumes: |
| 37 | + /// 1. The provided `trie` has been populated with state data consistent with a known state root |
| 38 | + /// (e.g., using witness data and verifying against a parent block's state root). |
| 39 | + /// 2. The `bytecode` map contains all bytecode corresponding to code hashes present in the |
| 40 | + /// account data within the `trie`. |
| 41 | + /// 3. The `ancestor_hashes` map contains the block hashes for the relevant ancestor blocks (up |
| 42 | + /// to 256 including the current block number). It assumes these hashes correspond to a |
| 43 | + /// contiguous chain of blocks. The caller is responsible for verifying the contiguity and |
| 44 | + /// the block limit. |
| 45 | + pub(crate) const fn new( |
| 46 | + trie: &'a SparseState, |
| 47 | + bytecode: &'a B256Map<Bytecode>, |
| 48 | + ancestor_hashes: &'a BTreeMap<u64, B256>, |
| 49 | + ) -> Self { |
| 50 | + Self { |
| 51 | + trie, |
| 52 | + block_hashes_by_block_number: ancestor_hashes, |
| 53 | + bytecode, |
91 | 54 | } |
92 | 55 | } |
93 | 56 | } |
94 | 57 |
|
95 | | -impl<CodeDb: KeyValueStoreGet<B256, Bytes>, BlockHashProvider: KeyValueStoreGet<u64, B256>> |
96 | | - DatabaseRef for EvmDatabase<CodeDb, BlockHashProvider> |
97 | | -{ |
98 | | - type Error = DatabaseError; |
99 | | - |
100 | | - /// Get basic account information. |
101 | | - fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> { |
102 | | - let Some(account) = self.state.get_account(address)? else { |
103 | | - return Ok(None); |
104 | | - }; |
105 | | - dev_trace!("load trie account of {address:?}: {account:?}"); |
106 | | - let code = self.load_code(account.code_hash); |
107 | | - let info = AccountInfo { |
108 | | - balance: account.balance, |
109 | | - nonce: account.nonce, |
110 | | - code_hash: account.code_hash, |
111 | | - code, |
112 | | - }; |
113 | | - |
114 | | - #[cfg(debug_assertions)] |
115 | | - if let Some(ref code) = info.code { |
116 | | - assert_eq!( |
117 | | - info.code_hash, |
118 | | - code.hash_slow(), |
119 | | - "code hash mismatch for account {address:?}", |
120 | | - ); |
121 | | - } |
| 58 | +impl Database for WitnessDatabase<'_> { |
| 59 | + /// The database error type. |
| 60 | + type Error = ProviderError; |
122 | 61 |
|
123 | | - Ok(Some(info)) |
| 62 | + /// Get basic account information by hashing the address and looking up the account RLP |
| 63 | + /// in the underlying [`StatelessTrie`] implementation. |
| 64 | + /// |
| 65 | + /// Returns `Ok(None)` if the account is not found in the trie. |
| 66 | + fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> { |
| 67 | + self.trie.account(address).map(|opt| { |
| 68 | + opt.map(|account| AccountInfo { |
| 69 | + balance: account.balance, |
| 70 | + nonce: account.nonce, |
| 71 | + code_hash: account.code_hash, |
| 72 | + code: None, |
| 73 | + }) |
| 74 | + }) |
124 | 75 | } |
125 | 76 |
|
126 | | - /// Get account code by its code hash. |
127 | | - fn code_by_hash_ref(&self, hash: B256) -> Result<Bytecode, Self::Error> { |
128 | | - self.load_code(hash) |
129 | | - .ok_or(DatabaseError::CodeNotLoaded(hash)) |
| 77 | + /// Get account code by its hash from the provided bytecode map. |
| 78 | + /// |
| 79 | + /// Returns an error if the bytecode for the given hash is not found in the map. |
| 80 | + fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> { |
| 81 | + self.bytecode.get(&code_hash).cloned().ok_or_else(|| { |
| 82 | + ProviderError::TrieWitnessError(format!("bytecode for {code_hash} not found")) |
| 83 | + }) |
130 | 84 | } |
131 | 85 |
|
132 | | - /// Get storage value of address at index. |
133 | | - fn storage_ref(&self, address: Address, index: U256) -> Result<U256, Self::Error> { |
134 | | - dev_trace!("get storage of {:?} at index {:?}", address, index); |
135 | | - Ok(self.state.get_storage(address, index)?) |
| 86 | + /// Get storage value of an account at a specific slot. |
| 87 | + /// |
| 88 | + /// Returns `U256::ZERO` if the slot is not found in the trie. |
| 89 | + fn storage(&mut self, address: Address, slot: U256) -> Result<U256, Self::Error> { |
| 90 | + self.trie.storage(address, slot) |
136 | 91 | } |
137 | 92 |
|
138 | | - /// Get block hash by block number. |
139 | | - fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> { |
140 | | - Ok(*self |
141 | | - .block_hashes |
142 | | - .get(&number) |
143 | | - .unwrap_or_else(|| panic!("block hash of number {number} not found"))) |
| 93 | + /// Get block hash by block number from the provided ancestor hashes map. |
| 94 | + /// |
| 95 | + /// Returns an error if the hash for the given block number is not found in the map. |
| 96 | + fn block_hash(&mut self, block_number: u64) -> Result<B256, Self::Error> { |
| 97 | + self.block_hashes_by_block_number |
| 98 | + .get(&block_number) |
| 99 | + .copied() |
| 100 | + .ok_or(ProviderError::StateForNumberNotFound(block_number)) |
144 | 101 | } |
145 | 102 | } |
146 | | - |
147 | | -impl DBErrorMarker for DatabaseError {} |
0 commit comments