reth_primitives_traits/block/
sealed.rs1use crate::{
4 block::{error::BlockRecoveryError, header::BlockHeader, RecoveredBlock},
5 transaction::signed::{RecoveryError, SignedTransaction},
6 Block, BlockBody, GotExpected, InMemorySize, SealedHeader,
7};
8use alloc::vec::Vec;
9use alloy_consensus::BlockHeader as _;
10use alloy_eips::{eip1898::BlockWithParent, BlockNumHash};
11use alloy_primitives::{Address, BlockHash, Sealable, Sealed, B256};
12use alloy_rlp::{Decodable, Encodable};
13use bytes::BufMut;
14use core::ops::Deref;
15
16#[derive(Debug, Clone, PartialEq, Eq)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22pub struct SealedBlock<B: Block> {
23 header: SealedHeader<B::Header>,
25 body: B::Body,
27}
28
29impl<B: Block> SealedBlock<B> {
30 pub fn seal_slow(block: B) -> Self {
35 let hash = block.header().hash_slow();
36 Self::new_unchecked(block, hash)
37 }
38
39 #[inline]
43 pub fn new_unchecked(block: B, hash: BlockHash) -> Self {
44 let (header, body) = block.split();
45 Self { header: SealedHeader::new(header, hash), body }
46 }
47
48 pub fn new_unhashed(block: B) -> Self {
50 let (header, body) = block.split();
51 Self { header: SealedHeader::new_unhashed(header), body }
52 }
53
54 pub fn seal_parts(header: B::Header, body: B::Body) -> Self {
60 Self::seal_slow(B::new(header, body))
61 }
62
63 pub fn from_parts_unhashed(header: B::Header, body: B::Body) -> Self {
65 Self::new_unhashed(B::new(header, body))
66 }
67
68 pub fn from_parts_unchecked(header: B::Header, body: B::Body, hash: BlockHash) -> Self {
70 Self::new_unchecked(B::new(header, body), hash)
71 }
72
73 pub fn from_sealed_parts(header: SealedHeader<B::Header>, body: B::Body) -> Self {
75 let (header, hash) = header.split();
76 Self::from_parts_unchecked(header, body, hash)
77 }
78
79 #[inline]
81 pub fn hash_ref(&self) -> &BlockHash {
82 self.header.hash_ref()
83 }
84
85 #[inline]
87 pub fn hash(&self) -> B256 {
88 self.header.hash()
89 }
90
91 #[doc(alias = "into_components")]
93 pub fn split(self) -> (B, BlockHash) {
94 let (header, hash) = self.header.split();
95 (B::new(header, self.body), hash)
96 }
97
98 pub fn into_block(self) -> B {
100 self.unseal()
101 }
102
103 pub fn unseal(self) -> B {
105 let header = self.header.unseal();
106 B::new(header, self.body)
107 }
108
109 pub fn clone_block(&self) -> B {
111 B::new(self.header.clone_header(), self.body.clone())
112 }
113
114 pub const fn with_senders(self, senders: Vec<Address>) -> RecoveredBlock<B> {
118 RecoveredBlock::new_sealed(self, senders)
119 }
120
121 pub fn try_with_senders(
129 self,
130 senders: Vec<Address>,
131 ) -> Result<RecoveredBlock<B>, BlockRecoveryError<Self>> {
132 RecoveredBlock::try_recover_sealed_with_senders(self, senders)
133 }
134
135 pub fn try_with_senders_unchecked(
143 self,
144 senders: Vec<Address>,
145 ) -> Result<RecoveredBlock<B>, BlockRecoveryError<Self>> {
146 RecoveredBlock::try_recover_sealed_with_senders_unchecked(self, senders)
147 }
148
149 pub fn try_recover(self) -> Result<RecoveredBlock<B>, BlockRecoveryError<Self>> {
154 RecoveredBlock::try_recover_sealed(self)
155 }
156
157 pub fn try_recover_unchecked(self) -> Result<RecoveredBlock<B>, BlockRecoveryError<Self>> {
162 RecoveredBlock::try_recover_sealed_unchecked(self)
163 }
164
165 pub const fn header(&self) -> &B::Header {
167 self.header.header()
168 }
169
170 pub const fn body(&self) -> &B::Body {
172 &self.body
173 }
174
175 pub fn rlp_length(&self) -> usize {
177 B::rlp_length(self.header(), self.body())
178 }
179
180 pub fn senders(&self) -> Result<Vec<Address>, RecoveryError> {
184 self.body().recover_signers()
185 }
186
187 pub fn num_hash(&self) -> BlockNumHash {
189 BlockNumHash::new(self.number(), self.hash())
190 }
191
192 pub fn block_with_parent(&self) -> BlockWithParent {
194 BlockWithParent { parent: self.parent_hash(), block: self.num_hash() }
195 }
196
197 pub const fn sealed_header(&self) -> &SealedHeader<B::Header> {
199 &self.header
200 }
201
202 pub fn sealed_header_ref(&self) -> SealedHeader<&B::Header> {
204 SealedHeader::new(self.header(), self.hash())
205 }
206
207 pub fn clone_sealed_header(&self) -> SealedHeader<B::Header> {
209 self.header.clone()
210 }
211
212 pub fn into_sealed_header(self) -> SealedHeader<B::Header> {
214 self.header
215 }
216
217 pub fn into_header(self) -> B::Header {
219 self.header.unseal()
220 }
221
222 pub fn into_body(self) -> B::Body {
224 self.body
225 }
226
227 pub fn split_header_body(self) -> (B::Header, B::Body) {
229 let header = self.header.unseal();
230 (header, self.body)
231 }
232
233 pub fn split_sealed_header_body(self) -> (SealedHeader<B::Header>, B::Body) {
235 (self.header, self.body)
236 }
237
238 #[inline]
240 pub fn blob_versioned_hashes_iter(&self) -> impl Iterator<Item = &B256> + '_ {
241 self.body().blob_versioned_hashes_iter()
242 }
243
244 #[inline]
246 pub fn transaction_count(&self) -> usize {
247 self.body().transaction_count()
248 }
249
250 pub fn ensure_transaction_root_valid(&self) -> Result<(), GotExpected<B256>> {
263 let calculated_root = self.body().calculate_tx_root();
264
265 if self.header().transactions_root() != calculated_root {
266 return Err(GotExpected {
267 got: calculated_root,
268 expected: self.header().transactions_root(),
269 })
270 }
271
272 Ok(())
273 }
274}
275
276impl<B> From<B> for SealedBlock<B>
277where
278 B: Block,
279{
280 fn from(block: B) -> Self {
281 Self::seal_slow(block)
282 }
283}
284
285impl<B> Default for SealedBlock<B>
286where
287 B: Block + Default,
288{
289 fn default() -> Self {
290 Self::seal_slow(Default::default())
291 }
292}
293
294impl<B: Block> InMemorySize for SealedBlock<B> {
295 #[inline]
296 fn size(&self) -> usize {
297 self.body.size() + self.header.size()
298 }
299}
300
301impl<B: Block> Deref for SealedBlock<B> {
302 type Target = B::Header;
303
304 fn deref(&self) -> &Self::Target {
305 self.header()
306 }
307}
308
309impl<B: Block> Encodable for SealedBlock<B> {
310 fn encode(&self, out: &mut dyn BufMut) {
311 self.clone().into_block().encode(out);
313 }
314}
315
316impl<B: Block> Decodable for SealedBlock<B> {
317 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
318 let block = B::decode(buf)?;
319 Ok(Self::seal_slow(block))
320 }
321}
322
323impl<B: Block> From<SealedBlock<B>> for Sealed<B> {
324 fn from(value: SealedBlock<B>) -> Self {
325 let (block, hash) = value.split();
326 Self::new_unchecked(block, hash)
327 }
328}
329
330impl<B: Block> From<Sealed<B>> for SealedBlock<B> {
331 fn from(value: Sealed<B>) -> Self {
332 let (block, hash) = value.into_parts();
333 Self::new_unchecked(block, hash)
334 }
335}
336
337impl<T, H> SealedBlock<alloy_consensus::Block<T, H>>
338where
339 T: Decodable + SignedTransaction,
340 H: BlockHeader,
341{
342 pub fn decode_sealed(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
349 let sealed = alloy_consensus::Block::<T, H>::decode_sealed(buf)?;
350 let (block, hash) = sealed.into_parts();
351 Ok(Self::new_unchecked(block, hash))
352 }
353}
354
355#[cfg(any(test, feature = "arbitrary"))]
356impl<'a, B> arbitrary::Arbitrary<'a> for SealedBlock<B>
357where
358 B: Block + arbitrary::Arbitrary<'a>,
359{
360 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
361 let block = B::arbitrary(u)?;
362 Ok(Self::seal_slow(block))
363 }
364}
365
366#[cfg(any(test, feature = "test-utils"))]
367impl<B: crate::test_utils::TestBlock> SealedBlock<B> {
368 pub const fn header_mut(&mut self) -> &mut B::Header {
370 self.header.header_mut()
371 }
372
373 pub fn set_hash(&mut self, hash: BlockHash) {
375 self.header.set_hash(hash)
376 }
377
378 pub const fn body_mut(&mut self) -> &mut B::Body {
380 &mut self.body
381 }
382
383 pub fn set_parent_hash(&mut self, hash: BlockHash) {
385 self.header.set_parent_hash(hash)
386 }
387
388 pub fn set_block_number(&mut self, number: alloy_primitives::BlockNumber) {
390 self.header.set_block_number(number)
391 }
392
393 pub fn set_timestamp(&mut self, timestamp: u64) {
395 self.header.set_timestamp(timestamp)
396 }
397
398 pub fn set_state_root(&mut self, state_root: alloy_primitives::B256) {
400 self.header.set_state_root(state_root)
401 }
402
403 pub fn set_difficulty(&mut self, difficulty: alloy_primitives::U256) {
405 self.header.set_difficulty(difficulty)
406 }
407}
408
409#[cfg(feature = "serde-bincode-compat")]
411pub(super) mod serde_bincode_compat {
412 use crate::{
413 serde_bincode_compat::{self, BincodeReprFor, SerdeBincodeCompat},
414 Block,
415 };
416 use serde::{Deserialize, Deserializer, Serialize, Serializer};
417 use serde_with::{serde_as, DeserializeAs, SerializeAs};
418
419 #[serde_as]
439 #[derive(derive_more::Debug, Serialize, Deserialize)]
440 pub struct SealedBlock<
441 'a,
442 T: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static,
443 > {
444 #[serde(
445 bound = "serde_bincode_compat::SealedHeader<'a, T::Header>: Serialize + serde::de::DeserializeOwned"
446 )]
447 header: serde_bincode_compat::SealedHeader<'a, T::Header>,
448 body: BincodeReprFor<'a, T::Body>,
449 }
450
451 impl<'a, T: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static>
452 From<&'a super::SealedBlock<T>> for SealedBlock<'a, T>
453 {
454 fn from(value: &'a super::SealedBlock<T>) -> Self {
455 Self { header: value.header.as_repr(), body: value.body.as_repr() }
456 }
457 }
458
459 impl<'a, T: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static>
460 From<SealedBlock<'a, T>> for super::SealedBlock<T>
461 {
462 fn from(value: SealedBlock<'a, T>) -> Self {
463 Self::from_sealed_parts(value.header.into(), SerdeBincodeCompat::from_repr(value.body))
464 }
465 }
466
467 impl<T: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static>
468 SerializeAs<super::SealedBlock<T>> for SealedBlock<'_, T>
469 {
470 fn serialize_as<S>(source: &super::SealedBlock<T>, serializer: S) -> Result<S::Ok, S::Error>
471 where
472 S: Serializer,
473 {
474 SealedBlock::from(source).serialize(serializer)
475 }
476 }
477
478 impl<'de, T: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static>
479 DeserializeAs<'de, super::SealedBlock<T>> for SealedBlock<'de, T>
480 {
481 fn deserialize_as<D>(deserializer: D) -> Result<super::SealedBlock<T>, D::Error>
482 where
483 D: Deserializer<'de>,
484 {
485 SealedBlock::deserialize(deserializer).map(Into::into)
486 }
487 }
488
489 impl<T: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static>
490 SerdeBincodeCompat for super::SealedBlock<T>
491 {
492 type BincodeRepr<'a> = SealedBlock<'a, T>;
493
494 fn as_repr(&self) -> Self::BincodeRepr<'_> {
495 self.into()
496 }
497
498 fn from_repr(repr: Self::BincodeRepr<'_>) -> Self {
499 repr.into()
500 }
501 }
502}
503
504#[cfg(test)]
505mod tests {
506 use super::*;
507 use alloy_rlp::{Decodable, Encodable};
508
509 #[test]
510 fn test_sealed_block_rlp_roundtrip() {
511 let header = alloy_consensus::Header {
513 number: 42,
514 gas_limit: 30_000_000,
515 gas_used: 21_000,
516 timestamp: 1_000_000,
517 base_fee_per_gas: Some(1_000_000_000),
518 ..Default::default()
519 };
520
521 let tx = alloy_consensus::TxLegacy {
523 chain_id: Some(1),
524 nonce: 0,
525 gas_price: 21_000_000_000,
526 gas_limit: 21_000,
527 to: alloy_primitives::TxKind::Call(Address::ZERO),
528 value: alloy_primitives::U256::from(100),
529 input: alloy_primitives::Bytes::default(),
530 };
531
532 let tx_signed =
533 alloy_consensus::TxEnvelope::Legacy(alloy_consensus::Signed::new_unchecked(
534 tx,
535 alloy_primitives::Signature::test_signature(),
536 B256::ZERO,
537 ));
538
539 let body = alloy_consensus::BlockBody {
541 transactions: vec![tx_signed],
542 ommers: vec![],
543 withdrawals: Some(Default::default()),
544 };
545
546 let block = alloy_consensus::Block::new(header, body);
548
549 let sealed_block = SealedBlock::seal_slow(block);
551
552 let mut encoded = Vec::new();
554 sealed_block.encode(&mut encoded);
555
556 let decoded = SealedBlock::<
558 alloy_consensus::Block<alloy_consensus::TxEnvelope, alloy_consensus::Header>,
559 >::decode(&mut encoded.as_slice())
560 .expect("Failed to decode sealed block");
561
562 assert_eq!(sealed_block.hash(), decoded.hash());
564 assert_eq!(sealed_block.header().number, decoded.header().number);
565 assert_eq!(sealed_block.header().state_root, decoded.header().state_root);
566 assert_eq!(sealed_block.body().transactions.len(), decoded.body().transactions.len());
567 }
568
569 #[test]
570 fn test_decode_sealed_produces_correct_hash() {
571 let header = alloy_consensus::Header {
573 number: 42,
574 gas_limit: 30_000_000,
575 gas_used: 21_000,
576 timestamp: 1_000_000,
577 base_fee_per_gas: Some(1_000_000_000),
578 ..Default::default()
579 };
580
581 let tx = alloy_consensus::TxLegacy {
583 chain_id: Some(1),
584 nonce: 0,
585 gas_price: 21_000_000_000,
586 gas_limit: 21_000,
587 to: alloy_primitives::TxKind::Call(Address::ZERO),
588 value: alloy_primitives::U256::from(100),
589 input: alloy_primitives::Bytes::default(),
590 };
591
592 let tx_signed =
593 alloy_consensus::TxEnvelope::Legacy(alloy_consensus::Signed::new_unchecked(
594 tx,
595 alloy_primitives::Signature::test_signature(),
596 B256::ZERO,
597 ));
598
599 let body = alloy_consensus::BlockBody {
601 transactions: vec![tx_signed],
602 ommers: vec![],
603 withdrawals: Some(Default::default()),
604 };
605
606 let block = alloy_consensus::Block::new(header, body);
608 let expected_hash = block.header.hash_slow();
609
610 let mut encoded = Vec::new();
612 block.encode(&mut encoded);
613
614 let decoded =
616 SealedBlock::<alloy_consensus::Block<alloy_consensus::TxEnvelope>>::decode_sealed(
617 &mut encoded.as_slice(),
618 )
619 .expect("Failed to decode sealed block");
620
621 assert_eq!(decoded.hash(), expected_hash);
623 assert_eq!(decoded.header().number, 42);
624 assert_eq!(decoded.body().transactions.len(), 1);
625 }
626
627 #[test]
628 fn test_sealed_block_from_sealed() {
629 let header = alloy_consensus::Header::default();
630 let body = alloy_consensus::BlockBody::<alloy_consensus::TxEnvelope>::default();
631 let block = alloy_consensus::Block::new(header, body);
632 let hash = block.header.hash_slow();
633
634 let sealed: Sealed<alloy_consensus::Block<alloy_consensus::TxEnvelope>> =
636 Sealed::new_unchecked(block.clone(), hash);
637
638 let sealed_block: SealedBlock<alloy_consensus::Block<alloy_consensus::TxEnvelope>> =
640 SealedBlock::from(sealed);
641
642 assert_eq!(sealed_block.hash(), hash);
643 assert_eq!(sealed_block.header().number, block.header.number);
644 }
645}