Skip to main content

reth_rpc_eth_types/
block.rs

1//! Block related types for RPC API.
2
3use std::sync::Arc;
4
5use alloy_consensus::{
6    transaction::{TransactionMeta, TxHashRef},
7    BlockHeader, TxReceipt,
8};
9use alloy_primitives::TxHash;
10use reth_primitives_traits::{
11    Block, BlockBody, BlockTy, IndexedTx, NodePrimitives, ReceiptTy, Recovered, RecoveredBlock,
12    SealedBlock,
13};
14use reth_rpc_convert::{transaction::ConvertReceiptInput, RpcConvert, RpcTypes};
15
16use crate::{utils::calculate_gas_used_and_next_log_index, TransactionSource};
17
18/// Cached data for a transaction lookup.
19#[derive(Debug, Clone)]
20pub struct CachedTransaction<B: Block, R> {
21    /// The block containing this transaction.
22    pub block: Arc<RecoveredBlock<B>>,
23    /// Index of the transaction within the block.
24    pub tx_index: usize,
25    /// Receipts for the block, if available.
26    pub receipts: Option<Arc<Vec<R>>>,
27}
28
29impl<B: Block, R> CachedTransaction<B, R> {
30    /// Creates a new cached transaction entry.
31    pub const fn new(
32        block: Arc<RecoveredBlock<B>>,
33        tx_index: usize,
34        receipts: Option<Arc<Vec<R>>>,
35    ) -> Self {
36        Self { block, tx_index, receipts }
37    }
38
39    /// Returns the `Recovered<&T>` transaction at the cached index.
40    pub fn recovered_transaction(&self) -> Option<Recovered<&<B::Body as BlockBody>::Transaction>> {
41        self.block.recovered_transaction(self.tx_index)
42    }
43
44    /// Converts this cached transaction into a [`TransactionSource::Block`].
45    ///
46    /// Returns `None` if the transaction index is out of bounds.
47    pub fn to_transaction_source(
48        &self,
49    ) -> Option<TransactionSource<<B::Body as BlockBody>::Transaction>> {
50        let tx = self.recovered_transaction()?;
51        Some(TransactionSource::Block {
52            transaction: tx.cloned(),
53            index: self.tx_index as u64,
54            block_hash: self.block.hash(),
55            block_number: self.block.number(),
56            block_timestamp: self.block.timestamp(),
57            base_fee: self.block.base_fee_per_gas(),
58        })
59    }
60
61    /// Returns the receipt at the cached transaction index, if receipts are available.
62    pub fn receipt(&self) -> Option<&R> {
63        self.receipts.as_ref()?.get(self.tx_index)
64    }
65
66    /// Constructs a [`TransactionMeta`] for this cached transaction using the given tx hash.
67    pub fn transaction_meta(&self, tx_hash: TxHash) -> TransactionMeta
68    where
69        B::Header: BlockHeader,
70    {
71        TransactionMeta {
72            tx_hash,
73            index: self.tx_index as u64,
74            block_hash: self.block.hash(),
75            block_number: self.block.number(),
76            base_fee: self.block.base_fee_per_gas(),
77            excess_blob_gas: self.block.header().excess_blob_gas(),
78            timestamp: self.block.timestamp(),
79        }
80    }
81
82    /// Converts this cached transaction into an RPC receipt using the given converter.
83    ///
84    /// Returns `None` if receipts are not available or the transaction index is out of bounds.
85    pub fn into_receipt<N, C>(
86        self,
87        converter: &C,
88    ) -> Option<Result<<C::Network as RpcTypes>::Receipt, C::Error>>
89    where
90        N: NodePrimitives<Block = B, Receipt = R>,
91        R: TxReceipt + Clone,
92        C: RpcConvert<Primitives = N>,
93    {
94        let receipts = self.receipts?;
95        let receipt = receipts.get(self.tx_index)?;
96        let tx_hash = *self.block.body().transactions().get(self.tx_index)?.tx_hash();
97        let tx = self.block.find_indexed(tx_hash)?;
98        convert_transaction_receipt::<N, C>(
99            self.block.as_ref(),
100            receipts.as_ref(),
101            tx,
102            receipt,
103            converter,
104        )
105    }
106}
107
108/// A pair of an [`Arc`] wrapped [`RecoveredBlock`] and its corresponding receipts.
109///
110/// This type is used throughout the RPC layer to efficiently pass around
111/// blocks with their execution receipts, avoiding unnecessary cloning.
112#[derive(Debug, Clone)]
113pub struct BlockAndReceipts<N: NodePrimitives> {
114    /// The recovered block.
115    pub block: Arc<RecoveredBlock<BlockTy<N>>>,
116    /// The receipts for the block.
117    pub receipts: Arc<Vec<ReceiptTy<N>>>,
118}
119
120impl<N: NodePrimitives> BlockAndReceipts<N> {
121    /// Creates a new [`BlockAndReceipts`] instance.
122    pub const fn new(
123        block: Arc<RecoveredBlock<BlockTy<N>>>,
124        receipts: Arc<Vec<ReceiptTy<N>>>,
125    ) -> Self {
126        Self { block, receipts }
127    }
128
129    /// Finds a transaction by hash and returns it along with its corresponding receipt.
130    ///
131    /// Returns `None` if the transaction is not found in this block.
132    pub fn find_transaction_and_receipt_by_hash(
133        &self,
134        tx_hash: TxHash,
135    ) -> Option<(IndexedTx<'_, N::Block>, &N::Receipt)> {
136        let indexed_tx = self.block.find_indexed(tx_hash)?;
137        let receipt = self.receipts.get(indexed_tx.index())?;
138        Some((indexed_tx, receipt))
139    }
140
141    /// Returns the underlying sealed block.
142    pub fn sealed_block(&self) -> &SealedBlock<BlockTy<N>> {
143        self.block.sealed_block()
144    }
145
146    /// Returns the rpc transaction receipt for the given transaction hash if it exists.
147    ///
148    /// This uses the given converter to turn [`Self::find_transaction_and_receipt_by_hash`] into
149    /// the rpc format.
150    pub fn find_and_convert_transaction_receipt<C>(
151        &self,
152        tx_hash: TxHash,
153        converter: &C,
154    ) -> Option<Result<<C::Network as RpcTypes>::Receipt, C::Error>>
155    where
156        C: RpcConvert<Primitives = N>,
157    {
158        let (tx, receipt) = self.find_transaction_and_receipt_by_hash(tx_hash)?;
159        convert_transaction_receipt(
160            self.block.as_ref(),
161            self.receipts.as_ref(),
162            tx,
163            receipt,
164            converter,
165        )
166    }
167}
168
169/// Converts a transaction and its receipt into the rpc receipt format using the given converter.
170pub fn convert_transaction_receipt<N, C>(
171    block: &RecoveredBlock<BlockTy<N>>,
172    all_receipts: &[ReceiptTy<N>],
173    tx: IndexedTx<'_, BlockTy<N>>,
174    receipt: &ReceiptTy<N>,
175    converter: &C,
176) -> Option<Result<<C::Network as RpcTypes>::Receipt, C::Error>>
177where
178    N: NodePrimitives,
179    C: RpcConvert<Primitives = N>,
180{
181    let meta = tx.meta();
182    let (gas_used, next_log_index) =
183        calculate_gas_used_and_next_log_index(meta.index, all_receipts);
184
185    converter
186        .convert_receipts_with_block(
187            vec![ConvertReceiptInput {
188                tx: tx.recovered_tx(),
189                gas_used: receipt.cumulative_gas_used() - gas_used,
190                receipt: receipt.clone(),
191                next_log_index,
192                meta,
193            }],
194            block.sealed_block(),
195        )
196        .map(|mut receipts| receipts.pop())
197        .transpose()
198}