Skip to content

chore(sdk): improve usability BlockBody and Block traits #12450

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 15 additions & 8 deletions crates/primitives-traits/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ homepage.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true
description = "Common types in reth."
description = "Reth abstraction of EVM data primitives."

[lints]
workspace = true
Expand All @@ -23,19 +23,25 @@ alloy-primitives.workspace = true
alloy-rlp.workspace = true
revm-primitives.workspace = true

# misc
# async
once_cell.workspace = true

# io
rayon.workspace = true

# codec
byteorder = { workspace = true, optional = true }
bytes.workspace = true
derive_more.workspace = true
roaring = "0.10.2"
serde_with = { workspace = true, optional = true }
auto_impl.workspace = true

# required by reth-codecs
modular-bitfield = { workspace = true, optional = true }
serde = { workspace = true, optional = true}

# arbitrary utils
# misc
derive_more.workspace = true
auto_impl.workspace = true

# test-utils
arbitrary = { workspace = true, features = ["derive"], optional = true }
proptest = { workspace = true, optional = true }
proptest-arbitrary-interop = { workspace = true, optional = true }
Expand All @@ -61,7 +67,8 @@ std = [
"alloy-genesis/std",
"alloy-primitives/std",
"revm-primitives/std",
"serde?/std"
"serde?/std",
"once_cell/std",
]
test-utils = [
"arbitrary",
Expand Down
208 changes: 200 additions & 8 deletions crates/primitives-traits/src/block/body.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,36 @@
//! Block body abstraction.

use alloc::fmt;
use alloc::vec::Vec;
use core::fmt;
#[cfg(feature = "std")]
use std::sync::LazyLock;

use alloy_consensus::Transaction;
use alloy_eips::eip4895::Withdrawal;
use alloy_primitives::{Address, B256};
use once_cell as _;
#[cfg(not(feature = "std"))]
use once_cell::sync::Lazy as LazyLock;
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};

use crate::{FullSignedTx, InMemorySize, MaybeArbitrary, MaybeSerde};
use crate::{
Block, FullSignedTx, InMemorySize, MaybeArbitrary, MaybeSerde, SignedTransaction, TxType,
};

/// Helper trait that unifies all behaviour required by transaction to support full node operations.
/// Expected number of transactions where we can expect a speed-up by recovering the senders in
/// parallel.
pub static PARALLEL_SENDER_RECOVERY_THRESHOLD: LazyLock<usize> =
LazyLock::new(|| match rayon::current_num_threads() {
0..=1 => usize::MAX,
2..=8 => 10,
_ => 5,
});

/// Helper trait that unifies all behaviour required by block to support full node operations.
pub trait FullBlockBody: BlockBody<Transaction: FullSignedTx> {}

impl<T> FullBlockBody for T where T: BlockBody<Transaction: FullSignedTx> {}

