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 if let TrieNode::Leaf(leaf) = TrieNode::decode(&mut &last[..])? {
234 if nibbles.ends_with(&leaf.key) {
235 let account = TrieAccount::decode(&mut &leaf.value[..])?;
236 break 'info Some(Account {
237 balance: account.balance,
238 nonce: account.nonce,
239 bytecode_hash: (account.code_hash != KECCAK_EMPTY)
240 .then_some(account.code_hash),
241 })
242 }
243 }
244 }
245 None
246 };
247
248 let storage_multiproof = self.storages.get(&hashed_address);
250 let storage_root = storage_multiproof.map(|m| m.root).unwrap_or(EMPTY_ROOT_HASH);
251 let mut storage_proofs = Vec::with_capacity(slots.len());
252 for slot in slots {
253 let proof = if let Some(multiproof) = &storage_multiproof {
254 multiproof.storage_proof(*slot)?
255 } else {
256 StorageProof::new(*slot)
257 };
258 storage_proofs.push(proof);
259 }
260 Ok(AccountProof { address, info, proof, storage_root, storage_proofs })
261 }
262
263 pub fn extend(&mut self, other: Self) {
266 self.account_subtree.extend_from(other.account_subtree);
267
268 self.branch_node_hash_masks.extend(other.branch_node_hash_masks);
269 self.branch_node_tree_masks.extend(other.branch_node_tree_masks);
270
271 for (hashed_address, storage) in other.storages {
272 match self.storages.entry(hashed_address) {
273 hash_map::Entry::Occupied(mut entry) => {
274 debug_assert_eq!(entry.get().root, storage.root);
275 let entry = entry.get_mut();
276 entry.subtree.extend_from(storage.subtree);
277 entry.branch_node_hash_masks.extend(storage.branch_node_hash_masks);
278 entry.branch_node_tree_masks.extend(storage.branch_node_tree_masks);
279 }
280 hash_map::Entry::Vacant(entry) => {
281 entry.insert(storage);
282 }
283 }
284 }
285 }
286}
287
288#[derive(Clone, Default, Debug, PartialEq, Eq)]
291pub struct DecodedMultiProof {
292 pub account_subtree: DecodedProofNodes,
294 pub branch_node_hash_masks: HashMap<Nibbles, TrieMask>,
296 pub branch_node_tree_masks: HashMap<Nibbles, TrieMask>,
298 pub storages: B256Map<DecodedStorageMultiProof>,
300}
301
302impl DecodedMultiProof {
303 pub fn account_proof_nodes(&self, path: &Nibbles) -> Vec<(Nibbles, TrieNode)> {
305 self.account_subtree.matching_nodes_sorted(path)
306 }
307
308 pub fn storage_proof_nodes(
310 &self,
311 hashed_address: B256,
312 slots: impl IntoIterator<Item = B256>,
313 ) -> Vec<(B256, Vec<(Nibbles, TrieNode)>)> {
314 self.storages
315 .get(&hashed_address)
316 .map(|storage_mp| {
317 slots
318 .into_iter()
319 .map(|slot| {
320 let nibbles = Nibbles::unpack(slot);
321 (slot, storage_mp.subtree.matching_nodes_sorted(&nibbles))
322 })
323 .collect()
324 })
325 .unwrap_or_default()
326 }
327
328 pub fn account_proof(
330 &self,
331 address: Address,
332 slots: &[B256],
333 ) -> Result<DecodedAccountProof, alloy_rlp::Error> {
334 let hashed_address = keccak256(address);
335 let nibbles = Nibbles::unpack(hashed_address);
336
337 let proof = self
339 .account_proof_nodes(&nibbles)
340 .into_iter()
341 .map(|(_, node)| node)
342 .collect::<Vec<_>>();
343
344 let info = 'info: {
347 if let Some(TrieNode::Leaf(leaf)) = proof.last() {
348 if nibbles.ends_with(&leaf.key) {
349 let account = TrieAccount::decode(&mut &leaf.value[..])?;
350 break 'info Some(Account {
351 balance: account.balance,
352 nonce: account.nonce,
353 bytecode_hash: (account.code_hash != KECCAK_EMPTY)
354 .then_some(account.code_hash),
355 })
356 }
357 }
358 None
359 };
360
361 let storage_multiproof = self.storages.get(&hashed_address);
363 let storage_root = storage_multiproof.map(|m| m.root).unwrap_or(EMPTY_ROOT_HASH);
364 let mut storage_proofs = Vec::with_capacity(slots.len());
365 for slot in slots {
366 let proof = if let Some(multiproof) = &storage_multiproof {
367 multiproof.storage_proof(*slot)?
368 } else {
369 DecodedStorageProof::new(*slot)
370 };
371 storage_proofs.push(proof);
372 }
373 Ok(DecodedAccountProof { address, info, proof, storage_root, storage_proofs })
374 }
375
376 pub fn extend(&mut self, other: Self) {
379 self.account_subtree.extend_from(other.account_subtree);
380
381 self.branch_node_hash_masks.extend(other.branch_node_hash_masks);
382 self.branch_node_tree_masks.extend(other.branch_node_tree_masks);
383
384 for (hashed_address, storage) in other.storages {
385 match self.storages.entry(hashed_address) {
386 hash_map::Entry::Occupied(mut entry) => {
387 debug_assert_eq!(entry.get().root, storage.root);
388 let entry = entry.get_mut();
389 entry.subtree.extend_from(storage.subtree);
390 entry.branch_node_hash_masks.extend(storage.branch_node_hash_masks);
391 entry.branch_node_tree_masks.extend(storage.branch_node_tree_masks);
392 }
393 hash_map::Entry::Vacant(entry) => {
394 entry.insert(storage);
395 }
396 }
397 }
398 }
399}
400
401#[derive(Clone, Debug, PartialEq, Eq)]
403pub struct StorageMultiProof {
404 pub root: B256,
406 pub subtree: ProofNodes,
408 pub branch_node_hash_masks: HashMap<Nibbles, TrieMask>,
410 pub branch_node_tree_masks: HashMap<Nibbles, TrieMask>,
412}
413
414impl StorageMultiProof {
415 pub fn empty() -> Self {
417 Self {
418 root: EMPTY_ROOT_HASH,
419 subtree: ProofNodes::from_iter([(
420 Nibbles::default(),
421 Bytes::from([EMPTY_STRING_CODE]),
422 )]),
423 branch_node_hash_masks: HashMap::default(),
424 branch_node_tree_masks: HashMap::default(),
425 }
426 }
427
428 pub fn storage_proof(&self, slot: B256) -> Result<StorageProof, alloy_rlp::Error> {
430 let nibbles = Nibbles::unpack(keccak256(slot));
431
432 let proof = self
434 .subtree
435 .matching_nodes_iter(&nibbles)
436 .sorted_by(|a, b| a.0.cmp(b.0))
437 .map(|(_, node)| node.clone())
438 .collect::<Vec<_>>();
439
440 let value = 'value: {
443 if let Some(last) = proof.last() {
444 if let TrieNode::Leaf(leaf) = TrieNode::decode(&mut &last[..])? {
445 if nibbles.ends_with(&leaf.key) {
446 break 'value U256::decode(&mut &leaf.value[..])?
447 }
448 }
449 }
450 U256::ZERO
451 };
452
453 Ok(StorageProof { key: slot, nibbles, value, proof })
454 }
455}
456
457#[derive(Clone, Debug, PartialEq, Eq)]
459pub struct DecodedStorageMultiProof {
460 pub root: B256,
462 pub subtree: DecodedProofNodes,
464 pub branch_node_hash_masks: HashMap<Nibbles, TrieMask>,
466 pub branch_node_tree_masks: HashMap<Nibbles, TrieMask>,
468}
469
470impl DecodedStorageMultiProof {
471 pub fn empty() -> Self {
473 Self {
474 root: EMPTY_ROOT_HASH,
475 subtree: DecodedProofNodes::from_iter([(Nibbles::default(), TrieNode::EmptyRoot)]),
476 branch_node_hash_masks: HashMap::default(),
477 branch_node_tree_masks: HashMap::default(),
478 }
479 }
480
481 pub fn storage_proof(&self, slot: B256) -> Result<DecodedStorageProof, alloy_rlp::Error> {
483 let nibbles = Nibbles::unpack(keccak256(slot));
484
485 let proof = self
487 .subtree
488 .matching_nodes_iter(&nibbles)
489 .sorted_by(|a, b| a.0.cmp(b.0))
490 .map(|(_, node)| node.clone())
491 .collect::<Vec<_>>();
492
493 let value = 'value: {
496 if let Some(TrieNode::Leaf(leaf)) = proof.last() {
497 if nibbles.ends_with(&leaf.key) {
498 break 'value U256::decode(&mut &leaf.value[..])?
499 }
500 }
501 U256::ZERO
502 };
503
504 Ok(DecodedStorageProof { key: slot, nibbles, value, proof })
505 }
506}
507
508#[derive(Clone, PartialEq, Eq, Debug)]
510#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
511#[cfg_attr(any(test, feature = "serde"), serde(rename_all = "camelCase"))]
512pub struct AccountProof {
513 pub address: Address,
515 pub info: Option<Account>,
517 pub proof: Vec<Bytes>,
520 pub storage_root: B256,
522 pub storage_proofs: Vec<StorageProof>,
524}
525
526#[cfg(feature = "eip1186")]
527impl AccountProof {
528 pub fn into_eip1186_response(
530 self,
531 slots: Vec<alloy_serde::JsonStorageKey>,
532 ) -> alloy_rpc_types_eth::EIP1186AccountProofResponse {
533 let info = self.info.unwrap_or_default();
534 alloy_rpc_types_eth::EIP1186AccountProofResponse {
535 address: self.address,
536 balance: info.balance,
537 code_hash: info.get_bytecode_hash(),
538 nonce: info.nonce,
539 storage_hash: self.storage_root,
540 account_proof: self.proof,
541 storage_proof: self
542 .storage_proofs
543 .into_iter()
544 .filter_map(|proof| {
545 let input_slot = slots.iter().find(|s| s.as_b256() == proof.key)?;
546 Some(proof.into_eip1186_proof(*input_slot))
547 })
548 .collect(),
549 }
550 }
551}
552
553impl Default for AccountProof {
554 fn default() -> Self {
555 Self::new(Address::default())
556 }
557}
558
559impl AccountProof {
560 pub const fn new(address: Address) -> Self {
562 Self {
563 address,
564 info: None,
565 proof: Vec::new(),
566 storage_root: EMPTY_ROOT_HASH,
567 storage_proofs: Vec::new(),
568 }
569 }
570
571 pub fn verify(&self, root: B256) -> Result<(), ProofVerificationError> {
573 for storage_proof in &self.storage_proofs {
575 storage_proof.verify(self.storage_root)?;
576 }
577
578 let expected = if self.info.is_none() && self.storage_root == EMPTY_ROOT_HASH {
580 None
581 } else {
582 Some(alloy_rlp::encode(
583 self.info.unwrap_or_default().into_trie_account(self.storage_root),
584 ))
585 };
586 let nibbles = Nibbles::unpack(keccak256(self.address));
587 verify_proof(root, nibbles, expected, &self.proof)
588 }
589}
590
591#[derive(Clone, PartialEq, Eq, Debug)]
593pub struct DecodedAccountProof {
594 pub address: Address,
596 pub info: Option<Account>,
598 pub proof: Vec<TrieNode>,
601 pub storage_root: B256,
603 pub storage_proofs: Vec<DecodedStorageProof>,
605}
606
607impl Default for DecodedAccountProof {
608 fn default() -> Self {
609 Self::new(Address::default())
610 }
611}
612
613impl DecodedAccountProof {
614 pub const fn new(address: Address) -> Self {
616 Self {
617 address,
618 info: None,
619 proof: Vec::new(),
620 storage_root: EMPTY_ROOT_HASH,
621 storage_proofs: Vec::new(),
622 }
623 }
624}
625
626#[derive(Clone, PartialEq, Eq, Default, Debug)]
628#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
629pub struct StorageProof {
630 pub key: B256,
632 pub nibbles: Nibbles,
634 pub value: U256,
636 pub proof: Vec<Bytes>,
639}
640
641impl StorageProof {
642 #[cfg(feature = "eip1186")]
644 pub fn into_eip1186_proof(
645 self,
646 slot: alloy_serde::JsonStorageKey,
647 ) -> alloy_rpc_types_eth::EIP1186StorageProof {
648 alloy_rpc_types_eth::EIP1186StorageProof { key: slot, value: self.value, proof: self.proof }
649 }
650}
651
652impl StorageProof {
653 pub fn new(key: B256) -> Self {
655 let nibbles = Nibbles::unpack(keccak256(key));
656 Self { key, nibbles, ..Default::default() }
657 }
658
659 pub fn new_with_hashed(key: B256, hashed_key: B256) -> Self {
661 Self { key, nibbles: Nibbles::unpack(hashed_key), ..Default::default() }
662 }
663
664 pub fn new_with_nibbles(key: B256, nibbles: Nibbles) -> Self {
666 Self { key, nibbles, ..Default::default() }
667 }
668
669 pub fn with_proof(mut self, proof: Vec<Bytes>) -> Self {
671 self.proof = proof;
672 self
673 }
674
675 pub fn verify(&self, root: B256) -> Result<(), ProofVerificationError> {
677 let expected =
678 if self.value.is_zero() { None } else { Some(encode_fixed_size(&self.value).to_vec()) };
679 verify_proof(root, self.nibbles.clone(), expected, &self.proof)
680 }
681}
682
683#[derive(Clone, PartialEq, Eq, Default, Debug)]
685pub struct DecodedStorageProof {
686 pub key: B256,
688 pub nibbles: Nibbles,
690 pub value: U256,
692 pub proof: Vec<TrieNode>,
695}
696
697impl DecodedStorageProof {
698 pub fn new(key: B256) -> Self {
700 let nibbles = Nibbles::unpack(keccak256(key));
701 Self { key, nibbles, ..Default::default() }
702 }
703
704 pub fn new_with_hashed(key: B256, hashed_key: B256) -> Self {
706 Self { key, nibbles: Nibbles::unpack(hashed_key), ..Default::default() }
707 }
708
709 pub fn new_with_nibbles(key: B256, nibbles: Nibbles) -> Self {
711 Self { key, nibbles, ..Default::default() }
712 }
713
714 pub fn with_proof(mut self, proof: Vec<TrieNode>) -> Self {
716 self.proof = proof;
717 self
718 }
719}
720
721#[cfg(any(test, feature = "test-utils"))]
724pub mod triehash {
725 use alloy_primitives::{keccak256, B256};
726 use alloy_rlp::RlpEncodable;
727 use hash_db::Hasher;
728 use plain_hasher::PlainHasher;
729
730 #[derive(Default, Debug, Clone, PartialEq, Eq, RlpEncodable)]
732 #[non_exhaustive]
733 pub struct KeccakHasher;
734
735 #[cfg(any(test, feature = "test-utils"))]
736 impl Hasher for KeccakHasher {
737 type Out = B256;
738 type StdHasher = PlainHasher;
739
740 const LENGTH: usize = 32;
741
742 fn hash(x: &[u8]) -> Self::Out {
743 keccak256(x)
744 }
745 }
746}
747
748#[cfg(test)]
749mod tests {
750 use super::*;
751
752 #[test]
753 fn test_multiproof_extend_account_proofs() {
754 let mut proof1 = MultiProof::default();
755 let mut proof2 = MultiProof::default();
756
757 let addr1 = B256::random();
758 let addr2 = B256::random();
759
760 proof1.account_subtree.insert(
761 Nibbles::unpack(addr1),
762 alloy_rlp::encode_fixed_size(&U256::from(42)).to_vec().into(),
763 );
764 proof2.account_subtree.insert(
765 Nibbles::unpack(addr2),
766 alloy_rlp::encode_fixed_size(&U256::from(43)).to_vec().into(),
767 );
768
769 proof1.extend(proof2);
770
771 assert!(proof1.account_subtree.contains_key(&Nibbles::unpack(addr1)));
772 assert!(proof1.account_subtree.contains_key(&Nibbles::unpack(addr2)));
773 }
774
775 #[test]
776 fn test_multiproof_extend_storage_proofs() {
777 let mut proof1 = MultiProof::default();
778 let mut proof2 = MultiProof::default();
779
780 let addr = B256::random();
781 let root = B256::random();
782
783 let mut subtree1 = ProofNodes::default();
784 subtree1.insert(
785 Nibbles::from_nibbles(vec![0]),
786 alloy_rlp::encode_fixed_size(&U256::from(42)).to_vec().into(),
787 );
788 proof1.storages.insert(
789 addr,
790 StorageMultiProof {
791 root,
792 subtree: subtree1,
793 branch_node_hash_masks: HashMap::default(),
794 branch_node_tree_masks: HashMap::default(),
795 },
796 );
797
798 let mut subtree2 = ProofNodes::default();
799 subtree2.insert(
800 Nibbles::from_nibbles(vec![1]),
801 alloy_rlp::encode_fixed_size(&U256::from(43)).to_vec().into(),
802 );
803 proof2.storages.insert(
804 addr,
805 StorageMultiProof {
806 root,
807 subtree: subtree2,
808 branch_node_hash_masks: HashMap::default(),
809 branch_node_tree_masks: HashMap::default(),
810 },
811 );
812
813 proof1.extend(proof2);
814
815 let storage = proof1.storages.get(&addr).unwrap();
816 assert_eq!(storage.root, root);
817 assert!(storage.subtree.contains_key(&Nibbles::from_nibbles(vec![0])));
818 assert!(storage.subtree.contains_key(&Nibbles::from_nibbles(vec![1])));
819 }
820
821 #[test]
822 fn test_multi_proof_retain_difference() {
823 let mut empty = MultiProofTargets::default();
824 empty.retain_difference(&Default::default());
825 assert!(empty.is_empty());
826
827 let targets = MultiProofTargets::accounts((0..10).map(B256::with_last_byte));
828
829 let mut diffed = targets.clone();
830 diffed.retain_difference(&MultiProofTargets::account(B256::with_last_byte(11)));
831 assert_eq!(diffed, targets);
832
833 diffed.retain_difference(&MultiProofTargets::accounts((0..5).map(B256::with_last_byte)));
834 assert_eq!(diffed, MultiProofTargets::accounts((5..10).map(B256::with_last_byte)));
835
836 diffed.retain_difference(&targets);
837 assert!(diffed.is_empty());
838
839 let mut targets = MultiProofTargets::default();
840 let (account1, account2, account3) =
841 (1..=3).map(B256::with_last_byte).collect_tuple().unwrap();
842 let account2_slots = (1..5).map(B256::with_last_byte).collect::<B256Set>();
843 targets.insert(account1, B256Set::from_iter([B256::with_last_byte(1)]));
844 targets.insert(account2, account2_slots.clone());
845 targets.insert(account3, B256Set::from_iter([B256::with_last_byte(1)]));
846
847 let mut diffed = targets.clone();
848 diffed.retain_difference(&MultiProofTargets::accounts((1..=3).map(B256::with_last_byte)));
849 assert_eq!(diffed, targets);
850
851 let mut account2_slots_expected_len = account2_slots.len();
853 for slot in account2_slots.iter().skip(1) {
854 diffed.retain_difference(&MultiProofTargets::account_with_slots(account2, [*slot]));
855 account2_slots_expected_len -= 1;
856 assert_eq!(
857 diffed.get(&account2).map(|slots| slots.len()),
858 Some(account2_slots_expected_len)
859 );
860 }
861
862 diffed.retain_difference(&targets);
863 assert!(diffed.is_empty());
864 }
865
866 #[test]
867 fn test_multi_proof_retain_difference_no_overlap() {
868 let mut targets = MultiProofTargets::default();
869
870 let (addr1, addr2) = (B256::random(), B256::random());
872 let (slot1, slot2) = (B256::random(), B256::random());
873 targets.insert(addr1, vec![slot1].into_iter().collect());
874 targets.insert(addr2, vec![slot2].into_iter().collect());
875
876 let mut retained = targets.clone();
877 retained.retain_difference(&Default::default());
878 assert_eq!(retained, targets);
879
880 let mut other_targets = MultiProofTargets::default();
882 let addr3 = B256::random();
883 let slot3 = B256::random();
884 other_targets.insert(addr3, B256Set::from_iter([slot3]));
885
886 let mut retained = targets.clone();
889 retained.retain_difference(&other_targets);
890 assert_eq!(retained, targets);
891 }
892
893 #[test]
894 fn test_get_prefetch_proof_targets_remove_subset() {
895 let mut targets = MultiProofTargets::default();
897 let (addr1, addr2) = (B256::random(), B256::random());
898 let (slot1, slot2) = (B256::random(), B256::random());
899 targets.insert(addr1, B256Set::from_iter([slot1]));
900 targets.insert(addr2, B256Set::from_iter([slot2]));
901
902 let other_targets = MultiProofTargets::account_with_slots(addr1, [slot1]);
904
905 let mut retained = targets.clone();
906 retained.retain_difference(&other_targets);
907
908 assert_eq!(retained.len(), 1);
910 assert!(!retained.contains_key(&addr1));
911 assert!(retained.contains_key(&addr2));
912
913 let slot3 = B256::random();
915 targets.get_mut(&addr1).unwrap().insert(slot3);
916
917 let mut retained = targets.clone();
918 retained.retain_difference(&other_targets);
919
920 assert_eq!(retained.len(), 2);
923 assert!(retained.contains_key(&addr1));
924 assert_eq!(retained.get(&addr1), Some(&B256Set::from_iter([slot3])));
925 assert!(retained.contains_key(&addr2));
926 assert_eq!(retained.get(&addr2), Some(&B256Set::from_iter([slot2])));
927 }
928}