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