/// Abstraction for block's body.
/// Abstraction of block's body.
#[auto_impl::auto_impl(&, Arc)]
pub trait BlockBody:
Send
Expand All @@ -28,9 +47,182 @@ pub trait BlockBody:
+ MaybeSerde
+ MaybeArbitrary
{
/// Ordered list of signed transactions as committed in block.
type Transaction: Transaction;
/// Signed transaction.
type Transaction: SignedTransaction + 'static;

// todo
// /// Header type (uncle blocks).
// type Header: BlockHeader + 'static;

/// Withdrawals in block.
type Withdrawals: IntoIterator<Item = Withdrawal> + 'static;

/// Returns reference to transactions in block.
fn transactions(&self) -> &[Self::Transaction];
fn transactions(&self) -> &Vec<Self::Transaction>;

// todo
// /// Returns reference to uncle block headers.
// fn ommers(&self) -> &[Self::Header];

/// Returns `Withdrawals` in the block, if any.
// todo: branch out into extension trait
fn withdrawals(&self) -> Option<&Self::Withdrawals>;

/// Calculate the transaction root for the block body.
fn calculate_tx_root(&self) -> B256;

/// Calculate the ommers root for the block body.
fn calculate_ommers_root(&self) -> B256;

/// Calculate the withdrawals root for the block body, if withdrawals exist. If there are no
/// withdrawals, this will return `None`.
// todo: can be default impl if `calculate_withdrawals_root` made into a method on
// `Withdrawals` and `Withdrawals` moved to alloy
fn calculate_withdrawals_root(&self) -> Option<B256>;

/// Recover signer addresses for all transactions in the block body.
fn recover_signers(&self) -> Option<Vec<Address>> {
if self.transactions().len() < *PARALLEL_SENDER_RECOVERY_THRESHOLD {
self.transactions().iter().map(|tx| tx.recover_signer()).collect()
} else {
self.transactions().par_iter().map(|tx| tx.recover_signer()).collect()
}
}

/// Returns whether or not the block body contains any blob transactions.
fn has_blob_transactions(&self) -> bool {
self.transactions().iter().any(|tx| tx.tx_type().is_eip4844())
}

/// Returns whether or not the block body contains any EIP-7702 transactions.
fn has_eip7702_transactions(&self) -> bool {
self.transactions().iter().any(|tx| tx.tx_type().is_eip7702())
}

/// Returns an iterator over all blob transactions of the block
fn blob_transactions_iter(&self) -> impl Iterator<Item = &Self::Transaction> {
self.transactions().iter().filter(|tx| tx.tx_type().is_eip4844())
}

/// Returns only the blob transactions, if any, from the block body.
fn blob_transactions(&self) -> Vec<&Self::Transaction> {
self.blob_transactions_iter().collect()
}

/// Returns references to all blob versioned hashes from the block body.
fn blob_versioned_hashes(&self) -> Vec<&B256>;

/// Returns all blob versioned hashes from the block body.
fn blob_versioned_hashes_copied(&self) -> Vec<B256>;
}

/// Helper trait to implement [`BlockBody`] functionality for [`Block`](crate::Block) types.
pub trait Body<Header: 'static, SignedTx: SignedTransaction + 'static, Withdrawals: 'static> {
/// See [`BlockBody`].
fn transactions(&self) -> &[SignedTx];

/// See [`BlockBody`].
fn withdrawals(&self) -> Option<&Withdrawals>;

// todo
// /// See [`BlockBody`].
//fn ommers(&self) -> &[Header];

/// See [`BlockBody`].
fn calculate_tx_root(&self) -> B256;

/// See [`BlockBody`].
fn calculate_ommers_root(&self) -> B256;

/// See [`BlockBody`].
fn calculate_withdrawals_root(&self) -> Option<B256>;

/// See [`BlockBody`].
fn recover_signers(&self) -> Option<Vec<Address>> {
if self.transactions().len() < *PARALLEL_SENDER_RECOVERY_THRESHOLD {
self.transactions().iter().map(|tx| tx.recover_signer()).collect()
} else {
self.transactions().par_iter().map(|tx| tx.recover_signer()).collect()
}
}

/// See [`BlockBody`].
fn has_blob_transactions(&self) -> bool {
self.transactions().iter().any(|tx| tx.tx_type().is_eip4844())
}

/// See [`BlockBody`].
fn has_eip7702_transactions(&self) -> bool {
self.transactions().iter().any(|tx| tx.tx_type().is_eip7702())
}

/// See [`BlockBody`].
fn blob_transactions_iter<'a>(&'a self) -> impl Iterator<Item = &'a SignedTx> + 'a
where
SignedTx: 'a,
{
self.transactions().iter().filter(|tx| tx.tx_type().is_eip4844())
}

/// See [`BlockBody`].
fn blob_transactions(&self) -> Vec<&SignedTx> {
self.blob_transactions_iter().collect()
}

/// See [`BlockBody`].
fn blob_versioned_hashes(&self) -> Vec<&B256>;

/// See [`BlockBody`].
fn blob_versioned_hashes_copied(&self) -> Vec<B256>;
}

