1mod header;
4pub use header::{BlockHeader, Header};
5
6mod traits;
7pub use traits::EthBlock;
8
9#[cfg(all(feature = "serde", feature = "serde-bincode-compat"))]
10pub(crate) use header::serde_bincode_compat;
11
12use crate::Transaction;
13use alloc::vec::Vec;
14use alloy_eips::{eip2718::WithEncoded, eip4895::Withdrawals, Encodable2718, Typed2718};
15use alloy_primitives::{Sealable, B256};
16use alloy_rlp::{Decodable, Encodable, RlpDecodable, RlpEncodable};
17
18#[derive(Debug, Clone, PartialEq, Eq, derive_more::Deref)]
26#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
27pub struct Block<T, H = Header> {
28 #[deref]
30 pub header: H,
31 pub body: BlockBody<T, H>,
33}
34
35impl<T, H> Block<T, H> {
36 pub const fn new(header: H, body: BlockBody<T, H>) -> Self {
38 Self { header, body }
39 }
40
41 pub fn uncle(header: H) -> Self {
43 Self { header, body: Default::default() }
44 }
45
46 pub fn into_header(self) -> H {
48 self.header
49 }
50
51 pub fn into_body(self) -> BlockBody<T, H> {
53 self.body
54 }
55
56 pub fn map_header<U>(self, mut f: impl FnMut(H) -> U) -> Block<T, U> {
58 Block { header: f(self.header), body: self.body.map_ommers(f) }
59 }
60
61 pub fn try_map_header<U, E>(
63 self,
64 mut f: impl FnMut(H) -> Result<U, E>,
65 ) -> Result<Block<T, U>, E> {
66 Ok(Block { header: f(self.header)?, body: self.body.try_map_ommers(f)? })
67 }
68
69 pub fn convert_transactions<U>(self) -> Block<U, H>
71 where
72 U: From<T>,
73 {
74 self.map_transactions(U::from)
75 }
76
77 pub fn try_convert_transactions<U>(self) -> Result<Block<U, H>, U::Error>
81 where
82 U: TryFrom<T>,
83 {
84 self.try_map_transactions(U::try_from)
85 }
86
87 pub fn map_transactions<U>(self, f: impl FnMut(T) -> U) -> Block<U, H> {
91 Block {
92 header: self.header,
93 body: BlockBody {
94 transactions: self.body.transactions.into_iter().map(f).collect(),
95 ommers: self.body.ommers,
96 withdrawals: self.body.withdrawals,
97 },
98 }
99 }
100
101 pub fn try_map_transactions<U, E>(
105 self,
106 f: impl FnMut(T) -> Result<U, E>,
107 ) -> Result<Block<U, H>, E> {
108 Ok(Block {
109 header: self.header,
110 body: BlockBody {
111 transactions: self
112 .body
113 .transactions
114 .into_iter()
115 .map(f)
116 .collect::<Result<_, _>>()?,
117 ommers: self.body.ommers,
118 withdrawals: self.body.withdrawals,
119 },
120 })
121 }
122
123 pub fn into_with_encoded2718(self) -> Block<WithEncoded<T>, H>
126 where
127 T: Encodable2718,
128 {
129 self.map_transactions(|tx| tx.into_encoded())
130 }
131
132 pub fn rlp_length_for(header: &H, body: &BlockBody<T, H>) -> usize
134 where
135 H: Encodable,
136 T: Encodable,
137 {
138 block_rlp::HelperRef {
139 header,
140 transactions: &body.transactions,
141 ommers: &body.ommers,
142 withdrawals: body.withdrawals.as_ref(),
143 }
144 .length()
145 }
146}
147
148impl<T, H> Default for Block<T, H>
149where
150 H: Default,
151{
152 fn default() -> Self {
153 Self { header: Default::default(), body: Default::default() }
154 }
155}
156
157impl<T, H> From<Block<T, H>> for BlockBody<T, H> {
158 fn from(block: Block<T, H>) -> Self {
159 block.into_body()
160 }
161}
162
163#[cfg(any(test, feature = "arbitrary"))]
164impl<'a, T, H> arbitrary::Arbitrary<'a> for Block<T, H>
165where
166 T: arbitrary::Arbitrary<'a>,
167 H: arbitrary::Arbitrary<'a>,
168{
169 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
170 Ok(Self { header: u.arbitrary()?, body: u.arbitrary()? })
171 }
172}
173
174#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
178#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
179#[rlp(trailing)]
180pub struct BlockBody<T, H = Header> {
181 pub transactions: Vec<T>,
183 pub ommers: Vec<H>,
185 pub withdrawals: Option<Withdrawals>,
187}
188
189impl<T, H> Default for BlockBody<T, H> {
190 fn default() -> Self {
191 Self { transactions: Vec::new(), ommers: Vec::new(), withdrawals: None }
192 }
193}
194
195impl<T, H> BlockBody<T, H> {
196 #[inline]
198 pub fn transactions(&self) -> impl Iterator<Item = &T> + '_ {
199 self.transactions.iter()
200 }
201
202 pub const fn into_block(self, header: H) -> Block<T, H> {
204 Block { header, body: self }
205 }
206
207 pub fn calculate_ommers_root(&self) -> B256
209 where
210 H: Encodable,
211 {
212 crate::proofs::calculate_ommers_root(&self.ommers)
213 }
214
215 pub fn ommers_hashes(&self) -> impl Iterator<Item = B256> + '_
217 where
218 H: Sealable,
219 {
220 self.ommers.iter().map(|h| h.hash_slow())
221 }
222
223 pub fn calculate_withdrawals_root(&self) -> Option<B256> {
226 self.withdrawals.as_ref().map(|w| crate::proofs::calculate_withdrawals_root(w))
227 }
228
229 pub fn map_ommers<U>(self, f: impl FnMut(H) -> U) -> BlockBody<T, U> {
231 BlockBody {
232 transactions: self.transactions,
233 ommers: self.ommers.into_iter().map(f).collect(),
234 withdrawals: self.withdrawals,
235 }
236 }
237
238 pub fn try_map_ommers<U, E>(
240 self,
241 f: impl FnMut(H) -> Result<U, E>,
242 ) -> Result<BlockBody<T, U>, E> {
243 Ok(BlockBody {
244 transactions: self.transactions,
245 ommers: self.ommers.into_iter().map(f).collect::<Result<Vec<_>, _>>()?,
246 withdrawals: self.withdrawals,
247 })
248 }
249}
250
251impl<T: Transaction, H> BlockBody<T, H> {
252 #[inline]
254 pub fn blob_versioned_hashes_iter(&self) -> impl Iterator<Item = &B256> + '_ {
255 self.eip4844_transactions_iter().filter_map(|tx| tx.blob_versioned_hashes()).flatten()
256 }
257}
258
259impl<T: Typed2718, H> BlockBody<T, H> {
260 #[inline]
262 pub fn has_eip4844_transactions(&self) -> bool {
263 self.transactions.iter().any(|tx| tx.is_eip4844())
264 }
265
266 #[inline]
268 pub fn has_eip7702_transactions(&self) -> bool {
269 self.transactions.iter().any(|tx| tx.is_eip7702())
270 }
271
272 #[inline]
274 pub fn eip4844_transactions_iter(&self) -> impl Iterator<Item = &T> + '_ {
275 self.transactions.iter().filter(|tx| tx.is_eip4844())
276 }
277}
278
279mod block_rlp {
282 use super::*;
283
284 #[derive(RlpDecodable)]
285 #[rlp(trailing)]
286 struct Helper<T, H> {
287 header: H,
288 transactions: Vec<T>,
289 ommers: Vec<H>,
290 withdrawals: Option<Withdrawals>,
291 }
292
293 #[derive(RlpEncodable)]
294 #[rlp(trailing)]
295 pub(crate) struct HelperRef<'a, T, H> {
296 pub(crate) header: &'a H,
297 pub(crate) transactions: &'a Vec<T>,
298 pub(crate) ommers: &'a Vec<H>,
299 pub(crate) withdrawals: Option<&'a Withdrawals>,
300 }
301
302 impl<'a, T, H> From<&'a Block<T, H>> for HelperRef<'a, T, H> {
303 fn from(block: &'a Block<T, H>) -> Self {
304 let Block { header, body: BlockBody { transactions, ommers, withdrawals } } = block;
305 Self { header, transactions, ommers, withdrawals: withdrawals.as_ref() }
306 }
307 }
308
309 impl<T: Encodable, H: Encodable> Encodable for Block<T, H> {
310 fn encode(&self, out: &mut dyn alloy_rlp::bytes::BufMut) {
311 let helper: HelperRef<'_, T, H> = self.into();
312 helper.encode(out)
313 }
314
315 fn length(&self) -> usize {
316 let helper: HelperRef<'_, T, H> = self.into();
317 helper.length()
318 }
319 }
320
321 impl<T: Decodable, H: Decodable> Decodable for Block<T, H> {
322 fn decode(b: &mut &[u8]) -> alloy_rlp::Result<Self> {
323 let Helper { header, transactions, ommers, withdrawals } = Helper::decode(b)?;
324 Ok(Self { header, body: BlockBody { transactions, ommers, withdrawals } })
325 }
326 }
327}
328
329#[cfg(any(test, feature = "arbitrary"))]
330impl<'a, T, H> arbitrary::Arbitrary<'a> for BlockBody<T, H>
331where
332 T: arbitrary::Arbitrary<'a>,
333 H: arbitrary::Arbitrary<'a>,
334{
335 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
336 let transactions = (0..u.int_in_range(0..=100)?)
339 .map(|_| T::arbitrary(u))
340 .collect::<arbitrary::Result<Vec<_>>>()?;
341
342 let ommers = (0..u.int_in_range(0..=1)?)
344 .map(|_| H::arbitrary(u))
345 .collect::<arbitrary::Result<Vec<_>>>()?;
346
347 Ok(Self { transactions, ommers, withdrawals: u.arbitrary()? })
348 }
349}
350
351#[cfg(test)]
352mod tests {
353 use super::*;
354 use crate::{Signed, TxEnvelope, TxLegacy};
355
356 #[test]
357 fn can_convert_block() {
358 let block: Block<Signed<TxLegacy>> = Block::default();
359 let _: Block<TxEnvelope> = block.convert_transactions();
360 }
361}