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