reth_revm/witness.rs
1use alloc::vec::Vec;
2use alloy_primitives::{keccak256, Bytes, B256};
3use reth_trie::{HashedPostState, HashedStorage};
4use revm::database::State;
5
6/// Tracks state changes during execution.
7#[derive(Debug, Clone, Default)]
8pub struct ExecutionWitnessRecord {
9 /// Records all state changes
10 pub hashed_state: HashedPostState,
11 /// Map of all contract codes (created / accessed) to their preimages that were required during
12 /// the execution of the block, including during state root recomputation.
13 ///
14 /// `keccak(bytecodes) => bytecodes`
15 pub codes: Vec<Bytes>,
16 /// Map of all hashed account and storage keys (addresses and slots) to their preimages
17 /// (unhashed account addresses and storage slots, respectively) that were required during
18 /// the execution of the block.
19 ///
20 /// `keccak(address|slot) => address|slot`
21 pub keys: Vec<Bytes>,
22 /// The lowest block number referenced by any BLOCKHASH opcode call during transaction
23 /// execution.
24 ///
25 /// This helps determine which ancestor block headers must be included in the
26 /// `ExecutionWitness`.
27 ///
28 /// `None` - when the BLOCKHASH opcode was not called during execution
29 pub lowest_block_number: Option<u64>,
30}
31
32impl ExecutionWitnessRecord {
33 /// Records the state after execution.
34 pub fn record_executed_state<DB>(&mut self, statedb: &State<DB>) {
35 self.codes = statedb
36 .cache
37 .contracts
38 .values()
39 .map(|code| code.original_bytes())
40 .chain(
41 // cache state does not have all the contracts, especially when
42 // a contract is created within the block
43 // the contract only exists in bundle state, therefore we need
44 // to include them as well
45 statedb.bundle_state.contracts.values().map(|code| code.original_bytes()),
46 )
47 .collect();
48
49 for (address, account) in &statedb.cache.accounts {
50 let hashed_address = keccak256(address);
51 self.hashed_state
52 .accounts
53 .insert(hashed_address, account.account.as_ref().map(|a| (&a.info).into()));
54
55 let storage = self
56 .hashed_state
57 .storages
58 .entry(hashed_address)
59 .or_insert_with(|| HashedStorage::new(account.status.was_destroyed()));
60
61 if let Some(account) = &account.account {
62 self.keys.push(address.to_vec().into());
63
64 for (slot, value) in &account.storage {
65 let slot = B256::from(*slot);
66 let hashed_slot = keccak256(slot);
67 storage.storage.insert(hashed_slot, *value);
68
69 self.keys.push(slot.into());
70 }
71 }
72 }
73 // BTreeMap keys are ordered, so the first key is the smallest
74 self.lowest_block_number = statedb.block_hashes.keys().next().copied()
75 }
76
77 /// Creates the record from the state after execution.
78 pub fn from_executed_state<DB>(state: &State<DB>) -> Self {
79 let mut record = Self::default();
80 record.record_executed_state(state);
81 record
82 }
83}