From 76f297da1219b58581765e0a4cb17fea39ce16be Mon Sep 17 00:00:00 2001
From: PG Herveou <pgherveou@gmail.com>
Date: Tue, 5 Nov 2024 15:41:06 +0100
Subject: [PATCH] [eth-rpc] proxy /health (#6360)

make the eth-rpc proxy /health and /health/readiness from the proxied
substrate chain
see #4802

---------

Co-authored-by: GitHub Action <action@github.com>
---
 Cargo.lock                                   |  1 +
 prdoc/pr_6360.prdoc                          |  9 ++++
 substrate/frame/revive/rpc/Cargo.toml        |  1 +
 substrate/frame/revive/rpc/src/cli.rs        | 15 ++++--
 substrate/frame/revive/rpc/src/client.rs     |  9 +++-
 substrate/frame/revive/rpc/src/lib.rs        |  3 ++
 substrate/frame/revive/rpc/src/rpc_health.rs | 50 ++++++++++++++++++++
 7 files changed, 84 insertions(+), 4 deletions(-)
 create mode 100644 prdoc/pr_6360.prdoc
 create mode 100644 substrate/frame/revive/rpc/src/rpc_health.rs

diff --git a/Cargo.lock b/Cargo.lock
index 6703a0250de..a8e7d7d4cdd 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -12562,6 +12562,7 @@ dependencies = [
  "rlp 0.6.1",
  "sc-cli",
  "sc-rpc",
+ "sc-rpc-api",
  "sc-service",
  "scale-info",
  "secp256k1",
diff --git a/prdoc/pr_6360.prdoc b/prdoc/pr_6360.prdoc
new file mode 100644
index 00000000000..270af29e37a
--- /dev/null
+++ b/prdoc/pr_6360.prdoc
@@ -0,0 +1,9 @@
+title: '[eth-rpc] proxy /health'
+doc:
+- audience: Runtime Dev
+  description: |-
+    make the eth-rpc proxy /health and /health/readiness from the proxied substrate chain
+    see #4802
+crates:
+- name: pallet-revive-eth-rpc
+  bump: minor
diff --git a/substrate/frame/revive/rpc/Cargo.toml b/substrate/frame/revive/rpc/Cargo.toml
index 56db91f920f..8bf93024024 100644
--- a/substrate/frame/revive/rpc/Cargo.toml
+++ b/substrate/frame/revive/rpc/Cargo.toml
@@ -56,6 +56,7 @@ sp-core = { workspace = true, default-features = true }
 sp-weights = { workspace = true, default-features = true }
 sp-runtime = { workspace = true, default-features = true }
 sc-rpc = { workspace = true, default-features = true }
+sc-rpc-api = { workspace = true, default-features = true }
 sc-cli = { workspace = true, default-features = true }
 sc-service = { workspace = true, default-features = true }
 prometheus-endpoint = { workspace = true, default-features = true }
diff --git a/substrate/frame/revive/rpc/src/cli.rs b/substrate/frame/revive/rpc/src/cli.rs
index fcb84e6b54b..c0f81fcafd7 100644
--- a/substrate/frame/revive/rpc/src/cli.rs
+++ b/substrate/frame/revive/rpc/src/cli.rs
@@ -15,7 +15,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 //! The Ethereum JSON-RPC server.
-use crate::{client::Client, EthRpcServer, EthRpcServerImpl};
+use crate::{
+	client::Client, EthRpcServer, EthRpcServerImpl, SystemHealthRpcServer,
+	SystemHealthRpcServerImpl,
+};
 use clap::Parser;
 use futures::{pin_mut, FutureExt};
 use jsonrpsee::server::RpcModule;
@@ -118,7 +121,10 @@ pub fn run(cmd: CliCommand) -> anyhow::Result<()> {
 
 		match tokio_handle.block_on(signals.try_until_signal(fut)) {
 			Ok(Ok(client)) => rpc_module(is_dev, client),
-			Ok(Err(err)) => Err(sc_service::Error::Application(err.into())),
+			Ok(Err(err)) => {
+				log::error!("Error connecting to the node at {node_rpc_url}: {err}");
+				Err(sc_service::Error::Application(err.into()))
+			},
 			Err(_) => Err(sc_service::Error::Application("Client connection interrupted".into())),
 		}
 	};
@@ -142,11 +148,14 @@ pub fn run(cmd: CliCommand) -> anyhow::Result<()> {
 
 /// Create the JSON-RPC module.
 fn rpc_module(is_dev: bool, client: Client) -> Result<RpcModule<()>, sc_service::Error> {
-	let eth_api = EthRpcServerImpl::new(client)
+	let eth_api = EthRpcServerImpl::new(client.clone())
 		.with_accounts(if is_dev { vec![crate::Account::default()] } else { vec![] })
 		.into_rpc();
 
+	let health_api = SystemHealthRpcServerImpl::new(client).into_rpc();
+
 	let mut module = RpcModule::new(());
 	module.merge(eth_api).map_err(|e| sc_service::Error::Application(e.into()))?;
+	module.merge(health_api).map_err(|e| sc_service::Error::Application(e.into()))?;
 	Ok(module)
 }
diff --git a/substrate/frame/revive/rpc/src/client.rs b/substrate/frame/revive/rpc/src/client.rs
index ba93d0af62a..a0552189f44 100644
--- a/substrate/frame/revive/rpc/src/client.rs
+++ b/substrate/frame/revive/rpc/src/client.rs
@@ -44,7 +44,7 @@ use std::{
 };
 use subxt::{
 	backend::{
-		legacy::LegacyRpcMethods,
+		legacy::{rpc_methods::SystemHealth, LegacyRpcMethods},
 		rpc::{
 			reconnecting_rpc_client::{Client as ReconnectingRpcClient, ExponentialBackoff},
 			RpcClient,
@@ -192,6 +192,7 @@ impl<const N: usize> BlockCache<N> {
 }
 
 /// A client connect to a node and maintains a cache of the last `CACHE_SIZE` blocks.
+#[derive(Clone)]
 pub struct Client {
 	/// The inner state of the client.
 	inner: Arc<ClientInner>,
@@ -555,6 +556,12 @@ impl Client {
 		cache.tx_hashes_by_block_and_index.get(block_hash).map(|v| v.len())
 	}
 
+	/// Get the system health.
+	pub async fn system_health(&self) -> Result<SystemHealth, ClientError> {
+		let health = self.inner.rpc.system_health().await?;
+		Ok(health)
+	}
+
 	/// Get the balance of the given address.
 	pub async fn balance(
 		&self,
diff --git a/substrate/frame/revive/rpc/src/lib.rs b/substrate/frame/revive/rpc/src/lib.rs
index a6d47063ef9..88a3cb64178 100644
--- a/substrate/frame/revive/rpc/src/lib.rs
+++ b/substrate/frame/revive/rpc/src/lib.rs
@@ -35,6 +35,9 @@ pub mod subxt_client;
 #[cfg(test)]
 mod tests;
 
+mod rpc_health;
+pub use rpc_health::*;
+
 mod rpc_methods_gen;
 pub use rpc_methods_gen::*;
 
diff --git a/substrate/frame/revive/rpc/src/rpc_health.rs b/substrate/frame/revive/rpc/src/rpc_health.rs
new file mode 100644
index 00000000000..f94d4b82a80
--- /dev/null
+++ b/substrate/frame/revive/rpc/src/rpc_health.rs
@@ -0,0 +1,50 @@
+// This file is part of Substrate.
+
+// Copyright (C) Parity Technologies (UK) Ltd.
+// SPDX-License-Identifier: Apache-2.0
+
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 	http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//! Heatlh JSON-RPC methods.
+
+use super::*;
+use jsonrpsee::{core::RpcResult, proc_macros::rpc};
+use sc_rpc_api::system::helpers::Health;
+
+#[rpc(server, client)]
+pub trait SystemHealthRpc {
+	/// Proxy the substrate chain system_health RPC call.
+	#[method(name = "system_health")]
+	async fn system_health(&self) -> RpcResult<Health>;
+}
+
+pub struct SystemHealthRpcServerImpl {
+	client: client::Client,
+}
+
+impl SystemHealthRpcServerImpl {
+	pub fn new(client: client::Client) -> Self {
+		Self { client }
+	}
+}
+
+#[async_trait]
+impl SystemHealthRpcServer for SystemHealthRpcServerImpl {
+	async fn system_health(&self) -> RpcResult<Health> {
+		let health = self.client.system_health().await?;
+		Ok(Health {
+			peers: health.peers,
+			is_syncing: health.is_syncing,
+			should_have_peers: health.should_have_peers,
+		})
+	}
+}
-- 
GitLab