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")]
733fn normalize_eip1186_empty_trie_proof(proof: Vec<Bytes>) -> Vec<Bytes> {
734 if proof.len() == 1 && proof[0].as_ref() == [EMPTY_STRING_CODE] {
735 Vec::new()
736 } else {
737 proof
738 }
739}
740
741#[cfg(feature = "eip1186")]
742impl AccountProof {
743 pub fn into_eip1186_response(
751 self,
752 slots: Vec<alloy_serde::JsonStorageKey>,
753 ) -> alloy_rpc_types_eth::EIP1186AccountProofResponse {
754 self.into_eip1186_response_with(slots, false)
755 }
756
757 pub fn into_eip1186_response_with(
768 self,
769 slots: Vec<alloy_serde::JsonStorageKey>,
770 zero_empty_account: bool,
771 ) -> alloy_rpc_types_eth::EIP1186AccountProofResponse {
772 let is_non_existent = self.info.is_none();
773 let info = self.info.unwrap_or_default();
774 let (code_hash, storage_hash) = if is_non_existent && zero_empty_account {
775 (B256::ZERO, B256::ZERO)
776 } else {
777 (info.get_bytecode_hash(), self.storage_root)
778 };
779 alloy_rpc_types_eth::EIP1186AccountProofResponse {
780 address: self.address,
781 balance: info.balance,
782 code_hash,
783 nonce: info.nonce,
784 storage_hash,
785 account_proof: normalize_eip1186_empty_trie_proof(self.proof),
786 storage_proof: self
787 .storage_proofs
788 .into_iter()
789 .filter_map(|proof| {
790 let input_slot = slots.iter().find(|s| s.as_b256() == proof.key)?;
791 Some(proof.into_eip1186_proof(*input_slot))
792 })
793 .collect(),
794 }
795 }
796
797 pub fn from_eip1186_proof(proof: alloy_rpc_types_eth::EIP1186AccountProofResponse) -> Self {
803 let alloy_rpc_types_eth::EIP1186AccountProofResponse {
804 nonce,
805 address,
806 balance,
807 code_hash,
808 storage_hash,
809 account_proof,
810 storage_proof,
811 ..
812 } = proof;
813 let storage_proofs = storage_proof.into_iter().map(Into::into).collect();
814
815 let (storage_root, info) = if nonce == 0 &&
816 balance.is_zero() &&
817 (storage_hash.is_zero() || storage_hash == EMPTY_ROOT_HASH) &&
818 (code_hash == KECCAK_EMPTY || code_hash.is_zero())
819 {
820 (EMPTY_ROOT_HASH, None)
829 } else {
830 (storage_hash, Some(Account { nonce, balance, bytecode_hash: code_hash.into() }))
831 };
832
833 Self { address, info, proof: account_proof, storage_root, storage_proofs }
834 }
835}
836
837#[cfg(feature = "eip1186")]
838impl From<alloy_rpc_types_eth::EIP1186AccountProofResponse> for AccountProof {
839 fn from(proof: alloy_rpc_types_eth::EIP1186AccountProofResponse) -> Self {
840 Self::from_eip1186_proof(proof)
841 }
842}
843
844impl Default for AccountProof {
845 fn default() -> Self {
846 Self::new(Address::default())
847 }
848}
849
850impl AccountProof {
851 pub const fn new(address: Address) -> Self {
853 Self {
854 address,
855 info: None,
856 proof: Vec::new(),
857 storage_root: EMPTY_ROOT_HASH,
858 storage_proofs: Vec::new(),
859 }
860 }
861
862 pub fn verify(&self, root: B256) -> Result<(), ProofVerificationError> {
864 for storage_proof in &self.storage_proofs {
866 storage_proof.verify(self.storage_root)?;
867 }
868
869 let expected = if self.info.is_none() && self.storage_root == EMPTY_ROOT_HASH {
871 None
872 } else {
873 Some(alloy_rlp::encode(
874 self.info.unwrap_or_default().into_trie_account(self.storage_root),
875 ))
876 };
877 let nibbles = Nibbles::unpack(keccak256(self.address));
878 verify_proof(root, nibbles, expected, &self.proof)
879 }
880}
881
882#[derive(Clone, PartialEq, Eq, Debug)]
884pub struct DecodedAccountProof {
885 pub address: Address,
887 pub info: Option<Account>,
889 pub proof: Vec<TrieNode>,
892 pub storage_root: B256,
894 pub storage_proofs: Vec<DecodedStorageProof>,
896}
897
898impl Default for DecodedAccountProof {
899 fn default() -> Self {
900 Self::new(Address::default())
901 }
902}
903
904impl DecodedAccountProof {
905 pub const fn new(address: Address) -> Self {
907 Self {
908 address,
909 info: None,
910 proof: Vec::new(),
911 storage_root: EMPTY_ROOT_HASH,
912 storage_proofs: Vec::new(),
913 }
914 }
915}
916
917#[derive(Clone, PartialEq, Eq, Default, Debug)]
919#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
920pub struct StorageProof {
921 pub key: B256,
923 pub nibbles: Nibbles,
925 pub value: U256,
927 pub proof: Vec<Bytes>,
930}
931
932impl StorageProof {
933 pub fn new(key: B256) -> Self {
935 let nibbles = Nibbles::unpack(keccak256(key));
936 Self { key, nibbles, ..Default::default() }
937 }
938
939 pub fn new_with_hashed(key: B256, hashed_key: B256) -> Self {
941 Self { key, nibbles: Nibbles::unpack(hashed_key), ..Default::default() }
942 }
943
944 pub fn new_with_nibbles(key: B256, nibbles: Nibbles) -> Self {
946 Self { key, nibbles, ..Default::default() }
947 }
948
949 pub fn with_proof(mut self, proof: Vec<Bytes>) -> Self {
951 self.proof = proof;
952 self
953 }
954
955 pub fn verify(&self, root: B256) -> Result<(), ProofVerificationError> {
957 let expected =
958 if self.value.is_zero() { None } else { Some(encode_fixed_size(&self.value).to_vec()) };
959 verify_proof(root, self.nibbles, expected, &self.proof)
960 }
961}
962
963#[cfg(feature = "eip1186")]
964impl StorageProof {
965 pub fn into_eip1186_proof(
967 self,
968 slot: alloy_serde::JsonStorageKey,
969 ) -> alloy_rpc_types_eth::EIP1186StorageProof {
970 alloy_rpc_types_eth::EIP1186StorageProof {
971 key: slot,
972 value: self.value,
973 proof: normalize_eip1186_empty_trie_proof(self.proof),
974 }
975 }
976
977 pub fn from_eip1186_proof(storage_proof: alloy_rpc_types_eth::EIP1186StorageProof) -> Self {
982 Self {
983 value: storage_proof.value,
984 proof: storage_proof.proof,
985 ..Self::new(storage_proof.key.as_b256())
986 }
987 }
988}
989
990#[cfg(feature = "eip1186")]
991impl From<alloy_rpc_types_eth::EIP1186StorageProof> for StorageProof {
992 fn from(proof: alloy_rpc_types_eth::EIP1186StorageProof) -> Self {
993 Self::from_eip1186_proof(proof)
994 }
995}
996
997#[derive(Clone, PartialEq, Eq, Default, Debug)]
999pub struct DecodedStorageProof {
1000 pub key: B256,
1002 pub nibbles: Nibbles,
1004 pub value: U256,
1006 pub proof: Vec<TrieNode>,
1009}
1010
1011impl DecodedStorageProof {
1012 pub fn new(key: B256) -> Self {
1014 let nibbles = Nibbles::unpack(keccak256(key));
1015 Self { key, nibbles, ..Default::default() }
1016 }
1017
1018 pub fn new_with_hashed(key: B256, hashed_key: B256) -> Self {
1020 Self { key, nibbles: Nibbles::unpack(hashed_key), ..Default::default() }
1021 }
1022
1023 pub fn new_with_nibbles(key: B256, nibbles: Nibbles) -> Self {
1025 Self { key, nibbles, ..Default::default() }
1026 }
1027
1028 pub fn with_proof(mut self, proof: Vec<TrieNode>) -> Self {
1030 self.proof = proof;
1031 self
1032 }
1033}
1034
1035#[cfg(any(test, feature = "test-utils"))]
1038pub mod triehash {
1039 use alloy_primitives::{keccak256, B256};
1040 use alloy_rlp::RlpEncodable;
1041 use hash_db::Hasher;
1042 use plain_hasher::PlainHasher;
1043
1044 #[derive(Default, Debug, Clone, PartialEq, Eq, RlpEncodable)]
1046 #[non_exhaustive]
1047 pub struct KeccakHasher;
1048
1049 #[cfg(any(test, feature = "test-utils"))]
1050 impl Hasher for KeccakHasher {
1051 type Out = B256;
1052 type StdHasher = PlainHasher;
1053
1054 const LENGTH: usize = 32;
1055
1056 fn hash(x: &[u8]) -> Self::Out {
1057 keccak256(x)
1058 }
1059 }
1060}
1061
1062#[cfg(test)]
1063mod tests {
1064 use super::*;
1065
1066 #[test]
1067 fn test_multiproof_extend_account_proofs() {
1068 let mut proof1 = MultiProof::default();
1069 let mut proof2 = MultiProof::default();
1070
1071 let addr1 = B256::random();
1072 let addr2 = B256::random();
1073
1074 proof1.account_subtree.insert(
1075 Nibbles::unpack(addr1),
1076 alloy_rlp::encode_fixed_size(&U256::from(42)).to_vec().into(),
1077 );
1078 proof2.account_subtree.insert(
1079 Nibbles::unpack(addr2),
1080 alloy_rlp::encode_fixed_size(&U256::from(43)).to_vec().into(),
1081 );
1082
1083 proof1.extend(proof2);
1084
1085 assert!(proof1.account_subtree.contains_key(&Nibbles::unpack(addr1)));
1086 assert!(proof1.account_subtree.contains_key(&Nibbles::unpack(addr2)));
1087 }
1088
1089 #[test]
1090 fn test_multiproof_extend_storage_proofs() {
1091 let mut proof1 = MultiProof::default();
1092 let mut proof2 = MultiProof::default();
1093
1094 let addr = B256::random();
1095 let root = B256::random();
1096
1097 let mut subtree1 = ProofNodes::default();
1098 subtree1.insert(
1099 Nibbles::from_nibbles(vec![0]),
1100 alloy_rlp::encode_fixed_size(&U256::from(42)).to_vec().into(),
1101 );
1102 proof1.storages.insert(
1103 addr,
1104 StorageMultiProof {
1105 root,
1106 subtree: subtree1,
1107 branch_node_masks: BranchNodeMasksMap::default(),
1108 },
1109 );
1110
1111 let mut subtree2 = ProofNodes::default();
1112 subtree2.insert(
1113 Nibbles::from_nibbles(vec![1]),
1114 alloy_rlp::encode_fixed_size(&U256::from(43)).to_vec().into(),
1115 );
1116 proof2.storages.insert(
1117 addr,
1118 StorageMultiProof {
1119 root,
1120 subtree: subtree2,
1121 branch_node_masks: BranchNodeMasksMap::default(),
1122 },
1123 );
1124
1125 proof1.extend(proof2);
1126
1127 let storage = proof1.storages.get(&addr).unwrap();
1128 assert_eq!(storage.root, root);
1129 assert!(storage.subtree.contains_key(&Nibbles::from_nibbles(vec![0])));
1130 assert!(storage.subtree.contains_key(&Nibbles::from_nibbles(vec![1])));
1131 }
1132
1133 #[test]
1134 fn test_multi_proof_retain_difference() {
1135 let mut empty = MultiProofTargets::default();
1136 empty.retain_difference(&Default::default());
1137 assert!(empty.is_empty());
1138
1139 let targets = MultiProofTargets::accounts((0..10).map(B256::with_last_byte));
1140
1141 let mut diffed = targets.clone();
1142 diffed.retain_difference(&MultiProofTargets::account(B256::with_last_byte(11)));
1143 assert_eq!(diffed, targets);
1144
1145 diffed.retain_difference(&MultiProofTargets::accounts((0..5).map(B256::with_last_byte)));
1146 assert_eq!(diffed, MultiProofTargets::accounts((5..10).map(B256::with_last_byte)));
1147
1148 diffed.retain_difference(&targets);
1149 assert!(diffed.is_empty());
1150
1151 let mut targets = MultiProofTargets::default();
1152 let (account1, account2, account3) =
1153 (1..=3).map(B256::with_last_byte).collect_tuple().unwrap();
1154 let account2_slots = (1..5).map(B256::with_last_byte).collect::<B256Set>();
1155 targets.insert(account1, B256Set::from_iter([B256::with_last_byte(1)]));
1156 targets.insert(account2, account2_slots.clone());
1157 targets.insert(account3, B256Set::from_iter([B256::with_last_byte(1)]));
1158
1159 let mut diffed = targets.clone();
1160 diffed.retain_difference(&MultiProofTargets::accounts((1..=3).map(B256::with_last_byte)));
1161 assert_eq!(diffed, targets);
1162
1163 let mut account2_slots_expected_len = account2_slots.len();
1165 for slot in account2_slots.iter().skip(1) {
1166 diffed.retain_difference(&MultiProofTargets::account_with_slots(account2, [*slot]));
1167 account2_slots_expected_len -= 1;
1168 assert_eq!(
1169 diffed.get(&account2).map(|slots| slots.len()),
1170 Some(account2_slots_expected_len)
1171 );
1172 }
1173
1174 diffed.retain_difference(&targets);
1175 assert!(diffed.is_empty());
1176 }
1177
1178 #[test]
1179 fn test_multi_proof_retain_difference_no_overlap() {
1180 let mut targets = MultiProofTargets::default();
1181
1182 let (addr1, addr2) = (B256::random(), B256::random());
1184 let (slot1, slot2) = (B256::random(), B256::random());
1185 targets.insert(addr1, std::iter::once(slot1).collect());
1186 targets.insert(addr2, std::iter::once(slot2).collect());
1187
1188 let mut retained = targets.clone();
1189 retained.retain_difference(&Default::default());
1190 assert_eq!(retained, targets);
1191
1192 let mut other_targets = MultiProofTargets::default();
1194 let addr3 = B256::random();
1195 let slot3 = B256::random();
1196 other_targets.insert(addr3, B256Set::from_iter([slot3]));
1197
1198 let mut retained = targets.clone();
1201 retained.retain_difference(&other_targets);
1202 assert_eq!(retained, targets);
1203 }
1204
1205 #[test]
1206 fn test_get_prefetch_proof_targets_remove_subset() {
1207 let mut targets = MultiProofTargets::default();
1209 let (addr1, addr2) = (B256::random(), B256::random());
1210 let (slot1, slot2) = (B256::random(), B256::random());
1211 targets.insert(addr1, B256Set::from_iter([slot1]));
1212 targets.insert(addr2, B256Set::from_iter([slot2]));
1213
1214 let other_targets = MultiProofTargets::account_with_slots(addr1, [slot1]);
1216
1217 let mut retained = targets.clone();
1218 retained.retain_difference(&other_targets);
1219
1220 assert_eq!(retained.len(), 1);
1222 assert!(!retained.contains_key(&addr1));
1223 assert!(retained.contains_key(&addr2));
1224
1225 let slot3 = B256::random();
1227 targets.get_mut(&addr1).unwrap().insert(slot3);
1228
1229 let mut retained = targets.clone();
1230 retained.retain_difference(&other_targets);
1231
1232 assert_eq!(retained.len(), 2);
1235 assert!(retained.contains_key(&addr1));
1236 assert_eq!(retained.get(&addr1), Some(&B256Set::from_iter([slot3])));
1237 assert!(retained.contains_key(&addr2));
1238 assert_eq!(retained.get(&addr2), Some(&B256Set::from_iter([slot2])));
1239 }
1240
1241 #[test]
1242 #[cfg(feature = "eip1186")]
1243 fn eip_1186_roundtrip() {
1244 let mut acc = AccountProof {
1245 address: Address::random(),
1246 info: Some(
1247 Account { nonce: 100, balance: U256::ZERO, bytecode_hash: Some(KECCAK_EMPTY) },
1249 ),
1250 proof: vec![],
1251 storage_root: B256::ZERO,
1252 storage_proofs: vec![],
1253 };
1254
1255 let rpc_proof = acc.clone().into_eip1186_response(Vec::new());
1256 let inverse: AccountProof = rpc_proof.into();
1257 assert_eq!(acc, inverse);
1258
1259 acc.info.as_mut().unwrap().nonce = 0;
1261 let rpc_proof = acc.clone().into_eip1186_response(Vec::new());
1262 let inverse: AccountProof = rpc_proof.into();
1263 acc.info.take();
1264 acc.storage_root = EMPTY_ROOT_HASH;
1265 assert_eq!(acc, inverse);
1266 }
1267
1268 #[test]
1269 #[cfg(feature = "eip1186")]
1270 fn from_eip1186_proof_accepts_geth_zero_hashes() {
1271 let geth_proof = alloy_rpc_types_eth::EIP1186AccountProofResponse {
1276 address: Address::random(),
1277 balance: U256::ZERO,
1278 code_hash: B256::ZERO,
1279 nonce: 0,
1280 storage_hash: B256::ZERO,
1281 account_proof: vec![],
1282 storage_proof: vec![],
1283 };
1284
1285 let acc: AccountProof = geth_proof.into();
1286 assert!(acc.info.is_none());
1288 assert_eq!(acc.storage_root, EMPTY_ROOT_HASH);
1289 }
1290
1291 #[test]
1292 #[cfg(feature = "eip1186")]
1293 fn from_eip1186_proof_accepts_empty_hashes() {
1294 let proof = alloy_rpc_types_eth::EIP1186AccountProofResponse {
1295 address: Address::random(),
1296 balance: U256::ZERO,
1297 code_hash: KECCAK_EMPTY,
1298 nonce: 0,
1299 storage_hash: EMPTY_ROOT_HASH,
1300 account_proof: vec![],
1301 storage_proof: vec![],
1302 };
1303
1304 let acc: AccountProof = proof.into();
1305 assert!(acc.info.is_none());
1306 assert_eq!(acc.storage_root, EMPTY_ROOT_HASH);
1307 }
1308
1309 #[test]
1310 #[cfg(feature = "eip1186")]
1311 fn into_eip1186_response_zero_empty_account() {
1312 let acc = AccountProof {
1314 address: Address::random(),
1315 info: None,
1316 proof: vec![],
1317 storage_root: EMPTY_ROOT_HASH,
1318 storage_proofs: vec![],
1319 };
1320
1321 let rpc_default = acc.clone().into_eip1186_response(Vec::new());
1323 assert_eq!(rpc_default.code_hash, KECCAK_EMPTY);
1324 assert_eq!(rpc_default.storage_hash, EMPTY_ROOT_HASH);
1325
1326 let rpc_compat_off = acc.clone().into_eip1186_response_with(Vec::new(), false);
1328 assert_eq!(rpc_compat_off.code_hash, KECCAK_EMPTY);
1329 assert_eq!(rpc_compat_off.storage_hash, EMPTY_ROOT_HASH);
1330
1331 let rpc_compat_on = acc.into_eip1186_response_with(Vec::new(), true);
1333 assert_eq!(rpc_compat_on.code_hash, B256::ZERO);
1334 assert_eq!(rpc_compat_on.storage_hash, B256::ZERO);
1335
1336 let existing_acc = AccountProof {
1338 address: Address::random(),
1339 info: Some(Account {
1340 nonce: 42,
1341 balance: U256::from(100),
1342 bytecode_hash: Some(KECCAK_EMPTY),
1343 }),
1344 proof: vec![],
1345 storage_root: B256::random(),
1346 storage_proofs: vec![],
1347 };
1348 let rpc_existing = existing_acc.clone().into_eip1186_response_with(Vec::new(), true);
1349 assert_eq!(rpc_existing.code_hash, KECCAK_EMPTY);
1350 assert_eq!(rpc_existing.storage_hash, existing_acc.storage_root);
1351 }
1352
1353 #[test]
1354 fn test_multiproof_targets_chunking_length() {
1355 let mut targets = MultiProofTargets::default();
1356 targets.insert(B256::with_last_byte(1), B256Set::default());
1357 targets.insert(
1358 B256::with_last_byte(2),
1359 B256Set::from_iter([B256::with_last_byte(10), B256::with_last_byte(20)]),
1360 );
1361 targets.insert(
1362 B256::with_last_byte(3),
1363 B256Set::from_iter([
1364 B256::with_last_byte(30),
1365 B256::with_last_byte(31),
1366 B256::with_last_byte(32),
1367 ]),
1368 );
1369
1370 let chunking_length = targets.chunking_length();
1371 for size in 1..=targets.clone().chunks(1).count() {
1372 let chunk_count = targets.clone().chunks(size).count();
1373 let expected_count = chunking_length.div_ceil(size);
1374 assert_eq!(
1375 chunk_count, expected_count,
1376 "chunking_length: {}, size: {}",
1377 chunking_length, size
1378 );
1379 }
1380 }
1381
1382 #[test]
1383 fn test_nonempty_storage_trie_returns_nonempty_proof() {
1384 let slot = B256::with_last_byte(1);
1385 let nibbles = Nibbles::unpack(keccak256(slot));
1386 let value = U256::from(999);
1387 let leaf = alloy_trie::nodes::LeafNode::new(nibbles, encode_fixed_size(&value).to_vec());
1388 let mut encoded = vec![];
1389 alloy_rlp::Encodable::encode(&leaf, &mut encoded);
1390
1391 let mut subtree = ProofNodes::default();
1392 subtree.insert(nibbles, encoded.into());
1393
1394 let multiproof = StorageMultiProof {
1395 root: B256::with_last_byte(0xFF),
1396 subtree,
1397 branch_node_masks: BranchNodeMasksMap::default(),
1398 };
1399
1400 let proof = multiproof.storage_proof(slot).unwrap();
1401 assert!(!proof.proof.is_empty(), "non-empty trie must return non-empty proof");
1402 assert_eq!(proof.value, value);
1403 }
1404
1405 #[cfg(feature = "eip1186")]
1406 #[test]
1407 fn eip1186_response_normalizes_empty_trie_proof() {
1408 let slot = B256::with_last_byte(1);
1409 let sentinel = || vec![Bytes::from([EMPTY_STRING_CODE])];
1410
1411 let account = AccountProof {
1413 address: Address::ZERO,
1414 info: None,
1415 proof: sentinel(),
1416 storage_root: EMPTY_ROOT_HASH,
1417 storage_proofs: vec![StorageProof::new(slot).with_proof(sentinel())],
1418 };
1419
1420 let resp = account.into_eip1186_response(vec![alloy_serde::JsonStorageKey::from(slot)]);
1421
1422 assert!(
1423 resp.account_proof.is_empty(),
1424 "empty account trie must yield empty account_proof, got {:?}",
1425 resp.account_proof
1426 );
1427 assert_eq!(resp.storage_proof.len(), 1);
1428 assert!(
1429 resp.storage_proof[0].proof.is_empty(),
1430 "empty storage trie must yield empty storage proof, got {:?}",
1431 resp.storage_proof[0].proof
1432 );
1433 }
1434
1435 #[cfg(feature = "eip1186")]
1436 #[test]
1437 fn eip1186_response_keeps_nonempty_proof() {
1438 let multi = vec![Bytes::from([0x01, 0x02]), Bytes::from([0x03])];
1441 let single_non_sentinel = vec![Bytes::from([0xf8, 0x44])];
1442
1443 let account = AccountProof {
1444 address: Address::ZERO,
1445 info: None,
1446 proof: multi.clone(),
1447 storage_root: EMPTY_ROOT_HASH,
1448 storage_proofs: vec![
1449 StorageProof::new(B256::with_last_byte(1)).with_proof(single_non_sentinel.clone())
1450 ],
1451 };
1452
1453 let resp = account.into_eip1186_response(vec![alloy_serde::JsonStorageKey::from(
1454 B256::with_last_byte(1),
1455 )]);
1456
1457 assert_eq!(resp.account_proof, multi);
1458 assert_eq!(resp.storage_proof[0].proof, single_non_sentinel);
1459 }
1460}