1use alloc::vec::Vec;
2use alloy_primitives::{keccak256, Bytes, B256};
3use reth_trie::{ExecutionWitnessMode, HashedPostState, HashedStorage};
4use revm::database::State;
5
6#[derive(Debug, Clone, Default)]
8pub struct ExecutionWitnessRecord {
9 pub hashed_state: HashedPostState,
11 pub codes: Vec<Bytes>,
16 pub keys: Vec<Bytes>,
22 pub lowest_block_number: Option<u64>,
30}
31
32impl ExecutionWitnessRecord {
33 pub fn record_executed_state<DB>(&mut self, statedb: &State<DB>, mode: ExecutionWitnessMode) {
35 self.codes = match mode {
36 ExecutionWitnessMode::Legacy => statedb
37 .cache
38 .contracts
39 .values()
40 .map(|code| code.original_bytes())
41 .chain(
42 statedb.bundle_state.contracts.values().map(|code| code.original_bytes()),
47 )
48 .collect(),
49 ExecutionWitnessMode::Canonical => {
50 let mut codes: Vec<_> = statedb
51 .cache
52 .contracts
53 .values()
54 .map(|c| c.original_bytes())
55 .filter(|code| !code.is_empty())
56 .collect();
57 codes.sort_unstable();
58 codes
59 }
60 };
61
62 for (address, account) in &statedb.cache.accounts {
63 let hashed_address = keccak256(address);
64 self.hashed_state
65 .accounts
66 .insert(hashed_address, account.account.as_ref().map(|a| (&a.info).into()));
67
68 let storage = self
69 .hashed_state
70 .storages
71 .entry(hashed_address)
72 .or_insert_with(|| HashedStorage::new(account.status.was_destroyed()));
73
74 if let Some(account) = &account.account {
75 self.keys.push(address.to_vec().into());
76
77 for (slot, value) in &account.storage {
78 let slot = B256::from(*slot);
79 let hashed_slot = keccak256(slot);
80 storage.storage.insert(hashed_slot, *value);
81
82 self.keys.push(slot.into());
83 }
84 }
85 }
86 self.lowest_block_number =
87 statedb.block_hashes.lowest().map(|(block_number, _)| block_number)
88 }
89
90 pub fn from_executed_state<DB>(state: &State<DB>, mode: ExecutionWitnessMode) -> Self {
92 let mut record = Self::default();
93 record.record_executed_state(state, mode);
94 record
95 }
96
97 #[cfg(feature = "witness")]
104 pub fn into_execution_witness<SP, HP>(
105 self,
106 state_provider: &SP,
107 headers_provider: &HP,
108 block_number: u64,
109 mode: ExecutionWitnessMode,
110 ) -> reth_storage_errors::provider::ProviderResult<alloy_rpc_types_debug::ExecutionWitness>
111 where
112 SP: reth_storage_api::StateProofProvider + ?Sized,
113 HP: reth_storage_api::HeaderProvider + ?Sized,
114 HP::Header: alloy_rlp::Encodable,
115 {
116 let Self { hashed_state, codes, keys, lowest_block_number } = self;
117
118 let state = state_provider.witness(Default::default(), hashed_state, mode)?;
119 let mut exec_witness =
120 alloy_rpc_types_debug::ExecutionWitness { state, codes, keys, ..Default::default() };
121
122 let smallest = lowest_block_number.unwrap_or_else(|| block_number.saturating_sub(1));
123 let range = smallest..block_number;
124
125 exec_witness.headers = headers_provider
126 .headers_range(range)?
127 .into_iter()
128 .map(|header| {
129 let mut buf = Vec::new();
130 alloy_rlp::Encodable::encode(&header, &mut buf);
131 buf.into()
132 })
133 .collect();
134
135 Ok(exec_witness)
136 }
137}