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 pub fn chunking_length(&self) -> usize {
95 self.values().map(|slots| 1 + slots.len().saturating_sub(1)).sum::<usize>()
96 }
97}
98
99#[derive(Debug)]
120pub struct ChunkedMultiProofTargets {
121 flattened_targets: alloc::vec::IntoIter<(B256, Option<B256>)>,
122 size: usize,
123}
124
125impl ChunkedMultiProofTargets {
126 fn new(targets: MultiProofTargets, size: usize) -> Self {
127 let flattened_targets = targets
128 .into_iter()
129 .flat_map(|(address, slots)| {
130 if slots.is_empty() {
131 itertools::Either::Left(core::iter::once((address, None)))
135 } else {
136 itertools::Either::Right(
137 slots.into_iter().map(move |slot| (address, Some(slot))),
138 )
139 }
140 })
141 .sorted();
142 Self { flattened_targets, size }
143 }
144}
145
146impl Iterator for ChunkedMultiProofTargets {
147 type Item = MultiProofTargets;
148
149 fn next(&mut self) -> Option<Self::Item> {
150 let chunk = self.flattened_targets.by_ref().take(self.size).fold(
151 MultiProofTargets::default(),
152 |mut acc, (address, slot)| {
153 let entry = acc.entry(address).or_default();
154 if let Some(slot) = slot {
155 entry.insert(slot);
156 }
157 acc
158 },
159 );
160
161 if chunk.is_empty() {
162 None
163 } else {
164 Some(chunk)
165 }
166 }
167}
168
169#[derive(Clone, Default, Debug, PartialEq, Eq)]
173pub struct MultiProof {
174 pub account_subtree: ProofNodes,
176 pub branch_node_hash_masks: HashMap<Nibbles, TrieMask>,
178 pub branch_node_tree_masks: HashMap<Nibbles, TrieMask>,
180 pub storages: B256Map<StorageMultiProof>,
182}
183
184impl MultiProof {
185 pub fn is_empty(&self) -> bool {
187 self.account_subtree.is_empty() &&
188 self.branch_node_hash_masks.is_empty() &&
189 self.branch_node_tree_masks.is_empty() &&
190 self.storages.is_empty()
191 }
192
193 pub fn account_proof_nodes(&self, path: &Nibbles) -> Vec<(Nibbles, Bytes)> {
195 self.account_subtree.matching_nodes_sorted(path)
196 }
197
198 pub fn storage_proof_nodes(
200 &self,
201 hashed_address: B256,
202 slots: impl IntoIterator<Item = B256>,
203 ) -> Vec<(B256, Vec<(Nibbles, Bytes)>)> {
204 self.storages
205 .get(&hashed_address)
206 .map(|storage_mp| {
207 slots
208 .into_iter()
209 .map(|slot| {
210 let nibbles = Nibbles::unpack(slot);
211 (slot, storage_mp.subtree.matching_nodes_sorted(&nibbles))
212 })
213 .collect()
214 })
215 .unwrap_or_default()
216 }
217
218 pub fn account_proof(
220 &self,
221 address: Address,
222 slots: &[B256],
223 ) -> Result<AccountProof, alloy_rlp::Error> {
224 let hashed_address = keccak256(address);
225 let nibbles = Nibbles::unpack(hashed_address);
226
227 let proof = self
229 .account_proof_nodes(&nibbles)
230 .into_iter()
231 .map(|(_, node)| node)
232 .collect::<Vec<_>>();
233
234 let info = 'info: {
237 if let Some(last) = proof.last() &&
238 let TrieNode::Leaf(leaf) = TrieNode::decode(&mut &last[..])? &&
239 nibbles.ends_with(&leaf.key)
240 {
241 let account = TrieAccount::decode(&mut &leaf.value[..])?;
242 break 'info Some(Account {
243 balance: account.balance,
244 nonce: account.nonce,
245 bytecode_hash: (account.code_hash != KECCAK_EMPTY).then_some(account.code_hash),
246 })
247 }
248 None
249 };
250
251 let storage_multiproof = self.storages.get(&hashed_address);
253 let storage_root = storage_multiproof.map(|m| m.root).unwrap_or(EMPTY_ROOT_HASH);
254 let mut storage_proofs = Vec::with_capacity(slots.len());
255 for slot in slots {
256 let proof = if let Some(multiproof) = &storage_multiproof {
257 multiproof.storage_proof(*slot)?
258 } else {
259 StorageProof::new(*slot)
260 };
261 storage_proofs.push(proof);
262 }
263 Ok(AccountProof { address, info, proof, storage_root, storage_proofs })
264 }
265
266 pub fn extend(&mut self, other: Self) {
269 self.account_subtree.extend_from(other.account_subtree);
270
271 self.branch_node_hash_masks.extend(other.branch_node_hash_masks);
272 self.branch_node_tree_masks.extend(other.branch_node_tree_masks);
273
274 for (hashed_address, storage) in other.storages {
275 match self.storages.entry(hashed_address) {
276 hash_map::Entry::Occupied(mut entry) => {
277 debug_assert_eq!(entry.get().root, storage.root);
278 let entry = entry.get_mut();
279 entry.subtree.extend_from(storage.subtree);
280 entry.branch_node_hash_masks.extend(storage.branch_node_hash_masks);
281 entry.branch_node_tree_masks.extend(storage.branch_node_tree_masks);
282 }
283 hash_map::Entry::Vacant(entry) => {
284 entry.insert(storage);
285 }
286 }
287 }
288 }
289
290 pub fn from_storage_proof(hashed_address: B256, storage_proof: StorageMultiProof) -> Self {
292 Self {
293 storages: B256Map::from_iter([(hashed_address, storage_proof)]),
294 ..Default::default()
295 }
296 }
297}
298
299#[derive(Clone, Default, Debug, PartialEq, Eq)]
302pub struct DecodedMultiProof {
303 pub account_subtree: DecodedProofNodes,
305 pub branch_node_hash_masks: HashMap<Nibbles, TrieMask>,
307 pub branch_node_tree_masks: HashMap<Nibbles, TrieMask>,
309 pub storages: B256Map<DecodedStorageMultiProof>,
311}
312
313impl DecodedMultiProof {
314 pub fn is_empty(&self) -> bool {
316 self.account_subtree.is_empty() &&
317 self.branch_node_hash_masks.is_empty() &&
318 self.branch_node_tree_masks.is_empty() &&
319 self.storages.is_empty()
320 }
321
322 pub fn account_proof_nodes(&self, path: &Nibbles) -> Vec<(Nibbles, TrieNode)> {
324 self.account_subtree.matching_nodes_sorted(path)
325 }
326
327 pub fn storage_proof_nodes(
329 &self,
330 hashed_address: B256,
331 slots: impl IntoIterator<Item = B256>,
332 ) -> Vec<(B256, Vec<(Nibbles, TrieNode)>)> {
333 self.storages
334 .get(&hashed_address)
335 .map(|storage_mp| {
336 slots
337 .into_iter()
338 .map(|slot| {
339 let nibbles = Nibbles::unpack(slot);
340 (slot, storage_mp.subtree.matching_nodes_sorted(&nibbles))
341 })
342 .collect()
343 })
344 .unwrap_or_default()
345 }
346
347 pub fn account_proof(
349 &self,
350 address: Address,
351 slots: &[B256],
352 ) -> Result<DecodedAccountProof, alloy_rlp::Error> {
353 let hashed_address = keccak256(address);
354 let nibbles = Nibbles::unpack(hashed_address);
355
356 let proof = self
358 .account_proof_nodes(&nibbles)
359 .into_iter()
360 .map(|(_, node)| node)
361 .collect::<Vec<_>>();
362
363 let info = 'info: {
366 if let Some(TrieNode::Leaf(leaf)) = proof.last() &&
367 nibbles.ends_with(&leaf.key)
368 {
369 let account = TrieAccount::decode(&mut &leaf.value[..])?;
370 break 'info Some(Account {
371 balance: account.balance,
372 nonce: account.nonce,
373 bytecode_hash: (account.code_hash != KECCAK_EMPTY).then_some(account.code_hash),
374 })
375 }
376 None
377 };
378
379 let storage_multiproof = self.storages.get(&hashed_address);
381 let storage_root = storage_multiproof.map(|m| m.root).unwrap_or(EMPTY_ROOT_HASH);
382 let mut storage_proofs = Vec::with_capacity(slots.len());
383 for slot in slots {
384 let proof = if let Some(multiproof) = &storage_multiproof {
385 multiproof.storage_proof(*slot)?
386 } else {
387 DecodedStorageProof::new(*slot)
388 };
389 storage_proofs.push(proof);
390 }
391 Ok(DecodedAccountProof { address, info, proof, storage_root, storage_proofs })
392 }
393
394 pub fn extend(&mut self, other: Self) {
397 self.account_subtree.extend_from(other.account_subtree);
398
399 self.branch_node_hash_masks.extend(other.branch_node_hash_masks);
400 self.branch_node_tree_masks.extend(other.branch_node_tree_masks);
401
402 for (hashed_address, storage) in other.storages {
403 match self.storages.entry(hashed_address) {
404 hash_map::Entry::Occupied(mut entry) => {
405 debug_assert_eq!(entry.get().root, storage.root);
406 let entry = entry.get_mut();
407 entry.subtree.extend_from(storage.subtree);
408 entry.branch_node_hash_masks.extend(storage.branch_node_hash_masks);
409 entry.branch_node_tree_masks.extend(storage.branch_node_tree_masks);
410 }
411 hash_map::Entry::Vacant(entry) => {
412 entry.insert(storage);
413 }
414 }
415 }
416 }
417
418 pub fn from_storage_proof(
420 hashed_address: B256,
421 storage_proof: DecodedStorageMultiProof,
422 ) -> Self {
423 Self {
424 storages: B256Map::from_iter([(hashed_address, storage_proof)]),
425 ..Default::default()
426 }
427 }
428}
429
430impl TryFrom<MultiProof> for DecodedMultiProof {
431 type Error = alloy_rlp::Error;
432
433 fn try_from(multi_proof: MultiProof) -> Result<Self, Self::Error> {
434 let account_subtree = DecodedProofNodes::try_from(multi_proof.account_subtree)?;
435 let storages = multi_proof
436 .storages
437 .into_iter()
438 .map(|(address, storage)| Ok((address, storage.try_into()?)))
439 .collect::<Result<B256Map<_>, alloy_rlp::Error>>()?;
440 Ok(Self {
441 account_subtree,
442 branch_node_hash_masks: multi_proof.branch_node_hash_masks,
443 branch_node_tree_masks: multi_proof.branch_node_tree_masks,
444 storages,
445 })
446 }
447}
448
449#[derive(Clone, Debug, PartialEq, Eq)]
451pub struct StorageMultiProof {
452 pub root: B256,
454 pub subtree: ProofNodes,
456 pub branch_node_hash_masks: HashMap<Nibbles, TrieMask>,
458 pub branch_node_tree_masks: HashMap<Nibbles, TrieMask>,
460}
461
462impl StorageMultiProof {
463 pub fn empty() -> Self {
465 Self {
466 root: EMPTY_ROOT_HASH,
467 subtree: ProofNodes::from_iter([(
468 Nibbles::default(),
469 Bytes::from([EMPTY_STRING_CODE]),
470 )]),
471 branch_node_hash_masks: HashMap::default(),
472 branch_node_tree_masks: HashMap::default(),
473 }
474 }
475
476 pub fn storage_proof(&self, slot: B256) -> Result<StorageProof, alloy_rlp::Error> {
478 let nibbles = Nibbles::unpack(keccak256(slot));
479
480 let proof = self
482 .subtree
483 .matching_nodes_iter(&nibbles)
484 .sorted_by(|a, b| a.0.cmp(b.0))
485 .map(|(_, node)| node.clone())
486 .collect::<Vec<_>>();
487
488 let value = 'value: {
491 if let Some(last) = proof.last() &&
492 let TrieNode::Leaf(leaf) = TrieNode::decode(&mut &last[..])? &&
493 nibbles.ends_with(&leaf.key)
494 {
495 break 'value U256::decode(&mut &leaf.value[..])?
496 }
497 U256::ZERO
498 };
499
500 Ok(StorageProof { key: slot, nibbles, value, proof })
501 }
502}
503
504#[derive(Clone, Debug, PartialEq, Eq)]
506pub struct DecodedStorageMultiProof {
507 pub root: B256,
509 pub subtree: DecodedProofNodes,
511 pub branch_node_hash_masks: HashMap<Nibbles, TrieMask>,
513 pub branch_node_tree_masks: HashMap<Nibbles, TrieMask>,
515}
516
517impl DecodedStorageMultiProof {
518 pub fn empty() -> Self {
520 Self {
521 root: EMPTY_ROOT_HASH,
522 subtree: DecodedProofNodes::from_iter([(Nibbles::default(), TrieNode::EmptyRoot)]),
523 branch_node_hash_masks: HashMap::default(),
524 branch_node_tree_masks: HashMap::default(),
525 }
526 }
527
528 pub fn storage_proof(&self, slot: B256) -> Result<DecodedStorageProof, alloy_rlp::Error> {
530 let nibbles = Nibbles::unpack(keccak256(slot));
531
532 let proof = self
534 .subtree
535 .matching_nodes_iter(&nibbles)
536 .sorted_by(|a, b| a.0.cmp(b.0))
537 .map(|(_, node)| node.clone())
538 .collect::<Vec<_>>();
539
540 let value = 'value: {
543 if let Some(TrieNode::Leaf(leaf)) = proof.last() &&
544 nibbles.ends_with(&leaf.key)
545 {
546 break 'value U256::decode(&mut &leaf.value[..])?
547 }
548 U256::ZERO
549 };
550
551 Ok(DecodedStorageProof { key: slot, nibbles, value, proof })
552 }
553}
554
555impl TryFrom<StorageMultiProof> for DecodedStorageMultiProof {
556 type Error = alloy_rlp::Error;
557
558 fn try_from(multi_proof: StorageMultiProof) -> Result<Self, Self::Error> {
559 let subtree = DecodedProofNodes::try_from(multi_proof.subtree)?;
560 Ok(Self {
561 root: multi_proof.root,
562 subtree,
563 branch_node_hash_masks: multi_proof.branch_node_hash_masks,
564 branch_node_tree_masks: multi_proof.branch_node_tree_masks,
565 })
566 }
567}
568
569#[derive(Clone, PartialEq, Eq, Debug)]
571#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
572#[cfg_attr(any(test, feature = "serde"), serde(rename_all = "camelCase"))]
573pub struct AccountProof {
574 pub address: Address,
576 pub info: Option<Account>,
578 pub proof: Vec<Bytes>,
581 pub storage_root: B256,
583 pub storage_proofs: Vec<StorageProof>,
585}
586
587#[cfg(feature = "eip1186")]
588impl AccountProof {
589 pub fn into_eip1186_response(
591 self,
592 slots: Vec<alloy_serde::JsonStorageKey>,
593 ) -> alloy_rpc_types_eth::EIP1186AccountProofResponse {
594 let info = self.info.unwrap_or_default();
595 alloy_rpc_types_eth::EIP1186AccountProofResponse {
596 address: self.address,
597 balance: info.balance,
598 code_hash: info.get_bytecode_hash(),
599 nonce: info.nonce,
600 storage_hash: self.storage_root,
601 account_proof: self.proof,
602 storage_proof: self
603 .storage_proofs
604 .into_iter()
605 .filter_map(|proof| {
606 let input_slot = slots.iter().find(|s| s.as_b256() == proof.key)?;
607 Some(proof.into_eip1186_proof(*input_slot))
608 })
609 .collect(),
610 }
611 }
612
613 pub fn from_eip1186_proof(proof: alloy_rpc_types_eth::EIP1186AccountProofResponse) -> Self {
619 let alloy_rpc_types_eth::EIP1186AccountProofResponse {
620 nonce,
621 address,
622 balance,
623 code_hash,
624 storage_hash,
625 account_proof,
626 storage_proof,
627 ..
628 } = proof;
629 let storage_proofs = storage_proof.into_iter().map(Into::into).collect();
630
631 let (storage_root, info) = if nonce == 0 &&
632 balance.is_zero() &&
633 storage_hash.is_zero() &&
634 code_hash == KECCAK_EMPTY
635 {
636 (EMPTY_ROOT_HASH, None)
639 } else {
640 (storage_hash, Some(Account { nonce, balance, bytecode_hash: code_hash.into() }))
641 };
642
643 Self { address, info, proof: account_proof, storage_root, storage_proofs }
644 }
645}
646
647#[cfg(feature = "eip1186")]
648impl From<alloy_rpc_types_eth::EIP1186AccountProofResponse> for AccountProof {
649 fn from(proof: alloy_rpc_types_eth::EIP1186AccountProofResponse) -> Self {
650 Self::from_eip1186_proof(proof)
651 }
652}
653
654impl Default for AccountProof {
655 fn default() -> Self {
656 Self::new(Address::default())
657 }
658}
659
660impl AccountProof {
661 pub const fn new(address: Address) -> Self {
663 Self {
664 address,
665 info: None,
666 proof: Vec::new(),
667 storage_root: EMPTY_ROOT_HASH,
668 storage_proofs: Vec::new(),
669 }
670 }
671
672 pub fn verify(&self, root: B256) -> Result<(), ProofVerificationError> {
674 for storage_proof in &self.storage_proofs {
676 storage_proof.verify(self.storage_root)?;
677 }
678
679 let expected = if self.info.is_none() && self.storage_root == EMPTY_ROOT_HASH {
681 None
682 } else {
683 Some(alloy_rlp::encode(
684 self.info.unwrap_or_default().into_trie_account(self.storage_root),
685 ))
686 };
687 let nibbles = Nibbles::unpack(keccak256(self.address));
688 verify_proof(root, nibbles, expected, &self.proof)
689 }
690}
691
692#[derive(Clone, PartialEq, Eq, Debug)]
694pub struct DecodedAccountProof {
695 pub address: Address,
697 pub info: Option<Account>,
699 pub proof: Vec<TrieNode>,
702 pub storage_root: B256,
704 pub storage_proofs: Vec<DecodedStorageProof>,
706}
707
708impl Default for DecodedAccountProof {
709 fn default() -> Self {
710 Self::new(Address::default())
711 }
712}
713
714impl DecodedAccountProof {
715 pub const fn new(address: Address) -> Self {
717 Self {
718 address,
719 info: None,
720 proof: Vec::new(),
721 storage_root: EMPTY_ROOT_HASH,
722 storage_proofs: Vec::new(),
723 }
724 }
725}
726
727#[derive(Clone, PartialEq, Eq, Default, Debug)]
729#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
730pub struct StorageProof {
731 pub key: B256,
733 pub nibbles: Nibbles,
735 pub value: U256,
737 pub proof: Vec<Bytes>,
740}
741
742impl StorageProof {
743 pub fn new(key: B256) -> Self {
745 let nibbles = Nibbles::unpack(keccak256(key));
746 Self { key, nibbles, ..Default::default() }
747 }
748
749 pub fn new_with_hashed(key: B256, hashed_key: B256) -> Self {
751 Self { key, nibbles: Nibbles::unpack(hashed_key), ..Default::default() }
752 }
753
754 pub fn new_with_nibbles(key: B256, nibbles: Nibbles) -> Self {
756 Self { key, nibbles, ..Default::default() }
757 }
758
759 pub fn with_proof(mut self, proof: Vec<Bytes>) -> Self {
761 self.proof = proof;
762 self
763 }
764
765 pub fn verify(&self, root: B256) -> Result<(), ProofVerificationError> {
767 let expected =
768 if self.value.is_zero() { None } else { Some(encode_fixed_size(&self.value).to_vec()) };
769 verify_proof(root, self.nibbles, expected, &self.proof)
770 }
771}
772
773#[cfg(feature = "eip1186")]
774impl StorageProof {
775 pub fn into_eip1186_proof(
777 self,
778 slot: alloy_serde::JsonStorageKey,
779 ) -> alloy_rpc_types_eth::EIP1186StorageProof {
780 alloy_rpc_types_eth::EIP1186StorageProof { key: slot, value: self.value, proof: self.proof }
781 }
782
783 pub fn from_eip1186_proof(storage_proof: alloy_rpc_types_eth::EIP1186StorageProof) -> Self {
788 Self {
789 value: storage_proof.value,
790 proof: storage_proof.proof,
791 ..Self::new(storage_proof.key.as_b256())
792 }
793 }
794}
795
796#[cfg(feature = "eip1186")]
797impl From<alloy_rpc_types_eth::EIP1186StorageProof> for StorageProof {
798 fn from(proof: alloy_rpc_types_eth::EIP1186StorageProof) -> Self {
799 Self::from_eip1186_proof(proof)
800 }
801}
802
803#[derive(Clone, PartialEq, Eq, Default, Debug)]
805pub struct DecodedStorageProof {
806 pub key: B256,
808 pub nibbles: Nibbles,
810 pub value: U256,
812 pub proof: Vec<TrieNode>,
815}
816
817impl DecodedStorageProof {
818 pub fn new(key: B256) -> Self {
820 let nibbles = Nibbles::unpack(keccak256(key));
821 Self { key, nibbles, ..Default::default() }
822 }
823
824 pub fn new_with_hashed(key: B256, hashed_key: B256) -> Self {
826 Self { key, nibbles: Nibbles::unpack(hashed_key), ..Default::default() }
827 }
828
829 pub fn new_with_nibbles(key: B256, nibbles: Nibbles) -> Self {
831 Self { key, nibbles, ..Default::default() }
832 }
833
834 pub fn with_proof(mut self, proof: Vec<TrieNode>) -> Self {
836 self.proof = proof;
837 self
838 }
839}
840
841#[cfg(any(test, feature = "test-utils"))]
844pub mod triehash {
845 use alloy_primitives::{keccak256, B256};
846 use alloy_rlp::RlpEncodable;
847 use hash_db::Hasher;
848 use plain_hasher::PlainHasher;
849
850 #[derive(Default, Debug, Clone, PartialEq, Eq, RlpEncodable)]
852 #[non_exhaustive]
853 pub struct KeccakHasher;
854
855 #[cfg(any(test, feature = "test-utils"))]
856 impl Hasher for KeccakHasher {
857 type Out = B256;
858 type StdHasher = PlainHasher;
859
860 const LENGTH: usize = 32;
861
862 fn hash(x: &[u8]) -> Self::Out {
863 keccak256(x)
864 }
865 }
866}
867
868#[cfg(test)]
869mod tests {
870 use super::*;
871
872 #[test]
873 fn test_multiproof_extend_account_proofs() {
874 let mut proof1 = MultiProof::default();
875 let mut proof2 = MultiProof::default();
876
877 let addr1 = B256::random();
878 let addr2 = B256::random();
879
880 proof1.account_subtree.insert(
881 Nibbles::unpack(addr1),
882 alloy_rlp::encode_fixed_size(&U256::from(42)).to_vec().into(),
883 );
884 proof2.account_subtree.insert(
885 Nibbles::unpack(addr2),
886 alloy_rlp::encode_fixed_size(&U256::from(43)).to_vec().into(),
887 );
888
889 proof1.extend(proof2);
890
891 assert!(proof1.account_subtree.contains_key(&Nibbles::unpack(addr1)));
892 assert!(proof1.account_subtree.contains_key(&Nibbles::unpack(addr2)));
893 }
894
895 #[test]
896 fn test_multiproof_extend_storage_proofs() {
897 let mut proof1 = MultiProof::default();
898 let mut proof2 = MultiProof::default();
899
900 let addr = B256::random();
901 let root = B256::random();
902
903 let mut subtree1 = ProofNodes::default();
904 subtree1.insert(
905 Nibbles::from_nibbles(vec![0]),
906 alloy_rlp::encode_fixed_size(&U256::from(42)).to_vec().into(),
907 );
908 proof1.storages.insert(
909 addr,
910 StorageMultiProof {
911 root,
912 subtree: subtree1,
913 branch_node_hash_masks: HashMap::default(),
914 branch_node_tree_masks: HashMap::default(),
915 },
916 );
917
918 let mut subtree2 = ProofNodes::default();
919 subtree2.insert(
920 Nibbles::from_nibbles(vec![1]),
921 alloy_rlp::encode_fixed_size(&U256::from(43)).to_vec().into(),
922 );
923 proof2.storages.insert(
924 addr,
925 StorageMultiProof {
926 root,
927 subtree: subtree2,
928 branch_node_hash_masks: HashMap::default(),
929 branch_node_tree_masks: HashMap::default(),
930 },
931 );
932
933 proof1.extend(proof2);
934
935 let storage = proof1.storages.get(&addr).unwrap();
936 assert_eq!(storage.root, root);
937 assert!(storage.subtree.contains_key(&Nibbles::from_nibbles(vec![0])));
938 assert!(storage.subtree.contains_key(&Nibbles::from_nibbles(vec![1])));
939 }
940
941 #[test]
942 fn test_multi_proof_retain_difference() {
943 let mut empty = MultiProofTargets::default();
944 empty.retain_difference(&Default::default());
945 assert!(empty.is_empty());
946
947 let targets = MultiProofTargets::accounts((0..10).map(B256::with_last_byte));
948
949 let mut diffed = targets.clone();
950 diffed.retain_difference(&MultiProofTargets::account(B256::with_last_byte(11)));
951 assert_eq!(diffed, targets);
952
953 diffed.retain_difference(&MultiProofTargets::accounts((0..5).map(B256::with_last_byte)));
954 assert_eq!(diffed, MultiProofTargets::accounts((5..10).map(B256::with_last_byte)));
955
956 diffed.retain_difference(&targets);
957 assert!(diffed.is_empty());
958
959 let mut targets = MultiProofTargets::default();
960 let (account1, account2, account3) =
961 (1..=3).map(B256::with_last_byte).collect_tuple().unwrap();
962 let account2_slots = (1..5).map(B256::with_last_byte).collect::<B256Set>();
963 targets.insert(account1, B256Set::from_iter([B256::with_last_byte(1)]));
964 targets.insert(account2, account2_slots.clone());
965 targets.insert(account3, B256Set::from_iter([B256::with_last_byte(1)]));
966
967 let mut diffed = targets.clone();
968 diffed.retain_difference(&MultiProofTargets::accounts((1..=3).map(B256::with_last_byte)));
969 assert_eq!(diffed, targets);
970
971 let mut account2_slots_expected_len = account2_slots.len();
973 for slot in account2_slots.iter().skip(1) {
974 diffed.retain_difference(&MultiProofTargets::account_with_slots(account2, [*slot]));
975 account2_slots_expected_len -= 1;
976 assert_eq!(
977 diffed.get(&account2).map(|slots| slots.len()),
978 Some(account2_slots_expected_len)
979 );
980 }
981
982 diffed.retain_difference(&targets);
983 assert!(diffed.is_empty());
984 }
985
986 #[test]
987 fn test_multi_proof_retain_difference_no_overlap() {
988 let mut targets = MultiProofTargets::default();
989
990 let (addr1, addr2) = (B256::random(), B256::random());
992 let (slot1, slot2) = (B256::random(), B256::random());
993 targets.insert(addr1, std::iter::once(slot1).collect());
994 targets.insert(addr2, std::iter::once(slot2).collect());
995
996 let mut retained = targets.clone();
997 retained.retain_difference(&Default::default());
998 assert_eq!(retained, targets);
999
1000 let mut other_targets = MultiProofTargets::default();
1002 let addr3 = B256::random();
1003 let slot3 = B256::random();
1004 other_targets.insert(addr3, B256Set::from_iter([slot3]));
1005
1006 let mut retained = targets.clone();
1009 retained.retain_difference(&other_targets);
1010 assert_eq!(retained, targets);
1011 }
1012
1013 #[test]
1014 fn test_get_prefetch_proof_targets_remove_subset() {
1015 let mut targets = MultiProofTargets::default();
1017 let (addr1, addr2) = (B256::random(), B256::random());
1018 let (slot1, slot2) = (B256::random(), B256::random());
1019 targets.insert(addr1, B256Set::from_iter([slot1]));
1020 targets.insert(addr2, B256Set::from_iter([slot2]));
1021
1022 let other_targets = MultiProofTargets::account_with_slots(addr1, [slot1]);
1024
1025 let mut retained = targets.clone();
1026 retained.retain_difference(&other_targets);
1027
1028 assert_eq!(retained.len(), 1);
1030 assert!(!retained.contains_key(&addr1));
1031 assert!(retained.contains_key(&addr2));
1032
1033 let slot3 = B256::random();
1035 targets.get_mut(&addr1).unwrap().insert(slot3);
1036
1037 let mut retained = targets.clone();
1038 retained.retain_difference(&other_targets);
1039
1040 assert_eq!(retained.len(), 2);
1043 assert!(retained.contains_key(&addr1));
1044 assert_eq!(retained.get(&addr1), Some(&B256Set::from_iter([slot3])));
1045 assert!(retained.contains_key(&addr2));
1046 assert_eq!(retained.get(&addr2), Some(&B256Set::from_iter([slot2])));
1047 }
1048
1049 #[test]
1050 #[cfg(feature = "eip1186")]
1051 fn eip_1186_roundtrip() {
1052 let mut acc = AccountProof {
1053 address: Address::random(),
1054 info: Some(
1055 Account { nonce: 100, balance: U256::ZERO, bytecode_hash: Some(KECCAK_EMPTY) },
1057 ),
1058 proof: vec![],
1059 storage_root: B256::ZERO,
1060 storage_proofs: vec![],
1061 };
1062
1063 let rpc_proof = acc.clone().into_eip1186_response(Vec::new());
1064 let inverse: AccountProof = rpc_proof.into();
1065 assert_eq!(acc, inverse);
1066
1067 acc.info.as_mut().unwrap().nonce = 0;
1069 let rpc_proof = acc.clone().into_eip1186_response(Vec::new());
1070 let inverse: AccountProof = rpc_proof.into();
1071 acc.info.take();
1072 acc.storage_root = EMPTY_ROOT_HASH;
1073 assert_eq!(acc, inverse);
1074 }
1075
1076 #[test]
1077 fn test_multiproof_targets_chunking_length() {
1078 let mut targets = MultiProofTargets::default();
1079 targets.insert(B256::with_last_byte(1), B256Set::default());
1080 targets.insert(
1081 B256::with_last_byte(2),
1082 B256Set::from_iter([B256::with_last_byte(10), B256::with_last_byte(20)]),
1083 );
1084 targets.insert(
1085 B256::with_last_byte(3),
1086 B256Set::from_iter([
1087 B256::with_last_byte(30),
1088 B256::with_last_byte(31),
1089 B256::with_last_byte(32),
1090 ]),
1091 );
1092
1093 let chunking_length = targets.chunking_length();
1094 for size in 1..=targets.clone().chunks(1).count() {
1095 let chunk_count = targets.clone().chunks(size).count();
1096 let expected_count = chunking_length.div_ceil(size);
1097 assert_eq!(
1098 chunk_count, expected_count,
1099 "chunking_length: {}, size: {}",
1100 chunking_length, size
1101 );
1102 }
1103 }
1104}