Skip to content

Commit

Permalink
feat(api,rpc): improve engine API abstraction
Browse files Browse the repository at this point in the history
  • Loading branch information
fgimenez committed Feb 29, 2024
1 parent f829633 commit c703308
Show file tree
Hide file tree
Showing 8 changed files with 77 additions and 83 deletions.
15 changes: 14 additions & 1 deletion crates/node-api/src/engine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -29,7 +30,19 @@ pub trait EngineTypes:
+ Unpin;

/// The built payload type.
type BuiltPayload: BuiltPayload + Clone + Unpin;
type BuiltPayload: BuiltPayload
+ Clone
+ Unpin
+ TryInto<Self::ExecutionPayloadV1>
+ TryInto<Self::ExecutionPayloadV2>
+ TryInto<Self::ExecutionPayloadV3>;

/// 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.
Expand Down
15 changes: 1 addition & 14 deletions crates/node-api/src/engine/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Expand Down
11 changes: 10 additions & 1 deletion crates/node-ethereum/src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@ 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)]
Expand All @@ -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,
Expand Down
8 changes: 7 additions & 1 deletion crates/node-optimism/src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ use reth_node_api::{
};
use reth_payload_builder::{EthBuiltPayload, OptimismPayloadBuilderAttributes};
use reth_primitives::{ChainSpec, Hardfork};
use reth_rpc_types::engine::OptimismPayloadAttributes;
use reth_rpc_types::{
engine::{ExecutionPayloadEnvelopeV2, ExecutionPayloadEnvelopeV3, OptimismPayloadAttributes},
ExecutionPayloadV1,
};

