diff --git a/Cargo.lock b/Cargo.lock index 0a1e7542c91d..a02801ecc77b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8567,7 +8567,9 @@ dependencies = [ "alloy-eips", "alloy-primitives", "alloy-rpc-types-debug", + "alloy-rpc-types-engine", "alloy-rpc-types-eth", + "jsonrpsee", "jsonrpsee-core", "jsonrpsee-types", "op-alloy-consensus", diff --git a/crates/optimism/rpc/Cargo.toml b/crates/optimism/rpc/Cargo.toml index ffc3447f0dba..bd77a1e4e755 100644 --- a/crates/optimism/rpc/Cargo.toml +++ b/crates/optimism/rpc/Cargo.toml @@ -43,6 +43,7 @@ alloy-primitives.workspace = true alloy-rpc-types-eth.workspace = true alloy-rpc-types-debug.workspace = true alloy-consensus.workspace = true +alloy-rpc-types-engine.workspace = true op-alloy-network.workspace = true op-alloy-rpc-types.workspace = true op-alloy-rpc-types-engine.workspace = true @@ -58,6 +59,7 @@ reqwest = { workspace = true, features = ["rustls-tls-native-roots"] } # rpc jsonrpsee-core.workspace = true jsonrpsee-types.workspace = true +jsonrpsee.workspace = true serde_json.workspace = true # misc @@ -75,3 +77,8 @@ optimism = [ "reth-optimism-payload-builder/optimism", "reth-optimism-primitives/optimism", ] +client = [ + "jsonrpsee/client", + "jsonrpsee/async-client", + "reth-rpc-eth-api/client", +] diff --git a/crates/optimism/rpc/src/engine.rs b/crates/optimism/rpc/src/engine.rs new file mode 100644 index 000000000000..3d5c2641f4ad --- /dev/null +++ b/crates/optimism/rpc/src/engine.rs @@ -0,0 +1,188 @@ +//! Implements the Optimism engine API RPC methods. + +use alloy_eips::eip7685::Requests; +use alloy_primitives::{BlockHash, B256}; +use alloy_rpc_types_engine::{ + ClientVersionV1, ExecutionPayloadBodiesV1, ExecutionPayloadInputV2, ExecutionPayloadV3, + ForkchoiceState, ForkchoiceUpdated, PayloadId, PayloadStatus, +}; +use jsonrpsee::proc_macros::rpc; +use jsonrpsee_core::RpcResult; +use reth_node_api::EngineTypes; + +/// Extension trait that gives access to Optimism engine API RPC methods. +/// +/// Note: +/// > The provider should use a JWT authentication layer. +/// +/// This follows the Optimism specs that can be found at: +/// +#[cfg_attr(not(feature = "client"), rpc(server, namespace = "engine"), server_bounds(Engine::PayloadAttributes: jsonrpsee::core::DeserializeOwned))] +#[cfg_attr(feature = "client", rpc(server, client, namespace = "engine", client_bounds(Engine::PayloadAttributes: jsonrpsee::core::Serialize + Clone), server_bounds(Engine::PayloadAttributes: jsonrpsee::core::DeserializeOwned)))] +pub trait OpEngineApi { + /// Sends the given payload to the execution layer client, as specified for the Shanghai fork. + /// + /// See also + /// + /// No modifications needed for OP compatibility. + #[method(name = "newPayloadV2")] + async fn new_payload_v2(&self, payload: ExecutionPayloadInputV2) -> RpcResult; + + /// Sends the given payload to the execution layer client, as specified for the Cancun fork. + /// + /// See also + /// + /// OP modifications: + /// - expected versioned hashes MUST be an empty array: therefore the `versioned_hashes` + /// parameter is removed. + /// - parent beacon block root MUST be the parent beacon block root from the L1 origin block of + /// the L2 block. + /// - blob versioned hashes MUST be empty list. + #[method(name = "newPayloadV3")] + async fn new_payload_v3( + &self, + payload: ExecutionPayloadV3, + versioned_hashes: Vec, + parent_beacon_block_root: B256, + ) -> RpcResult; + + /// Sends the given payload to the execution layer client, as specified for the Prague fork. + /// + /// See also + /// + /// - blob versioned hashes MUST be empty list. + /// - execution layer requests MUST be empty list. + #[method(name = "newPayloadV4")] + async fn new_payload_v4( + &self, + payload: ExecutionPayloadV3, + versioned_hashes: Vec, + parent_beacon_block_root: B256, + execution_requests: Requests, + ) -> RpcResult; + + /// Updates the execution layer client with the given fork choice, as specified for the Shanghai + /// fork. + /// + /// Caution: This should not accept the `parentBeaconBlockRoot` field in the payload attributes. + /// + /// See also + /// + /// OP modifications: + /// - The `payload_attributes` parameter is extended with the [`EngineTypes::PayloadAttributes`](EngineTypes) type as described in + #[method(name = "forkchoiceUpdatedV2")] + async fn fork_choice_updated_v2( + &self, + fork_choice_state: ForkchoiceState, + payload_attributes: Option, + ) -> RpcResult; + + /// Updates the execution layer client with the given fork choice, as specified for the Cancun + /// fork. + /// + /// See also + /// + /// OP modifications: + /// - Must be called with an Ecotone payload + /// - Attributes must contain the parent beacon block root field + /// - The `payload_attributes` parameter is extended with the [`EngineTypes::PayloadAttributes`](EngineTypes) type as described in + #[method(name = "forkchoiceUpdatedV3")] + async fn fork_choice_updated_v3( + &self, + fork_choice_state: ForkchoiceState, + payload_attributes: Option, + ) -> RpcResult; + + /// Retrieves an execution payload from a previously started build process, as specified for the + /// Shanghai fork. + /// + /// See also + /// + /// Note: + /// > Provider software MAY stop the corresponding build process after serving this call. + /// + /// No modifications needed for OP compatibility. + #[method(name = "getPayloadV2")] + async fn get_payload_v2( + &self, + payload_id: PayloadId, + ) -> RpcResult; + + /// Retrieves an execution payload from a previously started build process, as specified for the + /// Cancun fork. + /// + /// See also + /// + /// Note: + /// > Provider software MAY stop the corresponding build process after serving this call. + /// + /// OP modifications: + /// - the response type is extended to [`EngineTypes::ExecutionPayloadEnvelopeV3`]. + #[method(name = "getPayloadV3")] + async fn get_payload_v3( + &self, + payload_id: PayloadId, + ) -> RpcResult; + + /// Returns the most recent version of the payload that is available in the corresponding + /// payload build process at the time of receiving this call. + /// + /// See also + /// + /// Note: + /// > Provider software MAY stop the corresponding build process after serving this call. + /// + /// OP modifications: + /// - the response type is extended to [`EngineTypes::ExecutionPayloadEnvelopeV4`]. + #[method(name = "getPayloadV4")] + async fn get_payload_v4( + &self, + payload_id: PayloadId, + ) -> RpcResult; + + /// Returns the execution payload bodies by the given hash. + /// + /// See also + #[method(name = "getPayloadBodiesByHashV1")] + async fn get_payload_bodies_by_hash_v1( + &self, + block_hashes: Vec, + ) -> RpcResult; + + /// Returns the execution payload bodies by the range starting at `start`, containing `count` + /// blocks. + /// + /// WARNING: This method is associated with the BeaconBlocksByRange message in the consensus + /// layer p2p specification, meaning the input should be treated as untrusted or potentially + /// adversarial. + /// + /// Implementers should take care when acting on the input to this method, specifically + /// ensuring that the range is limited properly, and that the range boundaries are computed + /// correctly and without panics. + /// + /// See also + #[method(name = "getPayloadBodiesByRangeV1")] + async fn get_payload_bodies_by_range_v1( + &self, + start: u64, + count: u64, + ) -> RpcResult; + + /// Returns the execution client version information. + /// + /// Note: + /// > The `client_version` parameter identifies the consensus client. + /// + /// See also + #[method(name = "getClientVersionV1")] + async fn get_client_version_v1( + &self, + client_version: ClientVersionV1, + ) -> RpcResult>; + + /// Returns the list of Engine API methods supported by the execution layer client software. + /// + /// See also + #[method(name = "exchangeCapabilities")] + async fn exchange_capabilities(&self, capabilities: Vec) -> RpcResult>; +} diff --git a/crates/optimism/rpc/src/lib.rs b/crates/optimism/rpc/src/lib.rs index b76058ce531b..d98b67191ccf 100644 --- a/crates/optimism/rpc/src/lib.rs +++ b/crates/optimism/rpc/src/lib.rs @@ -10,12 +10,16 @@ // The `optimism` feature must be enabled to use this crate. #![cfg(feature = "optimism")] +pub mod engine; pub mod error; pub mod eth; pub mod miner; pub mod sequencer; pub mod witness; +#[cfg(feature = "client")] +pub use engine::OpEngineApiClient; +pub use engine::OpEngineApiServer; pub use error::{OpEthApiError, OpInvalidTransactionError, SequencerClientError}; pub use eth::{OpEthApi, OpReceiptBuilder}; pub use sequencer::SequencerClient;