Skip to main content

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