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_encoded_from_parts(header: &H, body: &BlockBody<T, H>) -> Vec<u8>
150 where
151 H: Encodable,
152 T: Encodable,
153 {
154 let helper = block_rlp::HelperRef::from_parts(header, body);
155 let mut buf = Vec::with_capacity(helper.length());
156 helper.encode(&mut buf);
157 buf
158 }
159
160 pub fn rlp_encode_from_parts(
164 header: &H,
165 body: &BlockBody<T, H>,
166 out: &mut dyn alloy_rlp::bytes::BufMut,
167 ) where
168 H: Encodable,
169 T: Encodable,
170 {
171 block_rlp::HelperRef::from_parts(header, body).encode(out)
172 }
173
174 pub fn rlp_length_for(header: &H, body: &BlockBody<T, H>) -> usize
176 where
177 H: Encodable,
178 T: Encodable,
179 {
180 block_rlp::HelperRef::from_parts(header, body).length()
181 }
182}
183
184impl<T, H> Default for Block<T, H>
185where
186 H: Default,
187{
188 fn default() -> Self {
189 Self { header: Default::default(), body: Default::default() }
190 }
191}
192
193impl<T, H> From<Block<T, H>> for BlockBody<T, H> {
194 fn from(block: Block<T, H>) -> Self {
195 block.into_body()
196 }
197}
198
199#[cfg(any(test, feature = "arbitrary"))]
200impl<'a, T, H> arbitrary::Arbitrary<'a> for Block<T, H>
201where
202 T: arbitrary::Arbitrary<'a>,
203 H: arbitrary::Arbitrary<'a>,
204{
205 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
206 Ok(Self { header: u.arbitrary()?, body: u.arbitrary()? })
207 }
208}
209
210#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
214#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
215#[rlp(trailing)]
216pub struct BlockBody<T, H = Header> {
217 pub transactions: Vec<T>,
219 pub ommers: Vec<H>,
221 pub withdrawals: Option<Withdrawals>,
223}
224
225impl<T, H> Default for BlockBody<T, H> {
226 fn default() -> Self {
227 Self { transactions: Vec::new(), ommers: Vec::new(), withdrawals: None }
228 }
229}
230
231impl<T, H> BlockBody<T, H> {
232 #[inline]
234 pub fn transactions(&self) -> impl Iterator<Item = &T> + '_ {
235 self.transactions.iter()
236 }
237
238 pub const fn into_block(self, header: H) -> Block<T, H> {
240 Block { header, body: self }
241 }
242
243 pub fn calculate_ommers_root(&self) -> B256
245 where
246 H: Encodable,
247 {
248 crate::proofs::calculate_ommers_root(&self.ommers)
249 }
250
251 pub fn ommers_hashes(&self) -> impl Iterator<Item = B256> + '_
253 where
254 H: Sealable,
255 {
256 self.ommers.iter().map(|h| h.hash_slow())
257 }
258
259 pub fn calculate_withdrawals_root(&self) -> Option<B256> {
262 self.withdrawals.as_ref().map(|w| crate::proofs::calculate_withdrawals_root(w))
263 }
264
265 pub fn map_ommers<U>(self, f: impl FnMut(H) -> U) -> BlockBody<T, U> {
267 BlockBody {
268 transactions: self.transactions,
269 ommers: self.ommers.into_iter().map(f).collect(),
270 withdrawals: self.withdrawals,
271 }
272 }
273
274 pub fn try_map_ommers<U, E>(
276 self,
277 f: impl FnMut(H) -> Result<U, E>,
278 ) -> Result<BlockBody<T, U>, E> {
279 Ok(BlockBody {
280 transactions: self.transactions,
281 ommers: self.ommers.into_iter().map(f).collect::<Result<Vec<_>, _>>()?,
282 withdrawals: self.withdrawals,
283 })
284 }
285}
286
287impl<T: Transaction, H> BlockBody<T, H> {
288 #[inline]
290 pub fn blob_versioned_hashes_iter(&self) -> impl Iterator<Item = &B256> + '_ {
291 self.eip4844_transactions_iter().filter_map(|tx| tx.blob_versioned_hashes()).flatten()
292 }
293}
294
295impl<T: Typed2718, H> BlockBody<T, H> {
296 #[inline]
298 pub fn has_eip4844_transactions(&self) -> bool {
299 self.transactions.iter().any(|tx| tx.is_eip4844())
300 }
301
302 #[inline]
304 pub fn has_eip7702_transactions(&self) -> bool {
305 self.transactions.iter().any(|tx| tx.is_eip7702())
306 }
307
308 #[inline]
310 pub fn eip4844_transactions_iter(&self) -> impl Iterator<Item = &T> + '_ {
311 self.transactions.iter().filter(|tx| tx.is_eip4844())
312 }
313}
314
315mod block_rlp {
318 use super::*;
319
320 #[derive(RlpDecodable)]
321 #[rlp(trailing)]
322 struct Helper<T, H> {
323 header: H,
324 transactions: Vec<T>,
325 ommers: Vec<H>,
326 withdrawals: Option<Withdrawals>,
327 }
328
329 #[derive(RlpEncodable)]
330 #[rlp(trailing)]
331 pub(crate) struct HelperRef<'a, T, H> {
332 pub(crate) header: &'a H,
333 pub(crate) transactions: &'a Vec<T>,
334 pub(crate) ommers: &'a Vec<H>,
335 pub(crate) withdrawals: Option<&'a Withdrawals>,
336 }
337
338 impl<'a, T, H> HelperRef<'a, T, H> {
339 pub(crate) const fn from_parts(header: &'a H, body: &'a BlockBody<T, H>) -> Self {
340 Self {
341 header,
342 transactions: &body.transactions,
343 ommers: &body.ommers,
344 withdrawals: body.withdrawals.as_ref(),
345 }
346 }
347 }
348
349 impl<'a, T, H> From<&'a Block<T, H>> for HelperRef<'a, T, H> {
350 fn from(block: &'a Block<T, H>) -> Self {
351 let Block { header, body: BlockBody { transactions, ommers, withdrawals } } = block;
352 Self { header, transactions, ommers, withdrawals: withdrawals.as_ref() }
353 }
354 }
355
356 impl<T: Encodable, H: Encodable> Encodable for Block<T, H> {
357 fn encode(&self, out: &mut dyn alloy_rlp::bytes::BufMut) {
358 let helper: HelperRef<'_, T, H> = self.into();
359 helper.encode(out)
360 }
361
362 fn length(&self) -> usize {
363 let helper: HelperRef<'_, T, H> = self.into();
364 helper.length()
365 }
366 }
367
368 impl<T: Decodable, H: Decodable> Decodable for Block<T, H> {
369 fn decode(b: &mut &[u8]) -> alloy_rlp::Result<Self> {
370 let Helper { header, transactions, ommers, withdrawals } = Helper::decode(b)?;
371 Ok(Self { header, body: BlockBody { transactions, ommers, withdrawals } })
372 }
373 }
374}
375
376#[cfg(any(test, feature = "arbitrary"))]
377impl<'a, T, H> arbitrary::Arbitrary<'a> for BlockBody<T, H>
378where
379 T: arbitrary::Arbitrary<'a>,
380 H: arbitrary::Arbitrary<'a>,
381{
382 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
383 let transactions = (0..u.int_in_range(0..=100)?)
385 .map(|_| T::arbitrary(u))
386 .collect::<arbitrary::Result<Vec<_>>>()?;
387
388 let ommers = (0..u.int_in_range(0..=1)?)
390 .map(|_| H::arbitrary(u))
391 .collect::<arbitrary::Result<Vec<_>>>()?;
392
393 Ok(Self { transactions, ommers, withdrawals: u.arbitrary()? })
394 }
395}
396
397#[cfg(test)]
398mod tests {
399 use super::*;
400 use crate::{Signed, TxEnvelope, TxLegacy};
401
402 #[test]
403 fn can_convert_block() {
404 let block: Block<Signed<TxLegacy>> = Block::default();
405 let _: Block<TxEnvelope> = block.convert_transactions();
406 }
407}