diff --git a/Cargo.lock b/Cargo.lock index 4689de9884..a1a91c6b50 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7325,6 +7325,7 @@ dependencies = [ "reth-rpc", "reth-rpc-engine-api", "reth-rpc-layer", + "reth-rpc-types", "reth-stages", "reth-static-file", "reth-tasks", diff --git a/crates/node-core/src/version.rs b/crates/node-core/src/version.rs index 868fa933ea..79190531b3 100644 --- a/crates/node-core/src/version.rs +++ b/crates/node-core/src/version.rs @@ -1,6 +1,12 @@ //! Version information for reth. - use reth_db::models::client_version::ClientVersion; +use reth_rpc_types::engine::ClientCode; + +/// The client code for Reth +pub const CLIENT_CODE: ClientCode = ClientCode::RH; + +/// The human readable name of the client +pub const NAME_CLIENT: &str = "Reth"; /// The latest version from Cargo.toml. pub const CARGO_PKG_VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/crates/node/builder/Cargo.toml b/crates/node/builder/Cargo.toml index e36ac2e2c3..77f2cd2e88 100644 --- a/crates/node/builder/Cargo.toml +++ b/crates/node/builder/Cargo.toml @@ -39,7 +39,7 @@ reth-config.workspace = true reth-downloaders.workspace = true reth-node-events.workspace = true reth-consensus.workspace = true - +reth-rpc-types.workspace = true ## async futures.workspace = true tokio = { workspace = true, features = [ diff --git a/crates/node/builder/src/launch/mod.rs b/crates/node/builder/src/launch/mod.rs index 4987586bc9..fec043c7fa 100644 --- a/crates/node/builder/src/launch/mod.rs +++ b/crates/node/builder/src/launch/mod.rs @@ -25,11 +25,13 @@ use reth_node_core::{ dirs::{ChainPath, DataDirPath}, engine::EngineMessageStreamExt, exit::NodeExitFuture, + version::{CARGO_PKG_VERSION, CLIENT_CODE, NAME_CLIENT, VERGEN_GIT_SHA}, }; use reth_node_events::{cl::ConsensusLayerHealthEvents, node}; use reth_primitives::format_ether; use reth_provider::{providers::BlockchainProvider, CanonStateSubscriptions}; use reth_rpc_engine_api::EngineApi; +use reth_rpc_types::engine::ClientVersionV1; use reth_tasks::TaskExecutor; use reth_tracing::tracing::{debug, info}; use reth_transaction_pool::TransactionPool; @@ -407,12 +409,19 @@ where ), ); + let client = ClientVersionV1 { + code: CLIENT_CODE, + name: NAME_CLIENT.to_string(), + version: CARGO_PKG_VERSION.to_string(), + commit: VERGEN_GIT_SHA.to_string(), + }; let engine_api = EngineApi::new( blockchain_db.clone(), ctx.chain_spec(), beacon_engine_handle, node_adapter.components.payload_builder().clone().into(), Box::new(ctx.task_executor().clone()), + client, ); info!(target: "reth::cli", "Engine API handler initialized"); diff --git a/crates/rpc/rpc-api/src/engine.rs b/crates/rpc/rpc-api/src/engine.rs index d320c74601..be20a4fbe0 100644 --- a/crates/rpc/rpc-api/src/engine.rs +++ b/crates/rpc/rpc-api/src/engine.rs @@ -8,8 +8,9 @@ use reth_engine_primitives::EngineTypes; use reth_primitives::{Address, BlockHash, BlockId, BlockNumberOrTag, Bytes, B256, U256, U64}; use reth_rpc_types::{ engine::{ - ExecutionPayloadBodiesV1, ExecutionPayloadInputV2, ExecutionPayloadV1, ExecutionPayloadV3, - ForkchoiceState, ForkchoiceUpdated, PayloadId, PayloadStatus, TransitionConfiguration, + ClientVersionV1, ExecutionPayloadBodiesV1, ExecutionPayloadInputV2, ExecutionPayloadV1, + ExecutionPayloadV3, ForkchoiceState, ForkchoiceUpdated, PayloadId, PayloadStatus, + TransitionConfiguration, }, state::StateOverride, BlockOverrides, Filter, Log, RichBlock, SyncStatus, TransactionRequest, @@ -154,6 +155,22 @@ pub trait EngineApi { transition_configuration: TransitionConfiguration, ) -> RpcResult; + /// This function will return the ClientVersionV1 object. + /// See also: + /// make fmt + /// + /// + /// - When connected to a single execution client, the consensus client **MUST** receive an + /// array with a single `ClientVersionV1` object. + /// - When connected to multiple execution clients via a multiplexer, the multiplexer **MUST** + /// concatenate the responses from each execution client into a single, + /// flat array before returning the response to the consensus client. + #[method(name = "getClientVersionV1")] + async fn get_client_version_v1( + &self, + client_version: ClientVersionV1, + ) -> RpcResult>; + /// See also #[method(name = "exchangeCapabilities")] async fn exchange_capabilities(&self, capabilities: Vec) -> RpcResult>; diff --git a/crates/rpc/rpc-builder/tests/it/utils.rs b/crates/rpc/rpc-builder/tests/it/utils.rs index dd58bf2de2..819a3a863a 100644 --- a/crates/rpc/rpc-builder/tests/it/utils.rs +++ b/crates/rpc/rpc-builder/tests/it/utils.rs @@ -12,6 +12,7 @@ use reth_rpc_builder::{ }; use reth_rpc_engine_api::EngineApi; use reth_rpc_layer::JwtSecret; +use reth_rpc_types::engine::{ClientCode, ClientVersionV1}; use reth_tasks::TokioTaskExecutor; use reth_transaction_pool::test_utils::{TestPool, TestPoolBuilder}; use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; @@ -28,12 +29,20 @@ pub async fn launch_auth(secret: JwtSecret) -> AuthServerHandle { let (tx, _rx) = unbounded_channel(); let beacon_engine_handle = BeaconConsensusEngineHandle::::new(tx, Default::default()); + let client = ClientVersionV1 { + code: ClientCode::RH, + name: "Reth".to_string(), + version: "v0.2.0-beta.5".to_string(), + commit: "defa64b2".to_string(), + }; + let engine_api = EngineApi::new( NoopProvider::default(), MAINNET.clone(), beacon_engine_handle, spawn_test_payload_service().into(), Box::::default(), + client, ); let module = AuthRpcModule::new(engine_api); module.start_server(config).await.unwrap() diff --git a/crates/rpc/rpc-engine-api/src/engine_api.rs b/crates/rpc/rpc-engine-api/src/engine_api.rs index a2275281e6..8d51884d59 100644 --- a/crates/rpc/rpc-engine-api/src/engine_api.rs +++ b/crates/rpc/rpc-engine-api/src/engine_api.rs @@ -11,9 +11,10 @@ use reth_primitives::{BlockHash, BlockHashOrNumber, BlockNumber, ChainSpec, Hard use reth_provider::{BlockReader, EvmEnvProvider, HeaderProvider, StateProviderFactory}; use reth_rpc_api::EngineApiServer; use reth_rpc_types::engine::{ - CancunPayloadFields, ExecutionPayload, ExecutionPayloadBodiesV1, ExecutionPayloadInputV2, - ExecutionPayloadV1, ExecutionPayloadV3, ExecutionPayloadV4, ForkchoiceState, ForkchoiceUpdated, - PayloadId, PayloadStatus, TransitionConfiguration, CAPABILITIES, + CancunPayloadFields, ClientVersionV1, ExecutionPayload, ExecutionPayloadBodiesV1, + ExecutionPayloadInputV2, ExecutionPayloadV1, ExecutionPayloadV3, ExecutionPayloadV4, + ForkchoiceState, ForkchoiceUpdated, PayloadId, PayloadStatus, TransitionConfiguration, + CAPABILITIES, }; use reth_rpc_types_compat::engine::payload::{ convert_payload_input_v2_to_payload, convert_to_payload_body_v1, @@ -48,6 +49,8 @@ struct EngineApiInner { task_spawner: Box, /// The latency and response type metrics for engine api calls metrics: EngineApiMetrics, + /// Identification of the execution client used by the consensus client + client: ClientVersionV1, } impl EngineApi @@ -62,6 +65,7 @@ where beacon_consensus: BeaconConsensusEngineHandle, payload_store: PayloadStore, task_spawner: Box, + client: ClientVersionV1, ) -> Self { let inner = Arc::new(EngineApiInner { provider, @@ -70,10 +74,18 @@ where payload_store, task_spawner, metrics: EngineApiMetrics::default(), + client, }); Self { inner } } + /// Fetches the client version. + async fn get_client_version_v1( + &self, + _client: ClientVersionV1, + ) -> EngineApiResult> { + Ok(vec![self.inner.client.clone()]) + } /// Fetches the attributes for the payload with the given id. async fn get_payload_attributes( &self, @@ -749,6 +761,18 @@ where self.inner.metrics.latency.exchange_transition_configuration.record(start.elapsed()); Ok(res?) } + /// Handler for `engine_getClientVersionV1` + /// + /// See also + async fn get_client_version_v1( + &self, + client: ClientVersionV1, + ) -> RpcResult> { + trace!(target: "rpc::engine", "Serving engine_getClientVersionV1"); + let res = EngineApi::get_client_version_v1(self, client).await; + + Ok(res?) + } /// Handler for `engine_exchangeCapabilitiesV1` /// See also @@ -773,9 +797,11 @@ mod tests { use reth_beacon_consensus::{BeaconConsensusEngineEvent, BeaconEngineMessage}; use reth_ethereum_engine_primitives::EthEngineTypes; use reth_interfaces::test_utils::generators::random_block; + use reth_payload_builder::test_utils::spawn_test_payload_service; use reth_primitives::{SealedBlock, B256, MAINNET}; use reth_provider::test_utils::MockEthProvider; + use reth_rpc_types::engine::{ClientCode, ClientVersionV1}; use reth_rpc_types_compat::engine::payload::execution_payload_from_sealed_block; use reth_tasks::TokioTaskExecutor; use reth_tokio_util::EventSender; @@ -783,6 +809,13 @@ mod tests { fn setup_engine_api() -> (EngineApiTestHandle, EngineApi, EthEngineTypes>) { + let client = ClientVersionV1 { + code: ClientCode::RH, + name: "Reth".to_string(), + version: "v0.2.0-beta.5".to_string(), + commit: "defa64b2".to_string(), + }; + let chain_spec: Arc = MAINNET.clone(); let provider = Arc::new(MockEthProvider::default()); let payload_store = spawn_test_payload_service(); @@ -795,11 +828,25 @@ mod tests { BeaconConsensusEngineHandle::new(to_engine, event_sender), payload_store.into(), task_executor, + client, ); let handle = EngineApiTestHandle { chain_spec, provider, from_api: engine_rx }; (handle, api) } + #[tokio::test] + async fn engine_client_version_v1() { + let client = ClientVersionV1 { + code: ClientCode::RH, + name: "Reth".to_string(), + version: "v0.2.0-beta.5".to_string(), + commit: "defa64b2".to_string(), + }; + let (_, api) = setup_engine_api(); + let res = api.get_client_version_v1(client.clone()).await; + assert_eq!(res.unwrap(), vec![client]); + } + struct EngineApiTestHandle { chain_spec: Arc, provider: Arc,