diff --git a/crates/provider/Cargo.toml b/crates/provider/Cargo.toml index f55d8282bda..3b3b36c5596 100644 --- a/crates/provider/Cargo.toml +++ b/crates/provider/Cargo.toml @@ -20,6 +20,7 @@ alloy-signer-wallet = { workspace = true, optional = true } alloy-rpc-client.workspace = true alloy-rpc-types-trace.workspace = true alloy-rpc-types.workspace = true +alloy-rpc-types-engine = { workspace = true, optional = true } alloy-transport-http = { workspace = true, optional = true } alloy-transport-ipc = { workspace = true, optional = true } alloy-transport-ws = { workspace = true, optional = true } @@ -70,3 +71,4 @@ reqwest-default-tls = ["alloy-transport-http?/reqwest-default-tls"] reqwest-rustls-tls = ["alloy-transport-http?/reqwest-rustls-tls"] reqwest-native-tls = ["alloy-transport-http?/reqwest-native-tls"] anvil = ["reqwest", "dep:alloy-node-bindings", "dep:alloy-signer-wallet"] +engine-api = ["dep:alloy-rpc-types-engine"] diff --git a/crates/provider/src/ext/admin.rs b/crates/provider/src/ext/admin.rs index 895c93f4078..9db0c204d10 100644 --- a/crates/provider/src/ext/admin.rs +++ b/crates/provider/src/ext/admin.rs @@ -43,7 +43,6 @@ where P: Provider, { async fn add_peer(&self, record: &str) -> TransportResult { - // self.client(). self.client().request("admin_addPeer", (record,)).await } diff --git a/crates/provider/src/ext/engine.rs b/crates/provider/src/ext/engine.rs new file mode 100644 index 00000000000..8470c0c3ba0 --- /dev/null +++ b/crates/provider/src/ext/engine.rs @@ -0,0 +1,253 @@ +use alloy_network::Network; +use alloy_primitives::{BlockHash, B256}; +use alloy_rpc_types_engine::{ + ClientVersionV1, ExecutionPayloadBodiesV1, ExecutionPayloadInputV2, ExecutionPayloadV1, + ExecutionPayloadV2, ExecutionPayloadV3, ForkchoiceState, ForkchoiceUpdated, PayloadAttributes, + PayloadId, PayloadStatus, +}; +use alloy_transport::{Transport, TransportResult}; + +use crate::Provider; + +/// Extension trait that gives access to engine api RPC methods. +/// +/// Note: +/// > The provider should use a JWT authentication layer. +#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] +pub trait EngineApi: Send + Sync { + /// Sends the given payload to the execution layer client, as specified for the Paris fork. + /// + /// Caution: This should not accept the `withdrawals` field + /// + /// See also + async fn new_payload_v1(&self, payload: ExecutionPayloadV1) -> TransportResult; + + /// Sends the given payload to the execution layer client, as specified for the Shanghai fork. + /// + /// See also + async fn new_payload_v2( + &self, + payload: ExecutionPayloadInputV2, + ) -> TransportResult; + + /// Sends the given payload to the execution layer client, as specified for the Cancun fork. + /// + /// See also + async fn new_payload_v3( + &self, + payload: ExecutionPayloadV3, + versioned_hashes: Vec, + parent_beacon_block_root: B256, + ) -> TransportResult; + + /// Updates the execution layer client with the given fork choice, as specified for the Paris + /// fork. + /// + /// Caution: This should not accept the `withdrawals` field in the payload attributes. + /// + /// See also + async fn fork_choice_updated_v1( + &self, + fork_choice_state: ForkchoiceState, + payload_attributes: Option, + ) -> TransportResult; + + /// 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 + async fn fork_choice_updated_v2( + &self, + fork_choice_state: ForkchoiceState, + payload_attributes: Option, + ) -> TransportResult; + + /// Updates the execution layer client with the given fork choice, as specified for the Cancun + /// fork. + /// + /// See also + async fn fork_choice_updated_v3( + &self, + fork_choice_state: ForkchoiceState, + payload_attributes: Option, + ) -> TransportResult; + + /// Retrieves an executionpayload from a previously started build process, as specified for the + /// Paris fork. + /// + /// Caution: This should not return the `withdrawals` field + /// + /// See also + /// + /// Note: + /// > Provider software MAY stop the corresponding build process after serving this call. + async fn get_payload_v1(&self, payload_id: PayloadId) -> TransportResult; + + /// Retrieves an executionpayload 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. + async fn get_payload_v2(&self, payload_id: PayloadId) -> TransportResult; + + /// Retrieves an executionpayload 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. + async fn get_payload_v3(&self, payload_id: PayloadId) -> TransportResult; + + /// Returns the execution payload bodies by the given hash. + /// + /// See also + async fn get_payload_bodies_by_hash_v1( + &self, + block_hashes: Vec, + ) -> TransportResult; + + /// 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 + async fn get_payload_bodies_by_range_v1( + &self, + start: u64, + count: u64, + ) -> TransportResult; + + /// Returns the execution client version information. + /// + /// Note: + /// > The `client_version` parameter identifies the consensus client. + /// + /// See also + async fn get_client_version_v1( + &self, + client_version: ClientVersionV1, + ) -> TransportResult>; + + /// Returns the list of Engine api methods supported by the execution layer client software. + /// + /// See also + async fn exchange_capabilities( + &self, + capabilities: Vec, + ) -> TransportResult>; +} + +#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] +impl EngineApi for P +where + N: Network, + T: Transport + Clone, + P: Provider, +{ + async fn new_payload_v1(&self, payload: ExecutionPayloadV1) -> TransportResult { + self.client().request("engine_newPayloadV1", (payload,)).await + } + + async fn new_payload_v2( + &self, + payload: ExecutionPayloadInputV2, + ) -> TransportResult { + self.client().request("engine_newPayloadV2", (payload,)).await + } + + async fn new_payload_v3( + &self, + payload: ExecutionPayloadV3, + versioned_hashes: Vec, + parent_beacon_block_root: B256, + ) -> TransportResult { + self.client() + .request("engine_newPayloadV3", (payload, versioned_hashes, parent_beacon_block_root)) + .await + } + + async fn fork_choice_updated_v1( + &self, + fork_choice_state: ForkchoiceState, + payload_attributes: Option, + ) -> TransportResult { + self.client() + .request("engine_forkchoiceUpdatedV1", (fork_choice_state, payload_attributes)) + .await + } + + async fn fork_choice_updated_v2( + &self, + fork_choice_state: ForkchoiceState, + payload_attributes: Option, + ) -> TransportResult { + self.client() + .request("engine_forkchoiceUpdatedV2", (fork_choice_state, payload_attributes)) + .await + } + + async fn fork_choice_updated_v3( + &self, + fork_choice_state: ForkchoiceState, + payload_attributes: Option, + ) -> TransportResult { + self.client() + .request("engine_forkchoiceUpdatedV3", (fork_choice_state, payload_attributes)) + .await + } + + async fn get_payload_v1(&self, payload_id: PayloadId) -> TransportResult { + self.client().request("engine_getPayloadV1", (payload_id,)).await + } + + async fn get_payload_v2(&self, payload_id: PayloadId) -> TransportResult { + self.client().request("engine_getPayloadV2", (payload_id,)).await + } + + async fn get_payload_v3(&self, payload_id: PayloadId) -> TransportResult { + self.client().request("engine_getPayloadV3", (payload_id,)).await + } + + async fn get_payload_bodies_by_hash_v1( + &self, + block_hashes: Vec, + ) -> TransportResult { + self.client().request("engine_getPayloadBodiesByHashV1", (block_hashes,)).await + } + + async fn get_payload_bodies_by_range_v1( + &self, + start: u64, + count: u64, + ) -> TransportResult { + self.client().request("engine_getPayloadBodiesByRangeV1", (start, count)).await + } + + async fn get_client_version_v1( + &self, + client_version: ClientVersionV1, + ) -> TransportResult> { + self.client().request("engine_getClientVersionV1", (client_version,)).await + } + + async fn exchange_capabilities( + &self, + capabilities: Vec, + ) -> TransportResult> { + self.client().request("engine_exchangeCapabilities", (capabilities,)).await + } +} diff --git a/crates/provider/src/ext/mod.rs b/crates/provider/src/ext/mod.rs index d19b8a352a4..924fb8e0dda 100644 --- a/crates/provider/src/ext/mod.rs +++ b/crates/provider/src/ext/mod.rs @@ -3,6 +3,11 @@ mod admin; pub use admin::AdminApi; +#[cfg(feature = "engine-api")] +mod engine; +#[cfg(feature = "engine-api")] +pub use engine::EngineApi; + mod debug; pub use debug::DebugApi;