diff --git a/Cargo.lock b/Cargo.lock index 723dc3a39..bfbd12764 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -174,7 +174,7 @@ dependencies = [ [[package]] name = "alloy-eips" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=76c70fb#76c70fb9d44ace661bbf33408c2527e3874c964e" +source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -184,7 +184,7 @@ dependencies = [ [[package]] name = "alloy-genesis" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=76c70fb#76c70fb9d44ace661bbf33408c2527e3874c964e" +source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -206,7 +206,7 @@ dependencies = [ [[package]] name = "alloy-node-bindings" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=76c70fb#76c70fb9d44ace661bbf33408c2527e3874c964e" +source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d" dependencies = [ "alloy-genesis", "alloy-primitives", @@ -267,7 +267,7 @@ dependencies = [ [[package]] name = "alloy-rpc-engine-types" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=76c70fb#76c70fb9d44ace661bbf33408c2527e3874c964e" +source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -282,7 +282,7 @@ dependencies = [ [[package]] name = "alloy-rpc-trace-types" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=76c70fb#76c70fb9d44ace661bbf33408c2527e3874c964e" +source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -293,7 +293,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=76c70fb#76c70fb9d44ace661bbf33408c2527e3874c964e" +source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -7039,7 +7039,7 @@ dependencies = [ [[package]] name = "revm-inspectors" version = "0.1.0" -source = "git+https://github.com/paradigmxyz/evm-inspectors?rev=75a187b#75a187ba967a29b30af2e5e848073c755068da06" +source = "git+https://github.com/paradigmxyz/evm-inspectors?rev=7068d39#7068d395eb56ee5fee8e6acc7c32d3dbc4616f3b" dependencies = [ "alloy-primitives", "alloy-rpc-trace-types", diff --git a/Cargo.toml b/Cargo.toml index 83988cff7..7577cb47d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -178,7 +178,7 @@ reth-trie = { path = "crates/trie" } # revm revm = { version = "6.1.0", features = ["std", "secp256k1"], default-features = false } revm-primitives = { version = "2.1.0", features = ["std"], default-features = false } -revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = "75a187b" } +revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = "7068d39" } # eth alloy-chains = { version = "0.1", feature = ["serde", "rlp", "arbitrary"] } @@ -187,12 +187,12 @@ alloy-dyn-abi = "0.6" alloy-sol-types = "0.6" alloy-rlp = "0.3" alloy-trie = "0.3" -alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "76c70fb" } -alloy-rpc-trace-types = { git = "https://github.com/alloy-rs/alloy", rev = "76c70fb" } -alloy-rpc-engine-types = { git = "https://github.com/alloy-rs/alloy", rev = "76c70fb" } -alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "76c70fb" } -alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "76c70fb" } -alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "76c70fb" } +alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } +alloy-rpc-trace-types = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } +alloy-rpc-engine-types = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } +alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } +alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } +alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" } ethers-core = { version = "2.0", default-features = false } ethers-providers = { version = "2.0", default-features = false } ethers-signers = { version = "2.0", default-features = false } diff --git a/bin/reth/src/commands/debug_cmd/build_block.rs b/bin/reth/src/commands/debug_cmd/build_block.rs index 1990a6b3d..c25d95296 100644 --- a/bin/reth/src/commands/debug_cmd/build_block.rs +++ b/bin/reth/src/commands/debug_cmd/build_block.rs @@ -296,8 +296,9 @@ impl Command { ); #[cfg(feature = "optimism")] - let payload_builder = reth_optimism_payload_builder::OptimismPayloadBuilder::default() - .compute_pending_block(); + let payload_builder = + reth_optimism_payload_builder::OptimismPayloadBuilder::new(self.chain.clone()) + .compute_pending_block(); #[cfg(not(feature = "optimism"))] let payload_builder = reth_ethereum_payload_builder::EthereumPayloadBuilder::default(); diff --git a/bin/reth/src/commands/debug_cmd/replay_engine.rs b/bin/reth/src/commands/debug_cmd/replay_engine.rs index a56ca9cee..1f10629a3 100644 --- a/bin/reth/src/commands/debug_cmd/replay_engine.rs +++ b/bin/reth/src/commands/debug_cmd/replay_engine.rs @@ -169,7 +169,8 @@ impl Command { // Optimism's payload builder is implemented on the OptimismPayloadBuilder type. #[cfg(feature = "optimism")] - let payload_builder = reth_optimism_payload_builder::OptimismPayloadBuilder::default(); + let payload_builder = + reth_optimism_payload_builder::OptimismPayloadBuilder::new(self.chain.clone()); let payload_generator = BasicPayloadJobGenerator::with_builder( blockchain_db.clone(), diff --git a/crates/node-api/src/engine/mod.rs b/crates/node-api/src/engine/mod.rs index 6ef3f3a0c..c63dad102 100644 --- a/crates/node-api/src/engine/mod.rs +++ b/crates/node-api/src/engine/mod.rs @@ -6,6 +6,7 @@ use reth_primitives::{ChainSpec, Hardfork}; /// Contains traits to abstract over payload attributes types and default implementations of the /// [PayloadAttributes] trait for ethereum mainnet and optimism types. pub mod traits; +use serde::{de::DeserializeOwned, ser::Serialize}; pub use traits::{BuiltPayload, PayloadAttributes, PayloadBuilderAttributes}; /// Contains error types used in the traits defined in this crate. @@ -18,7 +19,7 @@ pub use payload::PayloadOrAttributes; /// The types that are used by the engine API. pub trait EngineTypes: - serde::de::DeserializeOwned + fmt::Debug + Unpin + Send + Sync + Clone + serde::de::DeserializeOwned + Serialize + fmt::Debug + Unpin + Send + Sync + Clone { /// The RPC payload attributes type the CL node emits via the engine API. type PayloadAttributes: PayloadAttributes + Unpin; @@ -29,7 +30,19 @@ pub trait EngineTypes: + Unpin; /// The built payload type. - type BuiltPayload: BuiltPayload + Clone + Unpin; + type BuiltPayload: BuiltPayload + + Clone + + Unpin + + TryInto + + TryInto + + TryInto; + + /// Execution Payload V1 type. + type ExecutionPayloadV1: DeserializeOwned + Serialize + Clone + Unpin + Send + Sync + 'static; + /// Execution Payload V2 type. + type ExecutionPayloadV2: DeserializeOwned + Serialize + Clone + Unpin + Send + Sync + 'static; + /// Execution Payload V3 type. + type ExecutionPayloadV3: DeserializeOwned + Serialize + Clone + Unpin + Send + Sync + 'static; /// Validates the presence or exclusion of fork-specific fields based on the payload attributes /// and the message version. diff --git a/crates/node-api/src/engine/traits.rs b/crates/node-api/src/engine/traits.rs index e80323dd4..076a7650e 100644 --- a/crates/node-api/src/engine/traits.rs +++ b/crates/node-api/src/engine/traits.rs @@ -4,12 +4,8 @@ use reth_primitives::{ Address, ChainSpec, Header, SealedBlock, Withdrawals, B256, U256, }; use reth_rpc_types::{ - engine::{ - ExecutionPayloadEnvelopeV2, ExecutionPayloadEnvelopeV3, OptimismPayloadAttributes, - PayloadAttributes as EthPayloadAttributes, PayloadId, - }, + engine::{OptimismPayloadAttributes, PayloadAttributes as EthPayloadAttributes, PayloadId}, withdrawal::Withdrawal, - ExecutionPayloadV1, }; /// Represents a built payload type that contains a built [SealedBlock] and can be converted into @@ -20,15 +16,6 @@ pub trait BuiltPayload: Send + Sync + std::fmt::Debug { /// Returns the fees collected for the built block fn fees(&self) -> U256; - - /// Converts the type into the response expected by `engine_getPayloadV1` - fn into_v1_payload(self) -> ExecutionPayloadV1; - - /// Converts the type into the response expected by `engine_getPayloadV2` - fn into_v2_payload(self) -> ExecutionPayloadEnvelopeV2; - - /// Converts the type into the response expected by `engine_getPayloadV3` - fn into_v3_payload(self) -> ExecutionPayloadEnvelopeV3; } /// This can be implemented by types that describe a currently running payload job. diff --git a/crates/node-ethereum/src/engine.rs b/crates/node-ethereum/src/engine.rs index eb5f65b79..07e2555e6 100644 --- a/crates/node-ethereum/src/engine.rs +++ b/crates/node-ethereum/src/engine.rs @@ -4,10 +4,16 @@ use reth_node_api::{ }; use reth_payload_builder::{EthBuiltPayload, EthPayloadBuilderAttributes}; use reth_primitives::ChainSpec; -use reth_rpc_types::engine::PayloadAttributes as EthPayloadAttributes; +use reth_rpc_types::{ + engine::{ + ExecutionPayloadEnvelopeV2, ExecutionPayloadEnvelopeV3, + PayloadAttributes as EthPayloadAttributes, + }, + ExecutionPayloadV1, +}; /// The types used in the default mainnet ethereum beacon consensus engine. -#[derive(Debug, Default, Clone, serde::Deserialize)] +#[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)] #[non_exhaustive] pub struct EthEngineTypes; @@ -15,6 +21,9 @@ impl EngineTypes for EthEngineTypes { type PayloadAttributes = EthPayloadAttributes; type PayloadBuilderAttributes = EthPayloadBuilderAttributes; type BuiltPayload = EthBuiltPayload; + type ExecutionPayloadV1 = ExecutionPayloadV1; + type ExecutionPayloadV2 = ExecutionPayloadEnvelopeV2; + type ExecutionPayloadV3 = ExecutionPayloadEnvelopeV3; fn validate_version_specific_fields( chain_spec: &ChainSpec, diff --git a/crates/node-optimism/src/engine.rs b/crates/node-optimism/src/engine.rs index a65427e76..ccfc4b792 100644 --- a/crates/node-optimism/src/engine.rs +++ b/crates/node-optimism/src/engine.rs @@ -2,19 +2,27 @@ use reth_node_api::{ engine::validate_parent_beacon_block_root_presence, AttributesValidationError, EngineApiMessageVersion, EngineTypes, PayloadOrAttributes, }; -use reth_payload_builder::{EthBuiltPayload, OptimismPayloadBuilderAttributes}; +use reth_payload_builder::{OptimismBuiltPayload, OptimismPayloadBuilderAttributes}; use reth_primitives::{ChainSpec, Hardfork}; -use reth_rpc_types::engine::OptimismPayloadAttributes; +use reth_rpc_types::{ + engine::{ + ExecutionPayloadEnvelopeV2, OptimismExecutionPayloadEnvelopeV3, OptimismPayloadAttributes, + }, + ExecutionPayloadV1, +}; /// The types used in the optimism beacon consensus engine. -#[derive(Debug, Default, Clone, serde::Deserialize)] +#[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)] #[non_exhaustive] pub struct OptimismEngineTypes; impl EngineTypes for OptimismEngineTypes { type PayloadAttributes = OptimismPayloadAttributes; type PayloadBuilderAttributes = OptimismPayloadBuilderAttributes; - type BuiltPayload = EthBuiltPayload; + type BuiltPayload = OptimismBuiltPayload; + type ExecutionPayloadV1 = ExecutionPayloadV1; + type ExecutionPayloadV2 = ExecutionPayloadEnvelopeV2; + type ExecutionPayloadV3 = OptimismExecutionPayloadEnvelopeV3; fn validate_version_specific_fields( chain_spec: &ChainSpec, diff --git a/crates/node-optimism/src/node.rs b/crates/node-optimism/src/node.rs index 545c88276..6171305ae 100644 --- a/crates/node-optimism/src/node.rs +++ b/crates/node-optimism/src/node.rs @@ -172,8 +172,9 @@ where ctx: &BuilderContext, pool: Pool, ) -> eyre::Result> { - let payload_builder = reth_optimism_payload_builder::OptimismPayloadBuilder::default() - .set_compute_pending_block(self.compute_pending_block); + let payload_builder = + reth_optimism_payload_builder::OptimismPayloadBuilder::new(ctx.chain_spec()) + .set_compute_pending_block(self.compute_pending_block); let conf = ctx.payload_builder_config(); let payload_job_config = BasicPayloadJobGeneratorConfig::default() diff --git a/crates/payload/basic/src/lib.rs b/crates/payload/basic/src/lib.rs index fb7d9d42e..bb0efc3d2 100644 --- a/crates/payload/basic/src/lib.rs +++ b/crates/payload/basic/src/lib.rs @@ -15,8 +15,8 @@ use futures_util::FutureExt; use reth_interfaces::RethResult; use reth_node_api::{BuiltPayload, PayloadBuilderAttributes}; use reth_payload_builder::{ - database::CachedReads, error::PayloadBuilderError, EthBuiltPayload, KeepPayloadJobAlive, - PayloadId, PayloadJob, PayloadJobGenerator, + database::CachedReads, error::PayloadBuilderError, KeepPayloadJobAlive, PayloadId, PayloadJob, + PayloadJobGenerator, }; use reth_primitives::{ bytes::BytesMut, @@ -890,7 +890,7 @@ where /// /// This compares the total fees of the blocks, higher is better. #[inline(always)] -pub fn is_better_payload(best_payload: Option<&EthBuiltPayload>, new_fees: U256) -> bool { +pub fn is_better_payload(best_payload: Option, new_fees: U256) -> bool { if let Some(best_payload) = best_payload { new_fees > best_payload.fees() } else { diff --git a/crates/payload/builder/src/lib.rs b/crates/payload/builder/src/lib.rs index a14b9308f..7a859833e 100644 --- a/crates/payload/builder/src/lib.rs +++ b/crates/payload/builder/src/lib.rs @@ -115,7 +115,7 @@ pub mod noop; #[cfg(any(test, feature = "test-utils"))] pub mod test_utils; -pub use optimism::OptimismPayloadBuilderAttributes; +pub use optimism::{OptimismBuiltPayload, OptimismPayloadBuilderAttributes}; pub use payload::{EthBuiltPayload, EthPayloadBuilderAttributes}; pub use reth_rpc_types::engine::PayloadId; pub use service::{PayloadBuilderHandle, PayloadBuilderService, PayloadStore}; diff --git a/crates/payload/builder/src/optimism.rs b/crates/payload/builder/src/optimism.rs index d4bb14059..f1fdf8053 100644 --- a/crates/payload/builder/src/optimism.rs +++ b/crates/payload/builder/src/optimism.rs @@ -1,13 +1,21 @@ use crate::EthPayloadBuilderAttributes; use alloy_rlp::{Encodable, Error as DecodeError}; -use reth_node_api::PayloadBuilderAttributes; +use reth_node_api::{BuiltPayload, PayloadBuilderAttributes}; use reth_primitives::{ revm::config::revm_spec_by_timestamp_after_merge, revm_primitives::{BlobExcessGasAndPrice, BlockEnv, CfgEnv, CfgEnvWithHandlerCfg, SpecId}, - Address, ChainSpec, Header, TransactionSigned, Withdrawals, B256, U256, + Address, BlobTransactionSidecar, ChainSpec, Header, SealedBlock, TransactionSigned, + Withdrawals, B256, U256, }; -use reth_rpc_types::engine::{OptimismPayloadAttributes, PayloadId}; -use reth_rpc_types_compat::engine::payload::convert_standalone_withdraw_to_withdrawal; +use reth_rpc_types::engine::{ + ExecutionPayloadEnvelopeV2, ExecutionPayloadV1, OptimismExecutionPayloadEnvelopeV3, + OptimismPayloadAttributes, PayloadId, +}; +use reth_rpc_types_compat::engine::payload::{ + block_to_payload_v3, convert_block_to_payload_field_v2, + convert_standalone_withdraw_to_withdrawal, try_block_to_payload_v1, +}; +use std::sync::Arc; /// Optimism Payload Builder Attributes #[derive(Debug, Clone, PartialEq, Eq)] @@ -152,6 +160,126 @@ impl PayloadBuilderAttributes for OptimismPayloadBuilderAttributes { } } +/// Contains the built payload. +#[derive(Debug, Clone)] +pub struct OptimismBuiltPayload { + /// Identifier of the payload + pub(crate) id: PayloadId, + /// The built block + pub(crate) block: SealedBlock, + /// The fees of the block + pub(crate) fees: U256, + /// The blobs, proofs, and commitments in the block. If the block is pre-cancun, this will be + /// empty. + pub(crate) sidecars: Vec, + /// The rollup's chainspec. + pub(crate) chain_spec: Arc, + /// The payload attributes. + pub(crate) attributes: OptimismPayloadBuilderAttributes, +} + +// === impl BuiltPayload === + +impl OptimismBuiltPayload { + /// Initializes the payload with the given initial block. + pub fn new( + id: PayloadId, + block: SealedBlock, + fees: U256, + chain_spec: Arc, + attributes: OptimismPayloadBuilderAttributes, + ) -> Self { + Self { id, block, fees, sidecars: Vec::new(), chain_spec, attributes } + } + + /// Returns the identifier of the payload. + pub fn id(&self) -> PayloadId { + self.id + } + + /// Returns the built block(sealed) + pub fn block(&self) -> &SealedBlock { + &self.block + } + + /// Fees of the block + pub fn fees(&self) -> U256 { + self.fees + } + + /// Adds sidecars to the payload. + pub fn extend_sidecars(&mut self, sidecars: Vec) { + self.sidecars.extend(sidecars) + } +} + +impl BuiltPayload for OptimismBuiltPayload { + fn block(&self) -> &SealedBlock { + &self.block + } + + fn fees(&self) -> U256 { + self.fees + } +} + +impl<'a> BuiltPayload for &'a OptimismBuiltPayload { + fn block(&self) -> &SealedBlock { + (**self).block() + } + + fn fees(&self) -> U256 { + (**self).fees() + } +} + +// V1 engine_getPayloadV1 response +impl From for ExecutionPayloadV1 { + fn from(value: OptimismBuiltPayload) -> Self { + try_block_to_payload_v1(value.block) + } +} + +// V2 engine_getPayloadV2 response +impl From for ExecutionPayloadEnvelopeV2 { + fn from(value: OptimismBuiltPayload) -> Self { + let OptimismBuiltPayload { block, fees, .. } = value; + + ExecutionPayloadEnvelopeV2 { + block_value: fees, + execution_payload: convert_block_to_payload_field_v2(block), + } + } +} + +impl From for OptimismExecutionPayloadEnvelopeV3 { + fn from(value: OptimismBuiltPayload) -> Self { + let OptimismBuiltPayload { block, fees, sidecars, chain_spec, attributes, .. } = value; + + let parent_beacon_block_root = + if chain_spec.is_cancun_active_at_timestamp(attributes.timestamp()) { + attributes.parent_beacon_block_root().unwrap_or(B256::ZERO) + } else { + B256::ZERO + }; + OptimismExecutionPayloadEnvelopeV3 { + execution_payload: block_to_payload_v3(block.clone()), + block_value: fees, + // From the engine API spec: + // + // > Client software **MAY** use any heuristics to decide whether to set + // `shouldOverrideBuilder` flag or not. If client software does not implement any + // heuristic this flag **SHOULD** be set to `false`. + // + // Spec: + // + should_override_builder: false, + blobs_bundle: sidecars.into_iter().map(Into::into).collect::>().into(), + parent_beacon_block_root, + } + } +} + /// Generates the payload id for the configured payload from the [OptimismPayloadAttributes]. /// /// Returns an 8-byte identifier by hashing the payload components with sha256 hash. diff --git a/crates/payload/builder/src/payload.rs b/crates/payload/builder/src/payload.rs index 78eec2133..165d70e2f 100644 --- a/crates/payload/builder/src/payload.rs +++ b/crates/payload/builder/src/payload.rs @@ -62,21 +62,6 @@ impl EthBuiltPayload { pub fn extend_sidecars(&mut self, sidecars: Vec) { self.sidecars.extend(sidecars) } - - /// Converts the type into the response expected by `engine_getPayloadV1` - pub fn into_v1_payload(self) -> ExecutionPayloadV1 { - self.into() - } - - /// Converts the type into the response expected by `engine_getPayloadV2` - pub fn into_v2_payload(self) -> ExecutionPayloadEnvelopeV2 { - self.into() - } - - /// Converts the type into the response expected by `engine_getPayloadV3` - pub fn into_v3_payload(self) -> ExecutionPayloadEnvelopeV3 { - self.into() - } } impl BuiltPayload for EthBuiltPayload { @@ -87,17 +72,15 @@ impl BuiltPayload for EthBuiltPayload { fn fees(&self) -> U256 { self.fees } +} - fn into_v1_payload(self) -> ExecutionPayloadV1 { - self.into() +impl<'a> BuiltPayload for &'a EthBuiltPayload { + fn block(&self) -> &SealedBlock { + (**self).block() } - fn into_v2_payload(self) -> ExecutionPayloadEnvelopeV2 { - self.into() - } - - fn into_v3_payload(self) -> ExecutionPayloadEnvelopeV3 { - self.into() + fn fees(&self) -> U256 { + (**self).fees() } } @@ -137,10 +120,6 @@ impl From for ExecutionPayloadEnvelopeV3 { // should_override_builder: false, blobs_bundle: sidecars.into_iter().map(Into::into).collect::>().into(), - // Optimism-specific: Post-cancun, the parent beacon block root is included in the - // enveloped payload. We set this as `None` here so that optimism-specific - // handling can fill the value. - parent_beacon_block_root: None, } } } diff --git a/crates/payload/optimism/src/lib.rs b/crates/payload/optimism/src/lib.rs index ac431d11f..9e6940e2a 100644 --- a/crates/payload/optimism/src/lib.rs +++ b/crates/payload/optimism/src/lib.rs @@ -18,14 +18,14 @@ mod builder { use crate::error::OptimismPayloadBuilderError; use reth_basic_payload_builder::*; use reth_payload_builder::{ - error::PayloadBuilderError, EthBuiltPayload, OptimismPayloadBuilderAttributes, + error::PayloadBuilderError, OptimismBuiltPayload, OptimismPayloadBuilderAttributes, }; use reth_primitives::{ constants::{BEACON_NONCE, EMPTY_RECEIPTS, EMPTY_TRANSACTIONS}, eip4844::calculate_excess_blob_gas, proofs, revm::env::tx_env_with_recovered, - Block, Hardfork, Header, IntoRecoveredTransaction, Receipt, Receipts, TxType, + Block, ChainSpec, Hardfork, Header, IntoRecoveredTransaction, Receipt, Receipts, TxType, EMPTY_OMMER_ROOT_HASH, U256, }; use reth_provider::{BundleStateWithReceipts, StateProviderFactory}; @@ -36,18 +36,25 @@ mod builder { primitives::{EVMError, EnvWithHandlerCfg, InvalidTransaction, ResultAndState}, DatabaseCommit, State, }; + use std::sync::Arc; use tracing::{debug, trace, warn}; /// Optimism's payload builder - #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] - #[non_exhaustive] + #[derive(Debug, Clone, PartialEq, Eq)] pub struct OptimismPayloadBuilder { /// The rollup's compute pending block configuration option. // TODO(clabby): Implement this feature. compute_pending_block: bool, + /// The rollup's chain spec. + chain_spec: Arc, } impl OptimismPayloadBuilder { + /// OptimismPayloadBuilder constructor. + pub fn new(chain_spec: Arc) -> Self { + Self { compute_pending_block: true, chain_spec } + } + /// Sets the rollup's compute pending block configuration option. pub fn set_compute_pending_block(mut self, compute_pending_block: bool) -> Self { self.compute_pending_block = compute_pending_block; @@ -63,6 +70,12 @@ mod builder { pub fn is_compute_pending_block(&self) -> bool { self.compute_pending_block } + + /// Sets the rollup's chainspec. + pub fn set_chain_spec(mut self, chain_spec: Arc) -> Self { + self.chain_spec = chain_spec; + self + } } /// Implementation of the [PayloadBuilder] trait for [OptimismPayloadBuilder]. @@ -72,19 +85,29 @@ mod builder { Pool: TransactionPool, { type Attributes = OptimismPayloadBuilderAttributes; - type BuiltPayload = EthBuiltPayload; + type BuiltPayload = OptimismBuiltPayload; fn try_build( &self, - args: BuildArguments, - ) -> Result, PayloadBuilderError> { + args: BuildArguments< + Pool, + Client, + OptimismPayloadBuilderAttributes, + OptimismBuiltPayload, + >, + ) -> Result, PayloadBuilderError> { optimism_payload_builder(args, self.compute_pending_block) } fn on_missing_payload( &self, - args: BuildArguments, - ) -> Option { + args: BuildArguments< + Pool, + Client, + OptimismPayloadBuilderAttributes, + OptimismBuiltPayload, + >, + ) -> Option { // In Optimism, the PayloadAttributes can specify a `no_tx_pool` option that implies we // should not pull transactions from the tx pool. In this case, we build the payload // upfront with the list of transactions sent in the attributes without caring about @@ -102,7 +125,7 @@ mod builder { fn build_empty_payload( client: &Client, config: PayloadConfig, - ) -> Result { + ) -> Result { let extra_data = config.extra_data(); let PayloadConfig { initialized_block_env, @@ -205,10 +228,12 @@ mod builder { let block = Block { header, body: vec![], ommers: vec![], withdrawals }; let sealed_block = block.seal_slow(); - Ok(EthBuiltPayload::new( + Ok(OptimismBuiltPayload::new( attributes.payload_attributes.payload_id(), sealed_block, U256::ZERO, + chain_spec, + attributes, )) } } @@ -223,9 +248,9 @@ mod builder { /// a result indicating success with the payload or an error in case of failure. #[inline] pub(crate) fn optimism_payload_builder( - args: BuildArguments, + args: BuildArguments, _compute_pending_block: bool, - ) -> Result, PayloadBuilderError> + ) -> Result, PayloadBuilderError> where Client: StateProviderFactory, Pool: TransactionPool, @@ -491,7 +516,7 @@ mod builder { &mut db, &chain_spec, attributes.payload_attributes.timestamp, - attributes.payload_attributes.withdrawals, + attributes.clone().payload_attributes.withdrawals, )?; // merge all transitions into bundle state, this would apply the withdrawal balance changes @@ -567,8 +592,13 @@ mod builder { let sealed_block = block.seal_slow(); debug!(target: "payload_builder", ?sealed_block, "sealed built block"); - let mut payload = - EthBuiltPayload::new(attributes.payload_attributes.id, sealed_block, total_fees); + let mut payload = OptimismBuiltPayload::new( + attributes.payload_attributes.id, + sealed_block, + total_fees, + chain_spec, + attributes, + ); // extend the payload with the blob sidecars from the executed txs payload.extend_sidecars(blob_sidecars); diff --git a/crates/primitives/src/chain/spec.rs b/crates/primitives/src/chain/spec.rs index a1b535caf..1cf1f3a6b 100644 --- a/crates/primitives/src/chain/spec.rs +++ b/crates/primitives/src/chain/spec.rs @@ -353,7 +353,7 @@ pub static BASE_MAINNET: Lazy> = Lazy::new(|| { /// A wrapper around [BaseFeeParams] that allows for specifying constant or dynamic EIP-1559 /// parameters based on the active [Hardfork]. -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] #[serde(untagged)] pub enum BaseFeeParamsKind { /// Constant [BaseFeeParams]; used for chains that don't have dynamic EIP-1559 parameters @@ -377,7 +377,7 @@ impl From for BaseFeeParamsKind { /// A type alias to a vector of tuples of [Hardfork] and [BaseFeeParams], sorted by [Hardfork] /// activation order. This is used to specify dynamic EIP-1559 parameters for chains like Optimism. -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct ForkBaseFeeParams(Vec<(Hardfork, BaseFeeParams)>); impl From> for ForkBaseFeeParams { @@ -456,7 +456,7 @@ impl BaseFeeParams { /// - Meta-information about the chain (the chain ID) /// - The genesis block of the chain ([`Genesis`]) /// - What hardforks are activated, and under which conditions -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] pub struct ChainSpec { /// The chain ID pub chain: Chain, diff --git a/crates/rpc/rpc-api/src/engine.rs b/crates/rpc/rpc-api/src/engine.rs index 50f4b89b0..0223b44d7 100644 --- a/crates/rpc/rpc-api/src/engine.rs +++ b/crates/rpc/rpc-api/src/engine.rs @@ -8,9 +8,8 @@ use reth_node_api::EngineTypes; use reth_primitives::{Address, BlockHash, BlockId, BlockNumberOrTag, Bytes, B256, U256, U64}; use reth_rpc_types::{ engine::{ - ExecutionPayloadBodiesV1, ExecutionPayloadEnvelopeV2, ExecutionPayloadEnvelopeV3, - ExecutionPayloadInputV2, ExecutionPayloadV1, ExecutionPayloadV3, ForkchoiceState, - ForkchoiceUpdated, PayloadId, PayloadStatus, TransitionConfiguration, + ExecutionPayloadBodiesV1, ExecutionPayloadInputV2, ExecutionPayloadV1, ExecutionPayloadV3, + ForkchoiceState, ForkchoiceUpdated, PayloadId, PayloadStatus, TransitionConfiguration, }, state::StateOverride, BlockOverrides, Filter, Log, RichBlock, SyncStatus, TransactionRequest, @@ -96,7 +95,7 @@ pub trait EngineApi { /// Note: /// > Provider software MAY stop the corresponding build process after serving this call. #[method(name = "getPayloadV1")] - async fn get_payload_v1(&self, payload_id: PayloadId) -> RpcResult; + async fn get_payload_v1(&self, payload_id: PayloadId) -> RpcResult; /// See also /// @@ -104,7 +103,7 @@ pub trait EngineApi { /// payload build process at the time of receiving this call. Note: /// > Provider software MAY stop the corresponding build process after serving this call. #[method(name = "getPayloadV2")] - async fn get_payload_v2(&self, payload_id: PayloadId) -> RpcResult; + async fn get_payload_v2(&self, payload_id: PayloadId) -> RpcResult; /// Post Cancun payload handler which also returns a blobs bundle. /// @@ -114,7 +113,7 @@ pub trait EngineApi { /// payload build process at the time of receiving this call. Note: /// > Provider software MAY stop the corresponding build process after serving this call. #[method(name = "getPayloadV3")] - async fn get_payload_v3(&self, payload_id: PayloadId) -> RpcResult; + async fn get_payload_v3(&self, payload_id: PayloadId) -> RpcResult; /// See also #[method(name = "getPayloadBodiesByHashV1")] diff --git a/crates/rpc/rpc-engine-api/src/engine_api.rs b/crates/rpc/rpc-engine-api/src/engine_api.rs index 60a299e6f..32a267f53 100644 --- a/crates/rpc/rpc-engine-api/src/engine_api.rs +++ b/crates/rpc/rpc-engine-api/src/engine_api.rs @@ -4,17 +4,17 @@ use jsonrpsee_core::RpcResult; use reth_beacon_consensus::BeaconConsensusEngineHandle; use reth_interfaces::consensus::ForkchoiceState; use reth_node_api::{ - validate_payload_timestamp, BuiltPayload, EngineApiMessageVersion, EngineTypes, - PayloadAttributes, PayloadBuilderAttributes, PayloadOrAttributes, + validate_payload_timestamp, EngineApiMessageVersion, EngineTypes, PayloadAttributes, + PayloadBuilderAttributes, PayloadOrAttributes, }; use reth_payload_builder::PayloadStore; use reth_primitives::{BlockHash, BlockHashOrNumber, BlockNumber, ChainSpec, Hardfork, B256, U64}; use reth_provider::{BlockReader, EvmEnvProvider, HeaderProvider, StateProviderFactory}; use reth_rpc_api::EngineApiServer; use reth_rpc_types::engine::{ - CancunPayloadFields, ExecutionPayload, ExecutionPayloadBodiesV1, ExecutionPayloadEnvelopeV2, - ExecutionPayloadEnvelopeV3, ExecutionPayloadInputV2, ExecutionPayloadV1, ExecutionPayloadV3, - ForkchoiceUpdated, PayloadId, PayloadStatus, TransitionConfiguration, CAPABILITIES, + CancunPayloadFields, ExecutionPayload, ExecutionPayloadBodiesV1, ExecutionPayloadInputV2, + ExecutionPayloadV1, ExecutionPayloadV3, ForkchoiceUpdated, PayloadId, PayloadStatus, + TransitionConfiguration, CAPABILITIES, }; use reth_rpc_types_compat::engine::payload::{ convert_payload_input_v2_to_payload, convert_to_payload_body_v1, @@ -22,7 +22,7 @@ use reth_rpc_types_compat::engine::payload::{ use reth_tasks::TaskSpawner; use std::{sync::Arc, time::Instant}; use tokio::sync::oneshot; -use tracing::trace; +use tracing::{trace, warn}; /// The Engine API response sender. pub type EngineApiSender = oneshot::Sender>; @@ -202,14 +202,18 @@ where pub async fn get_payload_v1( &self, payload_id: PayloadId, - ) -> EngineApiResult { - Ok(self - .inner + ) -> EngineApiResult { + self.inner .payload_store .resolve(payload_id) .await .ok_or(EngineApiError::UnknownPayload)? - .map(|payload| payload.into_v1_payload())?) + .map_err(|_| EngineApiError::UnknownPayload)? + .try_into() + .map_err(|_| { + warn!("could not transform built payload into ExecutionPayloadV1"); + EngineApiError::UnknownPayload + }) } /// Returns the most recent version of the payload that is available in the corresponding @@ -222,7 +226,7 @@ where pub async fn get_payload_v2( &self, payload_id: PayloadId, - ) -> EngineApiResult { + ) -> EngineApiResult { // First we fetch the payload attributes to check the timestamp let attributes = self.get_payload_attributes(payload_id).await?; @@ -234,13 +238,17 @@ where )?; // Now resolve the payload - Ok(self - .inner + self.inner .payload_store .resolve(payload_id) .await .ok_or(EngineApiError::UnknownPayload)? - .map(|payload| payload.into_v2_payload())?) + .map_err(|_| EngineApiError::UnknownPayload)? + .try_into() + .map_err(|_| { + warn!("could not transform built payload into ExecutionPayloadV2"); + EngineApiError::UnknownPayload + }) } /// Returns the most recent version of the payload that is available in the corresponding @@ -253,7 +261,7 @@ where pub async fn get_payload_v3( &self, payload_id: PayloadId, - ) -> EngineApiResult { + ) -> EngineApiResult { // First we fetch the payload attributes to check the timestamp let attributes = self.get_payload_attributes(payload_id).await?; @@ -265,25 +273,17 @@ where )?; // Now resolve the payload - let mut resolved_payload = self - .inner + self.inner .payload_store .resolve(payload_id) .await .ok_or(EngineApiError::UnknownPayload)? - .map(|payload| payload.into_v3_payload())?; - - // After `Cancun` is enabled on optimism, an extra field `parent_beacon_block_root` is - // included in the enveloped V3 payload. On ethereum, this field is not included. - if self.inner.chain_spec.is_optimism() && - self.inner - .chain_spec - .is_fork_active_at_timestamp(Hardfork::Cancun, attributes.timestamp()) - { - resolved_payload.parent_beacon_block_root = attributes.parent_beacon_block_root(); - } - - Ok(resolved_payload) + .map_err(|_| EngineApiError::UnknownPayload)? + .try_into() + .map_err(|_| { + warn!("could not transform built payload into ExecutionPayloadV2"); + EngineApiError::UnknownPayload + }) } /// Returns the execution payload bodies by the range starting at `start`, containing `count` @@ -581,7 +581,10 @@ where /// /// Note: /// > Provider software MAY stop the corresponding build process after serving this call. - async fn get_payload_v1(&self, payload_id: PayloadId) -> RpcResult { + async fn get_payload_v1( + &self, + payload_id: PayloadId, + ) -> RpcResult { trace!(target: "rpc::engine", "Serving engine_getPayloadV1"); let start = Instant::now(); let res = EngineApi::get_payload_v1(self, payload_id).await; @@ -598,7 +601,10 @@ where /// /// Note: /// > Provider software MAY stop the corresponding build process after serving this call. - async fn get_payload_v2(&self, payload_id: PayloadId) -> RpcResult { + async fn get_payload_v2( + &self, + payload_id: PayloadId, + ) -> RpcResult { trace!(target: "rpc::engine", "Serving engine_getPayloadV2"); let start = Instant::now(); let res = EngineApi::get_payload_v2(self, payload_id).await; @@ -615,7 +621,10 @@ where /// /// Note: /// > Provider software MAY stop the corresponding build process after serving this call. - async fn get_payload_v3(&self, payload_id: PayloadId) -> RpcResult { + async fn get_payload_v3( + &self, + payload_id: PayloadId, + ) -> RpcResult { trace!(target: "rpc::engine", "Serving engine_getPayloadV3"); let start = Instant::now(); let res = EngineApi::get_payload_v3(self, payload_id).await; diff --git a/deny.toml b/deny.toml index ca9b3a734..48cada1ff 100644 --- a/deny.toml +++ b/deny.toml @@ -45,6 +45,7 @@ allow = [ "ISC", "Unicode-DFS-2016", "Unlicense", + "Unicode-3.0", # https://github.com/briansmith/ring/issues/902 "LicenseRef-ring", # https://github.com/briansmith/webpki/issues/148 diff --git a/examples/custom-node/src/main.rs b/examples/custom-node/src/main.rs index 076b864e5..b494c5cbf 100644 --- a/examples/custom-node/src/main.rs +++ b/examples/custom-node/src/main.rs @@ -48,8 +48,12 @@ use reth_payload_builder::{ }; use reth_primitives::{Address, ChainSpec, Genesis, Header, Withdrawals, B256}; use reth_rpc_types::{ - engine::{PayloadAttributes as EthPayloadAttributes, PayloadId}, + engine::{ + ExecutionPayloadEnvelopeV2, ExecutionPayloadEnvelopeV3, + PayloadAttributes as EthPayloadAttributes, PayloadId, + }, withdrawal::Withdrawal, + ExecutionPayloadV1, }; use reth_tracing::{RethTracer, Tracer}; use serde::{Deserialize, Serialize}; @@ -95,7 +99,9 @@ impl PayloadAttributes for CustomPayloadAttributes { // custom validation logic - ensure that the custom field is not zero if self.custom == 0 { - return Err(AttributesValidationError::invalid_params(CustomError::CustomFieldIsNotZero)) + return Err(AttributesValidationError::invalid_params( + CustomError::CustomFieldIsNotZero, + )); } Ok(()) @@ -153,7 +159,7 @@ impl PayloadBuilderAttributes for CustomPayloadBuilderAttributes { /// Custom engine types - uses a custom payload attributes RPC type, but uses the default /// payload builder attributes type. -#[derive(Clone, Debug, Default, Deserialize)] +#[derive(Clone, Debug, Default, Deserialize, Serialize)] #[non_exhaustive] pub struct CustomEngineTypes; @@ -161,6 +167,9 @@ impl EngineTypes for CustomEngineTypes { type PayloadAttributes = CustomPayloadAttributes; type PayloadBuilderAttributes = CustomPayloadBuilderAttributes; type BuiltPayload = EthBuiltPayload; + type ExecutionPayloadV1 = ExecutionPayloadV1; + type ExecutionPayloadV2 = ExecutionPayloadEnvelopeV2; + type ExecutionPayloadV3 = ExecutionPayloadEnvelopeV3; fn validate_version_specific_fields( chain_spec: &ChainSpec,