1use 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#[derive(Debug, Clone)]
20pub struct CachedTransaction<B: Block, R> {
21 pub block: Arc<RecoveredBlock<B>>,
23 pub tx_index: usize,
25 pub receipts: Option<Arc<Vec<R>>>,
27}
28
29impl<B: Block, R> CachedTransaction<B, R> {
30 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 pub fn recovered_transaction(&self) -> Option<Recovered<&<B::Body as BlockBody>::Transaction>> {
41 self.block.recovered_transaction(self.tx_index)
42 }
43
44 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 pub fn receipt(&self) -> Option<&R> {
63 self.receipts.as_ref()?.get(self.tx_index)
64 }
65
66 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 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#[derive(Debug, Clone)]
113pub struct BlockAndReceipts<N: NodePrimitives> {
114 pub block: Arc<RecoveredBlock<BlockTy<N>>>,
116 pub receipts: Arc<Vec<ReceiptTy<N>>>,
118}
119
120impl<N: NodePrimitives> BlockAndReceipts<N> {
121 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 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 pub fn sealed_block(&self) -> &SealedBlock<BlockTy<N>> {
143 self.block.sealed_block()
144 }
145
146 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
169pub 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}