1use 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#[derive(Debug, Clone)]
21pub struct CachedTransaction<B: Block, R> {
22 pub block: Arc<RecoveredBlock<B>>,
24 pub tx_index: usize,
26 pub receipts: Option<Arc<Vec<R>>>,
28}
29
30impl<B: Block, R> CachedTransaction<B, R> {
31 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 pub fn recovered_transaction(&self) -> Option<Recovered<&<B::Body as BlockBody>::Transaction>> {
42 self.block.recovered_transaction(self.tx_index)
43 }
44
45 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 pub fn receipt(&self) -> Option<&R> {
64 self.receipts.as_ref()?.get(self.tx_index)
65 }
66
67 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 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#[derive(Debug, Clone)]
114pub struct BlockAndReceipts<N: NodePrimitives> {
115 pub block: Arc<RecoveredBlock<BlockTy<N>>>,
117 pub receipts: SharedReceipts<ReceiptTy<N>>,
119}
120
121impl<N: NodePrimitives> BlockAndReceipts<N> {
122 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 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 pub fn sealed_block(&self) -> &SealedBlock<BlockTy<N>> {
144 self.block.sealed_block()
145 }
146
147 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#[derive(Debug, Clone)]
166pub enum SharedReceipts<R> {
167 Receipts(Arc<Vec<R>>),
169 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 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
209pub 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}