Skip to main content

reth_trie/proof_v2/
multiproof_calculator.rs

1//! Reusable multiproof generation over a fixed state snapshot.
2
3use super::{DeferredValueEncoder, LeafValueEncoder, ProofCalculator, StorageProofCalculator};
4use crate::{
5    hashed_cursor::{HashedCursorFactory, HashedStorageCursor},
6    prefix_set::{PrefixSet, TriePrefixSets},
7    trie_cursor::{TrieCursorFactory, TrieStorageCursor},
8    DecodedMultiProofV2, MultiProofTargetsV2,
9};
10use alloy_primitives::{map::B256Map, B256, U256};
11use alloy_rlp::Encodable;
12use parking_lot::Mutex;
13use reth_execution_errors::StateProofError;
14use reth_primitives_traits::Account;
15use std::{fmt, marker::PhantomPinned, pin::Pin, sync::Arc};
16
17/// Generates account and storage multiproofs for one immutable state snapshot.
18///
19/// The calculator owns its cursor factories and may be reused for any number of requests. It stays
20/// usable after an error, but must be dropped after a panic during a request. The factories must
21/// keep describing the same state for the lifetime of the calculator. Cursor factories control
22/// whether their cursors are instrumented.
23///
24/// `'a` is the lifetime of the cursors opened from the owned factories. It is not tied to a
25/// caller-supplied borrow; it only needs to be satisfied by `T` and `H`.
26pub struct MultiProofCalculator<'a, T: TrieCursorFactory + 'a, H: HashedCursorFactory + 'a> {
27    account_calculator: Option<AccountCalculatorOf<'a, T, H>>,
28    storage_calculator: Option<Arc<Mutex<StorageCalculatorOf<'a, T, H>>>>,
29    prefix_sets: TriePrefixSets,
30    known_storage_roots: Arc<B256Map<B256>>,
31    // Cursors borrow these pinned factories. `Drop` releases them first.
32    factories: Pin<Box<MultiProofCalculatorFactories<T, H>>>,
33}
34
35impl<'a, T: TrieCursorFactory + 'a, H: HashedCursorFactory + 'a> MultiProofCalculator<'a, T, H> {
36    /// Creates a calculator for the supplied factories and frozen changed-key prefixes.
37    ///
38    /// Prefixes invalidate cached branch hashes for keys whose leaf values have changed, and apply
39    /// to every request served by this calculator.
40    ///
41    /// `known_storage_roots` supplies roots keyed by hashed account address. Each root must be
42    /// valid for the account leaf being encoded. Accounts absent from the map calculate their
43    /// storage roots from the cursors. Explicit storage proof requests always use the cursors.
44    pub fn new(
45        trie_cursor_factory: T,
46        hashed_cursor_factory: H,
47        prefix_sets: TriePrefixSets,
48        known_storage_roots: Arc<B256Map<B256>>,
49    ) -> Self {
50        Self {
51            account_calculator: None,
52            storage_calculator: None,
53            prefix_sets,
54            known_storage_roots,
55            factories: Box::pin(MultiProofCalculatorFactories {
56                trie_cursor_factory,
57                hashed_cursor_factory,
58                _pin: PhantomPinned,
59            }),
60        }
61    }
62
63    /// Generates account and storage proofs for `targets`.
64    ///
65    /// An explicit storage address with no slot targets requests its root node. Empty targets
66    /// return an empty proof.
67    pub fn multiproof(
68        &mut self,
69        targets: MultiProofTargetsV2,
70    ) -> Result<DecodedMultiProofV2, StateProofError> {
71        if targets.is_empty() {
72            return Ok(DecodedMultiProofV2::default())
73        }
74        let MultiProofTargetsV2 { mut account_targets, storage_targets } = targets;
75        let mut proof = DecodedMultiProofV2::default();
76
77        // Open the storage calculator, which account proofs also use for storage roots.
78        let storage_calculator = match &mut self.storage_calculator {
79            Some(storage_calculator) => storage_calculator,
80            slot @ None => {
81                // SAFETY: Cursors are private to this calculator and released before factories.
82                let factories = unsafe { Self::borrow_factories(self.factories.as_ref()) };
83                // Storage cursors are retargeted to each requested account before use.
84                let trie_cursor = factories.trie_cursor_factory.storage_trie_cursor(B256::ZERO)?;
85                let hashed_cursor =
86                    factories.hashed_cursor_factory.hashed_storage_cursor(B256::ZERO)?;
87                // Prefixes are taken only after the fallible cursor opens, so a failed
88                // initialization can be retried.
89                let prefix_sets = std::mem::take(&mut self.prefix_sets.storage_prefix_sets);
90                slot.insert(Arc::new(Mutex::new(PrefixSetStorageCalculator {
91                    calculator: StorageProofCalculator::new_storage(trie_cursor, hashed_cursor),
92                    prefix_sets,
93                    hashed_address: None,
94                })))
95            }
96        };
97
98        // Calculate account proofs, reusing known storage roots or computing missing roots inline.
99        if !account_targets.is_empty() {
100            let account_calculator = match &mut self.account_calculator {
101                Some(account_calculator) => account_calculator,
102                slot @ None => {
103                    // SAFETY: As above, cursors are released before factories.
104                    let factories = unsafe { Self::borrow_factories(self.factories.as_ref()) };
105                    let trie_cursor = factories.trie_cursor_factory.account_trie_cursor()?;
106                    let hashed_cursor = factories.hashed_cursor_factory.hashed_account_cursor()?;
107                    let prefix_set = std::mem::take(&mut self.prefix_sets.account_prefix_set);
108                    slot.insert(
109                        ProofCalculator::new(trie_cursor, hashed_cursor)
110                            .with_prefix_set(prefix_set),
111                    )
112                }
113            };
114            let mut encoder = AccountValueEncoder {
115                storage_calculator: storage_calculator.clone(),
116                known_storage_roots: self.known_storage_roots.clone(),
117            };
118            proof.account_proofs = account_calculator.proof(&mut encoder, &mut account_targets)?;
119        }
120
121        // Calculate storage proofs, or root nodes for accounts without slot targets.
122        if !storage_targets.is_empty() {
123            let mut storage_calculator = storage_calculator.lock();
124            for (hashed_address, mut targets) in storage_targets {
125                let calculator = storage_calculator.calculator_for(hashed_address);
126                let nodes = if targets.is_empty() {
127                    vec![calculator.storage_root_node(hashed_address)?]
128                } else {
129                    calculator.storage_proof(hashed_address, &mut targets)?
130                };
131                proof.storage_proofs.insert(hashed_address, nodes);
132            }
133        }
134        Ok(proof)
135    }
136
137    /// Extends the borrow of the pinned factories to the cursor lifetime `'a`.
138    ///
139    /// # Safety
140    ///
141    /// Every reference, cursor and encoder derived from the result must be dropped before the
142    /// factories, and none may escape this calculator.
143    const unsafe fn borrow_factories(
144        factories: Pin<&MultiProofCalculatorFactories<T, H>>,
145    ) -> &'a MultiProofCalculatorFactories<T, H> {
146        // SAFETY: The factories are pinned and immutable, and the caller keeps every borrower
147        // inside this calculator, which releases them before the factories.
148        unsafe { &*(factories.get_ref() as *const MultiProofCalculatorFactories<T, H>) }
149    }
150}
151
152impl<'a, T: TrieCursorFactory + 'a, H: HashedCursorFactory + 'a> Drop
153    for MultiProofCalculator<'a, T, H>
154{
155    fn drop(&mut self) {
156        // Cursors borrow `factories`, so release them before the fields drop.
157        self.account_calculator = None;
158        self.storage_calculator = None;
159    }
160}
161
162impl<T: TrieCursorFactory, H: HashedCursorFactory> fmt::Debug for MultiProofCalculator<'_, T, H> {
163    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164        f.debug_struct("MultiProofCalculator")
165            .field("account_initialized", &self.account_calculator.is_some())
166            .field("storage_initialized", &self.storage_calculator.is_some())
167            .finish_non_exhaustive()
168    }
169}
170
171/// Account calculator opened from the cursor factories of a [`MultiProofCalculator`].
172type AccountCalculatorOf<'a, T, H> = ProofCalculator<
173    <T as TrieCursorFactory>::AccountTrieCursor<'a>,
174    <H as HashedCursorFactory>::AccountCursor<'a>,
175    AccountValueEncoder<
176        <T as TrieCursorFactory>::StorageTrieCursor<'a>,
177        <H as HashedCursorFactory>::StorageCursor<'a>,
178    >,
179>;
180
181/// Storage calculator opened from the cursor factories of a [`MultiProofCalculator`].
182type StorageCalculatorOf<'a, T, H> = PrefixSetStorageCalculator<
183    <T as TrieCursorFactory>::StorageTrieCursor<'a>,
184    <H as HashedCursorFactory>::StorageCursor<'a>,
185>;
186
187/// Cursor factories owned by a [`MultiProofCalculator`] at a stable address.
188struct MultiProofCalculatorFactories<T, H> {
189    trie_cursor_factory: T,
190    hashed_cursor_factory: H,
191    _pin: PhantomPinned,
192}
193
194/// Storage proof calculator that applies each account's changed-slot prefixes.
195///
196/// All storage calculations go through this type so the prefixes always match the account being
197/// calculated.
198struct PrefixSetStorageCalculator<TC, HC> {
199    calculator: StorageProofCalculator<TC, HC>,
200    /// Changed-slot prefixes keyed by hashed address.
201    prefix_sets: B256Map<PrefixSet>,
202    /// Account whose prefixes are currently applied to `calculator`.
203    hashed_address: Option<B256>,
204}
205
206impl<TC: TrieStorageCursor, HC: HashedStorageCursor<Value = U256>>
207    PrefixSetStorageCalculator<TC, HC>
208{
209    fn calculator_for(&mut self, hashed_address: B256) -> &mut StorageProofCalculator<TC, HC> {
210        if self.hashed_address != Some(hashed_address) {
211            self.calculator
212                .set_prefix_set(self.prefix_sets.get(&hashed_address).cloned().unwrap_or_default());
213            self.hashed_address = Some(hashed_address);
214        }
215        &mut self.calculator
216    }
217}
218
219/// Account value encoder that reuses known storage roots or computes them on demand.
220struct AccountValueEncoder<TC, HC> {
221    // Arc<Mutex<_>> keeps the calculator Send whenever its cursors are. The mutex never poisons,
222    // which is why the calculator must not be reused after a panic.
223    storage_calculator: Arc<Mutex<PrefixSetStorageCalculator<TC, HC>>>,
224    known_storage_roots: Arc<B256Map<B256>>,
225}
226
227impl<TC: TrieStorageCursor, HC: HashedStorageCursor<Value = U256>> LeafValueEncoder
228    for AccountValueEncoder<TC, HC>
229{
230    type Value = Account;
231    type DeferredEncoder = AccountDeferredValueEncoder<TC, HC>;
232
233    fn deferred_encoder(
234        &mut self,
235        hashed_address: B256,
236        account: Account,
237    ) -> Self::DeferredEncoder {
238        if let Some(root) = self.known_storage_roots.get(&hashed_address) {
239            AccountDeferredValueEncoder::Known { storage_root: *root, account }
240        } else {
241            AccountDeferredValueEncoder::Calculate {
242                storage_calculator: self.storage_calculator.clone(),
243                hashed_address,
244                account,
245            }
246        }
247    }
248}
249
250/// Deferred account encoder using a known storage root or synchronous root calculation.
251enum AccountDeferredValueEncoder<TC, HC> {
252    Known {
253        storage_root: B256,
254        account: Account,
255    },
256    Calculate {
257        storage_calculator: Arc<Mutex<PrefixSetStorageCalculator<TC, HC>>>,
258        hashed_address: B256,
259        account: Account,
260    },
261}
262
263impl<TC: TrieStorageCursor, HC: HashedStorageCursor<Value = U256>> DeferredValueEncoder
264    for AccountDeferredValueEncoder<TC, HC>
265{
266    fn encode(self, buf: &mut Vec<u8>) -> Result<(), StateProofError> {
267        let (account, root) = match self {
268            Self::Known { storage_root, account } => (account, storage_root),
269            Self::Calculate { storage_calculator, hashed_address, account } => {
270                let root = storage_calculator
271                    .lock()
272                    .calculator_for(hashed_address)
273                    .storage_root(hashed_address)?;
274                (account, root)
275            }
276        };
277        account.into_trie_account(root).encode(buf);
278        Ok(())
279    }
280}