Files
khadhroony-solana-project/crates/ksp-onchain-transport-lib/src/grpc_channel.rs
2026-08-24 09:54:14 +02:00

100 lines
4.2 KiB
Rust

// file: crates/ksp-onchain-transport-lib/src/grpc_channel.rs
// version: 1
/// Prepared Yellowstone gRPC channel owned by KSP Transport.
///
/// `pre.002` intentionally prepares a lazy HTTP/2 channel without performing network I/O. TLS configuration, provider-neutral metadata injection and live
/// connection/error semantics are added by `pre.003`. The underlying Tonic channel and upstream Yellowstone protobuf types are never exposed publicly.
pub struct YellowstoneGrpcChannel {
endpoint_name: std::string::String,
provider: crate::YellowstoneGrpcProviderName,
cluster: crate::YellowstoneGrpcClusterName,
channel: tonic::transport::Channel,
}
impl YellowstoneGrpcChannel {
/// Prepares one lazy Yellowstone gRPC channel from validated Transport-owned endpoint settings.
///
/// This operation performs no network connection. HTTPS endpoints remain syntactically accepted here; TLS is deliberately completed in `pre.003`
/// before any live call is allowed.
pub fn prepare(endpoint: &crate::YellowstoneGrpcEndpointSettings) -> ksp_core_lib::Result<Self> {
if let std::result::Result::Err(error) = endpoint.validate() {
return std::result::Result::Err(error);
}
if !endpoint.enabled() {
return std::result::Result::Err(
ksp_core_lib::Error::new(crate::ERROR_CODE_INVALID_SETTINGS, "disabled Yellowstone gRPC endpoint cannot prepare a channel")
.with_context("field", "grpc_endpoint.enabled")
.with_context("endpoint_name", endpoint.name()),
);
}
let tonic_endpoint = match tonic::transport::Endpoint::from_shared(endpoint.url().as_str().to_owned()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => {
ksp_logging_lib::warn!(
target: crate::TRACING_TARGET,
endpoint_name = endpoint.name(),
provider = endpoint.provider().as_str(),
cluster = endpoint.cluster().as_str(),
"failed to prepare Yellowstone gRPC channel URI"
);
return std::result::Result::Err(
ksp_core_lib::Error::new(crate::ERROR_CODE_GRPC_CHANNEL_FAILED, "Yellowstone gRPC channel could not be prepared")
.with_context("endpoint_name", endpoint.name()),
);
},
};
let tonic_endpoint = tonic_endpoint
.connect_timeout(endpoint.session().connect_timeout())
.timeout(endpoint.session().unary_timeout())
.buffer_size(endpoint.session().request_channel_capacity());
let channel = tonic_endpoint.connect_lazy();
ksp_logging_lib::debug!(
target: crate::TRACING_TARGET,
endpoint_name = endpoint.name(),
provider = endpoint.provider().as_str(),
cluster = endpoint.cluster().as_str(),
"prepared lazy Yellowstone gRPC channel"
);
return std::result::Result::Ok(Self {
endpoint_name: endpoint.name().to_owned(),
provider: endpoint.provider().clone(),
cluster: endpoint.cluster().clone(),
channel,
});
}
/// Returns the safe logical endpoint name.
#[must_use]
pub fn endpoint_name(&self) -> &str {
return self.endpoint_name.as_str();
}
/// Returns the safe open provider descriptor.
#[must_use]
pub const fn provider(&self) -> &crate::YellowstoneGrpcProviderName {
return &self.provider;
}
/// Returns the safe open cluster descriptor.
#[must_use]
pub const fn cluster(&self) -> &crate::YellowstoneGrpcClusterName {
return &self.cluster;
}
}
impl std::fmt::Debug for YellowstoneGrpcChannel {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
return formatter
.debug_struct("YellowstoneGrpcChannel")
.field("endpoint_name", &self.endpoint_name)
.field("provider", &self.provider)
.field("cluster", &self.cluster)
.finish();
}
}
#[cfg(test)]
#[path = "../unit_tests/grpc_channel.rs"]
mod tests;