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::{keccak256, Sealable, Sealed, 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: Encodable2718> Block<T, Header> {
186 pub fn from_transactions(
191 mut header: Header,
192 transactions: impl IntoIterator<Item = T>,
193 ) -> Self {
194 let transactions: Vec<T> = transactions.into_iter().collect();
195 header.transactions_root = crate::proofs::calculate_transaction_root(&transactions);
196 header.ommers_hash = crate::EMPTY_OMMER_ROOT_HASH;
197 Self::new(header, BlockBody { transactions, ommers: Vec::new(), withdrawals: None })
198 }
199}
200
201impl<T, H> Default for Block<T, H>
202where
203 H: Default,
204{
205 fn default() -> Self {
206 Self { header: Default::default(), body: Default::default() }
207 }
208}
209
210impl<T, H> From<Block<T, H>> for BlockBody<T, H> {
211 fn from(block: Block<T, H>) -> Self {
212 block.into_body()
213 }
214}
215
216#[cfg(any(test, feature = "arbitrary"))]
217impl<'a, T, H> arbitrary::Arbitrary<'a> for Block<T, H>
218where
219 T: arbitrary::Arbitrary<'a>,
220 H: arbitrary::Arbitrary<'a>,
221{
222 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
223 Ok(Self { header: u.arbitrary()?, body: u.arbitrary()? })
224 }
225}
226
227#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
231#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
232#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
233#[rlp(trailing)]
234pub struct BlockBody<T, H = Header> {
235 pub transactions: Vec<T>,
237 pub ommers: Vec<H>,
239 pub withdrawals: Option<Withdrawals>,
241}
242
243impl<T, H> Default for BlockBody<T, H> {
244 fn default() -> Self {
245 Self { transactions: Vec::new(), ommers: Vec::new(), withdrawals: None }
246 }
247}
248
249impl<T, H> BlockBody<T, H> {
250 #[inline]
252 pub fn transactions(&self) -> impl Iterator<Item = &T> + '_ {
253 self.transactions.iter()
254 }
255
256 pub const fn into_block(self, header: H) -> Block<T, H> {
258 Block { header, body: self }
259 }
260
261 pub fn calculate_ommers_root(&self) -> B256
263 where
264 H: Encodable,
265 {
266 crate::proofs::calculate_ommers_root(&self.ommers)
267 }
268
269 pub fn ommers_hashes(&self) -> impl Iterator<Item = B256> + '_
271 where
272 H: Sealable,
273 {
274 self.ommers.iter().map(|h| h.hash_slow())
275 }
276
277 pub fn calculate_withdrawals_root(&self) -> Option<B256> {
280 self.withdrawals.as_ref().map(|w| crate::proofs::calculate_withdrawals_root(w))
281 }
282
283 pub fn map_ommers<U>(self, f: impl FnMut(H) -> U) -> BlockBody<T, U> {
285 BlockBody {
286 transactions: self.transactions,
287 ommers: self.ommers.into_iter().map(f).collect(),
288 withdrawals: self.withdrawals,
289 }
290 }
291
292 pub fn try_map_ommers<U, E>(
294 self,
295 f: impl FnMut(H) -> Result<U, E>,
296 ) -> Result<BlockBody<T, U>, E> {
297 Ok(BlockBody {
298 transactions: self.transactions,
299 ommers: self.ommers.into_iter().map(f).collect::<Result<Vec<_>, _>>()?,
300 withdrawals: self.withdrawals,
301 })
302 }
303}
304
305impl<T: Transaction, H> BlockBody<T, H> {
306 #[inline]
308 pub fn blob_versioned_hashes_iter(&self) -> impl Iterator<Item = &B256> + '_ {
309 self.eip4844_transactions_iter().filter_map(|tx| tx.blob_versioned_hashes()).flatten()
310 }
311}
312
313impl<T: Typed2718, H> BlockBody<T, H> {
314 #[inline]
316 pub fn has_eip4844_transactions(&self) -> bool {
317 self.transactions.iter().any(|tx| tx.is_eip4844())
318 }
319
320 #[inline]
322 pub fn has_eip7702_transactions(&self) -> bool {
323 self.transactions.iter().any(|tx| tx.is_eip7702())
324 }
325
326 #[inline]
328 pub fn eip4844_transactions_iter(&self) -> impl Iterator<Item = &T> + '_ {
329 self.transactions.iter().filter(|tx| tx.is_eip4844())
330 }
331}
332
333mod block_rlp {
336 use super::*;
337
338 #[derive(RlpDecodable)]
339 #[rlp(trailing)]
340 struct Helper<T, H> {
341 header: H,
342 transactions: Vec<T>,
343 ommers: Vec<H>,
344 withdrawals: Option<Withdrawals>,
345 }
346
347 #[derive(RlpEncodable)]
348 #[rlp(trailing)]
349 pub(crate) struct HelperRef<'a, T, H> {
350 pub(crate) header: &'a H,
351 pub(crate) transactions: &'a Vec<T>,
352 pub(crate) ommers: &'a Vec<H>,
353 pub(crate) withdrawals: Option<&'a Withdrawals>,
354 }
355
356 impl<'a, T, H> HelperRef<'a, T, H> {
357 pub(crate) const fn from_parts(header: &'a H, body: &'a BlockBody<T, H>) -> Self {
358 Self {
359 header,
360 transactions: &body.transactions,
361 ommers: &body.ommers,
362 withdrawals: body.withdrawals.as_ref(),
363 }
364 }
365 }
366
367 impl<'a, T, H> From<&'a Block<T, H>> for HelperRef<'a, T, H> {
368 fn from(block: &'a Block<T, H>) -> Self {
369 let Block { header, body: BlockBody { transactions, ommers, withdrawals } } = block;
370 Self { header, transactions, ommers, withdrawals: withdrawals.as_ref() }
371 }
372 }
373
374 impl<T: Encodable, H: Encodable> Encodable for Block<T, H> {
375 fn encode(&self, out: &mut dyn alloy_rlp::bytes::BufMut) {
376 let helper: HelperRef<'_, T, H> = self.into();
377 helper.encode(out)
378 }
379
380 fn length(&self) -> usize {
381 let helper: HelperRef<'_, T, H> = self.into();
382 helper.length()
383 }
384 }
385
386 impl<T: Decodable, H: Decodable> Decodable for Block<T, H> {
387 fn decode(b: &mut &[u8]) -> alloy_rlp::Result<Self> {
388 let Helper { header, transactions, ommers, withdrawals } = Helper::decode(b)?;
389 Ok(Self { header, body: BlockBody { transactions, ommers, withdrawals } })
390 }
391 }
392
393 impl<T: Decodable, H: Decodable> Block<T, H> {
394 pub fn decode_sealed(buf: &mut &[u8]) -> alloy_rlp::Result<Sealed<Self>> {
399 let block_rlp_head = alloy_rlp::Header::decode(buf)?;
401 if !block_rlp_head.list {
402 return Err(alloy_rlp::Error::UnexpectedString);
403 }
404
405 let header_start = *buf;
407 let header = H::decode(buf)?;
408 let header_hash = keccak256(&header_start[..header_start.len() - buf.len()]);
409
410 let transactions = Vec::<T>::decode(buf)?;
412 let ommers = Vec::<H>::decode(buf)?;
413 let withdrawals = if buf.is_empty() { None } else { Some(Decodable::decode(buf)?) };
414
415 let block = Self { header, body: BlockBody { transactions, ommers, withdrawals } };
416
417 Ok(Sealed::new_unchecked(block, header_hash))
418 }
419 }
420}
421
422#[cfg(any(test, feature = "arbitrary"))]
423impl<'a, T, H> arbitrary::Arbitrary<'a> for BlockBody<T, H>
424where
425 T: arbitrary::Arbitrary<'a>,
426 H: arbitrary::Arbitrary<'a>,
427{
428 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
429 let transactions = (0..u.int_in_range(0..=100)?)
431 .map(|_| T::arbitrary(u))
432 .collect::<arbitrary::Result<Vec<_>>>()?;
433
434 let ommers = (0..u.int_in_range(0..=1)?)
436 .map(|_| H::arbitrary(u))
437 .collect::<arbitrary::Result<Vec<_>>>()?;
438
439 Ok(Self { transactions, ommers, withdrawals: u.arbitrary()? })
440 }
441}
442
443#[cfg(test)]
444mod tests {
445 use super::*;
446 use crate::{Signed, TxEnvelope, TxLegacy};
447 use alloy_rlp::Encodable;
448
449 #[test]
450 fn can_convert_block() {
451 let block: Block<Signed<TxLegacy>> = Block::default();
452 let _: Block<TxEnvelope> = block.convert_transactions();
453 }
454
455 #[test]
456 fn decode_sealed_produces_correct_hash() {
457 let block: Block<TxEnvelope> = Block::default();
458 let expected_hash = block.header.hash_slow();
459
460 let mut encoded = Vec::new();
461 block.encode(&mut encoded);
462
463 let mut buf = encoded.as_slice();
464 let sealed = Block::<TxEnvelope>::decode_sealed(&mut buf).unwrap();
465
466 assert_eq!(sealed.hash(), expected_hash);
467 assert_eq!(*sealed.inner(), block);
468 }
469
470 #[test]
471 fn header_decode_sealed_produces_correct_hash() {
472 let header = Header::default();
473 let expected_hash = header.hash_slow();
474
475 let mut encoded = Vec::new();
476 header.encode(&mut encoded);
477
478 let mut buf = encoded.as_slice();
479 let sealed = Header::decode_sealed(&mut buf).unwrap();
480
481 assert_eq!(sealed.hash(), expected_hash);
482 assert_eq!(*sealed.inner(), header);
483 assert!(buf.is_empty());
484 }
485
486 #[test]
487 fn decode_sealed_roundtrip_with_transactions() {
488 use crate::{SignableTransaction, TxLegacy};
489 use alloy_primitives::{Address, Signature, TxKind, U256};
490
491 let tx = TxLegacy {
492 nonce: 1,
493 gas_price: 100,
494 gas_limit: 21000,
495 to: TxKind::Call(Address::ZERO),
496 value: U256::from(1000),
497 input: Default::default(),
498 chain_id: Some(1),
499 };
500 let sig = Signature::new(U256::from(1), U256::from(2), false);
501 let signed = tx.into_signed(sig);
502 let envelope: TxEnvelope = signed.into();
503
504 let block = Block {
505 header: Header { number: 42, gas_limit: 30_000_000, ..Default::default() },
506 body: BlockBody { transactions: vec![envelope], ommers: vec![], withdrawals: None },
507 };
508
509 let expected_hash = block.header.hash_slow();
510
511 let mut encoded = Vec::new();
512 block.encode(&mut encoded);
513
514 let mut buf = encoded.as_slice();
515 let sealed = Block::<TxEnvelope>::decode_sealed(&mut buf).unwrap();
516
517 assert_eq!(sealed.hash(), expected_hash);
518 assert_eq!(sealed.header.number, 42);
519 assert_eq!(sealed.body.transactions.len(), 1);
520 assert!(buf.is_empty());
521 }
522}
523
524#[cfg(all(test, feature = "arbitrary"))]
525mod fuzz_tests {
526 use super::*;
527 use alloy_primitives::Bytes;
528 use alloy_rlp::{Decodable, Encodable};
529 use arbitrary::{Arbitrary, Unstructured};
530
531 #[test]
532 fn fuzz_decode_sealed_block_roundtrip() {
533 let mut data = [0u8; 8192];
534 for (i, byte) in data.iter_mut().enumerate() {
535 *byte = (i.wrapping_mul(31).wrapping_add(17)) as u8;
536 }
537
538 let mut success_count = 0;
539 for offset in 0..200 {
540 let slice = &data[offset..];
541 let mut u = Unstructured::new(slice);
542
543 if let Ok(block) = Block::<Bytes, Header>::arbitrary(&mut u) {
544 let expected_hash = block.header.hash_slow();
545
546 let mut encoded = Vec::new();
547 block.encode(&mut encoded);
548
549 let mut buf = encoded.as_slice();
550 if Block::<Bytes>::decode(&mut buf).is_ok() {
551 let mut buf = encoded.as_slice();
552 let sealed = Block::<Bytes>::decode_sealed(&mut buf).unwrap();
553
554 assert_eq!(sealed.hash(), expected_hash);
555 assert_eq!(*sealed.inner(), block);
556 success_count += 1;
557 }
558 }
559 }
560 assert!(success_count > 0, "No blocks were successfully tested");
561 }
562
563 #[test]
564 fn fuzz_header_decode_sealed_roundtrip() {
565 let mut data = [0u8; 4096];
566 for (i, byte) in data.iter_mut().enumerate() {
567 *byte = (i.wrapping_mul(37).wrapping_add(23)) as u8;
568 }
569
570 let mut success_count = 0;
571 for offset in 0..200 {
572 let slice = &data[offset..];
573 let mut u = Unstructured::new(slice);
574
575 if let Ok(header) = Header::arbitrary(&mut u) {
576 let expected_hash = header.hash_slow();
577
578 let mut encoded = Vec::new();
579 header.encode(&mut encoded);
580
581 let mut buf = encoded.as_slice();
582 if Header::decode(&mut buf).is_ok() {
583 let mut buf = encoded.as_slice();
584 let sealed = Header::decode_sealed(&mut buf).unwrap();
585
586 assert_eq!(sealed.hash(), expected_hash);
587 assert_eq!(*sealed.inner(), header);
588 success_count += 1;
589 }
590 }
591 }
592 assert!(success_count > 0, "No headers were successfully tested");
593 }
594}