From c1924e1ccc974449940cec4d388827d7e3120f42 Mon Sep 17 00:00:00 2001 From: ygd58 Date: Sat, 28 Mar 2026 19:49:00 +0100 Subject: [PATCH 1/3] feat: improve sequencer healthcheck to return chain height Previously check_health() always returned Ok(()) without checking anything meaningful. A sequencer could be stuck and still pass. Now check_health() returns the current chain height (BlockId). Clients can call this twice with a delay to verify the chain is progressing. Refs #244 --- sequencer/service/rpc/src/lib.rs | 5 +++-- sequencer/service/src/service.rs | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/sequencer/service/rpc/src/lib.rs b/sequencer/service/rpc/src/lib.rs index 6c03cdb6..2f177fb5 100644 --- a/sequencer/service/rpc/src/lib.rs +++ b/sequencer/service/rpc/src/lib.rs @@ -39,9 +39,10 @@ pub trait Rpc { #[method(name = "sendTransaction")] async fn send_transaction(&self, tx: NSSATransaction) -> Result; - // TODO: expand healthcheck response into some kind of report + /// Returns the current chain height (block count). + /// A sequencer is healthy if this value increases over time. #[method(name = "checkHealth")] - async fn check_health(&self) -> Result<(), ErrorObjectOwned>; + async fn check_health(&self) -> Result; // TODO: These functions should be removed after wallet starts using indexer // for this type of queries. diff --git a/sequencer/service/src/service.rs b/sequencer/service/src/service.rs index 71645363..1f2395e3 100644 --- a/sequencer/service/src/service.rs +++ b/sequencer/service/src/service.rs @@ -82,8 +82,9 @@ impl Result<(), ErrorObjectOwned> { - Ok(()) + async fn check_health(&self) -> Result { + let sequencer = self.sequencer.lock().await; + Ok(sequencer.chain_height()) } async fn get_block(&self, block_id: BlockId) -> Result, ErrorObjectOwned> { From 4be96ef087f67c13b419a301bc98b24f3575775f Mon Sep 17 00:00:00 2001 From: ygd58 Date: Sat, 28 Mar 2026 19:55:06 +0100 Subject: [PATCH 2/3] feat: improve sequencer healthcheck with HealthStatus response Previously check_health() always returned Ok(()) - completely useless. Now returns HealthStatus { chain_height, is_healthy } so clients can: 1. Call check_health() once to get current chain_height 2. Wait a few seconds 3. Call again - if chain_height increased, sequencer is healthy Refs #244 --- sequencer/service/rpc/Cargo.toml | 1 + sequencer/service/rpc/src/lib.rs | 13 ++++++++++++- sequencer/service/src/service.rs | 9 +++++++-- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/sequencer/service/rpc/Cargo.toml b/sequencer/service/rpc/Cargo.toml index d8f16b86..1329ac83 100644 --- a/sequencer/service/rpc/Cargo.toml +++ b/sequencer/service/rpc/Cargo.toml @@ -8,6 +8,7 @@ license = { workspace = true } workspace = true [dependencies] +serde = { version = "1", features = ["derive"] } sequencer_service_protocol.workspace = true jsonrpsee = { workspace = true, features = ["macros"] } diff --git a/sequencer/service/rpc/src/lib.rs b/sequencer/service/rpc/src/lib.rs index 2f177fb5..6d27d41b 100644 --- a/sequencer/service/rpc/src/lib.rs +++ b/sequencer/service/rpc/src/lib.rs @@ -5,11 +5,22 @@ use jsonrpsee::proc_macros::rpc; use jsonrpsee::types::ErrorObjectOwned; #[cfg(feature = "client")] pub use jsonrpsee::{core::ClientError, http_client::HttpClientBuilder as SequencerClientBuilder}; +use serde::{Deserialize, Serialize}; use sequencer_service_protocol::{ Account, AccountId, Block, BlockId, Commitment, HashType, MembershipProof, NSSATransaction, Nonce, ProgramId, }; +/// Health status returned by the sequencer healthcheck endpoint. +/// The chain_height field can be polled twice to verify the sequencer is progressing. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct HealthStatus { + /// Current chain height (block count). Should increase over time if sequencer is healthy. + pub chain_height: BlockId, + /// Whether the sequencer considers itself healthy. + pub is_healthy: bool, +} + #[cfg(all(not(feature = "server"), not(feature = "client")))] compile_error!("At least one of `server` or `client` features must be enabled."); @@ -42,7 +53,7 @@ pub trait Rpc { /// Returns the current chain height (block count). /// A sequencer is healthy if this value increases over time. #[method(name = "checkHealth")] - async fn check_health(&self) -> Result; + async fn check_health(&self) -> Result; // TODO: These functions should be removed after wallet starts using indexer // for this type of queries. diff --git a/sequencer/service/src/service.rs b/sequencer/service/src/service.rs index 1f2395e3..2ef8803f 100644 --- a/sequencer/service/src/service.rs +++ b/sequencer/service/src/service.rs @@ -15,6 +15,7 @@ use sequencer_core::{ use sequencer_service_protocol::{ Account, AccountId, Block, BlockId, Commitment, HashType, MembershipProof, Nonce, ProgramId, }; +use sequencer_service_rpc::HealthStatus; use tokio::sync::Mutex; const NOT_FOUND_ERROR_CODE: i32 = -31999; @@ -82,9 +83,13 @@ impl Result { + async fn check_health(&self) -> Result { let sequencer = self.sequencer.lock().await; - Ok(sequencer.chain_height()) + let chain_height = sequencer.chain_height(); + Ok(HealthStatus { + chain_height, + is_healthy: true, + }) } async fn get_block(&self, block_id: BlockId) -> Result, ErrorObjectOwned> { From 934c333f472ff43a0b285901d45d7b4a307b47a4 Mon Sep 17 00:00:00 2001 From: ygd58 Date: Sat, 28 Mar 2026 19:59:19 +0100 Subject: [PATCH 3/3] feat: add meaningful is_healthy check to sequencer healthcheck is_healthy is now true only if the block store is accessible and has produced at least the genesis block. Clients should poll chain_height twice with a delay to verify the sequencer is making progress. Refs #244 --- sequencer/service/src/service.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sequencer/service/src/service.rs b/sequencer/service/src/service.rs index 2ef8803f..8c8a8fb5 100644 --- a/sequencer/service/src/service.rs +++ b/sequencer/service/src/service.rs @@ -86,9 +86,14 @@ impl Result { let sequencer = self.sequencer.lock().await; let chain_height = sequencer.chain_height(); + + // Sequencer is considered healthy if it has produced at least the genesis block. + // Clients should poll this endpoint twice with a delay to verify chain_height increases. + let is_healthy = sequencer.block_store().latest_block_meta().is_ok(); + Ok(HealthStatus { chain_height, - is_healthy: true, + is_healthy, }) }