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 base_fee: self.block.base_fee_per_gas(),
57 })
58 }
59
60 pub fn receipt(&self) -> Option<&R> {
62 self.receipts.as_ref()?.get(self.tx_index)
63 }
64
65 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 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#[derive(Debug, Clone)]
112pub struct BlockAndReceipts<N: NodePrimitives> {
113 pub block: Arc<RecoveredBlock<BlockTy<N>>>,
115 pub receipts: Arc<Vec<ReceiptTy<N>>>,
117}
118
119impl<N: NodePrimitives> BlockAndReceipts<N> {
120 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 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 pub fn sealed_block(&self) -> &SealedBlock<BlockTy<N>> {
142 self.block.sealed_block()
143 }
144
145 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
168pub 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}