1use crate::{Nibbles, TrieAccount};
4use alloy_consensus::constants::KECCAK_EMPTY;
5use alloy_primitives::{
6 keccak256,
7 map::{hash_map, B256HashMap, B256HashSet, HashMap},
8 Address, Bytes, B256, U256,
9};
10use alloy_rlp::{encode_fixed_size, Decodable, EMPTY_STRING_CODE};
11use alloy_trie::{
12 nodes::TrieNode,
13 proof::{verify_proof, ProofNodes, ProofVerificationError},
14 TrieMask, EMPTY_ROOT_HASH,
15};
16use derive_more::derive::{Deref, DerefMut, From, Into, IntoIterator};
17use itertools::Itertools;
18use reth_primitives_traits::Account;
19
20#[derive(Debug, Default, Clone, Deref, DerefMut, From, Into, IntoIterator)]
22pub struct MultiProofTargets(B256HashMap<B256HashSet>);
23
24impl MultiProofTargets {
25 pub fn extend(&mut self, other: Self) {
27 for (address, slots) in other.0 {
28 self.0.entry(address).or_default().extend(slots);
29 }
30 }
31
32 pub fn extend_ref(&mut self, other: &Self) {
34 for (address, slots) in &other.0 {
35 self.0.entry(*address).or_default().extend(slots);
36 }
37 }
38}
39
40#[derive(Clone, Default, Debug, PartialEq, Eq)]
44pub struct MultiProof {
45 pub account_subtree: ProofNodes,
47 pub branch_node_hash_masks: HashMap<Nibbles, TrieMask>,
49 pub storages: B256HashMap<StorageMultiProof>,
51}
52
53impl MultiProof {
54 pub fn account_proof_nodes(&self, path: &Nibbles) -> Vec<(Nibbles, Bytes)> {
56 self.account_subtree.matching_nodes_sorted(path)
57 }
58
59 pub fn storage_proof_nodes(
61 &self,
62 hashed_address: B256,
63 slots: impl IntoIterator<Item = B256>,
64 ) -> Vec<(B256, Vec<(Nibbles, Bytes)>)> {
65 self.storages
66 .get(&hashed_address)
67 .map(|storage_mp| {
68 slots
69 .into_iter()
70 .map(|slot| {
71 let nibbles = Nibbles::unpack(slot);
72 (slot, storage_mp.subtree.matching_nodes_sorted(&nibbles))
73 })
74 .collect()
75 })
76 .unwrap_or_default()
77 }
78
79 pub fn account_proof(
81 &self,
82 address: Address,
83 slots: &[B256],
84 ) -> Result<AccountProof, alloy_rlp::Error> {
85 let hashed_address = keccak256(address);
86 let nibbles = Nibbles::unpack(hashed_address);
87
88 let proof = self
90 .account_proof_nodes(&nibbles)
91 .into_iter()
92 .map(|(_, node)| node)
93 .collect::<Vec<_>>();
94
95 let info = 'info: {
98 if let Some(last) = proof.last() {
99 if let TrieNode::Leaf(leaf) = TrieNode::decode(&mut &last[..])? {
100 if nibbles.ends_with(&leaf.key) {
101 let account = TrieAccount::decode(&mut &leaf.value[..])?;
102 break 'info Some(Account {
103 balance: account.balance,
104 nonce: account.nonce,
105 bytecode_hash: (account.code_hash != KECCAK_EMPTY)
106 .then_some(account.code_hash),
107 })
108 }
109 }
110 }
111 None
112 };
113
114 let storage_multiproof = self.storages.get(&hashed_address);
116 let storage_root = storage_multiproof.map(|m| m.root).unwrap_or(EMPTY_ROOT_HASH);
117 let mut storage_proofs = Vec::with_capacity(slots.len());
118 for slot in slots {
119 let proof = if let Some(multiproof) = &storage_multiproof {
120 multiproof.storage_proof(*slot)?
121 } else {
122 StorageProof::new(*slot)
123 };
124 storage_proofs.push(proof);
125 }
126 Ok(AccountProof { address, info, proof, storage_root, storage_proofs })
127 }
128
129 pub fn extend(&mut self, other: Self) {
132 self.account_subtree.extend_from(other.account_subtree);
133
134 self.branch_node_hash_masks.extend(other.branch_node_hash_masks);
135
136 for (hashed_address, storage) in other.storages {
137 match self.storages.entry(hashed_address) {
138 hash_map::Entry::Occupied(mut entry) => {
139 debug_assert_eq!(entry.get().root, storage.root);
140 let entry = entry.get_mut();
141 entry.subtree.extend_from(storage.subtree);
142 entry.branch_node_hash_masks.extend(storage.branch_node_hash_masks);
143 }
144 hash_map::Entry::Vacant(entry) => {
145 entry.insert(storage);
146 }
147 }
148 }
149 }
150}
151
152#[derive(Clone, Debug, PartialEq, Eq)]
154pub struct StorageMultiProof {
155 pub root: B256,
157 pub subtree: ProofNodes,
159 pub branch_node_hash_masks: HashMap<Nibbles, TrieMask>,
161}
162
163impl StorageMultiProof {
164 pub fn empty() -> Self {
166 Self {
167 root: EMPTY_ROOT_HASH,
168 subtree: ProofNodes::from_iter([(
169 Nibbles::default(),
170 Bytes::from([EMPTY_STRING_CODE]),
171 )]),
172 branch_node_hash_masks: HashMap::default(),
173 }
174 }
175
176 pub fn storage_proof(&self, slot: B256) -> Result<StorageProof, alloy_rlp::Error> {
178 let nibbles = Nibbles::unpack(keccak256(slot));
179
180 let proof = self
182 .subtree
183 .matching_nodes_iter(&nibbles)
184 .sorted_by(|a, b| a.0.cmp(b.0))
185 .map(|(_, node)| node.clone())
186 .collect::<Vec<_>>();
187
188 let value = 'value: {
191 if let Some(last) = proof.last() {
192 if let TrieNode::Leaf(leaf) = TrieNode::decode(&mut &last[..])? {
193 if nibbles.ends_with(&leaf.key) {
194 break 'value U256::decode(&mut &leaf.value[..])?
195 }
196 }
197 }
198 U256::ZERO
199 };
200
201 Ok(StorageProof { key: slot, nibbles, value, proof })
202 }
203}
204
205#[derive(Clone, PartialEq, Eq, Debug)]
207#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
208#[cfg_attr(any(test, feature = "serde"), serde(rename_all = "camelCase"))]
209pub struct AccountProof {
210 pub address: Address,
212 pub info: Option<Account>,
214 pub proof: Vec<Bytes>,
217 pub storage_root: B256,
219 pub storage_proofs: Vec<StorageProof>,
221}
222
223#[cfg(feature = "eip1186")]
224impl AccountProof {
225 pub fn into_eip1186_response(
227 self,
228 slots: Vec<alloy_serde::JsonStorageKey>,
229 ) -> alloy_rpc_types_eth::EIP1186AccountProofResponse {
230 let info = self.info.unwrap_or_default();
231 alloy_rpc_types_eth::EIP1186AccountProofResponse {
232 address: self.address,
233 balance: info.balance,
234 code_hash: info.get_bytecode_hash(),
235 nonce: info.nonce,
236 storage_hash: self.storage_root,
237 account_proof: self.proof,
238 storage_proof: self
239 .storage_proofs
240 .into_iter()
241 .filter_map(|proof| {
242 let input_slot = slots.iter().find(|s| s.as_b256() == proof.key)?;
243 Some(proof.into_eip1186_proof(*input_slot))
244 })
245 .collect(),
246 }
247 }
248}
249
250impl Default for AccountProof {
251 fn default() -> Self {
252 Self::new(Address::default())
253 }
254}
255
256impl AccountProof {
257 pub const fn new(address: Address) -> Self {
259 Self {
260 address,
261 info: None,
262 proof: Vec::new(),
263 storage_root: EMPTY_ROOT_HASH,
264 storage_proofs: Vec::new(),
265 }
266 }
267
268 pub fn verify(&self, root: B256) -> Result<(), ProofVerificationError> {
270 for storage_proof in &self.storage_proofs {
272 storage_proof.verify(self.storage_root)?;
273 }
274
275 let expected = if self.info.is_none() && self.storage_root == EMPTY_ROOT_HASH {
277 None
278 } else {
279 Some(alloy_rlp::encode(TrieAccount::from((
280 self.info.unwrap_or_default(),
281 self.storage_root,
282 ))))
283 };
284 let nibbles = Nibbles::unpack(keccak256(self.address));
285 verify_proof(root, nibbles, expected, &self.proof)
286 }
287}
288
289#[derive(Clone, PartialEq, Eq, Default, Debug)]
291#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
292pub struct StorageProof {
293 pub key: B256,
295 pub nibbles: Nibbles,
297 pub value: U256,
299 pub proof: Vec<Bytes>,
302}
303
304impl StorageProof {
305 #[cfg(feature = "eip1186")]
307 pub fn into_eip1186_proof(
308 self,
309 slot: alloy_serde::JsonStorageKey,
310 ) -> alloy_rpc_types_eth::EIP1186StorageProof {
311 alloy_rpc_types_eth::EIP1186StorageProof { key: slot, value: self.value, proof: self.proof }
312 }
313}
314
315impl StorageProof {
316 pub fn new(key: B256) -> Self {
318 let nibbles = Nibbles::unpack(keccak256(key));
319 Self { key, nibbles, ..Default::default() }
320 }
321
322 pub fn new_with_hashed(key: B256, hashed_key: B256) -> Self {
324 Self { key, nibbles: Nibbles::unpack(hashed_key), ..Default::default() }
325 }
326
327 pub fn new_with_nibbles(key: B256, nibbles: Nibbles) -> Self {
329 Self { key, nibbles, ..Default::default() }
330 }
331
332 pub fn with_proof(mut self, proof: Vec<Bytes>) -> Self {
334 self.proof = proof;
335 self
336 }
337
338 pub fn verify(&self, root: B256) -> Result<(), ProofVerificationError> {
340 let expected =
341 if self.value.is_zero() { None } else { Some(encode_fixed_size(&self.value).to_vec()) };
342 verify_proof(root, self.nibbles.clone(), expected, &self.proof)
343 }
344}
345
346#[cfg(any(test, feature = "test-utils"))]
349pub mod triehash {
350 use alloy_primitives::{keccak256, B256};
351 use alloy_rlp::RlpEncodable;
352 use hash_db::Hasher;
353 use plain_hasher::PlainHasher;
354
355 #[derive(Default, Debug, Clone, PartialEq, Eq, RlpEncodable)]
357 #[non_exhaustive]
358 pub struct KeccakHasher;
359
360 #[cfg(any(test, feature = "test-utils"))]
361 impl Hasher for KeccakHasher {
362 type Out = B256;
363 type StdHasher = PlainHasher;
364
365 const LENGTH: usize = 32;
366
367 fn hash(x: &[u8]) -> Self::Out {
368 keccak256(x)
369 }
370 }
371}
372
373#[cfg(test)]
374mod tests {
375 use super::*;
376
377 #[test]
378 fn test_multiproof_extend_account_proofs() {
379 let mut proof1 = MultiProof::default();
380 let mut proof2 = MultiProof::default();
381
382 let addr1 = B256::random();
383 let addr2 = B256::random();
384
385 proof1.account_subtree.insert(
386 Nibbles::unpack(addr1),
387 alloy_rlp::encode_fixed_size(&U256::from(42)).to_vec().into(),
388 );
389 proof2.account_subtree.insert(
390 Nibbles::unpack(addr2),
391 alloy_rlp::encode_fixed_size(&U256::from(43)).to_vec().into(),
392 );
393
394 proof1.extend(proof2);
395
396 assert!(proof1.account_subtree.contains_key(&Nibbles::unpack(addr1)));
397 assert!(proof1.account_subtree.contains_key(&Nibbles::unpack(addr2)));
398 }
399
400 #[test]
401 fn test_multiproof_extend_storage_proofs() {
402 let mut proof1 = MultiProof::default();
403 let mut proof2 = MultiProof::default();
404
405 let addr = B256::random();
406 let root = B256::random();
407
408 let mut subtree1 = ProofNodes::default();
409 subtree1.insert(
410 Nibbles::from_nibbles(vec![0]),
411 alloy_rlp::encode_fixed_size(&U256::from(42)).to_vec().into(),
412 );
413 proof1.storages.insert(
414 addr,
415 StorageMultiProof {
416 root,
417 subtree: subtree1,
418 branch_node_hash_masks: HashMap::default(),
419 },
420 );
421
422 let mut subtree2 = ProofNodes::default();
423 subtree2.insert(
424 Nibbles::from_nibbles(vec![1]),
425 alloy_rlp::encode_fixed_size(&U256::from(43)).to_vec().into(),
426 );
427 proof2.storages.insert(
428 addr,
429 StorageMultiProof {
430 root,
431 subtree: subtree2,
432 branch_node_hash_masks: HashMap::default(),
433 },
434 );
435
436 proof1.extend(proof2);
437
438 let storage = proof1.storages.get(&addr).unwrap();
439 assert_eq!(storage.root, root);
440 assert!(storage.subtree.contains_key(&Nibbles::from_nibbles(vec![0])));
441 assert!(storage.subtree.contains_key(&Nibbles::from_nibbles(vec![1])));
442 }
443}