v0.2.8-pre.002

This commit is contained in:
2026-08-23 13:15:42 +02:00
parent 315e7e67e5
commit 0871b85df9
12 changed files with 767 additions and 50 deletions

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-onchain-transport-lib/src/lib.rs
// version: 29
// version: 30
#![warn(missing_docs)]
#![deny(unreachable_pub)]
@@ -24,7 +24,8 @@
//! routing. `0.2.7-pre.007` adds finite reconnect, deterministic resubscribe and continuity-gap tracking. `0.2.7-pre.008` makes per-subscription notification
//! backpressure terminal and observable, preserves safe terminal error codes, performs best-effort remote cleanup and proves bounded capacity reuse.
//! `0.2.7-pre.009` opens the first stable typed WebSocket wrappers for account, program-account and transaction-log subscriptions without exposing a raw
//! provider-extension subscription API.
//! provider-extension subscription API. `0.2.8-pre.002` adds a Helius LaserStream WebSocket protocol discriminator and two typed protocol facades while
//! keeping the `WsSession` actor/socket implementation unique and the historical generic constructor standard-only.
mod client;
mod constants;
@@ -47,6 +48,7 @@ mod ws_accounts;
mod ws_blocks;
mod ws_cluster;
mod ws_lifecycle;
mod ws_protocol_session;
mod ws_session;
mod ws_settings;
mod ws_subscription;
@@ -350,7 +352,11 @@ pub use self::ws_lifecycle::WsSubscriptionKind;
pub use self::ws_lifecycle::WsSubscriptionSnapshot;
/// Observable lifecycle state of one logical WebSocket subscription.
pub use self::ws_lifecycle::WsSubscriptionState;
/// Shareable handle for one explicitly created physical WebSocket session.
/// Typed facade for one Helius LaserStream WebSocket physical session.
pub use self::ws_protocol_session::HeliusLaserStreamWsSession;
/// Typed facade for one standard Solana WebSocket physical session.
pub use self::ws_protocol_session::SolanaStandardWsSession;
/// Shareable compatibility handle for one explicitly created standard Solana physical WebSocket session.
pub use self::ws_session::WsSession;
/// Open cluster or network descriptor used by WebSocket endpoint settings.
pub use self::ws_settings::WsClusterName;

View File

