reth_primitives_traits/block/
recovered.rs

1//! Recovered Block variant.
2
3use crate::{
4    block::{error::SealedBlockRecoveryError, SealedBlock},
5    transaction::signed::{RecoveryError, SignedTransaction},
6    Block, BlockBody, InMemorySize, SealedHeader,
7};
8use alloc::vec::Vec;
9use alloy_consensus::{transaction::Recovered, BlockHeader};
10use alloy_eips::{eip1898::BlockWithParent, BlockNumHash};
11use alloy_primitives::{Address, BlockHash, BlockNumber, Bloom, Bytes, Sealed, B256, B64, U256};
12use derive_more::Deref;
13
14/// A block with senders recovered from the block's transactions.
15///
16/// This type is a [`SealedBlock`] with a list of senders that match the transactions in the block.
17///
18/// ## Sealing
19///
20/// This type uses lazy sealing to avoid hashing the header until it is needed:
21///
22/// [`RecoveredBlock::new_unhashed`] creates a recovered block without hashing the header.
23/// [`RecoveredBlock::new`] creates a recovered block with the corresponding block hash.
24///
25/// ## Recovery
26///
27/// Sender recovery is fallible and can fail if any of the transactions fail to recover the sender.
28/// A [`SealedBlock`] can be upgraded to a [`RecoveredBlock`] using the
29/// [`RecoveredBlock::try_recover`] or [`SealedBlock::try_recover`] method.
30#[derive(Debug, Clone, Deref)]
31#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
32pub struct RecoveredBlock<B: Block> {
33    /// Block
34    #[deref]
35    #[cfg_attr(
36        feature = "serde",
37        serde(bound = "SealedBlock<B>: serde::Serialize + serde::de::DeserializeOwned")
38    )]
39    block: SealedBlock<B>,
40    /// List of senders that match the transactions in the block
41    senders: Vec<Address>,
42}
43
44impl<B: Block> RecoveredBlock<B> {
45    /// Creates a new recovered block instance with the given senders as provided and the block
46    /// hash.
47    ///
48    /// Note: This expects that the given senders match the transactions in the block.
49    pub fn new(block: B, senders: Vec<Address>, hash: BlockHash) -> Self {
50        Self { block: SealedBlock::new_unchecked(block, hash), senders }
51    }
52
53    /// Creates a new recovered block instance with the given senders as provided.
54    ///
55    /// Note: This expects that the given senders match the transactions in the block.
56    pub fn new_unhashed(block: B, senders: Vec<Address>) -> Self {
57        Self { block: SealedBlock::new_unhashed(block), senders }
58    }
59
60    /// Returns the recovered senders.
61    pub fn senders(&self) -> &[Address] {
62        &self.senders
63    }
64
65    /// Returns an iterator over the recovered senders.
66    pub fn senders_iter(&self) -> impl Iterator<Item = &Address> {
67        self.senders.iter()
68    }
69
70    /// Consumes the type and returns the inner block.
71    pub fn into_block(self) -> B {
72        self.block.into_block()
73    }
74
75    /// Returns a reference to the sealed block.
76    pub const fn sealed_block(&self) -> &SealedBlock<B> {
77        &self.block
78    }
79
80    /// Creates a new recovered block instance with the given [`SealedBlock`] and senders as
81    /// provided
82    pub const fn new_sealed(block: SealedBlock<B>, senders: Vec<Address>) -> Self {
83        Self { block, senders }
84    }
85
86    /// A safer variant of [`Self::new_unhashed`] that checks if the number of senders is equal to
87    /// the number of transactions in the block and recovers the senders from the transactions, if
88    /// not using [`SignedTransaction::recover_signer`](crate::transaction::signed::SignedTransaction)
89    /// to recover the senders.
90    pub fn try_new(
91        block: B,
92        senders: Vec<Address>,
93        hash: BlockHash,
94    ) -> Result<Self, SealedBlockRecoveryError<B>> {
95        let senders = if block.body().transaction_count() == senders.len() {
96            senders
97        } else {
98            let Ok(senders) = block.body().try_recover_signers() else {
99                return Err(SealedBlockRecoveryError::new(SealedBlock::new_unchecked(block, hash)));
100            };
101            senders
102        };
103        Ok(Self::new(block, senders, hash))
104    }
105
106    /// A safer variant of [`Self::new`] that checks if the number of senders is equal to
107    /// the number of transactions in the block and recovers the senders from the transactions, if
108    /// not using [`SignedTransaction::recover_signer_unchecked`](crate::transaction::signed::SignedTransaction)
109    /// to recover the senders.
110    pub fn try_new_unchecked(
111        block: B,
112        senders: Vec<Address>,
113        hash: BlockHash,
114    ) -> Result<Self, SealedBlockRecoveryError<B>> {
115        let senders = if block.body().transaction_count() == senders.len() {
116            senders
117        } else {
118            let Ok(senders) = block.body().try_recover_signers_unchecked() else {
119                return Err(SealedBlockRecoveryError::new(SealedBlock::new_unchecked(block, hash)));
120            };
121            senders
122        };
123        Ok(Self::new(block, senders, hash))
124    }
125
126    /// A safer variant of [`Self::new_unhashed`] that checks if the number of senders is equal to
127    /// the number of transactions in the block and recovers the senders from the transactions, if
128    /// not using [`SignedTransaction::recover_signer`](crate::transaction::signed::SignedTransaction)
129    /// to recover the senders.
130    pub fn try_new_unhashed(block: B, senders: Vec<Address>) -> Result<Self, RecoveryError> {
131        let senders = if block.body().transaction_count() == senders.len() {
132            senders
133        } else {
134            block.body().try_recover_signers()?
135        };
136        Ok(Self::new_unhashed(block, senders))
137    }
138
139    /// A safer variant of [`Self::new_unhashed`] that checks if the number of senders is equal to
140    /// the number of transactions in the block and recovers the senders from the transactions, if
141    /// not using [`SignedTransaction::recover_signer_unchecked`](crate::transaction::signed::SignedTransaction)
142    /// to recover the senders.
143    pub fn try_new_unhashed_unchecked(
144        block: B,
145        senders: Vec<Address>,
146    ) -> Result<Self, RecoveryError> {
147        let senders = if block.body().transaction_count() == senders.len() {
148            senders
149        } else {
150            block.body().try_recover_signers_unchecked()?
151        };
152        Ok(Self::new_unhashed(block, senders))
153    }
154
155    /// Recovers the senders from the transactions in the block using
156    /// [`SignedTransaction::recover_signer`](crate::transaction::signed::SignedTransaction).
157    ///
158    /// Returns an error if any of the transactions fail to recover the sender.
159    pub fn try_recover(block: B) -> Result<Self, RecoveryError> {
160        let senders = block.body().try_recover_signers()?;
161        Ok(Self::new_unhashed(block, senders))
162    }
163
164    /// Recovers the senders from the transactions in the block using
165    /// [`SignedTransaction::recover_signer_unchecked`](crate::transaction::signed::SignedTransaction).
166    ///
167    /// Returns an error if any of the transactions fail to recover the sender.
168    pub fn try_recover_unchecked(block: B) -> Result<Self, RecoveryError> {
169        let senders = block.body().try_recover_signers_unchecked()?;
170        Ok(Self::new_unhashed(block, senders))
171    }
172
173    /// Recovers the senders from the transactions in the block using
174    /// [`SignedTransaction::recover_signer`](crate::transaction::signed::SignedTransaction).
175    ///
176    /// Returns an error if any of the transactions fail to recover the sender.
177    pub fn try_recover_sealed(block: SealedBlock<B>) -> Result<Self, SealedBlockRecoveryError<B>> {
178        let Ok(senders) = block.body().try_recover_signers() else {
179            return Err(SealedBlockRecoveryError::new(block));
180        };
181        let (block, hash) = block.split();
182        Ok(Self::new(block, senders, hash))
183    }
184
185    /// Recovers the senders from the transactions in the sealed block using
186    /// [`SignedTransaction::recover_signer_unchecked`](crate::transaction::signed::SignedTransaction).
187    ///
188    /// Returns an error if any of the transactions fail to recover the sender.
189    pub fn try_recover_sealed_unchecked(
190        block: SealedBlock<B>,
191    ) -> Result<Self, SealedBlockRecoveryError<B>> {
192        let Ok(senders) = block.body().try_recover_signers_unchecked() else {
193            return Err(SealedBlockRecoveryError::new(block));
194        };
195        let (block, hash) = block.split();
196        Ok(Self::new(block, senders, hash))
197    }
198
199    /// A safer variant of [`Self::new_unhashed`] that checks if the number of senders is equal to
200    /// the number of transactions in the block and recovers the senders from the transactions, if
201    /// not using [`SignedTransaction::recover_signer_unchecked`](crate::transaction::signed::SignedTransaction)
202    /// to recover the senders.
203    ///
204    /// Returns an error if any of the transactions fail to recover the sender.
205    pub fn try_recover_sealed_with_senders(
206        block: SealedBlock<B>,
207        senders: Vec<Address>,
208    ) -> Result<Self, SealedBlockRecoveryError<B>> {
209        let (block, hash) = block.split();
210        Self::try_new(block, senders, hash)
211    }
212
213    /// A safer variant of [`Self::new`] that checks if the number of senders is equal to
214    /// the number of transactions in the block and recovers the senders from the transactions, if
215    /// not using [`SignedTransaction::recover_signer_unchecked`](crate::transaction::signed::SignedTransaction)
216    /// to recover the senders.
217    pub fn try_recover_sealed_with_senders_unchecked(
218        block: SealedBlock<B>,
219        senders: Vec<Address>,
220    ) -> Result<Self, SealedBlockRecoveryError<B>> {
221        let (block, hash) = block.split();
222        Self::try_new_unchecked(block, senders, hash)
223    }
224
225    /// Returns the block hash.
226    pub fn hash_ref(&self) -> &BlockHash {
227        self.block.hash_ref()
228    }
229
230    /// Returns a copy of the block hash.
231    pub fn hash(&self) -> BlockHash {
232        *self.hash_ref()
233    }
234
235    /// Return the number hash tuple.
236    pub fn num_hash(&self) -> BlockNumHash {
237        BlockNumHash::new(self.header().number(), self.hash())
238    }
239
240    /// Return a [`BlockWithParent`] for this header.
241    pub fn block_with_parent(&self) -> BlockWithParent {
242        BlockWithParent { parent: self.header().parent_hash(), block: self.num_hash() }
243    }
244
245    /// Clone the header.
246    pub fn clone_header(&self) -> B::Header {
247        self.header().clone()
248    }
249
250    /// Clones the internal header and returns a [`SealedHeader`] sealed with the hash.
251    pub fn clone_sealed_header(&self) -> SealedHeader<B::Header> {
252        SealedHeader::new(self.clone_header(), self.hash())
253    }
254
255    /// Clones the wrapped block and returns the [`SealedBlock`] sealed with the hash.
256    pub fn clone_sealed_block(&self) -> SealedBlock<B> {
257        self.block.clone()
258    }
259
260    /// Consumes the block and returns the block's header.
261    pub fn into_header(self) -> B::Header {
262        self.block.into_header()
263    }
264
265    /// Consumes the block and returns the block's body.
266    pub fn into_body(self) -> B::Body {
267        self.block.into_body()
268    }
269
270    /// Consumes the block and returns the [`SealedBlock`] and drops the recovered senders.
271    pub fn into_sealed_block(self) -> SealedBlock<B> {
272        self.block
273    }
274
275    /// Consumes the type and returns its components.
276    pub fn split_sealed(self) -> (SealedBlock<B>, Vec<Address>) {
277        (self.block, self.senders)
278    }
279
280    /// Consumes the type and returns its components.
281    #[doc(alias = "into_components")]
282    pub fn split(self) -> (B, Vec<Address>) {
283        (self.block.into_block(), self.senders)
284    }
285
286    /// Returns an iterator over all transactions and their sender.
287    #[inline]
288    pub fn transactions_with_sender(
289        &self,
290    ) -> impl Iterator<Item = (&Address, &<B::Body as BlockBody>::Transaction)> + '_ {
291        self.senders.iter().zip(self.block.body().transactions())
292    }
293
294    /// Returns an iterator over cloned `Recovered<Transaction>`
295    #[inline]
296    pub fn clone_transactions_recovered(
297        &self,
298    ) -> impl Iterator<Item = Recovered<<B::Body as BlockBody>::Transaction>> + '_ {
299        self.transactions_with_sender()
300            .map(|(sender, tx)| Recovered::new_unchecked(tx.clone(), *sender))
301    }
302
303    /// Returns an iterator over `Recovered<&Transaction>`
304    #[inline]
305    pub fn transactions_recovered(
306        &self,
307    ) -> impl Iterator<Item = Recovered<&'_ <B::Body as BlockBody>::Transaction>> + '_ {
308        self.transactions_with_sender().map(|(sender, tx)| Recovered::new_unchecked(tx, *sender))
309    }
310
311    /// Consumes the type and returns an iterator over all [`Recovered`] transactions in the block.
312    #[inline]
313    pub fn into_transactions_recovered(
314        self,
315    ) -> impl Iterator<Item = Recovered<<B::Body as BlockBody>::Transaction>> {
316        self.block
317            .split()
318            .0
319            .into_body()
320            .into_transactions()
321            .into_iter()
322            .zip(self.senders)
323            .map(|(tx, sender)| tx.with_signer(sender))
324    }
325
326    /// Consumes the block and returns the transactions of the block.
327    #[inline]
328    pub fn into_transactions(self) -> Vec<<B::Body as BlockBody>::Transaction> {
329        self.block.split().0.into_body().into_transactions()
330    }
331}
332
333impl<B: Block> BlockHeader for RecoveredBlock<B> {
334    fn parent_hash(&self) -> B256 {
335        self.header().parent_hash()
336    }
337
338    fn ommers_hash(&self) -> B256 {
339        self.header().ommers_hash()
340    }
341
342    fn beneficiary(&self) -> Address {
343        self.header().beneficiary()
344    }
345
346    fn state_root(&self) -> B256 {
347        self.header().state_root()
348    }
349
350    fn transactions_root(&self) -> B256 {
351        self.header().transactions_root()
352    }
353
354    fn receipts_root(&self) -> B256 {
355        self.header().receipts_root()
356    }
357
358    fn withdrawals_root(&self) -> Option<B256> {
359        self.header().withdrawals_root()
360    }
361
362    fn logs_bloom(&self) -> Bloom {
363        self.header().logs_bloom()
364    }
365
366    fn difficulty(&self) -> U256 {
367        self.header().difficulty()
368    }
369
370    fn number(&self) -> BlockNumber {
371        self.header().number()
372    }
373
374    fn gas_limit(&self) -> u64 {
375        self.header().gas_limit()
376    }
377
378    fn gas_used(&self) -> u64 {
379        self.header().gas_used()
380    }
381
382    fn timestamp(&self) -> u64 {
383        self.header().timestamp()
384    }
385
386    fn mix_hash(&self) -> Option<B256> {
387        self.header().mix_hash()
388    }
389
390    fn nonce(&self) -> Option<B64> {
391        self.header().nonce()
392    }
393
394    fn base_fee_per_gas(&self) -> Option<u64> {
395        self.header().base_fee_per_gas()
396    }
397
398    fn blob_gas_used(&self) -> Option<u64> {
399        self.header().blob_gas_used()
400    }
401
402    fn excess_blob_gas(&self) -> Option<u64> {
403        self.header().excess_blob_gas()
404    }
405
406    fn parent_beacon_block_root(&self) -> Option<B256> {
407        self.header().parent_beacon_block_root()
408    }
409
410    fn requests_hash(&self) -> Option<B256> {
411        self.header().requests_hash()
412    }
413
414    fn extra_data(&self) -> &Bytes {
415        self.header().extra_data()
416    }
417}
418
419impl<B: Block> Eq for RecoveredBlock<B> {}
420
421impl<B: Block> PartialEq for RecoveredBlock<B> {
422    fn eq(&self, other: &Self) -> bool {
423        self.hash_ref().eq(other.hash_ref()) &&
424            self.block.eq(&other.block) &&
425            self.senders.eq(&other.senders)
426    }
427}
428
429impl<B: Block + Default> Default for RecoveredBlock<B> {
430    #[inline]
431    fn default() -> Self {
432        Self::new_unhashed(B::default(), Default::default())
433    }
434}
435
436impl<B: Block> InMemorySize for RecoveredBlock<B> {
437    #[inline]
438    fn size(&self) -> usize {
439        self.block.size() + self.senders.len() * core::mem::size_of::<Address>()
440    }
441}
442
443impl<B: Block> From<RecoveredBlock<B>> for Sealed<B> {
444    fn from(value: RecoveredBlock<B>) -> Self {
445        value.block.into()
446    }
447}
448
449#[cfg(any(test, feature = "arbitrary"))]
450impl<'a, B> arbitrary::Arbitrary<'a> for RecoveredBlock<B>
451where
452    B: Block + arbitrary::Arbitrary<'a>,
453{
454    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
455        let block = B::arbitrary(u)?;
456        Ok(Self::try_recover(block).unwrap())
457    }
458}
459
460#[cfg(any(test, feature = "test-utils"))]
461impl<B: Block> RecoveredBlock<B> {
462    /// Returns a mutable reference to the recovered senders.
463    pub const fn senders_mut(&mut self) -> &mut Vec<Address> {
464        &mut self.senders
465    }
466
467    /// Appends the sender to the list of senders.
468    pub fn push_sender(&mut self, sender: Address) {
469        self.senders.push(sender);
470    }
471}
472
473#[cfg(any(test, feature = "test-utils"))]
474impl<B> core::ops::DerefMut for RecoveredBlock<B>
475where
476    B: Block,
477{
478    fn deref_mut(&mut self) -> &mut Self::Target {
479        &mut self.block
480    }
481}
482
483#[cfg(any(test, feature = "test-utils"))]
484impl<B: crate::test_utils::TestBlock> RecoveredBlock<B> {
485    /// Updates the block header.
486    pub fn set_header(&mut self, header: B::Header) {
487        *self.header_mut() = header
488    }
489
490    /// Updates the block hash.
491    pub fn set_hash(&mut self, hash: BlockHash) {
492        self.block.set_hash(hash)
493    }
494
495    /// Returns a mutable reference to the header.
496    pub const fn header_mut(&mut self) -> &mut B::Header {
497        self.block.header_mut()
498    }
499
500    /// Returns a mutable reference to the header.
501    pub const fn block_mut(&mut self) -> &mut B::Body {
502        self.block.body_mut()
503    }
504
505    /// Updates the parent block hash.
506    pub fn set_parent_hash(&mut self, hash: BlockHash) {
507        self.block.set_parent_hash(hash);
508    }
509
510    /// Updates the block number.
511    pub fn set_block_number(&mut self, number: alloy_primitives::BlockNumber) {
512        self.block.set_block_number(number);
513    }
514
515    /// Updates the block state root.
516    pub fn set_state_root(&mut self, state_root: alloy_primitives::B256) {
517        self.block.set_state_root(state_root);
518    }
519
520    /// Updates the block difficulty.
521    pub fn set_difficulty(&mut self, difficulty: alloy_primitives::U256) {
522        self.block.set_difficulty(difficulty);
523    }
524}
525
526/// Bincode-compatible [`RecoveredBlock`] serde implementation.
527#[cfg(feature = "serde-bincode-compat")]
528pub(super) mod serde_bincode_compat {
529    use crate::{
530        serde_bincode_compat::{self, SerdeBincodeCompat},
531        Block,
532    };
533    use alloc::{borrow::Cow, vec::Vec};
534    use alloy_primitives::Address;
535    use serde::{Deserialize, Deserializer, Serialize, Serializer};
536    use serde_with::{DeserializeAs, SerializeAs};
537
538    /// Bincode-compatible [`super::RecoveredBlock`] serde implementation.
539    ///
540    /// Intended to use with the [`serde_with::serde_as`] macro in the following way:
541    /// ```rust
542    /// use reth_primitives_traits::{
543    ///     block::RecoveredBlock,
544    ///     serde_bincode_compat::{self, SerdeBincodeCompat},
545    ///     Block,
546    /// };
547    /// use serde::{Deserialize, Serialize};
548    /// use serde_with::serde_as;
549    ///
550    /// #[serde_as]
551    /// #[derive(Serialize, Deserialize)]
552    /// struct Data<T: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static> {
553    ///     #[serde_as(as = "serde_bincode_compat::RecoveredBlock<'_, T>")]
554    ///     block: RecoveredBlock<T>,
555    /// }
556    /// ```
557    #[derive(derive_more::Debug, Serialize, Deserialize)]
558    pub struct RecoveredBlock<
559        'a,
560        T: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static,
561    > {
562        #[serde(
563            bound = "serde_bincode_compat::SealedBlock<'a, T>: Serialize + serde::de::DeserializeOwned"
564        )]
565        block: serde_bincode_compat::SealedBlock<'a, T>,
566        #[expect(clippy::owned_cow)]
567        senders: Cow<'a, Vec<Address>>,
568    }
569
570    impl<'a, T: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static>
571        From<&'a super::RecoveredBlock<T>> for RecoveredBlock<'a, T>
572    {
573        fn from(value: &'a super::RecoveredBlock<T>) -> Self {
574            Self { block: (&value.block).into(), senders: Cow::Borrowed(&value.senders) }
575        }
576    }
577
578    impl<'a, T: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static>
579        From<RecoveredBlock<'a, T>> for super::RecoveredBlock<T>
580    {
581        fn from(value: RecoveredBlock<'a, T>) -> Self {
582            Self::new_sealed(value.block.into(), value.senders.into_owned())
583        }
584    }
585
586    impl<T: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static>
587        SerializeAs<super::RecoveredBlock<T>> for RecoveredBlock<'_, T>
588    {
589        fn serialize_as<S>(
590            source: &super::RecoveredBlock<T>,
591            serializer: S,
592        ) -> Result<S::Ok, S::Error>
593        where
594            S: Serializer,
595        {
596            RecoveredBlock::from(source).serialize(serializer)
597        }
598    }
599
600    impl<'de, T: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static>
601        DeserializeAs<'de, super::RecoveredBlock<T>> for RecoveredBlock<'de, T>
602    {
603        fn deserialize_as<D>(deserializer: D) -> Result<super::RecoveredBlock<T>, D::Error>
604        where
605            D: Deserializer<'de>,
606        {
607            RecoveredBlock::deserialize(deserializer).map(Into::into)
608        }
609    }
610
611    impl<T: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static>
612        SerdeBincodeCompat for super::RecoveredBlock<T>
613    {
614        type BincodeRepr<'a> = RecoveredBlock<'a, T>;
615
616        fn as_repr(&self) -> Self::BincodeRepr<'_> {
617            self.into()
618        }
619
620        fn from_repr(repr: Self::BincodeRepr<'_>) -> Self {
621            repr.into()
622        }
623    }
624}