1use crate::{BranchNodeMasksMap, 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},
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 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_masks: BranchNodeMasksMap,
179 pub storages: B256Map<StorageMultiProof>,
181}
182
183impl MultiProof {
184 pub fn is_empty(&self) -> bool {
186 self.account_subtree.is_empty() &&
187 self.branch_node_masks.is_empty() &&
188 self.storages.is_empty()
189 }
190
191 pub fn account_proof_nodes(&self, path: &Nibbles) -> Vec<(Nibbles, Bytes)> {
193 self.account_subtree.matching_nodes_sorted(path)
194 }
195
196 pub fn storage_proof_nodes(
198 &self,
199 hashed_address: B256,
200 slots: impl IntoIterator<Item = B256>,
201 ) -> Vec<(B256, Vec<(Nibbles, Bytes)>)> {
202 self.storages
203 .get(&hashed_address)
204 .map(|storage_mp| {
205 slots
206 .into_iter()
207 .map(|slot| {
208 let nibbles = Nibbles::unpack(slot);
209 (slot, storage_mp.subtree.matching_nodes_sorted(&nibbles))
210 })
211 .collect()
212 })
213 .unwrap_or_default()
214 }
215
216 pub fn account_proof(
218 &self,
219 address: Address,
220 slots: &[B256],
221 ) -> Result<AccountProof, alloy_rlp::Error> {
222 let hashed_address = keccak256(address);
223 let nibbles = Nibbles::unpack(hashed_address);
224
225 let proof = self
227 .account_proof_nodes(&nibbles)
228 .into_iter()
229 .map(|(_, node)| node)
230 .collect::<Vec<_>>();
231
232 let info = 'info: {
235 if let Some(last) = proof.last() &&
236 let TrieNode::Leaf(leaf) = TrieNode::decode(&mut &last[..])? &&
237 nibbles.ends_with(&leaf.key)
238 {
239 let account = TrieAccount::decode(&mut &leaf.value[..])?;
240 break 'info Some(Account {
241 balance: account.balance,
242 nonce: account.nonce,
243 bytecode_hash: (account.code_hash != KECCAK_EMPTY).then_some(account.code_hash),
244 })
245 }
246 None
247 };
248
249 let storage_multiproof = self.storages.get(&hashed_address);
251 let storage_root = storage_multiproof.map(|m| m.root).unwrap_or(EMPTY_ROOT_HASH);
252 let mut storage_proofs = Vec::with_capacity(slots.len());
253 for slot in slots {
254 let proof = if let Some(multiproof) = &storage_multiproof {
255 multiproof.storage_proof(*slot)?
256 } else {
257 StorageProof::new(*slot)
258 };
259 storage_proofs.push(proof);
260 }
261 Ok(AccountProof { address, info, proof, storage_root, storage_proofs })
262 }
263
264 pub fn extend(&mut self, other: Self) {
267 self.account_subtree.extend_from(other.account_subtree);
268 self.branch_node_masks.extend(other.branch_node_masks);
269
270 for (hashed_address, storage) in other.storages {
271 match self.storages.entry(hashed_address) {
272 hash_map::Entry::Occupied(mut entry) => {
273 debug_assert_eq!(entry.get().root, storage.root);
274 let entry = entry.get_mut();
275 entry.subtree.extend_from(storage.subtree);
276 entry.branch_node_masks.extend(storage.branch_node_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_masks: BranchNodeMasksMap,
303 pub storages: B256Map<DecodedStorageMultiProof>,
305}
306
307impl DecodedMultiProof {
308 pub fn is_empty(&self) -> bool {
310 self.account_subtree.is_empty() &&
311 self.branch_node_masks.is_empty() &&
312 self.storages.is_empty()
313 }
314
315 pub fn account_proof_nodes(&self, path: &Nibbles) -> Vec<(Nibbles, TrieNode)> {
317 self.account_subtree.matching_nodes_sorted(path)
318 }
319
320 pub fn storage_proof_nodes(
322 &self,
323 hashed_address: B256,
324 slots: impl IntoIterator<Item = B256>,
325 ) -> Vec<(B256, Vec<(Nibbles, TrieNode)>)> {
326 self.storages
327 .get(&hashed_address)
328 .map(|storage_mp| {
329 slots
330 .into_iter()
331 .map(|slot| {
332 let nibbles = Nibbles::unpack(slot);
333 (slot, storage_mp.subtree.matching_nodes_sorted(&nibbles))
334 })
335 .collect()
336 })
337 .unwrap_or_default()
338 }
339
340 pub fn account_proof(
342 &self,
343 address: Address,
344 slots: &[B256],
345 ) -> Result<DecodedAccountProof, alloy_rlp::Error> {
346 let hashed_address = keccak256(address);
347 let nibbles = Nibbles::unpack(hashed_address);
348
349 let proof = self
351 .account_proof_nodes(&nibbles)
352 .into_iter()
353 .map(|(_, node)| node)
354 .collect::<Vec<_>>();
355
356 let info = 'info: {
359 if let Some(TrieNode::Leaf(leaf)) = proof.last() &&
360 nibbles.ends_with(&leaf.key)
361 {
362 let account = TrieAccount::decode(&mut &leaf.value[..])?;
363 break 'info Some(Account {
364 balance: account.balance,
365 nonce: account.nonce,
366 bytecode_hash: (account.code_hash != KECCAK_EMPTY).then_some(account.code_hash),
367 })
368 }
369 None
370 };
371
372 let storage_multiproof = self.storages.get(&hashed_address);
374 let storage_root = storage_multiproof.map(|m| m.root).unwrap_or(EMPTY_ROOT_HASH);
375 let mut storage_proofs = Vec::with_capacity(slots.len());
376 for slot in slots {
377 let proof = if let Some(multiproof) = &storage_multiproof {
378 multiproof.storage_proof(*slot)?
379 } else {
380 DecodedStorageProof::new(*slot)
381 };
382 storage_proofs.push(proof);
383 }
384 Ok(DecodedAccountProof { address, info, proof, storage_root, storage_proofs })
385 }
386
387 pub fn extend(&mut self, other: Self) {
390 self.account_subtree.extend_from(other.account_subtree);
391 self.branch_node_masks.extend(other.branch_node_masks);
392
393 for (hashed_address, storage) in other.storages {
394 match self.storages.entry(hashed_address) {
395 hash_map::Entry::Occupied(mut entry) => {
396 debug_assert_eq!(entry.get().root, storage.root);
397 let entry = entry.get_mut();
398 entry.subtree.extend_from(storage.subtree);
399 entry.branch_node_masks.extend(storage.branch_node_masks);
400 }
401 hash_map::Entry::Vacant(entry) => {
402 entry.insert(storage);
403 }
404 }
405 }
406 }
407
408 pub fn from_storage_proof(
410 hashed_address: B256,
411 storage_proof: DecodedStorageMultiProof,
412 ) -> Self {
413 Self {
414 storages: B256Map::from_iter([(hashed_address, storage_proof)]),
415 ..Default::default()
416 }
417 }
418}
419
420impl TryFrom<MultiProof> for DecodedMultiProof {
421 type Error = alloy_rlp::Error;
422
423 fn try_from(multi_proof: MultiProof) -> Result<Self, Self::Error> {
424 let account_subtree = DecodedProofNodes::try_from(multi_proof.account_subtree)?;
425 let storages = multi_proof
426 .storages
427 .into_iter()
428 .map(|(address, storage)| Ok((address, storage.try_into()?)))
429 .collect::<Result<B256Map<_>, alloy_rlp::Error>>()?;
430 Ok(Self { account_subtree, branch_node_masks: multi_proof.branch_node_masks, storages })
431 }
432}
433
434#[derive(Clone, Debug, PartialEq, Eq)]
436pub struct StorageMultiProof {
437 pub root: B256,
439 pub subtree: ProofNodes,
441 pub branch_node_masks: BranchNodeMasksMap,
444}
445
446impl StorageMultiProof {
447 pub fn empty() -> Self {
449 Self {
450 root: EMPTY_ROOT_HASH,
451 subtree: ProofNodes::from_iter([(
452 Nibbles::default(),
453 Bytes::from([EMPTY_STRING_CODE]),
454 )]),
455 branch_node_masks: BranchNodeMasksMap::default(),
456 }
457 }
458
459 pub fn storage_proof(&self, slot: B256) -> Result<StorageProof, alloy_rlp::Error> {
461 let nibbles = Nibbles::unpack(keccak256(slot));
462
463 let proof = self
465 .subtree
466 .matching_nodes_iter(&nibbles)
467 .sorted_by(|a, b| a.0.cmp(b.0))
468 .map(|(_, node)| node.clone())
469 .collect::<Vec<_>>();
470
471 let value = 'value: {
474 if let Some(last) = proof.last() &&
475 let TrieNode::Leaf(leaf) = TrieNode::decode(&mut &last[..])? &&
476 nibbles.ends_with(&leaf.key)
477 {
478 break 'value U256::decode(&mut &leaf.value[..])?
479 }
480 U256::ZERO
481 };
482
483 Ok(StorageProof { key: slot, nibbles, value, proof })
484 }
485}
486
487#[derive(Clone, Debug, PartialEq, Eq)]
489pub struct DecodedStorageMultiProof {
490 pub root: B256,
492 pub subtree: DecodedProofNodes,
494 pub branch_node_masks: BranchNodeMasksMap,
497}
498
499impl DecodedStorageMultiProof {
500 pub fn empty() -> Self {
502 Self {
503 root: EMPTY_ROOT_HASH,
504 subtree: DecodedProofNodes::from_iter([(Nibbles::default(), TrieNode::EmptyRoot)]),
505 branch_node_masks: BranchNodeMasksMap::default(),
506 }
507 }
508
509 pub fn storage_proof(&self, slot: B256) -> Result<DecodedStorageProof, alloy_rlp::Error> {
511 let nibbles = Nibbles::unpack(keccak256(slot));
512
513 let proof = self
515 .subtree
516 .matching_nodes_iter(&nibbles)
517 .sorted_by(|a, b| a.0.cmp(b.0))
518 .map(|(_, node)| node.clone())
519 .collect::<Vec<_>>();
520
521 let value = 'value: {
524 if let Some(TrieNode::Leaf(leaf)) = proof.last() &&
525 nibbles.ends_with(&leaf.key)
526 {
527 break 'value U256::decode(&mut &leaf.value[..])?
528 }
529 U256::ZERO
530 };
531
532 Ok(DecodedStorageProof { key: slot, nibbles, value, proof })
533 }
534}
535
536impl TryFrom<StorageMultiProof> for DecodedStorageMultiProof {
537 type Error = alloy_rlp::Error;
538
539 fn try_from(multi_proof: StorageMultiProof) -> Result<Self, Self::Error> {
540 let subtree = DecodedProofNodes::try_from(multi_proof.subtree)?;
541 Ok(Self {
542 root: multi_proof.root,
543 subtree,
544 branch_node_masks: multi_proof.branch_node_masks,
545 })
546 }
547}
548
549#[derive(Clone, PartialEq, Eq, Debug)]
551#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
552#[cfg_attr(any(test, feature = "serde"), serde(rename_all = "camelCase"))]
553pub struct AccountProof {
554 pub address: Address,
556 pub info: Option<Account>,
558 pub proof: Vec<Bytes>,
561 pub storage_root: B256,
563 pub storage_proofs: Vec<StorageProof>,
565}
566
567#[cfg(feature = "eip1186")]
568impl AccountProof {
569 pub fn into_eip1186_response(
571 self,
572 slots: Vec<alloy_serde::JsonStorageKey>,
573 ) -> alloy_rpc_types_eth::EIP1186AccountProofResponse {
574 let info = self.info.unwrap_or_default();
575 alloy_rpc_types_eth::EIP1186AccountProofResponse {
576 address: self.address,
577 balance: info.balance,
578 code_hash: info.get_bytecode_hash(),
579 nonce: info.nonce,
580 storage_hash: self.storage_root,
581 account_proof: self.proof,
582 storage_proof: self
583 .storage_proofs
584 .into_iter()
585 .filter_map(|proof| {
586 let input_slot = slots.iter().find(|s| s.as_b256() == proof.key)?;
587 Some(proof.into_eip1186_proof(*input_slot))
588 })
589 .collect(),
590 }
591 }
592
593 pub fn from_eip1186_proof(proof: alloy_rpc_types_eth::EIP1186AccountProofResponse) -> Self {
599 let alloy_rpc_types_eth::EIP1186AccountProofResponse {
600 nonce,
601 address,
602 balance,
603 code_hash,
604 storage_hash,
605 account_proof,
606 storage_proof,
607 ..
608 } = proof;
609 let storage_proofs = storage_proof.into_iter().map(Into::into).collect();
610
611 let (storage_root, info) = if nonce == 0 &&
612 balance.is_zero() &&
613 storage_hash.is_zero() &&
614 code_hash == KECCAK_EMPTY
615 {
616 (EMPTY_ROOT_HASH, None)
619 } else {
620 (storage_hash, Some(Account { nonce, balance, bytecode_hash: code_hash.into() }))
621 };
622
623 Self { address, info, proof: account_proof, storage_root, storage_proofs }
624 }
625}
626
627#[cfg(feature = "eip1186")]
628impl From<alloy_rpc_types_eth::EIP1186AccountProofResponse> for AccountProof {
629 fn from(proof: alloy_rpc_types_eth::EIP1186AccountProofResponse) -> Self {
630 Self::from_eip1186_proof(proof)
631 }
632}
633
634impl Default for AccountProof {
635 fn default() -> Self {
636 Self::new(Address::default())
637 }
638}
639
640impl AccountProof {
641 pub const fn new(address: Address) -> Self {
643 Self {
644 address,
645 info: None,
646 proof: Vec::new(),
647 storage_root: EMPTY_ROOT_HASH,
648 storage_proofs: Vec::new(),
649 }
650 }
651
652 pub fn verify(&self, root: B256) -> Result<(), ProofVerificationError> {
654 for storage_proof in &self.storage_proofs {
656 storage_proof.verify(self.storage_root)?;
657 }
658
659 let expected = if self.info.is_none() && self.storage_root == EMPTY_ROOT_HASH {
661 None
662 } else {
663 Some(alloy_rlp::encode(
664 self.info.unwrap_or_default().into_trie_account(self.storage_root),
665 ))
666 };
667 let nibbles = Nibbles::unpack(keccak256(self.address));
668 verify_proof(root, nibbles, expected, &self.proof)
669 }
670}
671
672#[derive(Clone, PartialEq, Eq, Debug)]
674pub struct DecodedAccountProof {
675 pub address: Address,
677 pub info: Option<Account>,
679 pub proof: Vec<TrieNode>,
682 pub storage_root: B256,
684 pub storage_proofs: Vec<DecodedStorageProof>,
686}
687
688impl Default for DecodedAccountProof {
689 fn default() -> Self {
690 Self::new(Address::default())
691 }
692}
693
694impl DecodedAccountProof {
695 pub const fn new(address: Address) -> Self {
697 Self {
698 address,
699 info: None,
700 proof: Vec::new(),
701 storage_root: EMPTY_ROOT_HASH,
702 storage_proofs: Vec::new(),
703 }
704 }
705}
706
707#[derive(Clone, PartialEq, Eq, Default, Debug)]
709#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
710pub struct StorageProof {
711 pub key: B256,
713 pub nibbles: Nibbles,
715 pub value: U256,
717 pub proof: Vec<Bytes>,
720}
721
722impl StorageProof {
723 pub fn new(key: B256) -> Self {
725 let nibbles = Nibbles::unpack(keccak256(key));
726 Self { key, nibbles, ..Default::default() }
727 }
728
729 pub fn new_with_hashed(key: B256, hashed_key: B256) -> Self {
731 Self { key, nibbles: Nibbles::unpack(hashed_key), ..Default::default() }
732 }
733
734 pub fn new_with_nibbles(key: B256, nibbles: Nibbles) -> Self {
736 Self { key, nibbles, ..Default::default() }
737 }
738
739 pub fn with_proof(mut self, proof: Vec<Bytes>) -> Self {
741 self.proof = proof;
742 self
743 }
744
745 pub fn verify(&self, root: B256) -> Result<(), ProofVerificationError> {
747 let expected =
748 if self.value.is_zero() { None } else { Some(encode_fixed_size(&self.value).to_vec()) };
749 verify_proof(root, self.nibbles, expected, &self.proof)
750 }
751}
752
753#[cfg(feature = "eip1186")]
754impl StorageProof {
755 pub fn into_eip1186_proof(
757 self,
758 slot: alloy_serde::JsonStorageKey,
759 ) -> alloy_rpc_types_eth::EIP1186StorageProof {
760 alloy_rpc_types_eth::EIP1186StorageProof { key: slot, value: self.value, proof: self.proof }
761 }
762
763 pub fn from_eip1186_proof(storage_proof: alloy_rpc_types_eth::EIP1186StorageProof) -> Self {
768 Self {
769 value: storage_proof.value,
770 proof: storage_proof.proof,
771 ..Self::new(storage_proof.key.as_b256())
772 }
773 }
774}
775
776#[cfg(feature = "eip1186")]
777impl From<alloy_rpc_types_eth::EIP1186StorageProof> for StorageProof {
778 fn from(proof: alloy_rpc_types_eth::EIP1186StorageProof) -> Self {
779 Self::from_eip1186_proof(proof)
780 }
781}
782
783#[derive(Clone, PartialEq, Eq, Default, Debug)]
785pub struct DecodedStorageProof {
786 pub key: B256,
788 pub nibbles: Nibbles,
790 pub value: U256,
792 pub proof: Vec<TrieNode>,
795}
796
797impl DecodedStorageProof {
798 pub fn new(key: B256) -> Self {
800 let nibbles = Nibbles::unpack(keccak256(key));
801 Self { key, nibbles, ..Default::default() }
802 }
803
804 pub fn new_with_hashed(key: B256, hashed_key: B256) -> Self {
806 Self { key, nibbles: Nibbles::unpack(hashed_key), ..Default::default() }
807 }
808
809 pub fn new_with_nibbles(key: B256, nibbles: Nibbles) -> Self {
811 Self { key, nibbles, ..Default::default() }
812 }
813
814 pub fn with_proof(mut self, proof: Vec<TrieNode>) -> Self {
816 self.proof = proof;
817 self
818 }
819}
820
821#[cfg(any(test, feature = "test-utils"))]
824pub mod triehash {
825 use alloy_primitives::{keccak256, B256};
826 use alloy_rlp::RlpEncodable;
827 use hash_db::Hasher;
828 use plain_hasher::PlainHasher;
829
830 #[derive(Default, Debug, Clone, PartialEq, Eq, RlpEncodable)]
832 #[non_exhaustive]
833 pub struct KeccakHasher;
834
835 #[cfg(any(test, feature = "test-utils"))]
836 impl Hasher for KeccakHasher {
837 type Out = B256;
838 type StdHasher = PlainHasher;
839
840 const LENGTH: usize = 32;
841
842 fn hash(x: &[u8]) -> Self::Out {
843 keccak256(x)
844 }
845 }
846}
847
848#[cfg(test)]
849mod tests {
850 use super::*;
851
852 #[test]
853 fn test_multiproof_extend_account_proofs() {
854 let mut proof1 = MultiProof::default();
855 let mut proof2 = MultiProof::default();
856
857 let addr1 = B256::random();
858 let addr2 = B256::random();
859
860 proof1.account_subtree.insert(
861 Nibbles::unpack(addr1),
862 alloy_rlp::encode_fixed_size(&U256::from(42)).to_vec().into(),
863 );
864 proof2.account_subtree.insert(
865 Nibbles::unpack(addr2),
866 alloy_rlp::encode_fixed_size(&U256::from(43)).to_vec().into(),
867 );
868
869 proof1.extend(proof2);
870
871 assert!(proof1.account_subtree.contains_key(&Nibbles::unpack(addr1)));
872 assert!(proof1.account_subtree.contains_key(&Nibbles::unpack(addr2)));
873 }
874
875 #[test]
876 fn test_multiproof_extend_storage_proofs() {
877 let mut proof1 = MultiProof::default();
878 let mut proof2 = MultiProof::default();
879
880 let addr = B256::random();
881 let root = B256::random();
882
883 let mut subtree1 = ProofNodes::default();
884 subtree1.insert(
885 Nibbles::from_nibbles(vec![0]),
886 alloy_rlp::encode_fixed_size(&U256::from(42)).to_vec().into(),
887 );
888 proof1.storages.insert(
889 addr,
890 StorageMultiProof {
891 root,
892 subtree: subtree1,
893 branch_node_masks: BranchNodeMasksMap::default(),
894 },
895 );
896
897 let mut subtree2 = ProofNodes::default();
898 subtree2.insert(
899 Nibbles::from_nibbles(vec![1]),
900 alloy_rlp::encode_fixed_size(&U256::from(43)).to_vec().into(),
901 );
902 proof2.storages.insert(
903 addr,
904 StorageMultiProof {
905 root,
906 subtree: subtree2,
907 branch_node_masks: BranchNodeMasksMap::default(),
908 },
909 );
910
911 proof1.extend(proof2);
912
913 let storage = proof1.storages.get(&addr).unwrap();
914 assert_eq!(storage.root, root);
915 assert!(storage.subtree.contains_key(&Nibbles::from_nibbles(vec![0])));
916 assert!(storage.subtree.contains_key(&Nibbles::from_nibbles(vec![1])));
917 }
918
919 #[test]
920 fn test_multi_proof_retain_difference() {
921 let mut empty = MultiProofTargets::default();
922 empty.retain_difference(&Default::default());
923 assert!(empty.is_empty());
924
925 let targets = MultiProofTargets::accounts((0..10).map(B256::with_last_byte));
926
927 let mut diffed = targets.clone();
928 diffed.retain_difference(&MultiProofTargets::account(B256::with_last_byte(11)));
929 assert_eq!(diffed, targets);
930
931 diffed.retain_difference(&MultiProofTargets::accounts((0..5).map(B256::with_last_byte)));
932 assert_eq!(diffed, MultiProofTargets::accounts((5..10).map(B256::with_last_byte)));
933
934 diffed.retain_difference(&targets);
935 assert!(diffed.is_empty());
936
937 let mut targets = MultiProofTargets::default();
938 let (account1, account2, account3) =
939 (1..=3).map(B256::with_last_byte).collect_tuple().unwrap();
940 let account2_slots = (1..5).map(B256::with_last_byte).collect::<B256Set>();
941 targets.insert(account1, B256Set::from_iter([B256::with_last_byte(1)]));
942 targets.insert(account2, account2_slots.clone());
943 targets.insert(account3, B256Set::from_iter([B256::with_last_byte(1)]));
944
945 let mut diffed = targets.clone();
946 diffed.retain_difference(&MultiProofTargets::accounts((1..=3).map(B256::with_last_byte)));
947 assert_eq!(diffed, targets);
948
949 let mut account2_slots_expected_len = account2_slots.len();
951 for slot in account2_slots.iter().skip(1) {
952 diffed.retain_difference(&MultiProofTargets::account_with_slots(account2, [*slot]));
953 account2_slots_expected_len -= 1;
954 assert_eq!(
955 diffed.get(&account2).map(|slots| slots.len()),
956 Some(account2_slots_expected_len)
957 );
958 }
959
960 diffed.retain_difference(&targets);
961 assert!(diffed.is_empty());
962 }
963
964 #[test]
965 fn test_multi_proof_retain_difference_no_overlap() {
966 let mut targets = MultiProofTargets::default();
967
968 let (addr1, addr2) = (B256::random(), B256::random());
970 let (slot1, slot2) = (B256::random(), B256::random());
971 targets.insert(addr1, std::iter::once(slot1).collect());
972 targets.insert(addr2, std::iter::once(slot2).collect());
973
974 let mut retained = targets.clone();
975 retained.retain_difference(&Default::default());
976 assert_eq!(retained, targets);
977
978 let mut other_targets = MultiProofTargets::default();
980 let addr3 = B256::random();
981 let slot3 = B256::random();
982 other_targets.insert(addr3, B256Set::from_iter([slot3]));
983
984 let mut retained = targets.clone();
987 retained.retain_difference(&other_targets);
988 assert_eq!(retained, targets);
989 }
990
991 #[test]
992 fn test_get_prefetch_proof_targets_remove_subset() {
993 let mut targets = MultiProofTargets::default();
995 let (addr1, addr2) = (B256::random(), B256::random());
996 let (slot1, slot2) = (B256::random(), B256::random());
997 targets.insert(addr1, B256Set::from_iter([slot1]));
998 targets.insert(addr2, B256Set::from_iter([slot2]));
999
1000 let other_targets = MultiProofTargets::account_with_slots(addr1, [slot1]);
1002
1003 let mut retained = targets.clone();
1004 retained.retain_difference(&other_targets);
1005
1006 assert_eq!(retained.len(), 1);
1008 assert!(!retained.contains_key(&addr1));
1009 assert!(retained.contains_key(&addr2));
1010
1011 let slot3 = B256::random();
1013 targets.get_mut(&addr1).unwrap().insert(slot3);
1014
1015 let mut retained = targets.clone();
1016 retained.retain_difference(&other_targets);
1017
1018 assert_eq!(retained.len(), 2);
1021 assert!(retained.contains_key(&addr1));
1022 assert_eq!(retained.get(&addr1), Some(&B256Set::from_iter([slot3])));
1023 assert!(retained.contains_key(&addr2));
1024 assert_eq!(retained.get(&addr2), Some(&B256Set::from_iter([slot2])));
1025 }
1026
1027 #[test]
1028 #[cfg(feature = "eip1186")]
1029 fn eip_1186_roundtrip() {
1030 let mut acc = AccountProof {
1031 address: Address::random(),
1032 info: Some(
1033 Account { nonce: 100, balance: U256::ZERO, bytecode_hash: Some(KECCAK_EMPTY) },
1035 ),
1036 proof: vec![],
1037 storage_root: B256::ZERO,
1038 storage_proofs: vec![],
1039 };
1040
1041 let rpc_proof = acc.clone().into_eip1186_response(Vec::new());
1042 let inverse: AccountProof = rpc_proof.into();
1043 assert_eq!(acc, inverse);
1044
1045 acc.info.as_mut().unwrap().nonce = 0;
1047 let rpc_proof = acc.clone().into_eip1186_response(Vec::new());
1048 let inverse: AccountProof = rpc_proof.into();
1049 acc.info.take();
1050 acc.storage_root = EMPTY_ROOT_HASH;
1051 assert_eq!(acc, inverse);
1052 }
1053
1054 #[test]
1055 fn test_multiproof_targets_chunking_length() {
1056 let mut targets = MultiProofTargets::default();
1057 targets.insert(B256::with_last_byte(1), B256Set::default());
1058 targets.insert(
1059 B256::with_last_byte(2),
1060 B256Set::from_iter([B256::with_last_byte(10), B256::with_last_byte(20)]),
1061 );
1062 targets.insert(
1063 B256::with_last_byte(3),
1064 B256Set::from_iter([
1065 B256::with_last_byte(30),
1066 B256::with_last_byte(31),
1067 B256::with_last_byte(32),
1068 ]),
1069 );
1070
1071 let chunking_length = targets.chunking_length();
1072 for size in 1..=targets.clone().chunks(1).count() {
1073 let chunk_count = targets.clone().chunks(size).count();
1074 let expected_count = chunking_length.div_ceil(size);
1075 assert_eq!(
1076 chunk_count, expected_count,
1077 "chunking_length: {}, size: {}",
1078 chunking_length, size
1079 );
1080 }
1081 }
1082}