v0.3.15-pre.002
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user