1use crate::{BranchNodeMasksMap, Nibbles, ProofTrieNodeV2, 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_unstable();
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 let reserve = if self.storages.is_empty() {
271 other.storages.len()
272 } else {
273 other.storages.len().div_ceil(2)
274 };
275 self.storages.reserve(reserve);
276 for (hashed_address, storage) in other.storages {
277 match self.storages.entry(hashed_address) {
278 hash_map::Entry::Occupied(mut entry) => {
279 debug_assert_eq!(entry.get().root, storage.root);
280 let entry = entry.get_mut();
281 entry.subtree.extend_from(storage.subtree);
282 entry.branch_node_masks.extend(storage.branch_node_masks);
283 }
284 hash_map::Entry::Vacant(entry) => {
285 entry.insert(storage);
286 }
287 }
288 }
289 }
290
291 pub fn from_storage_proof(hashed_address: B256, storage_proof: StorageMultiProof) -> Self {
293 Self {
294 storages: B256Map::from_iter([(hashed_address, storage_proof)]),
295 ..Default::default()
296 }
297 }
298}
299
300#[derive(Clone, Default, Debug, PartialEq, Eq)]
303pub struct DecodedMultiProof {
304 pub account_subtree: DecodedProofNodes,
306 pub branch_node_masks: BranchNodeMasksMap,
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_masks.is_empty() &&
318 self.storages.is_empty()
319 }
320
321 pub fn account_proof_nodes(&self, path: &Nibbles) -> Vec<(Nibbles, TrieNode)> {
323 self.account_subtree.matching_nodes_sorted(path)
324 }
325
326 pub fn storage_proof_nodes(
328 &self,
329 hashed_address: B256,
330 slots: impl IntoIterator<Item = B256>,
331 ) -> Vec<(B256, Vec<(Nibbles, TrieNode)>)> {
332 self.storages
333 .get(&hashed_address)
334 .map(|storage_mp| {
335 slots
336 .into_iter()
337 .map(|slot| {
338 let nibbles = Nibbles::unpack(slot);
339 (slot, storage_mp.subtree.matching_nodes_sorted(&nibbles))
340 })
341 .collect()
342 })
343 .unwrap_or_default()
344 }
345
346 pub fn account_proof(
348 &self,
349 address: Address,
350 slots: &[B256],
351 ) -> Result<DecodedAccountProof, alloy_rlp::Error> {
352 let hashed_address = keccak256(address);
353 let nibbles = Nibbles::unpack(hashed_address);
354
355 let proof = self
357 .account_proof_nodes(&nibbles)
358 .into_iter()
359 .map(|(_, node)| node)
360 .collect::<Vec<_>>();
361
362 let info = 'info: {
365 if let Some(TrieNode::Leaf(leaf)) = proof.last() &&
366 nibbles.ends_with(&leaf.key)
367 {
368 let account = TrieAccount::decode(&mut &leaf.value[..])?;
369 break 'info Some(Account {
370 balance: account.balance,
371 nonce: account.nonce,
372 bytecode_hash: (account.code_hash != KECCAK_EMPTY).then_some(account.code_hash),
373 })
374 }
375 None
376 };
377
378 let storage_multiproof = self.storages.get(&hashed_address);
380 let storage_root = storage_multiproof.map(|m| m.root).unwrap_or(EMPTY_ROOT_HASH);
381 let mut storage_proofs = Vec::with_capacity(slots.len());
382 for slot in slots {
383 let proof = if let Some(multiproof) = &storage_multiproof {
384 multiproof.storage_proof(*slot)?
385 } else {
386 DecodedStorageProof::new(*slot)
387 };
388 storage_proofs.push(proof);
389 }
390 Ok(DecodedAccountProof { address, info, proof, storage_root, storage_proofs })
391 }
392
393 pub fn extend(&mut self, other: Self) {
396 self.account_subtree.extend_from(other.account_subtree);
397 self.branch_node_masks.extend(other.branch_node_masks);
398
399 let reserve = if self.storages.is_empty() {
400 other.storages.len()
401 } else {
402 other.storages.len().div_ceil(2)
403 };
404 self.storages.reserve(reserve);
405 for (hashed_address, storage) in other.storages {
406 match self.storages.entry(hashed_address) {
407 hash_map::Entry::Occupied(mut entry) => {
408 debug_assert_eq!(entry.get().root, storage.root);
409 let entry = entry.get_mut();
410 entry.subtree.extend_from(storage.subtree);
411 entry.branch_node_masks.extend(storage.branch_node_masks);
412 }
413 hash_map::Entry::Vacant(entry) => {
414 entry.insert(storage);
415 }
416 }
417 }
418 }
419
420 pub fn from_storage_proof(
422 hashed_address: B256,
423 storage_proof: DecodedStorageMultiProof,
424 ) -> Self {
425 Self {
426 storages: B256Map::from_iter([(hashed_address, storage_proof)]),
427 ..Default::default()
428 }
429 }
430}
431
432impl TryFrom<MultiProof> for DecodedMultiProof {
433 type Error = alloy_rlp::Error;
434
435 fn try_from(multi_proof: MultiProof) -> Result<Self, Self::Error> {
436 let account_subtree = DecodedProofNodes::try_from(multi_proof.account_subtree)?;
437 let storages = multi_proof
438 .storages
439 .into_iter()
440 .map(|(address, storage)| Ok((address, storage.try_into()?)))
441 .collect::<Result<B256Map<_>, alloy_rlp::Error>>()?;
442 Ok(Self { account_subtree, branch_node_masks: multi_proof.branch_node_masks, storages })
443 }
444}
445
446#[derive(Clone, Debug, PartialEq, Eq, Default)]
449pub struct DecodedMultiProofV2 {
450 pub account_proofs: Vec<ProofTrieNodeV2>,
452 pub storage_proofs: B256Map<Vec<ProofTrieNodeV2>>,
454}
455
456impl DecodedMultiProofV2 {
457 pub fn is_empty(&self) -> bool {
459 self.account_proofs.is_empty() && self.storage_proofs.is_empty()
460 }
461
462 pub fn extend(&mut self, other: Self) {
466 self.account_proofs.extend(other.account_proofs);
467 for (hashed_address, other_storage_proofs) in other.storage_proofs {
468 match self.storage_proofs.entry(hashed_address) {
469 hash_map::Entry::Vacant(entry) => {
470 entry.insert(other_storage_proofs);
471 }
472 hash_map::Entry::Occupied(mut entry) => {
473 entry.get_mut().extend(other_storage_proofs);
474 }
475 }
476 }
477 }
478}
479
480impl From<DecodedMultiProof> for DecodedMultiProofV2 {
481 fn from(proof: DecodedMultiProof) -> Self {
482 let account_proofs =
483 decoded_proof_nodes_to_v2(proof.account_subtree, &proof.branch_node_masks);
484 let storage_proofs = proof
485 .storages
486 .into_iter()
487 .map(|(address, storage)| {
488 (address, decoded_proof_nodes_to_v2(storage.subtree, &storage.branch_node_masks))
489 })
490 .collect();
491 Self { account_proofs, storage_proofs }
492 }
493}
494
495fn decoded_proof_nodes_to_v2(
498 nodes: DecodedProofNodes,
499 masks: &BranchNodeMasksMap,
500) -> Vec<ProofTrieNodeV2> {
501 let mut sorted: Vec<_> = nodes.into_inner().into_iter().collect();
502 sorted.sort_unstable_by(|a, b| crate::depth_first_cmp(&a.0, &b.0));
503 ProofTrieNodeV2::from_sorted_trie_nodes(
504 sorted.into_iter().map(|(path, node)| (path, node, masks.get(&path).copied())),
505 )
506}
507
508#[derive(Clone, Debug, PartialEq, Eq)]
510pub struct StorageMultiProof {
511 pub root: B256,
513 pub subtree: ProofNodes,
515 pub branch_node_masks: BranchNodeMasksMap,
518}
519
520impl StorageMultiProof {
521 pub fn empty() -> Self {
523 Self {
524 root: EMPTY_ROOT_HASH,
525 subtree: ProofNodes::from_iter([(
526 Nibbles::default(),
527 Bytes::from([EMPTY_STRING_CODE]),
528 )]),
529 branch_node_masks: BranchNodeMasksMap::default(),
530 }
531 }
532
533 pub fn storage_proof(&self, slot: B256) -> Result<StorageProof, alloy_rlp::Error> {
535 let nibbles = Nibbles::unpack(keccak256(slot));
536
537 let proof = self
539 .subtree
540 .matching_nodes_iter(&nibbles)
541 .sorted_by(|a, b| a.0.cmp(b.0))
542 .map(|(_, node)| node.clone())
543 .collect::<Vec<_>>();
544
545 let value = 'value: {
548 if let Some(last) = proof.last() &&
549 let TrieNode::Leaf(leaf) = TrieNode::decode(&mut &last[..])? &&
550 nibbles.ends_with(&leaf.key)
551 {
552 break 'value U256::decode(&mut &leaf.value[..])?
553 }
554 U256::ZERO
555 };
556
557 Ok(StorageProof { key: slot, nibbles, value, proof })
558 }
559}
560
561#[derive(Clone, Debug, PartialEq, Eq)]
563pub struct DecodedStorageMultiProof {
564 pub root: B256,
566 pub subtree: DecodedProofNodes,
568 pub branch_node_masks: BranchNodeMasksMap,
571}
572
573impl DecodedStorageMultiProof {
574 pub fn empty() -> Self {
576 Self {
577 root: EMPTY_ROOT_HASH,
578 subtree: DecodedProofNodes::from_iter([(Nibbles::default(), TrieNode::EmptyRoot)]),
579 branch_node_masks: BranchNodeMasksMap::default(),
580 }
581 }
582
583 pub fn storage_proof(&self, slot: B256) -> Result<DecodedStorageProof, alloy_rlp::Error> {
585 let nibbles = Nibbles::unpack(keccak256(slot));
586
587 let proof = self
589 .subtree
590 .matching_nodes_iter(&nibbles)
591 .sorted_by(|a, b| a.0.cmp(b.0))
592 .map(|(_, node)| node.clone())
593 .collect::<Vec<_>>();
594
595 let value = 'value: {
598 if let Some(TrieNode::Leaf(leaf)) = proof.last() &&
599 nibbles.ends_with(&leaf.key)
600 {
601 break 'value U256::decode(&mut &leaf.value[..])?
602 }
603 U256::ZERO
604 };
605
606 Ok(DecodedStorageProof { key: slot, nibbles, value, proof })
607 }
608}
609
610impl TryFrom<StorageMultiProof> for DecodedStorageMultiProof {
611 type Error = alloy_rlp::Error;
612
613 fn try_from(multi_proof: StorageMultiProof) -> Result<Self, Self::Error> {
614 let subtree = DecodedProofNodes::try_from(multi_proof.subtree)?;
615 Ok(Self {
616 root: multi_proof.root,
617 subtree,
618 branch_node_masks: multi_proof.branch_node_masks,
619 })
620 }
621}
622
623#[derive(Clone, PartialEq, Eq, Debug)]
625#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
626#[cfg_attr(any(test, feature = "serde"), serde(rename_all = "camelCase"))]
627pub struct AccountProof {
628 pub address: Address,
630 pub info: Option<Account>,
632 pub proof: Vec<Bytes>,
635 pub storage_root: B256,
637 pub storage_proofs: Vec<StorageProof>,
639}
640
641#[cfg(feature = "eip1186")]
642impl AccountProof {
643 pub fn into_eip1186_response(
645 self,
646 slots: Vec<alloy_serde::JsonStorageKey>,
647 ) -> alloy_rpc_types_eth::EIP1186AccountProofResponse {
648 let info = self.info.unwrap_or_default();
649 alloy_rpc_types_eth::EIP1186AccountProofResponse {
650 address: self.address,
651 balance: info.balance,
652 code_hash: info.get_bytecode_hash(),
653 nonce: info.nonce,
654 storage_hash: self.storage_root,
655 account_proof: self.proof,
656 storage_proof: self
657 .storage_proofs
658 .into_iter()
659 .filter_map(|proof| {
660 let input_slot = slots.iter().find(|s| s.as_b256() == proof.key)?;
661 Some(proof.into_eip1186_proof(*input_slot))
662 })
663 .collect(),
664 }
665 }
666
667 pub fn from_eip1186_proof(proof: alloy_rpc_types_eth::EIP1186AccountProofResponse) -> Self {
673 let alloy_rpc_types_eth::EIP1186AccountProofResponse {
674 nonce,
675 address,
676 balance,
677 code_hash,
678 storage_hash,
679 account_proof,
680 storage_proof,
681 ..
682 } = proof;
683 let storage_proofs = storage_proof.into_iter().map(Into::into).collect();
684
685 let (storage_root, info) = if nonce == 0 &&
686 balance.is_zero() &&
687 storage_hash.is_zero() &&
688 code_hash == KECCAK_EMPTY
689 {
690 (EMPTY_ROOT_HASH, None)
693 } else {
694 (storage_hash, Some(Account { nonce, balance, bytecode_hash: code_hash.into() }))
695 };
696
697 Self { address, info, proof: account_proof, storage_root, storage_proofs }
698 }
699}
700
701#[cfg(feature = "eip1186")]
702impl From<alloy_rpc_types_eth::EIP1186AccountProofResponse> for AccountProof {
703 fn from(proof: alloy_rpc_types_eth::EIP1186AccountProofResponse) -> Self {
704 Self::from_eip1186_proof(proof)
705 }
706}
707
708impl Default for AccountProof {
709 fn default() -> Self {
710 Self::new(Address::default())
711 }
712}
713
714impl AccountProof {
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 pub fn verify(&self, root: B256) -> Result<(), ProofVerificationError> {
728 for storage_proof in &self.storage_proofs {
730 storage_proof.verify(self.storage_root)?;
731 }
732
733 let expected = if self.info.is_none() && self.storage_root == EMPTY_ROOT_HASH {
735 None
736 } else {
737 Some(alloy_rlp::encode(
738 self.info.unwrap_or_default().into_trie_account(self.storage_root),
739 ))
740 };
741 let nibbles = Nibbles::unpack(keccak256(self.address));
742 verify_proof(root, nibbles, expected, &self.proof)
743 }
744}
745
746#[derive(Clone, PartialEq, Eq, Debug)]
748pub struct DecodedAccountProof {
749 pub address: Address,
751 pub info: Option<Account>,
753 pub proof: Vec<TrieNode>,
756 pub storage_root: B256,
758 pub storage_proofs: Vec<DecodedStorageProof>,
760}
761
762impl Default for DecodedAccountProof {
763 fn default() -> Self {
764 Self::new(Address::default())
765 }
766}
767
768impl DecodedAccountProof {
769 pub const fn new(address: Address) -> Self {
771 Self {
772 address,
773 info: None,
774 proof: Vec::new(),
775 storage_root: EMPTY_ROOT_HASH,
776 storage_proofs: Vec::new(),
777 }
778 }
779}
780
781#[derive(Clone, PartialEq, Eq, Default, Debug)]
783#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
784pub struct StorageProof {
785 pub key: B256,
787 pub nibbles: Nibbles,
789 pub value: U256,
791 pub proof: Vec<Bytes>,
794}
795
796impl StorageProof {
797 pub fn new(key: B256) -> Self {
799 let nibbles = Nibbles::unpack(keccak256(key));
800 Self { key, nibbles, ..Default::default() }
801 }
802
803 pub fn new_with_hashed(key: B256, hashed_key: B256) -> Self {
805 Self { key, nibbles: Nibbles::unpack(hashed_key), ..Default::default() }
806 }
807
808 pub fn new_with_nibbles(key: B256, nibbles: Nibbles) -> Self {
810 Self { key, nibbles, ..Default::default() }
811 }
812
813 pub fn with_proof(mut self, proof: Vec<Bytes>) -> Self {
815 self.proof = proof;
816 self
817 }
818
819 pub fn verify(&self, root: B256) -> Result<(), ProofVerificationError> {
821 let expected =
822 if self.value.is_zero() { None } else { Some(encode_fixed_size(&self.value).to_vec()) };
823 verify_proof(root, self.nibbles, expected, &self.proof)
824 }
825}
826
827#[cfg(feature = "eip1186")]
828impl StorageProof {
829 pub fn into_eip1186_proof(
831 self,
832 slot: alloy_serde::JsonStorageKey,
833 ) -> alloy_rpc_types_eth::EIP1186StorageProof {
834 alloy_rpc_types_eth::EIP1186StorageProof { key: slot, value: self.value, proof: self.proof }
835 }
836
837 pub fn from_eip1186_proof(storage_proof: alloy_rpc_types_eth::EIP1186StorageProof) -> Self {
842 Self {
843 value: storage_proof.value,
844 proof: storage_proof.proof,
845 ..Self::new(storage_proof.key.as_b256())
846 }
847 }
848}
849
850#[cfg(feature = "eip1186")]
851impl From<alloy_rpc_types_eth::EIP1186StorageProof> for StorageProof {
852 fn from(proof: alloy_rpc_types_eth::EIP1186StorageProof) -> Self {
853 Self::from_eip1186_proof(proof)
854 }
855}
856
857#[derive(Clone, PartialEq, Eq, Default, Debug)]
859pub struct DecodedStorageProof {
860 pub key: B256,
862 pub nibbles: Nibbles,
864 pub value: U256,
866 pub proof: Vec<TrieNode>,
869}
870
871impl DecodedStorageProof {
872 pub fn new(key: B256) -> Self {
874 let nibbles = Nibbles::unpack(keccak256(key));
875 Self { key, nibbles, ..Default::default() }
876 }
877
878 pub fn new_with_hashed(key: B256, hashed_key: B256) -> Self {
880 Self { key, nibbles: Nibbles::unpack(hashed_key), ..Default::default() }
881 }
882
883 pub fn new_with_nibbles(key: B256, nibbles: Nibbles) -> Self {
885 Self { key, nibbles, ..Default::default() }
886 }
887
888 pub fn with_proof(mut self, proof: Vec<TrieNode>) -> Self {
890 self.proof = proof;
891 self
892 }
893}
894
895#[cfg(any(test, feature = "test-utils"))]
898pub mod triehash {
899 use alloy_primitives::{keccak256, B256};
900 use alloy_rlp::RlpEncodable;
901 use hash_db::Hasher;
902 use plain_hasher::PlainHasher;
903
904 #[derive(Default, Debug, Clone, PartialEq, Eq, RlpEncodable)]
906 #[non_exhaustive]
907 pub struct KeccakHasher;
908
909 #[cfg(any(test, feature = "test-utils"))]
910 impl Hasher for KeccakHasher {
911 type Out = B256;
912 type StdHasher = PlainHasher;
913
914 const LENGTH: usize = 32;
915
916 fn hash(x: &[u8]) -> Self::Out {
917 keccak256(x)
918 }
919 }
920}
921
922#[cfg(test)]
923mod tests {
924 use super::*;
925
926 #[test]
927 fn test_multiproof_extend_account_proofs() {
928 let mut proof1 = MultiProof::default();
929 let mut proof2 = MultiProof::default();
930
931 let addr1 = B256::random();
932 let addr2 = B256::random();
933
934 proof1.account_subtree.insert(
935 Nibbles::unpack(addr1),
936 alloy_rlp::encode_fixed_size(&U256::from(42)).to_vec().into(),
937 );
938 proof2.account_subtree.insert(
939 Nibbles::unpack(addr2),
940 alloy_rlp::encode_fixed_size(&U256::from(43)).to_vec().into(),
941 );
942
943 proof1.extend(proof2);
944
945 assert!(proof1.account_subtree.contains_key(&Nibbles::unpack(addr1)));
946 assert!(proof1.account_subtree.contains_key(&Nibbles::unpack(addr2)));
947 }
948
949 #[test]
950 fn test_multiproof_extend_storage_proofs() {
951 let mut proof1 = MultiProof::default();
952 let mut proof2 = MultiProof::default();
953
954 let addr = B256::random();
955 let root = B256::random();
956
957 let mut subtree1 = ProofNodes::default();
958 subtree1.insert(
959 Nibbles::from_nibbles(vec![0]),
960 alloy_rlp::encode_fixed_size(&U256::from(42)).to_vec().into(),
961 );
962 proof1.storages.insert(
963 addr,
964 StorageMultiProof {
965 root,
966 subtree: subtree1,
967 branch_node_masks: BranchNodeMasksMap::default(),
968 },
969 );
970
971 let mut subtree2 = ProofNodes::default();
972 subtree2.insert(
973 Nibbles::from_nibbles(vec![1]),
974 alloy_rlp::encode_fixed_size(&U256::from(43)).to_vec().into(),
975 );
976 proof2.storages.insert(
977 addr,
978 StorageMultiProof {
979 root,
980 subtree: subtree2,
981 branch_node_masks: BranchNodeMasksMap::default(),
982 },
983 );
984
985 proof1.extend(proof2);
986
987 let storage = proof1.storages.get(&addr).unwrap();
988 assert_eq!(storage.root, root);
989 assert!(storage.subtree.contains_key(&Nibbles::from_nibbles(vec![0])));
990 assert!(storage.subtree.contains_key(&Nibbles::from_nibbles(vec![1])));
991 }
992
993 #[test]
994 fn test_multi_proof_retain_difference() {
995 let mut empty = MultiProofTargets::default();
996 empty.retain_difference(&Default::default());
997 assert!(empty.is_empty());
998
999 let targets = MultiProofTargets::accounts((0..10).map(B256::with_last_byte));
1000
1001 let mut diffed = targets.clone();
1002 diffed.retain_difference(&MultiProofTargets::account(B256::with_last_byte(11)));
1003 assert_eq!(diffed, targets);
1004
1005 diffed.retain_difference(&MultiProofTargets::accounts((0..5).map(B256::with_last_byte)));
1006 assert_eq!(diffed, MultiProofTargets::accounts((5..10).map(B256::with_last_byte)));
1007
1008 diffed.retain_difference(&targets);
1009 assert!(diffed.is_empty());
1010
1011 let mut targets = MultiProofTargets::default();
1012 let (account1, account2, account3) =
1013 (1..=3).map(B256::with_last_byte).collect_tuple().unwrap();
1014 let account2_slots = (1..5).map(B256::with_last_byte).collect::<B256Set>();
1015 targets.insert(account1, B256Set::from_iter([B256::with_last_byte(1)]));
1016 targets.insert(account2, account2_slots.clone());
1017 targets.insert(account3, B256Set::from_iter([B256::with_last_byte(1)]));
1018
1019 let mut diffed = targets.clone();
1020 diffed.retain_difference(&MultiProofTargets::accounts((1..=3).map(B256::with_last_byte)));
1021 assert_eq!(diffed, targets);
1022
1023 let mut account2_slots_expected_len = account2_slots.len();
1025 for slot in account2_slots.iter().skip(1) {
1026 diffed.retain_difference(&MultiProofTargets::account_with_slots(account2, [*slot]));
1027 account2_slots_expected_len -= 1;
1028 assert_eq!(
1029 diffed.get(&account2).map(|slots| slots.len()),
1030 Some(account2_slots_expected_len)
1031 );
1032 }
1033
1034 diffed.retain_difference(&targets);
1035 assert!(diffed.is_empty());
1036 }
1037
1038 #[test]
1039 fn test_multi_proof_retain_difference_no_overlap() {
1040 let mut targets = MultiProofTargets::default();
1041
1042 let (addr1, addr2) = (B256::random(), B256::random());
1044 let (slot1, slot2) = (B256::random(), B256::random());
1045 targets.insert(addr1, std::iter::once(slot1).collect());
1046 targets.insert(addr2, std::iter::once(slot2).collect());
1047
1048 let mut retained = targets.clone();
1049 retained.retain_difference(&Default::default());
1050 assert_eq!(retained, targets);
1051
1052 let mut other_targets = MultiProofTargets::default();
1054 let addr3 = B256::random();
1055 let slot3 = B256::random();
1056 other_targets.insert(addr3, B256Set::from_iter([slot3]));
1057
1058 let mut retained = targets.clone();
1061 retained.retain_difference(&other_targets);
1062 assert_eq!(retained, targets);
1063 }
1064
1065 #[test]
1066 fn test_get_prefetch_proof_targets_remove_subset() {
1067 let mut targets = MultiProofTargets::default();
1069 let (addr1, addr2) = (B256::random(), B256::random());
1070 let (slot1, slot2) = (B256::random(), B256::random());
1071 targets.insert(addr1, B256Set::from_iter([slot1]));
1072 targets.insert(addr2, B256Set::from_iter([slot2]));
1073
1074 let other_targets = MultiProofTargets::account_with_slots(addr1, [slot1]);
1076
1077 let mut retained = targets.clone();
1078 retained.retain_difference(&other_targets);
1079
1080 assert_eq!(retained.len(), 1);
1082 assert!(!retained.contains_key(&addr1));
1083 assert!(retained.contains_key(&addr2));
1084
1085 let slot3 = B256::random();
1087 targets.get_mut(&addr1).unwrap().insert(slot3);
1088
1089 let mut retained = targets.clone();
1090 retained.retain_difference(&other_targets);
1091
1092 assert_eq!(retained.len(), 2);
1095 assert!(retained.contains_key(&addr1));
1096 assert_eq!(retained.get(&addr1), Some(&B256Set::from_iter([slot3])));
1097 assert!(retained.contains_key(&addr2));
1098 assert_eq!(retained.get(&addr2), Some(&B256Set::from_iter([slot2])));
1099 }
1100
1101 #[test]
1102 #[cfg(feature = "eip1186")]
1103 fn eip_1186_roundtrip() {
1104 let mut acc = AccountProof {
1105 address: Address::random(),
1106 info: Some(
1107 Account { nonce: 100, balance: U256::ZERO, bytecode_hash: Some(KECCAK_EMPTY) },
1109 ),
1110 proof: vec![],
1111 storage_root: B256::ZERO,
1112 storage_proofs: vec![],
1113 };
1114
1115 let rpc_proof = acc.clone().into_eip1186_response(Vec::new());
1116 let inverse: AccountProof = rpc_proof.into();
1117 assert_eq!(acc, inverse);
1118
1119 acc.info.as_mut().unwrap().nonce = 0;
1121 let rpc_proof = acc.clone().into_eip1186_response(Vec::new());
1122 let inverse: AccountProof = rpc_proof.into();
1123 acc.info.take();
1124 acc.storage_root = EMPTY_ROOT_HASH;
1125 assert_eq!(acc, inverse);
1126 }
1127
1128 #[test]
1129 fn test_multiproof_targets_chunking_length() {
1130 let mut targets = MultiProofTargets::default();
1131 targets.insert(B256::with_last_byte(1), B256Set::default());
1132 targets.insert(
1133 B256::with_last_byte(2),
1134 B256Set::from_iter([B256::with_last_byte(10), B256::with_last_byte(20)]),
1135 );
1136 targets.insert(
1137 B256::with_last_byte(3),
1138 B256Set::from_iter([
1139 B256::with_last_byte(30),
1140 B256::with_last_byte(31),
1141 B256::with_last_byte(32),
1142 ]),
1143 );
1144
1145 let chunking_length = targets.chunking_length();
1146 for size in 1..=targets.clone().chunks(1).count() {
1147 let chunk_count = targets.clone().chunks(size).count();
1148 let expected_count = chunking_length.div_ceil(size);
1149 assert_eq!(
1150 chunk_count, expected_count,
1151 "chunking_length: {}, size: {}",
1152 chunking_length, size
1153 );
1154 }
1155 }
1156}