bankai_verify/evm/execution.rs
1extern crate alloc;
2use alloc::vec::Vec;
3
4use alloy_rlp::{Decodable, Encodable};
5use bankai_types::fetch::evm::execution::{AccountProof, ExecutionHeaderProof, TxProof};
6use bankai_types::verify::evm::execution::{Account, ExecutionHeader, TxEnvelope};
7
8use alloy_primitives::{keccak256, FixedBytes};
9use alloy_rlp::encode as rlp_encode;
10use alloy_trie::{proof::verify_proof as mpt_verify, Nibbles};
11
12use crate::bankai::mmr::MmrVerifier;
13use crate::VerifyError;
14
15/// Verifier for EVM execution layer proofs
16///
17/// Provides methods to verify execution headers, account states, and transactions
18/// against trusted MMR roots and header state roots. All verifications are cryptographically
19/// sound and establish trust through the STWO proof → MMR proof → Merkle proof chain.
20pub struct ExecutionVerifier;
21
22impl ExecutionVerifier {
23 /// Verifies an execution layer header using an MMR inclusion proof
24 ///
25 /// This method establishes trust in an execution header by:
26 /// 1. Verifying the MMR root matches the expected root from the STWO proof
27 /// 2. Verifying the MMR inclusion proof
28 /// 3. Verifying the header hash matches the value committed in the MMR
29 ///
30 /// Once verified, the header can be trusted and used to verify accounts and transactions.
31 ///
32 /// # Arguments
33 ///
34 /// * `proof` - The execution header proof containing the header and MMR inclusion proof
35 /// * `root` - The trusted MMR root from the verified STWO proof
36 ///
37 /// # Returns
38 ///
39 /// Returns the verified `ExecutionHeader` containing all block data (number, timestamp,
40 /// state root, transactions root, etc.)
41 ///
42 /// # Errors
43 ///
44 /// Returns an error if:
45 /// - `InvalidMmrRoot`: The MMR root in the proof doesn't match the expected root
46 /// - `InvalidMmrProof`: The MMR inclusion proof is invalid
47 /// - `InvalidHeaderHash`: The header hash doesn't match the MMR commitment
48 ///
49 /// # Example
50 ///
51 /// ```no_run
52 /// use bankai_verify::evm::ExecutionVerifier;
53 /// use bankai_types::fetch::evm::execution::ExecutionHeaderProof;
54 /// use alloy_primitives::FixedBytes;
55 ///
56 /// # fn example(proof: ExecutionHeaderProof, mmr_root: FixedBytes<32>) -> Result<(), Box<dyn std::error::Error>> {
57 /// let verified_header = ExecutionVerifier::verify_header_proof(&proof, mmr_root)?;
58 /// println!("Verified block {}", verified_header.number);
59 /// println!("State root: {:?}", verified_header.state_root);
60 /// # Ok(())
61 /// # }
62 /// ```
63 pub fn verify_header_proof(
64 proof: &ExecutionHeaderProof,
65 root: FixedBytes<32>,
66 ) -> Result<ExecutionHeader, VerifyError> {
67 if proof.mmr_proof.root != root {
68 return Err(VerifyError::InvalidMmrRoot);
69 }
70
71 MmrVerifier::verify_mmr_proof(&proof.mmr_proof.clone())
72 .map_err(|_| VerifyError::InvalidMmrProof)?;
73
74 let hash = proof.header.inner.hash_slow();
75 if hash != proof.mmr_proof.header_hash {
76 return Err(VerifyError::InvalidHeaderHash);
77 }
78
79 Ok(proof.header.clone().inner)
80 }
81
82 /// Verifies an account's state using a Merkle Patricia Trie proof
83 ///
84 /// This method verifies an account's state (balance, nonce, code hash, storage root)
85 /// against a previously verified execution header. The verification uses a Merkle Patricia
86 /// Trie proof to establish that the account state is included in the header's state root.
87 ///
88 /// # Arguments
89 ///
90 /// * `account_proof` - The account proof containing the account state and MPT proof
91 /// * `headers` - List of previously verified execution headers. Must contain the header
92 /// for the block number referenced in the account proof
93 ///
94 /// # Returns
95 ///
96 /// Returns the verified `Account` containing:
97 /// - Balance (in wei)
98 /// - Nonce (transaction count)
99 /// - Code hash (contract code hash, or empty for EOAs)
100 /// - Storage root (Merkle root of contract storage)
101 ///
102 /// # Errors
103 ///
104 /// Returns an error if:
105 /// - `InvalidExecutionHeaderProof`: The referenced header is not in the verified headers list
106 /// - `InvalidStateRoot`: The state root in the proof doesn't match the header's state root
107 /// - `InvalidAccountProof`: The MPT proof verification failed
108 ///
109 /// # Example
110 ///
111 /// ```no_run
112 /// use bankai_verify::evm::ExecutionVerifier;
113 /// use bankai_types::fetch::evm::execution::AccountProof;
114 /// use bankai_types::verify::evm::execution::ExecutionHeader;
115 ///
116 /// # fn example(
117 /// # account_proof: AccountProof,
118 /// # verified_headers: Vec<ExecutionHeader>
119 /// # ) -> Result<(), Box<dyn std::error::Error>> {
120 /// let account = ExecutionVerifier::verify_account_proof(&account_proof, &verified_headers)?;
121 /// println!("Account balance: {} wei", account.balance);
122 /// println!("Account nonce: {}", account.nonce);
123 /// # Ok(())
124 /// # }
125 /// ```
126 pub fn verify_account_proof(
127 account_proof: &AccountProof,
128 headers: &[ExecutionHeader],
129 ) -> Result<Account, VerifyError> {
130 let header = headers
131 .iter()
132 .find(|h| h.number == account_proof.block_number)
133 .ok_or(VerifyError::InvalidExecutionHeaderProof)?;
134
135 if header.state_root != account_proof.state_root {
136 return Err(VerifyError::InvalidStateRoot);
137 }
138
139 let expected_value = rlp_encode(account_proof.account).to_vec();
140 let key = Nibbles::unpack(keccak256(account_proof.address));
141
142 mpt_verify(
143 header.state_root,
144 key,
145 Some(expected_value),
146 account_proof.mpt_proof.iter(),
147 )
148 .map_err(|_| VerifyError::InvalidAccountProof)?;
149
150 Ok(account_proof.account)
151 }
152
153 /// Verifies one or more storage slots from the same contract using Merkle Patricia Trie proofs.
154 ///
155 /// This method establishes that storage slot values are committed in the state of a given
156 /// block by:
157 /// 1. Verifying the contract account is included in the block's state trie (against the
158 /// verified header's `state_root`)
159 /// 2. Verifying each storage slot is included in the contract's storage trie (against the
160 /// account's `storage_root`)
161 ///
162 /// # Arguments
163 ///
164 /// * `slot_proof` - The storage slot proof containing the account proof and individual
165 /// storage slot proofs
166 /// * `headers` - List of previously verified execution headers. Must contain the header
167 /// for the block number referenced in the storage slot proof
168 ///
169 /// # Returns
170 ///
171 /// Returns a vector of verified (slot_key, slot_value) pairs in the same order as the input.
172 ///
173 /// # Errors
174 ///
175 /// Returns an error if:
176 /// - `InvalidExecutionHeaderProof`: The referenced header is not in the verified headers list
177 /// - `InvalidStateRoot`: The state root in the proof doesn't match the header's state root
178 /// - `InvalidAccountProof`: The account MPT proof verification failed
179 /// - `InvalidStorageProof`: Any storage slot MPT proof verification failed
180 ///
181 /// # Example
182 ///
183 /// ```no_run
184 /// use bankai_verify::evm::ExecutionVerifier;
185 /// use bankai_types::fetch::evm::execution::StorageSlotProof;
186 /// use bankai_types::verify::evm::execution::ExecutionHeader;
187 ///
188 /// # fn example(
189 /// # slot_proof: StorageSlotProof,
190 /// # verified_headers: Vec<ExecutionHeader>
191 /// # ) -> Result<(), Box<dyn std::error::Error>> {
192 /// let values = ExecutionVerifier::verify_storage_slot_proof(&slot_proof, &verified_headers)?;
193 /// for (key, value) in values {
194 /// println!("Slot {:?} = {}", key, value);
195 /// }
196 /// # Ok(())
197 /// # }
198 /// ```
199 pub fn verify_storage_slot_proof(
200 slot_proof: &bankai_types::fetch::evm::execution::StorageSlotProof,
201 headers: &[ExecutionHeader],
202 ) -> Result<Vec<(alloy_primitives::U256, alloy_primitives::U256)>, VerifyError> {
203 let header = headers
204 .iter()
205 .find(|h| h.number == slot_proof.block_number)
206 .ok_or(VerifyError::InvalidExecutionHeaderProof)?;
207
208 if header.state_root != slot_proof.state_root {
209 return Err(VerifyError::InvalidStateRoot);
210 }
211
212 let expected_account = rlp_encode(slot_proof.account).to_vec();
213 let account_key = Nibbles::unpack(keccak256(slot_proof.address));
214 mpt_verify(
215 header.state_root,
216 account_key,
217 Some(expected_account),
218 slot_proof.account_mpt_proof.iter(),
219 )
220 .map_err(|_| VerifyError::InvalidAccountProof)?;
221
222 let mut results = Vec::with_capacity(slot_proof.slots.len());
223 for slot in &slot_proof.slots {
224 Self::verify_storage_slot_entry(
225 slot_proof.account.storage_root,
226 slot.slot_key,
227 slot.slot_value,
228 &slot.storage_mpt_proof,
229 )?;
230 results.push((slot.slot_key, slot.slot_value));
231 }
232
233 Ok(results)
234 }
235
236 /// Internal helper to verify a single storage slot entry against a storage root
237 fn verify_storage_slot_entry(
238 storage_root: FixedBytes<32>,
239 slot_key: alloy_primitives::U256,
240 slot_value: alloy_primitives::U256,
241 storage_mpt_proof: &[alloy_primitives::Bytes],
242 ) -> Result<(), VerifyError> {
243 let slot_key_bytes = slot_key.to_be_bytes::<32>();
244 let storage_key = Nibbles::unpack(keccak256(slot_key_bytes));
245 let expected_storage_value = if slot_value.is_zero() {
246 None
247 } else {
248 Some(rlp_encode(slot_value).to_vec())
249 };
250
251 mpt_verify(
252 storage_root,
253 storage_key,
254 expected_storage_value,
255 storage_mpt_proof.iter(),
256 )
257 .map_err(|_| VerifyError::InvalidStorageProof)?;
258
259 Ok(())
260 }
261
262 /// Verifies a transaction using a Merkle Patricia Trie proof
263 ///
264 /// This method verifies that a transaction was included in a specific block by validating
265 /// an MPT proof against a previously verified execution header. The proof establishes that
266 /// the transaction exists at a specific index in the block's transaction list.
267 ///
268 /// # Arguments
269 ///
270 /// * `proof` - The transaction proof containing the encoded transaction and MPT proof
271 /// * `headers` - List of previously verified execution headers. Must contain the header
272 /// for the block number referenced in the transaction proof
273 ///
274 /// # Returns
275 ///
276 /// Returns the verified `TxEnvelope` containing the full transaction data including:
277 /// - Transaction type (Legacy, EIP-1559, EIP-2930, etc.)
278 /// - From/to addresses
279 /// - Value transferred
280 /// - Gas limit and gas price
281 /// - Input data
282 /// - Signature (v, r, s)
283 ///
284 /// # Errors
285 ///
286 /// Returns an error if:
287 /// - `InvalidExecutionHeaderProof`: The referenced header is not in the verified headers list
288 /// - `InvalidTxProof`: The MPT proof verification failed
289 /// - `InvalidRlpDecode`: The transaction data could not be decoded
290 ///
291 /// # Example
292 ///
293 /// ```no_run
294 /// use bankai_verify::evm::ExecutionVerifier;
295 /// use bankai_types::fetch::evm::execution::TxProof;
296 /// use bankai_types::verify::evm::execution::ExecutionHeader;
297 ///
298 /// # fn example(
299 /// # tx_proof: TxProof,
300 /// # verified_headers: Vec<ExecutionHeader>
301 /// # ) -> Result<(), Box<dyn std::error::Error>> {
302 /// let tx = ExecutionVerifier::verify_tx_proof(&tx_proof, &verified_headers)?;
303 /// println!("Verified transaction in block {}", tx_proof.block_number);
304 /// # Ok(())
305 /// # }
306 /// ```
307 pub fn verify_tx_proof(
308 proof: &TxProof,
309 headers: &[ExecutionHeader],
310 ) -> Result<TxEnvelope, VerifyError> {
311 let header = headers
312 .iter()
313 .find(|h| h.number == proof.block_number)
314 .ok_or(VerifyError::InvalidExecutionHeaderProof)?;
315
316 let mut rlp_tx_index = Vec::new();
317 proof.tx_index.encode(&mut rlp_tx_index);
318 let key = Nibbles::unpack(&rlp_tx_index);
319
320 mpt_verify(
321 header.transactions_root,
322 key,
323 Some(proof.encoded_tx.clone()),
324 proof.proof.iter(),
325 )
326 .map_err(|_| VerifyError::InvalidTxProof)?;
327
328 let tx = TxEnvelope::decode(&mut proof.encoded_tx.as_slice())
329 .map_err(|_| VerifyError::InvalidRlpDecode)?;
330
331 Ok(tx)
332 }
333}