1mod header;
4pub use header::{BlockHeader, Header};
5
6mod traits;
7pub use traits::EthBlock;
8
9mod meta;
10pub use meta::{HeaderInfo, HeaderRoots};
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))]
30#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
31pub struct Block<T, H = Header> {
32 #[deref]
34 pub header: H,
35 pub body: BlockBody<T, H>,
37}
38
39impl<T, H> Block<T, H> {
40 pub const fn new(header: H, body: BlockBody<T, H>) -> Self {
42 Self { header, body }
43 }
44
45 pub fn uncle(header: H) -> Self {
47 Self { header, body: Default::default() }
48 }
49
50 pub fn into_header(self) -> H {
52 self.header
53 }
54
55 pub fn into_body(self) -> BlockBody<T, H> {
57 self.body
58 }
59
60 pub fn map_header<U>(self, mut f: impl FnMut(H) -> U) -> Block<T, U> {
62 Block { header: f(self.header), body: self.body.map_ommers(f) }
63 }
64
65 pub fn try_map_header<U, E>(
67 self,
68 mut f: impl FnMut(H) -> Result<U, E>,
69 ) -> Result<Block<T, U>, E> {
70 Ok(Block { header: f(self.header)?, body: self.body.try_map_ommers(f)? })
71 }
72
73 pub fn convert_transactions<U>(self) -> Block<U, H>
75 where
76 U: From<T>,
77 {
78 self.map_transactions(U::from)
79 }
80
81 pub fn try_convert_transactions<U>(self) -> Result<Block<U, H>, U::Error>
85 where
86 U: TryFrom<T>,
87 {
88 self.try_map_transactions(U::try_from)
89 }
90
91 pub fn map_transactions<U>(self, f: impl FnMut(T) -> U) -> Block<U, H> {
95 Block {
96 header: self.header,
97 body: BlockBody {
98 transactions: self.body.transactions.into_iter().map(f).collect(),
99 ommers: self.body.ommers,
100 withdrawals: self.body.withdrawals,
101 },
102 }
103 }
104
105 pub fn try_map_transactions<U, E>(
109 self,
110 f: impl FnMut(T) -> Result<U, E>,
111 ) -> Result<Block<U, H>, E> {
112 Ok(Block {
113 header: self.header,
114 body: BlockBody {
115 transactions: self
116 .body
117 .transactions
118 .into_iter()
119 .map(f)
120 .collect::<Result<_, _>>()?,
121 ommers: self.body.ommers,
122 withdrawals: self.body.withdrawals,
123 },
124 })
125 }
126
127 pub fn into_with_encoded2718(self) -> Block<WithEncoded<T>, H>
130 where
131 T: Encodable2718,
132 {
133 self.map_transactions(|tx| tx.into_encoded())
134 }
135
136 pub fn with_header(mut self, header: H) -> Self {
141 self.header = header;
142 self
143 }
144
145 pub fn rlp_encoded_from_parts(header: &H, body: &BlockBody<T, H>) -> Vec<u8>
151 where
152 H: Encodable,
153 T: Encodable,
154 {
155 let helper = block_rlp::HelperRef::from_parts(header, body);
156 let mut buf = Vec::with_capacity(helper.length());
157 helper.encode(&mut buf);
158 buf
159 }
160
161 pub fn rlp_encode_from_parts(
165 header: &H,
166 body: &BlockBody<T, H>,
167 out: &mut dyn alloy_rlp::bytes::BufMut,
168 ) where
169 H: Encodable,
170 T: Encodable,
171 {
172 block_rlp::HelperRef::from_parts(header, body).encode(out)
173 }
174
175 pub fn rlp_length_for(header: &H, body: &BlockBody<T, H>) -> usize
177 where
178 H: Encodable,
179 T: Encodable,
180 {
181 block_rlp::HelperRef::from_parts(header, body).length()
182 }
183}
184
185impl<T, H> Default for Block<T, H>
186where
187 H: Default,
188{
189 fn default() -> Self {
190 Self { header: Default::default(), body: Default::default() }
191 }
192}
193
194impl<T, H> From<Block<T, H>> for BlockBody<T, H> {
195 fn from(block: Block<T, H>) -> Self {
196 block.into_body()
197 }
198}
199
200#[cfg(any(test, feature = "arbitrary"))]
201impl<'a, T, H> arbitrary::Arbitrary<'a> for Block<T, H>
202where
203 T: arbitrary::Arbitrary<'a>,
204 H: arbitrary::Arbitrary<'a>,
205{
206 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
207 Ok(Self { header: u.arbitrary()?, body: u.arbitrary()? })
208 }
209}
210
211#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
215#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
216#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
217#[rlp(trailing)]
218pub struct BlockBody<T, H = Header> {
219 pub transactions: Vec<T>,
221 pub ommers: Vec<H>,
223 pub withdrawals: Option<Withdrawals>,
225}
226
227impl<T, H> Default for BlockBody<T, H> {
228 fn default() -> Self {
229 Self { transactions: Vec::new(), ommers: Vec::new(), withdrawals: None }
230 }
231}
232
233impl<T, H> BlockBody<T, H> {
234 #[inline]
236 pub fn transactions(&self) -> impl Iterator<Item = &T> + '_ {
237 self.transactions.iter()
238 }
239
240 pub const fn into_block(self, header: H) -> Block<T, H> {
242 Block { header, body: self }
243 }
244
245 pub fn calculate_ommers_root(&self) -> B256
247 where
248 H: Encodable,
249 {
250 crate::proofs::calculate_ommers_root(&self.ommers)
251 }
252
253 pub fn ommers_hashes(&self) -> impl Iterator<Item = B256> + '_
255 where
256 H: Sealable,
257 {
258 self.ommers.iter().map(|h| h.hash_slow())
259 }
260
261 pub fn calculate_withdrawals_root(&self) -> Option<B256> {
264 self.withdrawals.as_ref().map(|w| crate::proofs::calculate_withdrawals_root(w))
265 }
266
267 pub fn map_ommers<U>(self, f: impl FnMut(H) -> U) -> BlockBody<T, U> {
269 BlockBody {
270 transactions: self.transactions,
271 ommers: self.ommers.into_iter().map(f).collect(),
272 withdrawals: self.withdrawals,
273 }
274 }
275
276 pub fn try_map_ommers<U, E>(
278 self,
279 f: impl FnMut(H) -> Result<U, E>,
280 ) -> Result<BlockBody<T, U>, E> {
281 Ok(BlockBody {
282 transactions: self.transactions,
283 ommers: self.ommers.into_iter().map(f).collect::<Result<Vec<_>, _>>()?,
284 withdrawals: self.withdrawals,
285 })
286 }
287}
288
289impl<T: Transaction, H> BlockBody<T, H> {
290 #[inline]
292 pub fn blob_versioned_hashes_iter(&self) -> impl Iterator<Item = &B256> + '_ {
293 self.eip4844_transactions_iter().filter_map(|tx| tx.blob_versioned_hashes()).flatten()
294 }
295}
296
297impl<T: Typed2718, H> BlockBody<T, H> {
298 #[inline]
300 pub fn has_eip4844_transactions(&self) -> bool {
301 self.transactions.iter().any(|tx| tx.is_eip4844())
302 }
303
304 #[inline]
306 pub fn has_eip7702_transactions(&self) -> bool {
307 self.transactions.iter().any(|tx| tx.is_eip7702())
308 }
309
310 #[inline]
312 pub fn eip4844_transactions_iter(&self) -> impl Iterator<Item = &T> + '_ {
313 self.transactions.iter().filter(|tx| tx.is_eip4844())
314 }
315}
316
317mod block_rlp {
320 use super::*;
321
322 #[derive(RlpDecodable)]
323 #[rlp(trailing)]
324 struct Helper<T, H> {
325 header: H,
326 transactions: Vec<T>,
327 ommers: Vec<H>,
328 withdrawals: Option<Withdrawals>,
329 }
330
331 #[derive(RlpEncodable)]
332 #[rlp(trailing)]
333 pub(crate) struct HelperRef<'a, T, H> {
334 pub(crate) header: &'a H,
335 pub(crate) transactions: &'a Vec<T>,
336 pub(crate) ommers: &'a Vec<H>,
337 pub(crate) withdrawals: Option<&'a Withdrawals>,
338 }
339
340 impl<'a, T, H> HelperRef<'a, T, H> {
341 pub(crate) const fn from_parts(header: &'a H, body: &'a BlockBody<T, H>) -> Self {
342 Self {
343 header,
344 transactions: &body.transactions,
345 ommers: &body.ommers,
346 withdrawals: body.withdrawals.as_ref(),
347 }
348 }
349 }
350
351 impl<'a, T, H> From<&'a Block<T, H>> for HelperRef<'a, T, H> {
352 fn from(block: &'a Block<T, H>) -> Self {
353 let Block { header, body: BlockBody { transactions, ommers, withdrawals } } = block;
354 Self { header, transactions, ommers, withdrawals: withdrawals.as_ref() }
355 }
356 }
357
358 impl<T: Encodable, H: Encodable> Encodable for Block<T, H> {
359 fn encode(&self, out: &mut dyn alloy_rlp::bytes::BufMut) {
360 let helper: HelperRef<'_, T, H> = self.into();
361 helper.encode(out)
362 }
363
364 fn length(&self) -> usize {
365 let helper: HelperRef<'_, T, H> = self.into();
366 helper.length()
367 }
368 }
369
370 impl<T: Decodable, H: Decodable> Decodable for Block<T, H> {
371 fn decode(b: &mut &[u8]) -> alloy_rlp::Result<Self> {
372 let Helper { header, transactions, ommers, withdrawals } = Helper::decode(b)?;
373 Ok(Self { header, body: BlockBody { transactions, ommers, withdrawals } })
374 }
375 }
376}
377
378#[cfg(any(test, feature = "arbitrary"))]
379impl<'a, T, H> arbitrary::Arbitrary<'a> for BlockBody<T, H>
380where
381 T: arbitrary::Arbitrary<'a>,
382 H: arbitrary::Arbitrary<'a>,
383{
384 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
385 let transactions = (0..u.int_in_range(0..=100)?)
387 .map(|_| T::arbitrary(u))
388 .collect::<arbitrary::Result<Vec<_>>>()?;
389
390 let ommers = (0..u.int_in_range(0..=1)?)
392 .map(|_| H::arbitrary(u))
393 .collect::<arbitrary::Result<Vec<_>>>()?;
394
395 Ok(Self { transactions, ommers, withdrawals: u.arbitrary()? })
396 }
397}
398
399#[cfg(test)]
400mod tests {
401 use super::*;
402 use crate::{Signed, TxEnvelope, TxLegacy};
403
404 #[test]
405 fn can_convert_block() {
406 let block: Block<Signed<TxLegacy>> = Block::default();
407 let _: Block<TxEnvelope> = block.convert_transactions();
408 }
409}