v0.2.9-pre.013-fix.001
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
# file: Cargo.toml
|
||||
# version: 254
|
||||
# version: 255
|
||||
|
||||
[workspace]
|
||||
resolver = "3"
|
||||
members = ["crates/ksp-app-config-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-logging-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-wallet-lib"]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.2.9-pre.13"
|
||||
version = "0.2.9-pre.13.fix.1"
|
||||
edition = "2024"
|
||||
license = "MIT"
|
||||
repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project"
|
||||
|
||||
@@ -170,6 +170,57 @@
|
||||
"url": "https://solana-yellowstone-grpc.publicnode.com:443"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"profile_id": "publicnode_testnet",
|
||||
"endpoints": [
|
||||
{
|
||||
"name": "solana_testnet_public",
|
||||
"enabled": true,
|
||||
"provider": "solana-public",
|
||||
"cluster": "testnet",
|
||||
"url": "https://api.testnet.solana.com",
|
||||
"connect_timeout_ms": 5000,
|
||||
"request_timeout_ms": 15000,
|
||||
"max_idle_connections_per_host": 8,
|
||||
"roles": [
|
||||
{
|
||||
"role": "default",
|
||||
"enabled": true,
|
||||
"request_kinds": [
|
||||
"*"
|
||||
],
|
||||
"priority": 100,
|
||||
"limits": {
|
||||
"requests_per_second": 5,
|
||||
"burst_capacity": 10,
|
||||
"max_concurrent_requests": 8,
|
||||
"pause_after_rate_limit_ms": 1000
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"ws_endpoints": [
|
||||
{
|
||||
"name": "solana_testnet_public_ws",
|
||||
"enabled": true,
|
||||
"provider": "solana-public",
|
||||
"cluster": "testnet",
|
||||
"kind": "solana_standard",
|
||||
"url": "wss://api.testnet.solana.com"
|
||||
}
|
||||
],
|
||||
"grpc_endpoints": [
|
||||
{
|
||||
"name": "publicnode_solana_testnet_yellowstone",
|
||||
"enabled": true,
|
||||
"provider": "publicnode",
|
||||
"cluster": "testnet",
|
||||
"protocol": "solana_yellowstone",
|
||||
"url": "https://solana-testnet-yellowstone-grpc.publicnode.com:443"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-config-lib/unit_tests/transport.rs
|
||||
// version: 7
|
||||
// version: 8
|
||||
|
||||
#[test]
|
||||
fn fixture_transport_profile_maps_complete_runtime_contract() {
|
||||
@@ -134,39 +134,44 @@ fn committed_transport_document_maps_default_and_explicit_profiles() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn committed_v3_publicnode_mainnet_maps_provider_neutral_yellowstone_grpc() {
|
||||
fn committed_v3_publicnode_profiles_map_provider_neutral_yellowstone_grpc_without_auth_metadata() {
|
||||
let engine = committed_engine();
|
||||
let engine = match engine {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
let environment = crate::ConfigEnvironment::from_maps(std::collections::BTreeMap::new(), std::collections::BTreeMap::new());
|
||||
let resolved = engine.load_resolved_transport_config(std::option::Option::Some("publicnode_mainnet"), &environment);
|
||||
assert!(resolved.is_ok(), "committed PublicNode Mainnet V3 profile should map: {resolved:?}");
|
||||
let resolved = match resolved {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
assert_eq!(resolved.profile_id(), "publicnode_mainnet");
|
||||
assert!(resolved.ws_settings().is_some());
|
||||
let grpc = resolved.grpc_settings();
|
||||
assert!(grpc.is_some(), "PublicNode Mainnet profile should expose Yellowstone gRPC settings");
|
||||
if let std::option::Option::Some(grpc) = grpc {
|
||||
assert_eq!(grpc.endpoints().len(), 1);
|
||||
let endpoint = &grpc.endpoints()[0];
|
||||
assert_eq!(endpoint.name(), "publicnode_solana_mainnet_yellowstone");
|
||||
assert_eq!(endpoint.provider().as_str(), "publicnode");
|
||||
assert_eq!(endpoint.cluster().as_str(), "mainnet-beta");
|
||||
assert_eq!(endpoint.url().as_str(), "https://solana-yellowstone-grpc.publicnode.com:443");
|
||||
assert!(endpoint.url().uses_tls());
|
||||
assert!(endpoint.metadata().is_empty());
|
||||
assert_eq!(endpoint.session().connect_timeout(), std::time::Duration::from_millis(10_000));
|
||||
assert_eq!(endpoint.session().unary_timeout(), std::time::Duration::from_millis(10_000));
|
||||
assert_eq!(endpoint.session().close_timeout(), std::time::Duration::from_millis(5_000));
|
||||
assert_eq!(endpoint.session().reconnect().max_retries(), 5);
|
||||
assert!(grpc.validate().is_ok(), "Config-produced Yellowstone gRPC settings should satisfy Transport validation");
|
||||
let debug = format!("{grpc:?}");
|
||||
assert!(!debug.contains("solana-yellowstone-grpc.publicnode.com"), "Transport settings Debug must not expose gRPC endpoint URLs");
|
||||
for (profile_id, endpoint_name, cluster, url) in [
|
||||
("publicnode_mainnet", "publicnode_solana_mainnet_yellowstone", "mainnet-beta", "https://solana-yellowstone-grpc.publicnode.com:443"),
|
||||
("publicnode_testnet", "publicnode_solana_testnet_yellowstone", "testnet", "https://solana-testnet-yellowstone-grpc.publicnode.com:443"),
|
||||
] {
|
||||
let resolved = engine.load_resolved_transport_config(std::option::Option::Some(profile_id), &environment);
|
||||
assert!(resolved.is_ok(), "committed PublicNode V3 profile {profile_id} should map: {resolved:?}");
|
||||
let resolved = match resolved {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => continue,
|
||||
};
|
||||
assert_eq!(resolved.profile_id(), profile_id);
|
||||
assert!(resolved.ws_settings().is_some());
|
||||
let grpc = resolved.grpc_settings();
|
||||
assert!(grpc.is_some(), "PublicNode profile {profile_id} should expose Yellowstone gRPC settings");
|
||||
if let std::option::Option::Some(grpc) = grpc {
|
||||
assert_eq!(grpc.endpoints().len(), 1);
|
||||
let endpoint = &grpc.endpoints()[0];
|
||||
assert_eq!(endpoint.name(), endpoint_name);
|
||||
assert_eq!(endpoint.provider().as_str(), "publicnode");
|
||||
assert_eq!(endpoint.cluster().as_str(), cluster);
|
||||
assert_eq!(endpoint.url().as_str(), url);
|
||||
assert!(endpoint.url().uses_tls());
|
||||
assert!(endpoint.metadata().is_empty(), "PublicNode public Yellowstone profiles must not invent authentication metadata");
|
||||
assert_eq!(endpoint.session().connect_timeout(), std::time::Duration::from_millis(10_000));
|
||||
assert_eq!(endpoint.session().unary_timeout(), std::time::Duration::from_millis(10_000));
|
||||
assert_eq!(endpoint.session().close_timeout(), std::time::Duration::from_millis(5_000));
|
||||
assert_eq!(endpoint.session().reconnect().max_retries(), 5);
|
||||
assert!(grpc.validate().is_ok(), "Config-produced Yellowstone gRPC settings should satisfy Transport validation");
|
||||
let debug = format!("{grpc:?}");
|
||||
assert!(!debug.contains("publicnode.com"), "Transport settings Debug must not expose gRPC endpoint URLs");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// file: crates/ksp-onchain-transport-lib/tests/yellowstone_publicnode_smoke.rs
|
||||
// version: 1
|
||||
// version: 2
|
||||
|
||||
//! Opt-in live PublicNode Mainnet smoke for the provider-neutral Yellowstone gRPC facade.
|
||||
//! Opt-in live PublicNode Mainnet/Testnet smokes for the provider-neutral Yellowstone gRPC Subscribe facade.
|
||||
|
||||
fn publicnode_mainnet_endpoint() -> ksp_core_lib::Result<ksp_onchain_transport_lib::YellowstoneGrpcEndpointSettings> {
|
||||
let url = match ksp_onchain_transport_lib::YellowstoneGrpcEndpointUrl::parse("https://solana-yellowstone-grpc.publicnode.com:443") {
|
||||
fn publicnode_endpoint(name: &str, cluster: &str, url: &str) -> ksp_core_lib::Result<ksp_onchain_transport_lib::YellowstoneGrpcEndpointSettings> {
|
||||
let url = match ksp_onchain_transport_lib::YellowstoneGrpcEndpointUrl::parse(url) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
@@ -19,29 +19,56 @@ fn publicnode_mainnet_endpoint() -> ksp_core_lib::Result<ksp_onchain_transport_l
|
||||
4 * 1024 * 1024,
|
||||
);
|
||||
return std::result::Result::Ok(ksp_onchain_transport_lib::YellowstoneGrpcEndpointSettings::new(
|
||||
"publicnode_mainnet_yellowstone",
|
||||
name,
|
||||
true,
|
||||
ksp_onchain_transport_lib::YellowstoneGrpcProviderName::new("publicnode"),
|
||||
ksp_onchain_transport_lib::YellowstoneGrpcClusterName::new("mainnet-beta"),
|
||||
ksp_onchain_transport_lib::YellowstoneGrpcClusterName::new(cluster),
|
||||
url,
|
||||
session,
|
||||
));
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
#[ignore = "opt-in live PublicNode Mainnet Yellowstone gRPC smoke; performs external TLS and unary gRPC requests"]
|
||||
async fn publicnode_mainnet_yellowstone_reaches_standard_version_and_slot_without_credentials() {
|
||||
let endpoint = publicnode_mainnet_endpoint().expect("programmatic PublicNode Yellowstone settings must construct an endpoint");
|
||||
let channel = ksp_onchain_transport_lib::YellowstoneGrpcChannel::connect(&endpoint).await.expect("PublicNode Yellowstone TLS connection must succeed");
|
||||
assert_eq!(channel.endpoint_name(), "publicnode_mainnet_yellowstone");
|
||||
assert_eq!(channel.provider().as_str(), "publicnode");
|
||||
assert_eq!(channel.cluster().as_str(), "mainnet-beta");
|
||||
let client = channel.standard_unary_client();
|
||||
let version = client.get_version().await.expect("PublicNode Yellowstone GetVersion must succeed");
|
||||
assert!(!version.version().is_empty());
|
||||
let slot = client
|
||||
.get_slot(std::option::Option::Some(ksp_onchain_transport_lib::SolanaCommitment::Confirmed))
|
||||
.await
|
||||
.expect("PublicNode Yellowstone GetSlot must succeed");
|
||||
assert!(slot.slot() > 0);
|
||||
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),
|
||||
};
|
||||
request.insert_slot_filter(name, ksp_onchain_transport_lib::YellowstoneSubscribeSlotFilter::new())?;
|
||||
return std::result::Result::Ok(request);
|
||||
}
|
||||
|
||||
async fn assert_publicnode_slot_stream(name: &str, cluster: &str, url: &str) {
|
||||
let endpoint = publicnode_endpoint(name, cluster, url).expect("programmatic PublicNode Yellowstone settings must construct an unauthenticated endpoint");
|
||||
let channel = ksp_onchain_transport_lib::YellowstoneGrpcChannel::connect(&endpoint).await.expect("PublicNode Yellowstone TLS connection must succeed");
|
||||
assert_eq!(channel.endpoint_name(), name);
|
||||
assert_eq!(channel.provider().as_str(), "publicnode");
|
||||
assert_eq!(channel.cluster().as_str(), cluster);
|
||||
let request = slot_request().expect("PublicNode Yellowstone slot request must be valid");
|
||||
let mut session = channel.open_standard_subscribe(request).await.expect("PublicNode Yellowstone Subscribe must open without authentication metadata");
|
||||
let slot = tokio::time::timeout(std::time::Duration::from_secs(20), async {
|
||||
loop {
|
||||
match session.next_update().await.expect("PublicNode Yellowstone Subscribe update must decode") {
|
||||
std::option::Option::Some(ksp_onchain_transport_lib::YellowstoneSubscribeUpdate::Slot(update)) => break update.slot(),
|
||||
std::option::Option::Some(_) => {},
|
||||
std::option::Option::None => panic!("PublicNode Yellowstone Subscribe ended before a slot update"),
|
||||
}
|
||||
}
|
||||
})
|
||||
.await
|
||||
.expect("PublicNode Yellowstone Subscribe must publish a slot update before the smoke deadline");
|
||||
assert!(slot > 0);
|
||||
session.close().await.expect("PublicNode Yellowstone Subscribe must close cleanly");
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
#[ignore = "opt-in live PublicNode Mainnet Yellowstone gRPC smoke; performs an unauthenticated external TLS/Subscribe request"]
|
||||
async fn publicnode_mainnet_yellowstone_streams_slots_without_authentication_metadata() {
|
||||
assert_publicnode_slot_stream("publicnode_mainnet_yellowstone", "mainnet-beta", "https://solana-yellowstone-grpc.publicnode.com:443").await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
#[ignore = "opt-in live PublicNode Testnet Yellowstone gRPC smoke; performs an unauthenticated external TLS/Subscribe request"]
|
||||
async fn publicnode_testnet_yellowstone_streams_slots_without_authentication_metadata() {
|
||||
assert_publicnode_slot_stream("publicnode_testnet_yellowstone", "testnet", "https://solana-testnet-yellowstone-grpc.publicnode.com:443").await;
|
||||
}
|
||||
|
||||
182
deltas/0.2.9/pre.013-fix.001.md
Normal file
182
deltas/0.2.9/pre.013-fix.001.md
Normal file
@@ -0,0 +1,182 @@
|
||||
<!-- file: deltas/0.2.9/pre.013-fix.001.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# Delta `0.2.9-pre.013-fix.001` — smoke PublicNode streaming + Testnet
|
||||
|
||||
## 1. Base
|
||||
|
||||
```text
|
||||
livraison : 0.2.9-pre.013
|
||||
Cargo : 0.2.9-pre.13
|
||||
```
|
||||
|
||||
Le gate déterministe opérateur de `pre.013` est vert jusqu'au smoke live :
|
||||
|
||||
```text
|
||||
cargo fmt --all PASS
|
||||
Rust workspace audit clean / 0 export candidate
|
||||
Markdown table audit clean, 87 tableaux / 254 fichiers
|
||||
cargo check --workspace PASS
|
||||
cargo clippy --workspace --all-targets PASS sans warning
|
||||
Transport unit 383/383
|
||||
Transport public API 49/49
|
||||
Transport release completeness 43/43
|
||||
Transport doctests 4/4
|
||||
workspace dependency canary 3/3
|
||||
```
|
||||
|
||||
Le smoke initial atteint le service PublicNode Mainnet mais `GetVersion` retourne `PERMISSION_DENIED`.
|
||||
|
||||
## 2. Correction du diagnostic
|
||||
|
||||
Cette réponse ne suffit pas à conclure qu'un `x-token` PublicNode est requis.
|
||||
|
||||
La surface Yellowstone upstream permet à un opérateur de désactiver les méthodes unary indépendamment du streaming `Subscribe`. PublicNode expose son service comme gateway Yellowstone gRPC public et aucune procédure d'authentification n'est retenue comme contrat KSP tant qu'elle n'est pas établie par une source provider suffisamment forte.
|
||||
|
||||
Conséquence :
|
||||
|
||||
```text
|
||||
aucun x-token inventé
|
||||
aucun secret PublicNode ajouté à Config
|
||||
aucune variable d'environnement d'authentification ajoutée
|
||||
smoke recentré sur Subscribe, fonctionnalité centrale de 0.2.9
|
||||
```
|
||||
|
||||
## 3. Endpoint Testnet acquis
|
||||
|
||||
L'opérateur a confirmé le host/port Yellowstone gRPC Testnet :
|
||||
|
||||
```text
|
||||
solana-testnet-yellowstone-grpc.publicnode.com:443
|
||||
```
|
||||
|
||||
KSP utilise l'URI TLS :
|
||||
|
||||
```text
|
||||
https://solana-testnet-yellowstone-grpc.publicnode.com:443
|
||||
```
|
||||
|
||||
## 4. Config V3
|
||||
|
||||
`config/std.transport.json` conserve le profil Mainnet PublicNode sans metadata d'authentification et ajoute :
|
||||
|
||||
```text
|
||||
profile_id = publicnode_testnet
|
||||
provider = publicnode
|
||||
cluster = testnet
|
||||
protocol = solana_yellowstone
|
||||
url = https://solana-testnet-yellowstone-grpc.publicnode.com:443
|
||||
metadata = none
|
||||
```
|
||||
|
||||
Comme pour le profil Mainnet, les axes HTTP/WS du profil restent des transports Solana standards distincts du provider gRPC.
|
||||
|
||||
Le canari Config existant couvre désormais les deux profils PublicNode et impose explicitement :
|
||||
|
||||
```text
|
||||
metadata gRPC vide
|
||||
provider/cluster/URL exacts
|
||||
validation Transport verte
|
||||
Debug sans URL endpoint
|
||||
```
|
||||
|
||||
## 5. Smoke Transport pur
|
||||
|
||||
Le smoke opt-in ne teste plus les méthodes unary provider-dépendantes `GetVersion` / `GetSlot`.
|
||||
|
||||
Il ouvre maintenant la surface standard réellement visée par la release :
|
||||
|
||||
```text
|
||||
TLS
|
||||
Subscribe bidirectionnel
|
||||
filtre slots standard
|
||||
réception d'un YellowstoneSubscribeUpdate::Slot
|
||||
slot > 0
|
||||
close borné
|
||||
```
|
||||
|
||||
Deux cas live sont présents :
|
||||
|
||||
```text
|
||||
PublicNode Mainnet Yellowstone Subscribe sans metadata d'authentification
|
||||
PublicNode Testnet Yellowstone Subscribe sans metadata d'authentification
|
||||
```
|
||||
|
||||
Le test reste dans `ksp-onchain-transport-lib`, construit ses settings programmatiquement et ne dépend ni de Config ni d'un secret opérateur.
|
||||
|
||||
## 6. Signal de version
|
||||
|
||||
Le correctif modifie une config runtime et un test Rust :
|
||||
|
||||
```text
|
||||
workspace.package.version = 0.2.9-pre.13.fix.1
|
||||
commit attendu = v0.2.9-pre.013-fix.001
|
||||
```
|
||||
|
||||
## 7. Gate opérateur
|
||||
|
||||
### 7.1 Déterministe
|
||||
|
||||
```bash
|
||||
cargo fmt --all
|
||||
python3 scripts/audit_rust_workspace_rules.py
|
||||
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.2.9
|
||||
cargo check --workspace
|
||||
cargo clippy --workspace --all-targets
|
||||
cargo test -p ksp-config-lib
|
||||
cargo test -p ksp-onchain-transport-lib
|
||||
cargo test -p ksp-core-lib --test workspace_dependencies
|
||||
```
|
||||
|
||||
Attendus minima :
|
||||
|
||||
```text
|
||||
Config unit 113
|
||||
Config public API 15
|
||||
Config ownership 5
|
||||
Transport unit 383
|
||||
Transport public API 49
|
||||
Transport release completeness 43
|
||||
Transport doctests 4
|
||||
workspace dependencies 3
|
||||
```
|
||||
|
||||
### 7.2 Smoke live
|
||||
|
||||
```bash
|
||||
cargo test -p ksp-onchain-transport-lib --test yellowstone_publicnode_smoke -- --ignored --nocapture
|
||||
```
|
||||
|
||||
Attendu :
|
||||
|
||||
```text
|
||||
2 passed
|
||||
0 failed
|
||||
0 ignored
|
||||
```
|
||||
|
||||
Aucun token ou secret ne doit être nécessaire pour ce smoke. Si `Subscribe` retourne à son tour `PERMISSION_DENIED`, l'hypothèse d'une authentification provider redevient ouverte et doit alors être établie avant tout nouveau correctif Config.
|
||||
|
||||
### 7.3 Graphes et workspace
|
||||
|
||||
```bash
|
||||
cargo tree -p ksp-onchain-transport-lib
|
||||
cargo tree -p ksp-onchain-transport-lib --duplicates
|
||||
cargo tree --duplicates
|
||||
cargo test --workspace
|
||||
```
|
||||
|
||||
## 8. Frontières
|
||||
|
||||
Ce fix reste strictement dans le couloir technique/live de `pre.013` :
|
||||
|
||||
```text
|
||||
aucun README/USAGE modifié
|
||||
aucun plan/validation modifié
|
||||
aucun CHANGELOG/ROADMAP modifié
|
||||
aucun prompt modifié
|
||||
aucun provider-specific engine ajouté
|
||||
aucun secret committé
|
||||
```
|
||||
|
||||
La prochaine tranche reste `0.2.9-pre.014` de réconciliation documentaire, uniquement après fermeture verte de ce fix.
|
||||
Reference in New Issue
Block a user