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