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            base_fee: self.block.base_fee_per_gas(),
57        })
58    }
59
60    /// Returns the receipt at the cached transaction index, if receipts are available.
61    pub fn receipt(&self) -> Option<&R> {
62        self.receipts.as_ref()?.get(self.tx_index)
63    }
64
65    /// Constructs a [`TransactionMeta`] for this cached transaction using the given tx hash.
66    pub fn transaction_meta(&self, tx_hash: TxHash) -> TransactionMeta
67    where
68        B::Header: BlockHeader,
69    {
70        TransactionMeta {
71            tx_hash,
72            index: self.tx_index as u64,
73            block_hash: self.block.hash(),
74            block_number: self.block.number(),
75            base_fee: self.block.base_fee_per_gas(),
76            excess_blob_gas: self.block.header().excess_blob_gas(),
77            timestamp: self.block.timestamp(),
78        }
79    }
80
81    /// Converts this cached transaction into an RPC receipt using the given converter.
82    ///
83    /// Returns `None` if receipts are not available or the transaction index is out of bounds.
84    pub fn into_receipt<N, C>(
85        self,
86        converter: &C,
87    ) -> Option<Result<<C::Network as RpcTypes>::Receipt, C::Error>>
88    where
89        N: NodePrimitives<Block = B, Receipt = R>,
90        R: TxReceipt + Clone,
91        C: RpcConvert<Primitives = N>,
92    {
93        let receipts = self.receipts?;
94        let receipt = receipts.get(self.tx_index)?;
95        let tx_hash = *self.block.body().transactions().get(self.tx_index)?.tx_hash();
96        let tx = self.block.find_indexed(tx_hash)?;
97        convert_transaction_receipt::<N, C>(
98            self.block.as_ref(),
99            receipts.as_ref(),
100            tx,
101            receipt,
102            converter,
103        )
104    }
105}
106
107/// A pair of an [`Arc`] wrapped [`RecoveredBlock`] and its corresponding receipts.
108///
109/// This type is used throughout the RPC layer to efficiently pass around
110/// blocks with their execution receipts, avoiding unnecessary cloning.
111#[derive(Debug, Clone)]
112pub struct BlockAndReceipts<N: NodePrimitives> {
113    /// The recovered block.
114    pub block: Arc<RecoveredBlock<BlockTy<N>>>,
115    /// The receipts for the block.
116    pub receipts: Arc<Vec<ReceiptTy<N>>>,
117}
118
119impl<N: NodePrimitives> BlockAndReceipts<N> {
120    /// Creates a new [`BlockAndReceipts`] instance.
121    pub const fn new(
122        block: Arc<RecoveredBlock<BlockTy<N>>>,
123        receipts: Arc<Vec<ReceiptTy<N>>>,
124    ) -> Self {
125        Self { block, receipts }
126    }
127
128    /// Finds a transaction by hash and returns it along with its corresponding receipt.
129    ///
130    /// Returns `None` if the transaction is not found in this block.
131    pub fn find_transaction_and_receipt_by_hash(
132        &self,
133        tx_hash: TxHash,
134    ) -> Option<(IndexedTx<'_, N::Block>, &N::Receipt)> {
135        let indexed_tx = self.block.find_indexed(tx_hash)?;
136        let receipt = self.receipts.get(indexed_tx.index())?;
137        Some((indexed_tx, receipt))
138    }
139
140    /// Returns the underlying sealed block.
141    pub fn sealed_block(&self) -> &SealedBlock<BlockTy<N>> {
142        self.block.sealed_block()
143    }
144
145    /// Returns the rpc transaction receipt for the given transaction hash if it exists.
146    ///
147    /// This uses the given converter to turn [`Self::find_transaction_and_receipt_by_hash`] into
148    /// the rpc format.
149    pub fn find_and_convert_transaction_receipt<C>(
150        &self,
151        tx_hash: TxHash,
152        converter: &C,
153    ) -> Option<Result<<C::Network as RpcTypes>::Receipt, C::Error>>
154    where
155        C: RpcConvert<Primitives = N>,
156    {
157        let (tx, receipt) = self.find_transaction_and_receipt_by_hash(tx_hash)?;
158        convert_transaction_receipt(
159            self.block.as_ref(),
160            self.receipts.as_ref(),
161            tx,
162            receipt,
163            converter,
164        )
165    }
166}
167
168/// Converts a transaction and its receipt into the rpc receipt format using the given converter.
169pub fn convert_transaction_receipt<N, C>(
170    block: &RecoveredBlock<BlockTy<N>>,
171    all_receipts: &[ReceiptTy<N>],
172    tx: IndexedTx<'_, BlockTy<N>>,
173    receipt: &ReceiptTy<N>,
174    converter: &C,
175) -> Option<Result<<C::Network as RpcTypes>::Receipt, C::Error>>
176where
177    N: NodePrimitives,
178    C: RpcConvert<Primitives = N>,
179{
180    let meta = tx.meta();
181    let (gas_used, next_log_index) =
182        calculate_gas_used_and_next_log_index(meta.index, all_receipts);
183
184    converter
185        .convert_receipts_with_block(
186            vec![ConvertReceiptInput {
187                tx: tx.recovered_tx(),
188                gas_used: receipt.cumulative_gas_used() - gas_used,
189                receipt: receipt.clone(),
190                next_log_index,
191                meta,
192            }],
193            block.sealed_block(),
194        )
195        .map(|mut receipts| receipts.pop())
196        .transpose()
197}