mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat: add SealedBlock in reth-primitives-traits (#13735)
This commit is contained in:
@ -1,10 +1,11 @@
|
||||
//! Block body abstraction.
|
||||
|
||||
use crate::{
|
||||
BlockHeader, FullSignedTx, InMemorySize, MaybeSerde, MaybeSerdeBincodeCompat, SignedTransaction,
|
||||
transaction::signed::RecoveryError, BlockHeader, FullSignedTx, InMemorySize, MaybeSerde,
|
||||
MaybeSerdeBincodeCompat, SignedTransaction,
|
||||
};
|
||||
use alloc::{fmt, vec::Vec};
|
||||
use alloy_consensus::{Header, Transaction};
|
||||
use alloy_consensus::{Header, Transaction, Typed2718};
|
||||
use alloy_eips::{eip2718::Encodable2718, eip4895::Withdrawals};
|
||||
use alloy_primitives::{Address, Bytes, B256};
|
||||
|
||||
@ -14,6 +15,9 @@ pub trait FullBlockBody: BlockBody<Transaction: FullSignedTx> + MaybeSerdeBincod
|
||||
impl<T> FullBlockBody for T where T: BlockBody<Transaction: FullSignedTx> + MaybeSerdeBincodeCompat {}
|
||||
|
||||
/// Abstraction for block's body.
|
||||
///
|
||||
/// This type is a container for everything that is included in a block except the header.
|
||||
/// For ethereum this includes transactions, ommers, and withdrawals.
|
||||
pub trait BlockBody:
|
||||
Send
|
||||
+ Sync
|
||||
@ -47,9 +51,15 @@ pub trait BlockBody:
|
||||
fn transaction_count(&self) -> usize {
|
||||
self.transactions().len()
|
||||
}
|
||||
|
||||
/// Consume the block body and return a [`Vec`] of transactions.
|
||||
fn into_transactions(self) -> Vec<Self::Transaction>;
|
||||
|
||||
/// Returns `true` if the block body contains a transaction of the given type.
|
||||
fn contains_transaction_type(&self, tx_type: u8) -> bool {
|
||||
self.transactions().iter().any(|tx| tx.is_type(tx_type))
|
||||
}
|
||||
|
||||
/// Calculate the transaction root for the block body.
|
||||
fn calculate_tx_root(&self) -> B256 {
|
||||
alloy_consensus::proofs::calculate_transaction_root(self.transactions())
|
||||
@ -115,6 +125,16 @@ pub trait BlockBody:
|
||||
crate::transaction::recover::recover_signers(self.transactions())
|
||||
}
|
||||
|
||||
/// Recover signer addresses for all transactions in the block body.
|
||||
///
|
||||
/// Returns an error if some transaction's signature is invalid.
|
||||
fn try_recover_signers(&self) -> Result<Vec<Address>, RecoveryError>
|
||||
where
|
||||
Self::Transaction: SignedTransaction,
|
||||
{
|
||||
self.recover_signers().ok_or(RecoveryError)
|
||||
}
|
||||
|
||||
/// Recover signer addresses for all transactions in the block body _without ensuring that the
|
||||
/// signature has a low `s` value_.
|
||||
///
|
||||
@ -125,6 +145,17 @@ pub trait BlockBody:
|
||||
{
|
||||
crate::transaction::recover::recover_signers_unchecked(self.transactions())
|
||||
}
|
||||
|
||||
/// Recover signer addresses for all transactions in the block body _without ensuring that the
|
||||
/// signature has a low `s` value_.
|
||||
///
|
||||
/// Returns an error if some transaction's signature is invalid.
|
||||
fn try_recover_signers_unchecked(&self) -> Result<Vec<Address>, RecoveryError>
|
||||
where
|
||||
Self::Transaction: SignedTransaction,
|
||||
{
|
||||
self.recover_signers_unchecked().ok_or(RecoveryError)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> BlockBody for alloy_consensus::BlockBody<T>
|
||||
|
||||
33
crates/primitives-traits/src/block/error.rs
Normal file
33
crates/primitives-traits/src/block/error.rs
Normal file
@ -0,0 +1,33 @@
|
||||
//! Error types for the `block` module.
|
||||
|
||||
use crate::transaction::signed::RecoveryError;
|
||||
|
||||
/// Type alias for [`BlockRecoveryError`] with a [`SealedBlock`](crate::SealedBlock) value.
|
||||
pub type SealedBlockRecoveryError<B> = BlockRecoveryError<crate::SealedBlock<B>>;
|
||||
|
||||
/// Error when recovering a block from [`SealedBlock`](crate::SealedBlock) to
|
||||
/// [`RecoveredBlock`](crate::RecoveredBlock).
|
||||
///
|
||||
/// This error is returned when the block recovery fails and contains the erroneous block, because
|
||||
/// recovering a block takes ownership of the block.
|
||||
#[derive(Debug, Clone, thiserror::Error)]
|
||||
#[error("Failed to recover the block")]
|
||||
pub struct BlockRecoveryError<T>(pub T);
|
||||
|
||||
impl<T> BlockRecoveryError<T> {
|
||||
/// Create a new error.
|
||||
pub const fn new(inner: T) -> Self {
|
||||
Self(inner)
|
||||
}
|
||||
|
||||
/// Unwrap the error and return the original value.
|
||||
pub fn into_inner(self) -> T {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<BlockRecoveryError<T>> for RecoveryError {
|
||||
fn from(_: BlockRecoveryError<T>) -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
@ -1,17 +1,33 @@
|
||||
//! Block abstraction.
|
||||
|
||||
pub(crate) mod sealed;
|
||||
pub use sealed::SealedBlock;
|
||||
|
||||
pub(crate) mod recovered;
|
||||
pub use recovered::RecoveredBlock;
|
||||
|
||||
pub mod body;
|
||||
pub mod error;
|
||||
pub mod header;
|
||||
|
||||
use alloc::fmt;
|
||||
use alloc::{fmt, vec::Vec};
|
||||
use alloy_consensus::Header;
|
||||
use alloy_primitives::{Address, B256};
|
||||
use alloy_rlp::{Decodable, Encodable};
|
||||
|
||||
use crate::{
|
||||
BlockBody, BlockHeader, FullBlockBody, FullBlockHeader, InMemorySize, MaybeSerde,
|
||||
BlockBody, BlockHeader, FullBlockBody, FullBlockHeader, InMemorySize, MaybeSerde, SealedHeader,
|
||||
SignedTransaction,
|
||||
};
|
||||
|
||||
/// Bincode-compatible header type serde implementations.
|
||||
#[cfg(feature = "serde-bincode-compat")]
|
||||
pub mod serde_bincode_compat {
|
||||
pub use super::{
|
||||
recovered::serde_bincode_compat::RecoveredBlock, sealed::serde_bincode_compat::SealedBlock,
|
||||
};
|
||||
}
|
||||
|
||||
/// Helper trait that unifies all behaviour required by block to support full node operations.
|
||||
pub trait FullBlock:
|
||||
Block<Header: FullBlockHeader, Body: FullBlockBody> + alloy_rlp::Encodable + alloy_rlp::Decodable
|
||||
@ -29,9 +45,10 @@ impl<T> FullBlock for T where
|
||||
pub type BlockTx<B> = <<B as Block>::Body as BlockBody>::Transaction;
|
||||
|
||||
/// Abstraction of block data type.
|
||||
// todo: make sealable super-trait, depends on <https://github.com/paradigmxyz/reth/issues/11449>
|
||||
// todo: make with senders extension trait, so block can be impl by block type already containing
|
||||
// senders
|
||||
///
|
||||
/// This type defines the structure of a block in the blockchain.
|
||||
/// A [`Block`] is composed of a header and a body.
|
||||
/// It is expected that a block can always be completely reconstructed from its header and body.
|
||||
pub trait Block:
|
||||
Send
|
||||
+ Sync
|
||||
@ -49,12 +66,30 @@ pub trait Block:
|
||||
/// Header part of the block.
|
||||
type Header: BlockHeader;
|
||||
|
||||
/// The block's body contains the transactions in the block.
|
||||
/// The block's body contains the transactions in the block and additional data, e.g.
|
||||
/// withdrawals in ethereum.
|
||||
type Body: BlockBody<OmmerHeader = Self::Header>;
|
||||
|
||||
/// Create new block instance.
|
||||
fn new(header: Self::Header, body: Self::Body) -> Self;
|
||||
|
||||
/// Create new a sealed block instance from a sealed header and the block body.
|
||||
fn new_sealed(header: SealedHeader<Self::Header>, body: Self::Body) -> SealedBlock<Self> {
|
||||
SealedBlock::from_sealed_parts(header, body)
|
||||
}
|
||||
|
||||
/// Seal the block with a known hash.
|
||||
///
|
||||
/// WARNING: This method does not perform validation whether the hash is correct.
|
||||
fn seal(self, hash: B256) -> SealedBlock<Self> {
|
||||
SealedBlock::new_unchecked(self, hash)
|
||||
}
|
||||
|
||||
/// Calculate the header hash and seal the block so that it can't be changed.
|
||||
fn seal_slow(self) -> SealedBlock<Self> {
|
||||
SealedBlock::seal_slow(self)
|
||||
}
|
||||
|
||||
/// Returns reference to block header.
|
||||
fn header(&self) -> &Self::Header;
|
||||
|
||||
@ -63,6 +98,84 @@ pub trait Block:
|
||||
|
||||
/// Splits the block into its header and body.
|
||||
fn split(self) -> (Self::Header, Self::Body);
|
||||
|
||||
/// Returns a tuple of references to the block's header and body.
|
||||
fn split_ref(&self) -> (&Self::Header, &Self::Body) {
|
||||
(self.header(), self.body())
|
||||
}
|
||||
|
||||
/// Consumes the block and returns the header.
|
||||
fn into_header(self) -> Self::Header {
|
||||
self.split().0
|
||||
}
|
||||
|
||||
/// Consumes the block and returns the body.
|
||||
fn into_body(self) -> Self::Body {
|
||||
self.split().1
|
||||
}
|
||||
|
||||
/// Returns the rlp length of the block with the given header and body.
|
||||
fn rlp_length(header: &Self::Header, body: &Self::Body) -> usize {
|
||||
// TODO(mattsse): replace default impl with <https://github.com/alloy-rs/alloy/pull/1906>
|
||||
header.length() + body.length()
|
||||
}
|
||||
|
||||
/// Expensive operation that recovers transaction signer.
|
||||
fn senders(&self) -> Option<Vec<Address>>
|
||||
where
|
||||
<Self::Body as BlockBody>::Transaction: SignedTransaction,
|
||||
{
|
||||
self.body().recover_signers()
|
||||
}
|
||||
|
||||
/// Transform into a [`RecoveredBlock`].
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If the number of senders does not match the number of transactions in the block
|
||||
/// and the signer recovery for one of the transactions fails.
|
||||
///
|
||||
/// Note: this is expected to be called with blocks read from disk.
|
||||
#[track_caller]
|
||||
fn with_senders_unchecked(self, senders: Vec<Address>) -> RecoveredBlock<Self>
|
||||
where
|
||||
<Self::Body as BlockBody>::Transaction: SignedTransaction,
|
||||
{
|
||||
self.try_with_senders_unchecked(senders).expect("stored block is valid")
|
||||
}
|
||||
|
||||
/// Transform into a [`RecoveredBlock`] using the given senders.
|
||||
///
|
||||
/// If the number of senders does not match the number of transactions in the block, this falls
|
||||
/// back to manually recovery, but _without ensuring that the signature has a low `s` value_.
|
||||
///
|
||||
/// Returns an error if a signature is invalid.
|
||||
#[track_caller]
|
||||
fn try_with_senders_unchecked(self, senders: Vec<Address>) -> Result<RecoveredBlock<Self>, Self>
|
||||
where
|
||||
<Self::Body as BlockBody>::Transaction: SignedTransaction,
|
||||
{
|
||||
let senders = if self.body().transactions().len() == senders.len() {
|
||||
senders
|
||||
} else {
|
||||
let Some(senders) = self.body().recover_signers_unchecked() else { return Err(self) };
|
||||
senders
|
||||
};
|
||||
|
||||
Ok(RecoveredBlock::new_unhashed(self, senders))
|
||||
}
|
||||
|
||||
/// **Expensive**. Transform into a [`RecoveredBlock`] by recovering senders in the contained
|
||||
/// transactions.
|
||||
///
|
||||
/// Returns `None` if a transaction is invalid.
|
||||
fn with_recovered_senders(self) -> Option<RecoveredBlock<Self>>
|
||||
where
|
||||
<Self::Body as BlockBody>::Transaction: SignedTransaction,
|
||||
{
|
||||
let senders = self.senders()?;
|
||||
Some(RecoveredBlock::new_unhashed(self, senders))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Block for alloy_consensus::Block<T>
|
||||
|
||||
598
crates/primitives-traits/src/block/recovered.rs
Normal file
598
crates/primitives-traits/src/block/recovered.rs
Normal file
@ -0,0 +1,598 @@
|
||||
//! Recovered Block variant.
|
||||
|
||||
use crate::{
|
||||
block::{error::SealedBlockRecoveryError, SealedBlock},
|
||||
transaction::signed::{RecoveryError, SignedTransactionIntoRecoveredExt},
|
||||
Block, BlockBody, InMemorySize, SealedHeader,
|
||||
};
|
||||
use alloc::vec::Vec;
|
||||
use alloy_consensus::{transaction::Recovered, BlockHeader};
|
||||
use alloy_eips::{eip1898::BlockWithParent, BlockNumHash};
|
||||
use alloy_primitives::{Address, BlockHash, BlockNumber, Bloom, Bytes, Sealed, B256, B64, U256};
|
||||
use derive_more::Deref;
|
||||
|
||||
/// A block with senders recovered from the block's transactions.
|
||||
///
|
||||
/// This type is a [`SealedBlock`] with a list of senders that match the transactions in the block.
|
||||
///
|
||||
/// ## Sealing
|
||||
///
|
||||
/// This type uses lazy sealing to avoid hashing the header until it is needed:
|
||||
///
|
||||
/// [`RecoveredBlock::new_unhashed`] creates a recovered block without hashing the header.
|
||||
/// [`RecoveredBlock::new`] creates a recovered block with the corresponding block hash.
|
||||
///
|
||||
/// ## Recovery
|
||||
///
|
||||
/// Sender recovery is fallible and can fail if any of the transactions fail to recover the sender.
|
||||
/// A [`SealedBlock`] can be upgraded to a [`RecoveredBlock`] using the
|
||||
/// [`RecoveredBlock::try_recover`] or [`SealedBlock::try_recover`] method.
|
||||
#[derive(Debug, Clone, Deref)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct RecoveredBlock<B: Block> {
|
||||
/// Block
|
||||
#[deref]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
serde(bound = "SealedBlock<B>: serde::Serialize + serde::de::DeserializeOwned")
|
||||
)]
|
||||
block: SealedBlock<B>,
|
||||
/// List of senders that match the transactions in the block
|
||||
senders: Vec<Address>,
|
||||
}
|
||||
|
||||
impl<B: Block> RecoveredBlock<B> {
|
||||
/// Creates a new recovered block instance with the given senders as provided and the block
|
||||
/// hash.
|
||||
///
|
||||
/// Note: This expects that the given senders match the transactions in the block.
|
||||
pub fn new(block: B, senders: Vec<Address>, hash: BlockHash) -> Self {
|
||||
Self { block: SealedBlock::new_unchecked(block, hash), senders }
|
||||
}
|
||||
|
||||
/// Creates a new recovered block instance with the given senders as provided.
|
||||
///
|
||||
/// Note: This expects that the given senders match the transactions in the block.
|
||||
pub fn new_unhashed(block: B, senders: Vec<Address>) -> Self {
|
||||
Self { block: SealedBlock::new_unhashed(block), senders }
|
||||
}
|
||||
|
||||
/// Returns the recovered senders.
|
||||
pub fn senders(&self) -> &[Address] {
|
||||
&self.senders
|
||||
}
|
||||
|
||||
/// Returns an iterator over the recovered senders.
|
||||
pub fn senders_iter(&self) -> impl Iterator<Item = &Address> {
|
||||
self.senders.iter()
|
||||
}
|
||||
|
||||
/// Consumes the type and returns the inner block.
|
||||
pub fn into_block(self) -> B {
|
||||
self.block.into_block()
|
||||
}
|
||||
|
||||
/// Returns a reference to the sealed block.
|
||||
pub const fn sealed_block(&self) -> &SealedBlock<B> {
|
||||
&self.block
|
||||
}
|
||||
|
||||
/// Creates a new recovered block instance with the given [`SealedBlock`] and senders as
|
||||
/// provided
|
||||
pub const fn new_sealed(block: SealedBlock<B>, senders: Vec<Address>) -> Self {
|
||||
Self { block, senders }
|
||||
}
|
||||
|
||||
/// A safer variant of [`Self::new_unhashed`] that checks if the number of senders is equal to
|
||||
/// the number of transactions in the block and recovers the senders from the transactions, if
|
||||
/// not using [`SignedTransaction::recover_signer`](crate::transaction::signed::SignedTransaction)
|
||||
/// to recover the senders.
|
||||
pub fn try_new(
|
||||
block: B,
|
||||
senders: Vec<Address>,
|
||||
hash: BlockHash,
|
||||
) -> Result<Self, SealedBlockRecoveryError<B>> {
|
||||
let senders = if block.body().transaction_count() == senders.len() {
|
||||
senders
|
||||
} else {
|
||||
let Ok(senders) = block.body().try_recover_signers() else {
|
||||
return Err(SealedBlockRecoveryError::new(SealedBlock::new_unchecked(block, hash)));
|
||||
};
|
||||
senders
|
||||
};
|
||||
Ok(Self::new(block, senders, hash))
|
||||
}
|
||||
|
||||
/// A safer variant of [`Self::new`] that checks if the number of senders is equal to
|
||||
/// the number of transactions in the block and recovers the senders from the transactions, if
|
||||
/// not using [`SignedTransaction::recover_signer_unchecked`](crate::transaction::signed::SignedTransaction)
|
||||
/// to recover the senders.
|
||||
pub fn try_new_unchecked(
|
||||
block: B,
|
||||
senders: Vec<Address>,
|
||||
hash: BlockHash,
|
||||
) -> Result<Self, SealedBlockRecoveryError<B>> {
|
||||
let senders = if block.body().transaction_count() == senders.len() {
|
||||
senders
|
||||
} else {
|
||||
let Ok(senders) = block.body().try_recover_signers_unchecked() else {
|
||||
return Err(SealedBlockRecoveryError::new(SealedBlock::new_unchecked(block, hash)));
|
||||
};
|
||||
senders
|
||||
};
|
||||
Ok(Self::new(block, senders, hash))
|
||||
}
|
||||
|
||||
/// A safer variant of [`Self::new_unhashed`] that checks if the number of senders is equal to
|
||||
/// the number of transactions in the block and recovers the senders from the transactions, if
|
||||
/// not using [`SignedTransaction::recover_signer`](crate::transaction::signed::SignedTransaction)
|
||||
/// to recover the senders.
|
||||
pub fn try_new_unhashed(block: B, senders: Vec<Address>) -> Result<Self, RecoveryError> {
|
||||
let senders = if block.body().transaction_count() == senders.len() {
|
||||
senders
|
||||
} else {
|
||||
block.body().try_recover_signers()?
|
||||
};
|
||||
Ok(Self::new_unhashed(block, senders))
|
||||
}
|
||||
|
||||
/// A safer variant of [`Self::new_unhashed`] that checks if the number of senders is equal to
|
||||
/// the number of transactions in the block and recovers the senders from the transactions, if
|
||||
/// not using [`SignedTransaction::recover_signer_unchecked`](crate::transaction::signed::SignedTransaction)
|
||||
/// to recover the senders.
|
||||
pub fn try_new_unhashed_unchecked(
|
||||
block: B,
|
||||
senders: Vec<Address>,
|
||||
) -> Result<Self, RecoveryError> {
|
||||
let senders = if block.body().transaction_count() == senders.len() {
|
||||
senders
|
||||
} else {
|
||||
block.body().try_recover_signers_unchecked()?
|
||||
};
|
||||
Ok(Self::new_unhashed(block, senders))
|
||||
}
|
||||
|
||||
/// Recovers the senders from the transactions in the block using
|
||||
/// [`SignedTransaction::recover_signer`](crate::transaction::signed::SignedTransaction).
|
||||
///
|
||||
/// Returns an error if any of the transactions fail to recover the sender.
|
||||
pub fn try_recover(block: B) -> Result<Self, RecoveryError> {
|
||||
let senders = block.body().try_recover_signers()?;
|
||||
Ok(Self::new_unhashed(block, senders))
|
||||
}
|
||||
|
||||
/// Recovers the senders from the transactions in the block using
|
||||
/// [`SignedTransaction::recover_signer_unchecked`](crate::transaction::signed::SignedTransaction).
|
||||
///
|
||||
/// Returns an error if any of the transactions fail to recover the sender.
|
||||
pub fn try_recover_unchecked(block: B) -> Result<Self, RecoveryError> {
|
||||
let senders = block.body().try_recover_signers_unchecked()?;
|
||||
Ok(Self::new_unhashed(block, senders))
|
||||
}
|
||||
|
||||
/// Recovers the senders from the transactions in the block using
|
||||
/// [`SignedTransaction::recover_signer`](crate::transaction::signed::SignedTransaction).
|
||||
///
|
||||
/// Returns an error if any of the transactions fail to recover the sender.
|
||||
pub fn try_recover_sealed(block: SealedBlock<B>) -> Result<Self, SealedBlockRecoveryError<B>> {
|
||||
let Ok(senders) = block.body().try_recover_signers() else {
|
||||
return Err(SealedBlockRecoveryError::new(block));
|
||||
};
|
||||
let (block, hash) = block.split();
|
||||
Ok(Self::new(block, senders, hash))
|
||||
}
|
||||
|
||||
/// Recovers the senders from the transactions in the sealed block using
|
||||
/// [`SignedTransaction::recover_signer_unchecked`](crate::transaction::signed::SignedTransaction).
|
||||
///
|
||||
/// Returns an error if any of the transactions fail to recover the sender.
|
||||
pub fn try_recover_sealed_unchecked(
|
||||
block: SealedBlock<B>,
|
||||
) -> Result<Self, SealedBlockRecoveryError<B>> {
|
||||
let Ok(senders) = block.body().try_recover_signers_unchecked() else {
|
||||
return Err(SealedBlockRecoveryError::new(block));
|
||||
};
|
||||
let (block, hash) = block.split();
|
||||
Ok(Self::new(block, senders, hash))
|
||||
}
|
||||
|
||||
/// A safer variant of [`Self::new_unhashed`] that checks if the number of senders is equal to
|
||||
/// the number of transactions in the block and recovers the senders from the transactions, if
|
||||
/// not using [`SignedTransaction::recover_signer_unchecked`](crate::transaction::signed::SignedTransaction)
|
||||
/// to recover the senders.
|
||||
///
|
||||
/// Returns an error if any of the transactions fail to recover the sender.
|
||||
pub fn try_recover_sealed_with_senders(
|
||||
block: SealedBlock<B>,
|
||||
senders: Vec<Address>,
|
||||
) -> Result<Self, SealedBlockRecoveryError<B>> {
|
||||
let (block, hash) = block.split();
|
||||
Self::try_new(block, senders, hash)
|
||||
}
|
||||
|
||||
/// A safer variant of [`Self::new`] that checks if the number of senders is equal to
|
||||
/// the number of transactions in the block and recovers the senders from the transactions, if
|
||||
/// not using [`SignedTransaction::recover_signer_unchecked`](crate::transaction::signed::SignedTransaction)
|
||||
/// to recover the senders.
|
||||
pub fn try_recover_sealed_with_senders_unchecked(
|
||||
block: SealedBlock<B>,
|
||||
senders: Vec<Address>,
|
||||
) -> Result<Self, SealedBlockRecoveryError<B>> {
|
||||
let (block, hash) = block.split();
|
||||
Self::try_new_unchecked(block, senders, hash)
|
||||
}
|
||||
|
||||
/// Returns the block hash.
|
||||
pub fn hash_ref(&self) -> &BlockHash {
|
||||
self.block.hash_ref()
|
||||
}
|
||||
|
||||
/// Returns a copy of the block hash.
|
||||
pub fn hash(&self) -> BlockHash {
|
||||
*self.hash_ref()
|
||||
}
|
||||
|
||||
/// Return the number hash tuple.
|
||||
pub fn num_hash(&self) -> BlockNumHash {
|
||||
BlockNumHash::new(self.header().number(), self.hash())
|
||||
}
|
||||
|
||||
/// Return a [`BlockWithParent`] for this header.
|
||||
pub fn block_with_parent(&self) -> BlockWithParent {
|
||||
BlockWithParent { parent: self.header().parent_hash(), block: self.num_hash() }
|
||||
}
|
||||
|
||||
/// Clone the header.
|
||||
pub fn clone_header(&self) -> B::Header {
|
||||
self.header().clone()
|
||||
}
|
||||
|
||||
/// Clones the internal header and returns a [`SealedHeader`] sealed with the hash.
|
||||
pub fn clone_sealed_header(&self) -> SealedHeader<B::Header> {
|
||||
SealedHeader::new(self.clone_header(), self.hash())
|
||||
}
|
||||
|
||||
/// Clones the wrapped block and returns the [`SealedBlock`] sealed with the hash.
|
||||
pub fn clone_sealed_block(&self) -> SealedBlock<B> {
|
||||
self.block.clone()
|
||||
}
|
||||
|
||||
/// Consumes the block and returns the block's header.
|
||||
pub fn into_header(self) -> B::Header {
|
||||
self.block.into_header()
|
||||
}
|
||||
|
||||
/// Consumes the block and returns the block's body.
|
||||
pub fn into_body(self) -> B::Body {
|
||||
self.block.into_body()
|
||||
}
|
||||
|
||||
/// Consumes the block and returns the [`SealedBlock`] and drops the recovered senders.
|
||||
pub fn into_sealed_block(self) -> SealedBlock<B> {
|
||||
self.block
|
||||
}
|
||||
|
||||
/// Consumes the type and returns its components.
|
||||
pub fn split_sealed(self) -> (SealedBlock<B>, Vec<Address>) {
|
||||
(self.block, self.senders)
|
||||
}
|
||||
|
||||
/// Consumes the type and returns its components.
|
||||
#[doc(alias = "into_components")]
|
||||
pub fn split(self) -> (B, Vec<Address>) {
|
||||
(self.block.into_block(), self.senders)
|
||||
}
|
||||
|
||||
/// Returns an iterator over all transactions and their sender.
|
||||
#[inline]
|
||||
pub fn transactions_with_sender(
|
||||
&self,
|
||||
) -> impl Iterator<Item = (&Address, &<B::Body as BlockBody>::Transaction)> + '_ {
|
||||
self.senders.iter().zip(self.block.body().transactions())
|
||||
}
|
||||
|
||||
/// Returns an iterator over all transactions in the block.
|
||||
#[inline]
|
||||
pub fn into_transactions_ecrecovered(
|
||||
self,
|
||||
) -> impl Iterator<Item = Recovered<<B::Body as BlockBody>::Transaction>> {
|
||||
self.block
|
||||
.split()
|
||||
.0
|
||||
.into_body()
|
||||
.into_transactions()
|
||||
.into_iter()
|
||||
.zip(self.senders)
|
||||
.map(|(tx, sender)| tx.with_signer(sender))
|
||||
}
|
||||
|
||||
/// Consumes the block and returns the transactions of the block.
|
||||
#[inline]
|
||||
pub fn into_transactions(self) -> Vec<<B::Body as BlockBody>::Transaction> {
|
||||
self.block.split().0.into_body().into_transactions()
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Block> BlockHeader for RecoveredBlock<B> {
|
||||
fn parent_hash(&self) -> B256 {
|
||||
self.header().parent_hash()
|
||||
}
|
||||
|
||||
fn ommers_hash(&self) -> B256 {
|
||||
self.header().ommers_hash()
|
||||
}
|
||||
|
||||
fn beneficiary(&self) -> Address {
|
||||
self.header().beneficiary()
|
||||
}
|
||||
|
||||
fn state_root(&self) -> B256 {
|
||||
self.header().state_root()
|
||||
}
|
||||
|
||||
fn transactions_root(&self) -> B256 {
|
||||
self.header().transactions_root()
|
||||
}
|
||||
|
||||
fn receipts_root(&self) -> B256 {
|
||||
self.header().receipts_root()
|
||||
}
|
||||
|
||||
fn withdrawals_root(&self) -> Option<B256> {
|
||||
self.header().withdrawals_root()
|
||||
}
|
||||
|
||||
fn logs_bloom(&self) -> Bloom {
|
||||
self.header().logs_bloom()
|
||||
}
|
||||
|
||||
fn difficulty(&self) -> U256 {
|
||||
self.header().difficulty()
|
||||
}
|
||||
|
||||
fn number(&self) -> BlockNumber {
|
||||
self.header().number()
|
||||
}
|
||||
|
||||
fn gas_limit(&self) -> u64 {
|
||||
self.header().gas_limit()
|
||||
}
|
||||
|
||||
fn gas_used(&self) -> u64 {
|
||||
self.header().gas_used()
|
||||
}
|
||||
|
||||
fn timestamp(&self) -> u64 {
|
||||
self.header().timestamp()
|
||||
}
|
||||
|
||||
fn mix_hash(&self) -> Option<B256> {
|
||||
self.header().mix_hash()
|
||||
}
|
||||
|
||||
fn nonce(&self) -> Option<B64> {
|
||||
self.header().nonce()
|
||||
}
|
||||
|
||||
fn base_fee_per_gas(&self) -> Option<u64> {
|
||||
self.header().base_fee_per_gas()
|
||||
}
|
||||
|
||||
fn blob_gas_used(&self) -> Option<u64> {
|
||||
self.header().blob_gas_used()
|
||||
}
|
||||
|
||||
fn excess_blob_gas(&self) -> Option<u64> {
|
||||
self.header().excess_blob_gas()
|
||||
}
|
||||
|
||||
fn parent_beacon_block_root(&self) -> Option<B256> {
|
||||
self.header().parent_beacon_block_root()
|
||||
}
|
||||
|
||||
fn requests_hash(&self) -> Option<B256> {
|
||||
self.header().requests_hash()
|
||||
}
|
||||
|
||||
fn extra_data(&self) -> &Bytes {
|
||||
self.header().extra_data()
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Block> Eq for RecoveredBlock<B> {}
|
||||
|
||||
impl<B: Block> PartialEq for RecoveredBlock<B> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.hash_ref().eq(other.hash_ref()) &&
|
||||
self.block.eq(&other.block) &&
|
||||
self.senders.eq(&other.senders)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Block + Default> Default for RecoveredBlock<B> {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
Self::new_unhashed(B::default(), Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Block> InMemorySize for RecoveredBlock<B> {
|
||||
#[inline]
|
||||
fn size(&self) -> usize {
|
||||
self.block.size() + self.senders.len() * core::mem::size_of::<Address>()
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Block> From<RecoveredBlock<B>> for Sealed<B> {
|
||||
fn from(value: RecoveredBlock<B>) -> Self {
|
||||
value.block.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "arbitrary"))]
|
||||
impl<'a, B> arbitrary::Arbitrary<'a> for RecoveredBlock<B>
|
||||
where
|
||||
B: Block + arbitrary::Arbitrary<'a>,
|
||||
{
|
||||
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
|
||||
let block = B::arbitrary(u)?;
|
||||
Ok(Self::try_recover(block).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-utils"))]
|
||||
impl<B: Block> RecoveredBlock<B> {
|
||||
/// Returns a mutable reference to the recovered senders.
|
||||
pub fn senders_mut(&mut self) -> &mut Vec<Address> {
|
||||
&mut self.senders
|
||||
}
|
||||
|
||||
/// Appends the sender to the list of senders.
|
||||
pub fn push_sender(&mut self, sender: Address) {
|
||||
self.senders.push(sender);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-utils"))]
|
||||
impl<B> core::ops::DerefMut for RecoveredBlock<B>
|
||||
where
|
||||
B: Block,
|
||||
{
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.block
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-utils"))]
|
||||
impl<B: crate::test_utils::TestBlock> RecoveredBlock<B> {
|
||||
/// Updates the block header.
|
||||
pub fn set_header(&mut self, header: B::Header) {
|
||||
*self.header_mut() = header
|
||||
}
|
||||
|
||||
/// Updates the block hash.
|
||||
pub fn set_hash(&mut self, hash: BlockHash) {
|
||||
self.block.set_hash(hash)
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the header.
|
||||
pub fn header_mut(&mut self) -> &mut B::Header {
|
||||
self.block.header_mut()
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the header.
|
||||
pub fn block_mut(&mut self) -> &mut B::Body {
|
||||
self.block.body_mut()
|
||||
}
|
||||
|
||||
/// Updates the parent block hash.
|
||||
pub fn set_parent_hash(&mut self, hash: BlockHash) {
|
||||
self.block.set_parent_hash(hash);
|
||||
}
|
||||
|
||||
/// Updates the block number.
|
||||
pub fn set_block_number(&mut self, number: alloy_primitives::BlockNumber) {
|
||||
self.block.set_block_number(number);
|
||||
}
|
||||
|
||||
/// Updates the block state root.
|
||||
pub fn set_state_root(&mut self, state_root: alloy_primitives::B256) {
|
||||
self.block.set_state_root(state_root);
|
||||
}
|
||||
|
||||
/// Updates the block difficulty.
|
||||
pub fn set_difficulty(&mut self, difficulty: alloy_primitives::U256) {
|
||||
self.block.set_difficulty(difficulty);
|
||||
}
|
||||
}
|
||||
|
||||
/// Bincode-compatible [`RecoveredBlock`] serde implementation.
|
||||
#[cfg(feature = "serde-bincode-compat")]
|
||||
pub(super) mod serde_bincode_compat {
|
||||
use crate::{
|
||||
serde_bincode_compat::{self, SerdeBincodeCompat},
|
||||
Block,
|
||||
};
|
||||
use alloc::{borrow::Cow, vec::Vec};
|
||||
use alloy_primitives::Address;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use serde_with::{DeserializeAs, SerializeAs};
|
||||
|
||||
/// Bincode-compatible [`super::RecoveredBlock`] serde implementation.
|
||||
///
|
||||
/// Intended to use with the [`serde_with::serde_as`] macro in the following way:
|
||||
/// ```rust
|
||||
/// use reth_primitives_traits::{
|
||||
/// block::RecoveredBlock,
|
||||
/// serde_bincode_compat::{self, SerdeBincodeCompat},
|
||||
/// Block,
|
||||
/// };
|
||||
/// use serde::{Deserialize, Serialize};
|
||||
/// use serde_with::serde_as;
|
||||
///
|
||||
/// #[serde_as]
|
||||
/// #[derive(Serialize, Deserialize)]
|
||||
/// struct Data<T: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static> {
|
||||
/// #[serde_as(as = "serde_bincode_compat::RecoveredBlock<'_, T>")]
|
||||
/// block: RecoveredBlock<T>,
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(derive_more::Debug, Serialize, Deserialize)]
|
||||
pub struct RecoveredBlock<
|
||||
'a,
|
||||
T: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static,
|
||||
> {
|
||||
#[serde(
|
||||
bound = "serde_bincode_compat::SealedBlock<'a, T>: Serialize + serde::de::DeserializeOwned"
|
||||
)]
|
||||
block: serde_bincode_compat::SealedBlock<'a, T>,
|
||||
senders: Cow<'a, Vec<Address>>,
|
||||
}
|
||||
|
||||
impl<'a, T: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static>
|
||||
From<&'a super::RecoveredBlock<T>> for RecoveredBlock<'a, T>
|
||||
{
|
||||
fn from(value: &'a super::RecoveredBlock<T>) -> Self {
|
||||
Self { block: (&value.block).into(), senders: Cow::Borrowed(&value.senders) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static>
|
||||
From<RecoveredBlock<'a, T>> for super::RecoveredBlock<T>
|
||||
{
|
||||
fn from(value: RecoveredBlock<'a, T>) -> Self {
|
||||
Self::new_sealed(value.block.into(), value.senders.into_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static>
|
||||
SerializeAs<super::RecoveredBlock<T>> for RecoveredBlock<'_, T>
|
||||
{
|
||||
fn serialize_as<S>(
|
||||
source: &super::RecoveredBlock<T>,
|
||||
serializer: S,
|
||||
) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
RecoveredBlock::from(source).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, T: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static>
|
||||
DeserializeAs<'de, super::RecoveredBlock<T>> for RecoveredBlock<'de, T>
|
||||
{
|
||||
fn deserialize_as<D>(deserializer: D) -> Result<super::RecoveredBlock<T>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
RecoveredBlock::deserialize(deserializer).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static>
|
||||
SerdeBincodeCompat for super::RecoveredBlock<T>
|
||||
{
|
||||
type BincodeRepr<'a> = RecoveredBlock<'a, T>;
|
||||
}
|
||||
}
|
||||
462
crates/primitives-traits/src/block/sealed.rs
Normal file
462
crates/primitives-traits/src/block/sealed.rs
Normal file
@ -0,0 +1,462 @@
|
||||
//! Sealed block types
|
||||
|
||||
use crate::{
|
||||
block::{error::BlockRecoveryError, RecoveredBlock},
|
||||
Block, BlockBody, GotExpected, InMemorySize, SealedHeader,
|
||||
};
|
||||
use alloc::vec::Vec;
|
||||
use alloy_consensus::BlockHeader;
|
||||
use alloy_eips::{eip1898::BlockWithParent, BlockNumHash};
|
||||
use alloy_primitives::{Address, BlockHash, Sealable, Sealed, B256};
|
||||
use alloy_rlp::{Decodable, Encodable};
|
||||
use bytes::BufMut;
|
||||
use core::ops::Deref;
|
||||
|
||||
/// Sealed full block composed of the block's header and body.
|
||||
///
|
||||
/// This type uses lazy sealing to avoid hashing the header until it is needed, see also
|
||||
/// [`SealedHeader`].
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct SealedBlock<B: Block> {
|
||||
/// Sealed Header.
|
||||
header: SealedHeader<B::Header>,
|
||||
/// the block's body.
|
||||
body: B::Body,
|
||||
}
|
||||
|
||||
impl<B: Block> SealedBlock<B> {
|
||||
/// Hashes the header and creates a sealed block.
|
||||
///
|
||||
/// This calculates the header hash. To create a [`SealedBlock`] without calculating the hash
|
||||
/// upfront see [`SealedBlock::new_unhashed`]
|
||||
pub fn seal_slow(block: B) -> Self {
|
||||
let hash = block.header().hash_slow();
|
||||
Self::new_unchecked(block, hash)
|
||||
}
|
||||
|
||||
/// Create a new sealed block instance using the block.
|
||||
///
|
||||
/// Caution: This assumes the given hash is the block's hash.
|
||||
#[inline]
|
||||
pub fn new_unchecked(block: B, hash: BlockHash) -> Self {
|
||||
let (header, body) = block.split();
|
||||
Self { header: SealedHeader::new(header, hash), body }
|
||||
}
|
||||
|
||||
/// Creates a `SealedBlock` from the block without the available hash
|
||||
pub fn new_unhashed(block: B) -> Self {
|
||||
let (header, body) = block.split();
|
||||
Self { header: SealedHeader::new_unhashed(header), body }
|
||||
}
|
||||
|
||||
/// Creates the [`SealedBlock`] from the block's parts by hashing the header.
|
||||
///
|
||||
///
|
||||
/// This calculates the header hash. To create a [`SealedBlock`] from its parts without
|
||||
/// calculating the hash upfront see [`SealedBlock::from_parts_unhashed`]
|
||||
pub fn seal_parts(header: B::Header, body: B::Body) -> Self {
|
||||
Self::seal_slow(B::new(header, body))
|
||||
}
|
||||
|
||||
/// Creates the [`SealedBlock`] from the block's parts without calculating the hash upfront.
|
||||
pub fn from_parts_unhashed(header: B::Header, body: B::Body) -> Self {
|
||||
Self::new_unhashed(B::new(header, body))
|
||||
}
|
||||
|
||||
/// Creates the [`SealedBlock`] from the block's parts.
|
||||
pub fn from_parts_unchecked(header: B::Header, body: B::Body, hash: BlockHash) -> Self {
|
||||
Self::new_unchecked(B::new(header, body), hash)
|
||||
}
|
||||
|
||||
/// Creates the [`SealedBlock`] from the [`SealedHeader`] and the body.
|
||||
pub fn from_sealed_parts(header: SealedHeader<B::Header>, body: B::Body) -> Self {
|
||||
let (header, hash) = header.split();
|
||||
Self::from_parts_unchecked(header, body, hash)
|
||||
}
|
||||
|
||||
/// Returns a reference to the block hash.
|
||||
#[inline]
|
||||
pub fn hash_ref(&self) -> &BlockHash {
|
||||
self.header.hash_ref()
|
||||
}
|
||||
|
||||
/// Returns the block hash.
|
||||
#[inline]
|
||||
pub fn hash(&self) -> B256 {
|
||||
self.header.hash()
|
||||
}
|
||||
|
||||
/// Consumes the type and returns its components.
|
||||
#[doc(alias = "into_components")]
|
||||
pub fn split(self) -> (B, BlockHash) {
|
||||
let (header, hash) = self.header.split();
|
||||
(B::new(header, self.body), hash)
|
||||
}
|
||||
|
||||
/// Consumes the type and returns the block.
|
||||
pub fn into_block(self) -> B {
|
||||
self.unseal()
|
||||
}
|
||||
|
||||
/// Consumes the type and returns the block.
|
||||
pub fn unseal(self) -> B {
|
||||
let header = self.header.unseal();
|
||||
B::new(header, self.body)
|
||||
}
|
||||
|
||||
/// Clones the wrapped block.
|
||||
pub fn clone_block(&self) -> B {
|
||||
B::new(self.header.clone_header(), self.body.clone())
|
||||
}
|
||||
|
||||
/// Converts this block into a [`RecoveredBlock`] with the given senders
|
||||
///
|
||||
/// Note: This method assumes the senders are correct and does not validate them.
|
||||
pub const fn with_senders(self, senders: Vec<Address>) -> RecoveredBlock<B> {
|
||||
RecoveredBlock::new_sealed(self, senders)
|
||||
}
|
||||
|
||||
/// Converts this block into a [`RecoveredBlock`] with the given senders if the number of
|
||||
/// senders is equal to the number of transactions in the block and recovers the senders from
|
||||
/// the transactions, if
|
||||
/// not using [`SignedTransaction::recover_signer`](crate::transaction::signed::SignedTransaction)
|
||||
/// to recover the senders.
|
||||
///
|
||||
/// Returns an error if any of the transactions fail to recover the sender.
|
||||
pub fn try_with_senders(
|
||||
self,
|
||||
senders: Vec<Address>,
|
||||
) -> Result<RecoveredBlock<B>, BlockRecoveryError<Self>> {
|
||||
RecoveredBlock::try_recover_sealed_with_senders(self, senders)
|
||||
}
|
||||
|
||||
/// Converts this block into a [`RecoveredBlock`] with the given senders if the number of
|
||||
/// senders is equal to the number of transactions in the block and recovers the senders from
|
||||
/// the transactions, if
|
||||
/// not using [`SignedTransaction::recover_signer_unchecked`](crate::transaction::signed::SignedTransaction)
|
||||
/// to recover the senders.
|
||||
///
|
||||
/// Returns an error if any of the transactions fail to recover the sender.
|
||||
pub fn try_with_senders_unchecked(
|
||||
self,
|
||||
senders: Vec<Address>,
|
||||
) -> Result<RecoveredBlock<B>, BlockRecoveryError<Self>> {
|
||||
RecoveredBlock::try_recover_sealed_with_senders_unchecked(self, senders)
|
||||
}
|
||||
|
||||
/// Recovers the senders from the transactions in the block using
|
||||
/// [`SignedTransaction::recover_signer`](crate::transaction::signed::SignedTransaction).
|
||||
///
|
||||
/// Returns an error if any of the transactions fail to recover the sender.
|
||||
pub fn try_recover(self) -> Result<RecoveredBlock<B>, BlockRecoveryError<Self>> {
|
||||
RecoveredBlock::try_recover_sealed(self)
|
||||
}
|
||||
|
||||
/// Recovers the senders from the transactions in the block using
|
||||
/// [`SignedTransaction::recover_signer_unchecked`](crate::transaction::signed::SignedTransaction).
|
||||
///
|
||||
/// Returns an error if any of the transactions fail to recover the sender.
|
||||
pub fn try_recover_unchecked(self) -> Result<RecoveredBlock<B>, BlockRecoveryError<Self>> {
|
||||
RecoveredBlock::try_recover_sealed_unchecked(self)
|
||||
}
|
||||
|
||||
/// Returns reference to block header.
|
||||
pub const fn header(&self) -> &B::Header {
|
||||
self.header.header()
|
||||
}
|
||||
|
||||
/// Returns reference to block body.
|
||||
pub const fn body(&self) -> &B::Body {
|
||||
&self.body
|
||||
}
|
||||
|
||||
/// Returns the length of the block.
|
||||
pub fn rlp_length(&self) -> usize {
|
||||
B::rlp_length(self.header(), self.body())
|
||||
}
|
||||
|
||||
/// Recovers all senders from the transactions in the block.
|
||||
///
|
||||
/// Returns `None` if any of the transactions fail to recover the sender.
|
||||
pub fn senders(&self) -> Option<Vec<Address>> {
|
||||
self.body().recover_signers()
|
||||
}
|
||||
|
||||
/// Return the number hash tuple.
|
||||
pub fn num_hash(&self) -> BlockNumHash {
|
||||
BlockNumHash::new(self.number(), self.hash())
|
||||
}
|
||||
|
||||
/// Return a [`BlockWithParent`] for this header.
|
||||
pub fn block_with_parent(&self) -> BlockWithParent {
|
||||
BlockWithParent { parent: self.parent_hash(), block: self.num_hash() }
|
||||
}
|
||||
|
||||
/// Returns the Sealed header.
|
||||
pub const fn sealed_header(&self) -> &SealedHeader<B::Header> {
|
||||
&self.header
|
||||
}
|
||||
|
||||
/// Returns the wrapped `SealedHeader<B::Header>` as `SealedHeader<&B::Header>`.
|
||||
pub fn sealed_header_ref(&self) -> SealedHeader<&B::Header> {
|
||||
SealedHeader::new(self.header(), self.hash())
|
||||
}
|
||||
|
||||
/// Clones the wrapped header and returns a [`SealedHeader`] sealed with the hash.
|
||||
pub fn clone_sealed_header(&self) -> SealedHeader<B::Header> {
|
||||
self.header.clone()
|
||||
}
|
||||
|
||||
/// Consumes the block and returns the sealed header.
|
||||
pub fn into_sealed_header(self) -> SealedHeader<B::Header> {
|
||||
self.header
|
||||
}
|
||||
|
||||
/// Consumes the block and returns the header.
|
||||
pub fn into_header(self) -> B::Header {
|
||||
self.header.unseal()
|
||||
}
|
||||
|
||||
/// Consumes the block and returns the body.
|
||||
pub fn into_body(self) -> B::Body {
|
||||
self.body
|
||||
}
|
||||
|
||||
/// Splits the block into body and header into separate components
|
||||
pub fn split_header_body(self) -> (B::Header, B::Body) {
|
||||
let header = self.header.unseal();
|
||||
(header, self.body)
|
||||
}
|
||||
|
||||
/// Splits the block into body and header into separate components.
|
||||
pub fn split_sealed_header_body(self) -> (SealedHeader<B::Header>, B::Body) {
|
||||
(self.header, self.body)
|
||||
}
|
||||
|
||||
/// Returns an iterator over all blob versioned hashes from the block body.
|
||||
#[inline]
|
||||
pub fn blob_versioned_hashes_iter(&self) -> impl Iterator<Item = &B256> + '_ {
|
||||
self.body().blob_versioned_hashes_iter()
|
||||
}
|
||||
|
||||
/// Returns the number of transactions in the block.
|
||||
#[inline]
|
||||
pub fn transaction_count(&self) -> usize {
|
||||
self.body().transaction_count()
|
||||
}
|
||||
|
||||
/// Ensures that the transaction root in the block header is valid.
|
||||
///
|
||||
/// The transaction root is the Keccak 256-bit hash of the root node of the trie structure
|
||||
/// populated with each transaction in the transactions list portion of the block.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns `Ok(())` if the calculated transaction root matches the one stored in the header,
|
||||
/// indicating that the transactions in the block are correctly represented in the trie.
|
||||
///
|
||||
/// Returns `Err(error)` if the transaction root validation fails, providing a `GotExpected`
|
||||
/// error containing the calculated and expected roots.
|
||||
pub fn ensure_transaction_root_valid(&self) -> Result<(), GotExpected<B256>> {
|
||||
let calculated_root = self.body().calculate_tx_root();
|
||||
|
||||
if self.header().transactions_root() != calculated_root {
|
||||
return Err(GotExpected {
|
||||
got: calculated_root,
|
||||
expected: self.header().transactions_root(),
|
||||
})
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B> From<B> for SealedBlock<B>
|
||||
where
|
||||
B: Block,
|
||||
{
|
||||
fn from(block: B) -> Self {
|
||||
Self::seal_slow(block)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B> Default for SealedBlock<B>
|
||||
where
|
||||
B: Block + Default,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self::seal_slow(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Block> InMemorySize for SealedBlock<B> {
|
||||
#[inline]
|
||||
fn size(&self) -> usize {
|
||||
self.body.size() + self.header.size()
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Block> Deref for SealedBlock<B> {
|
||||
type Target = B::Header;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.header()
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Block> Encodable for SealedBlock<B> {
|
||||
fn encode(&self, out: &mut dyn BufMut) {
|
||||
self.body.encode(out);
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Block> Decodable for SealedBlock<B> {
|
||||
fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
|
||||
let block = B::decode(buf)?;
|
||||
Ok(Self::seal_slow(block))
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Block> From<SealedBlock<B>> for Sealed<B> {
|
||||
fn from(value: SealedBlock<B>) -> Self {
|
||||
let (block, hash) = value.split();
|
||||
Self::new_unchecked(block, hash)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "arbitrary"))]
|
||||
impl<'a, B> arbitrary::Arbitrary<'a> for SealedBlock<B>
|
||||
where
|
||||
B: Block + arbitrary::Arbitrary<'a>,
|
||||
{
|
||||
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
|
||||
let block = B::arbitrary(u)?;
|
||||
Ok(Self::seal_slow(block))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-utils"))]
|
||||
impl<B: crate::test_utils::TestBlock> SealedBlock<B> {
|
||||
/// Returns a mutable reference to the header.
|
||||
pub fn header_mut(&mut self) -> &mut B::Header {
|
||||
self.header.header_mut()
|
||||
}
|
||||
|
||||
/// Updates the block hash.
|
||||
pub fn set_hash(&mut self, hash: BlockHash) {
|
||||
self.header.set_hash(hash)
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the header.
|
||||
pub fn body_mut(&mut self) -> &mut B::Body {
|
||||
&mut self.body
|
||||
}
|
||||
|
||||
/// Updates the parent block hash.
|
||||
pub fn set_parent_hash(&mut self, hash: BlockHash) {
|
||||
self.header.set_parent_hash(hash)
|
||||
}
|
||||
|
||||
/// Updates the block number.
|
||||
pub fn set_block_number(&mut self, number: alloy_primitives::BlockNumber) {
|
||||
self.header.set_block_number(number)
|
||||
}
|
||||
|
||||
/// Updates the block state root.
|
||||
pub fn set_state_root(&mut self, state_root: alloy_primitives::B256) {
|
||||
self.header.set_state_root(state_root)
|
||||
}
|
||||
|
||||
/// Updates the block difficulty.
|
||||
pub fn set_difficulty(&mut self, difficulty: alloy_primitives::U256) {
|
||||
self.header.set_difficulty(difficulty)
|
||||
}
|
||||
}
|
||||
|
||||
/// Bincode-compatible [`SealedBlock`] serde implementation.
|
||||
#[cfg(feature = "serde-bincode-compat")]
|
||||
pub(super) mod serde_bincode_compat {
|
||||
use crate::{
|
||||
serde_bincode_compat::{self, BincodeReprFor, SerdeBincodeCompat},
|
||||
Block,
|
||||
};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use serde_with::{serde_as, DeserializeAs, SerializeAs};
|
||||
|
||||
/// Bincode-compatible [`super::SealedBlock`] serde implementation.
|
||||
///
|
||||
/// Intended to use with the [`serde_with::serde_as`] macro in the following way:
|
||||
/// ```rust
|
||||
/// use reth_primitives_traits::{
|
||||
/// block::SealedBlock,
|
||||
/// serde_bincode_compat::{self, SerdeBincodeCompat},
|
||||
/// Block,
|
||||
/// };
|
||||
/// use serde::{Deserialize, Serialize};
|
||||
/// use serde_with::serde_as;
|
||||
///
|
||||
/// #[serde_as]
|
||||
/// #[derive(Serialize, Deserialize)]
|
||||
/// struct Data<T: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static> {
|
||||
/// #[serde_as(as = "serde_bincode_compat::SealedBlock<'_, T>")]
|
||||
/// block: SealedBlock<T>,
|
||||
/// }
|
||||
/// ```
|
||||
#[serde_as]
|
||||
#[derive(derive_more::Debug, Serialize, Deserialize)]
|
||||
pub struct SealedBlock<
|
||||
'a,
|
||||
T: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static,
|
||||
> {
|
||||
#[serde(
|
||||
bound = "serde_bincode_compat::SealedHeader<'a, T::Header>: Serialize + serde::de::DeserializeOwned"
|
||||
)]
|
||||
header: serde_bincode_compat::SealedHeader<'a, T::Header>,
|
||||
body: BincodeReprFor<'a, T::Body>,
|
||||
}
|
||||
|
||||
impl<'a, T: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static>
|
||||
From<&'a super::SealedBlock<T>> for SealedBlock<'a, T>
|
||||
{
|
||||
fn from(value: &'a super::SealedBlock<T>) -> Self {
|
||||
Self { header: (&value.header).into(), body: (&value.body).into() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static>
|
||||
From<SealedBlock<'a, T>> for super::SealedBlock<T>
|
||||
{
|
||||
fn from(value: SealedBlock<'a, T>) -> Self {
|
||||
Self::from_sealed_parts(value.header.into(), value.body.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static>
|
||||
SerializeAs<super::SealedBlock<T>> for SealedBlock<'_, T>
|
||||
{
|
||||
fn serialize_as<S>(source: &super::SealedBlock<T>, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
SealedBlock::from(source).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, T: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static>
|
||||
DeserializeAs<'de, super::SealedBlock<T>> for SealedBlock<'de, T>
|
||||
{
|
||||
fn deserialize_as<D>(deserializer: D) -> Result<super::SealedBlock<T>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
SealedBlock::deserialize(deserializer).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static>
|
||||
SerdeBincodeCompat for super::SealedBlock<T>
|
||||
{
|
||||
type BincodeRepr<'a> = SealedBlock<'a, T>;
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
use crate::InMemorySize;
|
||||
use crate::{sync::OnceLock, InMemorySize};
|
||||
pub use alloy_consensus::Header;
|
||||
use alloy_consensus::Sealed;
|
||||
use alloy_eips::{eip1898::BlockWithParent, BlockNumHash};
|
||||
@ -8,14 +8,20 @@ use bytes::BufMut;
|
||||
use core::mem;
|
||||
use derive_more::{AsRef, Deref};
|
||||
|
||||
/// A [`Header`] that is sealed at a precalculated hash, use [`SealedHeader::unseal()`] if you want
|
||||
/// to modify header.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, AsRef, Deref)]
|
||||
/// Seals the header with the block hash.
|
||||
///
|
||||
/// This type uses lazy sealing to avoid hashing the header until it is needed:
|
||||
///
|
||||
/// [`SealedHeader::new_unhashed`] creates a sealed header without hashing the header.
|
||||
/// [`SealedHeader::new`] creates a sealed header with the corresponding block hash.
|
||||
/// [`SealedHeader::hash`] computes the hash if it has not been computed yet.
|
||||
#[derive(Debug, Clone, AsRef, Deref)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(any(test, feature = "reth-codec"), reth_codecs::add_arbitrary_tests(rlp))]
|
||||
pub struct SealedHeader<H = Header> {
|
||||
/// Locked Header hash.
|
||||
hash: BlockHash,
|
||||
/// Block hash
|
||||
#[cfg_attr(feature = "serde", serde(skip))]
|
||||
hash: OnceLock<BlockHash>,
|
||||
/// Locked Header fields.
|
||||
#[as_ref]
|
||||
#[deref]
|
||||
@ -23,10 +29,16 @@ pub struct SealedHeader<H = Header> {
|
||||
}
|
||||
|
||||
impl<H> SealedHeader<H> {
|
||||
/// Creates the sealed header without hashing the header.
|
||||
#[inline]
|
||||
pub fn new_unhashed(header: H) -> Self {
|
||||
Self { header, hash: Default::default() }
|
||||
}
|
||||
|
||||
/// Creates the sealed header with the corresponding block hash.
|
||||
#[inline]
|
||||
pub const fn new(header: H, hash: BlockHash) -> Self {
|
||||
Self { header, hash }
|
||||
pub fn new(header: H, hash: BlockHash) -> Self {
|
||||
Self { header, hash: hash.into() }
|
||||
}
|
||||
|
||||
/// Returns the sealed Header fields.
|
||||
@ -43,35 +55,62 @@ impl<H> SealedHeader<H> {
|
||||
self.header.clone()
|
||||
}
|
||||
|
||||
/// Returns header/block hash.
|
||||
#[inline]
|
||||
pub const fn hash(&self) -> BlockHash {
|
||||
self.hash
|
||||
/// Consumes the type and returns the wrapped header.
|
||||
pub fn into_header(self) -> H {
|
||||
self.header
|
||||
}
|
||||
|
||||
/// Extract raw header that can be modified.
|
||||
/// Consumes the type and returns the wrapped header.
|
||||
pub fn unseal(self) -> H {
|
||||
self.header
|
||||
}
|
||||
|
||||
/// This is the inverse of [`Header::seal_slow`] which returns the raw header and hash.
|
||||
pub fn split(self) -> (H, BlockHash) {
|
||||
(self.header, self.hash)
|
||||
/// Converts from &`SealedHeader<H>` to `SealedHeader<&H>`.
|
||||
pub fn sealed_ref(&self) -> SealedHeader<&H> {
|
||||
SealedHeader { hash: self.hash.clone(), header: &self.header }
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: Sealable> SealedHeader<H> {
|
||||
/// Hashes the header and creates a sealed header.
|
||||
pub fn seal(header: H) -> Self {
|
||||
pub fn seal_slow(header: H) -> Self {
|
||||
let hash = header.hash_slow();
|
||||
Self::new(header, hash)
|
||||
}
|
||||
|
||||
/// Returns the block hash.
|
||||
///
|
||||
/// Note: if the hash has not been computed yet, this will compute the hash:
|
||||
/// [`Sealable::hash_slow`].
|
||||
pub fn hash_ref(&self) -> &BlockHash {
|
||||
self.hash.get_or_init(|| self.header.hash_slow())
|
||||
}
|
||||
|
||||
/// Returns a copy of the block hash.
|
||||
pub fn hash(&self) -> BlockHash {
|
||||
*self.hash_ref()
|
||||
}
|
||||
|
||||
/// This is the inverse of [`Header::seal_slow`] which returns the raw header and hash.
|
||||
pub fn split(self) -> (H, BlockHash) {
|
||||
let hash = self.hash();
|
||||
(self.header, hash)
|
||||
}
|
||||
|
||||
/// Clones the header and returns a new sealed header.
|
||||
pub fn cloned(self) -> Self
|
||||
where
|
||||
H: Clone,
|
||||
{
|
||||
let (header, hash) = self.split();
|
||||
Self::new(header, hash)
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: alloy_consensus::BlockHeader> SealedHeader<H> {
|
||||
impl<H: alloy_consensus::BlockHeader + Sealable> SealedHeader<H> {
|
||||
/// Return the number hash tuple.
|
||||
pub fn num_hash(&self) -> BlockNumHash {
|
||||
BlockNumHash::new(self.number(), self.hash)
|
||||
BlockNumHash::new(self.number(), self.hash())
|
||||
}
|
||||
|
||||
/// Return a [`BlockWithParent`] for this header.
|
||||
@ -80,6 +119,20 @@ impl<H: alloy_consensus::BlockHeader> SealedHeader<H> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: Sealable> Eq for SealedHeader<H> {}
|
||||
|
||||
impl<H: Sealable> PartialEq for SealedHeader<H> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.hash() == other.hash()
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: Sealable> core::hash::Hash for SealedHeader<H> {
|
||||
fn hash<Ha: core::hash::Hasher>(&self, state: &mut Ha) {
|
||||
self.hash().hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: InMemorySize> InMemorySize for SealedHeader<H> {
|
||||
/// Calculates a heuristic for the in-memory size of the [`SealedHeader`].
|
||||
#[inline]
|
||||
@ -90,7 +143,7 @@ impl<H: InMemorySize> InMemorySize for SealedHeader<H> {
|
||||
|
||||
impl<H: Sealable + Default> Default for SealedHeader<H> {
|
||||
fn default() -> Self {
|
||||
Self::seal(H::default())
|
||||
Self::seal_slow(H::default())
|
||||
}
|
||||
}
|
||||
|
||||
@ -115,13 +168,14 @@ impl Decodable for SealedHeader {
|
||||
// update original buffer
|
||||
*buf = *b;
|
||||
|
||||
Ok(Self { header, hash })
|
||||
Ok(Self::new(header, hash))
|
||||
}
|
||||
}
|
||||
|
||||
impl<H> From<SealedHeader<H>> for Sealed<H> {
|
||||
impl<H: Sealable> From<SealedHeader<H>> for Sealed<H> {
|
||||
fn from(value: SealedHeader<H>) -> Self {
|
||||
Self::new_unchecked(value.header, value.hash)
|
||||
let (header, hash) = value.split();
|
||||
Self::new_unchecked(header, hash)
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,7 +187,7 @@ where
|
||||
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
|
||||
let header = H::arbitrary(u)?;
|
||||
|
||||
Ok(Self::seal(header))
|
||||
Ok(Self::seal_slow(header))
|
||||
}
|
||||
}
|
||||
|
||||
@ -146,7 +200,7 @@ impl<H: crate::test_utils::TestHeader> SealedHeader<H> {
|
||||
|
||||
/// Updates the block hash.
|
||||
pub fn set_hash(&mut self, hash: BlockHash) {
|
||||
self.hash = hash
|
||||
self.hash = hash.into()
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the header.
|
||||
@ -178,12 +232,11 @@ impl<H: crate::test_utils::TestHeader> SealedHeader<H> {
|
||||
/// Bincode-compatible [`SealedHeader`] serde implementation.
|
||||
#[cfg(feature = "serde-bincode-compat")]
|
||||
pub(super) mod serde_bincode_compat {
|
||||
use alloy_primitives::BlockHash;
|
||||
use crate::serde_bincode_compat::SerdeBincodeCompat;
|
||||
use alloy_primitives::{BlockHash, Sealable};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use serde_with::{DeserializeAs, SerializeAs};
|
||||
|
||||
use crate::serde_bincode_compat::SerdeBincodeCompat;
|
||||
|
||||
/// Bincode-compatible [`super::SealedHeader`] serde implementation.
|
||||
///
|
||||
/// Intended to use with the [`serde_with::serde_as`] macro in the following way:
|
||||
@ -201,20 +254,22 @@ pub(super) mod serde_bincode_compat {
|
||||
/// ```
|
||||
#[derive(derive_more::Debug, Serialize, Deserialize)]
|
||||
#[debug(bound(H::BincodeRepr<'a>: core::fmt::Debug))]
|
||||
pub struct SealedHeader<'a, H: SerdeBincodeCompat = super::Header> {
|
||||
pub struct SealedHeader<'a, H: Sealable + SerdeBincodeCompat = super::Header> {
|
||||
hash: BlockHash,
|
||||
header: H::BincodeRepr<'a>,
|
||||
}
|
||||
|
||||
impl<'a, H: SerdeBincodeCompat> From<&'a super::SealedHeader<H>> for SealedHeader<'a, H> {
|
||||
impl<'a, H: Sealable + SerdeBincodeCompat> From<&'a super::SealedHeader<H>>
|
||||
for SealedHeader<'a, H>
|
||||
{
|
||||
fn from(value: &'a super::SealedHeader<H>) -> Self {
|
||||
Self { hash: value.hash, header: (&value.header).into() }
|
||||
Self { hash: value.hash(), header: (&value.header).into() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, H: SerdeBincodeCompat> From<SealedHeader<'a, H>> for super::SealedHeader<H> {
|
||||
impl<'a, H: Sealable + SerdeBincodeCompat> From<SealedHeader<'a, H>> for super::SealedHeader<H> {
|
||||
fn from(value: SealedHeader<'a, H>) -> Self {
|
||||
Self { hash: value.hash, header: value.header.into() }
|
||||
Self::new(value.header.into(), value.hash)
|
||||
}
|
||||
}
|
||||
|
||||
@ -236,9 +291,10 @@ pub(super) mod serde_bincode_compat {
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: SerdeBincodeCompat> SerdeBincodeCompat for super::SealedHeader<H> {
|
||||
impl<H: Sealable + SerdeBincodeCompat> SerdeBincodeCompat for super::SealedHeader<H> {
|
||||
type BincodeRepr<'a> = SealedHeader<'a, H>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::{serde_bincode_compat, SealedHeader};
|
||||
|
||||
@ -17,6 +17,32 @@
|
||||
//! - `secp256k1`: Adds secp256k1 support for transaction signing/recovery. (By default the no-std
|
||||
//! friendly `k256` is used)
|
||||
//! - `rayon`: Uses `rayon` for parallel transaction sender recovery in [`BlockBody`] by default.
|
||||
//!
|
||||
//! ## Overview
|
||||
//!
|
||||
//! This crate defines various traits and types that form the foundation of the reth stack.
|
||||
//! The top-level trait is [`Block`] which represents a block in the blockchain. A [`Block`] is
|
||||
//! composed of a [`Header`] and a [`BlockBody`]. A [`BlockBody`] contains the transactions in the
|
||||
//! block any additional data that is part of the block. A [`Header`] contains the metadata of the
|
||||
//! block.
|
||||
//!
|
||||
//! ### Sealing (Hashing)
|
||||
//!
|
||||
//! The block hash is derived from the [`Header`] and is used to uniquely identify the block. This
|
||||
//! operation is referred to as sealing in the context of this crate. Sealing is an expensive
|
||||
//! operation. This crate provides various wrapper types that cache the hash of the block to avoid
|
||||
//! recomputing it: [`SealedHeader`] and [`SealedBlock`]. All sealed types can be downgraded to
|
||||
//! their unsealed counterparts.
|
||||
//!
|
||||
//! ### Recovery
|
||||
//!
|
||||
//! The raw consensus transactions that make up a block don't include the sender's address. This
|
||||
//! information is recovered from the transaction signature. This operation is referred to as
|
||||
//! recovery in the context of this crate and is an expensive operation. The [`RecoveredBlock`]
|
||||
//! represents a [`SealedBlock`] with the sender addresses recovered. A [`SealedBlock`] can be
|
||||
//! upgraded to a [`RecoveredBlock`] by recovering the sender addresses:
|
||||
//! [`SealedBlock::try_recover`]. A [`RecoveredBlock`] can be downgraded to a [`SealedBlock`] by
|
||||
//! removing the sender addresses: [`RecoveredBlock::into_sealed_block`].
|
||||
|
||||
#![doc(
|
||||
html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png",
|
||||
@ -52,7 +78,7 @@ pub mod block;
|
||||
pub use block::{
|
||||
body::{BlockBody, FullBlockBody},
|
||||
header::{BlockHeader, FullBlockHeader},
|
||||
Block, FullBlock,
|
||||
Block, FullBlock, RecoveredBlock, SealedBlock,
|
||||
};
|
||||
|
||||
mod encoded;
|
||||
@ -138,6 +164,6 @@ impl<T> MaybeSerdeBincodeCompat for T {}
|
||||
#[cfg(any(test, feature = "arbitrary", feature = "test-utils"))]
|
||||
pub mod test_utils {
|
||||
pub use crate::header::test_utils::{generate_valid_header, valid_header_strategy};
|
||||
#[cfg(feature = "test-utils")]
|
||||
#[cfg(any(test, feature = "test-utils"))]
|
||||
pub use crate::{block::TestBlock, header::test_utils::TestHeader};
|
||||
}
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
use crate::{Block, FullBlock, FullBlockBody, FullBlockHeader, FullReceipt, FullSignedTx, Receipt};
|
||||
use crate::{
|
||||
Block, FullBlock, FullBlockBody, FullBlockHeader, FullReceipt, FullSignedTx,
|
||||
MaybeSerdeBincodeCompat, Receipt,
|
||||
};
|
||||
use core::fmt;
|
||||
|
||||
/// Configures all the primitive types of the node.
|
||||
@ -6,7 +9,7 @@ pub trait NodePrimitives:
|
||||
Send + Sync + Unpin + Clone + Default + fmt::Debug + PartialEq + Eq + 'static
|
||||
{
|
||||
/// Block primitive.
|
||||
type Block: Block<Header = Self::BlockHeader, Body = Self::BlockBody>;
|
||||
type Block: Block<Header = Self::BlockHeader, Body = Self::BlockBody> + MaybeSerdeBincodeCompat;
|
||||
/// Block header primitive.
|
||||
type BlockHeader: FullBlockHeader;
|
||||
/// Block body primitive.
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
use core::fmt::Debug;
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
|
||||
pub use super::header::{serde_bincode_compat as header, serde_bincode_compat::*};
|
||||
pub use block_bincode::BlockBody;
|
||||
pub use super::{
|
||||
block::{serde_bincode_compat as block, serde_bincode_compat::*},
|
||||
header::{serde_bincode_compat as header, serde_bincode_compat::*},
|
||||
};
|
||||
pub use block_bincode::{Block, BlockBody};
|
||||
|
||||
/// Trait for types that can be serialized and deserialized using bincode.
|
||||
pub trait SerdeBincodeCompat: Sized + 'static {
|
||||
@ -14,6 +17,9 @@ impl SerdeBincodeCompat for alloy_consensus::Header {
|
||||
type BincodeRepr<'a> = alloy_consensus::serde_bincode_compat::Header<'a>;
|
||||
}
|
||||
|
||||
/// Type alias for the [`SerdeBincodeCompat::BincodeRepr`] associated type.
|
||||
pub type BincodeReprFor<'a, T> = <T as SerdeBincodeCompat>::BincodeRepr<'a>;
|
||||
|
||||
mod block_bincode {
|
||||
use crate::serde_bincode_compat::SerdeBincodeCompat;
|
||||
use alloc::{borrow::Cow, vec::Vec};
|
||||
@ -22,6 +28,77 @@ mod block_bincode {
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use serde_with::{DeserializeAs, SerializeAs};
|
||||
|
||||
/// Bincode-compatible [`alloy_consensus::Block`] serde implementation.
|
||||
///
|
||||
/// Intended to use with the [`serde_with::serde_as`] macro in the following way:
|
||||
/// ```rust
|
||||
/// use alloy_consensus::Block;
|
||||
/// use reth_primitives_traits::serde_bincode_compat::{self, SerdeBincodeCompat};
|
||||
/// use serde::{Deserialize, Serialize};
|
||||
/// use serde_with::serde_as;
|
||||
///
|
||||
/// #[serde_as]
|
||||
/// #[derive(Serialize, Deserialize)]
|
||||
/// struct Data<T: SerdeBincodeCompat, H: SerdeBincodeCompat> {
|
||||
/// #[serde_as(as = "serde_bincode_compat::Block<'_, T, H>")]
|
||||
/// body: Block<T, H>,
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(derive_more::Debug, Serialize, Deserialize)]
|
||||
#[debug(bound())]
|
||||
pub struct Block<'a, T: SerdeBincodeCompat, H: SerdeBincodeCompat> {
|
||||
header: H::BincodeRepr<'a>,
|
||||
#[serde(bound = "BlockBody<'a, T>: Serialize + serde::de::DeserializeOwned")]
|
||||
body: BlockBody<'a, T>,
|
||||
}
|
||||
|
||||
impl<'a, T: SerdeBincodeCompat, H: SerdeBincodeCompat> From<&'a alloy_consensus::Block<T, H>>
|
||||
for Block<'a, T, H>
|
||||
{
|
||||
fn from(value: &'a alloy_consensus::Block<T, H>) -> Self {
|
||||
Self { header: (&value.header).into(), body: (&value.body).into() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: SerdeBincodeCompat, H: SerdeBincodeCompat> From<Block<'a, T, H>>
|
||||
for alloy_consensus::Block<T, H>
|
||||
{
|
||||
fn from(value: Block<'a, T, H>) -> Self {
|
||||
Self { header: value.header.into(), body: value.body.into() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: SerdeBincodeCompat, H: SerdeBincodeCompat> SerializeAs<alloy_consensus::Block<T, H>>
|
||||
for Block<'_, T, H>
|
||||
{
|
||||
fn serialize_as<S>(
|
||||
source: &alloy_consensus::Block<T, H>,
|
||||
serializer: S,
|
||||
) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
Block::from(source).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, T: SerdeBincodeCompat, H: SerdeBincodeCompat>
|
||||
DeserializeAs<'de, alloy_consensus::Block<T, H>> for Block<'de, T, H>
|
||||
{
|
||||
fn deserialize_as<D>(deserializer: D) -> Result<alloy_consensus::Block<T, H>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Block::deserialize(deserializer).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: SerdeBincodeCompat, H: SerdeBincodeCompat> SerdeBincodeCompat
|
||||
for alloy_consensus::Block<T, H>
|
||||
{
|
||||
type BincodeRepr<'a> = Block<'a, T, H>;
|
||||
}
|
||||
|
||||
/// Bincode-compatible [`alloy_consensus::BlockBody`] serde implementation.
|
||||
///
|
||||
/// Intended to use with the [`serde_with::serde_as`] macro in the following way:
|
||||
|
||||
@ -64,6 +64,13 @@ pub trait SignedTransaction:
|
||||
/// the signature has a low `s` value.
|
||||
fn recover_signer(&self) -> Option<Address>;
|
||||
|
||||
/// Recover signer from signature and hash.
|
||||
///
|
||||
/// Returns an error if the transaction's signature is invalid.
|
||||
fn try_recover(&self) -> Result<Address, RecoveryError> {
|
||||
self.recover_signer().ok_or(RecoveryError)
|
||||
}
|
||||
|
||||
/// Recover signer from signature and hash _without ensuring that the signature has a low `s`
|
||||
/// value_.
|
||||
///
|
||||
@ -73,6 +80,14 @@ pub trait SignedTransaction:
|
||||
self.recover_signer_unchecked_with_buf(&mut Vec::new())
|
||||
}
|
||||
|
||||
/// Recover signer from signature and hash _without ensuring that the signature has a low `s`
|
||||
/// value_.
|
||||
///
|
||||
/// Returns an error if the transaction's signature is invalid.
|
||||
fn try_recover_unchecked(&self) -> Result<Address, RecoveryError> {
|
||||
self.recover_signer_unchecked().ok_or(RecoveryError)
|
||||
}
|
||||
|
||||
/// Same as [`Self::recover_signer_unchecked`] but receives a buffer to operate on. This is used
|
||||
/// during batch recovery to avoid allocating a new buffer for each transaction.
|
||||
fn recover_signer_unchecked_with_buf(&self, buf: &mut Vec<u8>) -> Option<Address>;
|
||||
@ -195,3 +210,8 @@ pub trait SignedTransactionIntoRecoveredExt: SignedTransaction {
|
||||
}
|
||||
|
||||
impl<T> SignedTransactionIntoRecoveredExt for T where T: SignedTransaction {}
|
||||
|
||||
/// Opaque error type for sender recovery.
|
||||
#[derive(Debug, Default, thiserror::Error)]
|
||||
#[error("Failed to recover the signer")]
|
||||
pub struct RecoveryError;
|
||||
|
||||
Reference in New Issue
Block a user