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