impl<T: Block>
Body<T::Header, <T::Body as BlockBody>::Transaction, <T::Body as BlockBody>::Withdrawals>
for T
{
#[inline]
fn transactions(&self) -> &[<T::Body as BlockBody>::Transaction] {
self.body().transactions()
}

#[inline]
fn withdrawals(&self) -> Option<&<T::Body as BlockBody>::Withdrawals> {
self.body().withdrawals()
}

// todo
//#[inline]
// fn ommers(&self) -> &[T::Header] {
// self.body().ommers()
//}

#[inline]
fn calculate_tx_root(&self) -> B256 {
self.body().calculate_tx_root()
}

#[inline]
fn calculate_ommers_root(&self) -> B256 {
self.body().calculate_ommers_root()
}

#[inline]
fn calculate_withdrawals_root(&self) -> Option<B256> {
self.body().calculate_withdrawals_root()
}

#[inline]
fn recover_signers(&self) -> Option<Vec<Address>> {
self.body().recover_signers()
}

#[inline]
fn blob_versioned_hashes(&self) -> Vec<&B256> {
self.body().blob_versioned_hashes()
}

#[inline]
fn blob_versioned_hashes_copied(&self) -> Vec<B256> {
self.body().blob_versioned_hashes_copied()
}
}
7 changes: 4 additions & 3 deletions crates/primitives-traits/src/block/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
pub mod body;
pub mod header;

use alloc::fmt;
use core::fmt;

use crate::{
BlockHeader, FullBlockBody, FullBlockHeader, InMemorySize, MaybeArbitrary, MaybeSerde,
BlockBody, BlockHeader, FullBlockBody, FullBlockHeader, InMemorySize, MaybeArbitrary,
MaybeSerde,
};

/// Helper trait that unifies all behaviour required by block to support full node operations.
Expand Down Expand Up @@ -44,7 +45,7 @@ pub trait Block:
type Header: BlockHeader + 'static;

/// The block's body contains the transactions in the block.
type Body: Send + Sync + Unpin + 'static;
type Body: BlockBody + 'static;

/// Returns reference to block header.
fn header(&self) -> &Self::Header;
Expand Down
7 changes: 6 additions & 1 deletion crates/primitives-traits/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
#[macro_use]
extern crate alloc;

/// Helper traits for calling block header and body methods directly on block type.
pub mod block_prelude {
pub use crate::{block::body::Body as _, Block};
}

/// Common constants.
pub mod constants;
pub use constants::gas_units::{format_gas, format_gas_throughput};
Expand All @@ -36,7 +41,7 @@ pub use integer_list::{IntegerList, IntegerListError};

pub mod block;
pub use block::{
body::{BlockBody, FullBlockBody},
body::{BlockBody, FullBlockBody, PARALLEL_SENDER_RECOVERY_THRESHOLD},
header::{BlockHeader, FullBlockHeader},
Block, FullBlock,
};
Expand Down
35 changes: 17 additions & 18 deletions crates/primitives-traits/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,24 +77,23 @@ impl NodePrimitives for () {
}

/// Helper trait that sets trait bounds on [`NodePrimitives`].
pub trait FullNodePrimitives
where
Self: NodePrimitives<
Block: FullBlock<Header = Self::BlockHeader, Body = Self::BlockBody>,
BlockHeader: FullBlockHeader,
BlockBody: FullBlockBody<Transaction = Self::SignedTx>,
SignedTx: FullSignedTx,
TxType: FullTxType,
Receipt: FullReceipt,
> + Send
+ Sync
+ Unpin
+ Clone
+ Default
+ fmt::Debug
+ PartialEq
+ Eq
+ 'static,
pub trait FullNodePrimitives:
NodePrimitives<
Block: FullBlock<Header = Self::BlockHeader, Body = Self::BlockBody>,
BlockHeader: FullBlockHeader,
BlockBody: FullBlockBody<Transaction = Self::SignedTx>,
SignedTx: FullSignedTx,
TxType: FullTxType,
Receipt: FullReceipt,
> + Send
+ Sync
+ Unpin
+ Clone
+ Default
+ fmt::Debug
+ PartialEq
+ Eq
+ 'static
{
}

Expand Down
Loading
Loading