Skip to main content

reth_rpc_api/
engine.rs

1//! Server traits for the engine API
2//!
3//! This contains the `engine_` namespace and the subset of the `eth_` namespace that is exposed to
4//! the consensus client.
5
6use alloy_eips::{
7    eip4844::{BlobAndProofV1, BlobAndProofV2, BlobCellsAndProofsV1},
8    eip7685::RequestsOrHash,
9    BlockId, BlockNumberOrTag,
10};
11use alloy_json_rpc::RpcObject;
12use alloy_primitives::{Address, BlockHash, Bytes, B128, B256, U256, U64};
13use alloy_rpc_types_engine::{
14    ClientVersionV1, ExecutionPayloadBodiesV1, ExecutionPayloadBodiesV2, ExecutionPayloadInputV2,
15    ExecutionPayloadV1, ExecutionPayloadV3, ExecutionPayloadV4, ForkchoiceState, ForkchoiceUpdated,
16    ForkchoiceUpdatedResponseV2, PayloadId, PayloadStatus, PayloadStatusV2,
17};
18use alloy_rpc_types_eth::{
19    state::StateOverride, BlockOverrides, EIP1186AccountProofResponse, Filter, Index, SyncStatus,
20};
21use alloy_serde::JsonStorageKey;
22use jsonrpsee::{core::RpcResult, proc_macros::rpc, RpcModule};
23use reth_engine_primitives::EngineTypes;
24use serde_json::Value;
25
26/// Helper trait for the engine api server.
27///
28/// This type-erases the concrete [`jsonrpsee`] server implementation and only returns the
29/// [`RpcModule`] that contains all the endpoints of the server.
30pub trait IntoEngineApiRpcModule {
31    /// Consumes the type and returns all the methods and subscriptions defined in the trait and
32    /// returns them as a single [`RpcModule`]
33    fn into_rpc_module(self) -> RpcModule<()>;
34}
35
36// NOTE: We can't use associated types in the `EngineApi` trait because of jsonrpsee, so we use a
37// generic here. It would be nice if the rpc macro would understand which types need to have serde.
38// By default, if the trait has a generic, the rpc macro will add e.g. `Engine: DeserializeOwned` to
39// the trait bounds, which is not what we want, because `Types` is not used directly in any of the
40// trait methods. Instead, we have to add the bounds manually. This would be disastrous if we had
41// more than one associated type used in the trait methods.
42
43#[cfg_attr(not(feature = "client"), rpc(server, namespace = "engine"), server_bounds(Engine::PayloadAttributes: jsonrpsee::core::DeserializeOwned))]
44#[cfg_attr(feature = "client", rpc(server, client, namespace = "engine", client_bounds(Engine::PayloadAttributes: jsonrpsee::core::Serialize + Clone), server_bounds(Engine::PayloadAttributes: jsonrpsee::core::DeserializeOwned)))]
45pub trait EngineApi<Engine: EngineTypes> {
46    /// See also <https://github.com/ethereum/execution-apis/blob/6709c2a795b707202e93c4f2867fa0bf2640a84f/src/engine/paris.md#engine_newpayloadv1>
47    /// Caution: This should not accept the `withdrawals` field
48    #[method(name = "newPayloadV1")]
49    async fn new_payload_v1(&self, payload: ExecutionPayloadV1) -> RpcResult<PayloadStatus>;
50
51    /// See also <https://github.com/ethereum/execution-apis/blob/584905270d8ad665718058060267061ecfd79ca5/src/engine/shanghai.md#engine_newpayloadv2>
52    #[method(name = "newPayloadV2")]
53    async fn new_payload_v2(&self, payload: ExecutionPayloadInputV2) -> RpcResult<PayloadStatus>;
54
55    /// Post Cancun payload handler
56    ///
57    /// See also <https://github.com/ethereum/execution-apis/blob/main/src/engine/cancun.md#engine_newpayloadv3>
58    #[method(name = "newPayloadV3")]
59    async fn new_payload_v3(
60        &self,
61        payload: ExecutionPayloadV3,
62        versioned_hashes: Vec<B256>,
63        parent_beacon_block_root: B256,
64    ) -> RpcResult<PayloadStatus>;
65
66    /// Post Prague payload handler
67    ///
68    /// See also <https://github.com/ethereum/execution-apis/blob/main/src/engine/prague.md#engine_newpayloadv4>
69    #[method(name = "newPayloadV4")]
70    async fn new_payload_v4(
71        &self,
72        payload: ExecutionPayloadV3,
73        versioned_hashes: Vec<B256>,
74        parent_beacon_block_root: B256,
75        execution_requests: RequestsOrHash,
76    ) -> RpcResult<PayloadStatus>;
77
78    /// Post Amsterdam payload handler
79    ///
80    /// See also <https://github.com/ethereum/execution-apis/blob/main/src/engine/amsterdam.md#engine_newpayloadv5>
81    #[method(name = "newPayloadV5")]
82    async fn new_payload_v5(
83        &self,
84        payload: ExecutionPayloadV4,
85        versioned_hashes: Vec<B256>,
86        parent_beacon_block_root: B256,
87        execution_requests: RequestsOrHash,
88    ) -> RpcResult<PayloadStatus>;
89
90    /// Post Bogota payload handler stub.
91    ///
92    /// See also <https://github.com/ethereum/execution-apis/blob/main/src/engine/bogota.md#engine_newpayloadv6>
93    #[method(name = "newPayloadV6")]
94    async fn new_payload_v6(
95        &self,
96        payload: ExecutionPayloadV4,
97        versioned_hashes: Vec<B256>,
98        parent_beacon_block_root: B256,
99        execution_requests: RequestsOrHash,
100        inclusion_list_transactions: Vec<Bytes>,
101    ) -> RpcResult<PayloadStatusV2>;
102
103    /// See also <https://github.com/ethereum/execution-apis/blob/6709c2a795b707202e93c4f2867fa0bf2640a84f/src/engine/paris.md#engine_forkchoiceupdatedv1>
104    ///
105    /// Caution: This should not accept the `withdrawals` field in the payload attributes.
106    #[method(name = "forkchoiceUpdatedV1")]
107    async fn fork_choice_updated_v1(
108        &self,
109        fork_choice_state: ForkchoiceState,
110        payload_attributes: Option<Engine::PayloadAttributes>,
111    ) -> RpcResult<ForkchoiceUpdated>;
112
113    /// Post Shanghai forkchoice update handler
114    ///
115    /// This is the same as `forkchoiceUpdatedV1`, but expects an additional `withdrawals` field in
116    /// the `payloadAttributes`, if payload attributes are provided.
117    ///
118    /// See also <https://github.com/ethereum/execution-apis/blob/6709c2a795b707202e93c4f2867fa0bf2640a84f/src/engine/shanghai.md#engine_forkchoiceupdatedv2>
119    ///
120    /// Caution: This should not accept the `parentBeaconBlockRoot` field in the payload
121    /// attributes.
122    #[method(name = "forkchoiceUpdatedV2")]
123    async fn fork_choice_updated_v2(
124        &self,
125        fork_choice_state: ForkchoiceState,
126        payload_attributes: Option<Engine::PayloadAttributes>,
127    ) -> RpcResult<ForkchoiceUpdated>;
128
129    /// Post Cancun forkchoice update handler
130    ///
131    /// This is the same as `forkchoiceUpdatedV2`, but expects an additional
132    /// `parentBeaconBlockRoot` field in the `payloadAttributes`, if payload attributes
133    /// are provided.
134    ///
135    /// See also <https://github.com/ethereum/execution-apis/blob/main/src/engine/cancun.md#engine_forkchoiceupdatedv3>
136    #[method(name = "forkchoiceUpdatedV3")]
137    async fn fork_choice_updated_v3(
138        &self,
139        fork_choice_state: ForkchoiceState,
140        payload_attributes: Option<Engine::PayloadAttributes>,
141    ) -> RpcResult<ForkchoiceUpdated>;
142
143    /// Post Amsterdam forkchoice update handler
144    ///
145    /// This is the same as `forkchoiceUpdatedV3`, but expects an additional
146    /// `slotNumber` field in the `payloadAttributes`, if payload attributes
147    /// are provided.
148    ///
149    /// `custody_columns` maps to the third positional JSON-RPC parameter, `custodyColumns`,
150    /// the custody-column bitmask used for [EIP-8070] sparse blobpool signaling. It is
151    /// `DATA|null` and must be 16 bytes when set. When calling `engine_forkchoiceUpdatedV4`
152    /// with custody columns but without payload attributes, the second parameter must still
153    /// be supplied as `null`, for example:
154    /// `[forkchoiceState, null, custodyColumns]`.
155    ///
156    /// [EIP-8070]: https://eips.ethereum.org/EIPS/eip-8070
157    ///
158    /// See also <https://github.com/ethereum/execution-apis/blob/main/src/engine/amsterdam.md#engine_forkchoiceupdatedv4>
159    #[method(name = "forkchoiceUpdatedV4")]
160    async fn fork_choice_updated_v4(
161        &self,
162        fork_choice_state: ForkchoiceState,
163        payload_attributes: Option<Engine::PayloadAttributes>,
164        custody_columns: Option<B128>,
165    ) -> RpcResult<ForkchoiceUpdated>;
166
167    /// Post Bogota forkchoice update stub.
168    ///
169    /// See also <https://github.com/ethereum/execution-apis/blob/main/src/engine/bogota.md#engine_forkchoiceupdatedv5>
170    #[method(name = "forkchoiceUpdatedV5")]
171    async fn fork_choice_updated_v5(
172        &self,
173        fork_choice_state: ForkchoiceState,
174        payload_attributes: Option<Engine::PayloadAttributes>,
175        custody_columns: Option<B128>,
176    ) -> RpcResult<ForkchoiceUpdatedResponseV2>;
177
178    /// See also <https://github.com/ethereum/execution-apis/blob/6709c2a795b707202e93c4f2867fa0bf2640a84f/src/engine/paris.md#engine_getpayloadv1>
179    ///
180    /// Returns the most recent version of the payload that is available in the corresponding
181    /// payload build process at the time of receiving this call.
182    ///
183    /// Caution: This should not return the `withdrawals` field
184    ///
185    /// Note:
186    /// > Provider software MAY stop the corresponding build process after serving this call.
187    #[method(name = "getPayloadV1")]
188    async fn get_payload_v1(
189        &self,
190        payload_id: PayloadId,
191    ) -> RpcResult<Engine::ExecutionPayloadEnvelopeV1>;
192
193    /// See also <https://github.com/ethereum/execution-apis/blob/6709c2a795b707202e93c4f2867fa0bf2640a84f/src/engine/shanghai.md#engine_getpayloadv2>
194    ///
195    /// Returns the most recent version of the payload that is available in the corresponding
196    /// payload build process at the time of receiving this call. Note:
197    /// > Provider software MAY stop the corresponding build process after serving this call.
198    #[method(name = "getPayloadV2")]
199    async fn get_payload_v2(
200        &self,
201        payload_id: PayloadId,
202    ) -> RpcResult<Engine::ExecutionPayloadEnvelopeV2>;
203
204    /// Post Cancun payload handler which also returns a blobs bundle.
205    ///
206    /// See also <https://github.com/ethereum/execution-apis/blob/main/src/engine/cancun.md#engine_getpayloadv3>
207    ///
208    /// Returns the most recent version of the payload that is available in the corresponding
209    /// payload build process at the time of receiving this call. Note:
210    /// > Provider software MAY stop the corresponding build process after serving this call.
211    #[method(name = "getPayloadV3")]
212    async fn get_payload_v3(
213        &self,
214        payload_id: PayloadId,
215    ) -> RpcResult<Engine::ExecutionPayloadEnvelopeV3>;
216
217    /// Post Prague payload handler.
218    ///
219    /// See also <https://github.com/ethereum/execution-apis/blob/main/src/engine/prague.md#engine_getpayloadv4>
220    ///
221    /// Returns the most recent version of the payload that is available in the corresponding
222    /// payload build process at the time of receiving this call. Note:
223    /// > Provider software MAY stop the corresponding build process after serving this call.
224    #[method(name = "getPayloadV4")]
225    async fn get_payload_v4(
226        &self,
227        payload_id: PayloadId,
228    ) -> RpcResult<Engine::ExecutionPayloadEnvelopeV4>;
229
230    /// Post Osaka payload handler.
231    ///
232    /// See also <https://github.com/ethereum/execution-apis/blob/15399c2e2f16a5f800bf3f285640357e2c245ad9/src/engine/osaka.md#engine_getpayloadv5>.
233    ///
234    /// Returns the most recent version of the payload that is available in the corresponding
235    /// payload build process at the time of receiving this call. Note:
236    /// > Provider software MAY stop the corresponding build process after serving this call.
237    #[method(name = "getPayloadV5")]
238    async fn get_payload_v5(
239        &self,
240        payload_id: PayloadId,
241    ) -> RpcResult<Engine::ExecutionPayloadEnvelopeV5>;
242
243    /// Post Amsterdam payload handler.
244    ///
245    /// See also <https://github.com/ethereum/execution-apis/blob/main/src/engine/amsterdam.md#engine_getpayloadv6>
246    ///
247    /// Returns the most recent version of the payload that is available in the corresponding
248    /// payload build process at the time of receiving this call. Note:
249    /// > Provider software MAY stop the corresponding build process after serving this call.
250    #[method(name = "getPayloadV6")]
251    async fn get_payload_v6(
252        &self,
253        payload_id: PayloadId,
254    ) -> RpcResult<Engine::ExecutionPayloadEnvelopeV6>;
255
256    /// Returns transactions selected from the local transaction pool for the FOCIL inclusion list.
257    ///
258    /// See also <https://github.com/ethereum/execution-apis/pull/609>.
259    #[method(name = "getInclusionListV1")]
260    async fn get_inclusion_list_v1(&self) -> RpcResult<Vec<Bytes>>;
261
262    /// See also <https://github.com/ethereum/execution-apis/blob/6452a6b194d7db269bf1dbd087a267251d3cc7f8/src/engine/shanghai.md#engine_getpayloadbodiesbyhashv1>
263    #[method(name = "getPayloadBodiesByHashV1")]
264    async fn get_payload_bodies_by_hash_v1(
265        &self,
266        block_hashes: Vec<BlockHash>,
267    ) -> RpcResult<ExecutionPayloadBodiesV1>;
268
269    /// Returns `ExecutionPayloadBodyV2` objects for the given block hashes.
270    ///
271    /// V2 includes the `block_access_list` field for EIP-7928 BAL support.
272    ///
273    /// See also <https://eips.ethereum.org/EIPS/eip-7928>
274    #[method(name = "getPayloadBodiesByHashV2")]
275    async fn get_payload_bodies_by_hash_v2(
276        &self,
277        block_hashes: Vec<BlockHash>,
278    ) -> RpcResult<ExecutionPayloadBodiesV2>;
279
280    /// See also <https://github.com/ethereum/execution-apis/blob/6452a6b194d7db269bf1dbd087a267251d3cc7f8/src/engine/shanghai.md#engine_getpayloadbodiesbyrangev1>
281    ///
282    /// Returns the execution payload bodies by the range starting at `start`, containing `count`
283    /// blocks.
284    ///
285    /// WARNING: This method is associated with the `BeaconBlocksByRange` message in the consensus
286    /// layer p2p specification, meaning the input should be treated as untrusted or potentially
287    /// adversarial.
288    ///
289    /// Implementers should take care when acting on the input to this method, specifically
290    /// ensuring that the range is limited properly, and that the range boundaries are computed
291    /// correctly and without panics.
292    #[method(name = "getPayloadBodiesByRangeV1")]
293    async fn get_payload_bodies_by_range_v1(
294        &self,
295        start: U64,
296        count: U64,
297    ) -> RpcResult<ExecutionPayloadBodiesV1>;
298
299    /// Returns `ExecutionPayloadBodyV2` objects for the given block range.
300    ///
301    /// V2 includes the `block_access_list` field for EIP-7928 BAL support.
302    ///
303    /// WARNING: This method is associated with the `BeaconBlocksByRange` message in the consensus
304    /// layer p2p specification, meaning the input should be treated as untrusted or potentially
305    /// adversarial.
306    ///
307    /// Implementers should take care when acting on the input to this method, specifically
308    /// ensuring that the range is limited properly, and that the range boundaries are computed
309    /// correctly and without panics.
310    ///
311    /// See also <https://eips.ethereum.org/EIPS/eip-7928>
312    #[method(name = "getPayloadBodiesByRangeV2")]
313    async fn get_payload_bodies_by_range_v2(
314        &self,
315        start: U64,
316        count: U64,
317    ) -> RpcResult<ExecutionPayloadBodiesV2>;
318
319    /// This function will return the [`ClientVersionV1`] object.
320    /// See also:
321    /// <https://github.com/ethereum/execution-apis/blob/03911ffc053b8b806123f1fc237184b0092a485a/src/engine/identification.md#engine_getclientversionv1>
322    ///
323    ///
324    /// - When connected to a single execution client, the consensus client **MUST** receive an
325    ///   array with a single `ClientVersionV1` object.
326    /// - When connected to multiple execution clients via a multiplexer, the multiplexer **MUST**
327    ///   concatenate the responses from each execution client into a single,
328    /// flat array before returning the response to the consensus client.
329    #[method(name = "getClientVersionV1")]
330    async fn get_client_version_v1(
331        &self,
332        client_version: ClientVersionV1,
333    ) -> RpcResult<Vec<ClientVersionV1>>;
334
335    /// See also <https://github.com/ethereum/execution-apis/blob/6452a6b194d7db269bf1dbd087a267251d3cc7f8/src/engine/common.md#capabilities>
336    #[method(name = "exchangeCapabilities")]
337    async fn exchange_capabilities(&self, capabilities: Vec<String>) -> RpcResult<Vec<String>>;
338
339    /// Report blob availability for the requested blob versioned hashes.
340    #[method(name = "hasBlobs")]
341    async fn has_blobs(&self, versioned_hashes: Vec<B256>) -> RpcResult<Vec<bool>>;
342
343    /// Fetch blobs for the consensus layer from the blob store.
344    #[method(name = "getBlobsV1")]
345    async fn get_blobs_v1(
346        &self,
347        versioned_hashes: Vec<B256>,
348    ) -> RpcResult<Vec<Option<BlobAndProofV1>>>;
349
350    /// Fetch blobs for the consensus layer from the blob store.
351    ///
352    /// Returns a response only if blobs and proofs are present for _all_ of the versioned hashes:
353    ///     2. Client software MUST return null in case of any missing or older version blobs.
354    #[method(name = "getBlobsV2")]
355    async fn get_blobs_v2(
356        &self,
357        versioned_hashes: Vec<B256>,
358    ) -> RpcResult<Option<Vec<BlobAndProofV2>>>;
359
360    /// Fetch blobs for the consensus layer from the blob store.
361    ///
362    /// Returns a response of the same length as the request. Missing or older-version blobs are
363    /// returned as `null` elements.
364    ///
365    /// Returns `null` if syncing.
366    #[method(name = "getBlobsV3")]
367    async fn get_blobs_v3(
368        &self,
369        versioned_hashes: Vec<B256>,
370    ) -> RpcResult<Option<Vec<Option<BlobAndProofV2>>>>;
371
372    /// Fetch blob cells for the consensus layer from the blob store.
373    ///
374    /// Returns a response of the same length as the request. Missing blobs are returned as `null`
375    /// elements; missing requested cells within an available blob are returned as `null` cell and
376    /// proof entries.
377    ///
378    /// Returns `null` if syncing.
379    #[method(name = "getBlobsV4")]
380    async fn get_blobs_v4(
381        &self,
382        versioned_hashes: Vec<B256>,
383        indices_bitarray: B128,
384    ) -> RpcResult<Option<Vec<Option<BlobCellsAndProofsV1>>>>;
385}
386
387/// A subset of the ETH rpc interface: <https://ethereum.github.io/execution-apis/api-documentation>
388///
389/// This also includes additional eth functions required by optimism.
390///
391/// Specifically for the engine auth server: <https://github.com/ethereum/execution-apis/blob/main/src/engine/common.md#underlying-protocol>
392#[cfg_attr(not(feature = "client"), rpc(server, namespace = "eth"))]
393#[cfg_attr(feature = "client", rpc(server, client, namespace = "eth"))]
394pub trait EngineEthApi<TxReq: RpcObject, B: RpcObject, R: RpcObject, L: RpcObject> {
395    /// Returns an object with data about the sync status or false.
396    #[method(name = "syncing")]
397    fn syncing(&self) -> RpcResult<SyncStatus>;
398
399    /// Returns the chain ID of the current network.
400    #[method(name = "chainId")]
401    async fn chain_id(&self) -> RpcResult<Option<U64>>;
402
403    /// Returns the number of most recent block.
404    #[method(name = "blockNumber")]
405    fn block_number(&self) -> RpcResult<U256>;
406
407    /// Executes a new message call immediately without creating a transaction on the block chain.
408    #[method(name = "call")]
409    async fn call(
410        &self,
411        request: TxReq,
412        block_id: Option<BlockId>,
413        state_overrides: Option<StateOverride>,
414        block_overrides: Option<Box<BlockOverrides>>,
415    ) -> RpcResult<Bytes>;
416
417    /// Returns code at a given address at given block number.
418    #[method(name = "getCode")]
419    async fn get_code(&self, address: Address, block_id: Option<BlockId>) -> RpcResult<Bytes>;
420
421    /// Returns information about a block by hash.
422    #[method(name = "getBlockByHash")]
423    async fn block_by_hash(&self, hash: B256, full: bool) -> RpcResult<Option<B>>;
424
425    /// Returns information about a block by number.
426    #[method(name = "getBlockByNumber")]
427    async fn block_by_number(&self, number: BlockNumberOrTag, full: bool) -> RpcResult<Option<B>>;
428
429    /// Returns all transaction receipts for a given block.
430    #[method(name = "getBlockReceipts")]
431    async fn block_receipts(&self, block_id: BlockId) -> RpcResult<Option<Vec<R>>>;
432
433    /// Returns the EIP-2718 encoded transaction by block hash and transaction index position.
434    #[method(name = "getRawTransactionByBlockHashAndIndex")]
435    async fn raw_transaction_by_block_hash_and_index(
436        &self,
437        hash: B256,
438        index: Index,
439    ) -> RpcResult<Option<Bytes>>;
440
441    /// Returns the EIP-2718 encoded transaction by block number and transaction index position.
442    #[method(name = "getRawTransactionByBlockNumberAndIndex")]
443    async fn raw_transaction_by_block_number_and_index(
444        &self,
445        number: BlockNumberOrTag,
446        index: Index,
447    ) -> RpcResult<Option<Bytes>>;
448
449    /// Sends signed transaction, returning its hash.
450    #[method(name = "sendRawTransaction")]
451    async fn send_raw_transaction(&self, bytes: Bytes) -> RpcResult<B256>;
452
453    /// Returns the receipt of a transaction by transaction hash.
454    #[method(name = "getTransactionReceipt")]
455    async fn transaction_receipt(&self, hash: B256) -> RpcResult<Option<R>>;
456
457    /// Returns logs matching given filter object.
458    #[method(name = "getLogs")]
459    async fn logs(&self, filter: Filter) -> RpcResult<Vec<L>>;
460
461    /// Returns the account and storage values of the specified account including the Merkle-proof.
462    /// This call can be used to verify that the data you are pulling from is not tampered with.
463    #[method(name = "getProof")]
464    async fn get_proof(
465        &self,
466        address: Address,
467        keys: Vec<JsonStorageKey>,
468        block_number: Option<BlockId>,
469    ) -> RpcResult<EIP1186AccountProofResponse>;
470
471    /// Returns the account and storage values of the specified targets including Merkle proofs.
472    #[method(name = "getMultiProof")]
473    async fn get_multi_proof(
474        &self,
475        targets: Vec<(Address, Vec<B256>)>,
476        block_number: Option<BlockId>,
477    ) -> RpcResult<Vec<EIP1186AccountProofResponse>>;
478
479    /// Returns the EIP-7928 block access list for a block by hash.
480    #[method(name = "getBlockAccessListByBlockHash")]
481    async fn block_access_list_by_block_hash(&self, hash: B256) -> RpcResult<Option<Value>>;
482
483    /// Returns the EIP-7928 block access list for a block by number.
484    #[method(name = "getBlockAccessListByBlockNumber")]
485    async fn block_access_list_by_block_number(
486        &self,
487        number: BlockNumberOrTag,
488    ) -> RpcResult<Option<Value>>;
489
490    /// Returns the EIP-7928 block access list for a block by block id.
491    #[method(name = "getBlockAccessList")]
492    async fn block_access_list(&self, block_id: BlockId) -> RpcResult<Option<Value>>;
493
494    /// Returns the EIP-7928 block access list bytes for a block by number.
495    #[method(name = "getBlockAccessListRaw")]
496    async fn block_access_list_raw(&self, block: BlockId) -> RpcResult<Option<Bytes>>;
497}