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        let Ok(signers) = self.body().recover_signers() else {
195            return Err(BlockRecoveryError::new(self))
196        };
197        Ok(RecoveredBlock::new_unhashed(self, signers))
198    }
199
200    /// A Convenience function to convert this type into the regular ethereum block that
201    /// consists of:
202    ///
203    /// - Header
204    ///
205    /// And the ethereum block body [`alloy_consensus::BlockBody`], see also
206    /// [`BlockBody::into_ethereum_body`].
207    /// - Transactions
208    /// - Withdrawals
209    /// - Ommers
210    ///
211    /// Note: This conversion can be incomplete. It is not expected that this `Block` is the same as
212    /// [`alloy_consensus::Block`] only that it can be converted into it which is useful for
213    /// the `eth_` RPC namespace (e.g. RPC block).
214    fn into_ethereum_block(
215        self,
216    ) -> alloy_consensus::Block<<Self::Body as BlockBody>::Transaction, Self::Header> {
217        let (header, body) = self.split();
218        alloy_consensus::Block::new(header, body.into_ethereum_body())
219    }
220}
221
222impl<T, H> Block for alloy_consensus::Block<T, H>
223where
224    T: SignedTransaction,
225    H: BlockHeader,
226{
227    type Header = H;
228    type Body = alloy_consensus::BlockBody<T, H>;
229
230    fn new(header: Self::Header, body: Self::Body) -> Self {
231        Self { header, body }
232    }
233
234    fn header(&self) -> &Self::Header {
235        &self.header
236    }
237
238    fn body(&self) -> &Self::Body {
239        &self.body
240    }
241
242    fn split(self) -> (Self::Header, Self::Body) {
243        (self.header, self.body)
244    }
245
246    fn rlp_length(header: &Self::Header, body: &Self::Body) -> usize {
247        Self::rlp_length_for(header, body)
248    }
249
250    fn into_ethereum_block(self) -> Self {
251        self
252    }
253}
254
255/// An extension trait for [`Block`]s that allows for mutable access to the block's internals.
256///
257/// This allows for modifying the block's header and body for testing purposes.
258#[cfg(any(test, feature = "test-utils"))]
259pub trait TestBlock: Block<Header: crate::test_utils::TestHeader> {
260    /// Returns mutable reference to block body.
261    fn body_mut(&mut self) -> &mut Self::Body;
262
263    /// Returns mutable reference to block header.
264    fn header_mut(&mut self) -> &mut Self::Header;
265
266    /// Updates the block header.
267    fn set_header(&mut self, header: Self::Header);
268
269    /// Updates the parent block hash.
270    fn set_parent_hash(&mut self, hash: alloy_primitives::BlockHash) {
271        crate::header::test_utils::TestHeader::set_parent_hash(self.header_mut(), hash);
272    }
273
274    /// Updates the block number.
275    fn set_block_number(&mut self, number: alloy_primitives::BlockNumber) {
276        crate::header::test_utils::TestHeader::set_block_number(self.header_mut(), number);
277    }
278
279    /// Updates the block state root.
280    fn set_state_root(&mut self, state_root: alloy_primitives::B256) {
281        crate::header::test_utils::TestHeader::set_state_root(self.header_mut(), state_root);
282    }
283
284    /// Updates the block difficulty.
285    fn set_difficulty(&mut self, difficulty: alloy_primitives::U256) {
286        crate::header::test_utils::TestHeader::set_difficulty(self.header_mut(), difficulty);
287    }
288}
289
290#[cfg(any(test, feature = "test-utils"))]
291impl<T, H> TestBlock for alloy_consensus::Block<T, H>
292where
293    T: SignedTransaction,
294    H: crate::test_utils::TestHeader,
295{
296    fn body_mut(&mut self) -> &mut Self::Body {
297        &mut self.body
298    }
299
300    fn header_mut(&mut self) -> &mut Self::Header {
301        &mut self.header
302    }
303
304    fn set_header(&mut self, header: Self::Header) {
305        self.header = header
306    }
307}