1use crate::{Nibbles, TrieAccount};
4use alloc::{borrow::Cow, vec::Vec};
5use alloy_consensus::constants::KECCAK_EMPTY;
6use alloy_primitives::{
7 keccak256,
8 map::{hash_map, B256Map, B256Set, HashMap},
9 Address, Bytes, B256, U256,
10};
11use alloy_rlp::{encode_fixed_size, Decodable, EMPTY_STRING_CODE};
12use alloy_trie::{
13 nodes::TrieNode,
14 proof::{verify_proof, DecodedProofNodes, ProofNodes, ProofVerificationError},
15 TrieMask, EMPTY_ROOT_HASH,
16};
17use derive_more::{Deref, DerefMut, IntoIterator};
18use itertools::Itertools;
19use reth_primitives_traits::Account;
20
21#[derive(Deref, DerefMut, IntoIterator, Clone, PartialEq, Eq, Default, Debug)]
23pub struct MultiProofTargets(B256Map<B256Set>);
24
25impl FromIterator<(B256, B256Set)> for MultiProofTargets {
26 fn from_iter<T: IntoIterator<Item = (B256, B256Set)>>(iter: T) -> Self {
27 Self(B256Map::from_iter(iter))
28 }
29}
30
31impl MultiProofTargets {
32 pub fn with_capacity(capacity: usize) -> Self {
34 Self(B256Map::with_capacity_and_hasher(capacity, Default::default()))
35 }
36
37 pub fn account(hashed_address: B256) -> Self {
39 Self::accounts([hashed_address])
40 }
41
42 pub fn account_with_slots<I: IntoIterator<Item = B256>>(
44 hashed_address: B256,
45 slots_iter: I,
46 ) -> Self {
47 Self(B256Map::from_iter([(hashed_address, slots_iter.into_iter().collect())]))
48 }
49
50 pub fn accounts<I: IntoIterator<Item = B256>>(iter: I) -> Self {
52 Self(iter.into_iter().map(|hashed_address| (hashed_address, Default::default())).collect())
53 }
54
55 pub fn retain_difference(&mut self, other: &Self) {
58 self.0.retain(|hashed_address, hashed_slots| {
59 if let Some(other_hashed_slots) = other.get(hashed_address) {
60 hashed_slots.retain(|hashed_slot| !other_hashed_slots.contains(hashed_slot));
61 !hashed_slots.is_empty()
62 } else {
63 true
64 }
65 });
66 }
67
68 pub fn extend(&mut self, other: Self) {
70 self.extend_inner(Cow::Owned(other));
71 }
72
73 pub fn extend_ref(&mut self, other: &Self) {
77 self.extend_inner(Cow::Borrowed(other));
78 }
79
80 fn extend_inner(&mut self, other: Cow<'_, Self>) {
81 for (hashed_address, hashed_slots) in other.iter() {
82 self.entry(*hashed_address).or_default().extend(hashed_slots);
83 }
84 }
85
86 pub fn chunks(self, size: usize) -> ChunkedMultiProofTargets {
90 ChunkedMultiProofTargets::new(self, size)
91 }
92}
93
94#[derive(Debug)]
115pub struct ChunkedMultiProofTargets {
116 flattened_targets: alloc::vec::IntoIter<(B256, Option<B256>)>,
117 size: usize,
118}
119
120impl ChunkedMultiProofTargets {
121 fn new(targets: MultiProofTargets, size: usize) -> Self {
122 let flattened_targets = targets
123 .into_iter()
124 .flat_map(|(address, slots)| {
125 if slots.is_empty() {
126 itertools::Either::Left(core::iter::once((address, None)))
130 } else {
131 itertools::Either::Right(
132 slots.into_iter().map(move |slot| (address, Some(slot))),
133 )
134 }
135 })
136 .sorted();
137 Self { flattened_targets, size }
138 }
139}
140
141impl Iterator for ChunkedMultiProofTargets {
142 type Item = MultiProofTargets;
143
144 fn next(&mut self) -> Option<Self::Item> {
145 let chunk = self.flattened_targets.by_ref().take(self.size).fold(
146 MultiProofTargets::default(),
147 |mut acc, (address, slot)| {
148 let entry = acc.entry(address).or_default();
149 if let Some(slot) = slot {
150 entry.insert(slot);
151 }
152 acc
153 },
154 );
155
156 if chunk.is_empty() {
157 None
158 } else {
159 Some(chunk)
160 }
161 }
162}
163
164#[derive(Clone, Default, Debug, PartialEq, Eq)]
168pub struct MultiProof {
169 pub account_subtree: ProofNodes,
171 pub branch_node_hash_masks: HashMap<Nibbles, TrieMask>,
173 pub branch_node_tree_masks: HashMap<Nibbles, TrieMask>,
175 pub storages: B256Map<StorageMultiProof>,
177}
178
179impl MultiProof {
180 pub fn is_empty(&self) -> bool {
182 self.account_subtree.is_empty() &&
183 self.branch_node_hash_masks.is_empty() &&
184 self.branch_node_tree_masks.is_empty() &&
185 self.storages.is_empty()
186 }
187
188 pub fn account_proof_nodes(&self, path: &Nibbles) -> Vec<(Nibbles, Bytes)> {
190 self.account_subtree.matching_nodes_sorted(path)
191 }
192
193 pub fn storage_proof_nodes(
195 &self,
196 hashed_address: B256,
197 slots: impl IntoIterator<Item = B256>,
198 ) -> Vec<(B256, Vec<(Nibbles, Bytes)>)> {
199 self.storages
200 .get(&hashed_address)
201 .map(|storage_mp| {
202 slots
203 .into_iter()
204 .map(|slot| {
205 let nibbles = Nibbles::unpack(slot);
206 (slot, storage_mp.subtree.matching_nodes_sorted(&nibbles))
207 })
208 .collect()
209 })
210 .unwrap_or_default()
211 }
212
213 pub fn account_proof(
215 &self,
216 address: Address,
217 slots: &[B256],
218 ) -> Result<AccountProof, alloy_rlp::Error> {
219 let hashed_address = keccak256(address);
220 let nibbles = Nibbles::unpack(hashed_address);
221
222 let proof = self
224 .account_proof_nodes(&nibbles)
225 .into_iter()
226 .map(|(_, node)| node)
227 .collect::<Vec<_>>();
228
229 let info = 'info: {
232 if let Some(last) = proof.last() &&
233 let TrieNode::Leaf(leaf) = TrieNode::decode(&mut &last[..])? &&
234 nibbles.ends_with(&leaf.key)
235 {
236 let account = TrieAccount::decode(&mut &leaf.value[..])?;
237 break 'info Some(Account {
238 balance: account.balance,
239 nonce: account.nonce,
240 bytecode_hash: (account.code_hash != KECCAK_EMPTY).then_some(account.code_hash),
241 })
242 }
243 None
244 };
245
246 let storage_multiproof = self.storages.get(&hashed_address);
248 let storage_root = storage_multiproof.map(|m| m.root).unwrap_or(EMPTY_ROOT_HASH);
249 let mut storage_proofs = Vec::with_capacity(slots.len());
250 for slot in slots {
251 let proof = if let Some(multiproof) = &storage_multiproof {
252 multiproof.storage_proof(*slot)?
253 } else {
254 StorageProof::new(*slot)
255 };
256 storage_proofs.push(proof);
257 }
258 Ok(AccountProof { address, info, proof, storage_root, storage_proofs })
259 }
260
261 pub fn extend(&mut self, other: Self) {
264 self.account_subtree.extend_from(other.account_subtree);
265
266 self.branch_node_hash_masks.extend(other.branch_node_hash_masks);
267 self.branch_node_tree_masks.extend(other.branch_node_tree_masks);
268
269 for (hashed_address, storage) in other.storages {
270 match self.storages.entry(hashed_address) {
271 hash_map::Entry::Occupied(mut entry) => {
272 debug_assert_eq!(entry.get().root, storage.root);
273 let entry = entry.get_mut();
274 entry.subtree.extend_from(storage.subtree);
275 entry.branch_node_hash_masks.extend(storage.branch_node_hash_masks);
276 entry.branch_node_tree_masks.extend(storage.branch_node_tree_masks);
277 }
278 hash_map::Entry::Vacant(entry) => {
279 entry.insert(storage);
280 }
281 }
282 }
283 }
284
285 pub fn from_storage_proof(hashed_address: B256, storage_proof: StorageMultiProof) -> Self {
287 Self {
288 storages: B256Map::from_iter([(hashed_address, storage_proof)]),
289 ..Default::default()
290 }
291 }
292}
293
294#[derive(Clone, Default, Debug, PartialEq, Eq)]
297pub struct DecodedMultiProof {
298 pub account_subtree: DecodedProofNodes,
300 pub branch_node_hash_masks: HashMap<Nibbles, TrieMask>,
302 pub branch_node_tree_masks: HashMap<Nibbles, TrieMask>,
304 pub storages: B256Map<DecodedStorageMultiProof>,
306}
307
308impl DecodedMultiProof {
309 pub fn is_empty(&self) -> bool {
311 self.account_subtree.is_empty() &&
312 self.branch_node_hash_masks.is_empty() &&
313 self.branch_node_tree_masks.is_empty() &&
314 self.storages.is_empty()
315 }
316
317 pub fn account_proof_nodes(&self, path: &Nibbles) -> Vec<(Nibbles, TrieNode)> {
319 self.account_subtree.matching_nodes_sorted(path)
320 }
321
322 pub fn storage_proof_nodes(
324 &self,
325 hashed_address: B256,
326 slots: impl IntoIterator<Item = B256>,
327 ) -> Vec<(B256, Vec<(Nibbles, TrieNode)>)> {
328 self.storages
329 .get(&hashed_address)
330 .map(|storage_mp| {
331 slots
332 .into_iter()
333 .map(|slot| {
334 let nibbles = Nibbles::unpack(slot);
335 (slot, storage_mp.subtree.matching_nodes_sorted(&nibbles))
336 })
337 .collect()
338 })
339 .unwrap_or_default()
340 }
341
342 pub fn account_proof(
344 &self,
345 address: Address,
346 slots: &[B256],
347 ) -> Result<DecodedAccountProof, alloy_rlp::Error> {
348 let hashed_address = keccak256(address);
349 let nibbles = Nibbles::unpack(hashed_address);
350
351 let proof = self
353 .account_proof_nodes(&nibbles)
354 .into_iter()
355 .map(|(_, node)| node)
356 .collect::<Vec<_>>();
357
358 let info = 'info: {
361 if let Some(TrieNode::Leaf(leaf)) = proof.last() &&
362 nibbles.ends_with(&leaf.key)
363 {
364 let account = TrieAccount::decode(&mut &leaf.value[..])?;
365 break 'info Some(Account {
366 balance: account.balance,
367 nonce: account.nonce,
368 bytecode_hash: (account.code_hash != KECCAK_EMPTY).then_some(account.code_hash),
369 })
370 }
371 None
372 };
373
374 let storage_multiproof = self.storages.get(&hashed_address);
376 let storage_root = storage_multiproof.map(|m| m.root).unwrap_or(EMPTY_ROOT_HASH);
377 let mut storage_proofs = Vec::with_capacity(slots.len());
378 for slot in slots {
379 let proof = if let Some(multiproof) = &storage_multiproof {
380 multiproof.storage_proof(*slot)?
381 } else {
382 DecodedStorageProof::new(*slot)
383 };
384 storage_proofs.push(proof);
385 }
386 Ok(DecodedAccountProof { address, info, proof, storage_root, storage_proofs })
387 }
388
389 pub fn extend(&mut self, other: Self) {
392 self.account_subtree.extend_from(other.account_subtree);
393
394 self.branch_node_hash_masks.extend(other.branch_node_hash_masks);
395 self.branch_node_tree_masks.extend(other.branch_node_tree_masks);
396
397 for (hashed_address, storage) in other.storages {
398 match self.storages.entry(hashed_address) {
399 hash_map::Entry::Occupied(mut entry) => {
400 debug_assert_eq!(entry.get().root, storage.root);
401 let entry = entry.get_mut();
402 entry.subtree.extend_from(storage.subtree);
403 entry.branch_node_hash_masks.extend(storage.branch_node_hash_masks);
404 entry.branch_node_tree_masks.extend(storage.branch_node_tree_masks);
405 }
406 hash_map::Entry::Vacant(entry) => {
407 entry.insert(storage);
408 }
409 }
410 }
411 }
412
413 pub fn from_storage_proof(
415 hashed_address: B256,
416 storage_proof: DecodedStorageMultiProof,
417 ) -> Self {
418 Self {
419 storages: B256Map::from_iter([(hashed_address, storage_proof)]),
420 ..Default::default()
421 }
422 }
423}
424
425impl TryFrom<MultiProof> for DecodedMultiProof {
426 type Error = alloy_rlp::Error;
427
428 fn try_from(multi_proof: MultiProof) -> Result<Self, Self::Error> {
429 let account_subtree = DecodedProofNodes::try_from(multi_proof.account_subtree)?;
430 let storages = multi_proof
431 .storages
432 .into_iter()
433 .map(|(address, storage)| Ok((address, storage.try_into()?)))
434 .collect::<Result<B256Map<_>, alloy_rlp::Error>>()?;
435 Ok(Self {
436 account_subtree,
437 branch_node_hash_masks: multi_proof.branch_node_hash_masks,
438 branch_node_tree_masks: multi_proof.branch_node_tree_masks,
439 storages,
440 })
441 }
442}
443
444#[derive(Clone, Debug, PartialEq, Eq)]
446pub struct StorageMultiProof {
447 pub root: B256,
449 pub subtree: ProofNodes,
451 pub branch_node_hash_masks: HashMap<Nibbles, TrieMask>,
453 pub branch_node_tree_masks: HashMap<Nibbles, TrieMask>,
455}
456
457impl StorageMultiProof {
458 pub fn empty() -> Self {
460 Self {
461 root: EMPTY_ROOT_HASH,
462 subtree: ProofNodes::from_iter([(
463 Nibbles::default(),
464 Bytes::from([EMPTY_STRING_CODE]),
465 )]),
466 branch_node_hash_masks: HashMap::default(),
467 branch_node_tree_masks: HashMap::default(),
468 }
469 }
470
471 pub fn storage_proof(&self, slot: B256) -> Result<StorageProof, alloy_rlp::Error> {
473 let nibbles = Nibbles::unpack(keccak256(slot));
474
475 let proof = self
477 .subtree
478 .matching_nodes_iter(&nibbles)
479 .sorted_by(|a, b| a.0.cmp(b.0))
480 .map(|(_, node)| node.clone())
481 .collect::<Vec<_>>();
482
483 let value = 'value: {
486 if let Some(last) = proof.last() &&
487 let TrieNode::Leaf(leaf) = TrieNode::decode(&mut &last[..])? &&
488 nibbles.ends_with(&leaf.key)
489 {
490 break 'value U256::decode(&mut &leaf.value[..])?
491 }
492 U256::ZERO
493 };
494
495 Ok(StorageProof { key: slot, nibbles, value, proof })
496 }
497}
498
499#[derive(Clone, Debug, PartialEq, Eq)]
501pub struct DecodedStorageMultiProof {
502 pub root: B256,
504 pub subtree: DecodedProofNodes,
506 pub branch_node_hash_masks: HashMap<Nibbles, TrieMask>,
508 pub branch_node_tree_masks: HashMap<Nibbles, TrieMask>,
510}
511
512impl DecodedStorageMultiProof {
513 pub fn empty() -> Self {
515 Self {
516 root: EMPTY_ROOT_HASH,
517 subtree: DecodedProofNodes::from_iter([(Nibbles::default(), TrieNode::EmptyRoot)]),
518 branch_node_hash_masks: HashMap::default(),
519 branch_node_tree_masks: HashMap::default(),
520 }
521 }
522
523 pub fn storage_proof(&self, slot: B256) -> Result<DecodedStorageProof, alloy_rlp::Error> {
525 let nibbles = Nibbles::unpack(keccak256(slot));
526
527 let proof = self
529 .subtree
530 .matching_nodes_iter(&nibbles)
531 .sorted_by(|a, b| a.0.cmp(b.0))
532 .map(|(_, node)| node.clone())
533 .collect::<Vec<_>>();
534
535 let value = 'value: {
538 if let Some(TrieNode::Leaf(leaf)) = proof.last() &&
539 nibbles.ends_with(&leaf.key)
540 {
541 break 'value U256::decode(&mut &leaf.value[..])?
542 }
543 U256::ZERO
544 };
545
546 Ok(DecodedStorageProof { key: slot, nibbles, value, proof })
547 }
548}
549
550impl TryFrom<StorageMultiProof> for DecodedStorageMultiProof {
551 type Error = alloy_rlp::Error;
552
553 fn try_from(multi_proof: StorageMultiProof) -> Result<Self, Self::Error> {
554 let subtree = DecodedProofNodes::try_from(multi_proof.subtree)?;
555 Ok(Self {
556 root: multi_proof.root,
557 subtree,
558 branch_node_hash_masks: multi_proof.branch_node_hash_masks,
559 branch_node_tree_masks: multi_proof.branch_node_tree_masks,
560 })
561 }
562}
563
564#[derive(Clone, PartialEq, Eq, Debug)]
566#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
567#[cfg_attr(any(test, feature = "serde"), serde(rename_all = "camelCase"))]
568pub struct AccountProof {
569 pub address: Address,
571 pub info: Option<Account>,
573 pub proof: Vec<Bytes>,
576 pub storage_root: B256,
578 pub storage_proofs: Vec<StorageProof>,
580}
581
582#[cfg(feature = "eip1186")]
583impl AccountProof {
584 pub fn into_eip1186_response(
586 self,
587 slots: Vec<alloy_serde::JsonStorageKey>,
588 ) -> alloy_rpc_types_eth::EIP1186AccountProofResponse {
589 let info = self.info.unwrap_or_default();
590 alloy_rpc_types_eth::EIP1186AccountProofResponse {
591 address: self.address,
592 balance: info.balance,
593 code_hash: info.get_bytecode_hash(),
594 nonce: info.nonce,
595 storage_hash: self.storage_root,
596 account_proof: self.proof,
597 storage_proof: self
598 .storage_proofs
599 .into_iter()
600 .filter_map(|proof| {
601 let input_slot = slots.iter().find(|s| s.as_b256() == proof.key)?;
602 Some(proof.into_eip1186_proof(*input_slot))
603 })
604 .collect(),
605 }
606 }
607
608 pub fn from_eip1186_proof(proof: alloy_rpc_types_eth::EIP1186AccountProofResponse) -> Self {
614 let alloy_rpc_types_eth::EIP1186AccountProofResponse {
615 nonce,
616 address,
617 balance,
618 code_hash,
619 storage_hash,
620 account_proof,
621 storage_proof,
622 ..
623 } = proof;
624 let storage_proofs = storage_proof.into_iter().map(Into::into).collect();
625
626 let (storage_root, info) = if nonce == 0 &&
627 balance.is_zero() &&
628 storage_hash.is_zero() &&
629 code_hash == KECCAK_EMPTY
630 {
631 (EMPTY_ROOT_HASH, None)
634 } else {
635 (storage_hash, Some(Account { nonce, balance, bytecode_hash: code_hash.into() }))
636 };
637
638 Self { address, info, proof: account_proof, storage_root, storage_proofs }
639 }
640}
641
642#[cfg(feature = "eip1186")]
643impl From<alloy_rpc_types_eth::EIP1186AccountProofResponse> for AccountProof {
644 fn from(proof: alloy_rpc_types_eth::EIP1186AccountProofResponse) -> Self {
645 Self::from_eip1186_proof(proof)
646 }
647}
648
649impl Default for AccountProof {
650 fn default() -> Self {
651 Self::new(Address::default())
652 }
653}
654
655impl AccountProof {
656 pub const fn new(address: Address) -> Self {
658 Self {
659 address,
660 info: None,
661 proof: Vec::new(),
662 storage_root: EMPTY_ROOT_HASH,
663 storage_proofs: Vec::new(),
664 }
665 }
666
667 pub fn verify(&self, root: B256) -> Result<(), ProofVerificationError> {
669 for storage_proof in &self.storage_proofs {
671 storage_proof.verify(self.storage_root)?;
672 }
673
674 let expected = if self.info.is_none() && self.storage_root == EMPTY_ROOT_HASH {
676 None
677 } else {
678 Some(alloy_rlp::encode(
679 self.info.unwrap_or_default().into_trie_account(self.storage_root),
680 ))
681 };
682 let nibbles = Nibbles::unpack(keccak256(self.address));
683 verify_proof(root, nibbles, expected, &self.proof)
684 }
685}
686
687#[derive(Clone, PartialEq, Eq, Debug)]
689pub struct DecodedAccountProof {
690 pub address: Address,
692 pub info: Option<Account>,
694 pub proof: Vec<TrieNode>,
697 pub storage_root: B256,
699 pub storage_proofs: Vec<DecodedStorageProof>,
701}
702
703impl Default for DecodedAccountProof {
704 fn default() -> Self {
705 Self::new(Address::default())
706 }
707}
708
709impl DecodedAccountProof {
710 pub const fn new(address: Address) -> Self {
712 Self {
713 address,
714 info: None,
715 proof: Vec::new(),
716 storage_root: EMPTY_ROOT_HASH,
717 storage_proofs: Vec::new(),
718 }
719 }
720}
721
722#[derive(Clone, PartialEq, Eq, Default, Debug)]
724#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
725pub struct StorageProof {
726 pub key: B256,
728 pub nibbles: Nibbles,
730 pub value: U256,
732 pub proof: Vec<Bytes>,
735}
736
737impl StorageProof {
738 pub fn new(key: B256) -> Self {
740 let nibbles = Nibbles::unpack(keccak256(key));
741 Self { key, nibbles, ..Default::default() }
742 }
743
744 pub fn new_with_hashed(key: B256, hashed_key: B256) -> Self {
746 Self { key, nibbles: Nibbles::unpack(hashed_key), ..Default::default() }
747 }
748
749 pub fn new_with_nibbles(key: B256, nibbles: Nibbles) -> Self {
751 Self { key, nibbles, ..Default::default() }
752 }
753
754 pub fn with_proof(mut self, proof: Vec<Bytes>) -> Self {
756 self.proof = proof;
757 self
758 }
759
760 pub fn verify(&self, root: B256) -> Result<(), ProofVerificationError> {
762 let expected =
763 if self.value.is_zero() { None } else { Some(encode_fixed_size(&self.value).to_vec()) };
764 verify_proof(root, self.nibbles, expected, &self.proof)
765 }
766}
767
768#[cfg(feature = "eip1186")]
769impl StorageProof {
770 pub fn into_eip1186_proof(
772 self,
773 slot: alloy_serde::JsonStorageKey,
774 ) -> alloy_rpc_types_eth::EIP1186StorageProof {
775 alloy_rpc_types_eth::EIP1186StorageProof { key: slot, value: self.value, proof: self.proof }
776 }
777
778 pub fn from_eip1186_proof(storage_proof: alloy_rpc_types_eth::EIP1186StorageProof) -> Self {
783 Self {
784 value: storage_proof.value,
785 proof: storage_proof.proof,
786 ..Self::new(storage_proof.key.as_b256())
787 }
788 }
789}
790
791#[cfg(feature = "eip1186")]
792impl From<alloy_rpc_types_eth::EIP1186StorageProof> for StorageProof {
793 fn from(proof: alloy_rpc_types_eth::EIP1186StorageProof) -> Self {
794 Self::from_eip1186_proof(proof)
795 }
796}
797
798#[derive(Clone, PartialEq, Eq, Default, Debug)]
800pub struct DecodedStorageProof {
801 pub key: B256,
803 pub nibbles: Nibbles,
805 pub value: U256,
807 pub proof: Vec<TrieNode>,
810}
811
812impl DecodedStorageProof {
813 pub fn new(key: B256) -> Self {
815 let nibbles = Nibbles::unpack(keccak256(key));
816 Self { key, nibbles, ..Default::default() }
817 }
818
819 pub fn new_with_hashed(key: B256, hashed_key: B256) -> Self {
821 Self { key, nibbles: Nibbles::unpack(hashed_key), ..Default::default() }
822 }
823
824 pub fn new_with_nibbles(key: B256, nibbles: Nibbles) -> Self {
826 Self { key, nibbles, ..Default::default() }
827 }
828
829 pub fn with_proof(mut self, proof: Vec<TrieNode>) -> Self {
831 self.proof = proof;
832 self
833 }
834}
835
836#[cfg(any(test, feature = "test-utils"))]
839pub mod triehash {
840 use alloy_primitives::{keccak256, B256};
841 use alloy_rlp::RlpEncodable;
842 use hash_db::Hasher;
843 use plain_hasher::PlainHasher;
844
845 #[derive(Default, Debug, Clone, PartialEq, Eq, RlpEncodable)]
847 #[non_exhaustive]
848 pub struct KeccakHasher;
849
850 #[cfg(any(test, feature = "test-utils"))]
851 impl Hasher for KeccakHasher {
852 type Out = B256;
853 type StdHasher = PlainHasher;
854
855 const LENGTH: usize = 32;
856
857 fn hash(x: &[u8]) -> Self::Out {
858 keccak256(x)
859 }
860 }
861}
862
863#[cfg(test)]
864mod tests {
865 use super::*;
866
867 #[test]
868 fn test_multiproof_extend_account_proofs() {
869 let mut proof1 = MultiProof::default();
870 let mut proof2 = MultiProof::default();
871
872 let addr1 = B256::random();
873 let addr2 = B256::random();
874
875 proof1.account_subtree.insert(
876 Nibbles::unpack(addr1),
877 alloy_rlp::encode_fixed_size(&U256::from(42)).to_vec().into(),
878 );
879 proof2.account_subtree.insert(
880 Nibbles::unpack(addr2),
881 alloy_rlp::encode_fixed_size(&U256::from(43)).to_vec().into(),
882 );
883
884 proof1.extend(proof2);
885
886 assert!(proof1.account_subtree.contains_key(&Nibbles::unpack(addr1)));
887 assert!(proof1.account_subtree.contains_key(&Nibbles::unpack(addr2)));
888 }
889
890 #[test]
891 fn test_multiproof_extend_storage_proofs() {
892 let mut proof1 = MultiProof::default();
893 let mut proof2 = MultiProof::default();
894
895 let addr = B256::random();
896 let root = B256::random();
897
898 let mut subtree1 = ProofNodes::default();
899 subtree1.insert(
900 Nibbles::from_nibbles(vec![0]),
901 alloy_rlp::encode_fixed_size(&U256::from(42)).to_vec().into(),
902 );
903 proof1.storages.insert(
904 addr,
905 StorageMultiProof {
906 root,
907 subtree: subtree1,
908 branch_node_hash_masks: HashMap::default(),
909 branch_node_tree_masks: HashMap::default(),
910 },
911 );
912
913 let mut subtree2 = ProofNodes::default();
914 subtree2.insert(
915 Nibbles::from_nibbles(vec![1]),
916 alloy_rlp::encode_fixed_size(&U256::from(43)).to_vec().into(),
917 );
918 proof2.storages.insert(
919 addr,
920 StorageMultiProof {
921 root,
922 subtree: subtree2,
923 branch_node_hash_masks: HashMap::default(),
924 branch_node_tree_masks: HashMap::default(),
925 },
926 );
927
928 proof1.extend(proof2);
929
930 let storage = proof1.storages.get(&addr).unwrap();
931 assert_eq!(storage.root, root);
932 assert!(storage.subtree.contains_key(&Nibbles::from_nibbles(vec![0])));
933 assert!(storage.subtree.contains_key(&Nibbles::from_nibbles(vec![1])));
934 }
935
936 #[test]
937 fn test_multi_proof_retain_difference() {
938 let mut empty = MultiProofTargets::default();
939 empty.retain_difference(&Default::default());
940 assert!(empty.is_empty());
941
942 let targets = MultiProofTargets::accounts((0..10).map(B256::with_last_byte));
943
944 let mut diffed = targets.clone();
945 diffed.retain_difference(&MultiProofTargets::account(B256::with_last_byte(11)));
946 assert_eq!(diffed, targets);
947
948 diffed.retain_difference(&MultiProofTargets::accounts((0..5).map(B256::with_last_byte)));
949 assert_eq!(diffed, MultiProofTargets::accounts((5..10).map(B256::with_last_byte)));
950
951 diffed.retain_difference(&targets);
952 assert!(diffed.is_empty());
953
954 let mut targets = MultiProofTargets::default();
955 let (account1, account2, account3) =
956 (1..=3).map(B256::with_last_byte).collect_tuple().unwrap();
957 let account2_slots = (1..5).map(B256::with_last_byte).collect::<B256Set>();
958 targets.insert(account1, B256Set::from_iter([B256::with_last_byte(1)]));
959 targets.insert(account2, account2_slots.clone());
960 targets.insert(account3, B256Set::from_iter([B256::with_last_byte(1)]));
961
962 let mut diffed = targets.clone();
963 diffed.retain_difference(&MultiProofTargets::accounts((1..=3).map(B256::with_last_byte)));
964 assert_eq!(diffed, targets);
965
966 let mut account2_slots_expected_len = account2_slots.len();
968 for slot in account2_slots.iter().skip(1) {
969 diffed.retain_difference(&MultiProofTargets::account_with_slots(account2, [*slot]));
970 account2_slots_expected_len -= 1;
971 assert_eq!(
972 diffed.get(&account2).map(|slots| slots.len()),
973 Some(account2_slots_expected_len)
974 );
975 }
976
977 diffed.retain_difference(&targets);
978 assert!(diffed.is_empty());
979 }
980
981 #[test]
982 fn test_multi_proof_retain_difference_no_overlap() {
983 let mut targets = MultiProofTargets::default();
984
985 let (addr1, addr2) = (B256::random(), B256::random());
987 let (slot1, slot2) = (B256::random(), B256::random());
988 targets.insert(addr1, std::iter::once(slot1).collect());
989 targets.insert(addr2, std::iter::once(slot2).collect());
990
991 let mut retained = targets.clone();
992 retained.retain_difference(&Default::default());
993 assert_eq!(retained, targets);
994
995 let mut other_targets = MultiProofTargets::default();
997 let addr3 = B256::random();
998 let slot3 = B256::random();
999 other_targets.insert(addr3, B256Set::from_iter([slot3]));
1000
1001 let mut retained = targets.clone();
1004 retained.retain_difference(&other_targets);
1005 assert_eq!(retained, targets);
1006 }
1007
1008 #[test]
1009 fn test_get_prefetch_proof_targets_remove_subset() {
1010 let mut targets = MultiProofTargets::default();
1012 let (addr1, addr2) = (B256::random(), B256::random());
1013 let (slot1, slot2) = (B256::random(), B256::random());
1014 targets.insert(addr1, B256Set::from_iter([slot1]));
1015 targets.insert(addr2, B256Set::from_iter([slot2]));
1016
1017 let other_targets = MultiProofTargets::account_with_slots(addr1, [slot1]);
1019
1020 let mut retained = targets.clone();
1021 retained.retain_difference(&other_targets);
1022
1023 assert_eq!(retained.len(), 1);
1025 assert!(!retained.contains_key(&addr1));
1026 assert!(retained.contains_key(&addr2));
1027
1028 let slot3 = B256::random();
1030 targets.get_mut(&addr1).unwrap().insert(slot3);
1031
1032 let mut retained = targets.clone();
1033 retained.retain_difference(&other_targets);
1034
1035 assert_eq!(retained.len(), 2);
1038 assert!(retained.contains_key(&addr1));
1039 assert_eq!(retained.get(&addr1), Some(&B256Set::from_iter([slot3])));
1040 assert!(retained.contains_key(&addr2));
1041 assert_eq!(retained.get(&addr2), Some(&B256Set::from_iter([slot2])));
1042 }
1043
1044 #[test]
1045 #[cfg(feature = "eip1186")]
1046 fn eip_1186_roundtrip() {
1047 let mut acc = AccountProof {
1048 address: Address::random(),
1049 info: Some(
1050 Account { nonce: 100, balance: U256::ZERO, bytecode_hash: Some(KECCAK_EMPTY) },
1052 ),
1053 proof: vec![],
1054 storage_root: B256::ZERO,
1055 storage_proofs: vec![],
1056 };
1057
1058 let rpc_proof = acc.clone().into_eip1186_response(Vec::new());
1059 let inverse: AccountProof = rpc_proof.into();
1060 assert_eq!(acc, inverse);
1061
1062 acc.info.as_mut().unwrap().nonce = 0;
1064 let rpc_proof = acc.clone().into_eip1186_response(Vec::new());
1065 let inverse: AccountProof = rpc_proof.into();
1066 acc.info.take();
1067 acc.storage_root = EMPTY_ROOT_HASH;
1068 assert_eq!(acc, inverse);
1069 }
1070}