From 6ce459aec7411cb5d2bc399b68f8beeabc3c119b Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Fri, 28 Feb 2025 14:05:14 +0100 Subject: [PATCH] chore(payload): Move `ExecutionPayloadValidator` into `reth-ethereum-payload-builder` (#14751) --- Cargo.lock | 15 ++- bin/reth/Cargo.toml | 1 - bin/reth/src/lib.rs | 2 +- crates/engine/service/Cargo.toml | 1 + crates/engine/service/src/service.rs | 3 +- crates/engine/tree/Cargo.toml | 2 + crates/engine/tree/src/tree/mod.rs | 3 +- crates/engine/util/Cargo.toml | 1 - crates/ethereum/engine-primitives/Cargo.toml | 3 - crates/ethereum/engine-primitives/src/lib.rs | 74 +------------ crates/ethereum/node/Cargo.toml | 7 +- crates/ethereum/node/src/engine.rs | 75 +++++++++++++ crates/ethereum/node/src/lib.rs | 3 + crates/ethereum/node/src/node.rs | 3 +- crates/ethereum/payload/Cargo.toml | 3 + crates/ethereum/payload/src/lib.rs | 3 + crates/ethereum/payload/src/validator.rs | 110 +++++++++++++++++++ crates/payload/validator/Cargo.toml | 10 +- crates/payload/validator/src/lib.rs | 109 +----------------- crates/rpc/rpc-builder/Cargo.toml | 1 + crates/rpc/rpc-builder/tests/it/utils.rs | 3 +- crates/rpc/rpc-engine-api/Cargo.toml | 1 + crates/rpc/rpc-engine-api/src/engine_api.rs | 3 +- examples/custom-engine-types/src/main.rs | 7 +- 24 files changed, 241 insertions(+), 202 deletions(-) create mode 100644 crates/ethereum/node/src/engine.rs create mode 100644 crates/ethereum/payload/src/validator.rs diff --git a/Cargo.lock b/Cargo.lock index c0e4b416cb..07d2879534 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6682,7 +6682,6 @@ dependencies = [ "reth-node-metrics", "reth-payload-builder", "reth-payload-primitives", - "reth-payload-validator", "reth-primitives", "reth-primitives-traits", "reth-provider", @@ -7401,6 +7400,7 @@ dependencies = [ "reth-evm-ethereum", "reth-exex-types", "reth-network-p2p", + "reth-node-ethereum", "reth-node-types", "reth-payload-builder", "reth-primitives", @@ -7448,6 +7448,7 @@ dependencies = [ "reth-exex-types", "reth-metrics", "reth-network-p2p", + "reth-node-ethereum", "reth-payload-builder", "reth-payload-primitives", "reth-primitives-traits", @@ -7495,7 +7496,6 @@ dependencies = [ "reth-evm", "reth-fs-util", "reth-payload-primitives", - "reth-payload-validator", "reth-primitives", "reth-primitives-traits", "reth-provider", @@ -7646,10 +7646,8 @@ dependencies = [ "alloy-primitives", "alloy-rlp", "alloy-rpc-types-engine", - "reth-chainspec", "reth-engine-primitives", "reth-payload-primitives", - "reth-payload-validator", "reth-primitives", "serde", "serde_json", @@ -7678,6 +7676,7 @@ dependencies = [ "alloy-consensus", "alloy-eips", "alloy-primitives", + "alloy-rpc-types-engine", "reth-basic-payload-builder", "reth-chainspec", "reth-errors", @@ -7688,6 +7687,8 @@ dependencies = [ "reth-payload-builder", "reth-payload-builder-primitives", "reth-payload-primitives", + "reth-payload-validator", + "reth-primitives", "reth-primitives-traits", "reth-revm", "reth-storage-api", @@ -8345,6 +8346,7 @@ dependencies = [ "reth-consensus", "reth-db", "reth-e2e-test-utils", + "reth-engine-primitives", "reth-ethereum-consensus", "reth-ethereum-engine-primitives", "reth-ethereum-payload-builder", @@ -8357,6 +8359,7 @@ dependencies = [ "reth-node-builder", "reth-node-core", "reth-payload-primitives", + "reth-primitives", "reth-provider", "reth-revm", "reth-rpc", @@ -8911,8 +8914,6 @@ version = "1.2.0" dependencies = [ "alloy-consensus", "alloy-rpc-types-engine", - "reth-chainspec", - "reth-primitives", "reth-primitives-traits", ] @@ -9239,6 +9240,7 @@ dependencies = [ "reth-network-api", "reth-network-peers", "reth-node-core", + "reth-node-ethereum", "reth-payload-builder", "reth-primitives", "reth-primitives-traits", @@ -9283,6 +9285,7 @@ dependencies = [ "reth-ethereum-engine-primitives", "reth-ethereum-primitives", "reth-metrics", + "reth-node-ethereum", "reth-payload-builder", "reth-payload-builder-primitives", "reth-payload-primitives", diff --git a/bin/reth/Cargo.toml b/bin/reth/Cargo.toml index d6eb957a0e..9ca05a0200 100644 --- a/bin/reth/Cargo.toml +++ b/bin/reth/Cargo.toml @@ -49,7 +49,6 @@ reth-tracing.workspace = true reth-tasks.workspace = true reth-payload-builder.workspace = true reth-payload-primitives.workspace = true -reth-payload-validator.workspace = true reth-basic-payload-builder.workspace = true reth-static-file.workspace = true reth-trie = { workspace = true, features = ["metrics"] } diff --git a/bin/reth/src/lib.rs b/bin/reth/src/lib.rs index cbe1a16607..627d4c475a 100644 --- a/bin/reth/src/lib.rs +++ b/bin/reth/src/lib.rs @@ -41,9 +41,9 @@ pub mod utils { /// Re-exported payload related types pub mod payload { + pub use reth_ethereum_payload_builder::EthereumExecutionPayloadValidator; pub use reth_payload_builder::*; pub use reth_payload_primitives::*; - pub use reth_payload_validator::ExecutionPayloadValidator; } /// Re-exported from `reth_node_api`. diff --git a/crates/engine/service/Cargo.toml b/crates/engine/service/Cargo.toml index ea5ce0e3a4..b58fa89e34 100644 --- a/crates/engine/service/Cargo.toml +++ b/crates/engine/service/Cargo.toml @@ -41,6 +41,7 @@ reth-evm-ethereum.workspace = true reth-exex-types.workspace = true reth-primitives.workspace = true reth-chainspec.workspace = true +reth-node-ethereum.workspace = true tokio = { workspace = true, features = ["sync"] } tokio-stream.workspace = true diff --git a/crates/engine/service/src/service.rs b/crates/engine/service/src/service.rs index eef64d6ed5..39d3c27520 100644 --- a/crates/engine/service/src/service.rs +++ b/crates/engine/service/src/service.rs @@ -163,10 +163,11 @@ mod tests { use reth_engine_primitives::BeaconEngineMessage; use reth_engine_tree::{test_utils::TestPipelineBuilder, tree::NoopInvalidBlockHook}; use reth_ethereum_consensus::EthBeaconConsensus; - use reth_ethereum_engine_primitives::{EthEngineTypes, EthereumEngineValidator}; + use reth_ethereum_engine_primitives::EthEngineTypes; use reth_evm_ethereum::{execute::EthExecutorProvider, EthEvmConfig}; use reth_exex_types::FinishedExExHeight; use reth_network_p2p::test_utils::TestFullBlockClient; + use reth_node_ethereum::EthereumEngineValidator; use reth_primitives::SealedHeader; use reth_provider::{ providers::BlockchainProvider, test_utils::create_test_provider_factory_with_chain_spec, diff --git a/crates/engine/tree/Cargo.toml b/crates/engine/tree/Cargo.toml index c98a578d63..d5bcb5183f 100644 --- a/crates/engine/tree/Cargo.toml +++ b/crates/engine/tree/Cargo.toml @@ -85,6 +85,7 @@ reth-static-file.workspace = true reth-testing-utils.workspace = true reth-tracing.workspace = true reth-trie-db.workspace = true +reth-node-ethereum.workspace = true # alloy alloy-rlp.workspace = true @@ -128,4 +129,5 @@ test-utils = [ "reth-trie-db/test-utils", "reth-trie-parallel/test-utils", "reth-ethereum-primitives/test-utils", + "reth-node-ethereum/test-utils", ] diff --git a/crates/engine/tree/src/tree/mod.rs b/crates/engine/tree/src/tree/mod.rs index 0bc099870e..24026fbc6e 100644 --- a/crates/engine/tree/src/tree/mod.rs +++ b/crates/engine/tree/src/tree/mod.rs @@ -2897,10 +2897,11 @@ mod tests { use reth_chainspec::{ChainSpec, HOLESKY, MAINNET}; use reth_engine_primitives::ForkchoiceStatus; use reth_ethereum_consensus::EthBeaconConsensus; - use reth_ethereum_engine_primitives::{EthEngineTypes, EthereumEngineValidator}; + use reth_ethereum_engine_primitives::EthEngineTypes; use reth_ethereum_primitives::{Block, EthPrimitives}; use reth_evm::test_utils::MockExecutorProvider; use reth_evm_ethereum::EthEvmConfig; + use reth_node_ethereum::EthereumEngineValidator; use reth_primitives_traits::Block as _; use reth_provider::test_utils::MockEthProvider; use reth_trie::{updates::TrieUpdates, HashedPostState}; diff --git a/crates/engine/util/Cargo.toml b/crates/engine/util/Cargo.toml index 65467adfb6..fd8c18ed4b 100644 --- a/crates/engine/util/Cargo.toml +++ b/crates/engine/util/Cargo.toml @@ -20,7 +20,6 @@ reth-chainspec.workspace = true reth-consensus-common.workspace = true reth-fs-util.workspace = true reth-engine-primitives.workspace = true -reth-payload-validator.workspace = true reth-evm.workspace = true reth-revm.workspace = true reth-provider.workspace = true diff --git a/crates/ethereum/engine-primitives/Cargo.toml b/crates/ethereum/engine-primitives/Cargo.toml index dda1a3554a..0654834da3 100644 --- a/crates/ethereum/engine-primitives/Cargo.toml +++ b/crates/ethereum/engine-primitives/Cargo.toml @@ -12,11 +12,9 @@ workspace = true [dependencies] # reth -reth-chainspec.workspace = true reth-primitives.workspace = true reth-engine-primitives.workspace = true reth-payload-primitives.workspace = true -reth-payload-validator.workspace = true # alloy alloy-primitives.workspace = true @@ -34,7 +32,6 @@ serde_json.workspace = true [features] default = ["std"] std = [ - "reth-chainspec/std", "reth-primitives/std", "alloy-primitives/std", "alloy-eips/std", diff --git a/crates/ethereum/engine-primitives/src/lib.rs b/crates/ethereum/engine-primitives/src/lib.rs index ad26315e52..f230df6553 100644 --- a/crates/ethereum/engine-primitives/src/lib.rs +++ b/crates/ethereum/engine-primitives/src/lib.rs @@ -12,21 +12,16 @@ extern crate alloc; mod payload; -use alloc::sync::Arc; +pub use payload::{EthBuiltPayload, EthPayloadBuilderAttributes}; + use alloy_rpc_types_engine::{ExecutionData, ExecutionPayload}; pub use alloy_rpc_types_engine::{ ExecutionPayloadEnvelopeV2, ExecutionPayloadEnvelopeV3, ExecutionPayloadEnvelopeV4, ExecutionPayloadV1, PayloadAttributes as EthPayloadAttributes, }; -pub use payload::{EthBuiltPayload, EthPayloadBuilderAttributes}; -use reth_chainspec::ChainSpec; -use reth_engine_primitives::{EngineTypes, EngineValidator, PayloadValidator}; -use reth_payload_primitives::{ - validate_version_specific_fields, BuiltPayload, EngineApiMessageVersion, - EngineObjectValidationError, NewPayloadError, PayloadOrAttributes, PayloadTypes, -}; -use reth_payload_validator::ExecutionPayloadValidator; -use reth_primitives::{Block, NodePrimitives, RecoveredBlock, SealedBlock}; +use reth_engine_primitives::EngineTypes; +use reth_payload_primitives::{BuiltPayload, PayloadTypes}; +use reth_primitives::{NodePrimitives, SealedBlock}; /// The types used in the default mainnet ethereum beacon consensus engine. #[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)] @@ -77,62 +72,3 @@ impl PayloadTypes for EthPayloadTypes { type PayloadAttributes = EthPayloadAttributes; type PayloadBuilderAttributes = EthPayloadBuilderAttributes; } - -/// Validator for the ethereum engine API. -#[derive(Debug, Clone)] -pub struct EthereumEngineValidator { - inner: ExecutionPayloadValidator, -} - -impl EthereumEngineValidator { - /// Instantiates a new validator. - pub const fn new(chain_spec: Arc) -> Self { - Self { inner: ExecutionPayloadValidator::new(chain_spec) } - } - - /// Returns the chain spec used by the validator. - #[inline] - fn chain_spec(&self) -> &ChainSpec { - self.inner.chain_spec() - } -} - -impl PayloadValidator for EthereumEngineValidator { - type Block = Block; - type ExecutionData = ExecutionData; - - fn ensure_well_formed_payload( - &self, - payload: ExecutionData, - ) -> Result, NewPayloadError> { - let sealed_block = self.inner.ensure_well_formed_payload(payload)?; - sealed_block.try_recover().map_err(|e| NewPayloadError::Other(e.into())) - } -} - -impl EngineValidator for EthereumEngineValidator -where - Types: EngineTypes, -{ - fn validate_version_specific_fields( - &self, - version: EngineApiMessageVersion, - payload_or_attrs: PayloadOrAttributes<'_, Self::ExecutionData, EthPayloadAttributes>, - ) -> Result<(), EngineObjectValidationError> { - validate_version_specific_fields(self.chain_spec(), version, payload_or_attrs) - } - - fn ensure_well_formed_attributes( - &self, - version: EngineApiMessageVersion, - attributes: &EthPayloadAttributes, - ) -> Result<(), EngineObjectValidationError> { - validate_version_specific_fields( - self.chain_spec(), - version, - PayloadOrAttributes::::PayloadAttributes( - attributes, - ), - ) - } -} diff --git a/crates/ethereum/node/Cargo.toml b/crates/ethereum/node/Cargo.toml index 9babed8e04..41d0d91e86 100644 --- a/crates/ethereum/node/Cargo.toml +++ b/crates/ethereum/node/Cargo.toml @@ -33,9 +33,13 @@ reth-chainspec.workspace = true reth-revm = { workspace = true, features = ["std"] } reth-trie-db.workspace = true reth-rpc-eth-types.workspace = true +reth-engine-primitives.workspace = true +reth-primitives.workspace = true +reth-payload-primitives.workspace = true +# ethereum alloy-rpc-types-eth.workspace = true - +alloy-rpc-types-engine.workspace = true # revm with required ethereum features revm = { workspace = true, features = ["secp256k1", "blst", "c-kzg"] } @@ -87,4 +91,5 @@ test-utils = [ "reth-trie-db/test-utils", "revm/test-utils", "reth-evm/test-utils", + "reth-primitives/test-utils", ] diff --git a/crates/ethereum/node/src/engine.rs b/crates/ethereum/node/src/engine.rs new file mode 100644 index 0000000000..bae688a098 --- /dev/null +++ b/crates/ethereum/node/src/engine.rs @@ -0,0 +1,75 @@ +//! Validates execution payload wrt Ethereum Execution Engine API version. + +use alloy_rpc_types_engine::ExecutionData; +pub use alloy_rpc_types_engine::{ + ExecutionPayloadEnvelopeV2, ExecutionPayloadEnvelopeV3, ExecutionPayloadEnvelopeV4, + ExecutionPayloadV1, PayloadAttributes as EthPayloadAttributes, +}; +use reth_chainspec::ChainSpec; +use reth_engine_primitives::{EngineTypes, EngineValidator, PayloadValidator}; +use reth_ethereum_payload_builder::EthereumExecutionPayloadValidator; +use reth_payload_primitives::{ + validate_version_specific_fields, EngineApiMessageVersion, EngineObjectValidationError, + NewPayloadError, PayloadOrAttributes, +}; +use reth_primitives::{Block, RecoveredBlock}; +use std::sync::Arc; + +/// Validator for the ethereum engine API. +#[derive(Debug, Clone)] +pub struct EthereumEngineValidator { + inner: EthereumExecutionPayloadValidator, +} + +impl EthereumEngineValidator { + /// Instantiates a new validator. + pub const fn new(chain_spec: Arc) -> Self { + Self { inner: EthereumExecutionPayloadValidator::new(chain_spec) } + } + + /// Returns the chain spec used by the validator. + #[inline] + fn chain_spec(&self) -> &ChainSpec { + self.inner.chain_spec() + } +} + +impl PayloadValidator for EthereumEngineValidator { + type Block = Block; + type ExecutionData = ExecutionData; + + fn ensure_well_formed_payload( + &self, + payload: ExecutionData, + ) -> Result, NewPayloadError> { + let sealed_block = self.inner.ensure_well_formed_payload(payload)?; + sealed_block.try_recover().map_err(|e| NewPayloadError::Other(e.into())) + } +} + +impl EngineValidator for EthereumEngineValidator +where + Types: EngineTypes, +{ + fn validate_version_specific_fields( + &self, + version: EngineApiMessageVersion, + payload_or_attrs: PayloadOrAttributes<'_, Self::ExecutionData, EthPayloadAttributes>, + ) -> Result<(), EngineObjectValidationError> { + validate_version_specific_fields(self.chain_spec(), version, payload_or_attrs) + } + + fn ensure_well_formed_attributes( + &self, + version: EngineApiMessageVersion, + attributes: &EthPayloadAttributes, + ) -> Result<(), EngineObjectValidationError> { + validate_version_specific_fields( + self.chain_spec(), + version, + PayloadOrAttributes::::PayloadAttributes( + attributes, + ), + ) + } +} diff --git a/crates/ethereum/node/src/lib.rs b/crates/ethereum/node/src/lib.rs index 9f2440c7d4..e3d5aea114 100644 --- a/crates/ethereum/node/src/lib.rs +++ b/crates/ethereum/node/src/lib.rs @@ -24,3 +24,6 @@ pub mod node; pub use node::EthereumNode; pub mod payload; + +pub mod engine; +pub use engine::EthereumEngineValidator; diff --git a/crates/ethereum/node/src/node.rs b/crates/ethereum/node/src/node.rs index 1dbd27ece6..5fcd8441a4 100644 --- a/crates/ethereum/node/src/node.rs +++ b/crates/ethereum/node/src/node.rs @@ -1,11 +1,10 @@ //! Ethereum Node types config. -pub use crate::payload::EthereumPayloadBuilder; +pub use crate::{payload::EthereumPayloadBuilder, EthereumEngineValidator}; use crate::{EthEngineTypes, EthEvmConfig}; use reth_chainspec::ChainSpec; use reth_consensus::{ConsensusError, FullConsensus}; use reth_ethereum_consensus::EthBeaconConsensus; -pub use reth_ethereum_engine_primitives::EthereumEngineValidator; use reth_ethereum_engine_primitives::{ EthBuiltPayload, EthPayloadAttributes, EthPayloadBuilderAttributes, }; diff --git a/crates/ethereum/payload/Cargo.toml b/crates/ethereum/payload/Cargo.toml index 6bf9c812f8..198db952c4 100644 --- a/crates/ethereum/payload/Cargo.toml +++ b/crates/ethereum/payload/Cargo.toml @@ -27,9 +27,12 @@ reth-evm.workspace = true reth-evm-ethereum.workspace = true reth-errors.workspace = true reth-chainspec.workspace = true +reth-payload-validator.workspace = true +reth-primitives.workspace = true # ethereum revm.workspace = true +alloy-rpc-types-engine.workspace = true # alloy alloy-eips.workspace = true diff --git a/crates/ethereum/payload/src/lib.rs b/crates/ethereum/payload/src/lib.rs index 3a48612522..c39e2a0b45 100644 --- a/crates/ethereum/payload/src/lib.rs +++ b/crates/ethereum/payload/src/lib.rs @@ -9,6 +9,9 @@ #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #![allow(clippy::useless_let_if_seq)] +pub mod validator; +pub use validator::EthereumExecutionPayloadValidator; + use alloy_consensus::{BlockHeader, Header, Transaction, Typed2718, EMPTY_OMMER_ROOT_HASH}; use alloy_eips::{eip4844::DATA_GAS_PER_BLOB, merge::BEACON_NONCE}; use alloy_primitives::U256; diff --git a/crates/ethereum/payload/src/validator.rs b/crates/ethereum/payload/src/validator.rs new file mode 100644 index 0000000000..89da600c78 --- /dev/null +++ b/crates/ethereum/payload/src/validator.rs @@ -0,0 +1,110 @@ +//! Validates execution payload wrt Ethereum consensus rules + +use alloy_rpc_types_engine::{ExecutionData, PayloadError}; +use reth_chainspec::EthereumHardforks; +use reth_payload_validator::{cancun, prague, shanghai}; +use reth_primitives::Block; +use reth_primitives_traits::{Block as _, SealedBlock, SignedTransaction}; +use std::sync::Arc; + +/// Execution payload validator. +#[derive(Clone, Debug)] +pub struct EthereumExecutionPayloadValidator { + /// Chain spec to validate against. + chain_spec: Arc, +} + +impl EthereumExecutionPayloadValidator { + /// Create a new validator. + pub const fn new(chain_spec: Arc) -> Self { + Self { chain_spec } + } + + /// Returns the chain spec used by the validator. + #[inline] + pub const fn chain_spec(&self) -> &Arc { + &self.chain_spec + } +} + +impl EthereumExecutionPayloadValidator { + /// Returns true if the Cancun hardfork is active at the given timestamp. + #[inline] + fn is_cancun_active_at_timestamp(&self, timestamp: u64) -> bool { + self.chain_spec().is_cancun_active_at_timestamp(timestamp) + } + + /// Returns true if the Shanghai hardfork is active at the given timestamp. + #[inline] + fn is_shanghai_active_at_timestamp(&self, timestamp: u64) -> bool { + self.chain_spec().is_shanghai_active_at_timestamp(timestamp) + } + + /// Returns true if the Prague hardfork is active at the given timestamp. + #[inline] + fn is_prague_active_at_timestamp(&self, timestamp: u64) -> bool { + self.chain_spec().is_prague_active_at_timestamp(timestamp) + } + + /// Ensures that the given payload does not violate any consensus rules that concern the block's + /// layout, like: + /// - missing or invalid base fee + /// - invalid extra data + /// - invalid transactions + /// - incorrect hash + /// - the versioned hashes passed with the payload do not exactly match transaction versioned + /// hashes + /// - the block does not contain blob transactions if it is pre-cancun + /// + /// The checks are done in the order that conforms with the engine-API specification. + /// + /// This is intended to be invoked after receiving the payload from the CLI. + /// The additional [`MaybeCancunPayloadFields`](alloy_rpc_types_engine::MaybeCancunPayloadFields) are not part of the payload, but are additional fields in the `engine_newPayloadV3` RPC call, See also + /// + /// If the cancun fields are provided this also validates that the versioned hashes in the block + /// match the versioned hashes passed in the + /// [`CancunPayloadFields`](alloy_rpc_types_engine::CancunPayloadFields), if the cancun payload + /// fields are provided. If the payload fields are not provided, but versioned hashes exist + /// in the block, this is considered an error: [`PayloadError::InvalidVersionedHashes`]. + /// + /// This validates versioned hashes according to the Engine API Cancun spec: + /// + pub fn ensure_well_formed_payload( + &self, + payload: ExecutionData, + ) -> Result>, PayloadError> { + let ExecutionData { payload, sidecar } = payload; + + let expected_hash = payload.block_hash(); + + // First parse the block + let sealed_block = payload.try_into_block_with_sidecar(&sidecar)?.seal_slow(); + + // Ensure the hash included in the payload matches the block hash + if expected_hash != sealed_block.hash() { + return Err(PayloadError::BlockHash { + execution: sealed_block.hash(), + consensus: expected_hash, + }) + } + + shanghai::ensure_well_formed_fields( + sealed_block.body(), + self.is_shanghai_active_at_timestamp(sealed_block.timestamp), + )?; + + cancun::ensure_well_formed_fields( + &sealed_block, + sidecar.cancun(), + self.is_cancun_active_at_timestamp(sealed_block.timestamp), + )?; + + prague::ensure_well_formed_fields( + sealed_block.body(), + sidecar.prague(), + self.is_prague_active_at_timestamp(sealed_block.timestamp), + )?; + + Ok(sealed_block) + } +} diff --git a/crates/payload/validator/Cargo.toml b/crates/payload/validator/Cargo.toml index e95300153e..9862ba964d 100644 --- a/crates/payload/validator/Cargo.toml +++ b/crates/payload/validator/Cargo.toml @@ -13,10 +13,16 @@ workspace = true [dependencies] # reth -reth-chainspec.workspace = true -reth-primitives.workspace = true reth-primitives-traits.workspace = true # alloy alloy-rpc-types-engine.workspace = true alloy-consensus.workspace = true + +[features] +default = ["std"] +std = [ + "alloy-consensus/std", + "alloy-rpc-types-engine/std", + "reth-primitives-traits/std", +] diff --git a/crates/payload/validator/src/lib.rs b/crates/payload/validator/src/lib.rs index a2088ba113..de952ebd6a 100644 --- a/crates/payload/validator/src/lib.rs +++ b/crates/payload/validator/src/lib.rs @@ -7,115 +7,8 @@ )] #![cfg_attr(not(test), warn(unused_crate_dependencies))] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +#![cfg_attr(not(feature = "std"), no_std)] pub mod cancun; pub mod prague; pub mod shanghai; - -use alloy_rpc_types_engine::{ExecutionData, PayloadError}; -use reth_chainspec::EthereumHardforks; -use reth_primitives::SealedBlock; -use reth_primitives_traits::{Block, SignedTransaction}; -use std::sync::Arc; - -/// Execution payload validator. -#[derive(Clone, Debug)] -pub struct ExecutionPayloadValidator { - /// Chain spec to validate against. - chain_spec: Arc, -} - -impl ExecutionPayloadValidator { - /// Create a new validator. - pub const fn new(chain_spec: Arc) -> Self { - Self { chain_spec } - } - - /// Returns the chain spec used by the validator. - #[inline] - pub const fn chain_spec(&self) -> &Arc { - &self.chain_spec - } -} - -impl ExecutionPayloadValidator { - /// Returns true if the Cancun hardfork is active at the given timestamp. - #[inline] - fn is_cancun_active_at_timestamp(&self, timestamp: u64) -> bool { - self.chain_spec().is_cancun_active_at_timestamp(timestamp) - } - - /// Returns true if the Shanghai hardfork is active at the given timestamp. - #[inline] - fn is_shanghai_active_at_timestamp(&self, timestamp: u64) -> bool { - self.chain_spec().is_shanghai_active_at_timestamp(timestamp) - } - - /// Returns true if the Prague hardfork is active at the given timestamp. - #[inline] - fn is_prague_active_at_timestamp(&self, timestamp: u64) -> bool { - self.chain_spec().is_prague_active_at_timestamp(timestamp) - } - - /// Ensures that the given payload does not violate any consensus rules that concern the block's - /// layout, like: - /// - missing or invalid base fee - /// - invalid extra data - /// - invalid transactions - /// - incorrect hash - /// - the versioned hashes passed with the payload do not exactly match transaction versioned - /// hashes - /// - the block does not contain blob transactions if it is pre-cancun - /// - /// The checks are done in the order that conforms with the engine-API specification. - /// - /// This is intended to be invoked after receiving the payload from the CLI. - /// The additional [`MaybeCancunPayloadFields`](alloy_rpc_types_engine::MaybeCancunPayloadFields) are not part of the payload, but are additional fields in the `engine_newPayloadV3` RPC call, See also - /// - /// If the cancun fields are provided this also validates that the versioned hashes in the block - /// match the versioned hashes passed in the - /// [`CancunPayloadFields`](alloy_rpc_types_engine::CancunPayloadFields), if the cancun payload - /// fields are provided. If the payload fields are not provided, but versioned hashes exist - /// in the block, this is considered an error: [`PayloadError::InvalidVersionedHashes`]. - /// - /// This validates versioned hashes according to the Engine API Cancun spec: - /// - pub fn ensure_well_formed_payload( - &self, - payload: ExecutionData, - ) -> Result>, PayloadError> { - let ExecutionData { payload, sidecar } = payload; - - let expected_hash = payload.block_hash(); - - // First parse the block - let sealed_block = payload.try_into_block_with_sidecar(&sidecar)?.seal_slow(); - - // Ensure the hash included in the payload matches the block hash - if expected_hash != sealed_block.hash() { - return Err(PayloadError::BlockHash { - execution: sealed_block.hash(), - consensus: expected_hash, - }) - } - - shanghai::ensure_well_formed_fields( - sealed_block.body(), - self.is_shanghai_active_at_timestamp(sealed_block.timestamp), - )?; - - cancun::ensure_well_formed_fields( - &sealed_block, - sidecar.cancun(), - self.is_cancun_active_at_timestamp(sealed_block.timestamp), - )?; - - prague::ensure_well_formed_fields( - sealed_block.body(), - sidecar.prague(), - self.is_prague_active_at_timestamp(sealed_block.timestamp), - )?; - - Ok(sealed_block) - } -} diff --git a/crates/rpc/rpc-builder/Cargo.toml b/crates/rpc/rpc-builder/Cargo.toml index 16c56bd9ca..db9be7c480 100644 --- a/crates/rpc/rpc-builder/Cargo.toml +++ b/crates/rpc/rpc-builder/Cargo.toml @@ -66,6 +66,7 @@ reth-transaction-pool = { workspace = true, features = ["test-utils"] } reth-rpc-types-compat.workspace = true reth-primitives.workspace = true reth-engine-primitives.workspace = true +reth-node-ethereum.workspace = true alloy-primitives.workspace = true alloy-rpc-types-eth.workspace = true diff --git a/crates/rpc/rpc-builder/tests/it/utils.rs b/crates/rpc/rpc-builder/tests/it/utils.rs index 0f672b3811..22fb52d07d 100644 --- a/crates/rpc/rpc-builder/tests/it/utils.rs +++ b/crates/rpc/rpc-builder/tests/it/utils.rs @@ -4,10 +4,11 @@ use alloy_rpc_types_engine::{ClientCode, ClientVersionV1}; use reth_chainspec::MAINNET; use reth_consensus::noop::NoopConsensus; use reth_engine_primitives::BeaconConsensusEngineHandle; -use reth_ethereum_engine_primitives::{EthEngineTypes, EthereumEngineValidator}; +use reth_ethereum_engine_primitives::EthEngineTypes; use reth_evm::execute::BasicBlockExecutorProvider; use reth_evm_ethereum::EthEvmConfig; use reth_network_api::noop::NoopNetwork; +use reth_node_ethereum::EthereumEngineValidator; use reth_payload_builder::test_utils::spawn_test_payload_service; use reth_provider::test_utils::NoopProvider; use reth_rpc::EthApi; diff --git a/crates/rpc/rpc-engine-api/Cargo.toml b/crates/rpc/rpc-engine-api/Cargo.toml index d21f8dc5c9..630bc13d00 100644 --- a/crates/rpc/rpc-engine-api/Cargo.toml +++ b/crates/rpc/rpc-engine-api/Cargo.toml @@ -53,5 +53,6 @@ reth-primitives-traits.workspace = true reth-payload-builder = { workspace = true, features = ["test-utils"] } reth-testing-utils.workspace = true alloy-rlp.workspace = true +reth-node-ethereum.workspace = true assert_matches.workspace = true diff --git a/crates/rpc/rpc-engine-api/src/engine_api.rs b/crates/rpc/rpc-engine-api/src/engine_api.rs index 7a81d06bad..975b7f2c0c 100644 --- a/crates/rpc/rpc-engine-api/src/engine_api.rs +++ b/crates/rpc/rpc-engine-api/src/engine_api.rs @@ -1160,8 +1160,9 @@ mod tests { use assert_matches::assert_matches; use reth_chainspec::{ChainSpec, EthereumHardfork, MAINNET}; use reth_engine_primitives::BeaconEngineMessage; - use reth_ethereum_engine_primitives::{EthEngineTypes, EthereumEngineValidator}; + use reth_ethereum_engine_primitives::EthEngineTypes; use reth_ethereum_primitives::Block; + use reth_node_ethereum::EthereumEngineValidator; use reth_payload_builder::test_utils::spawn_test_payload_service; use reth_provider::test_utils::MockEthProvider; use reth_tasks::TokioTaskExecutor; diff --git a/examples/custom-engine-types/src/main.rs b/examples/custom-engine-types/src/main.rs index 4d00e5f13c..9efc49ea36 100644 --- a/examples/custom-engine-types/src/main.rs +++ b/examples/custom-engine-types/src/main.rs @@ -37,7 +37,6 @@ use reth::{ BuilderContext, FullNodeTypes, Node, NodeAdapter, NodeBuilder, NodeComponentsBuilder, }, network::NetworkHandle, - payload::ExecutionPayloadValidator, primitives::{Block, EthPrimitives, RecoveredBlock, SealedBlock, TransactionSigned}, providers::{EthStorage, StateProviderFactory}, rpc::{eth::EthApi, types::engine::ExecutionPayload}, @@ -48,7 +47,7 @@ use reth::{ use reth_basic_payload_builder::{BuildArguments, BuildOutcome, PayloadBuilder, PayloadConfig}; use reth_chainspec::{Chain, ChainSpec, ChainSpecProvider}; use reth_engine_local::payload::UnsupportedLocalAttributes; -use reth_ethereum_payload_builder::EthereumBuilderConfig; +use reth_ethereum_payload_builder::{EthereumBuilderConfig, EthereumExecutionPayloadValidator}; use reth_node_api::{ payload::{EngineApiMessageVersion, EngineObjectValidationError, PayloadOrAttributes}, validate_version_specific_fields, AddOnsContext, EngineTypes, EngineValidator, @@ -182,13 +181,13 @@ impl EngineTypes for CustomEngineTypes { /// Custom engine validator #[derive(Debug, Clone)] pub struct CustomEngineValidator { - inner: ExecutionPayloadValidator, + inner: EthereumExecutionPayloadValidator, } impl CustomEngineValidator { /// Instantiates a new validator. pub const fn new(chain_spec: Arc) -> Self { - Self { inner: ExecutionPayloadValidator::new(chain_spec) } + Self { inner: EthereumExecutionPayloadValidator::new(chain_spec) } } /// Returns the chain spec used by the validator.