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;