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 with_header(mut self, header: H) -> Self {
137 self.header = header;
138 self
139 }
140
141 pub fn rlp_length_for(header: &H, body: &BlockBody<T, H>) -> usize
143 where
144 H: Encodable,
145 T: Encodable,
146 {
147 block_rlp::HelperRef {
148 header,
149 transactions: &body.transactions,
150 ommers: &body.ommers,
151 withdrawals: body.withdrawals.as_ref(),
152 }
153 .length()
154 }
155}
156
157impl<T, H> Default for Block<T, H>
158where
159 H: Default,
160{
161 fn default() -> Self {
162 Self { header: Default::default(), body: Default::default() }
163 }
164}
165
166impl<T, H> From<Block<T, H>> for BlockBody<T, H> {
167 fn from(block: Block<T, H>) -> Self {
168 block.into_body()
169 }
170}
171
172#[cfg(any(test, feature = "arbitrary"))]
173impl<'a, T, H> arbitrary::Arbitrary<'a> for Block<T, H>
174where
175 T: arbitrary::Arbitrary<'a>,
176 H: arbitrary::Arbitrary<'a>,
177{
178 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
179 Ok(Self { header: u.arbitrary()?, body: u.arbitrary()? })
180 }
181}
182
183#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
187#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
188#[rlp(trailing)]
189pub struct BlockBody<T, H = Header> {
190 pub transactions: Vec<T>,
192 pub ommers: Vec<H>,
194 pub withdrawals: Option<Withdrawals>,
196}
197
198impl<T, H> Default for BlockBody<T, H> {
199 fn default() -> Self {
200 Self { transactions: Vec::new(), ommers: Vec::new(), withdrawals: None }
201 }
202}
203
204impl<T, H> BlockBody<T, H> {
205 #[inline]
207 pub fn transactions(&self) -> impl Iterator<Item = &T> + '_ {
208 self.transactions.iter()
209 }
210
211 pub const fn into_block(self, header: H) -> Block<T, H> {
213 Block { header, body: self }
214 }
215
216 pub fn calculate_ommers_root(&self) -> B256
218 where
219 H: Encodable,
220 {
221 crate::proofs::calculate_ommers_root(&self.ommers)
222 }
223
224 pub fn ommers_hashes(&self) -> impl Iterator<Item = B256> + '_
226 where
227 H: Sealable,
228 {
229 self.ommers.iter().map(|h| h.hash_slow())
230 }
231
232 pub fn calculate_withdrawals_root(&self) -> Option<B256> {
235 self.withdrawals.as_ref().map(|w| crate::proofs::calculate_withdrawals_root(w))
236 }
237
238 pub fn map_ommers<U>(self, f: impl FnMut(H) -> U) -> BlockBody<T, U> {
240 BlockBody {
241 transactions: self.transactions,
242 ommers: self.ommers.into_iter().map(f).collect(),
243 withdrawals: self.withdrawals,
244 }
245 }
246
247 pub fn try_map_ommers<U, E>(
249 self,
250 f: impl FnMut(H) -> Result<U, E>,
251 ) -> Result<BlockBody<T, U>, E> {
252 Ok(BlockBody {
253 transactions: self.transactions,
254 ommers: self.ommers.into_iter().map(f).collect::<Result<Vec<_>, _>>()?,
255 withdrawals: self.withdrawals,
256 })
257 }
258}
259
260impl<T: Transaction, H> BlockBody<T, H> {
261 #[inline]
263 pub fn blob_versioned_hashes_iter(&self) -> impl Iterator<Item = &B256> + '_ {
264 self.eip4844_transactions_iter().filter_map(|tx| tx.blob_versioned_hashes()).flatten()
265 }
266}
267
268impl<T: Typed2718, H> BlockBody<T, H> {
269 #[inline]
271 pub fn has_eip4844_transactions(&self) -> bool {
272 self.transactions.iter().any(|tx| tx.is_eip4844())
273 }
274
275 #[inline]
277 pub fn has_eip7702_transactions(&self) -> bool {
278 self.transactions.iter().any(|tx| tx.is_eip7702())
279 }
280
281 #[inline]
283 pub fn eip4844_transactions_iter(&self) -> impl Iterator<Item = &T> + '_ {
284 self.transactions.iter().filter(|tx| tx.is_eip4844())
285 }
286}
287
288mod block_rlp {
291 use super::*;
292
293 #[derive(RlpDecodable)]
294 #[rlp(trailing)]
295 struct Helper<T, H> {
296 header: H,
297 transactions: Vec<T>,
298 ommers: Vec<H>,
299 withdrawals: Option<Withdrawals>,
300 }
301
302 #[derive(RlpEncodable)]
303 #[rlp(trailing)]
304 pub(crate) struct HelperRef<'a, T, H> {
305 pub(crate) header: &'a H,
306 pub(crate) transactions: &'a Vec<T>,
307 pub(crate) ommers: &'a Vec<H>,
308 pub(crate) withdrawals: Option<&'a Withdrawals>,
309 }
310
311 impl<'a, T, H> From<&'a Block<T, H>> for HelperRef<'a, T, H> {
312 fn from(block: &'a Block<T, H>) -> Self {
313 let Block { header, body: BlockBody { transactions, ommers, withdrawals } } = block;
314 Self { header, transactions, ommers, withdrawals: withdrawals.as_ref() }
315 }
316 }
317
318 impl<T: Encodable, H: Encodable> Encodable for Block<T, H> {
319 fn encode(&self, out: &mut dyn alloy_rlp::bytes::BufMut) {
320 let helper: HelperRef<'_, T, H> = self.into();
321 helper.encode(out)
322 }
323
324 fn length(&self) -> usize {
325 let helper: HelperRef<'_, T, H> = self.into();
326 helper.length()
327 }
328 }
329
330 impl<T: Decodable, H: Decodable> Decodable for Block<T, H> {
331 fn decode(b: &mut &[u8]) -> alloy_rlp::Result<Self> {
332 let Helper { header, transactions, ommers, withdrawals } = Helper::decode(b)?;
333 Ok(Self { header, body: BlockBody { transactions, ommers, withdrawals } })
334 }
335 }
336}
337
338#[cfg(any(test, feature = "arbitrary"))]
339impl<'a, T, H> arbitrary::Arbitrary<'a> for BlockBody<T, H>
340where
341 T: arbitrary::Arbitrary<'a>,
342 H: arbitrary::Arbitrary<'a>,
343{
344 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
345 let transactions = (0..u.int_in_range(0..=100)?)
347 .map(|_| T::arbitrary(u))
348 .collect::<arbitrary::Result<Vec<_>>>()?;
349
350 let ommers = (0..u.int_in_range(0..=1)?)
352 .map(|_| H::arbitrary(u))
353 .collect::<arbitrary::Result<Vec<_>>>()?;
354
355 Ok(Self { transactions, ommers, withdrawals: u.arbitrary()? })
356 }
357}
358
359#[cfg(test)]
360mod tests {
361 use super::*;
362 use crate::{Signed, TxEnvelope, TxLegacy};
363
364 #[test]
365 fn can_convert_block() {
366 let block: Block<Signed<TxLegacy>> = Block::default();
367 let _: Block<TxEnvelope> = block.convert_transactions();
368 }
369}