v0.3.15-pre.006-fix.001
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<!-- file: crates/ksp-config-lib/README.md -->
|
||||
<!-- version: 13 -->
|
||||
<!-- version: 14 -->
|
||||
|
||||
# ksp-config-lib
|
||||
|
||||
@@ -104,7 +104,7 @@ Le document Logging refuse les valeurs de sensibilité `Secret` dans sa configur
|
||||
- [`../../config/std.store.json`](../../config/std.store.json) — targets Store PostgreSQL Devnet/Mainnet/Testnet et settings runtime bornés ;
|
||||
- [`../../config/std.wallet.json`](../../config/std.wallet.json) — racine Wallet globale et sous-répertoire optionnel par profil ;
|
||||
- [`../../config/composite.ksp-app-backfill-desk.json`](../../config/composite.ksp-app-backfill-desk.json) — composition Logging/Transport/Store de Backfill Desk ;
|
||||
- [`../../config/composite.ksp-app-raw-transaction-ingest-desk.json`](../../config/composite.ksp-app-raw-transaction-ingest-desk.json) — profils cohérents Logging/Transport/Store du Raw Transaction Ingest Desk ;
|
||||
- [`../../config/composite.ksp-app-raw-transaction-ingest-desk.json`](../../config/composite.ksp-app-raw-transaction-ingest-desk.json) — profils réseau-centriques Logging/Transport/Store du Raw Transaction Ingest Desk, capables d’agréger plusieurs sources Transport same-network ;
|
||||
- [`../../config/composite.ksp-app-solprices-desk.json`](../../config/composite.ksp-app-solprices-desk.json) — composition Logging/Off-chain Transport de SOL Prices Desk ;
|
||||
- [`../../config/composite.ksp-app-store-desk.json`](../../config/composite.ksp-app-store-desk.json) — composition Logging/Store de Store Desk ;
|
||||
- [`../../config/composite.ksp-app-wallet-desk.json`](../../config/composite.ksp-app-wallet-desk.json) — composition Logging/Off-chain Transport/On-chain Transport/Wallet de Wallet Desk ;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: crates/ksp-config-lib/USAGE.md -->
|
||||
<!-- version: 16 -->
|
||||
<!-- version: 17 -->
|
||||
|
||||
# Utilisation de ksp-config-lib
|
||||
|
||||
@@ -158,13 +158,13 @@ secret_metadata = metadata dont la valeur doit avoir une provenance KSP_SECRET_*
|
||||
|
||||
`protocol` n'est pas un nouveau `WsProtocolKind` et ne transforme pas PublicNode en protocole. Config résout les placeholders, vérifie la classe de sensibilité des metadata puis construit `YellowstoneGrpcTransportSettings`. Transport ne lit jamais l'environnement. Les valeurs de `secret_metadata` sont disponibles au runtime mais redacted dans `safe_value` et dans les représentations `Debug`.
|
||||
|
||||
Le provider affiche `solana-yellowstone-grpc.publicnode.com:443` ; le profil versionné `publicnode_mainnet` le représente sous la forme URL TLS requise par Transport, `https://solana-yellowstone-grpc.publicnode.com:443`, sans credential. Aucun hostname Testnet n'est versionné tant que sa valeur exacte n'est pas confirmée depuis une source opérateur/officielle.
|
||||
Les profils versionnés `publicnode_mainnet` et `publicnode_testnet` représentent leurs endpoints Yellowstone sous la forme URL TLS requise par Transport et conservent le `x-token` dans `secret_metadata`. Ils restent des sources Transport network-scoped et ne définissent jamais l’identité du réseau applicatif.
|
||||
|
||||
L'accesseur historique `into_transport_settings()` conserve volontairement son tuple `(HTTP, Option<WS>)`. Un consumer ayant besoin des trois backends utilise `into_all_transport_settings()` ou les accesseurs séparés afin de ne pas casser silencieusement les consumers V2.
|
||||
|
||||
Les protocoles WebSocket restent `kind = "solana_standard"` et `kind = "helius_laserstream"`. Chaque endpoint peut en plus déclarer `capabilities`, liste de descriptors `WsSubscriptionKind` (`account`, `block`, `logs`, `program`, `root`, `signature`, `slot`, `slots_updates`, `vote`, `helius_transaction`). L'absence de cette liste reste backward-compatible mais ne constitue aucune preuve de support ; une liste présente est mappée explicitement vers Transport puis validée contre le protocole. Config ne déduit jamais une capability depuis `provider`.
|
||||
|
||||
Les profils Solana standard versionnés déclarent conservativement les six familles stables `account/logs/program/root/signature/slot` et ne déclarent donc pas `block`. Les profils `helius_devnet` et `helius_mainnet` déclarent les sept familles Helius déjà supportées par la façade (`account/logs/program/root/signature/slot/slots_updates`) plus `helius_transaction`, sans `block` ni `vote`. Helius LaserStream WebSocket conserve ses URLs Config-owned et sa clé `KSP_SECRET_HELIUS_API_KEY`; cette surface est indépendante de Yellowstone gRPC.
|
||||
Les profils Solana standard versionnés déclarent conservativement les six familles stables `account/logs/program/root/signature/slot` et ne déclarent donc pas `block`. Le profil `testnet_public` fournit le socle HTTP + WS standard Testnet sans credential ; `publicnode_testnet` reste une source Yellowstone optionnelle séparée. Les profils `helius_devnet` et `helius_mainnet` déclarent les sept familles Helius déjà supportées par la façade (`account/logs/program/root/signature/slot/slots_updates`) plus `helius_transaction`, sans `block` ni `vote`. Helius LaserStream WebSocket conserve ses URLs Config-owned et sa clé `KSP_SECRET_HELIUS_API_KEY`; cette surface est indépendante de Yellowstone gRPC.
|
||||
|
||||
Les scalaires `*_ms` restent des valeurs Config et sont convertis en `std::time::Duration` par l'adapter. La dépendance reste unidirectionnelle : Config connaît les contrats Transport pour les construire ; Transport ne connaît ni Config, ni `.env`, ni les variables KSP.
|
||||
|
||||
@@ -347,6 +347,8 @@ Elles n'altèrent jamais l'environnement hérité du processus. Une valeur proce
|
||||
|
||||
Sur Unix, un nouveau `.env` est créé avec des permissions privées `0600`; les permissions existantes sont préservées lors des remplacements atomiques.
|
||||
|
||||
Le composite `cfg.composite.ksp-app-raw-transaction-ingest-desk` expose des profils applicatifs `devnet`, `mainnet` et `testnet`. Un même profil peut référencer `transport`, `transport.helius` et `transport.yellowstone` tant que tous les profils Transport résolus appartiennent au même réseau que Store. Le provider identifie une source de capability, jamais un réseau logique.
|
||||
|
||||
## 8. `.env.example`
|
||||
|
||||
`/.env.example` est l'inventaire versionné. `/.env` reste local et ignoré.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-config-lib/unit_tests/composite.rs
|
||||
// version: 5
|
||||
// version: 6
|
||||
|
||||
const TEST_COMPOSITE_FILENAME: &str = "examples/composite.example.json";
|
||||
const TEST_COMPOSITE_FILE_ID: &str = "cfg.composite.test";
|
||||
@@ -129,7 +129,7 @@ fn duplicate_component_ids_are_rejected_inside_one_composite_profile() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn v0_3_15_pre_003_raw_ingest_composite_keeps_transport_and_store_networks_coherent() {
|
||||
fn v0_3_15_pre_006_fix_001_raw_ingest_composite_is_network_centric_and_keeps_all_transport_sources_coherent() {
|
||||
let workspace = workspace_root();
|
||||
let bootstrap = crate::ConfigBootstrapOptions::from_paths(workspace.join("config"), workspace.join("config/schemas"));
|
||||
assert!(bootstrap.is_ok(), "committed Config roots should be valid: {bootstrap:?}");
|
||||
@@ -148,47 +148,58 @@ fn v0_3_15_pre_003_raw_ingest_composite_keeps_transport_and_store_networks_coher
|
||||
process.insert("KSP_SECRET_PUBLICNODE_MAINNET_GRPC_X_TOKEN".to_owned(), "publicnode-mainnet-composite-canary".to_owned());
|
||||
process.insert("KSP_SECRET_PUBLICNODE_TESTNET_GRPC_X_TOKEN".to_owned(), "publicnode-testnet-composite-canary".to_owned());
|
||||
let environment = crate::ConfigEnvironment::from_maps(process, std::collections::BTreeMap::new());
|
||||
for (profile_id, expected_network) in [
|
||||
("devnet_public", "devnet"),
|
||||
("helius_devnet", "devnet"),
|
||||
("orbitflare_devnet", "devnet"),
|
||||
("mainnet_public", "mainnet"),
|
||||
("helius_mainnet", "mainnet"),
|
||||
("publicnode_mainnet", "mainnet"),
|
||||
("publicnode_testnet", "testnet"),
|
||||
for (profile_id, expected_network, expected_transport_profiles) in [
|
||||
("devnet", "devnet", vec!["devnet_public", "helius_devnet", "orbitflare_devnet"]),
|
||||
("mainnet", "mainnet", vec!["mainnet_public", "helius_mainnet", "publicnode_mainnet"]),
|
||||
("testnet", "testnet", vec!["testnet_public", "publicnode_testnet"]),
|
||||
] {
|
||||
let composite = engine.load_resolved_composite(&file_id, std::option::Option::Some(profile_id));
|
||||
assert!(composite.is_ok(), "Raw Ingest composite profile {profile_id} should resolve: {composite:?}");
|
||||
assert!(composite.is_ok(), "Raw Ingest network profile {profile_id} should resolve: {composite:?}");
|
||||
let composite = match composite {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => continue,
|
||||
};
|
||||
assert_eq!(composite.profile_id(), profile_id);
|
||||
let transport_component = composite.component("transport");
|
||||
let store_component = composite.component("store");
|
||||
assert!(transport_component.is_some(), "profile {profile_id} should contain Transport");
|
||||
assert!(store_component.is_some(), "profile {profile_id} should contain Store");
|
||||
let (transport_component, store_component) = match (transport_component, store_component) {
|
||||
(std::option::Option::Some(transport), std::option::Option::Some(store)) => (transport, store),
|
||||
_ => continue,
|
||||
let store_component = match store_component {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => continue,
|
||||
};
|
||||
assert_eq!(transport_component.resolved().profile_id(), profile_id);
|
||||
assert_eq!(store_component.resolved().profile_id(), expected_network);
|
||||
let transport = engine.resolve_transport_config_profile(transport_component.resolved(), &environment);
|
||||
let store = engine.resolve_store_config_profile(store_component.resolved(), &environment);
|
||||
assert!(transport.is_ok(), "Transport profile {profile_id} should map: {transport:?}");
|
||||
assert!(store.is_ok(), "Store profile {expected_network} should map: {store:?}");
|
||||
let (transport, store) = match (transport, store) {
|
||||
(std::result::Result::Ok(transport), std::result::Result::Ok(store)) => (transport, store),
|
||||
_ => continue,
|
||||
};
|
||||
assert_eq!(store.settings().network().as_str(), expected_network);
|
||||
assert!(transport.http_settings().endpoints().iter().all(|endpoint| return endpoint.cluster().as_str() == expected_network));
|
||||
if let std::option::Option::Some(ws) = transport.ws_settings() {
|
||||
assert!(ws.endpoints().iter().all(|endpoint| return endpoint.cluster().as_str() == expected_network));
|
||||
if let std::result::Result::Ok(store) = store {
|
||||
assert_eq!(store.settings().network().as_str(), expected_network);
|
||||
}
|
||||
if let std::option::Option::Some(grpc) = transport.grpc_settings() {
|
||||
assert!(grpc.endpoints().iter().all(|endpoint| return endpoint.cluster().as_str() == expected_network));
|
||||
let transport_components = composite
|
||||
.components()
|
||||
.iter()
|
||||
.filter(|(component_id, component)| {
|
||||
return component_id.starts_with("transport") && component.resolved().file_id().as_str() == crate::FILE_ID_STD_TRANSPORT;
|
||||
})
|
||||
.map(|(_, component)| return component)
|
||||
.collect::<std::vec::Vec<_>>();
|
||||
assert_eq!(transport_components.len(), expected_transport_profiles.len());
|
||||
let actual_profiles = transport_components.iter().map(|component| return component.resolved().profile_id()).collect::<std::vec::Vec<_>>();
|
||||
for expected_transport_profile in expected_transport_profiles {
|
||||
assert!(
|
||||
actual_profiles.contains(&expected_transport_profile),
|
||||
"network profile {profile_id} should include Transport source {expected_transport_profile}"
|
||||
);
|
||||
}
|
||||
for component in transport_components {
|
||||
let transport = engine.resolve_transport_config_profile(component.resolved(), &environment);
|
||||
assert!(transport.is_ok(), "Transport source {} should map: {transport:?}", component.resolved().profile_id());
|
||||
if let std::result::Result::Ok(transport) = transport {
|
||||
assert!(transport.http_settings().endpoints().iter().all(|endpoint| return endpoint.cluster().as_str() == expected_network));
|
||||
if let std::option::Option::Some(ws) = transport.ws_settings() {
|
||||
assert!(ws.endpoints().iter().all(|endpoint| return endpoint.cluster().as_str() == expected_network));
|
||||
}
|
||||
if let std::option::Option::Some(grpc) = transport.grpc_settings() {
|
||||
assert!(grpc.endpoints().iter().all(|endpoint| return endpoint.cluster().as_str() == expected_network));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"format_version": 1,
|
||||
"default_profile": "devnet_public",
|
||||
"default_profile": "devnet",
|
||||
"profiles": [
|
||||
{
|
||||
"profile_id": "devnet_public",
|
||||
"profile_id": "devnet",
|
||||
"documents": [
|
||||
{
|
||||
"component_id": "logging",
|
||||
@@ -16,42 +16,12 @@
|
||||
"profile_id": "secret_test"
|
||||
},
|
||||
{
|
||||
"component_id": "store",
|
||||
"file_id": "cfg.std.store",
|
||||
"profile_id": "devnet"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"profile_id": "helius_devnet",
|
||||
"documents": [
|
||||
{
|
||||
"component_id": "logging",
|
||||
"file_id": "cfg.std.logging",
|
||||
"profile_id": "local_dev"
|
||||
},
|
||||
{
|
||||
"component_id": "transport",
|
||||
"component_id": "transport.helius",
|
||||
"file_id": "cfg.std.transport",
|
||||
"profile_id": "helius_devnet"
|
||||
},
|
||||
{
|
||||
"component_id": "store",
|
||||
"file_id": "cfg.std.store",
|
||||
"profile_id": "devnet"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"profile_id": "orbitflare_devnet",
|
||||
"documents": [
|
||||
{
|
||||
"component_id": "logging",
|
||||
"file_id": "cfg.std.logging",
|
||||
"profile_id": "local_dev"
|
||||
},
|
||||
{
|
||||
"component_id": "transport",
|
||||
"component_id": "transport.yellowstone",
|
||||
"file_id": "cfg.std.transport",
|
||||
"profile_id": "secret_test"
|
||||
},
|
||||
@@ -63,7 +33,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"profile_id": "mainnet_public",
|
||||
"profile_id": "mainnet",
|
||||
"documents": [
|
||||
{
|
||||
"component_id": "logging",
|
||||
@@ -75,6 +45,16 @@
|
||||
"file_id": "cfg.std.transport",
|
||||
"profile_id": "secret_test"
|
||||
},
|
||||
{
|
||||
"component_id": "transport.helius",
|
||||
"file_id": "cfg.std.transport",
|
||||
"profile_id": "helius_devnet"
|
||||
},
|
||||
{
|
||||
"component_id": "transport.yellowstone",
|
||||
"file_id": "cfg.std.transport",
|
||||
"profile_id": "secret_test"
|
||||
},
|
||||
{
|
||||
"component_id": "store",
|
||||
"file_id": "cfg.std.store",
|
||||
@@ -83,7 +63,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"profile_id": "helius_mainnet",
|
||||
"profile_id": "testnet",
|
||||
"documents": [
|
||||
{
|
||||
"component_id": "logging",
|
||||
@@ -96,42 +76,7 @@
|
||||
"profile_id": "secret_test"
|
||||
},
|
||||
{
|
||||
"component_id": "store",
|
||||
"file_id": "cfg.std.store",
|
||||
"profile_id": "mainnet"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"profile_id": "publicnode_mainnet",
|
||||
"documents": [
|
||||
{
|
||||
"component_id": "logging",
|
||||
"file_id": "cfg.std.logging",
|
||||
"profile_id": "local_dev"
|
||||
},
|
||||
{
|
||||
"component_id": "transport",
|
||||
"file_id": "cfg.std.transport",
|
||||
"profile_id": "secret_test"
|
||||
},
|
||||
{
|
||||
"component_id": "store",
|
||||
"file_id": "cfg.std.store",
|
||||
"profile_id": "mainnet"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"profile_id": "publicnode_testnet",
|
||||
"documents": [
|
||||
{
|
||||
"component_id": "logging",
|
||||
"file_id": "cfg.std.logging",
|
||||
"profile_id": "local_dev"
|
||||
},
|
||||
{
|
||||
"component_id": "transport",
|
||||
"component_id": "transport.yellowstone",
|
||||
"file_id": "cfg.std.transport",
|
||||
"profile_id": "secret_test"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-config-lib/unit_tests/transport.rs
|
||||
// version: 14
|
||||
// version: 15
|
||||
|
||||
#[test]
|
||||
fn fixture_transport_profile_maps_complete_runtime_contract() {
|
||||
@@ -211,6 +211,33 @@ fn committed_v3_orbitflare_devnet_profile_maps_provider_neutral_yellowstone_grpc
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn v0_3_15_pre_006_fix_001_testnet_public_is_keyless_standard_transport_without_yellowstone() {
|
||||
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("testnet_public"), &environment);
|
||||
assert!(resolved.is_ok(), "committed keyless Testnet Transport profile should map without provider credentials: {resolved:?}");
|
||||
if let std::result::Result::Ok(resolved) = resolved {
|
||||
assert_eq!(resolved.profile_id(), "testnet_public");
|
||||
assert_eq!(resolved.settings().endpoints().len(), 1);
|
||||
assert_eq!(resolved.settings().endpoints()[0].cluster().as_str(), "testnet");
|
||||
let ws = resolved.ws_settings();
|
||||
assert!(ws.is_some());
|
||||
if let std::option::Option::Some(ws) = ws {
|
||||
assert_eq!(ws.endpoints().len(), 1);
|
||||
assert_eq!(ws.endpoints()[0].cluster().as_str(), "testnet");
|
||||
assert_eq!(ws.endpoints()[0].protocol(), ksp_onchain_transport_lib::WsProtocolKind::SolanaStandard);
|
||||
assert!(ws.endpoints()[0].supports_subscription(ksp_onchain_transport_lib::WsSubscriptionKind::Logs));
|
||||
assert!(!ws.endpoints()[0].supports_subscription(ksp_onchain_transport_lib::WsSubscriptionKind::Block));
|
||||
}
|
||||
assert!(resolved.grpc_settings().is_none(), "keyless Testnet base profile must not conflate PublicNode Yellowstone with the network itself");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn committed_v3_publicnode_profiles_map_provider_neutral_yellowstone_grpc_with_network_scoped_secret_x_tokens() {
|
||||
let engine = committed_engine();
|
||||
|
||||
Reference in New Issue
Block a user