Files
khadhroony-solana-project/crates/ksp-onchain-transport-lib/src/ws_lifecycle.rs
2026-08-23 15:41:54 +02:00

367 lines
12 KiB
Rust

// file: crates/ksp-onchain-transport-lib/src/ws_lifecycle.rs
// version: 7
/// Stable local identity assigned to one physical WebSocket session.
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct WsSessionId(std::num::NonZeroU64);
impl WsSessionId {
/// Creates a session identity from a non-zero local value.
#[must_use]
pub const fn new(value: std::num::NonZeroU64) -> Self {
return Self(value);
}
/// Returns the stable local numeric value.
#[must_use]
pub const fn get(self) -> u64 {
return self.0.get();
}
}
/// Stable local identity assigned to one logical WebSocket subscription.
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct WsSubscriptionId(std::num::NonZeroU64);
impl WsSubscriptionId {
/// Creates a subscription identity from a non-zero local value.
#[must_use]
pub const fn new(value: std::num::NonZeroU64) -> Self {
return Self(value);
}
/// Returns the stable local numeric value.
#[must_use]
pub const fn get(self) -> u64 {
return self.0.get();
}
}
/// Observable lifecycle state of one physical WebSocket session.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum WsSessionState {
/// No physical connection is currently active and no connection attempt is running.
Disconnected,
/// The actor is establishing the physical connection.
Connecting,
/// The physical connection is active.
Active,
/// The actor is reconnecting after an unexpected physical disconnect.
Reconnecting {
/// One-based reconnect attempt currently in progress or waiting for backoff.
attempt: u32,
},
/// Explicit shutdown has started and new subscriptions are refused.
Closing,
/// Explicit shutdown completed.
Closed,
/// The session reached a terminal failure state.
Failed,
}
/// Observable lifecycle state of one logical WebSocket subscription.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum WsSubscriptionState {
/// The local subscription exists but its initial subscribe request has not completed.
Requested,
/// The logical subscription is bound to an active remote subscription.
Active,
/// The logical subscription is being restored after reconnect.
Resubscribing,
/// Local cancellation has won and remote cleanup is in progress when possible.
Cancelling,
/// The logical subscription reached a non-error terminal state.
Closed,
/// The logical subscription reached a terminal failure state.
Failed,
}
/// WebSocket subscription family represented by one logical subscription.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[non_exhaustive]
pub enum WsSubscriptionKind {
/// `accountSubscribe` family.
Account,
/// `blockSubscribe` family.
Block,
/// `logsSubscribe` family.
Logs,
/// `programSubscribe` family.
Program,
/// `rootSubscribe` family.
Root,
/// `signatureSubscribe` family.
Signature,
/// `slotSubscribe` family.
Slot,
/// `slotsUpdatesSubscribe` family.
SlotsUpdates,
/// `voteSubscribe` family.
Vote,
/// Helius LaserStream WebSocket `transactionSubscribe` extension family.
HeliusTransaction,
}
impl WsSubscriptionKind {
/// Returns the stable KSP descriptor for this WebSocket subscription family.
#[must_use]
pub const fn as_str(self) -> &'static str {
return match self {
Self::Account => "account",
Self::Block => "block",
Self::Logs => "logs",
Self::Program => "program",
Self::Root => "root",
Self::Signature => "signature",
Self::Slot => "slot",
Self::SlotsUpdates => "slots_updates",
Self::Vote => "vote",
Self::HeliusTransaction => "helius_transaction",
};
}
/// Returns the exact subscribe JSON-RPC method for this family.
pub(crate) const fn subscribe_method(self) -> &'static str {
return match self {
Self::Account => "accountSubscribe",
Self::Block => "blockSubscribe",
Self::Logs => "logsSubscribe",
Self::Program => "programSubscribe",
Self::Root => "rootSubscribe",
Self::Signature => "signatureSubscribe",
Self::Slot => "slotSubscribe",
Self::SlotsUpdates => "slotsUpdatesSubscribe",
Self::Vote => "voteSubscribe",
Self::HeliusTransaction => "transactionSubscribe",
};
}
/// Returns the exact unsubscribe JSON-RPC method for this family.
pub(crate) const fn unsubscribe_method(self) -> &'static str {
return match self {
Self::Account => "accountUnsubscribe",
Self::Block => "blockUnsubscribe",
Self::Logs => "logsUnsubscribe",
Self::Program => "programUnsubscribe",
Self::Root => "rootUnsubscribe",
Self::Signature => "signatureUnsubscribe",
Self::Slot => "slotUnsubscribe",
Self::SlotsUpdates => "slotsUpdatesUnsubscribe",
Self::Vote => "voteUnsubscribe",
Self::HeliusTransaction => "transactionUnsubscribe",
};
}
/// Returns the exact notification method emitted for this family.
pub(crate) const fn notification_method(self) -> &'static str {
return match self {
Self::Account => "accountNotification",
Self::Block => "blockNotification",
Self::Logs => "logsNotification",
Self::Program => "programNotification",
Self::Root => "rootNotification",
Self::Signature => "signatureNotification",
Self::Slot => "slotNotification",
Self::SlotsUpdates => "slotsUpdatesNotification",
Self::Vote => "voteNotification",
Self::HeliusTransaction => "transactionNotification",
};
}
/// Returns whether Solana documents this standard subscription family as unstable.
///
/// Provider extensions are stable here unless explicitly classified otherwise.
pub(crate) const fn is_unstable(self) -> bool {
return matches!(self, Self::Block | Self::SlotsUpdates | Self::Vote);
}
/// Emits the centralized KSP warning required before opening an unstable standard subscription.
pub(crate) fn warn_if_unstable(self) {
if self.is_unstable() {
ksp_logging_lib::warn!(
target: crate::TRACING_TARGET,
rpc_method = self.subscribe_method(),
subscription_kind = self.as_str(),
documentation_status = "unstable",
"unstable Solana WebSocket subscription requested"
);
}
return;
}
}
/// Safe lifecycle projection for one logical WebSocket subscription.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct WsSubscriptionSnapshot {
id: crate::WsSubscriptionId,
kind: crate::WsSubscriptionKind,
state: crate::WsSubscriptionState,
remote_bound: bool,
terminal_error_code: std::option::Option<ksp_core_lib::ErrorCode>,
}
impl WsSubscriptionSnapshot {
/// Creates one safe subscription lifecycle projection for Transport runtime internals.
#[must_use]
pub(crate) const fn new(
id: crate::WsSubscriptionId,
kind: crate::WsSubscriptionKind,
state: crate::WsSubscriptionState,
remote_bound: bool,
terminal_error_code: std::option::Option<ksp_core_lib::ErrorCode>,
) -> Self {
return Self { id, kind, state, remote_bound, terminal_error_code };
}
/// Returns the stable local subscription identity.
#[must_use]
pub const fn id(&self) -> crate::WsSubscriptionId {
return self.id;
}
/// Returns the logical WebSocket subscription family.
#[must_use]
pub const fn kind(&self) -> crate::WsSubscriptionKind {
return self.kind;
}
/// Returns the current logical lifecycle state.
#[must_use]
pub const fn state(&self) -> crate::WsSubscriptionState {
return self.state;
}
/// Returns whether a current remote subscription ID is bound internally.
///
/// The remote ID itself is deliberately absent because it is ephemeral across reconnects.
#[must_use]
pub const fn remote_bound(&self) -> bool {
return self.remote_bound;
}
/// Returns the safe terminal error code when this subscription ended because of a failure.
#[must_use]
pub const fn terminal_error_code(&self) -> std::option::Option<ksp_core_lib::ErrorCode> {
return self.terminal_error_code;
}
}
/// Safe runtime snapshot for one physical WebSocket session.
///
/// The snapshot deliberately contains logical endpoint metadata and local identities only. It never stores the endpoint URL, credentials, request payloads or
/// raw notifications.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct WsSessionSnapshot {
id: crate::WsSessionId,
endpoint_name: std::string::String,
provider: crate::WsProviderName,
cluster: crate::WsClusterName,
protocol: crate::WsProtocolKind,
state: crate::WsSessionState,
pending_request_count: usize,
continuity_gap_count: u64,
overflow_count: u64,
subscriptions: std::vec::Vec<crate::WsSubscriptionSnapshot>,
}
impl WsSessionSnapshot {
/// Creates one safe session projection for Transport runtime internals.
#[must_use]
#[allow(clippy::too_many_arguments)]
pub(crate) fn new(
id: crate::WsSessionId,
endpoint_name: impl std::convert::Into<std::string::String>,
provider: crate::WsProviderName,
cluster: crate::WsClusterName,
protocol: crate::WsProtocolKind,
state: crate::WsSessionState,
pending_request_count: usize,
continuity_gap_count: u64,
overflow_count: u64,
subscriptions: std::vec::Vec<crate::WsSubscriptionSnapshot>,
) -> Self {
return Self {
id,
endpoint_name: endpoint_name.into(),
provider,
cluster,
protocol,
state,
pending_request_count,
continuity_gap_count,
overflow_count,
subscriptions,
};
}
/// Returns the stable local session identity.
#[must_use]
pub const fn id(&self) -> crate::WsSessionId {
return self.id;
}
/// Returns the safe logical endpoint name.
#[must_use]
pub fn endpoint_name(&self) -> &str {
return self.endpoint_name.as_str();
}
/// Returns the provider descriptor without endpoint credentials.
#[must_use]
pub const fn provider(&self) -> &crate::WsProviderName {
return &self.provider;
}
/// Returns the cluster descriptor.
#[must_use]
pub const fn cluster(&self) -> &crate::WsClusterName {
return &self.cluster;
}
/// Returns the WebSocket protocol family.
#[must_use]
pub const fn protocol(&self) -> crate::WsProtocolKind {
return self.protocol;
}
/// Returns the current physical session lifecycle state.
#[must_use]
pub const fn state(&self) -> crate::WsSessionState {
return self.state;
}
/// Returns the number of JSON-RPC requests currently awaiting responses.
#[must_use]
pub const fn pending_request_count(&self) -> usize {
return self.pending_request_count;
}
/// Returns the number of observed physical continuity gaps for this session.
#[must_use]
pub const fn continuity_gap_count(&self) -> u64 {
return self.continuity_gap_count;
}
/// Returns the cumulative number of notification queue overflows observed by this session.
#[must_use]
pub const fn overflow_count(&self) -> u64 {
return self.overflow_count;
}
/// Returns safe lifecycle projections for logical subscriptions owned by this session.
#[must_use]
pub fn subscriptions(&self) -> &[crate::WsSubscriptionSnapshot] {
return self.subscriptions.as_slice();
}
/// Returns the number of logical subscriptions currently projected by the session.
#[must_use]
pub fn subscription_count(&self) -> usize {
return self.subscriptions.len();
}
}
#[cfg(test)]
#[path = "../unit_tests/ws_lifecycle.rs"]
mod tests;