v0.3.15-pre.002

This commit is contained in:
2026-09-13 06:07:42 +02:00
parent 84ef8bb1f1
commit 8ea261d3a4
7 changed files with 532 additions and 9 deletions

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-onchain-transport-lib/src/ws_settings.rs
// version: 4
// version: 5
const DEFAULT_WS_CLOSE_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(5);
const DEFAULT_WS_COMMAND_QUEUE_CAPACITY: usize = 128;
@@ -401,6 +401,7 @@ pub struct WsEndpointSettings {
provider: crate::WsProviderName,
cluster: crate::WsClusterName,
protocol: crate::WsProtocolKind,
subscription_capabilities: std::option::Option<std::vec::Vec<crate::WsSubscriptionKind>>,
url: crate::WsEndpointUrl,
session: crate::WsSessionSettings,
}
@@ -417,7 +418,42 @@ impl WsEndpointSettings {
url: crate::WsEndpointUrl,
session: crate::WsSessionSettings,
) -> Self {
return Self { name: name.into(), enabled, provider, cluster, protocol, url, session };
return Self {
name: name.into(),
enabled,
provider,
cluster,
protocol,
subscription_capabilities: std::option::Option::None,
url,
session,
};
}
/// Declares the exact WebSocket subscription families supported by this endpoint.
///
/// An endpoint created without this builder remains valid for backward compatibility, but its capabilities are considered undeclared and therefore
/// never constitute positive support evidence for a subscription family. Validation rejects an explicitly declared empty set, duplicates and
/// capabilities that cannot belong to the selected protocol surface.
#[must_use]
pub fn with_subscription_capabilities(mut self, capabilities: std::vec::Vec<crate::WsSubscriptionKind>) -> Self {
self.subscription_capabilities = std::option::Option::Some(capabilities);
return self;
}
/// Returns the explicitly declared WebSocket subscription capabilities, or `None` when the endpoint still uses the legacy undeclared form.
#[must_use]
pub fn subscription_capabilities(&self) -> std::option::Option<&[crate::WsSubscriptionKind]> {
return self.subscription_capabilities.as_deref();
}
/// Returns whether this endpoint explicitly declares support for one WebSocket subscription family.
///
/// Undeclared legacy capability state is fail-closed and therefore returns `false`.
#[must_use]
pub fn supports_subscription(&self, kind: crate::WsSubscriptionKind) -> bool {
return ws_protocol_supports_subscription(self.protocol, kind)
&& self.subscription_capabilities().is_some_and(|capabilities| return capabilities.contains(&kind));
}
/// Returns the logical endpoint name.
@@ -532,6 +568,32 @@ fn validate_ws_endpoint(endpoint: &crate::WsEndpointSettings, endpoint_index: us
if let std::result::Result::Err(error) = validate_ws_descriptor(endpoint.cluster().as_str(), cluster_field.as_str()) {
return std::result::Result::Err(error);
}
if let std::result::Result::Some(capabilities) = endpoint.subscription_capabilities() {
if capabilities.is_empty() {
return ws_invalid_settings(
"explicit WebSocket subscription capabilities must not be empty",
format!("ws_endpoints[{endpoint_index}].subscription_capabilities").as_str(),
);
}
for (capability_index, capability) in capabilities.iter().copied().enumerate() {
let field = format!("ws_endpoints[{endpoint_index}].subscription_capabilities[{capability_index}]");
if !ws_protocol_supports_subscription(endpoint.protocol(), capability) {
return std::result::Result::Err(
ksp_core_lib::Error::new(crate::ERROR_CODE_INVALID_SETTINGS, "WebSocket subscription capability is incompatible with endpoint protocol")
.with_context("field", field)
.with_context("subscription_kind", capability.as_str())
.with_context("protocol", endpoint.protocol().as_str()),
);
}
if capabilities[..capability_index].contains(&capability) {
return std::result::Result::Err(
ksp_core_lib::Error::new(crate::ERROR_CODE_INVALID_SETTINGS, "WebSocket subscription capabilities must be unique")
.with_context("field", field)
.with_context("subscription_kind", capability.as_str()),
);
}
}
}
if let std::result::Result::Err(error) = endpoint.session().validate() {
return std::result::Result::Err(error);
}
@@ -547,6 +609,34 @@ fn validate_ws_endpoint(endpoint: &crate::WsEndpointSettings, endpoint_index: us
return std::result::Result::Ok(());
}
const fn ws_protocol_supports_subscription(protocol: crate::WsProtocolKind, kind: crate::WsSubscriptionKind) -> bool {
return match protocol {
crate::WsProtocolKind::SolanaStandard => matches!(
kind,
crate::WsSubscriptionKind::Account
| crate::WsSubscriptionKind::Block
| crate::WsSubscriptionKind::Logs
| crate::WsSubscriptionKind::Program
| crate::WsSubscriptionKind::Root
| crate::WsSubscriptionKind::Signature
| crate::WsSubscriptionKind::Slot
| crate::WsSubscriptionKind::SlotsUpdates
| crate::WsSubscriptionKind::Vote
),
crate::WsProtocolKind::HeliusLaserStream => matches!(
kind,
crate::WsSubscriptionKind::Account
| crate::WsSubscriptionKind::Logs
| crate::WsSubscriptionKind::Program
| crate::WsSubscriptionKind::Root
| crate::WsSubscriptionKind::Signature
| crate::WsSubscriptionKind::Slot
| crate::WsSubscriptionKind::SlotsUpdates
| crate::WsSubscriptionKind::HeliusTransaction
),
};
}
fn validate_ws_descriptor(value: &str, field: &str) -> ksp_core_lib::Result<()> {
if value.trim().is_empty() {
return ws_invalid_settings("WebSocket transport descriptor must not be empty", field);