1use crate::{event, host::db::ProviderDb, mpt::EMPTY_ROOT_HASH, MerkleTrie, StateAccount};
16use alloy::{
17 consensus::BlockHeader,
18 eips::eip2930::{AccessList, AccessListItem},
19 network::{BlockResponse, Network},
20 providers::Provider,
21 rpc::types::EIP1186AccountProofResponse,
22};
23use alloy_consensus::ReceiptEnvelope;
24use alloy_eips::eip2718::Encodable2718;
25use alloy_primitives::{
26 map::{hash_map, AddressHashMap, B256HashMap, B256HashSet, Entry, HashMap, HashSet},
27 Address, BlockNumber, Bytes, Log, StorageKey, StorageValue, B256, U256,
28};
29use alloy_rpc_types::{Filter, TransactionReceipt};
30use anyhow::{ensure, Context, Result};
31use revm::{
32 primitives::KECCAK_EMPTY,
33 state::{AccountInfo, Bytecode},
34 Database as RevmDatabase,
35};
36use std::{
37 fmt::{self, Debug},
38 hash::{BuildHasher, Hash},
39};
40
41pub struct ProofDb<D> {
43 accounts: AddressHashMap<B256HashSet>,
44 contracts: B256HashMap<Bytes>,
45 block_hash_numbers: HashSet<BlockNumber>,
46 log_filters: Vec<Filter>,
47 proofs: AddressHashMap<AccountProof>,
48 inner: D,
49}
50
51#[derive(Clone, Debug, PartialEq, Eq)]
52struct AccountProof {
53 account: StateAccount,
55 account_proof: Vec<Bytes>,
57 storage_proofs: B256HashMap<StorageProof>,
59}
60
61#[derive(Clone, Debug, PartialEq, Eq)]
62struct StorageProof {
63 value: StorageValue,
65 proof: Vec<Bytes>,
67}
68
69impl<D> Debug for ProofDb<D> {
70 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71 f.debug_struct("PreflightDb")
72 .field("accounts", &self.accounts)
73 .field("contracts", &self.contracts)
74 .field("block_hash_numbers", &self.block_hash_numbers)
75 .field("log_filters", &self.log_filters)
76 .finish_non_exhaustive()
77 }
78}
79
80impl<D> ProofDb<D> {
81 pub(crate) fn new(db: D) -> Self
83 where
84 D: RevmDatabase,
85 {
86 Self {
87 accounts: Default::default(),
88 contracts: Default::default(),
89 block_hash_numbers: Default::default(),
90 log_filters: Default::default(),
91 proofs: Default::default(),
92 inner: db,
93 }
94 }
95
96 pub(crate) fn add_proof(&mut self, proof: EIP1186AccountProofResponse) -> Result<()> {
100 add_proof(&mut self.proofs, proof)
101 }
102
103 pub(crate) fn contracts(&self) -> &B256HashMap<Bytes> {
105 &self.contracts
106 }
107
108 pub(crate) fn inner(&self) -> &D {
110 &self.inner
111 }
112
113 #[must_use = "merge consumes self and returns a new ProofDb"]
117 pub(crate) fn merge(self, other: Self) -> Self {
118 let accounts = merge_checked_maps(self.accounts, other.accounts);
119 let contracts = merge_checked_maps(self.contracts, other.contracts);
120 let proofs = merge_checked_maps(self.proofs, other.proofs);
121 let mut block_hash_numbers = self.block_hash_numbers;
123 block_hash_numbers.extend(other.block_hash_numbers);
124 let log_filters = self
126 .log_filters
127 .into_iter()
128 .chain(other.log_filters)
129 .collect::<HashSet<_>>()
130 .into_iter()
131 .collect();
132
133 ProofDb {
135 accounts,
136 contracts,
137 block_hash_numbers,
138 log_filters,
139 proofs,
140 inner: self.inner,
141 }
142 }
143}
144
145impl<N: Network, P: Provider<N>> ProofDb<ProviderDb<N, P>> {
146 pub(crate) async fn add_access_list(&mut self, access_list: AccessList) -> Result<()> {
148 for AccessListItem {
149 address,
150 storage_keys,
151 } in access_list.0
152 {
153 let storage_keys: Vec<_> = storage_keys
154 .into_iter()
155 .filter(filter_existing_keys(self.proofs.get(&address)))
156 .collect();
157
158 let proof = self.get_proof(address, storage_keys).await?;
159 self.add_proof(proof)
160 .context("invalid eth_getProof response")?;
161 }
162
163 Ok(())
164 }
165
166 pub(crate) async fn state_account(&mut self, address: Address) -> Result<StateAccount> {
168 log::trace!("ACCOUNT: address={address}");
169 self.accounts.entry(address).or_default();
170
171 if !self.proofs.contains_key(&address) {
172 let proof = self.get_proof(address, vec![]).await?;
173 self.add_proof(proof)
174 .context("invalid eth_getProof response")?;
175 }
176 let proof = self.proofs.get(&address).unwrap();
177
178 Ok(proof.account)
179 }
180
181 pub(crate) async fn ancestor_proof(
183 &self,
184 block_number: BlockNumber,
185 ) -> Result<Vec<<N as Network>::HeaderResponse>> {
186 let mut ancestors = Vec::new();
187 if let Some(&block_hash_min_number) = self.block_hash_numbers.iter().min() {
188 assert!(block_hash_min_number <= block_number);
189
190 let provider = self.inner.provider();
191 for number in (block_hash_min_number..block_number).rev() {
192 let rpc_block = provider
193 .get_block_by_number(number.into())
194 .await
195 .context("eth_getBlockByNumber failed")?
196 .with_context(|| format!("block {number} not found"))?;
197 ancestors.push(rpc_block.header().clone());
198 }
199 }
200
201 Ok(ancestors)
202 }
203
204 pub(crate) async fn state_proof(&mut self) -> Result<(MerkleTrie, Vec<MerkleTrie>)> {
207 ensure!(
208 !self.accounts.is_empty()
209 || !self.block_hash_numbers.is_empty()
210 || !self.log_filters.is_empty(),
211 "no accounts accessed: use Contract::preflight"
212 );
213
214 if self.accounts.is_empty() {
216 let hash = self.inner.block();
217 let block = self
218 .inner
219 .provider()
220 .get_block_by_hash(hash)
221 .await
222 .context("eth_getBlockByHash failed")?
223 .with_context(|| format!("block {hash} not found"))?;
224
225 return Ok((
226 MerkleTrie::from_digest(block.header().state_root()),
227 Vec::default(),
228 ));
229 }
230
231 let proofs = &mut self.proofs;
232 for (address, storage_keys) in &self.accounts {
233 let account_proof = proofs.get(address);
234 let storage_keys: Vec<_> = storage_keys
235 .iter()
236 .cloned()
237 .filter(filter_existing_keys(account_proof))
238 .collect();
239
240 if account_proof.is_none() || !storage_keys.is_empty() {
241 log::trace!("PROOF: address={}, #keys={}", address, storage_keys.len());
242 let proof = self
243 .inner
244 .get_proof(*address, storage_keys)
245 .await
246 .context("eth_getProof failed")?;
247 ensure!(
248 &proof.address == address,
249 "eth_getProof response does not match request"
250 );
251 add_proof(proofs, proof).context("invalid eth_getProof response")?;
252 }
253 }
254
255 let state_nodes = self
256 .accounts
257 .keys()
258 .filter_map(|address| proofs.get(address))
259 .flat_map(|proof| proof.account_proof.iter());
260 let state_trie = MerkleTrie::from_rlp_nodes(state_nodes).context("accountProof invalid")?;
261
262 let mut storage_tries: B256HashMap<MerkleTrie> = B256HashMap::default();
263 for (address, storage_keys) in &self.accounts {
264 if storage_keys.is_empty() {
266 continue;
267 }
268
269 let proof = proofs.get(address).unwrap();
271
272 let storage_nodes = storage_keys
273 .iter()
274 .filter_map(|key| proof.storage_proofs.get(key))
275 .flat_map(|proof| proof.proof.iter());
276 let storage_root = proof.account.storage_root;
277
278 match storage_tries.entry(storage_root) {
279 Entry::Occupied(mut entry) => {
280 entry
282 .get_mut()
283 .hydrate_from_rlp_nodes(storage_nodes)
284 .with_context(|| format!("invalid storage proof for address {address}"))?;
285 ensure!(
286 entry.get().hash_slow() == storage_root,
287 "storage root mismatch"
288 );
289 }
290 Entry::Vacant(entry) => {
291 let storage_trie = MerkleTrie::from_rlp_nodes(storage_nodes)
293 .with_context(|| format!("invalid storage proof for address {address}"))?;
294 ensure!(
295 storage_trie.hash_slow() == storage_root,
296 "storage root mismatch"
297 );
298 entry.insert(storage_trie);
299 }
300 }
301 }
302 let storage_tries = storage_tries.into_values().collect();
303
304 Ok((state_trie, storage_tries))
305 }
306
307 pub async fn receipt_proof(&self) -> Result<Option<Vec<ReceiptEnvelope>>> {
308 if self.log_filters.is_empty() {
309 return Ok(None);
310 }
311
312 let provider = self.inner.provider();
313 let block_hash = self.inner.block();
314
315 let block = provider
316 .get_block_by_hash(block_hash)
317 .await
318 .context("eth_getBlockByHash failed")?
319 .with_context(|| format!("block {block_hash} not found"))?;
320 let header = block.header();
321
322 let bloom_match = self
324 .log_filters
325 .iter()
326 .any(|filter| event::matches_filter(header.logs_bloom(), filter));
327 if !bloom_match {
328 return Ok(None);
329 }
330
331 let rpc_receipts = provider
332 .get_block_receipts(block_hash.into())
333 .await
334 .context("eth_getBlockReceipts failed")?
335 .with_context(|| format!("block {block_hash} not found"))?;
336
337 let receipts = convert_rpc_receipts::<N>(rpc_receipts, header.receipts_root())
339 .context("invalid receipts; inconsistent API response or incompatible response type")?;
340
341 Ok(Some(receipts))
342 }
343
344 async fn get_proof(
345 &self,
346 address: Address,
347 storage_keys: Vec<StorageKey>,
348 ) -> Result<EIP1186AccountProofResponse> {
349 log::trace!("PROOF: address={}, #keys={}", address, storage_keys.len());
350 let proof = self
351 .inner
352 .get_proof(address, storage_keys)
353 .await
354 .context("eth_getProof failed")?;
355 ensure!(
356 proof.address == address,
357 "eth_getProof response does not match request"
358 );
359
360 Ok(proof)
361 }
362}
363
364impl<DB: RevmDatabase> RevmDatabase for ProofDb<DB> {
365 type Error = DB::Error;
366
367 fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
368 log::trace!("BASIC: address={address}");
369 self.accounts.entry(address).or_default();
370
371 self.inner.basic(address)
374 }
375
376 fn code_by_hash(&mut self, hash: B256) -> Result<Bytecode, Self::Error> {
377 log::trace!("CODE: hash={hash}");
378 let code = self.inner.code_by_hash(hash)?;
379 self.contracts.insert(hash, code.original_bytes());
380
381 Ok(code)
382 }
383
384 fn storage(&mut self, address: Address, index: U256) -> Result<U256, Self::Error> {
385 let key = StorageKey::from(index);
386 self.accounts.entry(address).or_default().insert(key);
387
388 match self
390 .proofs
391 .get(&address)
392 .and_then(|account| account.storage_proofs.get(&key))
393 {
394 Some(storage_proof) => Ok(storage_proof.value),
395 None => {
396 log::trace!("STORAGE: address={address}, index={key}");
397 self.inner.storage(address, index)
398 }
399 }
400 }
401
402 fn block_hash(&mut self, number: u64) -> Result<B256, Self::Error> {
403 log::trace!("BLOCK: number={number}");
404 self.block_hash_numbers.insert(number);
405
406 self.inner.block_hash(number)
407 }
408}
409
410impl<DB: crate::EvmDatabase> crate::EvmDatabase for ProofDb<DB> {
411 fn logs(&mut self, filter: Filter) -> Result<Vec<Log>, <Self as RevmDatabase>::Error> {
412 log::trace!("LOGS: filter={:?}", &filter);
413 let logs = self.inner.logs(filter.clone())?;
414
415 self.log_filters.push(filter);
416
417 Ok(logs)
418 }
419}
420
421fn merge_checked_maps<K, V, S, T>(mut map: HashMap<K, V, S>, iter: T) -> HashMap<K, V, S>
424where
425 K: Eq + Hash + Debug,
426 V: PartialEq + Debug,
427 S: BuildHasher,
428 T: IntoIterator<Item = (K, V)>,
429{
430 let iter = iter.into_iter();
431 let (lower_bound, _) = iter.size_hint();
432 map.reserve(lower_bound);
433
434 for (key, value2) in iter {
435 match map.entry(key) {
436 hash_map::Entry::Vacant(entry) => {
437 entry.insert(value2);
438 }
439 hash_map::Entry::Occupied(entry) => {
440 let value1 = entry.get();
441 if value1 != &value2 {
442 panic!(
443 "mismatching values for key {:?}: existing={:?}, other={:?}",
444 entry.key(),
445 value1,
446 value2
447 );
448 }
449 }
450 }
451 }
452
453 map
454}
455
456fn filter_existing_keys(account_proof: Option<&AccountProof>) -> impl Fn(&StorageKey) -> bool + '_ {
457 move |key| {
458 !account_proof
459 .map(|p| p.storage_proofs.contains_key(key))
460 .unwrap_or_default()
461 }
462}
463
464fn add_proof(
465 proofs: &mut AddressHashMap<AccountProof>,
466 proof_response: EIP1186AccountProofResponse,
467) -> Result<()> {
468 let storage_proofs = proof_response
470 .storage_proof
471 .into_iter()
472 .map(|proof| {
473 (
474 proof.key.as_b256(),
475 StorageProof {
476 value: proof.value,
477 proof: proof.proof,
478 },
479 )
480 })
481 .collect();
482
483 let account = StateAccount {
486 nonce: proof_response.nonce,
487 balance: proof_response.balance,
488 storage_root: default_if_zero(proof_response.storage_hash, EMPTY_ROOT_HASH),
489 code_hash: default_if_zero(proof_response.code_hash, KECCAK_EMPTY),
490 };
491
492 match proofs.entry(proof_response.address) {
493 hash_map::Entry::Occupied(mut entry) => {
494 let account_proof = entry.get_mut();
495 ensure!(
496 account_proof.account == account
497 && account_proof.account_proof == proof_response.account_proof,
498 "inconsistent proof response"
499 );
500 account_proof.storage_proofs = merge_checked_maps(
501 std::mem::take(&mut account_proof.storage_proofs),
502 storage_proofs,
503 );
504 }
505 hash_map::Entry::Vacant(entry) => {
506 entry.insert(AccountProof {
507 account,
508 account_proof: proof_response.account_proof,
509 storage_proofs,
510 });
511 }
512 }
513
514 Ok(())
515}
516
517fn default_if_zero(hash: B256, default: B256) -> B256 {
518 if hash.is_zero() {
519 default
520 } else {
521 hash
522 }
523}
524
525fn convert_rpc_receipts<N: Network>(
527 rpc_receipts: impl IntoIterator<Item = <N as Network>::ReceiptResponse>,
528 receipts_root: B256,
529) -> Result<Vec<ReceiptEnvelope>> {
530 let receipts = rpc_receipts
531 .into_iter()
532 .map(|rpc_receipt| {
533 let json = serde_json::to_value(rpc_receipt).context("failed to serialize")?;
539 let tx_receipt: TransactionReceipt = serde_json::from_value(json)
540 .context("failed to parse as Ethereum transaction receipt")?;
541
542 Ok(tx_receipt.inner.into_primitives_receipt())
543 })
544 .collect::<Result<Vec<_>>>()?;
545
546 let root =
548 alloy_trie::root::ordered_trie_root_with_encoder(&receipts, |r, out| r.encode_2718(out));
549 ensure!(root == receipts_root, "receipts root mismatch");
550
551 Ok(receipts)
552}