@@ -0,0 +1,182 @@
// file: crates/ksp-onchain-transport-lib/src/ws_protocol_session.rs
// version: 1
/// Typed facade for one standard Solana WebSocket physical session.
///
/// The facade delegates to the same [`crate::WsSession`] actor used by the compatibility API. It owns no socket, registry, reconnect loop or queue of its
/// own and therefore does not duplicate the physical WebSocket runtime.
#[derive(Clone)]
pub struct SolanaStandardWsSession {
inner: crate::WsSession,
}
impl SolanaStandardWsSession {
/// Opens one standard Solana WebSocket session through the shared physical actor.
pub async fn connect(endpoint: crate::WsEndpointSettings) -> ksp_core_lib::Result<Self> {
let connected = crate::WsSession::connect_for_protocol(endpoint, crate::WsProtocolKind::SolanaStandard).await;
return match connected {
std::result::Result::Ok(inner) => std::result::Result::Ok(Self { inner }),
std::result::Result::Err(error) => std::result::Result::Err(error),
};
}
/// Returns the stable local session identity.
#[must_use]
pub const fn id(&self) -> crate::WsSessionId {
return self.inner.id();
}
/// Returns the latest safe runtime snapshot published by the shared actor.
#[must_use]
pub fn snapshot(&self) -> crate::WsSessionSnapshot {
return self.inner.snapshot();
}
/// Returns the latest observable physical-session state.
#[must_use]
pub fn state(&self) -> crate::WsSessionState {
return self.inner.state();
}
/// Explicitly closes the shared physical session under its configured close timeout.
pub async fn close(&self) -> ksp_core_lib::Result<()> {
return self.inner.close().await;
}
/// Subscribes to changes for one Solana account through standard `accountSubscribe`.
pub async fn account_subscribe(
&self,
account: &ksp_core_lib::Pubkey,
config: std::option::Option<&crate::SolanaAccountSubscribeConfig>,
) -> ksp_core_lib::Result<crate::WsSubscription<crate::SolanaRpcResponse<crate::SolanaAccount>>> {
return self.inner.account_subscribe(account, config).await;
}
/// Subscribes to unstable standard Solana block notifications through `blockSubscribe`.
pub async fn block_subscribe(
&self,
filter: &crate::SolanaBlockSubscribeFilter,
config: std::option::Option<&crate::SolanaBlockSubscribeConfig>,
) -> ksp_core_lib::Result<crate::WsSubscription<crate::SolanaRpcResponse<crate::SolanaBlockNotification>>> {
return self.inner.block_subscribe(filter, config).await;
}
/// Subscribes to Solana transaction logs through standard `logsSubscribe`.
pub async fn logs_subscribe(
&self,
filter: &crate::SolanaLogsSubscribeFilter,
config: std::option::Option<&crate::SolanaCommitmentConfig>,
) -> ksp_core_lib::Result<crate::WsSubscription<crate::SolanaRpcResponse<crate::SolanaLogsNotification>>> {
return self.inner.logs_subscribe(filter, config).await;
}
/// Subscribes to account changes owned by one Solana program through standard `programSubscribe`.
pub async fn program_subscribe(
&self,
program_id: &ksp_core_lib::Pubkey,
config: std::option::Option<&crate::SolanaProgramSubscribeConfig>,
) -> ksp_core_lib::Result<crate::WsSubscription<crate::SolanaProgramNotification>> {
return self.inner.program_subscribe(program_id, config).await;
}
/// Subscribes to standard Solana root-slot notifications through `rootSubscribe`.
pub async fn root_subscribe(&self) -> ksp_core_lib::Result<crate::WsSubscription<u64>> {
return self.inner.root_subscribe().await;
}
/// Subscribes to one Solana transaction signature through standard `signatureSubscribe`.
pub async fn signature_subscribe(
&self,
signature: &str,
config: std::option::Option<&crate::SolanaSignatureSubscribeConfig>,
) -> ksp_core_lib::Result<crate::WsSubscription<crate::SolanaRpcResponse<crate::SolanaSignatureNotification>>> {
return self.inner.signature_subscribe(signature, config).await;
}
/// Subscribes to standard Solana slot-processing notifications through `slotSubscribe`.
pub async fn slot_subscribe(&self) -> ksp_core_lib::Result<crate::WsSubscription<crate::SolanaSlotNotification>> {
return self.inner.slot_subscribe().await;
}
/// Subscribes to unstable standard Solana slot-lifecycle notifications through `slotsUpdatesSubscribe`.
pub async fn slots_updates_subscribe(&self) -> ksp_core_lib::Result<crate::WsSubscription<crate::SolanaSlotUpdate>> {
return self.inner.slots_updates_subscribe().await;
}
/// Subscribes to unstable pre-consensus gossip vote notifications through `voteSubscribe`.
pub async fn vote_subscribe(&self) -> ksp_core_lib::Result<crate::WsSubscription<crate::SolanaVoteNotification>> {
return self.inner.vote_subscribe().await;
}
}
impl std::fmt::Debug for SolanaStandardWsSession {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
return formatter.debug_struct("SolanaStandardWsSession").field("id", &self.id()).field("snapshot", &self.snapshot()).finish();
}
}
/// Typed facade for one Helius LaserStream WebSocket physical session.
///
/// `0.2.8-pre.002` intentionally exposes only physical-session lifecycle through this facade. The six Helius-supported standard subscription wrappers are
/// added in the next tranche after exact delegation and API-absence canaries are established. No public inner handle is exposed, so callers cannot bypass
/// the provider-specific surface by recovering a generic [`crate::WsSession`].
///
/// ```compile_fail
/// async fn unsupported(session: &ksp_onchain_transport_lib::HeliusLaserStreamWsSession) {
/// let _ = session.block_subscribe().await;
/// }
/// ```
///
/// ```compile_fail
/// fn no_escape_hatch(session: ksp_onchain_transport_lib::HeliusLaserStreamWsSession) {
/// let _ = session.into_inner();
/// }
/// ```
#[derive(Clone)]
pub struct HeliusLaserStreamWsSession {
inner: crate::WsSession,
}
impl HeliusLaserStreamWsSession {
/// Opens one Helius LaserStream WebSocket session through the shared physical actor.
pub async fn connect(endpoint: crate::WsEndpointSettings) -> ksp_core_lib::Result<Self> {
let connected = crate::WsSession::connect_for_protocol(endpoint, crate::WsProtocolKind::HeliusLaserStream).await;
return match connected {
std::result::Result::Ok(inner) => std::result::Result::Ok(Self { inner }),
std::result::Result::Err(error) => std::result::Result::Err(error),
};
}
/// Returns the stable local session identity.
#[must_use]
pub const fn id(&self) -> crate::WsSessionId {
return self.inner.id();
}
/// Returns the latest safe runtime snapshot published by the shared actor.
#[must_use]
pub fn snapshot(&self) -> crate::WsSessionSnapshot {
return self.inner.snapshot();
}
/// Returns the latest observable physical-session state.
#[must_use]
pub fn state(&self) -> crate::WsSessionState {
return self.inner.state();
}
/// Explicitly closes the shared physical session under its configured close timeout.
pub async fn close(&self) -> ksp_core_lib::Result<()> {
return self.inner.close().await;
}
}
impl std::fmt::Debug for HeliusLaserStreamWsSession {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
return formatter.debug_struct("HeliusLaserStreamWsSession").field("id", &self.id()).field("snapshot", &self.snapshot()).finish();
}
}
#[cfg(test)]
#[path = "../unit_tests/ws_protocol_session.rs"]
mod tests;

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-onchain-transport-lib/src/ws_session.rs
// version: 12
// version: 13
use futures_util::SinkExt; // rust-rules: trait-import
use futures_util::StreamExt; // rust-rules: trait-import
@@ -24,15 +24,32 @@ pub struct WsSession {
}
impl WsSession {
/// Opens one physical WebSocket connection for the supplied endpoint settings.
/// Opens one physical standard Solana WebSocket connection for the supplied endpoint settings.
///
/// Calling this function twice with the same endpoint creates two independent physical sessions. The function returns only after the WebSocket
/// handshake succeeds or the configured command timeout expires.
/// This historical constructor remains standard-only after provider-specific protocol kinds are added. Calling this function twice with the same endpoint
/// creates two independent physical sessions. Provider-specific callers must use their typed protocol facade instead of obtaining a generic handle.
pub async fn connect(endpoint: crate::WsEndpointSettings) -> ksp_core_lib::Result<Self> {
return Self::connect_for_protocol(endpoint, crate::WsProtocolKind::SolanaStandard).await;
}
/// Opens one physical WebSocket connection after validating the typed facade protocol.
pub(crate) async fn connect_for_protocol(endpoint: crate::WsEndpointSettings, expected_protocol: crate::WsProtocolKind) -> ksp_core_lib::Result<Self> {
let validation = crate::WsTransportSettings::new(std::vec![endpoint.clone()]).validate();
if let std::result::Result::Err(error) = validation {
return std::result::Result::Err(error);
}
if endpoint.protocol() != expected_protocol {
return std::result::Result::Err(
ksp_core_lib::Error::new(crate::ERROR_CODE_INVALID_SETTINGS, "WebSocket session constructor does not accept the endpoint protocol")
.with_context("field", "ws_endpoints.protocol")
.with_context("expected_protocol", expected_protocol.as_str())
.with_context("actual_protocol", endpoint.protocol().as_str()),
);
}
return Self::connect_physical(endpoint).await;
}
async fn connect_physical(endpoint: crate::WsEndpointSettings) -> ksp_core_lib::Result<Self> {
let id_result = next_session_id();
let id = match id_result {
std::result::Result::Ok(id) => id,

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-onchain-transport-lib/src/ws_settings.rs
// version: 3
// version: 4
const DEFAULT_WS_CLOSE_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(5);
const DEFAULT_WS_COMMAND_QUEUE_CAPACITY: usize = 128;
@@ -119,13 +119,15 @@ impl WsClusterName {
/// WebSocket protocol family understood by KSP Transport.
///
/// `0.2.7` exposes only standard Solana WebSocket. The non-exhaustive contract allows later provider-specific families without changing the common endpoint
/// container or injecting provider-only options into [`WsSessionSettings`].
/// The protocol discriminator belongs specifically to the WebSocket runtime. Provider products using another transport, including a future Helius
/// LaserStream gRPC backend, require a distinct transport-owned descriptor instead of reusing this enum.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[non_exhaustive]
pub enum WsProtocolKind {
/// Standard Solana JSON-RPC WebSocket PubSub.
SolanaStandard,
/// Helius LaserStream WebSocket protocol surface.
HeliusLaserStream,
}
impl WsProtocolKind {
@@ -134,6 +136,7 @@ impl WsProtocolKind {
pub const fn as_str(self) -> &'static str {
return match self {
Self::SolanaStandard => "solana_standard",
Self::HeliusLaserStream => "helius_laserstream",
};
}
}