reth_rpc_types_compat/
block.rs

1//! Compatibility functions for rpc `Block` type.
2
3use alloy_consensus::{BlockHeader, Sealable, Sealed, Typed2718};
4use alloy_eips::eip4895::Withdrawals;
5use alloy_primitives::{B256, U256};
6use alloy_rpc_types_eth::{
7    Block, BlockTransactions, BlockTransactionsKind, Header, TransactionInfo,
8};
9use reth_primitives::{transaction::SignedTransactionIntoRecoveredExt, BlockWithSenders};
10use reth_primitives_traits::{Block as BlockTrait, BlockBody, SignedTransaction};
11
12use crate::{transaction::from_recovered_with_block_context, TransactionCompat};
13
14/// Converts the given primitive block into a [`Block`] response with the given
15/// [`BlockTransactionsKind`]
16///
17/// If a `block_hash` is provided, then this is used, otherwise the block hash is computed.
18#[expect(clippy::type_complexity)]
19pub fn from_block<T, B>(
20    block: BlockWithSenders<B>,
21    kind: BlockTransactionsKind,
22    block_hash: Option<B256>,
23    tx_resp_builder: &T,
24) -> Result<Block<T::Transaction, Header<B::Header>>, T::Error>
25where
26    T: TransactionCompat<<<B as BlockTrait>::Body as BlockBody>::Transaction>,
27    B: BlockTrait,
28{
29    match kind {
30        BlockTransactionsKind::Hashes => {
31            Ok(from_block_with_tx_hashes::<T::Transaction, B>(block, block_hash))
32        }
33        BlockTransactionsKind::Full => from_block_full::<T, B>(block, block_hash, tx_resp_builder),
34    }
35}
36
37/// Create a new [`Block`] response from a [primitive block](reth_primitives::Block), using the
38/// total difficulty to populate its field in the rpc response.
39///
40/// This will populate the `transactions` field with only the hashes of the transactions in the
41/// block: [`BlockTransactions::Hashes`]
42pub fn from_block_with_tx_hashes<T, B>(
43    block: BlockWithSenders<B>,
44    block_hash: Option<B256>,
45) -> Block<T, Header<B::Header>>
46where
47    B: BlockTrait,
48{
49    let block_hash = block_hash.unwrap_or_else(|| block.header().hash_slow());
50    let transactions = block.body().transactions().iter().map(|tx| *tx.tx_hash()).collect();
51
52    from_block_with_transactions(
53        block.length(),
54        block_hash,
55        block.block,
56        BlockTransactions::Hashes(transactions),
57    )
58}
59
60/// Create a new [`Block`] response from a [primitive block](reth_primitives::Block), using the
61/// total difficulty to populate its field in the rpc response.
62///
63/// This will populate the `transactions` field with the _full_
64/// [`TransactionCompat::Transaction`] objects: [`BlockTransactions::Full`]
65#[expect(clippy::type_complexity)]
66pub fn from_block_full<T, B>(
67    block: BlockWithSenders<B>,
68    block_hash: Option<B256>,
69    tx_resp_builder: &T,
70) -> Result<Block<T::Transaction, Header<B::Header>>, T::Error>
71where
72    T: TransactionCompat<<<B as BlockTrait>::Body as BlockBody>::Transaction>,
73    B: BlockTrait,
74{
75    let block_hash = block_hash.unwrap_or_else(|| block.block.header().hash_slow());
76    let block_number = block.block.header().number();
77    let base_fee_per_gas = block.block.header().base_fee_per_gas();
78
79    // NOTE: we can safely remove the body here because not needed to finalize the `Block` in
80    // `from_block_with_transactions`, however we need to compute the length before
81    let block_length = block.block.length();
82    let transactions = block.block.body().transactions().to_vec();
83    let transactions_with_senders = transactions.into_iter().zip(block.senders);
84    let transactions = transactions_with_senders
85        .enumerate()
86        .map(|(idx, (tx, sender))| {
87            let tx_type = Some(tx.ty() as isize);
88            let tx_hash = *tx.tx_hash();
89            let signed_tx_ec_recovered = tx.with_signer(sender);
90            let tx_info = TransactionInfo {
91                hash: Some(tx_hash),
92                block_hash: Some(block_hash),
93                block_number: Some(block_number),
94                base_fee: base_fee_per_gas.map(u128::from),
95                index: Some(idx as u64),
96                tx_type,
97            };
98
99            from_recovered_with_block_context::<_, T>(
100                signed_tx_ec_recovered,
101                tx_info,
102                tx_resp_builder,
103            )
104        })
105        .collect::<Result<Vec<_>, T::Error>>()?;
106
107    Ok(from_block_with_transactions(
108        block_length,
109        block_hash,
110        block.block,
111        BlockTransactions::Full(transactions),
112    ))
113}
114
115#[inline]
116fn from_block_with_transactions<T, B: BlockTrait>(
117    block_length: usize,
118    block_hash: B256,
119    block: B,
120    transactions: BlockTransactions<T>,
121) -> Block<T, Header<B::Header>> {
122    let withdrawals = block
123        .header()
124        .withdrawals_root()
125        .is_some()
126        .then(|| block.body().withdrawals().cloned().map(Withdrawals::into_inner).map(Into::into))
127        .flatten();
128
129    let uncles = block
130        .body()
131        .ommers()
132        .map(|o| o.iter().map(|h| h.hash_slow()).collect())
133        .unwrap_or_default();
134    let (header, _) = block.split();
135    let header = Header::from_consensus(
136        Sealed::new_unchecked(header, block_hash),
137        None,
138        Some(U256::from(block_length)),
139    );
140
141    Block { header, uncles, transactions, withdrawals }
142}