reth_primitives_traits/block/
mod.rs

1//! Block abstraction.
2//!
3//! This module provides the core block types and transformations:
4//!
5//! ```rust
6//! # use reth_primitives_traits::{Block, SealedBlock, RecoveredBlock};
7//! # fn example<B: Block + 'static>(block: B) -> Result<(), Box<dyn std::error::Error>>
8//! # where B::Body: reth_primitives_traits::BlockBody<Transaction: reth_primitives_traits::SignedTransaction> {
9//! // Basic block flow
10//! let block: B = block;
11//!
12//! // Seal (compute hash)
13//! let sealed: SealedBlock<B> = block.seal();
14//!
15//! // Recover senders
16//! let recovered: RecoveredBlock<B> = sealed.try_recover()?;
17//!
18//! // Access components
19//! let senders = recovered.senders();
20//! let hash = recovered.hash();
21//! # Ok(())
22//! # }
23//! ```
24
25pub(crate) mod sealed;
26pub use sealed::SealedBlock;
27
28pub(crate) mod recovered;
29pub use recovered::RecoveredBlock;
30
31pub mod body;
32pub mod error;
33pub mod header;
34
35use alloc::{fmt, vec::Vec};
36use alloy_primitives::{Address, B256};
37use alloy_rlp::{Decodable, Encodable};
38
39use crate::{
40    block::error::BlockRecoveryError, transaction::signed::RecoveryError, BlockBody, BlockHeader,
41    FullBlockBody, FullBlockHeader, InMemorySize, MaybeSerde, SealedHeader, SignedTransaction,
42};
43
44/// Bincode-compatible header type serde implementations.
45#[cfg(feature = "serde-bincode-compat")]
46pub mod serde_bincode_compat {
47    pub use super::{
48        recovered::serde_bincode_compat::RecoveredBlock, sealed::serde_bincode_compat::SealedBlock,
49    };
50}
51
52/// Helper trait that unifies all behaviour required by block to support full node operations.
53pub trait FullBlock:
54    Block<Header: FullBlockHeader, Body: FullBlockBody> + alloy_rlp::Encodable + alloy_rlp::Decodable
55{
56}
57
58impl<T> FullBlock for T where
59    T: Block<Header: FullBlockHeader, Body: FullBlockBody>
60        + alloy_rlp::Encodable
61        + alloy_rlp::Decodable
62{
63}
64
65/// Helper trait to access [`BlockBody::Transaction`] given a [`Block`].
66pub type BlockTx<B> = <<B as Block>::Body as BlockBody>::Transaction;
67
68/// Abstraction of block data type.
69///
70/// This type defines the structure of a block in the blockchain.
71/// A [`Block`] is composed of a header and a body.
72/// It is expected that a block can always be completely reconstructed from its header and body
73pub trait Block:
74    Send
75    + Sync
76    + Unpin
77    + Clone
78    + Default
79    + fmt::Debug
80    + PartialEq
81    + Eq
82    + InMemorySize
83    + MaybeSerde
84    + Encodable
85    + Decodable
86{
87    /// Header part of the block.
88    type Header: BlockHeader;
89
90    /// The block's body contains the transactions in the block and additional data, e.g.
91    /// withdrawals in ethereum.
92    type Body: BlockBody<OmmerHeader = Self::Header>;
93
94    /// Create new block instance.
95    fn new(header: Self::Header, body: Self::Body) -> Self;
96
97    /// Create new a sealed block instance from a sealed header and the block body.
98    fn new_sealed(header: SealedHeader<Self::Header>, body: Self::Body) -> SealedBlock<Self> {
99        SealedBlock::from_sealed_parts(header, body)
100    }
101
102    /// Seal the block with a known hash.
103    ///
104    /// WARNING: This method does not perform validation whether the hash is correct.
105    fn seal_unchecked(self, hash: B256) -> SealedBlock<Self> {
106        SealedBlock::new_unchecked(self, hash)
107    }
108
109    /// Creates the [`SealedBlock`] from the block's parts without calculating the hash upfront.
110    fn seal(self) -> SealedBlock<Self> {
111        SealedBlock::new_unhashed(self)
112    }
113
114    /// Calculate the header hash and seal the block so that it can't be changed.
115    fn seal_slow(self) -> SealedBlock<Self> {
116        SealedBlock::seal_slow(self)
117    }
118
119    /// Returns reference to block header.
120    fn header(&self) -> &Self::Header;
121
122    /// Returns reference to block body.
123    fn body(&self) -> &Self::Body;
124
125    /// Splits the block into its header and body.
126    fn split(self) -> (Self::Header, Self::Body);
127
128    /// Returns a tuple of references to the block's header and body.
129    fn split_ref(&self) -> (&Self::Header, &Self::Body) {
130        (self.header(), self.body())
131    }
132
133    /// Consumes the block and returns the header.
134    fn into_header(self) -> Self::Header {
135        self.split().0
136    }
137
138    /// Consumes the block and returns the body.
139    fn into_body(self) -> Self::Body {
140        self.split().1
141    }
142
143    /// Returns the rlp length of the block with the given header and body.
144    fn rlp_length(header: &Self::Header, body: &Self::Body) -> usize;
145
146    /// Expensive operation that recovers transaction signer.
147    fn recover_signers(&self) -> Result<Vec<Address>, RecoveryError>
148    where
149        <Self::Body as BlockBody>::Transaction: SignedTransaction,
150    {
151        self.body().recover_signers()
152    }
153
154    /// Transform the block into a [`RecoveredBlock`] using the given senders.
155    ///
156    /// If the number of senders does not match the number of transactions in the block, this falls
157    /// back to manually recovery, but _without ensuring that the signature has a low `s` value_.
158    ///
159    /// Returns the block as error if a signature is invalid.
160    fn try_into_recovered_unchecked(
161        self,
162        senders: Vec<Address>,
163    ) -> Result<RecoveredBlock<Self>, BlockRecoveryError<Self>>
164    where
165        <Self::Body as BlockBody>::Transaction: SignedTransaction,
166    {
167        let senders = if self.body().transactions().len() == senders.len() {
168            senders
169        } else {
170            // Fall back to recovery if lengths don't match
171            let Ok(senders) = self.body().recover_signers_unchecked() else {
172                return Err(BlockRecoveryError::new(self))
173            };
174            senders
175        };
176        Ok(RecoveredBlock::new_unhashed(self, senders))
177    }
178
179    /// Transform the block into a [`RecoveredBlock`] using the given signers.
180    ///
181    /// Note: This method assumes the signers are correct and does not validate them.
182    fn into_recovered_with_signers(self, signers: Vec<Address>) -> RecoveredBlock<Self>
183    where
184        <Self::Body as BlockBody>::Transaction: SignedTransaction,
185    {
186        RecoveredBlock::new_unhashed(self, signers)
187    }
188
189    /// **Expensive**. Transform into a [`RecoveredBlock`] by recovering senders in the contained
190    /// transactions.
191    ///
192    /// Returns the block as error if a signature is invalid.
193    fn try_into_recovered(self) -> Result<RecoveredBlock<Self>, BlockRecoveryError<Self>>
194    where
195        <Self::Body as BlockBody>::Transaction: SignedTransaction,
196    {
197        let Ok(signers) = self.body().recover_signers() else {
198            return Err(BlockRecoveryError::new(self))
199        };
200        Ok(RecoveredBlock::new_unhashed(self, signers))
201    }
202
203    /// A Convenience function to convert this type into the regular ethereum block that
204    /// consists of:
205    ///
206    /// - Header
207    ///
208    /// And the ethereum block body [`alloy_consensus::BlockBody`], see also
209    /// [`BlockBody::into_ethereum_body`].
210    /// - Transactions
211    /// - Withdrawals
212    /// - Ommers
213    ///
214    /// Note: This conversion can be incomplete. It is not expected that this `Block` is the same as
215    /// [`alloy_consensus::Block`] only that it can be converted into it which is useful for
216    /// the `eth_` RPC namespace (e.g. RPC block).
217    fn into_ethereum_block(
218        self,
219    ) -> alloy_consensus::Block<<Self::Body as BlockBody>::Transaction, Self::Header> {
220        let (header, body) = self.split();
221        alloy_consensus::Block::new(header, body.into_ethereum_body())
222    }
223}
224
225impl<T, H> Block for alloy_consensus::Block<T, H>
226where
227    T: SignedTransaction,
228    H: BlockHeader,
229{
230    type Header = H;
231    type Body = alloy_consensus::BlockBody<T, H>;
232
233    fn new(header: Self::Header, body: Self::Body) -> Self {
234        Self { header, body }
235    }
236
237    fn header(&self) -> &Self::Header {
238        &self.header
239    }
240
241    fn body(&self) -> &Self::Body {
242        &self.body
243    }
244
245    fn split(self) -> (Self::Header, Self::Body) {
246        (self.header, self.body)
247    }
248
249    fn rlp_length(header: &Self::Header, body: &Self::Body) -> usize {
250        Self::rlp_length_for(header, body)
251    }
252
253    fn into_ethereum_block(self) -> Self {
254        self
255    }
256}
257
258/// An extension trait for [`Block`]s that allows for mutable access to the block's internals.
259///
260/// This allows for modifying the block's header and body for testing purposes.
261#[cfg(any(test, feature = "test-utils"))]
262pub trait TestBlock: Block<Header: crate::test_utils::TestHeader> {
263    /// Returns mutable reference to block body.
264    fn body_mut(&mut self) -> &mut Self::Body;
265
266    /// Returns mutable reference to block header.
267    fn header_mut(&mut self) -> &mut Self::Header;
268
269    /// Updates the block header.
270    fn set_header(&mut self, header: Self::Header);
271
272    /// Updates the parent block hash.
273    fn set_parent_hash(&mut self, hash: alloy_primitives::BlockHash) {
274        crate::header::test_utils::TestHeader::set_parent_hash(self.header_mut(), hash);
275    }
276
277    /// Updates the block number.
278    fn set_block_number(&mut self, number: alloy_primitives::BlockNumber) {
279        crate::header::test_utils::TestHeader::set_block_number(self.header_mut(), number);
280    }
281
282    /// Updates the block state root.
283    fn set_state_root(&mut self, state_root: alloy_primitives::B256) {
284        crate::header::test_utils::TestHeader::set_state_root(self.header_mut(), state_root);
285    }
286
287    /// Updates the block difficulty.
288    fn set_difficulty(&mut self, difficulty: alloy_primitives::U256) {
289        crate::header::test_utils::TestHeader::set_difficulty(self.header_mut(), difficulty);
290    }
291}
292
293#[cfg(any(test, feature = "test-utils"))]
294impl<T, H> TestBlock for alloy_consensus::Block<T, H>
295where
296    T: SignedTransaction,
297    H: crate::test_utils::TestHeader,
298{
299    fn body_mut(&mut self) -> &mut Self::Body {
300        &mut self.body
301    }
302
303    fn header_mut(&mut self) -> &mut Self::Header {
304        &mut self.header
305    }
306
307    fn set_header(&mut self, header: Self::Header) {
308        self.header = header
309    }
310}