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