From 44e0e0907e388c49ba3931bd53673d61160aa7e3 Mon Sep 17 00:00:00 2001 From: Arseni Kalma Date: Wed, 3 Sep 2025 09:14:21 +0200 Subject: [PATCH 1/5] Add health check endpoint code --- zebra-rpc/Cargo.toml | 3 + zebra-rpc/src/methods.rs | 68 +++++++++++++++++++ zebra-rpc/src/methods/tests/snapshot.rs | 19 ++++++ .../get_health_info_status@mainnet_10.snap | 8 +++ .../get_health_info_status@testnet_10.snap | 8 +++ zebra-rpc/src/server.rs | 9 ++- zebrad/Cargo.toml | 7 +- zebrad/tests/acceptance.rs | 29 ++++++++ 8 files changed, 148 insertions(+), 3 deletions(-) create mode 100644 zebra-rpc/src/methods/tests/snapshots/get_health_info_status@mainnet_10.snap create mode 100644 zebra-rpc/src/methods/tests/snapshots/get_health_info_status@testnet_10.snap diff --git a/zebra-rpc/Cargo.toml b/zebra-rpc/Cargo.toml index 1562a77677e..4dfae1f97ea 100644 --- a/zebra-rpc/Cargo.toml +++ b/zebra-rpc/Cargo.toml @@ -40,6 +40,9 @@ getblocktemplate-rpcs = [ "zebra-chain/getblocktemplate-rpcs", ] +# Health RPC support +gethealthinfo-rpc = [] + # Experimental internal miner support internal-miner = [] diff --git a/zebra-rpc/src/methods.rs b/zebra-rpc/src/methods.rs index 8becc5bb79c..f3aa86c4ab9 100644 --- a/zebra-rpc/src/methods.rs +++ b/zebra-rpc/src/methods.rs @@ -59,6 +59,23 @@ mod tests; #[rpc(server)] /// RPC method signatures. pub trait Rpc { + // TODO: Consider mentioning this new method in the zcashd RPC docs and add a reference to it here. + /// Returns node health and build metadata, as a [`GetHealthInfo`] JSON struct. + /// + /// zcashd reference: none + /// method: post + /// tags: control + /// + /// # Notes + /// + /// - This method provides a simple liveness/readiness signal and basic build info. + /// - When the HTTP health endpoint is enabled via `ServerBuilder::health_api`, + /// it is also available as `GET /health` (no parameters). + // NOTE: We can't put a cfg with a feature flag on this method because #[rpc] ignores it. + // So we have to provide a dummy struct and a dummy impl below to support builds without the feature. + #[rpc(name = "gethealthinfo")] + fn get_health_info(&self) -> Result; + #[rpc(name = "getinfo")] /// Returns software information from the RPC server, as a [`GetInfo`] JSON struct. /// @@ -506,6 +523,17 @@ where State::Future: Send, Tip: ChainTip + Clone + Send + Sync + 'static, { + #[cfg(feature = "gethealthinfo-rpc")] + fn get_health_info(&self) -> Result { + Ok(GetHealthInfo::snapshot()) + } + + // Dummy impl: return MethodNotFound if the feature is disabled. + #[cfg(not(feature = "gethealthinfo-rpc"))] + fn get_health_info(&self) -> Result { + Err(jsonrpc_core::Error::method_not_found()) + } + fn get_info(&self) -> Result { let response = GetInfo { build: self.build_version.clone(), @@ -1396,6 +1424,46 @@ where .ok_or_server_error("No blocks in state") } +/// Response to a `gethealthinfo` RPC request. +/// +/// See the notes for the [`Rpc::get_health_info` method]. +#[cfg(feature = "gethealthinfo-rpc")] +#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct GetHealthInfo { + /// Static health status string. + status: String, + /// Zebra package version + version: String, + /// Build Git tag (if available). + git_tag: String, + /// Full Git commit hash (if available). + git_commit: String, + /// Server timestamp in RFC 3339 format. + timestamp: String, +} + +// Dummy type so the trait always compiles when the feature is off. +#[cfg(not(feature = "gethealthinfo-rpc"))] +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +pub struct GetHealthInfo; + +#[cfg(feature = "gethealthinfo-rpc")] +impl GetHealthInfo { + /// Creates a snapshot of the node's health and build metadata. + #[inline] + pub fn snapshot() -> Self { + Self { + status: "healthy".to_string(), + version: env!("CARGO_PKG_VERSION").to_string(), + git_tag: option_env!("GIT_TAG").unwrap_or("unknown").to_string(), + git_commit: option_env!("GIT_COMMIT_FULL") + .unwrap_or("unknown") + .to_string(), + timestamp: chrono::Utc::now().to_rfc3339(), + } + } +} + /// Response to a `getinfo` RPC request. /// /// See the notes for the [`Rpc::get_info` method]. diff --git a/zebra-rpc/src/methods/tests/snapshot.rs b/zebra-rpc/src/methods/tests/snapshot.rs index f4d7804088e..e9d0a441933 100644 --- a/zebra-rpc/src/methods/tests/snapshot.rs +++ b/zebra-rpc/src/methods/tests/snapshot.rs @@ -217,6 +217,15 @@ async fn test_rpc_response_data_for_network(network: &Network) { return; } + // `gethealthinfo` + #[cfg(feature = "gethealthinfo-rpc")] + { + let get_health_info = rpc + .get_health_info() + .expect("We should have a GetHealthInfo struct"); + snapshot_rpc_gethealthinfo(get_health_info, &settings); + } + // `getinfo` let get_info = rpc.get_info().expect("We should have a GetInfo struct"); snapshot_rpc_getinfo(get_info, &settings); @@ -538,6 +547,16 @@ async fn test_mocked_rpc_response_data_for_network(network: &Network) { }); } +/// Snapshot `gethealthinfo` response, using `cargo insta` and JSON serialization. +#[cfg(feature = "gethealthinfo-rpc")] +fn snapshot_rpc_gethealthinfo(info: GetHealthInfo, settings: &insta::Settings) { + // Snapshot only the `status` field since other fields vary per build/run. + let status_only = serde_json::json!({ "status": info.status }); + settings.bind(|| { + insta::assert_json_snapshot!("get_health_info_status", status_only); + }); +} + /// Snapshot `getinfo` response, using `cargo insta` and JSON serialization. fn snapshot_rpc_getinfo(info: GetInfo, settings: &insta::Settings) { settings.bind(|| { diff --git a/zebra-rpc/src/methods/tests/snapshots/get_health_info_status@mainnet_10.snap b/zebra-rpc/src/methods/tests/snapshots/get_health_info_status@mainnet_10.snap new file mode 100644 index 00000000000..050e5d6cc43 --- /dev/null +++ b/zebra-rpc/src/methods/tests/snapshots/get_health_info_status@mainnet_10.snap @@ -0,0 +1,8 @@ +--- +source: zebra-rpc/src/methods/tests/snapshot.rs +assertion_line: 556 +expression: status_only +--- +{ + "status": "healthy" +} diff --git a/zebra-rpc/src/methods/tests/snapshots/get_health_info_status@testnet_10.snap b/zebra-rpc/src/methods/tests/snapshots/get_health_info_status@testnet_10.snap new file mode 100644 index 00000000000..050e5d6cc43 --- /dev/null +++ b/zebra-rpc/src/methods/tests/snapshots/get_health_info_status@testnet_10.snap @@ -0,0 +1,8 @@ +--- +source: zebra-rpc/src/methods/tests/snapshot.rs +assertion_line: 556 +expression: status_only +--- +{ + "status": "healthy" +} diff --git a/zebra-rpc/src/server.rs b/zebra-rpc/src/server.rs index 73fcde65f6b..d30d9e73ccc 100644 --- a/zebra-rpc/src/server.rs +++ b/zebra-rpc/src/server.rs @@ -213,11 +213,16 @@ impl RpcServer { // Use a different tokio executor from the rest of Zebra, // so that large RPCs and any task handling bugs don't impact Zebra. - let server_instance = ServerBuilder::new(io) + let builder = ServerBuilder::new(io) .threads(parallel_cpu_threads) // TODO: disable this security check if we see errors from lightwalletd //.allowed_hosts(DomainsValidation::Disabled) - .request_middleware(middleware) + .request_middleware(middleware); + + #[cfg(feature = "gethealthinfo-rpc")] + let builder = builder.health_api(("/health", "gethealthinfo")); + + let server_instance = builder .start_http(&listen_addr) .expect("Unable to start RPC server"); diff --git a/zebrad/Cargo.toml b/zebrad/Cargo.toml index 72a8afd7dc0..2086810d71f 100644 --- a/zebrad/Cargo.toml +++ b/zebrad/Cargo.toml @@ -52,7 +52,7 @@ features = [ [features] # In release builds, don't compile debug logging code, to improve performance. -default = ["release_max_level_info", "progress-bar", "getblocktemplate-rpcs"] +default = ["release_max_level_info", "progress-bar", "getblocktemplate-rpcs", "gethealthinfo-rpc"] # Default features for official ZF binary release builds default-release-binaries = ["default", "sentry"] @@ -71,6 +71,11 @@ getblocktemplate-rpcs = [ "zebra-chain/getblocktemplate-rpcs", ] +# Health RPC support +gethealthinfo-rpc = [ + "zebra-rpc/gethealthinfo-rpc" +] + # Experimental internal miner support internal-miner = [ "thread-priority", diff --git a/zebrad/tests/acceptance.rs b/zebrad/tests/acceptance.rs index 4c717f62eb0..4be4ae5ab2c 100644 --- a/zebrad/tests/acceptance.rs +++ b/zebrad/tests/acceptance.rs @@ -1594,6 +1594,18 @@ async fn rpc_endpoint(parallel_cpu_threads: bool) -> Result<()> { // Create an http client let client = RpcRequestClient::new(rpc_address); + // Make the call to the `gethealthinfo` RPC method if feature is enabled. + #[cfg(feature = "gethealthinfo-rpc")] + { + let res = client.call("gethealthinfo", "[]".to_string()).await?; + assert!(res.status().is_success()); + + let body = res.bytes().await?; + let parsed: Value = serde_json::from_slice(&body)?; + let status = parsed["result"]["status"].as_str().unwrap(); + assert_eq!(status, "healthy"); + } + // Make the call to the `getinfo` RPC method let res = client.call("getinfo", "[]".to_string()).await?; @@ -1651,6 +1663,23 @@ async fn rpc_endpoint_client_content_type() -> Result<()> { // Create an http client let client = RpcRequestClient::new(rpc_address); + #[cfg(feature = "gethealthinfo-rpc")] + { + // Just test with plain content type, similar to getinfo. + let res = client + .call_with_content_type( + "gethealthinfo", + "[]".to_string(), + "application/json".to_string(), + ) + .await?; + assert!(res.status().is_success()); + + let body = res.bytes().await?; + let parsed: Value = serde_json::from_slice(&body)?; + assert_eq!(parsed["result"]["status"], "healthy"); + } + // Call to `getinfo` RPC method with a no content type. let res = client .call_with_no_content_type("getinfo", "[]".to_string()) From 26599d6c979f684b8e50f8c624100bf0e58387e5 Mon Sep 17 00:00:00 2001 From: Arseni Kalma Date: Mon, 15 Sep 2025 09:01:43 +0200 Subject: [PATCH 2/5] PR comment fixes --- zebra-rpc/Cargo.toml | 2 - zebra-rpc/src/methods.rs | 61 +++++++++---------------- zebra-rpc/src/methods/tests/snapshot.rs | 34 ++++++-------- zebra-rpc/src/server.rs | 10 ++-- zebrad/Cargo.toml | 6 +-- zebrad/tests/acceptance.rs | 46 ++++++++----------- 6 files changed, 61 insertions(+), 98 deletions(-) diff --git a/zebra-rpc/Cargo.toml b/zebra-rpc/Cargo.toml index 4dfae1f97ea..59bc6c3f08e 100644 --- a/zebra-rpc/Cargo.toml +++ b/zebra-rpc/Cargo.toml @@ -40,8 +40,6 @@ getblocktemplate-rpcs = [ "zebra-chain/getblocktemplate-rpcs", ] -# Health RPC support -gethealthinfo-rpc = [] # Experimental internal miner support internal-miner = [] diff --git a/zebra-rpc/src/methods.rs b/zebra-rpc/src/methods.rs index f3aa86c4ab9..2e0c62b670e 100644 --- a/zebra-rpc/src/methods.rs +++ b/zebra-rpc/src/methods.rs @@ -59,23 +59,6 @@ mod tests; #[rpc(server)] /// RPC method signatures. pub trait Rpc { - // TODO: Consider mentioning this new method in the zcashd RPC docs and add a reference to it here. - /// Returns node health and build metadata, as a [`GetHealthInfo`] JSON struct. - /// - /// zcashd reference: none - /// method: post - /// tags: control - /// - /// # Notes - /// - /// - This method provides a simple liveness/readiness signal and basic build info. - /// - When the HTTP health endpoint is enabled via `ServerBuilder::health_api`, - /// it is also available as `GET /health` (no parameters). - // NOTE: We can't put a cfg with a feature flag on this method because #[rpc] ignores it. - // So we have to provide a dummy struct and a dummy impl below to support builds without the feature. - #[rpc(name = "gethealthinfo")] - fn get_health_info(&self) -> Result; - #[rpc(name = "getinfo")] /// Returns software information from the RPC server, as a [`GetInfo`] JSON struct. /// @@ -319,6 +302,20 @@ pub trait Rpc { address_strings: AddressStrings, ) -> BoxFuture>>; + /// Returns node health and build metadata, as a [`GetHealthInfo`] JSON struct. + /// + /// zcashd reference: none + /// method: post + /// tags: control + /// + /// # Notes + /// + /// - This method provides a simple liveness/readiness signal and basic build info. + /// - When the HTTP health endpoint is enabled via `ServerBuilder::health_api`, + /// it is also available as `GET /health` (no parameters). + #[rpc(name = "gethealthinfo")] + fn get_health_info(&self) -> Result; + /// Stop the running zebrad process. /// /// # Notes @@ -523,17 +520,6 @@ where State::Future: Send, Tip: ChainTip + Clone + Send + Sync + 'static, { - #[cfg(feature = "gethealthinfo-rpc")] - fn get_health_info(&self) -> Result { - Ok(GetHealthInfo::snapshot()) - } - - // Dummy impl: return MethodNotFound if the feature is disabled. - #[cfg(not(feature = "gethealthinfo-rpc"))] - fn get_health_info(&self) -> Result { - Err(jsonrpc_core::Error::method_not_found()) - } - fn get_info(&self) -> Result { let response = GetInfo { build: self.build_version.clone(), @@ -1386,6 +1372,10 @@ where .boxed() } + fn get_health_info(&self) -> Result { + Ok(GetHealthInfo::new()) + } + fn stop(&self) -> Result { #[cfg(not(target_os = "windows"))] if self.network.is_regtest() { @@ -1427,7 +1417,6 @@ where /// Response to a `gethealthinfo` RPC request. /// /// See the notes for the [`Rpc::get_health_info` method]. -#[cfg(feature = "gethealthinfo-rpc")] #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] pub struct GetHealthInfo { /// Static health status string. @@ -1442,24 +1431,18 @@ pub struct GetHealthInfo { timestamp: String, } -// Dummy type so the trait always compiles when the feature is off. -#[cfg(not(feature = "gethealthinfo-rpc"))] -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] -pub struct GetHealthInfo; - -#[cfg(feature = "gethealthinfo-rpc")] impl GetHealthInfo { - /// Creates a snapshot of the node's health and build metadata. + /// Creates a new health info instance with current node status and build metadata. #[inline] - pub fn snapshot() -> Self { + pub fn new() -> Self { Self { - status: "healthy".to_string(), + status: "healthy".into(), version: env!("CARGO_PKG_VERSION").to_string(), git_tag: option_env!("GIT_TAG").unwrap_or("unknown").to_string(), git_commit: option_env!("GIT_COMMIT_FULL") .unwrap_or("unknown") .to_string(), - timestamp: chrono::Utc::now().to_rfc3339(), + timestamp: Utc::now().to_rfc3339(), } } } diff --git a/zebra-rpc/src/methods/tests/snapshot.rs b/zebra-rpc/src/methods/tests/snapshot.rs index e9d0a441933..f1f5002627f 100644 --- a/zebra-rpc/src/methods/tests/snapshot.rs +++ b/zebra-rpc/src/methods/tests/snapshot.rs @@ -217,15 +217,6 @@ async fn test_rpc_response_data_for_network(network: &Network) { return; } - // `gethealthinfo` - #[cfg(feature = "gethealthinfo-rpc")] - { - let get_health_info = rpc - .get_health_info() - .expect("We should have a GetHealthInfo struct"); - snapshot_rpc_gethealthinfo(get_health_info, &settings); - } - // `getinfo` let get_info = rpc.get_info().expect("We should have a GetInfo struct"); snapshot_rpc_getinfo(get_info, &settings); @@ -467,6 +458,12 @@ async fn test_rpc_response_data_for_network(network: &Network) { .await .expect("We should have a vector of strings"); snapshot_rpc_getaddressutxos(get_address_utxos, &settings); + + // `gethealthinfo` + let get_health_info = rpc + .get_health_info() + .expect("We should have a GetHealthInfo struct"); + snapshot_rpc_gethealthinfo(get_health_info, &settings); } async fn test_mocked_rpc_response_data_for_network(network: &Network) { @@ -547,16 +544,6 @@ async fn test_mocked_rpc_response_data_for_network(network: &Network) { }); } -/// Snapshot `gethealthinfo` response, using `cargo insta` and JSON serialization. -#[cfg(feature = "gethealthinfo-rpc")] -fn snapshot_rpc_gethealthinfo(info: GetHealthInfo, settings: &insta::Settings) { - // Snapshot only the `status` field since other fields vary per build/run. - let status_only = serde_json::json!({ "status": info.status }); - settings.bind(|| { - insta::assert_json_snapshot!("get_health_info_status", status_only); - }); -} - /// Snapshot `getinfo` response, using `cargo insta` and JSON serialization. fn snapshot_rpc_getinfo(info: GetInfo, settings: &insta::Settings) { settings.bind(|| { @@ -688,6 +675,15 @@ fn snapshot_rpc_getaddressutxos(utxos: Vec, settings: &insta::S settings.bind(|| insta::assert_json_snapshot!("get_address_utxos", utxos)); } +/// Snapshot `gethealthinfo` response, using `cargo insta` and JSON serialization. +fn snapshot_rpc_gethealthinfo(info: GetHealthInfo, settings: &insta::Settings) { + // Snapshot only the `status` field since other fields vary per build/run. + let status_only = serde_json::json!({ "status": info.status }); + settings.bind(|| { + insta::assert_json_snapshot!("get_health_info_status", status_only); + }); +} + /// Utility function to convert a `Network` to a lowercase string. fn network_string(network: &Network) -> String { let mut net_suffix = network.to_string(); diff --git a/zebra-rpc/src/server.rs b/zebra-rpc/src/server.rs index d30d9e73ccc..d5b726a7180 100644 --- a/zebra-rpc/src/server.rs +++ b/zebra-rpc/src/server.rs @@ -213,16 +213,12 @@ impl RpcServer { // Use a different tokio executor from the rest of Zebra, // so that large RPCs and any task handling bugs don't impact Zebra. - let builder = ServerBuilder::new(io) + let server_instance = ServerBuilder::new(io) .threads(parallel_cpu_threads) // TODO: disable this security check if we see errors from lightwalletd //.allowed_hosts(DomainsValidation::Disabled) - .request_middleware(middleware); - - #[cfg(feature = "gethealthinfo-rpc")] - let builder = builder.health_api(("/health", "gethealthinfo")); - - let server_instance = builder + .request_middleware(middleware) + .health_api(("/health", "gethealthinfo")) .start_http(&listen_addr) .expect("Unable to start RPC server"); diff --git a/zebrad/Cargo.toml b/zebrad/Cargo.toml index 2086810d71f..7ce09908e98 100644 --- a/zebrad/Cargo.toml +++ b/zebrad/Cargo.toml @@ -52,7 +52,7 @@ features = [ [features] # In release builds, don't compile debug logging code, to improve performance. -default = ["release_max_level_info", "progress-bar", "getblocktemplate-rpcs", "gethealthinfo-rpc"] +default = ["release_max_level_info", "progress-bar", "getblocktemplate-rpcs"] # Default features for official ZF binary release builds default-release-binaries = ["default", "sentry"] @@ -71,10 +71,6 @@ getblocktemplate-rpcs = [ "zebra-chain/getblocktemplate-rpcs", ] -# Health RPC support -gethealthinfo-rpc = [ - "zebra-rpc/gethealthinfo-rpc" -] # Experimental internal miner support internal-miner = [ diff --git a/zebrad/tests/acceptance.rs b/zebrad/tests/acceptance.rs index 4be4ae5ab2c..b22cdc70194 100644 --- a/zebrad/tests/acceptance.rs +++ b/zebrad/tests/acceptance.rs @@ -1594,17 +1594,14 @@ async fn rpc_endpoint(parallel_cpu_threads: bool) -> Result<()> { // Create an http client let client = RpcRequestClient::new(rpc_address); - // Make the call to the `gethealthinfo` RPC method if feature is enabled. - #[cfg(feature = "gethealthinfo-rpc")] - { - let res = client.call("gethealthinfo", "[]".to_string()).await?; - assert!(res.status().is_success()); + // Make the call to the `gethealthinfo` RPC method. + let res = client.call("gethealthinfo", "[]".to_string()).await?; + assert!(res.status().is_success()); - let body = res.bytes().await?; - let parsed: Value = serde_json::from_slice(&body)?; - let status = parsed["result"]["status"].as_str().unwrap(); - assert_eq!(status, "healthy"); - } + let body = res.bytes().await?; + let parsed: Value = serde_json::from_slice(&body)?; + let status = parsed["result"]["status"].as_str().unwrap(); + assert_eq!(status, "healthy"); // Make the call to the `getinfo` RPC method let res = client.call("getinfo", "[]".to_string()).await?; @@ -1663,22 +1660,19 @@ async fn rpc_endpoint_client_content_type() -> Result<()> { // Create an http client let client = RpcRequestClient::new(rpc_address); - #[cfg(feature = "gethealthinfo-rpc")] - { - // Just test with plain content type, similar to getinfo. - let res = client - .call_with_content_type( - "gethealthinfo", - "[]".to_string(), - "application/json".to_string(), - ) - .await?; - assert!(res.status().is_success()); - - let body = res.bytes().await?; - let parsed: Value = serde_json::from_slice(&body)?; - assert_eq!(parsed["result"]["status"], "healthy"); - } + // Just test with plain content type, similar to getinfo. + let res = client + .call_with_content_type( + "gethealthinfo", + "[]".to_string(), + "application/json".to_string(), + ) + .await?; + assert!(res.status().is_success()); + + let body = res.bytes().await?; + let parsed: Value = serde_json::from_slice(&body)?; + assert_eq!(parsed["result"]["status"], "healthy"); // Call to `getinfo` RPC method with a no content type. let res = client From be078e0320bd2062b57bdb8cdb545d85ef0f0871 Mon Sep 17 00:00:00 2001 From: Arseni Kalma Date: Mon, 15 Sep 2025 09:16:28 +0200 Subject: [PATCH 3/5] Add last changes to into, and fix new line --- zebra-rpc/Cargo.toml | 1 - zebra-rpc/src/methods.rs | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/zebra-rpc/Cargo.toml b/zebra-rpc/Cargo.toml index 59bc6c3f08e..1562a77677e 100644 --- a/zebra-rpc/Cargo.toml +++ b/zebra-rpc/Cargo.toml @@ -40,7 +40,6 @@ getblocktemplate-rpcs = [ "zebra-chain/getblocktemplate-rpcs", ] - # Experimental internal miner support internal-miner = [] diff --git a/zebra-rpc/src/methods.rs b/zebra-rpc/src/methods.rs index 2e0c62b670e..6c092989446 100644 --- a/zebra-rpc/src/methods.rs +++ b/zebra-rpc/src/methods.rs @@ -1437,11 +1437,11 @@ impl GetHealthInfo { pub fn new() -> Self { Self { status: "healthy".into(), - version: env!("CARGO_PKG_VERSION").to_string(), - git_tag: option_env!("GIT_TAG").unwrap_or("unknown").to_string(), + version: env!("CARGO_PKG_VERSION").into(), + git_tag: option_env!("GIT_TAG").unwrap_or("unknown").into(), git_commit: option_env!("GIT_COMMIT_FULL") .unwrap_or("unknown") - .to_string(), + .into(), timestamp: Utc::now().to_rfc3339(), } } From fdc6b78a06b8b2f0387b47df579ca87f5da0392a Mon Sep 17 00:00:00 2001 From: Arseni Kalma Date: Tue, 16 Sep 2025 00:26:05 +0200 Subject: [PATCH 4/5] Fix cargo fmt --- zebra-rpc/src/methods.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/zebra-rpc/src/methods.rs b/zebra-rpc/src/methods.rs index 6c092989446..5130ac00f33 100644 --- a/zebra-rpc/src/methods.rs +++ b/zebra-rpc/src/methods.rs @@ -1439,9 +1439,7 @@ impl GetHealthInfo { status: "healthy".into(), version: env!("CARGO_PKG_VERSION").into(), git_tag: option_env!("GIT_TAG").unwrap_or("unknown").into(), - git_commit: option_env!("GIT_COMMIT_FULL") - .unwrap_or("unknown") - .into(), + git_commit: option_env!("GIT_COMMIT_FULL").unwrap_or("unknown").into(), timestamp: Utc::now().to_rfc3339(), } } From c84a62b0581d6f1c48693e0b541213baafb905bc Mon Sep 17 00:00:00 2001 From: Arseni Kalma Date: Tue, 16 Sep 2025 09:09:44 +0200 Subject: [PATCH 5/5] Fix cargo test --- zebra-rpc/src/methods.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/zebra-rpc/src/methods.rs b/zebra-rpc/src/methods.rs index 5130ac00f33..f1b24be58f5 100644 --- a/zebra-rpc/src/methods.rs +++ b/zebra-rpc/src/methods.rs @@ -1431,6 +1431,12 @@ pub struct GetHealthInfo { timestamp: String, } +impl Default for GetHealthInfo { + fn default() -> Self { + Self::new() + } +} + impl GetHealthInfo { /// Creates a new health info instance with current node status and build metadata. #[inline]