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