reth_primitives_traits/block/
mod.rs

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