reth_primitives_traits/block/
body.rs

1//! Block body abstraction.
2
3use crate::{
4    transaction::signed::RecoveryError, BlockHeader, FullSignedTx, InMemorySize, MaybeSerde,
5    MaybeSerdeBincodeCompat, SignedTransaction,
6};
7use alloc::{fmt, vec::Vec};
8use alloy_consensus::{
9    transaction::{Recovered, TxHashRef},
10    Transaction, Typed2718,
11};
12use alloy_eips::{eip2718::Encodable2718, eip4895::Withdrawals};
13use alloy_primitives::{Address, Bytes, B256};
14
15/// Helper trait that unifies all behaviour required by transaction to support full node operations.
16pub trait FullBlockBody: BlockBody<Transaction: FullSignedTx> + MaybeSerdeBincodeCompat {}
17
18impl<T> FullBlockBody for T where T: BlockBody<Transaction: FullSignedTx> + MaybeSerdeBincodeCompat {}
19
20/// Abstraction for block's body.
21///
22/// This type is a container for everything that is included in a block except the header.
23/// For ethereum this includes transactions, ommers, and withdrawals.
24pub trait BlockBody:
25    Send
26    + Sync
27    + Unpin
28    + Clone
29    + Default
30    + fmt::Debug
31    + PartialEq
32    + Eq
33    + alloy_rlp::Encodable
34    + alloy_rlp::Decodable
35    + InMemorySize
36    + MaybeSerde
37    + 'static
38{
39    /// Ordered list of signed transactions as committed in the block.
40    type Transaction: SignedTransaction;
41
42    /// Ommer header type.
43    type OmmerHeader: BlockHeader;
44
45    /// Returns reference to transactions in the block.
46    fn transactions(&self) -> &[Self::Transaction];
47
48    /// A Convenience function to convert this type into the regular ethereum block body that
49    /// consists of:
50    ///
51    /// - Transactions
52    /// - Withdrawals
53    /// - Ommers
54    ///
55    /// Note: This conversion can be incomplete. It is not expected that this `Body` is the same as
56    /// [`alloy_consensus::BlockBody`] only that it can be converted into it which is useful for
57    /// the `eth_` RPC namespace (e.g. RPC block).
58    fn into_ethereum_body(self)
59        -> alloy_consensus::BlockBody<Self::Transaction, Self::OmmerHeader>;
60
61    /// Returns an iterator over the transactions in the block.
62    fn transactions_iter(&self) -> impl Iterator<Item = &Self::Transaction> + '_ {
63        self.transactions().iter()
64    }
65
66    /// Returns the transaction with the matching hash.
67    ///
68    /// This is a convenience function for `transactions_iter().find()`
69    fn transaction_by_hash(&self, hash: &B256) -> Option<&Self::Transaction> {
70        self.transactions_iter().find(|tx| tx.tx_hash() == hash)
71    }
72
73    /// Returns true if the block body contains a transaction with the given hash.
74    ///
75    /// This is a convenience function for `transaction_by_hash().is_some()`
76    fn contains_transaction(&self, hash: &B256) -> bool {
77        self.transaction_by_hash(hash).is_some()
78    }
79
80    /// Clones the transactions in the block.
81    ///
82    /// This is a convenience function for `transactions().to_vec()`
83    fn clone_transactions(&self) -> Vec<Self::Transaction> {
84        self.transactions().to_vec()
85    }
86
87    /// Returns an iterator over all transaction hashes in the block body.
88    fn transaction_hashes_iter(&self) -> impl Iterator<Item = &B256> + '_ {
89        self.transactions_iter().map(|tx| tx.tx_hash())
90    }
91
92    /// Returns the number of the transactions in the block.
93    fn transaction_count(&self) -> usize {
94        self.transactions().len()
95    }
96
97    /// Consume the block body and return a [`Vec`] of transactions.
98    fn into_transactions(self) -> Vec<Self::Transaction>;
99
100    /// Returns `true` if the block body contains a transaction of the given type.
101    fn contains_transaction_type(&self, tx_type: u8) -> bool {
102        self.transactions_iter().any(|tx| tx.is_type(tx_type))
103    }
104
105    /// Calculate the transaction root for the block body.
106    fn calculate_tx_root(&self) -> B256 {
107        alloy_consensus::proofs::calculate_transaction_root(self.transactions())
108    }
109
110    /// Returns block withdrawals if any.
111    fn withdrawals(&self) -> Option<&Withdrawals>;
112
113    /// Calculate the withdrawals root for the block body.
114    ///
115    /// Returns `Some(root)` if withdrawals are present, otherwise `None`.
116    fn calculate_withdrawals_root(&self) -> Option<B256> {
117        self.withdrawals().map(|withdrawals| {
118            alloy_consensus::proofs::calculate_withdrawals_root(withdrawals.as_slice())
119        })
120    }
121
122    /// Returns block ommers if any.
123    fn ommers(&self) -> Option<&[Self::OmmerHeader]>;
124
125    /// Calculate the ommers root for the block body.
126    ///
127    /// Returns `Some(root)` if ommers are present, otherwise `None`.
128    fn calculate_ommers_root(&self) -> Option<B256> {
129        self.ommers().map(alloy_consensus::proofs::calculate_ommers_root)
130    }
131
132    /// Calculates the total blob gas used by _all_ EIP-4844 transactions in the block.
133    fn blob_gas_used(&self) -> u64 {
134        self.transactions_iter().filter_map(|tx| tx.blob_gas_used()).sum()
135    }
136
137    /// Returns an iterator over all blob versioned hashes in the block body.
138    fn blob_versioned_hashes_iter(&self) -> impl Iterator<Item = &B256> + '_ {
139        self.transactions_iter().filter_map(|tx| tx.blob_versioned_hashes()).flatten()
140    }
141
142    /// Returns an iterator over the encoded 2718 transactions.
143    ///
144    /// This is also known as `raw transactions`.
145    ///
146    /// See also [`Encodable2718`].
147    #[doc(alias = "raw_transactions_iter")]
148    fn encoded_2718_transactions_iter(&self) -> impl Iterator<Item = Vec<u8>> + '_ {
149        self.transactions_iter().map(|tx| tx.encoded_2718())
150    }
151
152    /// Returns a vector of encoded 2718 transactions.
153    ///
154    /// This is also known as `raw transactions`.
155    ///
156    /// See also [`Encodable2718`].
157    #[doc(alias = "raw_transactions")]
158    fn encoded_2718_transactions(&self) -> Vec<Bytes> {
159        self.encoded_2718_transactions_iter().map(Into::into).collect()
160    }
161
162    /// Recover signer addresses for all transactions in the block body.
163    fn recover_signers(&self) -> Result<Vec<Address>, RecoveryError> {
164        crate::transaction::recover::recover_signers(self.transactions())
165    }
166
167    /// Recover signer addresses for all transactions in the block body.
168    ///
169    /// Returns an error if some transaction's signature is invalid.
170    fn try_recover_signers(&self) -> Result<Vec<Address>, RecoveryError> {
171        self.recover_signers()
172    }
173
174    /// Recover signer addresses for all transactions in the block body _without ensuring that the
175    /// signature has a low `s` value_.
176    ///
177    /// Returns `RecoveryError`, if some transaction's signature is invalid.
178    fn recover_signers_unchecked(&self) -> Result<Vec<Address>, RecoveryError> {
179        crate::transaction::recover::recover_signers_unchecked(self.transactions())
180    }
181
182    /// Recover signer addresses for all transactions in the block body _without ensuring that the
183    /// signature has a low `s` value_.
184    ///
185    /// Returns an error if some transaction's signature is invalid.
186    fn try_recover_signers_unchecked(&self) -> Result<Vec<Address>, RecoveryError> {
187        self.recover_signers_unchecked()
188    }
189
190    /// Recovers signers for all transactions in the block body and returns a vector of
191    /// [`Recovered`].
192    fn recover_transactions(&self) -> Result<Vec<Recovered<Self::Transaction>>, RecoveryError> {
193        self.recover_signers().map(|signers| {
194            self.transactions()
195                .iter()
196                .zip(signers)
197                .map(|(tx, signer)| tx.clone().with_signer(signer))
198                .collect()
199        })
200    }
201
202    /// Returns an iterator over `Recovered<&Transaction>` for all transactions in the block body.
203    ///
204    /// This method recovers signers and returns an iterator without cloning transactions,
205    /// making it more efficient than [`BlockBody::recover_transactions`] when owned values are not
206    /// required.
207    ///
208    /// # Errors
209    ///
210    /// Returns an error if any transaction's signature is invalid.
211    fn recover_transactions_ref(
212        &self,
213    ) -> Result<impl Iterator<Item = Recovered<&Self::Transaction>> + '_, RecoveryError> {
214        let signers = self.recover_signers()?;
215        Ok(self
216            .transactions()
217            .iter()
218            .zip(signers)
219            .map(|(tx, signer)| Recovered::new_unchecked(tx, signer)))
220    }
221
222    /// Returns an iterator over `Recovered<&Transaction>` for all transactions in the block body
223    /// _without ensuring that the signature has a low `s` value_.
224    ///
225    /// This method recovers signers and returns an iterator without cloning transactions,
226    /// making it more efficient than recovering with owned transactions when owned values are not
227    /// required.
228    ///
229    /// # Errors
230    ///
231    /// Returns an error if any transaction's signature is invalid.
232    fn recover_transactions_unchecked_ref(
233        &self,
234    ) -> Result<impl Iterator<Item = Recovered<&Self::Transaction>> + '_, RecoveryError> {
235        let signers = self.recover_signers_unchecked()?;
236        Ok(self
237            .transactions()
238            .iter()
239            .zip(signers)
240            .map(|(tx, signer)| Recovered::new_unchecked(tx, signer)))
241    }
242}
243
244impl<T, H> BlockBody for alloy_consensus::BlockBody<T, H>
245where
246    T: SignedTransaction,
247    H: BlockHeader,
248{
249    type Transaction = T;
250    type OmmerHeader = H;
251
252    fn transactions(&self) -> &[Self::Transaction] {
253        &self.transactions
254    }
255
256    fn into_ethereum_body(self) -> Self {
257        self
258    }
259
260    fn into_transactions(self) -> Vec<Self::Transaction> {
261        self.transactions
262    }
263
264    fn withdrawals(&self) -> Option<&Withdrawals> {
265        self.withdrawals.as_ref()
266    }
267
268    fn ommers(&self) -> Option<&[Self::OmmerHeader]> {
269        Some(&self.ommers)
270    }
271}
272
273/// This is a helper alias to make it easy to refer to the inner `Transaction` associated type of a
274/// given type that implements [`BlockBody`].
275pub type BodyTx<N> = <N as BlockBody>::Transaction;
276
277/// This is a helper alias to make it easy to refer to the inner `OmmerHeader` associated type of a
278/// given type that implements [`BlockBody`].
279pub type BodyOmmer<N> = <N as BlockBody>::OmmerHeader;