v0.2.10-pre.002

This commit is contained in:
2026-08-25 10:29:16 +02:00
parent 7f567bc1bb
commit af807afff5
10 changed files with 667 additions and 59 deletions

View File

@@ -1,5 +1,5 @@
<!-- file: crates/ksp-onchain-transport-lib/README.md -->
<!-- version: 22 -->
<!-- version: 23 -->
# `ksp-onchain-transport-lib`
@@ -220,7 +220,6 @@ Pour Helius, lactor envoie automatiquement un control frame WebSocket `Ping`
LaserStream **gRPC** reste un backend distinct, hors de cette façade, de `WsProtocolKind` et de la Config WebSocket `helius_laserstream`.
## Yellowstone gRPC standard
La foundation `0.2.9` ajoute un troisième backend réseau distinct de HTTP et WebSocket. Le moteur est KSP-owned : `yellowstone-grpc-proto` fournit le wire publié, tandis que Tonic reste encapsulé derrière les types crate-root KSP. Aucun client Tonic brut ni type protobuf upstream nest réexporté.

View File

@@ -0,0 +1,126 @@
// file: crates/ksp-onchain-transport-lib/tests/yellowstone_orbitflare_smoke.rs
// version: 1
//! Opt-in live OrbitFlare Devnet characterization smoke for provider-neutral Yellowstone gRPC Subscribe.
fn orbitflare_devnet_endpoint() -> ksp_core_lib::Result<ksp_onchain_transport_lib::YellowstoneGrpcEndpointSettings> {
let url = match ksp_onchain_transport_lib::YellowstoneGrpcEndpointUrl::parse("http://devnet.rpc.orbitflare.com:10000") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let session = ksp_onchain_transport_lib::YellowstoneGrpcSessionSettings::new(
std::time::Duration::from_secs(10),
std::time::Duration::from_secs(10),
std::time::Duration::from_secs(5),
ksp_onchain_transport_lib::YellowstoneGrpcReconnectSettings::new(0, std::time::Duration::from_millis(250), std::time::Duration::from_secs(2)),
8,
8,
16 * 1024 * 1024,
4 * 1024 * 1024,
);
return std::result::Result::Ok(ksp_onchain_transport_lib::YellowstoneGrpcEndpointSettings::new(
"orbitflare_devnet_yellowstone",
true,
ksp_onchain_transport_lib::YellowstoneGrpcProviderName::new("orbitflare"),
ksp_onchain_transport_lib::YellowstoneGrpcClusterName::new("devnet"),
url,
session,
));
}
fn slot_request() -> ksp_core_lib::Result<ksp_onchain_transport_lib::YellowstoneSubscribeRequest> {
let mut request = ksp_onchain_transport_lib::YellowstoneSubscribeRequest::new();
let name = match ksp_onchain_transport_lib::YellowstoneSubscribeFilterName::new("slots") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
match request.insert_slot_filter(name, ksp_onchain_transport_lib::YellowstoneSubscribeSlotFilter::new()) {
std::result::Result::Ok(()) => {},
std::result::Result::Err(error) => return std::result::Result::Err(error),
}
request.set_commitment(std::option::Option::Some(ksp_onchain_transport_lib::SolanaCommitment::Confirmed));
return std::result::Result::Ok(request);
}
#[tokio::test(flavor = "current_thread")]
#[ignore = "opt-in live OrbitFlare Devnet Yellowstone gRPC characterization; connects without metadata, waits for Slot plus standard server Ping, then closes"]
async fn orbitflare_devnet_yellowstone_streams_slots_and_emits_standard_server_ping_without_metadata() {
let endpoint = match orbitflare_devnet_endpoint() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("programmatic OrbitFlare Devnet Yellowstone settings must be valid without metadata: {error:?}"),
};
assert!(endpoint.metadata().is_empty(), "OrbitFlare Devnet characterization must not invent gRPC metadata");
assert!(!endpoint.url().uses_tls(), "the documented OrbitFlare shared Devnet endpoint is intentionally HTTP/2 plaintext");
let endpoint_debug = format!("{endpoint:?}");
assert!(!endpoint_debug.contains("orbitflare.com"), "OrbitFlare endpoint URL must not appear in endpoint Debug");
assert!(!endpoint_debug.contains("X-ORBIT-KEY"), "OrbitFlare Customer API credentials must not enter Yellowstone settings");
let channel = match ksp_onchain_transport_lib::YellowstoneGrpcChannel::connect(&endpoint).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
panic!("OrbitFlare Devnet Yellowstone connection without metadata must succeed or classify the provider auth gate: {error:?}")
},
};
assert_eq!(channel.endpoint_name(), "orbitflare_devnet_yellowstone");
assert_eq!(channel.provider().as_str(), "orbitflare");
assert_eq!(channel.cluster().as_str(), "devnet");
let request = match slot_request() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("OrbitFlare Yellowstone slot request must be valid: {error:?}"),
};
let mut session = match channel.open_standard_subscribe(request).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
panic!("OrbitFlare Devnet standard Subscribe without metadata must open or classify the provider auth gate: {error:?}")
},
};
let deadline = tokio::time::sleep(std::time::Duration::from_secs(45));
tokio::pin!(deadline);
let mut slot_seen = false;
let mut ping_seen = false;
loop {
tokio::select! {
() = &mut deadline => break,
next_update = session.next_update() => {
let next_update = match next_update {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("OrbitFlare Devnet Yellowstone Subscribe update must decode: {error:?}"),
};
match next_update {
std::option::Option::Some(ksp_onchain_transport_lib::YellowstoneSubscribeUpdate::Slot(update)) => {
if update.slot() > 0 {
slot_seen = true;
}
},
std::option::Option::Some(ksp_onchain_transport_lib::YellowstoneSubscribeUpdate::Ping(_)) => ping_seen = true,
std::option::Option::Some(_) => {},
std::option::Option::None => break,
}
if slot_seen && ping_seen {
break;
}
},
}
}
let close_timeout_result = tokio::time::timeout(std::time::Duration::from_secs(7), session.close()).await;
let close_result = match close_timeout_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
panic!("KSP Yellowstone Subscribe close must remain bounded beyond the configured five-second provider half-close deadline: {error}")
},
};
match close_result {
std::result::Result::Ok(()) => {},
std::result::Result::Err(error) => {
assert_eq!(
error.code(),
ksp_onchain_transport_lib::ERROR_CODE_TIMEOUT,
"after live characterization, OrbitFlare close may time out waiting for provider half-close but must not fail for another reason: {error:?}"
);
},
}
assert!(slot_seen, "OrbitFlare Devnet Yellowstone must publish at least one non-zero confirmed slot during the 45-second characterization window");
assert!(
ping_seen,
"OrbitFlare Devnet must emit the standard Yellowstone server Ping during the 45-second characterization window before any provider-specific heartbeat is justified"
);
}