/// The types used in the optimism beacon consensus engine.
#[derive(Debug, Default, Clone, serde::Deserialize)]
Expand All @@ -15,6 +18,9 @@ impl EngineTypes for OptimismEngineTypes {
type PayloadAttributes = OptimismPayloadAttributes;
type PayloadBuilderAttributes = OptimismPayloadBuilderAttributes;
type BuiltPayload = EthBuiltPayload;
type ExecutionPayloadV1 = ExecutionPayloadV1;
type ExecutionPayloadV2 = ExecutionPayloadEnvelopeV2;
type ExecutionPayloadV3 = ExecutionPayloadEnvelopeV3;

fn validate_version_specific_fields(
chain_spec: &ChainSpec,
Expand Down
27 changes: 0 additions & 27 deletions crates/payload/builder/src/payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,21 +62,6 @@ impl EthBuiltPayload {
pub fn extend_sidecars(&mut self, sidecars: Vec<BlobTransactionSidecar>) {
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 {
Expand All @@ -87,18 +72,6 @@ impl BuiltPayload for EthBuiltPayload {
fn fees(&self) -> U256 {
self.fees
}

fn into_v1_payload(self) -> ExecutionPayloadV1 {
self.into()
}

fn into_v2_payload(self) -> ExecutionPayloadEnvelopeV2 {
self.into()
}

fn into_v3_payload(self) -> ExecutionPayloadEnvelopeV3 {
self.into()
}
}

// V1 engine_getPayloadV1 response
Expand Down
11 changes: 5 additions & 6 deletions crates/rpc/rpc-api/src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -96,15 +95,15 @@ pub trait EngineApi<Engine: EngineTypes> {
/// 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<ExecutionPayloadV1>;
async fn get_payload_v1(&self, payload_id: PayloadId) -> RpcResult<Engine::ExecutionPayloadV1>;

/// See also <https://github.com/ethereum/execution-apis/blob/6709c2a795b707202e93c4f2867fa0bf2640a84f/src/engine/shanghai.md#engine_getpayloadv2>
///
/// Returns the most recent version of the payload that is available in the corresponding
/// 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<ExecutionPayloadEnvelopeV2>;
async fn get_payload_v2(&self, payload_id: PayloadId) -> RpcResult<Engine::ExecutionPayloadV2>;

/// Post Cancun payload handler which also returns a blobs bundle.
///
Expand All @@ -114,7 +113,7 @@ pub trait EngineApi<Engine: EngineTypes> {
/// 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<ExecutionPayloadEnvelopeV3>;
async fn get_payload_v3(&self, payload_id: PayloadId) -> RpcResult<Engine::ExecutionPayloadV3>;

/// See also <https://github.com/ethereum/execution-apis/blob/6452a6b194d7db269bf1dbd087a267251d3cc7f8/src/engine/shanghai.md#engine_getpayloadbodiesbyhashv1>
#[method(name = "getPayloadBodiesByHashV1")]
Expand Down
64 changes: 32 additions & 32 deletions crates/rpc/rpc-engine-api/src/engine_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -202,14 +202,15 @@ where
pub async fn get_payload_v1(
&self,
payload_id: PayloadId,
) -> EngineApiResult<ExecutionPayloadV1> {
Ok(self
.inner
) -> EngineApiResult<EngineT::ExecutionPayloadV1> {
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(|_| EngineApiError::UnknownPayload)
}

/// Returns the most recent version of the payload that is available in the corresponding
Expand All @@ -222,7 +223,7 @@ where
pub async fn get_payload_v2(
&self,
payload_id: PayloadId,
) -> EngineApiResult<ExecutionPayloadEnvelopeV2> {
) -> EngineApiResult<EngineT::ExecutionPayloadV2> {
// First we fetch the payload attributes to check the timestamp
let attributes = self.get_payload_attributes(payload_id).await?;

Expand All @@ -234,13 +235,14 @@ 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(|_| EngineApiError::UnknownPayload)
}

/// Returns the most recent version of the payload that is available in the corresponding
Expand All @@ -253,7 +255,7 @@ where
pub async fn get_payload_v3(
&self,
payload_id: PayloadId,
) -> EngineApiResult<ExecutionPayloadEnvelopeV3> {
) -> EngineApiResult<EngineT::ExecutionPayloadV3> {
// First we fetch the payload attributes to check the timestamp
let attributes = self.get_payload_attributes(payload_id).await?;

Expand All @@ -265,25 +267,14 @@ 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(|_| EngineApiError::UnknownPayload)
}

/// Returns the execution payload bodies by the range starting at `start`, containing `count`
Expand Down Expand Up @@ -581,7 +572,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<ExecutionPayloadV1> {
async fn get_payload_v1(
&self,
payload_id: PayloadId,
) -> RpcResult<EngineT::ExecutionPayloadV1> {
trace!(target: "rpc::engine", "Serving engine_getPayloadV1");
let start = Instant::now();
let res = EngineApi::get_payload_v1(self, payload_id).await;
Expand All @@ -598,7 +592,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<ExecutionPayloadEnvelopeV2> {
async fn get_payload_v2(
&self,
payload_id: PayloadId,
) -> RpcResult<EngineT::ExecutionPayloadV2> {
trace!(target: "rpc::engine", "Serving engine_getPayloadV2");
let start = Instant::now();
let res = EngineApi::get_payload_v2(self, payload_id).await;
Expand All @@ -615,7 +612,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<ExecutionPayloadEnvelopeV3> {
async fn get_payload_v3(
&self,
payload_id: PayloadId,
) -> RpcResult<EngineT::ExecutionPayloadV3> {
trace!(target: "rpc::engine", "Serving engine_getPayloadV3");
let start = Instant::now();
let res = EngineApi::get_payload_v3(self, payload_id).await;
Expand Down
9 changes: 8 additions & 1 deletion examples/custom-node/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,12 @@ use reth_primitives::{
};
use reth_rpc_api::{EngineApiClient, EthApiClient};
use reth_rpc_types::{
engine::{ForkchoiceState, PayloadAttributes as EthPayloadAttributes, PayloadId},
engine::{
ExecutionPayloadEnvelopeV2, ExecutionPayloadEnvelopeV3, ForkchoiceState,
PayloadAttributes as EthPayloadAttributes, PayloadId,
},
withdrawal::Withdrawal,
ExecutionPayloadV1,
};
use serde::{Deserialize, Serialize};
use std::convert::Infallible;
Expand Down Expand Up @@ -180,6 +184,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,
Expand Down

0 comments on commit c703308

Please sign in to comment.