feat: make more block types generic (#12812)

This commit is contained in:
Arsenii Kulikov
2024-11-25 14:50:10 +04:00
committed by GitHub
parent 02824da4fc
commit dcaa06a01a
62 changed files with 534 additions and 333 deletions

View File

@ -1,13 +1,16 @@
use crate::{GotExpected, SealedHeader, TransactionSigned, TransactionSignedEcRecovered};
use crate::{
traits::BlockExt, transaction::SignedTransactionIntoRecoveredExt, BlockBodyTxExt, GotExpected,
SealedHeader, TransactionSigned, TransactionSignedEcRecovered,
};
use alloc::vec::Vec;
use alloy_consensus::Header;
use alloy_eips::{eip2718::Encodable2718, eip4895::Withdrawals};
use alloy_primitives::{Address, Bytes, Sealable, B256};
use alloy_primitives::{Address, Bytes, B256};
use alloy_rlp::{Decodable, Encodable, RlpDecodable, RlpEncodable};
use derive_more::{Deref, DerefMut};
#[cfg(any(test, feature = "arbitrary"))]
pub use reth_primitives_traits::test_utils::{generate_valid_header, valid_header_strategy};
use reth_primitives_traits::InMemorySize;
use reth_primitives_traits::{BlockBody as _, InMemorySize, SignedTransaction};
use serde::{Deserialize, Serialize};
/// Ethereum full block.
@ -23,73 +26,14 @@ pub struct Block {
pub body: BlockBody,
}
impl Block {
/// Calculate the header hash and seal the block so that it can't be changed.
pub fn seal_slow(self) -> SealedBlock {
SealedBlock { header: SealedHeader::seal(self.header), body: self.body }
}
/// Seal the block with a known hash.
///
/// WARNING: This method does not perform validation whether the hash is correct.
pub fn seal(self, hash: B256) -> SealedBlock {
SealedBlock { header: SealedHeader::new(self.header, hash), body: self.body }
}
/// Expensive operation that recovers transaction signer. See [`SealedBlockWithSenders`].
pub fn senders(&self) -> Option<Vec<Address>> {
self.body.recover_signers()
}
/// Transform into a [`BlockWithSenders`].
///
/// # 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]
pub fn with_senders_unchecked(self, senders: Vec<Address>) -> BlockWithSenders {
self.try_with_senders_unchecked(senders).expect("stored block is valid")
}
/// Transform into a [`BlockWithSenders`] 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_.
/// See also [`TransactionSigned::recover_signer_unchecked`]
///
/// Returns an error if a signature is invalid.
#[track_caller]
pub fn try_with_senders_unchecked(
self,
senders: Vec<Address>,
) -> Result<BlockWithSenders, Self> {
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(BlockWithSenders::new_unchecked(self, senders))
}
/// **Expensive**. Transform into a [`BlockWithSenders`] by recovering senders in the contained
/// transactions.
///
/// Returns `None` if a transaction is invalid.
pub fn with_recovered_senders(self) -> Option<BlockWithSenders> {
let senders = self.senders()?;
Some(BlockWithSenders::new_unchecked(self, senders))
}
}
impl reth_primitives_traits::Block for Block {
type Header = Header;
type Body = BlockBody;
fn new(header: Self::Header, body: Self::Body) -> Self {
Self { header, body }
}
fn header(&self) -> &Self::Header {
&self.header
}
@ -97,6 +41,10 @@ impl reth_primitives_traits::Block for Block {
fn body(&self) -> &Self::Body {
&self.body
}
fn split(self) -> (Self::Header, Self::Body) {
(self.header, self.body)
}
}
impl InMemorySize for Block {
@ -204,44 +152,44 @@ impl<'a> arbitrary::Arbitrary<'a> for Block {
/// Sealed block with senders recovered from transactions.
#[derive(Debug, Clone, PartialEq, Eq, Default, Deref, DerefMut)]
pub struct BlockWithSenders {
pub struct BlockWithSenders<B = Block> {
/// Block
#[deref]
#[deref_mut]
pub block: Block,
pub block: B,
/// List of senders that match the transactions in the block
pub senders: Vec<Address>,
}
impl BlockWithSenders {
impl<B: reth_primitives_traits::Block> BlockWithSenders<B> {
/// New block with senders
pub const fn new_unchecked(block: Block, senders: Vec<Address>) -> Self {
pub const fn new_unchecked(block: B, senders: Vec<Address>) -> Self {
Self { block, senders }
}
/// New block with senders. Return none if len of tx and senders does not match
pub fn new(block: Block, senders: Vec<Address>) -> Option<Self> {
(block.body.transactions.len() == senders.len()).then_some(Self { block, senders })
pub fn new(block: B, senders: Vec<Address>) -> Option<Self> {
(block.body().transactions().len() == senders.len()).then_some(Self { block, senders })
}
/// Seal the block with a known hash.
///
/// WARNING: This method does not perform validation whether the hash is correct.
#[inline]
pub fn seal(self, hash: B256) -> SealedBlockWithSenders {
pub fn seal(self, hash: B256) -> SealedBlockWithSenders<B> {
let Self { block, senders } = self;
SealedBlockWithSenders { block: block.seal(hash), senders }
SealedBlockWithSenders::<B> { block: block.seal(hash), senders }
}
/// Calculate the header hash and seal the block with senders so that it can't be changed.
#[inline]
pub fn seal_slow(self) -> SealedBlockWithSenders {
pub fn seal_slow(self) -> SealedBlockWithSenders<B> {
SealedBlockWithSenders { block: self.block.seal_slow(), senders: self.senders }
}
/// Split Structure to its components
#[inline]
pub fn into_components(self) -> (Block, Vec<Address>) {
pub fn into_components(self) -> (B, Vec<Address>) {
(self.block, self.senders)
}
@ -249,18 +197,27 @@ impl BlockWithSenders {
#[inline]
pub fn transactions_with_sender(
&self,
) -> impl Iterator<Item = (&Address, &TransactionSigned)> + '_ {
self.senders.iter().zip(self.block.body.transactions())
) -> impl Iterator<Item = (&Address, &<B::Body as reth_primitives_traits::BlockBody>::Transaction)>
+ '_ {
self.senders.iter().zip(self.block.body().transactions())
}
/// Returns an iterator over all transactions in the chain.
#[inline]
pub fn into_transactions_ecrecovered(
self,
) -> impl Iterator<Item = TransactionSignedEcRecovered> {
) -> impl Iterator<
Item = TransactionSignedEcRecovered<
<B::Body as reth_primitives_traits::BlockBody>::Transaction,
>,
>
where
<B::Body as reth_primitives_traits::BlockBody>::Transaction: SignedTransaction,
{
self.block
.body
.transactions
.split()
.1
.into_transactions()
.into_iter()
.zip(self.senders)
.map(|(tx, sender)| tx.with_signer(sender))
@ -268,8 +225,10 @@ impl BlockWithSenders {
/// Consumes the block and returns the transactions of the block.
#[inline]
pub fn into_transactions(self) -> Vec<TransactionSigned> {
self.block.body.transactions
pub fn into_transactions(
self,
) -> Vec<<B::Body as reth_primitives_traits::BlockBody>::Transaction> {
self.block.split().1.into_transactions()
}
}
@ -308,10 +267,9 @@ impl<H, B> SealedBlock<H, B> {
}
impl SealedBlock {
/// Splits the sealed block into underlying components
#[inline]
pub fn split(self) -> (SealedHeader, Vec<TransactionSigned>, Vec<Header>) {
(self.header, self.body.transactions, self.body.ommers)
/// Unseal the block
pub fn unseal(self) -> Block {
Block { header: self.header.unseal(), body: self.body }
}
/// Returns an iterator over all blob transactions of the block
@ -320,82 +278,6 @@ impl SealedBlock {
self.body.blob_transactions_iter()
}
/// Returns only the blob transactions, if any, from the block body.
#[inline]
pub fn blob_transactions(&self) -> Vec<&TransactionSigned> {
self.blob_transactions_iter().collect()
}
/// 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.blob_transactions_iter()
.filter_map(|tx| tx.as_eip4844().map(|blob_tx| &blob_tx.blob_versioned_hashes))
.flatten()
}
/// Returns all blob versioned hashes from the block body.
#[inline]
pub fn blob_versioned_hashes(&self) -> Vec<&B256> {
self.blob_versioned_hashes_iter().collect()
}
/// Expensive operation that recovers transaction signer. See [`SealedBlockWithSenders`].
pub fn senders(&self) -> Option<Vec<Address>> {
self.body.recover_signers()
}
/// Seal sealed block with recovered transaction senders.
pub fn seal_with_senders(self) -> Option<SealedBlockWithSenders> {
self.try_seal_with_senders().ok()
}
/// Seal sealed block with recovered transaction senders.
pub fn try_seal_with_senders(self) -> Result<SealedBlockWithSenders, Self> {
match self.senders() {
Some(senders) => Ok(SealedBlockWithSenders { block: self, senders }),
None => Err(self),
}
}
/// Transform into a [`SealedBlockWithSenders`].
///
/// # 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.
#[track_caller]
pub fn with_senders_unchecked(self, senders: Vec<Address>) -> SealedBlockWithSenders {
self.try_with_senders_unchecked(senders).expect("stored block is valid")
}
/// Transform into a [`SealedBlockWithSenders`] 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_.
/// See also [`TransactionSigned::recover_signer_unchecked`]
///
/// Returns an error if a signature is invalid.
#[track_caller]
pub fn try_with_senders_unchecked(
self,
senders: Vec<Address>,
) -> Result<SealedBlockWithSenders, Self> {
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(SealedBlockWithSenders { block: self, senders })
}
/// Unseal the block
pub fn unseal(self) -> Block {
Block { header: self.header.unseal(), body: self.body }
}
/// Calculates the total gas used by blob transactions in the sealed block.
pub fn blob_gas_used(&self) -> u64 {
self.blob_transactions().iter().filter_map(|tx| tx.blob_gas_used()).sum()
@ -413,6 +295,102 @@ impl SealedBlock {
self.body.has_eip7702_transactions()
}
/// Returns only the blob transactions, if any, from the block body.
#[inline]
pub fn blob_transactions(&self) -> Vec<&TransactionSigned> {
self.blob_transactions_iter().collect()
}
/// 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.blob_transactions_iter()
.filter_map(|tx| tx.as_eip4844().map(|blob_tx| &blob_tx.blob_versioned_hashes))
.flatten()
}
}
impl<H, B> SealedBlock<H, B>
where
H: reth_primitives_traits::BlockHeader,
B: reth_primitives_traits::BlockBody,
{
/// Splits the sealed block into underlying components
#[inline]
pub fn split(self) -> (SealedHeader<H>, B) {
(self.header, self.body)
}
/// Expensive operation that recovers transaction signer. See [`SealedBlockWithSenders`].
pub fn senders(&self) -> Option<Vec<Address>>
where
B::Transaction: SignedTransaction,
{
self.body.recover_signers()
}
/// Seal sealed block with recovered transaction senders.
pub fn seal_with_senders<T>(self) -> Option<SealedBlockWithSenders<T>>
where
B::Transaction: SignedTransaction,
T: reth_primitives_traits::Block<Header = H, Body = B>,
{
self.try_seal_with_senders().ok()
}
/// Seal sealed block with recovered transaction senders.
pub fn try_seal_with_senders<T>(self) -> Result<SealedBlockWithSenders<T>, Self>
where
B::Transaction: SignedTransaction,
T: reth_primitives_traits::Block<Header = H, Body = B>,
{
match self.senders() {
Some(senders) => Ok(SealedBlockWithSenders { block: self, senders }),
None => Err(self),
}
}
/// Transform into a [`SealedBlockWithSenders`].
///
/// # 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.
#[track_caller]
pub fn with_senders_unchecked<T>(self, senders: Vec<Address>) -> SealedBlockWithSenders<T>
where
B::Transaction: SignedTransaction,
T: reth_primitives_traits::Block<Header = H, Body = B>,
{
self.try_with_senders_unchecked(senders).expect("stored block is valid")
}
/// Transform into a [`SealedBlockWithSenders`] 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_.
/// See also [`TransactionSigned::recover_signer_unchecked`]
///
/// Returns an error if a signature is invalid.
#[track_caller]
pub fn try_with_senders_unchecked<T>(
self,
senders: Vec<Address>,
) -> Result<SealedBlockWithSenders<T>, Self>
where
B::Transaction: SignedTransaction,
T: reth_primitives_traits::Block<Header = H, Body = B>,
{
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(SealedBlockWithSenders { block: self, senders })
}
/// 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
@ -425,13 +403,16 @@ impl SealedBlock {
///
/// 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>> {
pub fn ensure_transaction_root_valid(&self) -> Result<(), GotExpected<B256>>
where
B::Transaction: Encodable2718,
{
let calculated_root = self.body.calculate_tx_root();
if self.header.transactions_root != calculated_root {
if self.header.transactions_root() != calculated_root {
return Err(GotExpected {
got: calculated_root,
expected: self.header.transactions_root,
expected: self.header.transactions_root(),
})
}
@ -440,8 +421,11 @@ impl SealedBlock {
/// Returns a vector of transactions RLP encoded with
/// [`alloy_eips::eip2718::Encodable2718::encoded_2718`].
pub fn raw_transactions(&self) -> Vec<Bytes> {
self.body.transactions().map(|tx| tx.encoded_2718().into()).collect()
pub fn raw_transactions(&self) -> Vec<Bytes>
where
B::Transaction: Encodable2718,
{
self.body.transactions().iter().map(|tx| tx.encoded_2718().into()).collect()
}
}
@ -477,6 +461,10 @@ where
type Header = H;
type Body = B;
fn new(header: Self::Header, body: Self::Body) -> Self {
Self { header: SealedHeader::seal(header), body }
}
fn header(&self) -> &Self::Header {
self.header.header()
}
@ -484,6 +472,10 @@ where
fn body(&self) -> &Self::Body {
&self.body
}
fn split(self) -> (Self::Header, Self::Body) {
(self.header.unseal(), self.body)
}
}
#[cfg(any(test, feature = "arbitrary"))]
@ -499,45 +491,48 @@ where
/// Sealed block with senders recovered from transactions.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Deref, DerefMut)]
pub struct SealedBlockWithSenders<H = Header, B = BlockBody> {
pub struct SealedBlockWithSenders<B: reth_primitives_traits::Block = Block> {
/// Sealed block
#[deref]
#[deref_mut]
pub block: SealedBlock<H, B>,
#[serde(bound = "SealedBlock<B::Header, B::Body>: Serialize + serde::de::DeserializeOwned")]
pub block: SealedBlock<B::Header, B::Body>,
/// List of senders that match transactions from block.
pub senders: Vec<Address>,
}
impl<H: Default + Sealable, B: Default> Default for SealedBlockWithSenders<H, B> {
impl<B: reth_primitives_traits::Block> Default for SealedBlockWithSenders<B> {
fn default() -> Self {
Self { block: SealedBlock::default(), senders: Default::default() }
}
}
impl<H, B: reth_primitives_traits::BlockBody> SealedBlockWithSenders<H, B> {
impl<B: reth_primitives_traits::Block> SealedBlockWithSenders<B> {
/// New sealed block with sender. Return none if len of tx and senders does not match
pub fn new(block: SealedBlock<H, B>, senders: Vec<Address>) -> Option<Self> {
pub fn new(block: SealedBlock<B::Header, B::Body>, senders: Vec<Address>) -> Option<Self> {
(block.body.transactions().len() == senders.len()).then_some(Self { block, senders })
}
}
impl SealedBlockWithSenders {
impl<B: reth_primitives_traits::Block> SealedBlockWithSenders<B> {
/// Split Structure to its components
#[inline]
pub fn into_components(self) -> (SealedBlock, Vec<Address>) {
pub fn into_components(self) -> (SealedBlock<B::Header, B::Body>, Vec<Address>) {
(self.block, self.senders)
}
/// Returns the unsealed [`BlockWithSenders`]
#[inline]
pub fn unseal(self) -> BlockWithSenders {
let Self { block, senders } = self;
BlockWithSenders::new_unchecked(block.unseal(), senders)
pub fn unseal(self) -> BlockWithSenders<B> {
let (block, senders) = self.into_components();
let (header, body) = block.split();
let header = header.unseal();
BlockWithSenders::new_unchecked(B::new(header, body), senders)
}
/// Returns an iterator over all transactions in the block.
#[inline]
pub fn transactions(&self) -> impl Iterator<Item = &TransactionSigned> + '_ {
pub fn transactions(&self) -> &[<B::Body as reth_primitives_traits::BlockBody>::Transaction] {
self.block.body.transactions()
}
@ -545,24 +540,34 @@ impl SealedBlockWithSenders {
#[inline]
pub fn transactions_with_sender(
&self,
) -> impl Iterator<Item = (&Address, &TransactionSigned)> + '_ {
) -> impl Iterator<Item = (&Address, &<B::Body as reth_primitives_traits::BlockBody>::Transaction)>
+ '_ {
self.senders.iter().zip(self.block.body.transactions())
}
/// Consumes the block and returns the transactions of the block.
#[inline]
pub fn into_transactions(self) -> Vec<TransactionSigned> {
self.block.body.transactions
pub fn into_transactions(
self,
) -> Vec<<B::Body as reth_primitives_traits::BlockBody>::Transaction> {
self.block.body.into_transactions()
}
/// Returns an iterator over all transactions in the chain.
#[inline]
pub fn into_transactions_ecrecovered(
self,
) -> impl Iterator<Item = TransactionSignedEcRecovered> {
) -> impl Iterator<
Item = TransactionSignedEcRecovered<
<B::Body as reth_primitives_traits::BlockBody>::Transaction,
>,
>
where
<B::Body as reth_primitives_traits::BlockBody>::Transaction: SignedTransaction,
{
self.block
.body
.transactions
.into_transactions()
.into_iter()
.zip(self.senders)
.map(|(tx, sender)| tx.with_signer(sender))
@ -608,11 +613,6 @@ impl BlockBody {
Block { header, body: self }
}
/// Calculate the transaction root for the block body.
pub fn calculate_tx_root(&self) -> B256 {
crate::proofs::calculate_transaction_root(&self.transactions)
}
/// Calculate the ommers root for the block body.
pub fn calculate_ommers_root(&self) -> B256 {
crate::proofs::calculate_ommers_root(&self.ommers)
@ -624,20 +624,6 @@ impl BlockBody {
self.withdrawals.as_ref().map(|w| crate::proofs::calculate_withdrawals_root(w))
}
/// Recover signer addresses for all transactions in the block body.
pub fn recover_signers(&self) -> Option<Vec<Address>> {
TransactionSigned::recover_signers(&self.transactions, self.transactions.len())
}
/// Recover signer addresses for all transactions in the block body _without ensuring that the
/// signature has a low `s` value_.
///
/// Returns `None`, if some transaction's signature is invalid, see also
/// [`TransactionSigned::recover_signer_unchecked`].
pub fn recover_signers_unchecked(&self) -> Option<Vec<Address>> {
TransactionSigned::recover_signers_unchecked(&self.transactions, self.transactions.len())
}
/// Returns whether or not the block body contains any blob transactions.
#[inline]
pub fn has_blob_transactions(&self) -> bool {
@ -703,6 +689,10 @@ impl reth_primitives_traits::BlockBody for BlockBody {
fn transactions(&self) -> &[Self::Transaction] {
&self.transactions
}
fn into_transactions(self) -> Vec<Self::Transaction> {
self.transactions
}
}
impl From<Block> for BlockBody {
@ -1168,9 +1158,9 @@ mod tests {
Some(BlockWithSenders { block: block.clone(), senders: vec![sender] })
);
let sealed = block.seal_slow();
assert_eq!(SealedBlockWithSenders::new(sealed.clone(), vec![]), None);
assert_eq!(SealedBlockWithSenders::<Block>::new(sealed.clone(), vec![]), None);
assert_eq!(
SealedBlockWithSenders::new(sealed.clone(), vec![sender]),
SealedBlockWithSenders::<Block>::new(sealed.clone(), vec![sender]),
Some(SealedBlockWithSenders { block: sealed, senders: vec![sender] })
);
}

View File

@ -21,6 +21,9 @@
extern crate alloc;
mod traits;
pub use traits::*;
#[cfg(feature = "alloy-compat")]
mod alloy_compat;
mod block;

View File

@ -1,7 +1,7 @@
//! Helper function for calculating Merkle proofs and hashes.
use crate::{Receipt, ReceiptWithBloom, ReceiptWithBloomRef, TransactionSigned};
use alloc::vec::Vec;
use crate::{Receipt, ReceiptWithBloom, ReceiptWithBloomRef};
use alloc::{borrow::Borrow, vec::Vec};
use alloy_consensus::{Header, EMPTY_OMMER_ROOT_HASH};
use alloy_eips::{eip2718::Encodable2718, eip4895::Withdrawal};
use alloy_primitives::{keccak256, B256};
@ -12,9 +12,9 @@ use alloy_trie::root::{ordered_trie_root, ordered_trie_root_with_encoder};
/// `(rlp(index), encoded(tx))` pairs.
pub fn calculate_transaction_root<T>(transactions: &[T]) -> B256
where
T: AsRef<TransactionSigned>,
T: Encodable2718,
{
ordered_trie_root_with_encoder(transactions, |tx: &T, buf| tx.as_ref().encode_2718(buf))
ordered_trie_root_with_encoder(transactions, |tx, buf| tx.borrow().encode_2718(buf))
}
/// Calculates the root hash of the withdrawals.

View File

@ -0,0 +1,123 @@
use crate::{
transaction::{recover_signers, recover_signers_unchecked},
BlockWithSenders, SealedBlock,
};
use alloc::vec::Vec;
use alloy_eips::eip2718::Encodable2718;
use reth_primitives_traits::{Block, BlockBody, SealedHeader, SignedTransaction};
use revm_primitives::{Address, B256};
/// Extension trait for [`reth_primitives_traits::Block`] implementations
/// allowing for conversions into common block parts containers such as [`SealedBlock`],
/// [`BlockWithSenders`], etc.
pub trait BlockExt: Block {
/// Calculate the header hash and seal the block so that it can't be changed.
fn seal_slow(self) -> SealedBlock<Self::Header, Self::Body> {
let (header, body) = self.split();
SealedBlock { header: SealedHeader::seal(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::Header, Self::Body> {
let (header, body) = self.split();
SealedBlock { header: SealedHeader::new(header, hash), body }
}
/// 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 [`BlockWithSenders`].
///
/// # 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>) -> BlockWithSenders<Self>
where
<Self::Body as BlockBody>::Transaction: SignedTransaction,
{
self.try_with_senders_unchecked(senders).expect("stored block is valid")
}
/// Transform into a [`BlockWithSenders`] 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_.
/// See also [`recover_signers_unchecked`]
///
/// Returns an error if a signature is invalid.
#[track_caller]
fn try_with_senders_unchecked(
self,
senders: Vec<Address>,
) -> Result<BlockWithSenders<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(BlockWithSenders::new_unchecked(self, senders))
}
/// **Expensive**. Transform into a [`BlockWithSenders`] by recovering senders in the contained
/// transactions.
///
/// Returns `None` if a transaction is invalid.
fn with_recovered_senders(self) -> Option<BlockWithSenders<Self>>
where
<Self::Body as BlockBody>::Transaction: SignedTransaction,
{
let senders = self.senders()?;
Some(BlockWithSenders::new_unchecked(self, senders))
}
}
impl<T: Block> BlockExt for T {}
/// Extension trait for [`BlockBody`] adding helper methods operating with transactions.
pub trait BlockBodyTxExt: BlockBody {
/// Calculate the transaction root for the block body.
fn calculate_tx_root(&self) -> B256
where
Self::Transaction: Encodable2718,
{
crate::proofs::calculate_transaction_root(self.transactions())
}
/// Recover signer addresses for all transactions in the block body.
fn recover_signers(&self) -> Option<Vec<Address>>
where
Self::Transaction: SignedTransaction,
{
recover_signers(self.transactions(), self.transactions().len())
}
/// Recover signer addresses for all transactions in the block body _without ensuring that the
/// signature has a low `s` value_.
///
/// Returns `None`, if some transaction's signature is invalid, see also
/// [`recover_signers_unchecked`].
fn recover_signers_unchecked(&self) -> Option<Vec<Address>>
where
Self::Transaction: SignedTransaction,
{
recover_signers_unchecked(self.transactions(), self.transactions().len())
}
}
impl<T: BlockBody> BlockBodyTxExt for T {}

View File

@ -1,9 +0,0 @@
//! Abstractions of primitive data types
pub mod block;
pub mod transaction;
pub use block::{body::BlockBody, Block};
pub use transaction::signed::SignedTransaction;
pub use alloy_consensus::BlockHeader;

View File

@ -1712,6 +1712,11 @@ pub trait SignedTransactionIntoRecoveredExt: SignedTransaction {
let signer = self.recover_signer_unchecked()?;
Some(TransactionSignedEcRecovered::from_signed_transaction(self, signer))
}
/// Returns the [`TransactionSignedEcRecovered`] transaction with the given sender.
fn with_signer(self, signer: Address) -> TransactionSignedEcRecovered<Self> {
TransactionSignedEcRecovered::from_signed_transaction(self, signer)
}
}
impl<T> SignedTransactionIntoRecoveredExt for T where T: SignedTransaction {}
@ -1936,6 +1941,22 @@ where
}
}
/// Recovers a list of signers from a transaction list iterator _without ensuring that the
/// signature has a low `s` value_.
///
/// Returns `None`, if some transaction's signature is invalid.
pub fn recover_signers_unchecked<'a, I, T>(txes: I, num_txes: usize) -> Option<Vec<Address>>
where
T: SignedTransaction,
I: IntoParallelIterator<Item = &'a T> + IntoIterator<Item = &'a T> + Send,
{
if num_txes < *PARALLEL_SENDER_RECOVERY_THRESHOLD {
txes.into_iter().map(|tx| tx.recover_signer_unchecked()).collect()
} else {
txes.into_par_iter().map(|tx| tx.recover_signer_unchecked()).collect()
}
}
#[cfg(test)]
mod tests {
use crate::{