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}