|
|
|
|
@ -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] })
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|