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 6c03cdb6..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."); @@ -39,9 +50,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..8c8a8fb5 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,8 +83,18 @@ impl Result<(), ErrorObjectOwned> { - Ok(()) + async fn check_health(&self) -> 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, + }) } async fn get_block(&self, block_id: BlockId) -> Result, ErrorObjectOwned> {