Skip to main content

reth_rpc_eth_types/
block.rs

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