reth_provider/providers/static_file/
jar.rs

1use super::{
2    metrics::{StaticFileProviderMetrics, StaticFileProviderOperation},
3    LoadedJarRef,
4};
5use crate::{
6    to_range, BlockHashReader, BlockNumReader, HeaderProvider, ReceiptProvider,
7    TransactionsProvider,
8};
9use alloy_consensus::transaction::TransactionMeta;
10use alloy_eips::{eip2718::Encodable2718, eip4895::Withdrawals, BlockHashOrNumber};
11use alloy_primitives::{Address, BlockHash, BlockNumber, TxHash, TxNumber, B256, U256};
12use reth_chainspec::ChainInfo;
13use reth_db::static_file::{
14    BlockHashMask, BodyIndicesMask, HeaderMask, HeaderWithHashMask, OmmersMask, ReceiptMask,
15    StaticFileCursor, TDWithHashMask, TotalDifficultyMask, TransactionMask, WithdrawalsMask,
16};
17use reth_db_api::{
18    models::StoredBlockBodyIndices,
19    table::{Decompress, Value},
20};
21use reth_node_types::{FullNodePrimitives, NodePrimitives};
22use reth_primitives_traits::{SealedHeader, SignedTransaction};
23use reth_storage_api::{BlockBodyIndicesProvider, OmmersProvider, WithdrawalsProvider};
24use reth_storage_errors::provider::{ProviderError, ProviderResult};
25use std::{
26    fmt::Debug,
27    ops::{Deref, RangeBounds, RangeInclusive},
28    sync::Arc,
29};
30/// Provider over a specific `NippyJar` and range.
31#[derive(Debug)]
32pub struct StaticFileJarProvider<'a, N> {
33    /// Main static file segment
34    jar: LoadedJarRef<'a>,
35    /// Another kind of static file segment to help query data from the main one.
36    auxiliary_jar: Option<Box<Self>>,
37    /// Metrics for the static files.
38    metrics: Option<Arc<StaticFileProviderMetrics>>,
39    /// Node primitives
40    _pd: std::marker::PhantomData<N>,
41}
42
43impl<'a, N: NodePrimitives> Deref for StaticFileJarProvider<'a, N> {
44    type Target = LoadedJarRef<'a>;
45    fn deref(&self) -> &Self::Target {
46        &self.jar
47    }
48}
49
50impl<'a, N: NodePrimitives> From<LoadedJarRef<'a>> for StaticFileJarProvider<'a, N> {
51    fn from(value: LoadedJarRef<'a>) -> Self {
52        StaticFileJarProvider {
53            jar: value,
54            auxiliary_jar: None,
55            metrics: None,
56            _pd: Default::default(),
57        }
58    }
59}
60
61impl<'a, N: NodePrimitives> StaticFileJarProvider<'a, N> {
62    /// Provides a cursor for more granular data access.
63    pub fn cursor<'b>(&'b self) -> ProviderResult<StaticFileCursor<'a>>
64    where
65        'b: 'a,
66    {
67        let result = StaticFileCursor::new(self.value(), self.mmap_handle())?;
68
69        if let Some(metrics) = &self.metrics {
70            metrics.record_segment_operation(
71                self.segment(),
72                StaticFileProviderOperation::InitCursor,
73                None,
74            );
75        }
76
77        Ok(result)
78    }
79
80    /// Adds a new auxiliary static file to help query data from the main one
81    pub fn with_auxiliary(mut self, auxiliary_jar: Self) -> Self {
82        self.auxiliary_jar = Some(Box::new(auxiliary_jar));
83        self
84    }
85
86    /// Enables metrics on the provider.
87    pub fn with_metrics(mut self, metrics: Arc<StaticFileProviderMetrics>) -> Self {
88        self.metrics = Some(metrics);
89        self
90    }
91}
92
93impl<N: NodePrimitives<BlockHeader: Value>> HeaderProvider for StaticFileJarProvider<'_, N> {
94    type Header = N::BlockHeader;
95
96    fn header(&self, block_hash: &BlockHash) -> ProviderResult<Option<Self::Header>> {
97        Ok(self
98            .cursor()?
99            .get_two::<HeaderWithHashMask<Self::Header>>(block_hash.into())?
100            .filter(|(_, hash)| hash == block_hash)
101            .map(|(header, _)| header))
102    }
103
104    fn header_by_number(&self, num: BlockNumber) -> ProviderResult<Option<Self::Header>> {
105        self.cursor()?.get_one::<HeaderMask<Self::Header>>(num.into())
106    }
107
108    fn header_td(&self, block_hash: &BlockHash) -> ProviderResult<Option<U256>> {
109        Ok(self
110            .cursor()?
111            .get_two::<TDWithHashMask>(block_hash.into())?
112            .filter(|(_, hash)| hash == block_hash)
113            .map(|(td, _)| td.into()))
114    }
115
116    fn header_td_by_number(&self, num: BlockNumber) -> ProviderResult<Option<U256>> {
117        Ok(self.cursor()?.get_one::<TotalDifficultyMask>(num.into())?.map(Into::into))
118    }
119
120    fn headers_range(
121        &self,
122        range: impl RangeBounds<BlockNumber>,
123    ) -> ProviderResult<Vec<Self::Header>> {
124        let range = to_range(range);
125
126        let mut cursor = self.cursor()?;
127        let mut headers = Vec::with_capacity((range.end - range.start) as usize);
128
129        for num in range {
130            if let Some(header) = cursor.get_one::<HeaderMask<Self::Header>>(num.into())? {
131                headers.push(header);
132            }
133        }
134
135        Ok(headers)
136    }
137
138    fn sealed_header(
139        &self,
140        number: BlockNumber,
141    ) -> ProviderResult<Option<SealedHeader<Self::Header>>> {
142        Ok(self
143            .cursor()?
144            .get_two::<HeaderWithHashMask<Self::Header>>(number.into())?
145            .map(|(header, hash)| SealedHeader::new(header, hash)))
146    }
147
148    fn sealed_headers_while(
149        &self,
150        range: impl RangeBounds<BlockNumber>,
151        mut predicate: impl FnMut(&SealedHeader<Self::Header>) -> bool,
152    ) -> ProviderResult<Vec<SealedHeader<Self::Header>>> {
153        let range = to_range(range);
154
155        let mut cursor = self.cursor()?;
156        let mut headers = Vec::with_capacity((range.end - range.start) as usize);
157
158        for number in range {
159            if let Some((header, hash)) =
160                cursor.get_two::<HeaderWithHashMask<Self::Header>>(number.into())?
161            {
162                let sealed = SealedHeader::new(header, hash);
163                if !predicate(&sealed) {
164                    break
165                }
166                headers.push(sealed);
167            }
168        }
169        Ok(headers)
170    }
171}
172
173impl<N: NodePrimitives> BlockHashReader for StaticFileJarProvider<'_, N> {
174    fn block_hash(&self, number: u64) -> ProviderResult<Option<B256>> {
175        self.cursor()?.get_one::<BlockHashMask>(number.into())
176    }
177
178    fn canonical_hashes_range(
179        &self,
180        start: BlockNumber,
181        end: BlockNumber,
182    ) -> ProviderResult<Vec<B256>> {
183        let mut cursor = self.cursor()?;
184        let mut hashes = Vec::with_capacity((end - start) as usize);
185
186        for number in start..end {
187            if let Some(hash) = cursor.get_one::<BlockHashMask>(number.into())? {
188                hashes.push(hash)
189            }
190        }
191        Ok(hashes)
192    }
193}
194
195impl<N: NodePrimitives> BlockNumReader for StaticFileJarProvider<'_, N> {
196    fn chain_info(&self) -> ProviderResult<ChainInfo> {
197        // Information on live database
198        Err(ProviderError::UnsupportedProvider)
199    }
200
201    fn best_block_number(&self) -> ProviderResult<BlockNumber> {
202        // Information on live database
203        Err(ProviderError::UnsupportedProvider)
204    }
205
206    fn last_block_number(&self) -> ProviderResult<BlockNumber> {
207        // Information on live database
208        Err(ProviderError::UnsupportedProvider)
209    }
210
211    fn block_number(&self, hash: B256) -> ProviderResult<Option<BlockNumber>> {
212        let mut cursor = self.cursor()?;
213
214        Ok(cursor
215            .get_one::<BlockHashMask>((&hash).into())?
216            .and_then(|res| (res == hash).then(|| cursor.number()).flatten()))
217    }
218}
219
220impl<N: NodePrimitives<SignedTx: Decompress + SignedTransaction>> TransactionsProvider
221    for StaticFileJarProvider<'_, N>
222{
223    type Transaction = N::SignedTx;
224
225    fn transaction_id(&self, hash: TxHash) -> ProviderResult<Option<TxNumber>> {
226        let mut cursor = self.cursor()?;
227
228        Ok(cursor
229            .get_one::<TransactionMask<Self::Transaction>>((&hash).into())?
230            .and_then(|res| (res.trie_hash() == hash).then(|| cursor.number()).flatten()))
231    }
232
233    fn transaction_by_id(&self, num: TxNumber) -> ProviderResult<Option<Self::Transaction>> {
234        self.cursor()?.get_one::<TransactionMask<Self::Transaction>>(num.into())
235    }
236
237    fn transaction_by_id_unhashed(
238        &self,
239        num: TxNumber,
240    ) -> ProviderResult<Option<Self::Transaction>> {
241        self.cursor()?.get_one::<TransactionMask<Self::Transaction>>(num.into())
242    }
243
244    fn transaction_by_hash(&self, hash: TxHash) -> ProviderResult<Option<Self::Transaction>> {
245        self.cursor()?.get_one::<TransactionMask<Self::Transaction>>((&hash).into())
246    }
247
248    fn transaction_by_hash_with_meta(
249        &self,
250        _hash: TxHash,
251    ) -> ProviderResult<Option<(Self::Transaction, TransactionMeta)>> {
252        // Information required on indexing table [`tables::TransactionBlocks`]
253        Err(ProviderError::UnsupportedProvider)
254    }
255
256    fn transaction_block(&self, _id: TxNumber) -> ProviderResult<Option<BlockNumber>> {
257        // Information on indexing table [`tables::TransactionBlocks`]
258        Err(ProviderError::UnsupportedProvider)
259    }
260
261    fn transactions_by_block(
262        &self,
263        _block_id: BlockHashOrNumber,
264    ) -> ProviderResult<Option<Vec<Self::Transaction>>> {
265        // Related to indexing tables. Live database should get the tx_range and call static file
266        // provider with `transactions_by_tx_range` instead.
267        Err(ProviderError::UnsupportedProvider)
268    }
269
270    fn transactions_by_block_range(
271        &self,
272        _range: impl RangeBounds<BlockNumber>,
273    ) -> ProviderResult<Vec<Vec<Self::Transaction>>> {
274        // Related to indexing tables. Live database should get the tx_range and call static file
275        // provider with `transactions_by_tx_range` instead.
276        Err(ProviderError::UnsupportedProvider)
277    }
278
279    fn transactions_by_tx_range(
280        &self,
281        range: impl RangeBounds<TxNumber>,
282    ) -> ProviderResult<Vec<Self::Transaction>> {
283        let range = to_range(range);
284        let mut cursor = self.cursor()?;
285        let mut txes = Vec::with_capacity((range.end - range.start) as usize);
286
287        for num in range {
288            if let Some(tx) = cursor.get_one::<TransactionMask<Self::Transaction>>(num.into())? {
289                txes.push(tx)
290            }
291        }
292        Ok(txes)
293    }
294
295    fn senders_by_tx_range(
296        &self,
297        range: impl RangeBounds<TxNumber>,
298    ) -> ProviderResult<Vec<Address>> {
299        let txs = self.transactions_by_tx_range(range)?;
300        Ok(reth_primitives_traits::transaction::recover::recover_signers(&txs)?)
301    }
302
303    fn transaction_sender(&self, num: TxNumber) -> ProviderResult<Option<Address>> {
304        Ok(self
305            .cursor()?
306            .get_one::<TransactionMask<Self::Transaction>>(num.into())?
307            .and_then(|tx| tx.recover_signer().ok()))
308    }
309}
310
311impl<N: NodePrimitives<SignedTx: Decompress + SignedTransaction, Receipt: Decompress>>
312    ReceiptProvider for StaticFileJarProvider<'_, N>
313{
314    type Receipt = N::Receipt;
315
316    fn receipt(&self, num: TxNumber) -> ProviderResult<Option<Self::Receipt>> {
317        self.cursor()?.get_one::<ReceiptMask<Self::Receipt>>(num.into())
318    }
319
320    fn receipt_by_hash(&self, hash: TxHash) -> ProviderResult<Option<Self::Receipt>> {
321        if let Some(tx_static_file) = &self.auxiliary_jar {
322            if let Some(num) = tx_static_file.transaction_id(hash)? {
323                return self.receipt(num)
324            }
325        }
326        Ok(None)
327    }
328
329    fn receipts_by_block(
330        &self,
331        _block: BlockHashOrNumber,
332    ) -> ProviderResult<Option<Vec<Self::Receipt>>> {
333        // Related to indexing tables. StaticFile should get the tx_range and call static file
334        // provider with `receipt()` instead for each
335        Err(ProviderError::UnsupportedProvider)
336    }
337
338    fn receipts_by_tx_range(
339        &self,
340        range: impl RangeBounds<TxNumber>,
341    ) -> ProviderResult<Vec<Self::Receipt>> {
342        let range = to_range(range);
343        let mut cursor = self.cursor()?;
344        let mut receipts = Vec::with_capacity((range.end - range.start) as usize);
345
346        for num in range {
347            if let Some(tx) = cursor.get_one::<ReceiptMask<Self::Receipt>>(num.into())? {
348                receipts.push(tx)
349            }
350        }
351        Ok(receipts)
352    }
353}
354
355impl<N: NodePrimitives> WithdrawalsProvider for StaticFileJarProvider<'_, N> {
356    fn withdrawals_by_block(
357        &self,
358        id: BlockHashOrNumber,
359        _: u64,
360    ) -> ProviderResult<Option<Withdrawals>> {
361        if let Some(num) = id.as_number() {
362            return Ok(self
363                .cursor()?
364                .get_one::<WithdrawalsMask>(num.into())?
365                .and_then(|s| s.withdrawals))
366        }
367        // Only accepts block number queries
368        Err(ProviderError::UnsupportedProvider)
369    }
370}
371
372impl<N: FullNodePrimitives<BlockHeader: Value>> OmmersProvider for StaticFileJarProvider<'_, N> {
373    fn ommers(&self, id: BlockHashOrNumber) -> ProviderResult<Option<Vec<Self::Header>>> {
374        if let Some(num) = id.as_number() {
375            return Ok(self
376                .cursor()?
377                .get_one::<OmmersMask<Self::Header>>(num.into())?
378                .map(|s| s.ommers))
379        }
380        // Only accepts block number queries
381        Err(ProviderError::UnsupportedProvider)
382    }
383}
384
385impl<N: NodePrimitives> BlockBodyIndicesProvider for StaticFileJarProvider<'_, N> {
386    fn block_body_indices(&self, num: u64) -> ProviderResult<Option<StoredBlockBodyIndices>> {
387        self.cursor()?.get_one::<BodyIndicesMask>(num.into())
388    }
389
390    fn block_body_indices_range(
391        &self,
392        range: RangeInclusive<BlockNumber>,
393    ) -> ProviderResult<Vec<StoredBlockBodyIndices>> {
394        let mut cursor = self.cursor()?;
395        let mut indices = Vec::with_capacity((range.end() - range.start() + 1) as usize);
396
397        for num in range {
398            if let Some(block) = cursor.get_one::<BodyIndicesMask>(num.into())? {
399                indices.push(block)
400            }
401        }
402        Ok(indices)
403    }
404}