diff --git a/Cargo.lock b/Cargo.lock index 650dc4607d9f..61eda1058120 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -182,9 +182,9 @@ dependencies = [ [[package]] name = "alloy-eip7702" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69fb9fd842fdf10a524bbf2c4de6942ad869c1c8c3d128a1b09e67ed5f7cedbd" +checksum = "5f6cee6a35793f3db8a5ffe60e86c695f321d081a567211245f503e8c498fce8" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -776,9 +776,9 @@ dependencies = [ [[package]] name = "alloy-trie" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40d8e28db02c006f7abb20f345ffb3cc99c465e36f676ba262534e654ae76042" +checksum = "b6b2e366c0debf0af77766c23694a3f863b02633050e71e096e257ffbd395e50" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -1525,9 +1525,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" +checksum = "1a68f1f47cdf0ec8ee4b941b2eee2a80cb796db73118c0dd09ac63fbe405be22" dependencies = [ "memchr", "regex-automata 0.4.9", @@ -1651,9 +1651,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aeb932158bd710538c73702db6945cb68a8fb08c519e6e12706b94263b36db8" +checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" dependencies = [ "jobserver", "libc", @@ -1752,9 +1752,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.20" +version = "4.5.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" +checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" dependencies = [ "clap_builder", "clap_derive", @@ -1762,9 +1762,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.20" +version = "4.5.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" +checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" dependencies = [ "anstream", "anstyle", @@ -1786,9 +1786,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" [[package]] name = "coins-bip32" @@ -1859,14 +1859,14 @@ dependencies = [ [[package]] name = "comfy-table" -version = "7.1.1" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b34115915337defe99b2aff5c2ce6771e5fbc4079f4b506301f5cf394c8452f7" +checksum = "24f165e7b643266ea80cb858aed492ad9280e3e05ce24d4a99d7d7b889b6a4d9" dependencies = [ - "crossterm 0.27.0", + "crossterm", "strum", "strum_macros", - "unicode-width", + "unicode-width 0.2.0", ] [[package]] @@ -2107,19 +2107,6 @@ version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" -[[package]] -name = "crossterm" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" -dependencies = [ - "bitflags 2.6.0", - "crossterm_winapi", - "libc", - "parking_lot", - "winapi", -] - [[package]] name = "crossterm" version = "0.28.1" @@ -3199,9 +3186,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.34" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", "miniz_oxide", @@ -6166,7 +6153,7 @@ dependencies = [ "bitflags 2.6.0", "cassowary", "compact_str", - "crossterm 0.28.1", + "crossterm", "instability", "itertools 0.13.0", "lru", @@ -6175,7 +6162,7 @@ dependencies = [ "strum_macros", "unicode-segmentation", "unicode-truncate", - "unicode-width", + "unicode-width 0.1.14", ] [[package]] @@ -6664,7 +6651,7 @@ dependencies = [ "backon", "clap", "comfy-table", - "crossterm 0.28.1", + "crossterm", "eyre", "fdlimit", "futures", @@ -8553,9 +8540,11 @@ dependencies = [ "bytes", "derive_more 1.0.0", "modular-bitfield", + "once_cell", "proptest", "proptest-arbitrary-interop", "rand 0.8.5", + "rayon", "reth-codecs", "revm-primitives", "roaring", @@ -9865,9 +9854,9 @@ dependencies = [ [[package]] name = "scc" -version = "2.2.4" +version = "2.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8d25269dd3a12467afe2e510f69fb0b46b698e5afb296b59f2145259deaf8e8" +checksum = "66b202022bb57c049555430e11fc22fea12909276a80a4c3d368da36ac1d88ed" dependencies = [ "sdd", ] @@ -11311,7 +11300,7 @@ checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" dependencies = [ "itertools 0.13.0", "unicode-segmentation", - "unicode-width", + "unicode-width 0.1.14", ] [[package]] @@ -11320,6 +11309,12 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + [[package]] name = "unicode-xid" version = "0.2.6" diff --git a/crates/e2e-test-utils/src/engine_api.rs b/crates/e2e-test-utils/src/engine_api.rs index cfa245e1de01..3b48ceb03195 100644 --- a/crates/e2e-test-utils/src/engine_api.rs +++ b/crates/e2e-test-utils/src/engine_api.rs @@ -55,8 +55,7 @@ impl EngineApiTestContext + PayloadEnvelopeExt, E::ExecutionPayloadEnvelopeV4: From + PayloadEnvelopeExt, { - let versioned_hashes = - payload.block().blob_versioned_hashes_iter().copied().collect::>(); + let versioned_hashes = payload.block().blob_versioned_hashes_copied(); // submit payload to engine api let submission = if self .chain_spec diff --git a/crates/net/eth-wire-types/src/primitives.rs b/crates/net/eth-wire-types/src/primitives.rs index eab36c3b6a7c..711c92d73cd8 100644 --- a/crates/net/eth-wire-types/src/primitives.rs +++ b/crates/net/eth-wire-types/src/primitives.rs @@ -3,7 +3,7 @@ use std::fmt::Debug; use alloy_rlp::{Decodable, Encodable}; -use reth_primitives_traits::{Block, BlockHeader}; +use reth_primitives_traits::{Block, BlockBody, BlockHeader}; /// Abstraction over primitive types which might appear in network messages. See /// [`crate::EthMessage`] for more context. @@ -23,7 +23,8 @@ pub trait NetworkPrimitives: + Eq + 'static; /// The block body type. - type BlockBody: Encodable + type BlockBody: BlockBody
+ + Encodable + Decodable + Send + Sync diff --git a/crates/optimism/primitives/Cargo.toml b/crates/optimism/primitives/Cargo.toml index 216e559a201e..1e0a202db775 100644 --- a/crates/optimism/primitives/Cargo.toml +++ b/crates/optimism/primitives/Cargo.toml @@ -12,21 +12,33 @@ description = "OP primitive types" workspace = true [dependencies] +# reth +reth-primitives-traits.workspace = true +reth-codecs = { workspace = true, optional = true } +reth-primitives = { workspace = true, features = ["reth-codec"], optional = true } + +# ethereum alloy-primitives.workspace = true alloy-consensus.workspace = true -op-alloy-consensus.workspace = true alloy-eips.workspace = true alloy-rlp.workspace = true -derive_more.workspace = true + +# op +op-alloy-consensus.workspace = true + +# codec bytes.workspace = true -reth-primitives-traits.workspace = true -reth-codecs = { workspace = true, optional = true } -reth-primitives = { workspace = true, features = ["reth-codec"], optional = true } -[features] -default = ["reth-codec"] -reth-codec = ["dep:reth-codecs", "dep:reth-primitives"] +# misc +derive_more.workspace = true [dev-dependencies] reth-codecs = { workspace = true, features = ["test-utils"] } -rstest.workspace = true \ No newline at end of file +rstest.workspace = true + +[features] +default = ["reth-codec"] +reth-codec = [ + "dep:reth-codecs", + "dep:reth-primitives", +] diff --git a/crates/payload/validator/src/lib.rs b/crates/payload/validator/src/lib.rs index e74b5f48d40f..ea7fb29f9cd2 100644 --- a/crates/payload/validator/src/lib.rs +++ b/crates/payload/validator/src/lib.rs @@ -64,7 +64,8 @@ impl ExecutionPayloadValidator { sealed_block: &SealedBlock, cancun_fields: &MaybeCancunPayloadFields, ) -> Result<(), PayloadError> { - let num_blob_versioned_hashes = sealed_block.blob_versioned_hashes_iter().count(); + let blob_versioned_hashes = sealed_block.blob_versioned_hashes(); + let num_blob_versioned_hashes = blob_versioned_hashes.len(); // Additional Cancun checks for blob transactions if let Some(versioned_hashes) = cancun_fields.versioned_hashes() { if num_blob_versioned_hashes != versioned_hashes.len() { @@ -73,9 +74,9 @@ impl ExecutionPayloadValidator { } // we can use `zip` safely here because we already compared their length for (payload_versioned_hash, block_versioned_hash) in - versioned_hashes.iter().zip(sealed_block.blob_versioned_hashes_iter()) + versioned_hashes.iter().zip(blob_versioned_hashes.iter()) { - if payload_versioned_hash != block_versioned_hash { + if payload_versioned_hash != *block_versioned_hash { return Err(PayloadError::InvalidVersionedHashes) } } diff --git a/crates/primitives-traits/Cargo.toml b/crates/primitives-traits/Cargo.toml index 30f1c43c86a9..16339eb46114 100644 --- a/crates/primitives-traits/Cargo.toml +++ b/crates/primitives-traits/Cargo.toml @@ -14,24 +14,28 @@ workspace = true [dependencies] reth-codecs.workspace = true +# ethereum alloy-consensus = { workspace = true, features = ["serde"] } alloy-eips.workspace = true alloy-genesis.workspace = true alloy-primitives.workspace = true alloy-rlp.workspace = true - revm-primitives = { workspace = true, features = ["serde"] } -# misc +# io +rayon.workspace = true + +# codec byteorder = "1" -derive_more.workspace = true roaring = "0.10.2" +serde.workspace = true serde_with = { workspace = true, optional = true } - -# required by reth-codecs bytes.workspace = true modular-bitfield.workspace = true -serde.workspace = true + +# misc +derive_more.workspace = true +once_cell.workspace = true # arbitrary utils arbitrary = { workspace = true, features = ["derive"], optional = true } @@ -57,7 +61,8 @@ std = [ "alloy-genesis/std", "alloy-primitives/std", "revm-primitives/std", - "serde/std" + "serde/std", + "once_cell/std", ] test-utils = [ "arbitrary", diff --git a/crates/primitives-traits/src/block/body.rs b/crates/primitives-traits/src/block/body.rs index bb52b89724b5..da137a4c018a 100644 --- a/crates/primitives-traits/src/block/body.rs +++ b/crates/primitives-traits/src/block/body.rs @@ -1,10 +1,28 @@ //! Block body abstraction. -use crate::InMemorySize; -use alloc::fmt; -use alloy_consensus::Transaction; +use alloc::{fmt, vec::Vec}; -/// Abstraction for block's body. +use alloy_eips::{eip4895::Withdrawal, eip7685::Requests}; +use alloy_primitives::{Address, B256}; +use reth_codecs::Compact; + +use crate::{ + BlockHeader, FullBlockHeader, FullSignedTx, InMemorySize, SignedTransaction, TransactionExt, + TxType, +}; + +/// Helper trait that unifies all behaviour required by block to support full node operations. +pub trait FullBlockBody: + BlockBody + Compact +{ +} + +impl FullBlockBody for T where + T: BlockBody + Compact +{ +} + +/// Abstraction of block's body. pub trait BlockBody: Send + Sync @@ -21,10 +39,115 @@ pub trait BlockBody: + InMemorySize + 'static { - /// Ordered list of signed transactions as committed in block. - // todo: requires trait for signed transaction - type Transaction: Transaction; + /// Signed transaction. + type Transaction: SignedTransaction; + + /// Header type (uncle blocks). + type Header: BlockHeader; + + /// Withdrawals in block. + type Withdrawals: IntoIterator; /// Returns reference to transactions in block. fn transactions(&self) -> &[Self::Transaction]; + + /// Returns `Withdrawals` in the block, if any. + // todo: branch out into extension trait + fn withdrawals(&self) -> Option<&Self::Withdrawals>; + + /// Returns reference to uncle block headers. + fn ommers(&self) -> &[Self::Header]; + + /// Returns [`Requests`] in block, if any. + fn requests(&self) -> Option<&Requests>; + + /// 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; + + /// Recover signer addresses for all transactions in the block body. + fn recover_signers(&self) -> Option> { + let num_txns = self.transactions().len(); + Self::Transaction::recover_signers(self.transactions(), num_txns) + } + + /// 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 { + 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; +} + +/// Helper trait to implement [`BlockBody`] functionality for [`Block`](crate::Block) types. +pub trait Body { + /// See [`BlockBody`]. + fn transactions(&self) -> &[SignedTx]; + /// See [`BlockBody`]. + fn withdrawals(&self) -> Option<&Withdrawals>; + /// See [`BlockBody`]. + fn ommers(&self) -> &[Header]; + /// See [`BlockBody`]. + fn requests(&self) -> Option<&Requests>; + /// See [`BlockBody`]. + fn calculate_tx_root(&self) -> B256; + /// See [`BlockBody`]. + fn calculate_ommers_root(&self) -> B256; + /// See [`BlockBody`]. + fn calculate_withdrawals_root(&self) -> Option; + /// See [`BlockBody`]. + fn recover_signers(&self) -> Option> { + let num_txns = self.transactions().len(); + SignedTx::recover_signers(self.transactions(), num_txns) + } + /// 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 + '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; } diff --git a/crates/primitives-traits/src/block/header.rs b/crates/primitives-traits/src/block/header.rs index 0c1fc3e57f2a..6bfa7f29be7b 100644 --- a/crates/primitives-traits/src/block/header.rs +++ b/crates/primitives-traits/src/block/header.rs @@ -1,10 +1,12 @@ //! Block header data primitive. -use crate::InMemorySize; -use alloy_primitives::Sealable; use core::fmt; + +use alloy_primitives::{Address, BlockNumber, Bloom, Bytes, Sealable, B256, B64, U256}; use reth_codecs::Compact; +use crate::InMemorySize; + /// Helper trait that unifies all behaviour required by block header to support full node /// operations. pub trait FullBlockHeader: BlockHeader + Compact {} @@ -21,6 +23,8 @@ pub trait BlockHeader: + fmt::Debug + PartialEq + Eq + + serde::Serialize + + for<'de> serde::Deserialize<'de> + alloy_rlp::Encodable + alloy_rlp::Decodable + alloy_consensus::BlockHeader @@ -49,3 +53,53 @@ impl BlockHeader for T where + 'static { } + +/// Helper trait to implement [`BlockHeader`] functionality for all [`Block`](crate::Block) types. +pub trait Header { + /// See [`alloy_consensus::BlockHeader`]. + fn parent_hash(&self) -> B256; + /// See [`alloy_consensus::BlockHeader`]. + fn ommers_hash(&self) -> B256; + /// See [`alloy_consensus::BlockHeader`]. + fn beneficiary(&self) -> Address; + /// See [`alloy_consensus::BlockHeader`]. + fn state_root(&self) -> B256; + /// See [`alloy_consensus::BlockHeader`]. + fn transactions_root(&self) -> B256; + /// See [`alloy_consensus::BlockHeader`]. + fn receipts_root(&self) -> B256; + /// See [`alloy_consensus::BlockHeader`]. + fn withdrawals_root(&self) -> Option; + /// See [`alloy_consensus::BlockHeader`]. + fn logs_bloom(&self) -> Bloom; + /// See [`alloy_consensus::BlockHeader`]. + fn difficulty(&self) -> U256; + /// See [`alloy_consensus::BlockHeader`]. + fn number(&self) -> BlockNumber; + /// See [`alloy_consensus::BlockHeader`]. + fn gas_limit(&self) -> u64; + /// See [`alloy_consensus::BlockHeader`]. + fn gas_used(&self) -> u64; + /// See [`alloy_consensus::BlockHeader`]. + fn timestamp(&self) -> u64; + /// See [`alloy_consensus::BlockHeader`]. + fn mix_hash(&self) -> Option; + /// See [`alloy_consensus::BlockHeader`]. + fn nonce(&self) -> Option; + /// See [`alloy_consensus::BlockHeader`]. + fn base_fee_per_gas(&self) -> Option; + /// See [`alloy_consensus::BlockHeader`]. + fn blob_gas_used(&self) -> Option; + /// See [`alloy_consensus::BlockHeader`]. + fn excess_blob_gas(&self) -> Option; + /// See [`alloy_consensus::BlockHeader`]. + fn parent_beacon_block_root(&self) -> Option; + /// See [`alloy_consensus::BlockHeader`]. + fn requests_hash(&self) -> Option; + /// See [`alloy_consensus::BlockHeader`]. + fn extra_data(&self) -> &Bytes; + /// See [`alloy_consensus::BlockHeader`]. + fn next_block_excess_blob_gas(&self) -> Option; + /// See [`alloy_consensus::BlockHeader`]. + fn next_block_blob_fee(&self) -> Option; +} diff --git a/crates/primitives-traits/src/block/mod.rs b/crates/primitives-traits/src/block/mod.rs index 33008c4381dc..13965d92e8f9 100644 --- a/crates/primitives-traits/src/block/mod.rs +++ b/crates/primitives-traits/src/block/mod.rs @@ -3,16 +3,19 @@ pub mod body; pub mod header; -use alloc::fmt; +use alloc::{fmt, vec::Vec}; +use alloy_consensus::BlockHeader as _; +use alloy_eips::eip7685::Requests; +use alloy_primitives::{Address, BlockNumber, Bloom, Bytes, B256, B64, U256}; use reth_codecs::Compact; -use crate::{BlockHeader, FullBlockHeader, InMemorySize}; +use crate::{BlockBody, BlockHeader, Body, FullBlockBody, FullBlockHeader, Header, InMemorySize}; /// Helper trait that unifies all behaviour required by block to support full node operations. -pub trait FullBlock: Block + Compact {} +pub trait FullBlock: Block + Compact {} -impl FullBlock for T where T: Block + Compact {} +impl FullBlock for T where T: Block + Compact {} /// Abstraction of block data type. // todo: make sealable super-trait, depends on @@ -29,13 +32,18 @@ pub trait Block: + Eq + serde::Serialize + for<'a> serde::Deserialize<'a> - + InMemorySize + + Header + + Body< + Self::Header, + ::Transaction, + ::Withdrawals, + > + InMemorySize { /// Header part of the block. type Header: BlockHeader; /// The block's body contains the transactions in the block. - type Body: Send + Sync + Unpin + 'static; + type Body: BlockBody
; /// Returns reference to block header. fn header(&self) -> &Self::Header; @@ -43,3 +51,175 @@ pub trait Block: /// Returns reference to block body. fn body(&self) -> &Self::Body; } + +impl Header for T { + #[inline] + fn parent_hash(&self) -> B256 { + self.header().parent_hash() + } + + #[inline] + fn ommers_hash(&self) -> B256 { + self.header().ommers_hash() + } + + #[inline] + fn beneficiary(&self) -> Address { + self.header().beneficiary() + } + + #[inline] + fn state_root(&self) -> B256 { + self.header().state_root() + } + + #[inline] + fn transactions_root(&self) -> B256 { + self.header().transactions_root() + } + + #[inline] + fn receipts_root(&self) -> B256 { + self.header().receipts_root() + } + + #[inline] + fn withdrawals_root(&self) -> Option { + self.header().withdrawals_root() + } + + #[inline] + fn logs_bloom(&self) -> Bloom { + self.header().logs_bloom() + } + + #[inline] + fn difficulty(&self) -> U256 { + self.header().difficulty() + } + + #[inline] + fn number(&self) -> BlockNumber { + self.header().number() + } + + #[inline] + fn gas_limit(&self) -> u64 { + self.header().gas_limit() + } + + #[inline] + fn gas_used(&self) -> u64 { + self.header().gas_used() + } + + #[inline] + fn timestamp(&self) -> u64 { + self.header().timestamp() + } + + #[inline] + fn mix_hash(&self) -> Option { + self.header().mix_hash() + } + + #[inline] + fn nonce(&self) -> Option { + self.header().nonce() + } + + #[inline] + fn base_fee_per_gas(&self) -> Option { + self.header().base_fee_per_gas() + } + + #[inline] + fn blob_gas_used(&self) -> Option { + self.header().blob_gas_used() + } + + #[inline] + fn excess_blob_gas(&self) -> Option { + self.header().excess_blob_gas() + } + + #[inline] + fn parent_beacon_block_root(&self) -> Option { + self.header().parent_beacon_block_root() + } + + #[inline] + fn requests_hash(&self) -> Option { + self.header().requests_hash() + } + + #[inline] + fn extra_data(&self) -> &Bytes { + self.header().extra_data() + } + + #[inline] + fn next_block_excess_blob_gas(&self) -> Option { + self.header().next_block_excess_blob_gas() + } + + #[inline] + fn next_block_blob_fee(&self) -> Option { + self.header().next_block_blob_fee() + } +} + +impl + Body::Transaction, ::Withdrawals> + for T +{ + #[inline] + fn transactions(&self) -> &[::Transaction] { + self.body().transactions() + } + + #[inline] + fn withdrawals(&self) -> Option<&::Withdrawals> { + self.body().withdrawals() + } + + #[inline] + fn ommers(&self) -> &[T::Header] { + self.body().ommers() + } + + #[inline] + fn requests(&self) -> Option<&Requests> { + self.body().requests() + } + + #[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 { + self.body().calculate_withdrawals_root() + } + + #[inline] + fn recover_signers(&self) -> Option> { + 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 { + self.body().blob_versioned_hashes_copied() + } +} diff --git a/crates/primitives-traits/src/lib.rs b/crates/primitives-traits/src/lib.rs index 584181f2c95b..de6973bdf5f4 100644 --- a/crates/primitives-traits/src/lib.rs +++ b/crates/primitives-traits/src/lib.rs @@ -26,7 +26,7 @@ pub use receipt::{FullReceipt, Receipt}; pub mod transaction; pub use transaction::{ - signed::{FullSignedTx, SignedTransaction}, + signed::{FullSignedTx, SignedTransaction, PARALLEL_SENDER_RECOVERY_THRESHOLD}, FullTransaction, Transaction, TransactionExt, }; @@ -35,8 +35,8 @@ pub use integer_list::{IntegerList, IntegerListError}; pub mod block; pub use block::{ - body::BlockBody, - header::{BlockHeader, FullBlockHeader}, + body::{BlockBody, Body, FullBlockBody}, + header::{BlockHeader, FullBlockHeader, Header}, Block, FullBlock, }; diff --git a/crates/primitives-traits/src/transaction/signed.rs b/crates/primitives-traits/src/transaction/signed.rs index 455a9886eb8f..48afd86e88bc 100644 --- a/crates/primitives-traits/src/transaction/signed.rs +++ b/crates/primitives-traits/src/transaction/signed.rs @@ -1,15 +1,30 @@ //! API of a signed transaction. -use alloc::fmt; +use alloc::{fmt, vec::Vec}; use core::hash::Hash; +#[cfg(feature = "std")] +use std::sync::LazyLock; use alloy_eips::eip2718::{Decodable2718, Encodable2718}; use alloy_primitives::{keccak256, Address, PrimitiveSignature, TxHash, B256}; +use once_cell as _; +#[cfg(not(feature = "std"))] +use once_cell::sync::Lazy as LazyLock; +use rayon::prelude::{IntoParallelIterator, ParallelIterator}; use reth_codecs::Compact; use revm_primitives::TxEnv; use crate::{transaction::TransactionExt, FullTransaction, MaybeArbitrary, Transaction}; +/// Expected number of transactions where we can expect a speed-up by recovering the senders in +/// parallel. +pub static PARALLEL_SENDER_RECOVERY_THRESHOLD: LazyLock = + 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 FullSignedTx: SignedTransaction + Compact {} @@ -65,6 +80,21 @@ pub trait SignedTransaction: /// `reth_primitives::transaction::recover_signer_unchecked`. fn recover_signer_unchecked(&self) -> Option
; + /// Recovers a list of signers from a transaction list iterator. + /// + /// Returns `None`, if some transaction's signature is invalid, see also + /// [`Self::recover_signer`]. + fn recover_signers<'a, T>(txes: T, num_txes: usize) -> Option> + where + T: IntoParallelIterator + IntoIterator + Send, + { + if num_txes < *PARALLEL_SENDER_RECOVERY_THRESHOLD { + txes.into_iter().map(|tx| tx.recover_signer()).collect() + } else { + txes.into_par_iter().map(|tx| tx.recover_signer()).collect() + } + } + /// Create a new signed transaction from a transaction and its signature. /// /// This will also calculate the transaction hash using its encoding. diff --git a/crates/primitives/src/block.rs b/crates/primitives/src/block.rs index d6476c29b4c4..634be233be7c 100644 --- a/crates/primitives/src/block.rs +++ b/crates/primitives/src/block.rs @@ -1,7 +1,7 @@ use crate::{GotExpected, SealedHeader, TransactionSigned, TransactionSignedEcRecovered}; use alloc::vec::Vec; use alloy_consensus::Header; -use alloy_eips::{eip2718::Encodable2718, eip4895::Withdrawals}; +use alloy_eips::{eip2718::Encodable2718, eip4895::Withdrawals, eip7685::Requests}; use alloy_primitives::{Address, Bytes, B256}; use alloy_rlp::{Decodable, Encodable, RlpDecodable, RlpEncodable}; use derive_more::{Deref, DerefMut}; @@ -329,10 +329,21 @@ impl SealedBlock { .flatten() } - /// Returns all blob versioned hashes from the block body. + /// Returns references to all blob versioned hashes from the block body. #[inline] pub fn blob_versioned_hashes(&self) -> Vec<&B256> { - self.blob_versioned_hashes_iter().collect() + self.body.blob_versioned_hashes() + } + + /// Returns all blob versioned hashes from the block body. + #[inline] + pub fn blob_versioned_hashes_copied(&self) -> Vec { + self.body + .transactions + .iter() + .filter_map(|tx| tx.blob_versioned_hashes()) + .flatten() + .collect() } /// Expensive operation that recovers transaction signer. See [`SealedBlockWithSenders`]. @@ -466,7 +477,7 @@ where impl reth_primitives_traits::Block for SealedBlock where H: reth_primitives_traits::BlockHeader, - B: reth_primitives_traits::BlockBody, + B: reth_primitives_traits::BlockBody
, Self: Serialize + for<'a> Deserialize<'a>, { type Header = H; @@ -661,6 +672,61 @@ impl BlockBody { } } +impl reth_primitives_traits::BlockBody for BlockBody { + type Transaction = TransactionSigned; + type Header = Header; + type Withdrawals = Withdrawals; + + #[inline] + fn transactions(&self) -> &[Self::Transaction] { + &self.transactions + } + + #[inline] + fn withdrawals(&self) -> Option<&Self::Withdrawals> { + self.withdrawals.as_ref() + } + + #[inline] + fn ommers(&self) -> &[Self::Header] { + &self.ommers + } + + #[inline] + fn requests(&self) -> Option<&Requests> { + None + } + + #[inline] + fn calculate_tx_root(&self) -> B256 { + crate::proofs::calculate_transaction_root(&self.transactions) + } + + #[inline] + fn calculate_ommers_root(&self) -> B256 { + crate::proofs::calculate_ommers_root(&self.ommers) + } + + #[inline] + fn calculate_withdrawals_root(&self) -> Option { + self.withdrawals.as_ref().map(|w| crate::proofs::calculate_withdrawals_root(w)) + } + + #[inline] + fn blob_versioned_hashes(&self) -> Vec<&B256> { + self.transactions + .iter() + .filter_map(|tx| tx.as_eip4844().map(|blob_tx| &blob_tx.blob_versioned_hashes)) + .flatten() + .collect() + } + + #[inline] + fn blob_versioned_hashes_copied(&self) -> Vec { + self.transactions.iter().filter_map(|tx| tx.blob_versioned_hashes()).flatten().collect() + } +} + impl InMemorySize for BlockBody { /// Calculates a heuristic for the in-memory size of the [`BlockBody`]. #[inline] @@ -675,14 +741,6 @@ impl InMemorySize for BlockBody { } } -impl reth_primitives_traits::BlockBody for BlockBody { - type Transaction = TransactionSigned; - - fn transactions(&self) -> &[Self::Transaction] { - &self.transactions - } -} - impl From for BlockBody { fn from(block: Block) -> Self { Self { diff --git a/crates/primitives/src/traits/mod.rs b/crates/primitives/src/traits/mod.rs deleted file mode 100644 index 49fb73ea5555..000000000000 --- a/crates/primitives/src/traits/mod.rs +++ /dev/null @@ -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; diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index f325b72776f5..be65b2bfedc9 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -19,16 +19,12 @@ use alloy_rlp::{Decodable, Encodable, Error as RlpError, Header}; use core::mem; use derive_more::{AsRef, Deref}; use once_cell as _; -#[cfg(not(feature = "std"))] -use once_cell::sync::Lazy as LazyLock; #[cfg(feature = "optimism")] use op_alloy_consensus::DepositTransaction; use rayon::prelude::{IntoParallelIterator, ParallelIterator}; -use reth_primitives_traits::InMemorySize; +use reth_primitives_traits::{InMemorySize, PARALLEL_SENDER_RECOVERY_THRESHOLD}; use serde::{Deserialize, Serialize}; use signature::decode_with_eip155_chain_id; -#[cfg(feature = "std")] -use std::sync::LazyLock; pub use error::{ InvalidTransactionError, TransactionConversionError, TryFromRecoveredTransactionError, @@ -68,21 +64,12 @@ pub use tx_type::{ }; use alloc::vec::Vec; -use reth_primitives_traits::{transaction::TransactionExt, SignedTransaction}; +use reth_primitives_traits::{SignedTransaction, TransactionExt}; use revm_primitives::{AuthorizationList, TxEnv}; /// Either a transaction hash or number. pub type TxHashOrNumber = BlockHashOrNumber; -/// Expected number of transactions where we can expect a speed-up by recovering the senders in -/// parallel. -pub static PARALLEL_SENDER_RECOVERY_THRESHOLD: LazyLock = - LazyLock::new(|| match rayon::current_num_threads() { - 0..=1 => usize::MAX, - 2..=8 => 10, - _ => 5, - }); - /// A raw transaction. /// /// Transaction types were introduced in [EIP-2718](https://eips.ethereum.org/EIPS/eip-2718). @@ -2295,7 +2282,7 @@ mod tests { #[test] fn test_parallel_recovery_order(txes in proptest::collection::vec( proptest_arbitrary_interop::arb::(), - *crate::transaction::PARALLEL_SENDER_RECOVERY_THRESHOLD * 5 + *reth_primitives_traits::PARALLEL_SENDER_RECOVERY_THRESHOLD * 5 )) { let mut rng =rand::thread_rng(); let secp = secp256k1::Secp256k1::new();