reth_trie/proof/
mod.rs

1use crate::{
2    hashed_cursor::{HashedCursorFactory, HashedStorageCursor},
3    node_iter::{TrieElement, TrieNodeIter},
4    prefix_set::{PrefixSetMut, TriePrefixSetsMut},
5    trie_cursor::TrieCursorFactory,
6    walker::TrieWalker,
7    HashBuilder, Nibbles, TRIE_ACCOUNT_RLP_MAX_SIZE,
8};
9use alloy_primitives::{
10    keccak256,
11    map::{B256HashMap, B256HashSet, HashMap, HashSet},
12    Address, B256,
13};
14use alloy_rlp::{BufMut, Encodable};
15use reth_execution_errors::trie::StateProofError;
16use reth_trie_common::{
17    proof::ProofRetainer, AccountProof, MultiProof, MultiProofTargets, StorageMultiProof,
18    TrieAccount,
19};
20
21mod blinded;
22pub use blinded::*;
23
24/// A struct for generating merkle proofs.
25///
26/// Proof generator adds the target address and slots to the prefix set, enables the proof retainer
27/// on the hash builder and follows the same algorithm as the state root calculator.
28/// See `StateRoot::root` for more info.
29#[derive(Debug)]
30pub struct Proof<T, H> {
31    /// The factory for traversing trie nodes.
32    trie_cursor_factory: T,
33    /// The factory for hashed cursors.
34    hashed_cursor_factory: H,
35    /// A set of prefix sets that have changes.
36    prefix_sets: TriePrefixSetsMut,
37    /// Flag indicating whether to include branch node hash masks in the proof.
38    collect_branch_node_hash_masks: bool,
39}
40
41impl<T, H> Proof<T, H> {
42    /// Create a new [`Proof`] instance.
43    pub fn new(t: T, h: H) -> Self {
44        Self {
45            trie_cursor_factory: t,
46            hashed_cursor_factory: h,
47            prefix_sets: TriePrefixSetsMut::default(),
48            collect_branch_node_hash_masks: false,
49        }
50    }
51
52    /// Set the trie cursor factory.
53    pub fn with_trie_cursor_factory<TF>(self, trie_cursor_factory: TF) -> Proof<TF, H> {
54        Proof {
55            trie_cursor_factory,
56            hashed_cursor_factory: self.hashed_cursor_factory,
57            prefix_sets: self.prefix_sets,
58            collect_branch_node_hash_masks: self.collect_branch_node_hash_masks,
59        }
60    }
61
62    /// Set the hashed cursor factory.
63    pub fn with_hashed_cursor_factory<HF>(self, hashed_cursor_factory: HF) -> Proof<T, HF> {
64        Proof {
65            trie_cursor_factory: self.trie_cursor_factory,
66            hashed_cursor_factory,
67            prefix_sets: self.prefix_sets,
68            collect_branch_node_hash_masks: self.collect_branch_node_hash_masks,
69        }
70    }
71
72    /// Set the prefix sets. They have to be mutable in order to allow extension with proof target.
73    pub fn with_prefix_sets_mut(mut self, prefix_sets: TriePrefixSetsMut) -> Self {
74        self.prefix_sets = prefix_sets;
75        self
76    }
77
78    /// Set the flag indicating whether to include branch node hash masks in the proof.
79    pub const fn with_branch_node_hash_masks(mut self, branch_node_hash_masks: bool) -> Self {
80        self.collect_branch_node_hash_masks = branch_node_hash_masks;
81        self
82    }
83}
84
85impl<T, H> Proof<T, H>
86where
87    T: TrieCursorFactory + Clone,
88    H: HashedCursorFactory + Clone,
89{
90    /// Generate an account proof from intermediate nodes.
91    pub fn account_proof(
92        self,
93        address: Address,
94        slots: &[B256],
95    ) -> Result<AccountProof, StateProofError> {
96        Ok(self
97            .multiproof(
98                HashMap::from_iter([(keccak256(address), slots.iter().map(keccak256).collect())])
99                    .into(),
100            )?
101            .account_proof(address, slots)?)
102    }
103
104    /// Generate a state multiproof according to specified targets.
105    pub fn multiproof(
106        mut self,
107        mut targets: MultiProofTargets,
108    ) -> Result<MultiProof, StateProofError> {
109        let hashed_account_cursor = self.hashed_cursor_factory.hashed_account_cursor()?;
110        let trie_cursor = self.trie_cursor_factory.account_trie_cursor()?;
111
112        // Create the walker.
113        let mut prefix_set = self.prefix_sets.account_prefix_set.clone();
114        prefix_set.extend_keys(targets.keys().map(Nibbles::unpack));
115        let walker = TrieWalker::new(trie_cursor, prefix_set.freeze());
116
117        // Create a hash builder to rebuild the root node since it is not available in the database.
118        let retainer = targets.keys().map(Nibbles::unpack).collect();
119        let mut hash_builder = HashBuilder::default()
120            .with_proof_retainer(retainer)
121            .with_updates(self.collect_branch_node_hash_masks);
122
123        // Initialize all storage multiproofs as empty.
124        // Storage multiproofs for non empty tries will be overwritten if necessary.
125        let mut storages: B256HashMap<_> =
126            targets.keys().map(|key| (*key, StorageMultiProof::empty())).collect();
127        let mut account_rlp = Vec::with_capacity(TRIE_ACCOUNT_RLP_MAX_SIZE);
128        let mut account_node_iter = TrieNodeIter::new(walker, hashed_account_cursor);
129        while let Some(account_node) = account_node_iter.try_next()? {
130            match account_node {
131                TrieElement::Branch(node) => {
132                    hash_builder.add_branch(node.key, node.value, node.children_are_in_trie);
133                }
134                TrieElement::Leaf(hashed_address, account) => {
135                    let proof_targets = targets.remove(&hashed_address);
136                    let leaf_is_proof_target = proof_targets.is_some();
137                    let storage_prefix_set = self
138                        .prefix_sets
139                        .storage_prefix_sets
140                        .remove(&hashed_address)
141                        .unwrap_or_default();
142                    let storage_multiproof = StorageProof::new_hashed(
143                        self.trie_cursor_factory.clone(),
144                        self.hashed_cursor_factory.clone(),
145                        hashed_address,
146                    )
147                    .with_prefix_set_mut(storage_prefix_set)
148                    .with_branch_node_hash_masks(self.collect_branch_node_hash_masks)
149                    .storage_multiproof(proof_targets.unwrap_or_default())?;
150
151                    // Encode account
152                    account_rlp.clear();
153                    let account = TrieAccount::from((account, storage_multiproof.root));
154                    account.encode(&mut account_rlp as &mut dyn BufMut);
155
156                    hash_builder.add_leaf(Nibbles::unpack(hashed_address), &account_rlp);
157
158                    // We might be adding leaves that are not necessarily our proof targets.
159                    if leaf_is_proof_target {
160                        // Overwrite storage multiproof.
161                        storages.insert(hashed_address, storage_multiproof);
162                    }
163                }
164            }
165        }
166        let _ = hash_builder.root();
167        let account_subtree = hash_builder.take_proof_nodes();
168        let branch_node_hash_masks = if self.collect_branch_node_hash_masks {
169            hash_builder
170                .updated_branch_nodes
171                .unwrap_or_default()
172                .into_iter()
173                .map(|(path, node)| (path, node.hash_mask))
174                .collect()
175        } else {
176            HashMap::default()
177        };
178
179        Ok(MultiProof { account_subtree, branch_node_hash_masks, storages })
180    }
181}
182
183/// Generates storage merkle proofs.
184#[derive(Debug)]
185pub struct StorageProof<T, H> {
186    /// The factory for traversing trie nodes.
187    trie_cursor_factory: T,
188    /// The factory for hashed cursors.
189    hashed_cursor_factory: H,
190    /// The hashed address of an account.
191    hashed_address: B256,
192    /// The set of storage slot prefixes that have changed.
193    prefix_set: PrefixSetMut,
194    /// Flag indicating whether to include branch node hash masks in the proof.
195    collect_branch_node_hash_masks: bool,
196}
197
198impl<T, H> StorageProof<T, H> {
199    /// Create a new [`StorageProof`] instance.
200    pub fn new(t: T, h: H, address: Address) -> Self {
201        Self::new_hashed(t, h, keccak256(address))
202    }
203
204    /// Create a new [`StorageProof`] instance with hashed address.
205    pub fn new_hashed(t: T, h: H, hashed_address: B256) -> Self {
206        Self {
207            trie_cursor_factory: t,
208            hashed_cursor_factory: h,
209            hashed_address,
210            prefix_set: PrefixSetMut::default(),
211            collect_branch_node_hash_masks: false,
212        }
213    }
214
215    /// Set the trie cursor factory.
216    pub fn with_trie_cursor_factory<TF>(self, trie_cursor_factory: TF) -> StorageProof<TF, H> {
217        StorageProof {
218            trie_cursor_factory,
219            hashed_cursor_factory: self.hashed_cursor_factory,
220            hashed_address: self.hashed_address,
221            prefix_set: self.prefix_set,
222            collect_branch_node_hash_masks: self.collect_branch_node_hash_masks,
223        }
224    }
225
226    /// Set the hashed cursor factory.
227    pub fn with_hashed_cursor_factory<HF>(self, hashed_cursor_factory: HF) -> StorageProof<T, HF> {
228        StorageProof {
229            trie_cursor_factory: self.trie_cursor_factory,
230            hashed_cursor_factory,
231            hashed_address: self.hashed_address,
232            prefix_set: self.prefix_set,
233            collect_branch_node_hash_masks: self.collect_branch_node_hash_masks,
234        }
235    }
236
237    /// Set the changed prefixes.
238    pub fn with_prefix_set_mut(mut self, prefix_set: PrefixSetMut) -> Self {
239        self.prefix_set = prefix_set;
240        self
241    }
242
243    /// Set the flag indicating whether to include branch node hash masks in the proof.
244    pub const fn with_branch_node_hash_masks(mut self, branch_node_hash_masks: bool) -> Self {
245        self.collect_branch_node_hash_masks = branch_node_hash_masks;
246        self
247    }
248}
249
250impl<T, H> StorageProof<T, H>
251where
252    T: TrieCursorFactory,
253    H: HashedCursorFactory,
254{
255    /// Generate an account proof from intermediate nodes.
256    pub fn storage_proof(
257        self,
258        slot: B256,
259    ) -> Result<reth_trie_common::StorageProof, StateProofError> {
260        let targets = HashSet::from_iter([keccak256(slot)]);
261        Ok(self.storage_multiproof(targets)?.storage_proof(slot)?)
262    }
263
264    /// Generate storage proof.
265    pub fn storage_multiproof(
266        mut self,
267        targets: B256HashSet,
268    ) -> Result<StorageMultiProof, StateProofError> {
269        let mut hashed_storage_cursor =
270            self.hashed_cursor_factory.hashed_storage_cursor(self.hashed_address)?;
271
272        // short circuit on empty storage
273        if hashed_storage_cursor.is_storage_empty()? {
274            return Ok(StorageMultiProof::empty())
275        }
276
277        let target_nibbles = targets.into_iter().map(Nibbles::unpack).collect::<Vec<_>>();
278        self.prefix_set.extend_keys(target_nibbles.clone());
279
280        let trie_cursor = self.trie_cursor_factory.storage_trie_cursor(self.hashed_address)?;
281        let walker = TrieWalker::new(trie_cursor, self.prefix_set.freeze());
282
283        let retainer = ProofRetainer::from_iter(target_nibbles);
284        let mut hash_builder = HashBuilder::default()
285            .with_proof_retainer(retainer)
286            .with_updates(self.collect_branch_node_hash_masks);
287        let mut storage_node_iter = TrieNodeIter::new(walker, hashed_storage_cursor);
288        while let Some(node) = storage_node_iter.try_next()? {
289            match node {
290                TrieElement::Branch(node) => {
291                    hash_builder.add_branch(node.key, node.value, node.children_are_in_trie);
292                }
293                TrieElement::Leaf(hashed_slot, value) => {
294                    hash_builder.add_leaf(
295                        Nibbles::unpack(hashed_slot),
296                        alloy_rlp::encode_fixed_size(&value.value).as_ref(),
297                    );
298                }
299            }
300        }
301
302        let root = hash_builder.root();
303        let subtree = hash_builder.take_proof_nodes();
304        let branch_node_hash_masks = if self.collect_branch_node_hash_masks {
305            hash_builder
306                .updated_branch_nodes
307                .unwrap_or_default()
308                .into_iter()
309                .map(|(path, node)| (path, node.hash_mask))
310                .collect()
311        } else {
312            HashMap::default()
313        };
314
315        Ok(StorageMultiProof { root, subtree, branch_node_hash_masks })
316    }
317}