1use crate::{BranchNodeMasks, BranchNodeMasksMap, Nibbles, ProofTrieNodeV2, TrieAccount};
4use alloc::{borrow::Cow, collections::VecDeque, 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 match self.entry(*hashed_address) {
83 hash_map::Entry::Vacant(entry) => {
84 entry.insert(hashed_slots.clone());
85 }
86 hash_map::Entry::Occupied(mut entry) => {
87 entry.get_mut().extend(hashed_slots);
88 }
89 }
90 }
91 }
92
93 pub fn chunks(self, size: usize) -> ChunkedMultiProofTargets {
97 ChunkedMultiProofTargets::new(self, size)
98 }
99
100 pub fn chunking_length(&self) -> usize {
102 self.values().map(|slots| 1 + slots.len().saturating_sub(1)).sum::<usize>()
103 }
104}
105
106#[derive(Debug)]
127pub struct ChunkedMultiProofTargets {
128 flattened_targets: alloc::vec::IntoIter<(B256, Option<B256>)>,
129 size: usize,
130}
131
132impl ChunkedMultiProofTargets {
133 fn new(targets: MultiProofTargets, size: usize) -> Self {
134 let flattened_targets = targets
135 .into_iter()
136 .flat_map(|(address, slots)| {
137 if slots.is_empty() {
138 itertools::Either::Left(core::iter::once((address, None)))
142 } else {
143 itertools::Either::Right(
144 slots.into_iter().map(move |slot| (address, Some(slot))),
145 )
146 }
147 })
148 .sorted_unstable();
149 Self { flattened_targets, size }
150 }
151}
152
153impl Iterator for ChunkedMultiProofTargets {
154 type Item = MultiProofTargets;
155
156 fn next(&mut self) -> Option<Self::Item> {
157 let chunk = self.flattened_targets.by_ref().take(self.size).fold(
158 MultiProofTargets::default(),
159 |mut acc, (address, slot)| {
160 let entry = acc.entry(address).or_default();
161 if let Some(slot) = slot {
162 entry.insert(slot);
163 }
164 acc
165 },
166 );
167
168 if chunk.is_empty() {
169 None
170 } else {
171 Some(chunk)
172 }
173 }
174}
175
176#[derive(Clone, Default, Debug, PartialEq, Eq)]
180pub struct MultiProof {
181 pub account_subtree: ProofNodes,
183 pub branch_node_masks: BranchNodeMasksMap,
186 pub storages: B256Map<StorageMultiProof>,
188}
189
190impl MultiProof {
191 pub fn is_empty(&self) -> bool {
193 self.account_subtree.is_empty() &&
194 self.branch_node_masks.is_empty() &&
195 self.storages.is_empty()
196 }
197
198 pub fn account_proof_nodes(&self, path: &Nibbles) -> Vec<(Nibbles, Bytes)> {
200 self.account_subtree.matching_nodes_sorted(path)
201 }
202
203 pub fn storage_proof_nodes(
205 &self,
206 hashed_address: B256,
207 slots: impl IntoIterator<Item = B256>,
208 ) -> Vec<(B256, Vec<(Nibbles, Bytes)>)> {
209 self.storages
210 .get(&hashed_address)
211 .map(|storage_mp| {
212 slots
213 .into_iter()
214 .map(|slot| {
215 let nibbles = Nibbles::unpack(slot);
216 (slot, storage_mp.subtree.matching_nodes_sorted(&nibbles))
217 })
218 .collect()
219 })
220 .unwrap_or_default()
221 }
222
223 pub fn account_proof(
225 &self,
226 address: Address,
227 slots: &[B256],
228 ) -> Result<AccountProof, alloy_rlp::Error> {
229 let hashed_address = keccak256(address);
230 let nibbles = Nibbles::unpack(hashed_address);
231
232 let proof = self
234 .account_proof_nodes(&nibbles)
235 .into_iter()
236 .map(|(_, node)| node)
237 .collect::<Vec<_>>();
238
239 let info = 'info: {
242 if let Some(last) = proof.last() &&
243 let TrieNode::Leaf(leaf) = TrieNode::decode(&mut &last[..])? &&
244 nibbles.ends_with(&leaf.key)
245 {
246 let account = TrieAccount::decode(&mut &leaf.value[..])?;
247 break 'info Some(Account {
248 balance: account.balance,
249 nonce: account.nonce,
250 bytecode_hash: (account.code_hash != KECCAK_EMPTY).then_some(account.code_hash),
251 })
252 }
253 None
254 };
255
256 let storage_multiproof = self.storages.get(&hashed_address);
258 let storage_root = storage_multiproof.map(|m| m.root).unwrap_or(EMPTY_ROOT_HASH);
259 let mut storage_proofs = Vec::with_capacity(slots.len());
260 for slot in slots {
261 let proof = if let Some(multiproof) = &storage_multiproof {
262 multiproof.storage_proof(*slot)?
263 } else {
264 StorageProof::new(*slot)
265 };
266 storage_proofs.push(proof);
267 }
268 Ok(AccountProof { address, info, proof, storage_root, storage_proofs })
269 }
270
271 pub fn extend(&mut self, other: Self) {
274 self.account_subtree.extend_from(other.account_subtree);
275 self.branch_node_masks.extend(other.branch_node_masks);
276
277 let reserve = if self.storages.is_empty() {
278 other.storages.len()
279 } else {
280 other.storages.len().div_ceil(2)
281 };
282 self.storages.reserve(reserve);
283 for (hashed_address, storage) in other.storages {
284 match self.storages.entry(hashed_address) {
285 hash_map::Entry::Occupied(mut entry) => {
286 debug_assert_eq!(entry.get().root, storage.root);
287 let entry = entry.get_mut();
288 entry.subtree.extend_from(storage.subtree);
289 entry.branch_node_masks.extend(storage.branch_node_masks);
290 }
291 hash_map::Entry::Vacant(entry) => {
292 entry.insert(storage);
293 }
294 }
295 }
296 }
297
298 pub fn from_storage_proof(hashed_address: B256, storage_proof: StorageMultiProof) -> Self {
300 Self {
301 storages: B256Map::from_iter([(hashed_address, storage_proof)]),
302 ..Default::default()
303 }
304 }
305}
306
307#[derive(Clone, Default, Debug, PartialEq, Eq)]
310pub struct DecodedMultiProof {
311 pub account_subtree: DecodedProofNodes,
313 pub branch_node_masks: BranchNodeMasksMap,
316 pub storages: B256Map<DecodedStorageMultiProof>,
318}
319
320impl DecodedMultiProof {
321 pub fn is_empty(&self) -> bool {
323 self.account_subtree.is_empty() &&
324 self.branch_node_masks.is_empty() &&
325 self.storages.is_empty()
326 }
327
328 pub fn account_proof_nodes(&self, path: &Nibbles) -> Vec<(Nibbles, TrieNode)> {
330 self.account_subtree.matching_nodes_sorted(path)
331 }
332
333 pub fn storage_proof_nodes(
335 &self,
336 hashed_address: B256,
337 slots: impl IntoIterator<Item = B256>,
338 ) -> Vec<(B256, Vec<(Nibbles, TrieNode)>)> {
339 self.storages
340 .get(&hashed_address)
341 .map(|storage_mp| {
342 slots
343 .into_iter()
344 .map(|slot| {
345 let nibbles = Nibbles::unpack(slot);
346 (slot, storage_mp.subtree.matching_nodes_sorted(&nibbles))
347 })
348 .collect()
349 })
350 .unwrap_or_default()
351 }
352
353 pub fn account_proof(
355 &self,
356 address: Address,
357 slots: &[B256],
358 ) -> Result<DecodedAccountProof, alloy_rlp::Error> {
359 let hashed_address = keccak256(address);
360 let nibbles = Nibbles::unpack(hashed_address);
361
362 let proof = self
364 .account_proof_nodes(&nibbles)
365 .into_iter()
366 .map(|(_, node)| node)
367 .collect::<Vec<_>>();
368
369 let info = 'info: {
372 if let Some(TrieNode::Leaf(leaf)) = proof.last() &&
373 nibbles.ends_with(&leaf.key)
374 {
375 let account = TrieAccount::decode(&mut &leaf.value[..])?;
376 break 'info Some(Account {
377 balance: account.balance,
378 nonce: account.nonce,
379 bytecode_hash: (account.code_hash != KECCAK_EMPTY).then_some(account.code_hash),
380 })
381 }
382 None
383 };
384
385 let storage_multiproof = self.storages.get(&hashed_address);
387 let storage_root = storage_multiproof.map(|m| m.root).unwrap_or(EMPTY_ROOT_HASH);
388 let mut storage_proofs = Vec::with_capacity(slots.len());
389 for slot in slots {
390 let proof = if let Some(multiproof) = &storage_multiproof {
391 multiproof.storage_proof(*slot)?
392 } else {
393 DecodedStorageProof::new(*slot)
394 };
395 storage_proofs.push(proof);
396 }
397 Ok(DecodedAccountProof { address, info, proof, storage_root, storage_proofs })
398 }
399
400 pub fn extend(&mut self, other: Self) {
403 self.account_subtree.extend_from(other.account_subtree);
404 self.branch_node_masks.extend(other.branch_node_masks);
405
406 let reserve = if self.storages.is_empty() {
407 other.storages.len()
408 } else {
409 other.storages.len().div_ceil(2)
410 };
411 self.storages.reserve(reserve);
412 for (hashed_address, storage) in other.storages {
413 match self.storages.entry(hashed_address) {
414 hash_map::Entry::Occupied(mut entry) => {
415 debug_assert_eq!(entry.get().root, storage.root);
416 let entry = entry.get_mut();
417 entry.subtree.extend_from(storage.subtree);
418 entry.branch_node_masks.extend(storage.branch_node_masks);
419 }
420 hash_map::Entry::Vacant(entry) => {
421 entry.insert(storage);
422 }
423 }
424 }
425 }
426
427 pub fn from_storage_proof(
429 hashed_address: B256,
430 storage_proof: DecodedStorageMultiProof,
431 ) -> Self {
432 Self {
433 storages: B256Map::from_iter([(hashed_address, storage_proof)]),
434 ..Default::default()
435 }
436 }
437}
438
439impl TryFrom<MultiProof> for DecodedMultiProof {
440 type Error = alloy_rlp::Error;
441
442 fn try_from(multi_proof: MultiProof) -> Result<Self, Self::Error> {
443 let account_subtree = DecodedProofNodes::try_from(multi_proof.account_subtree)?;
444 let storages = multi_proof
445 .storages
446 .into_iter()
447 .map(|(address, storage)| Ok((address, storage.try_into()?)))
448 .collect::<Result<B256Map<_>, alloy_rlp::Error>>()?;
449 Ok(Self { account_subtree, branch_node_masks: multi_proof.branch_node_masks, storages })
450 }
451}
452
453#[derive(Clone, Debug, PartialEq, Eq, Default)]
456pub struct DecodedMultiProofV2 {
457 pub account_proofs: Vec<ProofTrieNodeV2>,
459 pub storage_proofs: B256Map<Vec<ProofTrieNodeV2>>,
461}
462
463impl DecodedMultiProofV2 {
464 pub fn is_empty(&self) -> bool {
466 self.account_proofs.is_empty() && self.storage_proofs.is_empty()
467 }
468
469 pub fn from_witness(
476 state_root: B256,
477 witness: &B256Map<impl AsRef<[u8]>>,
478 ) -> Result<Self, alloy_rlp::Error> {
479 let mut account_nodes: Vec<(Nibbles, TrieNode, Option<BranchNodeMasks>)> = Vec::new();
480 let mut storage_nodes: B256Map<Vec<(Nibbles, TrieNode, Option<BranchNodeMasks>)>> =
481 B256Map::default();
482
483 let mut queue: VecDeque<(B256, Nibbles, Option<B256>)> =
484 VecDeque::from([(state_root, Nibbles::default(), None)]);
485
486 while let Some((hash, path, maybe_account)) = queue.pop_front() {
487 let Some(rlp_bytes) = witness.get(&hash) else { continue };
488 let trie_node = TrieNode::decode(&mut rlp_bytes.as_ref())?;
489
490 match &trie_node {
491 TrieNode::Branch(branch) => {
492 for (idx, maybe_child) in branch.as_ref().children() {
493 if let Some(child_hash) =
494 maybe_child.and_then(alloy_trie::nodes::RlpNode::as_hash)
495 {
496 let mut child_path = path;
497 child_path.push_unchecked(idx);
498 queue.push_back((child_hash, child_path, maybe_account));
499 }
500 }
501 }
502 TrieNode::Extension(ext) => {
503 if let Some(child_hash) = ext.child.as_hash() {
504 let mut child_path = path;
505 child_path.extend(&ext.key);
506 queue.push_back((child_hash, child_path, maybe_account));
507 }
508 }
509 TrieNode::Leaf(leaf) => {
510 if maybe_account.is_none() {
511 let mut full_path = path;
512 full_path.extend(&leaf.key);
513 let hashed_address = B256::from_slice(&full_path.pack());
514 let account = TrieAccount::decode(&mut &leaf.value[..])?;
515 if account.storage_root != EMPTY_ROOT_HASH {
516 queue.push_back((
517 account.storage_root,
518 Nibbles::default(),
519 Some(hashed_address),
520 ));
521 }
522 }
523 }
524 TrieNode::EmptyRoot => {}
525 }
526
527 if let Some(account) = maybe_account {
528 storage_nodes.entry(account).or_default().push((path, trie_node, None));
529 } else {
530 account_nodes.push((path, trie_node, None));
531 }
532 }
533
534 account_nodes.sort_by(|(a, _, _), (b, _, _)| b.cmp(a));
535 let account_proofs = ProofTrieNodeV2::from_sorted_trie_nodes(account_nodes);
536
537 let mut storage_proofs = B256Map::default();
538 for (account, mut nodes) in storage_nodes {
539 nodes.sort_by(|(a, _, _), (b, _, _)| b.cmp(a));
540 storage_proofs.insert(account, ProofTrieNodeV2::from_sorted_trie_nodes(nodes));
541 }
542
543 Ok(Self { account_proofs, storage_proofs })
544 }
545
546 pub fn extend(&mut self, other: Self) {
550 self.account_proofs.extend(other.account_proofs);
551 for (hashed_address, other_storage_proofs) in other.storage_proofs {
552 match self.storage_proofs.entry(hashed_address) {
553 hash_map::Entry::Vacant(entry) => {
554 entry.insert(other_storage_proofs);
555 }
556 hash_map::Entry::Occupied(mut entry) => {
557 entry.get_mut().extend(other_storage_proofs);
558 }
559 }
560 }
561 }
562}
563
564impl From<DecodedMultiProof> for DecodedMultiProofV2 {
565 fn from(proof: DecodedMultiProof) -> Self {
566 let account_proofs =
567 decoded_proof_nodes_to_v2(proof.account_subtree, &proof.branch_node_masks);
568 let storage_proofs = proof
569 .storages
570 .into_iter()
571 .map(|(address, storage)| {
572 (address, decoded_proof_nodes_to_v2(storage.subtree, &storage.branch_node_masks))
573 })
574 .collect();
575 Self { account_proofs, storage_proofs }
576 }
577}
578
579fn decoded_proof_nodes_to_v2(
582 nodes: DecodedProofNodes,
583 masks: &BranchNodeMasksMap,
584) -> Vec<ProofTrieNodeV2> {
585 let mut sorted: Vec<_> = nodes.into_inner().into_iter().collect();
586 sorted.sort_unstable_by(|a, b| crate::depth_first_cmp(&a.0, &b.0));
587 ProofTrieNodeV2::from_sorted_trie_nodes(
588 sorted.into_iter().map(|(path, node)| (path, node, masks.get(&path).copied())),
589 )
590}
591
592#[derive(Clone, Debug, PartialEq, Eq)]
594pub struct StorageMultiProof {
595 pub root: B256,
597 pub subtree: ProofNodes,
599 pub branch_node_masks: BranchNodeMasksMap,
602}
603
604impl StorageMultiProof {
605 pub fn empty() -> Self {
607 Self {
608 root: EMPTY_ROOT_HASH,
609 subtree: ProofNodes::from_iter([(
610 Nibbles::default(),
611 Bytes::from([EMPTY_STRING_CODE]),
612 )]),
613 branch_node_masks: BranchNodeMasksMap::default(),
614 }
615 }
616
617 pub fn storage_proof(&self, slot: B256) -> Result<StorageProof, alloy_rlp::Error> {
619 let nibbles = Nibbles::unpack(keccak256(slot));
620
621 let proof = self
623 .subtree
624 .matching_nodes_iter(&nibbles)
625 .sorted_by(|a, b| a.0.cmp(b.0))
626 .map(|(_, node)| node.clone())
627 .collect::<Vec<_>>();
628
629 let value = 'value: {
632 if let Some(last) = proof.last() &&
633 let TrieNode::Leaf(leaf) = TrieNode::decode(&mut &last[..])? &&
634 nibbles.ends_with(&leaf.key)
635 {
636 break 'value U256::decode(&mut &leaf.value[..])?
637 }
638 U256::ZERO
639 };
640
641 Ok(StorageProof { key: slot, nibbles, value, proof })
642 }
643}
644
645#[derive(Clone, Debug, PartialEq, Eq)]
647pub struct DecodedStorageMultiProof {
648 pub root: B256,
650 pub subtree: DecodedProofNodes,
652 pub branch_node_masks: BranchNodeMasksMap,
655}
656
657impl DecodedStorageMultiProof {
658 pub fn empty() -> Self {
660 Self {
661 root: EMPTY_ROOT_HASH,
662 subtree: DecodedProofNodes::from_iter([(Nibbles::default(), TrieNode::EmptyRoot)]),
663 branch_node_masks: BranchNodeMasksMap::default(),
664 }
665 }
666
667 pub fn storage_proof(&self, slot: B256) -> Result<DecodedStorageProof, alloy_rlp::Error> {
669 let nibbles = Nibbles::unpack(keccak256(slot));
670
671 let proof = self
673 .subtree
674 .matching_nodes_iter(&nibbles)
675 .sorted_by(|a, b| a.0.cmp(b.0))
676 .map(|(_, node)| node.clone())
677 .collect::<Vec<_>>();
678
679 let value = 'value: {
682 if let Some(TrieNode::Leaf(leaf)) = proof.last() &&
683 nibbles.ends_with(&leaf.key)
684 {
685 break 'value U256::decode(&mut &leaf.value[..])?
686 }
687 U256::ZERO
688 };
689
690 Ok(DecodedStorageProof { key: slot, nibbles, value, proof })
691 }
692}
693
694impl TryFrom<StorageMultiProof> for DecodedStorageMultiProof {
695 type Error = alloy_rlp::Error;
696
697 fn try_from(multi_proof: StorageMultiProof) -> Result<Self, Self::Error> {
698 let subtree = DecodedProofNodes::try_from(multi_proof.subtree)?;
699 Ok(Self {
700 root: multi_proof.root,
701 subtree,
702 branch_node_masks: multi_proof.branch_node_masks,
703 })
704 }
705}
706
707#[derive(Clone, PartialEq, Eq, Debug)]
709#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
710#[cfg_attr(any(test, feature = "serde"), serde(rename_all = "camelCase"))]
711pub struct AccountProof {
712 pub address: Address,
714 pub info: Option<Account>,
716 pub proof: Vec<Bytes>,
719 pub storage_root: B256,
721 pub storage_proofs: Vec<StorageProof>,
723}
724
725#[cfg(feature = "eip1186")]
726impl AccountProof {
727 pub fn into_eip1186_response(
729 self,
730 slots: Vec<alloy_serde::JsonStorageKey>,
731 ) -> alloy_rpc_types_eth::EIP1186AccountProofResponse {
732 let info = self.info.unwrap_or_default();
733 alloy_rpc_types_eth::EIP1186AccountProofResponse {
734 address: self.address,
735 balance: info.balance,
736 code_hash: info.get_bytecode_hash(),
737 nonce: info.nonce,
738 storage_hash: self.storage_root,
739 account_proof: self.proof,
740 storage_proof: self
741 .storage_proofs
742 .into_iter()
743 .filter_map(|proof| {
744 let input_slot = slots.iter().find(|s| s.as_b256() == proof.key)?;
745 Some(proof.into_eip1186_proof(*input_slot))
746 })
747 .collect(),
748 }
749 }
750
751 pub fn from_eip1186_proof(proof: alloy_rpc_types_eth::EIP1186AccountProofResponse) -> Self {
757 let alloy_rpc_types_eth::EIP1186AccountProofResponse {
758 nonce,
759 address,
760 balance,
761 code_hash,
762 storage_hash,
763 account_proof,
764 storage_proof,
765 ..
766 } = proof;
767 let storage_proofs = storage_proof.into_iter().map(Into::into).collect();
768
769 let (storage_root, info) = if nonce == 0 &&
770 balance.is_zero() &&
771 storage_hash.is_zero() &&
772 code_hash == KECCAK_EMPTY
773 {
774 (EMPTY_ROOT_HASH, None)
777 } else {
778 (storage_hash, Some(Account { nonce, balance, bytecode_hash: code_hash.into() }))
779 };
780
781 Self { address, info, proof: account_proof, storage_root, storage_proofs }
782 }
783}
784
785#[cfg(feature = "eip1186")]
786impl From<alloy_rpc_types_eth::EIP1186AccountProofResponse> for AccountProof {
787 fn from(proof: alloy_rpc_types_eth::EIP1186AccountProofResponse) -> Self {
788 Self::from_eip1186_proof(proof)
789 }
790}
791
792impl Default for AccountProof {
793 fn default() -> Self {
794 Self::new(Address::default())
795 }
796}
797
798impl AccountProof {
799 pub const fn new(address: Address) -> Self {
801 Self {
802 address,
803 info: None,
804 proof: Vec::new(),
805 storage_root: EMPTY_ROOT_HASH,
806 storage_proofs: Vec::new(),
807 }
808 }
809
810 pub fn verify(&self, root: B256) -> Result<(), ProofVerificationError> {
812 for storage_proof in &self.storage_proofs {
814 storage_proof.verify(self.storage_root)?;
815 }
816
817 let expected = if self.info.is_none() && self.storage_root == EMPTY_ROOT_HASH {
819 None
820 } else {
821 Some(alloy_rlp::encode(
822 self.info.unwrap_or_default().into_trie_account(self.storage_root),
823 ))
824 };
825 let nibbles = Nibbles::unpack(keccak256(self.address));
826 verify_proof(root, nibbles, expected, &self.proof)
827 }
828}
829
830#[derive(Clone, PartialEq, Eq, Debug)]
832pub struct DecodedAccountProof {
833 pub address: Address,
835 pub info: Option<Account>,
837 pub proof: Vec<TrieNode>,
840 pub storage_root: B256,
842 pub storage_proofs: Vec<DecodedStorageProof>,
844}
845
846impl Default for DecodedAccountProof {
847 fn default() -> Self {
848 Self::new(Address::default())
849 }
850}
851
852impl DecodedAccountProof {
853 pub const fn new(address: Address) -> Self {
855 Self {
856 address,
857 info: None,
858 proof: Vec::new(),
859 storage_root: EMPTY_ROOT_HASH,
860 storage_proofs: Vec::new(),
861 }
862 }
863}
864
865#[derive(Clone, PartialEq, Eq, Default, Debug)]
867#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
868pub struct StorageProof {
869 pub key: B256,
871 pub nibbles: Nibbles,
873 pub value: U256,
875 pub proof: Vec<Bytes>,
878}
879
880impl StorageProof {
881 pub fn new(key: B256) -> Self {
883 let nibbles = Nibbles::unpack(keccak256(key));
884 Self { key, nibbles, ..Default::default() }
885 }
886
887 pub fn new_with_hashed(key: B256, hashed_key: B256) -> Self {
889 Self { key, nibbles: Nibbles::unpack(hashed_key), ..Default::default() }
890 }
891
892 pub fn new_with_nibbles(key: B256, nibbles: Nibbles) -> Self {
894 Self { key, nibbles, ..Default::default() }
895 }
896
897 pub fn with_proof(mut self, proof: Vec<Bytes>) -> Self {
899 self.proof = proof;
900 self
901 }
902
903 pub fn verify(&self, root: B256) -> Result<(), ProofVerificationError> {
905 let expected =
906 if self.value.is_zero() { None } else { Some(encode_fixed_size(&self.value).to_vec()) };
907 verify_proof(root, self.nibbles, expected, &self.proof)
908 }
909}
910
911#[cfg(feature = "eip1186")]
912impl StorageProof {
913 pub fn into_eip1186_proof(
915 self,
916 slot: alloy_serde::JsonStorageKey,
917 ) -> alloy_rpc_types_eth::EIP1186StorageProof {
918 alloy_rpc_types_eth::EIP1186StorageProof { key: slot, value: self.value, proof: self.proof }
919 }
920
921 pub fn from_eip1186_proof(storage_proof: alloy_rpc_types_eth::EIP1186StorageProof) -> Self {
926 Self {
927 value: storage_proof.value,
928 proof: storage_proof.proof,
929 ..Self::new(storage_proof.key.as_b256())
930 }
931 }
932}
933
934#[cfg(feature = "eip1186")]
935impl From<alloy_rpc_types_eth::EIP1186StorageProof> for StorageProof {
936 fn from(proof: alloy_rpc_types_eth::EIP1186StorageProof) -> Self {
937 Self::from_eip1186_proof(proof)
938 }
939}
940
941#[derive(Clone, PartialEq, Eq, Default, Debug)]
943pub struct DecodedStorageProof {
944 pub key: B256,
946 pub nibbles: Nibbles,
948 pub value: U256,
950 pub proof: Vec<TrieNode>,
953}
954
955impl DecodedStorageProof {
956 pub fn new(key: B256) -> Self {
958 let nibbles = Nibbles::unpack(keccak256(key));
959 Self { key, nibbles, ..Default::default() }
960 }
961
962 pub fn new_with_hashed(key: B256, hashed_key: B256) -> Self {
964 Self { key, nibbles: Nibbles::unpack(hashed_key), ..Default::default() }
965 }
966
967 pub fn new_with_nibbles(key: B256, nibbles: Nibbles) -> Self {
969 Self { key, nibbles, ..Default::default() }
970 }
971
972 pub fn with_proof(mut self, proof: Vec<TrieNode>) -> Self {
974 self.proof = proof;
975 self
976 }
977}
978
979#[cfg(any(test, feature = "test-utils"))]
982pub mod triehash {
983 use alloy_primitives::{keccak256, B256};
984 use alloy_rlp::RlpEncodable;
985 use hash_db::Hasher;
986 use plain_hasher::PlainHasher;
987
988 #[derive(Default, Debug, Clone, PartialEq, Eq, RlpEncodable)]
990 #[non_exhaustive]
991 pub struct KeccakHasher;
992
993 #[cfg(any(test, feature = "test-utils"))]
994 impl Hasher for KeccakHasher {
995 type Out = B256;
996 type StdHasher = PlainHasher;
997
998 const LENGTH: usize = 32;
999
1000 fn hash(x: &[u8]) -> Self::Out {
1001 keccak256(x)
1002 }
1003 }
1004}
1005
1006#[cfg(test)]
1007mod tests {
1008 use super::*;
1009
1010 #[test]
1011 fn test_multiproof_extend_account_proofs() {
1012 let mut proof1 = MultiProof::default();
1013 let mut proof2 = MultiProof::default();
1014
1015 let addr1 = B256::random();
1016 let addr2 = B256::random();
1017
1018 proof1.account_subtree.insert(
1019 Nibbles::unpack(addr1),
1020 alloy_rlp::encode_fixed_size(&U256::from(42)).to_vec().into(),
1021 );
1022 proof2.account_subtree.insert(
1023 Nibbles::unpack(addr2),
1024 alloy_rlp::encode_fixed_size(&U256::from(43)).to_vec().into(),
1025 );
1026
1027 proof1.extend(proof2);
1028
1029 assert!(proof1.account_subtree.contains_key(&Nibbles::unpack(addr1)));
1030 assert!(proof1.account_subtree.contains_key(&Nibbles::unpack(addr2)));
1031 }
1032
1033 #[test]
1034 fn test_multiproof_extend_storage_proofs() {
1035 let mut proof1 = MultiProof::default();
1036 let mut proof2 = MultiProof::default();
1037
1038 let addr = B256::random();
1039 let root = B256::random();
1040
1041 let mut subtree1 = ProofNodes::default();
1042 subtree1.insert(
1043 Nibbles::from_nibbles(vec![0]),
1044 alloy_rlp::encode_fixed_size(&U256::from(42)).to_vec().into(),
1045 );
1046 proof1.storages.insert(
1047 addr,
1048 StorageMultiProof {
1049 root,
1050 subtree: subtree1,
1051 branch_node_masks: BranchNodeMasksMap::default(),
1052 },
1053 );
1054
1055 let mut subtree2 = ProofNodes::default();
1056 subtree2.insert(
1057 Nibbles::from_nibbles(vec![1]),
1058 alloy_rlp::encode_fixed_size(&U256::from(43)).to_vec().into(),
1059 );
1060 proof2.storages.insert(
1061 addr,
1062 StorageMultiProof {
1063 root,
1064 subtree: subtree2,
1065 branch_node_masks: BranchNodeMasksMap::default(),
1066 },
1067 );
1068
1069 proof1.extend(proof2);
1070
1071 let storage = proof1.storages.get(&addr).unwrap();
1072 assert_eq!(storage.root, root);
1073 assert!(storage.subtree.contains_key(&Nibbles::from_nibbles(vec![0])));
1074 assert!(storage.subtree.contains_key(&Nibbles::from_nibbles(vec![1])));
1075 }
1076
1077 #[test]
1078 fn test_multi_proof_retain_difference() {
1079 let mut empty = MultiProofTargets::default();
1080 empty.retain_difference(&Default::default());
1081 assert!(empty.is_empty());
1082
1083 let targets = MultiProofTargets::accounts((0..10).map(B256::with_last_byte));
1084
1085 let mut diffed = targets.clone();
1086 diffed.retain_difference(&MultiProofTargets::account(B256::with_last_byte(11)));
1087 assert_eq!(diffed, targets);
1088
1089 diffed.retain_difference(&MultiProofTargets::accounts((0..5).map(B256::with_last_byte)));
1090 assert_eq!(diffed, MultiProofTargets::accounts((5..10).map(B256::with_last_byte)));
1091
1092 diffed.retain_difference(&targets);
1093 assert!(diffed.is_empty());
1094
1095 let mut targets = MultiProofTargets::default();
1096 let (account1, account2, account3) =
1097 (1..=3).map(B256::with_last_byte).collect_tuple().unwrap();
1098 let account2_slots = (1..5).map(B256::with_last_byte).collect::<B256Set>();
1099 targets.insert(account1, B256Set::from_iter([B256::with_last_byte(1)]));
1100 targets.insert(account2, account2_slots.clone());
1101 targets.insert(account3, B256Set::from_iter([B256::with_last_byte(1)]));
1102
1103 let mut diffed = targets.clone();
1104 diffed.retain_difference(&MultiProofTargets::accounts((1..=3).map(B256::with_last_byte)));
1105 assert_eq!(diffed, targets);
1106
1107 let mut account2_slots_expected_len = account2_slots.len();
1109 for slot in account2_slots.iter().skip(1) {
1110 diffed.retain_difference(&MultiProofTargets::account_with_slots(account2, [*slot]));
1111 account2_slots_expected_len -= 1;
1112 assert_eq!(
1113 diffed.get(&account2).map(|slots| slots.len()),
1114 Some(account2_slots_expected_len)
1115 );
1116 }
1117
1118 diffed.retain_difference(&targets);
1119 assert!(diffed.is_empty());
1120 }
1121
1122 #[test]
1123 fn test_multi_proof_retain_difference_no_overlap() {
1124 let mut targets = MultiProofTargets::default();
1125
1126 let (addr1, addr2) = (B256::random(), B256::random());
1128 let (slot1, slot2) = (B256::random(), B256::random());
1129 targets.insert(addr1, std::iter::once(slot1).collect());
1130 targets.insert(addr2, std::iter::once(slot2).collect());
1131
1132 let mut retained = targets.clone();
1133 retained.retain_difference(&Default::default());
1134 assert_eq!(retained, targets);
1135
1136 let mut other_targets = MultiProofTargets::default();
1138 let addr3 = B256::random();
1139 let slot3 = B256::random();
1140 other_targets.insert(addr3, B256Set::from_iter([slot3]));
1141
1142 let mut retained = targets.clone();
1145 retained.retain_difference(&other_targets);
1146 assert_eq!(retained, targets);
1147 }
1148
1149 #[test]
1150 fn test_get_prefetch_proof_targets_remove_subset() {
1151 let mut targets = MultiProofTargets::default();
1153 let (addr1, addr2) = (B256::random(), B256::random());
1154 let (slot1, slot2) = (B256::random(), B256::random());
1155 targets.insert(addr1, B256Set::from_iter([slot1]));
1156 targets.insert(addr2, B256Set::from_iter([slot2]));
1157
1158 let other_targets = MultiProofTargets::account_with_slots(addr1, [slot1]);
1160
1161 let mut retained = targets.clone();
1162 retained.retain_difference(&other_targets);
1163
1164 assert_eq!(retained.len(), 1);
1166 assert!(!retained.contains_key(&addr1));
1167 assert!(retained.contains_key(&addr2));
1168
1169 let slot3 = B256::random();
1171 targets.get_mut(&addr1).unwrap().insert(slot3);
1172
1173 let mut retained = targets.clone();
1174 retained.retain_difference(&other_targets);
1175
1176 assert_eq!(retained.len(), 2);
1179 assert!(retained.contains_key(&addr1));
1180 assert_eq!(retained.get(&addr1), Some(&B256Set::from_iter([slot3])));
1181 assert!(retained.contains_key(&addr2));
1182 assert_eq!(retained.get(&addr2), Some(&B256Set::from_iter([slot2])));
1183 }
1184
1185 #[test]
1186 #[cfg(feature = "eip1186")]
1187 fn eip_1186_roundtrip() {
1188 let mut acc = AccountProof {
1189 address: Address::random(),
1190 info: Some(
1191 Account { nonce: 100, balance: U256::ZERO, bytecode_hash: Some(KECCAK_EMPTY) },
1193 ),
1194 proof: vec![],
1195 storage_root: B256::ZERO,
1196 storage_proofs: vec![],
1197 };
1198
1199 let rpc_proof = acc.clone().into_eip1186_response(Vec::new());
1200 let inverse: AccountProof = rpc_proof.into();
1201 assert_eq!(acc, inverse);
1202
1203 acc.info.as_mut().unwrap().nonce = 0;
1205 let rpc_proof = acc.clone().into_eip1186_response(Vec::new());
1206 let inverse: AccountProof = rpc_proof.into();
1207 acc.info.take();
1208 acc.storage_root = EMPTY_ROOT_HASH;
1209 assert_eq!(acc, inverse);
1210 }
1211
1212 #[test]
1213 fn test_multiproof_targets_chunking_length() {
1214 let mut targets = MultiProofTargets::default();
1215 targets.insert(B256::with_last_byte(1), B256Set::default());
1216 targets.insert(
1217 B256::with_last_byte(2),
1218 B256Set::from_iter([B256::with_last_byte(10), B256::with_last_byte(20)]),
1219 );
1220 targets.insert(
1221 B256::with_last_byte(3),
1222 B256Set::from_iter([
1223 B256::with_last_byte(30),
1224 B256::with_last_byte(31),
1225 B256::with_last_byte(32),
1226 ]),
1227 );
1228
1229 let chunking_length = targets.chunking_length();
1230 for size in 1..=targets.clone().chunks(1).count() {
1231 let chunk_count = targets.clone().chunks(size).count();
1232 let expected_count = chunking_length.div_ceil(size);
1233 assert_eq!(
1234 chunk_count, expected_count,
1235 "chunking_length: {}, size: {}",
1236 chunking_length, size
1237 );
1238 }
1239 }
1240}