Skip to main content

reth_trie_common/
proofs.rs

1//! Merkle trie proofs.
2
3use crate::{BranchNodeMasksMap, Nibbles, ProofTrieNode, 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/// Proof targets map.
22#[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    /// Creates an empty `MultiProofTargets` with at least the specified capacity.
33    pub fn with_capacity(capacity: usize) -> Self {
34        Self(B256Map::with_capacity_and_hasher(capacity, Default::default()))
35    }
36
37    /// Create `MultiProofTargets` with a single account as a target.
38    pub fn account(hashed_address: B256) -> Self {
39        Self::accounts([hashed_address])
40    }
41
42    /// Create `MultiProofTargets` with a single account and slots as targets.
43    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    /// Create `MultiProofTargets` only from accounts.
51    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    /// Retains the targets representing the difference,
56    /// i.e., the values that are in `self` but not in `other`.
57    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    /// Extend multi proof targets with contents of other.
69    pub fn extend(&mut self, other: Self) {
70        self.extend_inner(Cow::Owned(other));
71    }
72
73    /// Extend multi proof targets with contents of other.
74    ///
75    /// Slightly less efficient than [`Self::extend`], but preferred to `extend(other.clone())`.
76    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    /// Returns an iterator that yields chunks of the specified size.
87    ///
88    /// See [`ChunkedMultiProofTargets`] for more information.
89    pub fn chunks(self, size: usize) -> ChunkedMultiProofTargets {
90        ChunkedMultiProofTargets::new(self, size)
91    }
92
93    /// Returns the number of items that will be considered during chunking in `[Self::chunks]`.
94    pub fn chunking_length(&self) -> usize {
95        self.values().map(|slots| 1 + slots.len().saturating_sub(1)).sum::<usize>()
96    }
97}
98
99/// An iterator that yields chunks of the proof targets of at most `size` account and storage
100/// targets.
101///
102/// For example, for the following proof targets:
103/// ```text
104/// - 0x1: [0x10, 0x20, 0x30]
105/// - 0x2: [0x40]
106/// - 0x3: []
107/// ```
108///
109/// and `size = 2`, the iterator will yield the following chunks:
110/// ```text
111/// - { 0x1: [0x10, 0x20] }
112/// - { 0x1: [0x30], 0x2: [0x40] }
113/// - { 0x3: [] }
114/// ```
115///
116/// It follows two rules:
117/// - If account has associated storage slots, each storage slot is counted towards the chunk size.
118/// - If account has no associated storage slots, the account is counted towards the chunk size.
119#[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                    // If the account has no storage slots, we still need to yield the account
132                    // address with empty storage slots. `None` here means that
133                    // there's no storage slot to fetch.
134                    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/// The state multiproof of target accounts and multiproofs of their storage tries.
170/// Multiproof is effectively a state subtrie that only contains the nodes
171/// in the paths of target accounts.
172#[derive(Clone, Default, Debug, PartialEq, Eq)]
173pub struct MultiProof {
174    /// State trie multiproof for requested accounts.
175    pub account_subtree: ProofNodes,
176    /// Consolidated branch node masks (`hash_mask`, `tree_mask`) for each path in the account
177    /// proof.
178    pub branch_node_masks: BranchNodeMasksMap,
179    /// Storage trie multiproofs.
180    pub storages: B256Map<StorageMultiProof>,
181}
182
183impl MultiProof {
184    /// Returns true if the multiproof is empty.
185    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    /// Return the account proof nodes for the given account path.
192    pub fn account_proof_nodes(&self, path: &Nibbles) -> Vec<(Nibbles, Bytes)> {
193        self.account_subtree.matching_nodes_sorted(path)
194    }
195
196    /// Return the storage proof nodes for the given storage slots of the account path.
197    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    /// Construct the account proof from the multiproof.
217    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        // Retrieve the account proof.
226        let proof = self
227            .account_proof_nodes(&nibbles)
228            .into_iter()
229            .map(|(_, node)| node)
230            .collect::<Vec<_>>();
231
232        // Inspect the last node in the proof. If it's a leaf node with matching suffix,
233        // then the node contains the encoded trie account.
234        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        // Retrieve proofs for requested storage slots.
250        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    /// Extends this multiproof with another one, merging both account and storage
265    /// proofs.
266    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    /// Create a [`MultiProof`] from a [`StorageMultiProof`].
292    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/// This is a type of [`MultiProof`] that uses decoded proofs, meaning these proofs are stored as a
301/// collection of [`TrieNode`]s instead of RLP-encoded bytes.
302#[derive(Clone, Default, Debug, PartialEq, Eq)]
303pub struct DecodedMultiProof {
304    /// State trie multiproof for requested accounts.
305    pub account_subtree: DecodedProofNodes,
306    /// Consolidated branch node masks (`hash_mask`, `tree_mask`) for each path in the account
307    /// proof.
308    pub branch_node_masks: BranchNodeMasksMap,
309    /// Storage trie multiproofs.
310    pub storages: B256Map<DecodedStorageMultiProof>,
311}
312
313impl DecodedMultiProof {
314    /// Returns true if the multiproof is empty.
315    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    /// Return the account proof nodes for the given account path.
322    pub fn account_proof_nodes(&self, path: &Nibbles) -> Vec<(Nibbles, TrieNode)> {
323        self.account_subtree.matching_nodes_sorted(path)
324    }
325
326    /// Return the storage proof nodes for the given storage slots of the account path.
327    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    /// Construct the account proof from the multiproof.
347    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        // Retrieve the account proof.
356        let proof = self
357            .account_proof_nodes(&nibbles)
358            .into_iter()
359            .map(|(_, node)| node)
360            .collect::<Vec<_>>();
361
362        // Inspect the last node in the proof. If it's a leaf node with matching suffix,
363        // then the node contains the encoded trie account.
364        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        // Retrieve proofs for requested storage slots.
379        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    /// Extends this multiproof with another one, merging both account and storage
394    /// proofs.
395    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    /// Create a [`DecodedMultiProof`] from a [`DecodedStorageMultiProof`].
421    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/// V2 decoded multiproof which contains the results of both account and storage V2 proof
447/// calculations.
448#[derive(Clone, Debug, PartialEq, Eq, Default)]
449pub struct DecodedMultiProofV2 {
450    /// Account trie proof nodes
451    pub account_proofs: Vec<ProofTrieNode>,
452    /// Storage trie proof nodes indexed by account
453    pub storage_proofs: B256Map<Vec<ProofTrieNode>>,
454}
455
456impl DecodedMultiProofV2 {
457    /// Returns true if there are no proofs
458    pub fn is_empty(&self) -> bool {
459        self.account_proofs.is_empty() && self.storage_proofs.is_empty()
460    }
461
462    /// Appends the given multiproof's data to this one.
463    ///
464    /// This implementation does not deduplicate redundant proofs.
465    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
480/// The merkle multiproof of storage trie.
481#[derive(Clone, Debug, PartialEq, Eq)]
482pub struct StorageMultiProof {
483    /// Storage trie root.
484    pub root: B256,
485    /// Storage multiproof for requested slots.
486    pub subtree: ProofNodes,
487    /// Consolidated branch node masks (`hash_mask`, `tree_mask`) for each path in the storage
488    /// proof.
489    pub branch_node_masks: BranchNodeMasksMap,
490}
491
492impl StorageMultiProof {
493    /// Create new storage multiproof for empty trie.
494    pub fn empty() -> Self {
495        Self {
496            root: EMPTY_ROOT_HASH,
497            subtree: ProofNodes::from_iter([(
498                Nibbles::default(),
499                Bytes::from([EMPTY_STRING_CODE]),
500            )]),
501            branch_node_masks: BranchNodeMasksMap::default(),
502        }
503    }
504
505    /// Return storage proofs for the target storage slot (unhashed).
506    pub fn storage_proof(&self, slot: B256) -> Result<StorageProof, alloy_rlp::Error> {
507        let nibbles = Nibbles::unpack(keccak256(slot));
508
509        // Retrieve the storage proof.
510        let proof = self
511            .subtree
512            .matching_nodes_iter(&nibbles)
513            .sorted_by(|a, b| a.0.cmp(b.0))
514            .map(|(_, node)| node.clone())
515            .collect::<Vec<_>>();
516
517        // Inspect the last node in the proof. If it's a leaf node with matching suffix,
518        // then the node contains the encoded slot value.
519        let value = 'value: {
520            if let Some(last) = proof.last() &&
521                let TrieNode::Leaf(leaf) = TrieNode::decode(&mut &last[..])? &&
522                nibbles.ends_with(&leaf.key)
523            {
524                break 'value U256::decode(&mut &leaf.value[..])?
525            }
526            U256::ZERO
527        };
528
529        Ok(StorageProof { key: slot, nibbles, value, proof })
530    }
531}
532
533/// The decoded merkle multiproof for a storage trie.
534#[derive(Clone, Debug, PartialEq, Eq)]
535pub struct DecodedStorageMultiProof {
536    /// Storage trie root.
537    pub root: B256,
538    /// Storage multiproof for requested slots.
539    pub subtree: DecodedProofNodes,
540    /// Consolidated branch node masks (`hash_mask`, `tree_mask`) for each path in the storage
541    /// proof.
542    pub branch_node_masks: BranchNodeMasksMap,
543}
544
545impl DecodedStorageMultiProof {
546    /// Create new storage multiproof for empty trie.
547    pub fn empty() -> Self {
548        Self {
549            root: EMPTY_ROOT_HASH,
550            subtree: DecodedProofNodes::from_iter([(Nibbles::default(), TrieNode::EmptyRoot)]),
551            branch_node_masks: BranchNodeMasksMap::default(),
552        }
553    }
554
555    /// Return storage proofs for the target storage slot (unhashed).
556    pub fn storage_proof(&self, slot: B256) -> Result<DecodedStorageProof, alloy_rlp::Error> {
557        let nibbles = Nibbles::unpack(keccak256(slot));
558
559        // Retrieve the storage proof.
560        let proof = self
561            .subtree
562            .matching_nodes_iter(&nibbles)
563            .sorted_by(|a, b| a.0.cmp(b.0))
564            .map(|(_, node)| node.clone())
565            .collect::<Vec<_>>();
566
567        // Inspect the last node in the proof. If it's a leaf node with matching suffix,
568        // then the node contains the encoded slot value.
569        let value = 'value: {
570            if let Some(TrieNode::Leaf(leaf)) = proof.last() &&
571                nibbles.ends_with(&leaf.key)
572            {
573                break 'value U256::decode(&mut &leaf.value[..])?
574            }
575            U256::ZERO
576        };
577
578        Ok(DecodedStorageProof { key: slot, nibbles, value, proof })
579    }
580}
581
582impl TryFrom<StorageMultiProof> for DecodedStorageMultiProof {
583    type Error = alloy_rlp::Error;
584
585    fn try_from(multi_proof: StorageMultiProof) -> Result<Self, Self::Error> {
586        let subtree = DecodedProofNodes::try_from(multi_proof.subtree)?;
587        Ok(Self {
588            root: multi_proof.root,
589            subtree,
590            branch_node_masks: multi_proof.branch_node_masks,
591        })
592    }
593}
594
595/// The merkle proof with the relevant account info.
596#[derive(Clone, PartialEq, Eq, Debug)]
597#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
598#[cfg_attr(any(test, feature = "serde"), serde(rename_all = "camelCase"))]
599pub struct AccountProof {
600    /// The address associated with the account.
601    pub address: Address,
602    /// Account info, if any.
603    pub info: Option<Account>,
604    /// Array of rlp-serialized merkle trie nodes which starting from the root node and
605    /// following the path of the hashed address as key.
606    pub proof: Vec<Bytes>,
607    /// The storage trie root.
608    pub storage_root: B256,
609    /// Array of storage proofs as requested.
610    pub storage_proofs: Vec<StorageProof>,
611}
612
613#[cfg(feature = "eip1186")]
614impl AccountProof {
615    /// Convert into an EIP-1186 account proof response
616    pub fn into_eip1186_response(
617        self,
618        slots: Vec<alloy_serde::JsonStorageKey>,
619    ) -> alloy_rpc_types_eth::EIP1186AccountProofResponse {
620        let info = self.info.unwrap_or_default();
621        alloy_rpc_types_eth::EIP1186AccountProofResponse {
622            address: self.address,
623            balance: info.balance,
624            code_hash: info.get_bytecode_hash(),
625            nonce: info.nonce,
626            storage_hash: self.storage_root,
627            account_proof: self.proof,
628            storage_proof: self
629                .storage_proofs
630                .into_iter()
631                .filter_map(|proof| {
632                    let input_slot = slots.iter().find(|s| s.as_b256() == proof.key)?;
633                    Some(proof.into_eip1186_proof(*input_slot))
634                })
635                .collect(),
636        }
637    }
638
639    /// Converts an
640    /// [`EIP1186AccountProofResponse`](alloy_rpc_types_eth::EIP1186AccountProofResponse) to an
641    /// [`AccountProof`].
642    ///
643    /// This is the inverse of [`Self::into_eip1186_response`]
644    pub fn from_eip1186_proof(proof: alloy_rpc_types_eth::EIP1186AccountProofResponse) -> Self {
645        let alloy_rpc_types_eth::EIP1186AccountProofResponse {
646            nonce,
647            address,
648            balance,
649            code_hash,
650            storage_hash,
651            account_proof,
652            storage_proof,
653            ..
654        } = proof;
655        let storage_proofs = storage_proof.into_iter().map(Into::into).collect();
656
657        let (storage_root, info) = if nonce == 0 &&
658            balance.is_zero() &&
659            storage_hash.is_zero() &&
660            code_hash == KECCAK_EMPTY
661        {
662            // Account does not exist in state. Return `None` here to prevent proof
663            // verification.
664            (EMPTY_ROOT_HASH, None)
665        } else {
666            (storage_hash, Some(Account { nonce, balance, bytecode_hash: code_hash.into() }))
667        };
668
669        Self { address, info, proof: account_proof, storage_root, storage_proofs }
670    }
671}
672
673#[cfg(feature = "eip1186")]
674impl From<alloy_rpc_types_eth::EIP1186AccountProofResponse> for AccountProof {
675    fn from(proof: alloy_rpc_types_eth::EIP1186AccountProofResponse) -> Self {
676        Self::from_eip1186_proof(proof)
677    }
678}
679
680impl Default for AccountProof {
681    fn default() -> Self {
682        Self::new(Address::default())
683    }
684}
685
686impl AccountProof {
687    /// Create new account proof entity.
688    pub const fn new(address: Address) -> Self {
689        Self {
690            address,
691            info: None,
692            proof: Vec::new(),
693            storage_root: EMPTY_ROOT_HASH,
694            storage_proofs: Vec::new(),
695        }
696    }
697
698    /// Verify the storage proofs and account proof against the provided state root.
699    pub fn verify(&self, root: B256) -> Result<(), ProofVerificationError> {
700        // Verify storage proofs.
701        for storage_proof in &self.storage_proofs {
702            storage_proof.verify(self.storage_root)?;
703        }
704
705        // Verify the account proof.
706        let expected = if self.info.is_none() && self.storage_root == EMPTY_ROOT_HASH {
707            None
708        } else {
709            Some(alloy_rlp::encode(
710                self.info.unwrap_or_default().into_trie_account(self.storage_root),
711            ))
712        };
713        let nibbles = Nibbles::unpack(keccak256(self.address));
714        verify_proof(root, nibbles, expected, &self.proof)
715    }
716}
717
718/// The merkle proof with the relevant account info.
719#[derive(Clone, PartialEq, Eq, Debug)]
720pub struct DecodedAccountProof {
721    /// The address associated with the account.
722    pub address: Address,
723    /// Account info.
724    pub info: Option<Account>,
725    /// Array of merkle trie nodes which starting from the root node and following the path of the
726    /// hashed address as key.
727    pub proof: Vec<TrieNode>,
728    /// The storage trie root.
729    pub storage_root: B256,
730    /// Array of storage proofs as requested.
731    pub storage_proofs: Vec<DecodedStorageProof>,
732}
733
734impl Default for DecodedAccountProof {
735    fn default() -> Self {
736        Self::new(Address::default())
737    }
738}
739
740impl DecodedAccountProof {
741    /// Create new account proof entity.
742    pub const fn new(address: Address) -> Self {
743        Self {
744            address,
745            info: None,
746            proof: Vec::new(),
747            storage_root: EMPTY_ROOT_HASH,
748            storage_proofs: Vec::new(),
749        }
750    }
751}
752
753/// The merkle proof of the storage entry.
754#[derive(Clone, PartialEq, Eq, Default, Debug)]
755#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
756pub struct StorageProof {
757    /// The raw storage key.
758    pub key: B256,
759    /// The hashed storage key nibbles.
760    pub nibbles: Nibbles,
761    /// The storage value.
762    pub value: U256,
763    /// Array of rlp-serialized merkle trie nodes which starting from the storage root node and
764    /// following the path of the hashed storage slot as key.
765    pub proof: Vec<Bytes>,
766}
767
768impl StorageProof {
769    /// Create new storage proof from the storage slot.
770    pub fn new(key: B256) -> Self {
771        let nibbles = Nibbles::unpack(keccak256(key));
772        Self { key, nibbles, ..Default::default() }
773    }
774
775    /// Create new storage proof from the storage slot and its pre-hashed image.
776    pub fn new_with_hashed(key: B256, hashed_key: B256) -> Self {
777        Self { key, nibbles: Nibbles::unpack(hashed_key), ..Default::default() }
778    }
779
780    /// Create new storage proof from the storage slot and its pre-hashed image.
781    pub fn new_with_nibbles(key: B256, nibbles: Nibbles) -> Self {
782        Self { key, nibbles, ..Default::default() }
783    }
784
785    /// Set proof nodes on storage proof.
786    pub fn with_proof(mut self, proof: Vec<Bytes>) -> Self {
787        self.proof = proof;
788        self
789    }
790
791    /// Verify the proof against the provided storage root.
792    pub fn verify(&self, root: B256) -> Result<(), ProofVerificationError> {
793        let expected =
794            if self.value.is_zero() { None } else { Some(encode_fixed_size(&self.value).to_vec()) };
795        verify_proof(root, self.nibbles, expected, &self.proof)
796    }
797}
798
799#[cfg(feature = "eip1186")]
800impl StorageProof {
801    /// Convert into an EIP-1186 storage proof
802    pub fn into_eip1186_proof(
803        self,
804        slot: alloy_serde::JsonStorageKey,
805    ) -> alloy_rpc_types_eth::EIP1186StorageProof {
806        alloy_rpc_types_eth::EIP1186StorageProof { key: slot, value: self.value, proof: self.proof }
807    }
808
809    /// Convert from an
810    /// [`EIP1186StorageProof`](alloy_rpc_types_eth::EIP1186StorageProof)
811    ///
812    /// This is the inverse of [`Self::into_eip1186_proof`].
813    pub fn from_eip1186_proof(storage_proof: alloy_rpc_types_eth::EIP1186StorageProof) -> Self {
814        Self {
815            value: storage_proof.value,
816            proof: storage_proof.proof,
817            ..Self::new(storage_proof.key.as_b256())
818        }
819    }
820}
821
822#[cfg(feature = "eip1186")]
823impl From<alloy_rpc_types_eth::EIP1186StorageProof> for StorageProof {
824    fn from(proof: alloy_rpc_types_eth::EIP1186StorageProof) -> Self {
825        Self::from_eip1186_proof(proof)
826    }
827}
828
829/// The merkle proof of the storage entry, using decoded proofs.
830#[derive(Clone, PartialEq, Eq, Default, Debug)]
831pub struct DecodedStorageProof {
832    /// The raw storage key.
833    pub key: B256,
834    /// The hashed storage key nibbles.
835    pub nibbles: Nibbles,
836    /// The storage value.
837    pub value: U256,
838    /// Array of merkle trie nodes which starting from the storage root node and following the path
839    /// of the hashed storage slot as key.
840    pub proof: Vec<TrieNode>,
841}
842
843impl DecodedStorageProof {
844    /// Create new storage proof from the storage slot.
845    pub fn new(key: B256) -> Self {
846        let nibbles = Nibbles::unpack(keccak256(key));
847        Self { key, nibbles, ..Default::default() }
848    }
849
850    /// Create new storage proof from the storage slot and its pre-hashed image.
851    pub fn new_with_hashed(key: B256, hashed_key: B256) -> Self {
852        Self { key, nibbles: Nibbles::unpack(hashed_key), ..Default::default() }
853    }
854
855    /// Create new storage proof from the storage slot and its pre-hashed image.
856    pub fn new_with_nibbles(key: B256, nibbles: Nibbles) -> Self {
857        Self { key, nibbles, ..Default::default() }
858    }
859
860    /// Set proof nodes on storage proof.
861    pub fn with_proof(mut self, proof: Vec<TrieNode>) -> Self {
862        self.proof = proof;
863        self
864    }
865}
866
867/// Implementation of hasher using our keccak256 hashing function
868/// for compatibility with `triehash` crate.
869#[cfg(any(test, feature = "test-utils"))]
870pub mod triehash {
871    use alloy_primitives::{keccak256, B256};
872    use alloy_rlp::RlpEncodable;
873    use hash_db::Hasher;
874    use plain_hasher::PlainHasher;
875
876    /// A [Hasher] that calculates a keccak256 hash of the given data.
877    #[derive(Default, Debug, Clone, PartialEq, Eq, RlpEncodable)]
878    #[non_exhaustive]
879    pub struct KeccakHasher;
880
881    #[cfg(any(test, feature = "test-utils"))]
882    impl Hasher for KeccakHasher {
883        type Out = B256;
884        type StdHasher = PlainHasher;
885
886        const LENGTH: usize = 32;
887
888        fn hash(x: &[u8]) -> Self::Out {
889            keccak256(x)
890        }
891    }
892}
893
894#[cfg(test)]
895mod tests {
896    use super::*;
897
898    #[test]
899    fn test_multiproof_extend_account_proofs() {
900        let mut proof1 = MultiProof::default();
901        let mut proof2 = MultiProof::default();
902
903        let addr1 = B256::random();
904        let addr2 = B256::random();
905
906        proof1.account_subtree.insert(
907            Nibbles::unpack(addr1),
908            alloy_rlp::encode_fixed_size(&U256::from(42)).to_vec().into(),
909        );
910        proof2.account_subtree.insert(
911            Nibbles::unpack(addr2),
912            alloy_rlp::encode_fixed_size(&U256::from(43)).to_vec().into(),
913        );
914
915        proof1.extend(proof2);
916
917        assert!(proof1.account_subtree.contains_key(&Nibbles::unpack(addr1)));
918        assert!(proof1.account_subtree.contains_key(&Nibbles::unpack(addr2)));
919    }
920
921    #[test]
922    fn test_multiproof_extend_storage_proofs() {
923        let mut proof1 = MultiProof::default();
924        let mut proof2 = MultiProof::default();
925
926        let addr = B256::random();
927        let root = B256::random();
928
929        let mut subtree1 = ProofNodes::default();
930        subtree1.insert(
931            Nibbles::from_nibbles(vec![0]),
932            alloy_rlp::encode_fixed_size(&U256::from(42)).to_vec().into(),
933        );
934        proof1.storages.insert(
935            addr,
936            StorageMultiProof {
937                root,
938                subtree: subtree1,
939                branch_node_masks: BranchNodeMasksMap::default(),
940            },
941        );
942
943        let mut subtree2 = ProofNodes::default();
944        subtree2.insert(
945            Nibbles::from_nibbles(vec![1]),
946            alloy_rlp::encode_fixed_size(&U256::from(43)).to_vec().into(),
947        );
948        proof2.storages.insert(
949            addr,
950            StorageMultiProof {
951                root,
952                subtree: subtree2,
953                branch_node_masks: BranchNodeMasksMap::default(),
954            },
955        );
956
957        proof1.extend(proof2);
958
959        let storage = proof1.storages.get(&addr).unwrap();
960        assert_eq!(storage.root, root);
961        assert!(storage.subtree.contains_key(&Nibbles::from_nibbles(vec![0])));
962        assert!(storage.subtree.contains_key(&Nibbles::from_nibbles(vec![1])));
963    }
964
965    #[test]
966    fn test_multi_proof_retain_difference() {
967        let mut empty = MultiProofTargets::default();
968        empty.retain_difference(&Default::default());
969        assert!(empty.is_empty());
970
971        let targets = MultiProofTargets::accounts((0..10).map(B256::with_last_byte));
972
973        let mut diffed = targets.clone();
974        diffed.retain_difference(&MultiProofTargets::account(B256::with_last_byte(11)));
975        assert_eq!(diffed, targets);
976
977        diffed.retain_difference(&MultiProofTargets::accounts((0..5).map(B256::with_last_byte)));
978        assert_eq!(diffed, MultiProofTargets::accounts((5..10).map(B256::with_last_byte)));
979
980        diffed.retain_difference(&targets);
981        assert!(diffed.is_empty());
982
983        let mut targets = MultiProofTargets::default();
984        let (account1, account2, account3) =
985            (1..=3).map(B256::with_last_byte).collect_tuple().unwrap();
986        let account2_slots = (1..5).map(B256::with_last_byte).collect::<B256Set>();
987        targets.insert(account1, B256Set::from_iter([B256::with_last_byte(1)]));
988        targets.insert(account2, account2_slots.clone());
989        targets.insert(account3, B256Set::from_iter([B256::with_last_byte(1)]));
990
991        let mut diffed = targets.clone();
992        diffed.retain_difference(&MultiProofTargets::accounts((1..=3).map(B256::with_last_byte)));
993        assert_eq!(diffed, targets);
994
995        // remove last 3 slots for account 2
996        let mut account2_slots_expected_len = account2_slots.len();
997        for slot in account2_slots.iter().skip(1) {
998            diffed.retain_difference(&MultiProofTargets::account_with_slots(account2, [*slot]));
999            account2_slots_expected_len -= 1;
1000            assert_eq!(
1001                diffed.get(&account2).map(|slots| slots.len()),
1002                Some(account2_slots_expected_len)
1003            );
1004        }
1005
1006        diffed.retain_difference(&targets);
1007        assert!(diffed.is_empty());
1008    }
1009
1010    #[test]
1011    fn test_multi_proof_retain_difference_no_overlap() {
1012        let mut targets = MultiProofTargets::default();
1013
1014        // populate some targets
1015        let (addr1, addr2) = (B256::random(), B256::random());
1016        let (slot1, slot2) = (B256::random(), B256::random());
1017        targets.insert(addr1, std::iter::once(slot1).collect());
1018        targets.insert(addr2, std::iter::once(slot2).collect());
1019
1020        let mut retained = targets.clone();
1021        retained.retain_difference(&Default::default());
1022        assert_eq!(retained, targets);
1023
1024        // add a different addr and slot to fetched proof targets
1025        let mut other_targets = MultiProofTargets::default();
1026        let addr3 = B256::random();
1027        let slot3 = B256::random();
1028        other_targets.insert(addr3, B256Set::from_iter([slot3]));
1029
1030        // check that the prefetch proof targets are the same because the fetched proof targets
1031        // don't overlap with the prefetch targets
1032        let mut retained = targets.clone();
1033        retained.retain_difference(&other_targets);
1034        assert_eq!(retained, targets);
1035    }
1036
1037    #[test]
1038    fn test_get_prefetch_proof_targets_remove_subset() {
1039        // populate some targets
1040        let mut targets = MultiProofTargets::default();
1041        let (addr1, addr2) = (B256::random(), B256::random());
1042        let (slot1, slot2) = (B256::random(), B256::random());
1043        targets.insert(addr1, B256Set::from_iter([slot1]));
1044        targets.insert(addr2, B256Set::from_iter([slot2]));
1045
1046        // add a subset of the first target to other proof targets
1047        let other_targets = MultiProofTargets::account_with_slots(addr1, [slot1]);
1048
1049        let mut retained = targets.clone();
1050        retained.retain_difference(&other_targets);
1051
1052        // check that the prefetch proof targets do not include the subset
1053        assert_eq!(retained.len(), 1);
1054        assert!(!retained.contains_key(&addr1));
1055        assert!(retained.contains_key(&addr2));
1056
1057        // now add one more slot to the prefetch targets
1058        let slot3 = B256::random();
1059        targets.get_mut(&addr1).unwrap().insert(slot3);
1060
1061        let mut retained = targets.clone();
1062        retained.retain_difference(&other_targets);
1063
1064        // check that the prefetch proof targets do not include the subset
1065        // but include the new slot
1066        assert_eq!(retained.len(), 2);
1067        assert!(retained.contains_key(&addr1));
1068        assert_eq!(retained.get(&addr1), Some(&B256Set::from_iter([slot3])));
1069        assert!(retained.contains_key(&addr2));
1070        assert_eq!(retained.get(&addr2), Some(&B256Set::from_iter([slot2])));
1071    }
1072
1073    #[test]
1074    #[cfg(feature = "eip1186")]
1075    fn eip_1186_roundtrip() {
1076        let mut acc = AccountProof {
1077            address: Address::random(),
1078            info: Some(
1079                // non-empty account
1080                Account { nonce: 100, balance: U256::ZERO, bytecode_hash: Some(KECCAK_EMPTY) },
1081            ),
1082            proof: vec![],
1083            storage_root: B256::ZERO,
1084            storage_proofs: vec![],
1085        };
1086
1087        let rpc_proof = acc.clone().into_eip1186_response(Vec::new());
1088        let inverse: AccountProof = rpc_proof.into();
1089        assert_eq!(acc, inverse);
1090
1091        // make account empty
1092        acc.info.as_mut().unwrap().nonce = 0;
1093        let rpc_proof = acc.clone().into_eip1186_response(Vec::new());
1094        let inverse: AccountProof = rpc_proof.into();
1095        acc.info.take();
1096        acc.storage_root = EMPTY_ROOT_HASH;
1097        assert_eq!(acc, inverse);
1098    }
1099
1100    #[test]
1101    fn test_multiproof_targets_chunking_length() {
1102        let mut targets = MultiProofTargets::default();
1103        targets.insert(B256::with_last_byte(1), B256Set::default());
1104        targets.insert(
1105            B256::with_last_byte(2),
1106            B256Set::from_iter([B256::with_last_byte(10), B256::with_last_byte(20)]),
1107        );
1108        targets.insert(
1109            B256::with_last_byte(3),
1110            B256Set::from_iter([
1111                B256::with_last_byte(30),
1112                B256::with_last_byte(31),
1113                B256::with_last_byte(32),
1114            ]),
1115        );
1116
1117        let chunking_length = targets.chunking_length();
1118        for size in 1..=targets.clone().chunks(1).count() {
1119            let chunk_count = targets.clone().chunks(size).count();
1120            let expected_count = chunking_length.div_ceil(size);
1121            assert_eq!(
1122                chunk_count, expected_count,
1123                "chunking_length: {}, size: {}",
1124                chunking_length, size
1125            );
1126        }
1127    }
1128}