1use alloy_eips::eip7685::Requests;
4use alloy_primitives::{BlockHash, B256, B64, U64};
5use alloy_rpc_types_engine::{
6 ClientVersionV1, ExecutionPayloadBodiesV1, ExecutionPayloadInputV2, ExecutionPayloadV3,
7 ForkchoiceState, ForkchoiceUpdated, PayloadId, PayloadStatus,
8};
9use derive_more::Constructor;
10use jsonrpsee::proc_macros::rpc;
11use jsonrpsee_core::{server::RpcModule, RpcResult};
12use op_alloy_rpc_types_engine::{
13 OpExecutionData, OpExecutionPayloadV4, ProtocolVersion, ProtocolVersionFormatV0,
14 SuperchainSignal,
15};
16use reth_chainspec::EthereumHardforks;
17use reth_node_api::{EngineApiValidator, EngineTypes};
18use reth_rpc_api::IntoEngineApiRpcModule;
19use reth_rpc_engine_api::EngineApi;
20use reth_storage_api::{BlockReader, HeaderProvider, StateProviderFactory};
21use reth_transaction_pool::TransactionPool;
22use tracing::{debug, info, trace};
23
24pub const OP_ENGINE_CAPABILITIES: &[&str] = &[
28 "engine_forkchoiceUpdatedV1",
29 "engine_forkchoiceUpdatedV2",
30 "engine_forkchoiceUpdatedV3",
31 "engine_getClientVersionV1",
32 "engine_getPayloadV2",
33 "engine_getPayloadV3",
34 "engine_getPayloadV4",
35 "engine_newPayloadV2",
36 "engine_newPayloadV3",
37 "engine_newPayloadV4",
38 "engine_getPayloadBodiesByHashV1",
39 "engine_getPayloadBodiesByRangeV1",
40 "engine_signalSuperchainV1",
41];
42
43pub const OP_STACK_SUPPORT: ProtocolVersion = ProtocolVersion::V0(ProtocolVersionFormatV0 {
46 build: B64::ZERO,
47 major: 9,
48 minor: 0,
49 patch: 0,
50 pre_release: 0,
51});
52
53#[cfg_attr(not(feature = "client"), rpc(server, namespace = "engine"), server_bounds(Engine::PayloadAttributes: jsonrpsee::core::DeserializeOwned))]
61#[cfg_attr(feature = "client", rpc(server, client, namespace = "engine", client_bounds(Engine::PayloadAttributes: jsonrpsee::core::Serialize + Clone), server_bounds(Engine::PayloadAttributes: jsonrpsee::core::DeserializeOwned)))]
62pub trait OpEngineApi<Engine: EngineTypes> {
63 #[method(name = "newPayloadV2")]
69 async fn new_payload_v2(&self, payload: ExecutionPayloadInputV2) -> RpcResult<PayloadStatus>;
70
71 #[method(name = "newPayloadV3")]
82 async fn new_payload_v3(
83 &self,
84 payload: ExecutionPayloadV3,
85 versioned_hashes: Vec<B256>,
86 parent_beacon_block_root: B256,
87 ) -> RpcResult<PayloadStatus>;
88
89 #[method(name = "newPayloadV4")]
96 async fn new_payload_v4(
97 &self,
98 payload: OpExecutionPayloadV4,
99 versioned_hashes: Vec<B256>,
100 parent_beacon_block_root: B256,
101 execution_requests: Requests,
102 ) -> RpcResult<PayloadStatus>;
103
104 #[method(name = "forkchoiceUpdatedV1")]
110 async fn fork_choice_updated_v1(
111 &self,
112 fork_choice_state: ForkchoiceState,
113 payload_attributes: Option<Engine::PayloadAttributes>,
114 ) -> RpcResult<ForkchoiceUpdated>;
115
116 #[method(name = "forkchoiceUpdatedV2")]
126 async fn fork_choice_updated_v2(
127 &self,
128 fork_choice_state: ForkchoiceState,
129 payload_attributes: Option<Engine::PayloadAttributes>,
130 ) -> RpcResult<ForkchoiceUpdated>;
131
132 #[method(name = "forkchoiceUpdatedV3")]
142 async fn fork_choice_updated_v3(
143 &self,
144 fork_choice_state: ForkchoiceState,
145 payload_attributes: Option<Engine::PayloadAttributes>,
146 ) -> RpcResult<ForkchoiceUpdated>;
147
148 #[method(name = "getPayloadV2")]
158 async fn get_payload_v2(
159 &self,
160 payload_id: PayloadId,
161 ) -> RpcResult<Engine::ExecutionPayloadEnvelopeV2>;
162
163 #[method(name = "getPayloadV3")]
174 async fn get_payload_v3(
175 &self,
176 payload_id: PayloadId,
177 ) -> RpcResult<Engine::ExecutionPayloadEnvelopeV3>;
178
179 #[method(name = "getPayloadV4")]
190 async fn get_payload_v4(
191 &self,
192 payload_id: PayloadId,
193 ) -> RpcResult<Engine::ExecutionPayloadEnvelopeV4>;
194
195 #[method(name = "getPayloadBodiesByHashV1")]
199 async fn get_payload_bodies_by_hash_v1(
200 &self,
201 block_hashes: Vec<BlockHash>,
202 ) -> RpcResult<ExecutionPayloadBodiesV1>;
203
204 #[method(name = "getPayloadBodiesByRangeV1")]
217 async fn get_payload_bodies_by_range_v1(
218 &self,
219 start: U64,
220 count: U64,
221 ) -> RpcResult<ExecutionPayloadBodiesV1>;
222
223 #[method(name = "engine_signalSuperchainV1")]
227 async fn signal_superchain_v1(&self, _signal: SuperchainSignal) -> RpcResult<ProtocolVersion>;
228
229 #[method(name = "getClientVersionV1")]
236 async fn get_client_version_v1(
237 &self,
238 client_version: ClientVersionV1,
239 ) -> RpcResult<Vec<ClientVersionV1>>;
240
241 #[method(name = "exchangeCapabilities")]
245 async fn exchange_capabilities(&self, capabilities: Vec<String>) -> RpcResult<Vec<String>>;
246}
247
248#[derive(Debug, Constructor)]
251pub struct OpEngineApi<Provider, EngineT: EngineTypes, Pool, Validator, ChainSpec> {
252 inner: EngineApi<Provider, EngineT, Pool, Validator, ChainSpec>,
253}
254
255impl<Provider, PayloadT, Pool, Validator, ChainSpec> Clone
256 for OpEngineApi<Provider, PayloadT, Pool, Validator, ChainSpec>
257where
258 PayloadT: EngineTypes,
259{
260 fn clone(&self) -> Self {
261 Self { inner: self.inner.clone() }
262 }
263}
264
265#[async_trait::async_trait]
266impl<Provider, EngineT, Pool, Validator, ChainSpec> OpEngineApiServer<EngineT>
267 for OpEngineApi<Provider, EngineT, Pool, Validator, ChainSpec>
268where
269 Provider: HeaderProvider + BlockReader + StateProviderFactory + 'static,
270 EngineT: EngineTypes<ExecutionData = OpExecutionData>,
271 Pool: TransactionPool + 'static,
272 Validator: EngineApiValidator<EngineT>,
273 ChainSpec: EthereumHardforks + Send + Sync + 'static,
274{
275 async fn new_payload_v2(&self, payload: ExecutionPayloadInputV2) -> RpcResult<PayloadStatus> {
276 trace!(target: "rpc::engine", "Serving engine_newPayloadV2");
277 let payload = OpExecutionData::v2(payload);
278 Ok(self.inner.new_payload_v2_metered(payload).await?)
279 }
280
281 async fn new_payload_v3(
282 &self,
283 payload: ExecutionPayloadV3,
284 versioned_hashes: Vec<B256>,
285 parent_beacon_block_root: B256,
286 ) -> RpcResult<PayloadStatus> {
287 trace!(target: "rpc::engine", "Serving engine_newPayloadV3");
288 let payload = OpExecutionData::v3(payload, versioned_hashes, parent_beacon_block_root);
289
290 Ok(self.inner.new_payload_v3_metered(payload).await?)
291 }
292
293 async fn new_payload_v4(
294 &self,
295 payload: OpExecutionPayloadV4,
296 versioned_hashes: Vec<B256>,
297 parent_beacon_block_root: B256,
298 execution_requests: Requests,
299 ) -> RpcResult<PayloadStatus> {
300 trace!(target: "rpc::engine", "Serving engine_newPayloadV4");
301 let payload = OpExecutionData::v4(
302 payload,
303 versioned_hashes,
304 parent_beacon_block_root,
305 execution_requests,
306 );
307
308 Ok(self.inner.new_payload_v4_metered(payload).await?)
309 }
310
311 async fn fork_choice_updated_v1(
312 &self,
313 fork_choice_state: ForkchoiceState,
314 payload_attributes: Option<EngineT::PayloadAttributes>,
315 ) -> RpcResult<ForkchoiceUpdated> {
316 Ok(self.inner.fork_choice_updated_v1_metered(fork_choice_state, payload_attributes).await?)
317 }
318
319 async fn fork_choice_updated_v2(
320 &self,
321 fork_choice_state: ForkchoiceState,
322 payload_attributes: Option<EngineT::PayloadAttributes>,
323 ) -> RpcResult<ForkchoiceUpdated> {
324 trace!(target: "rpc::engine", "Serving engine_forkchoiceUpdatedV2");
325 Ok(self.inner.fork_choice_updated_v2_metered(fork_choice_state, payload_attributes).await?)
326 }
327
328 async fn fork_choice_updated_v3(
329 &self,
330 fork_choice_state: ForkchoiceState,
331 payload_attributes: Option<EngineT::PayloadAttributes>,
332 ) -> RpcResult<ForkchoiceUpdated> {
333 trace!(target: "rpc::engine", "Serving engine_forkchoiceUpdatedV3");
334 Ok(self.inner.fork_choice_updated_v3_metered(fork_choice_state, payload_attributes).await?)
335 }
336
337 async fn get_payload_v2(
338 &self,
339 payload_id: PayloadId,
340 ) -> RpcResult<EngineT::ExecutionPayloadEnvelopeV2> {
341 debug!(target: "rpc::engine", id = %payload_id, "Serving engine_getPayloadV2");
342 Ok(self.inner.get_payload_v2_metered(payload_id).await?)
343 }
344
345 async fn get_payload_v3(
346 &self,
347 payload_id: PayloadId,
348 ) -> RpcResult<EngineT::ExecutionPayloadEnvelopeV3> {
349 trace!(target: "rpc::engine", "Serving engine_getPayloadV3");
350 Ok(self.inner.get_payload_v3_metered(payload_id).await?)
351 }
352
353 async fn get_payload_v4(
354 &self,
355 payload_id: PayloadId,
356 ) -> RpcResult<EngineT::ExecutionPayloadEnvelopeV4> {
357 trace!(target: "rpc::engine", "Serving engine_getPayloadV4");
358 Ok(self.inner.get_payload_v4_metered(payload_id).await?)
359 }
360
361 async fn get_payload_bodies_by_hash_v1(
362 &self,
363 block_hashes: Vec<BlockHash>,
364 ) -> RpcResult<ExecutionPayloadBodiesV1> {
365 trace!(target: "rpc::engine", "Serving engine_getPayloadBodiesByHashV1");
366 Ok(self.inner.get_payload_bodies_by_hash_v1_metered(block_hashes).await?)
367 }
368
369 async fn get_payload_bodies_by_range_v1(
370 &self,
371 start: U64,
372 count: U64,
373 ) -> RpcResult<ExecutionPayloadBodiesV1> {
374 trace!(target: "rpc::engine", "Serving engine_getPayloadBodiesByRangeV1");
375 Ok(self.inner.get_payload_bodies_by_range_v1_metered(start.to(), count.to()).await?)
376 }
377
378 async fn signal_superchain_v1(&self, signal: SuperchainSignal) -> RpcResult<ProtocolVersion> {
379 trace!(target: "rpc::engine", "Serving signal_superchain_v1");
380 info!(
381 target: "rpc::engine",
382 "Received superchain version signal local={:?} required={:?} recommended={:?}",
383 OP_STACK_SUPPORT,
384 signal.required,
385 signal.recommended
386 );
387 Ok(OP_STACK_SUPPORT)
388 }
389
390 async fn get_client_version_v1(
391 &self,
392 client: ClientVersionV1,
393 ) -> RpcResult<Vec<ClientVersionV1>> {
394 trace!(target: "rpc::engine", "Serving engine_getClientVersionV1");
395 Ok(self.inner.get_client_version_v1(client)?)
396 }
397
398 async fn exchange_capabilities(&self, _capabilities: Vec<String>) -> RpcResult<Vec<String>> {
399 Ok(self.inner.capabilities().list())
400 }
401}
402
403impl<Provider, EngineT, Pool, Validator, ChainSpec> IntoEngineApiRpcModule
404 for OpEngineApi<Provider, EngineT, Pool, Validator, ChainSpec>
405where
406 EngineT: EngineTypes,
407 Self: OpEngineApiServer<EngineT>,
408{
409 fn into_rpc_module(self) -> RpcModule<()> {
410 self.into_rpc().remove_context()
411 }
412}