v0.3.15-pre.003

This commit is contained in:
2026-09-13 08:11:35 +02:00
parent dc126497db
commit bd376dc479
32 changed files with 1198 additions and 105 deletions

View File

@@ -1,12 +1,12 @@
# file: Cargo.toml # file: Cargo.toml
# version: 596 # version: 597
[workspace] [workspace]
resolver = "3" resolver = "3"
members = ["crates/ksp-app-backfill-desk", "crates/ksp-app-config-desk", "crates/ksp-app-solprices-desk", "crates/ksp-app-store-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-interface-lib", "crates/ksp-job-api", "crates/ksp-job-backfill-lib", "crates/ksp-logging-lib", "crates/ksp-offchain-transport-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-program-api", "crates/ksp-raw-transaction-lib", "crates/ksp-store-api", "crates/ksp-store-lib", "crates/ksp-store-postgres-lib", "crates/ksp-wallet-lib", "crates/ksp-worker-api", "crates/ksp-worker-raw-transaction-ingest-lib"] members = ["crates/ksp-app-backfill-desk", "crates/ksp-app-config-desk", "crates/ksp-app-solprices-desk", "crates/ksp-app-store-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-interface-lib", "crates/ksp-job-api", "crates/ksp-job-backfill-lib", "crates/ksp-logging-lib", "crates/ksp-offchain-transport-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-program-api", "crates/ksp-raw-transaction-lib", "crates/ksp-store-api", "crates/ksp-store-lib", "crates/ksp-store-postgres-lib", "crates/ksp-wallet-lib", "crates/ksp-worker-api", "crates/ksp-worker-raw-transaction-ingest-lib"]
[workspace.package] [workspace.package]
version = "0.3.15-pre.2.fix.1" version = "0.3.15-pre.3"
edition = "2024" edition = "2024"
license = "MIT" license = "MIT"
repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project" repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project"

View File

@@ -0,0 +1,146 @@
{
"format_version": 1,
"default_profile": "devnet_public",
"profiles": [
{
"profile_id": "devnet_public",
"documents": [
{
"component_id": "logging",
"file_id": "cfg.std.logging",
"profile_id": "supertrace"
},
{
"component_id": "transport",
"file_id": "cfg.std.transport",
"profile_id": "devnet_public"
},
{
"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": "supertrace"
},
{
"component_id": "transport",
"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": "supertrace"
},
{
"component_id": "transport",
"file_id": "cfg.std.transport",
"profile_id": "orbitflare_devnet"
},
{
"component_id": "store",
"file_id": "cfg.std.store",
"profile_id": "devnet"
}
]
},
{
"profile_id": "mainnet_public",
"documents": [
{
"component_id": "logging",
"file_id": "cfg.std.logging",
"profile_id": "supertrace"
},
{
"component_id": "transport",
"file_id": "cfg.std.transport",
"profile_id": "mainnet_public"
},
{
"component_id": "store",
"file_id": "cfg.std.store",
"profile_id": "mainnet"
}
]
},
{
"profile_id": "helius_mainnet",
"documents": [
{
"component_id": "logging",
"file_id": "cfg.std.logging",
"profile_id": "supertrace"
},
{
"component_id": "transport",
"file_id": "cfg.std.transport",
"profile_id": "helius_mainnet"
},
{
"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": "supertrace"
},
{
"component_id": "transport",
"file_id": "cfg.std.transport",
"profile_id": "publicnode_mainnet"
},
{
"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": "supertrace"
},
{
"component_id": "transport",
"file_id": "cfg.std.transport",
"profile_id": "publicnode_testnet"
},
{
"component_id": "store",
"file_id": "cfg.std.store",
"profile_id": "testnet"
}
]
}
]
}

View File

@@ -87,7 +87,15 @@
"provider": "solana-public", "provider": "solana-public",
"cluster": "mainnet", "cluster": "mainnet",
"kind": "solana_standard", "kind": "solana_standard",
"url": "${KSP_PUBLIC_SOLANA_MAINNET_WS_URL:-wss://api.mainnet.solana.com}" "url": "${KSP_PUBLIC_SOLANA_MAINNET_WS_URL:-wss://api.mainnet.solana.com}",
"capabilities": [
"account",
"logs",
"program",
"root",
"signature",
"slot"
]
}, },
{ {
"name": "mainnet_helius_ws", "name": "mainnet_helius_ws",
@@ -99,7 +107,17 @@
"session": { "session": {
"notification_queue_capacity": 512, "notification_queue_capacity": 512,
"max_active_subscriptions": 2048 "max_active_subscriptions": 2048
} },
"capabilities": [
"account",
"logs",
"program",
"root",
"signature",
"slot",
"slots_updates",
"helius_transaction"
]
} }
] ]
}, },
@@ -140,7 +158,17 @@
"provider": "helius", "provider": "helius",
"cluster": "devnet", "cluster": "devnet",
"kind": "helius_laserstream", "kind": "helius_laserstream",
"url": "wss://devnet.helius-rpc.com/?api-key=${KSP_SECRET_HELIUS_API_KEY:-replace-me}" "url": "wss://devnet.helius-rpc.com/?api-key=${KSP_SECRET_HELIUS_API_KEY:-replace-me}",
"capabilities": [
"account",
"logs",
"program",
"root",
"signature",
"slot",
"slots_updates",
"helius_transaction"
]
} }
] ]
} }

View File

@@ -348,6 +348,26 @@
}, },
"session": { "session": {
"$ref": "#/$defs/wsSessionOverride" "$ref": "#/$defs/wsSessionOverride"
},
"capabilities": {
"type": "array",
"minItems": 1,
"maxItems": 10,
"uniqueItems": true,
"items": {
"enum": [
"account",
"block",
"logs",
"program",
"root",
"signature",
"slot",
"slots_updates",
"vote",
"helius_transaction"
]
}
} }
} }
}, },

View File

@@ -75,7 +75,66 @@
"provider": "solana-public", "provider": "solana-public",
"cluster": "devnet", "cluster": "devnet",
"kind": "solana_standard", "kind": "solana_standard",
"url": "${KSP_PUBLIC_SOLANA_DEVNET_WS_URL:-wss://api.devnet.solana.com}" "url": "${KSP_PUBLIC_SOLANA_DEVNET_WS_URL:-wss://api.devnet.solana.com}",
"capabilities": [
"account",
"logs",
"program",
"root",
"signature",
"slot"
]
}
]
},
{
"profile_id": "helius_devnet",
"endpoints": [
{
"name": "solana_devnet_public",
"enabled": true,
"provider": "solana-public",
"cluster": "devnet",
"url": "${KSP_PUBLIC_SOLANA_DEVNET_HTTP_URL:-https://api.devnet.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": "helius_devnet_ws",
"enabled": true,
"provider": "helius",
"cluster": "devnet",
"kind": "helius_laserstream",
"url": "wss://devnet.helius-rpc.com/?api-key=${KSP_SECRET_HELIUS_API_KEY}",
"capabilities": [
"account",
"logs",
"program",
"root",
"signature",
"slot",
"slots_updates",
"helius_transaction"
]
} }
] ]
}, },
@@ -116,7 +175,15 @@
"provider": "solana-public", "provider": "solana-public",
"cluster": "devnet", "cluster": "devnet",
"kind": "solana_standard", "kind": "solana_standard",
"url": "${KSP_PUBLIC_SOLANA_DEVNET_WS_URL:-wss://api.devnet.solana.com}" "url": "${KSP_PUBLIC_SOLANA_DEVNET_WS_URL:-wss://api.devnet.solana.com}",
"capabilities": [
"account",
"logs",
"program",
"root",
"signature",
"slot"
]
} }
], ],
"grpc_endpoints": [ "grpc_endpoints": [
@@ -174,7 +241,66 @@
"provider": "solana-public", "provider": "solana-public",
"cluster": "mainnet", "cluster": "mainnet",
"kind": "solana_standard", "kind": "solana_standard",
"url": "${KSP_PUBLIC_SOLANA_MAINNET_WS_URL:-wss://api.mainnet.solana.com}" "url": "${KSP_PUBLIC_SOLANA_MAINNET_WS_URL:-wss://api.mainnet.solana.com}",
"capabilities": [
"account",
"logs",
"program",
"root",
"signature",
"slot"
]
}
]
},
{
"profile_id": "helius_mainnet",
"endpoints": [
{
"name": "solana_mainnet_public",
"enabled": true,
"provider": "solana-public",
"cluster": "mainnet",
"url": "${KSP_PUBLIC_SOLANA_MAINNET_HTTP_URL:-https://api.mainnet.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": "helius_mainnet_ws",
"enabled": true,
"provider": "helius",
"cluster": "mainnet",
"kind": "helius_laserstream",
"url": "wss://mainnet.helius-rpc.com/?api-key=${KSP_SECRET_HELIUS_API_KEY}",
"capabilities": [
"account",
"logs",
"program",
"root",
"signature",
"slot",
"slots_updates",
"helius_transaction"
]
} }
] ]
}, },
@@ -269,7 +395,15 @@
"provider": "solana-public", "provider": "solana-public",
"cluster": "mainnet", "cluster": "mainnet",
"kind": "solana_standard", "kind": "solana_standard",
"url": "${KSP_PUBLIC_SOLANA_MAINNET_WS_URL:-wss://api.mainnet.solana.com}" "url": "${KSP_PUBLIC_SOLANA_MAINNET_WS_URL:-wss://api.mainnet.solana.com}",
"capabilities": [
"account",
"logs",
"program",
"root",
"signature",
"slot"
]
} }
] ]
}, },
@@ -310,7 +444,15 @@
"provider": "solana-public", "provider": "solana-public",
"cluster": "mainnet", "cluster": "mainnet",
"kind": "solana_standard", "kind": "solana_standard",
"url": "${KSP_PUBLIC_SOLANA_MAINNET_WS_URL:-wss://api.mainnet.solana.com}" "url": "${KSP_PUBLIC_SOLANA_MAINNET_WS_URL:-wss://api.mainnet.solana.com}",
"capabilities": [
"account",
"logs",
"program",
"root",
"signature",
"slot"
]
} }
], ],
"grpc_endpoints": [ "grpc_endpoints": [
@@ -367,7 +509,15 @@
"provider": "solana-public", "provider": "solana-public",
"cluster": "testnet", "cluster": "testnet",
"kind": "solana_standard", "kind": "solana_standard",
"url": "wss://api.testnet.solana.com" "url": "wss://api.testnet.solana.com",
"capabilities": [
"account",
"logs",
"program",
"root",
"signature",
"slot"
]
} }
], ],
"grpc_endpoints": [ "grpc_endpoints": [

View File

@@ -53,6 +53,7 @@
], ],
"resources": { "resources": {
"../../config/composite.ksp-app-backfill-desk.json": "config/composite.ksp-app-backfill-desk.json", "../../config/composite.ksp-app-backfill-desk.json": "config/composite.ksp-app-backfill-desk.json",
"../../config/composite.ksp-app-raw-transaction-ingest-desk.json": "config/composite.ksp-app-raw-transaction-ingest-desk.json",
"../../config/composite.ksp-app-solprices-desk.json": "config/composite.ksp-app-solprices-desk.json", "../../config/composite.ksp-app-solprices-desk.json": "config/composite.ksp-app-solprices-desk.json",
"../../config/composite.ksp-app-store-desk.json": "config/composite.ksp-app-store-desk.json", "../../config/composite.ksp-app-store-desk.json": "config/composite.ksp-app-store-desk.json",
"../../config/composite.ksp-app-wallet-desk.json": "config/composite.ksp-app-wallet-desk.json", "../../config/composite.ksp-app-wallet-desk.json": "config/composite.ksp-app-wallet-desk.json",

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-backfill-desk/tests/desktop_contract.rs // file: crates/ksp-app-backfill-desk/tests/desktop_contract.rs
// version: 17 // version: 18
//! Structural desktop contract checks for the Backfill Desk scaffold. //! Structural desktop contract checks for the Backfill Desk scaffold.
@@ -145,9 +145,10 @@ fn pre_003_composite_packaging_follows_atomic_config_registry_contract() {
std::option::Option::Some(value) => value, std::option::Option::Some(value) => value,
std::option::Option::None => return, std::option::Option::None => return,
}; };
assert_eq!(resources.len(), 15); assert_eq!(resources.len(), 16);
for required in [ for required in [
"../../config/composite.ksp-app-backfill-desk.json", "../../config/composite.ksp-app-backfill-desk.json",
"../../config/composite.ksp-app-raw-transaction-ingest-desk.json",
"../../config/composite.ksp-app-solprices-desk.json", "../../config/composite.ksp-app-solprices-desk.json",
"../../config/composite.ksp-app-store-desk.json", "../../config/composite.ksp-app-store-desk.json",
"../../config/composite.ksp-app-wallet-desk.json", "../../config/composite.ksp-app-wallet-desk.json",

View File

@@ -53,6 +53,7 @@
], ],
"resources": { "resources": {
"../../config/composite.ksp-app-backfill-desk.json": "config/composite.ksp-app-backfill-desk.json", "../../config/composite.ksp-app-backfill-desk.json": "config/composite.ksp-app-backfill-desk.json",
"../../config/composite.ksp-app-raw-transaction-ingest-desk.json": "config/composite.ksp-app-raw-transaction-ingest-desk.json",
"../../config/composite.ksp-app-solprices-desk.json": "config/composite.ksp-app-solprices-desk.json", "../../config/composite.ksp-app-solprices-desk.json": "config/composite.ksp-app-solprices-desk.json",
"../../config/composite.ksp-app-store-desk.json": "config/composite.ksp-app-store-desk.json", "../../config/composite.ksp-app-store-desk.json": "config/composite.ksp-app-store-desk.json",
"../../config/composite.ksp-app-wallet-desk.json": "config/composite.ksp-app-wallet-desk.json", "../../config/composite.ksp-app-wallet-desk.json": "config/composite.ksp-app-wallet-desk.json",

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-config-desk/tests/desktop_contract.rs // file: crates/ksp-app-config-desk/tests/desktop_contract.rs
// version: 13 // version: 14
//! Desktop build/shell contract audits for Config Desk. //! Desktop build/shell contract audits for Config Desk.
@@ -110,11 +110,15 @@ fn pre_018_packaged_runtime_bundles_config_resources_and_activates_shared_writab
let resources = tauri.pointer("/bundle/resources").and_then(serde_json::Value::as_object); let resources = tauri.pointer("/bundle/resources").and_then(serde_json::Value::as_object);
assert!(resources.is_some(), "packaged Config resources map must exist"); assert!(resources.is_some(), "packaged Config resources map must exist");
if let std::option::Option::Some(resources) = resources { if let std::option::Option::Some(resources) = resources {
assert_eq!(resources.len(), 15); assert_eq!(resources.len(), 16);
assert_eq!( assert_eq!(
resources.get("../../config/composite.ksp-app-backfill-desk.json").and_then(serde_json::Value::as_str), resources.get("../../config/composite.ksp-app-backfill-desk.json").and_then(serde_json::Value::as_str),
std::option::Option::Some("config/composite.ksp-app-backfill-desk.json"), std::option::Option::Some("config/composite.ksp-app-backfill-desk.json"),
); );
assert_eq!(
resources.get("../../config/composite.ksp-app-raw-transaction-ingest-desk.json").and_then(serde_json::Value::as_str),
std::option::Option::Some("config/composite.ksp-app-raw-transaction-ingest-desk.json"),
);
assert_eq!( assert_eq!(
resources.get("../../config/composite.ksp-app-solprices-desk.json").and_then(serde_json::Value::as_str), resources.get("../../config/composite.ksp-app-solprices-desk.json").and_then(serde_json::Value::as_str),
std::option::Option::Some("config/composite.ksp-app-solprices-desk.json"), std::option::Option::Some("config/composite.ksp-app-solprices-desk.json"),

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-config-desk/unit_tests/profiles.rs // file: crates/ksp-app-config-desk/unit_tests/profiles.rs
// version: 8 // version: 9
#[test] #[test]
fn profile_inventory_exposes_registered_profile_documents() { fn profile_inventory_exposes_registered_profile_documents() {
@@ -21,6 +21,9 @@ fn profile_inventory_exposes_registered_profile_documents() {
assert!(inventory.iter().any(|document| -> bool { assert!(inventory.iter().any(|document| -> bool {
return document.file_id == ksp_config_lib::FILE_ID_STD_WALLET; return document.file_id == ksp_config_lib::FILE_ID_STD_WALLET;
})); }));
assert!(inventory.iter().any(|document| -> bool {
return document.file_id == ksp_config_lib::FILE_ID_COMPOSITE_KSP_APP_RAW_TRANSACTION_INGEST_DESK;
}));
assert!(inventory.iter().any(|document| -> bool { assert!(inventory.iter().any(|document| -> bool {
return document.file_id == ksp_config_lib::FILE_ID_COMPOSITE_KSP_APP_WALLET_DESK; return document.file_id == ksp_config_lib::FILE_ID_COMPOSITE_KSP_APP_WALLET_DESK;
})); }));

View File

@@ -53,6 +53,7 @@
], ],
"resources": { "resources": {
"../../config/composite.ksp-app-backfill-desk.json": "config/composite.ksp-app-backfill-desk.json", "../../config/composite.ksp-app-backfill-desk.json": "config/composite.ksp-app-backfill-desk.json",
"../../config/composite.ksp-app-raw-transaction-ingest-desk.json": "config/composite.ksp-app-raw-transaction-ingest-desk.json",
"../../config/composite.ksp-app-solprices-desk.json": "config/composite.ksp-app-solprices-desk.json", "../../config/composite.ksp-app-solprices-desk.json": "config/composite.ksp-app-solprices-desk.json",
"../../config/composite.ksp-app-store-desk.json": "config/composite.ksp-app-store-desk.json", "../../config/composite.ksp-app-store-desk.json": "config/composite.ksp-app-store-desk.json",
"../../config/composite.ksp-app-wallet-desk.json": "config/composite.ksp-app-wallet-desk.json", "../../config/composite.ksp-app-wallet-desk.json": "config/composite.ksp-app-wallet-desk.json",

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-solprices-desk/tests/desktop_contract.rs // file: crates/ksp-app-solprices-desk/tests/desktop_contract.rs
// version: 12 // version: 13
//! Desktop scaffold, shared-template and Config packaging contract audits for SOL Prices Desk `0.2.12`. //! Desktop scaffold, shared-template and Config packaging contract audits for SOL Prices Desk `0.2.12`.
@@ -85,17 +85,21 @@ fn pre_002_package_is_mixed_lib_bin_and_frontend_is_scaffold_only() {
} }
#[test] #[test]
fn v0_3_8_pre_006_packaging_contains_current_fifteen_config_resources() { fn v0_3_8_pre_006_packaging_contains_current_sixteen_config_resources() {
let root = app_root(); let root = app_root();
let tauri = read_json(root.join("tauri.conf.json").as_path()); let tauri = read_json(root.join("tauri.conf.json").as_path());
let resources = tauri.pointer("/bundle/resources").and_then(serde_json::Value::as_object); let resources = tauri.pointer("/bundle/resources").and_then(serde_json::Value::as_object);
assert!(resources.is_some()); assert!(resources.is_some());
if let std::option::Option::Some(resources) = resources { if let std::option::Option::Some(resources) = resources {
assert_eq!(resources.len(), 15); assert_eq!(resources.len(), 16);
assert_eq!( assert_eq!(
resources.get("../../config/composite.ksp-app-backfill-desk.json").and_then(serde_json::Value::as_str), resources.get("../../config/composite.ksp-app-backfill-desk.json").and_then(serde_json::Value::as_str),
std::option::Option::Some("config/composite.ksp-app-backfill-desk.json"), std::option::Option::Some("config/composite.ksp-app-backfill-desk.json"),
); );
assert_eq!(
resources.get("../../config/composite.ksp-app-raw-transaction-ingest-desk.json").and_then(serde_json::Value::as_str),
std::option::Option::Some("config/composite.ksp-app-raw-transaction-ingest-desk.json"),
);
assert_eq!( assert_eq!(
resources.get("../../config/composite.ksp-app-solprices-desk.json").and_then(serde_json::Value::as_str), resources.get("../../config/composite.ksp-app-solprices-desk.json").and_then(serde_json::Value::as_str),
std::option::Option::Some("config/composite.ksp-app-solprices-desk.json"), std::option::Option::Some("config/composite.ksp-app-solprices-desk.json"),

View File

@@ -53,6 +53,7 @@
], ],
"resources": { "resources": {
"../../config/composite.ksp-app-backfill-desk.json": "config/composite.ksp-app-backfill-desk.json", "../../config/composite.ksp-app-backfill-desk.json": "config/composite.ksp-app-backfill-desk.json",
"../../config/composite.ksp-app-raw-transaction-ingest-desk.json": "config/composite.ksp-app-raw-transaction-ingest-desk.json",
"../../config/composite.ksp-app-solprices-desk.json": "config/composite.ksp-app-solprices-desk.json", "../../config/composite.ksp-app-solprices-desk.json": "config/composite.ksp-app-solprices-desk.json",
"../../config/composite.ksp-app-store-desk.json": "config/composite.ksp-app-store-desk.json", "../../config/composite.ksp-app-store-desk.json": "config/composite.ksp-app-store-desk.json",
"../../config/composite.ksp-app-wallet-desk.json": "config/composite.ksp-app-wallet-desk.json", "../../config/composite.ksp-app-wallet-desk.json": "config/composite.ksp-app-wallet-desk.json",

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-store-desk/tests/desktop_contract.rs // file: crates/ksp-app-store-desk/tests/desktop_contract.rs
// version: 10 // version: 11
//! Desktop template and frontend contract tests for the Store Desk scaffold. //! Desktop template and frontend contract tests for the Store Desk scaffold.
@@ -153,7 +153,8 @@ fn pre_006_composite_and_packaged_resources_cover_store_desk_and_the_complete_co
let resources = tauri["bundle"]["resources"].as_object(); let resources = tauri["bundle"]["resources"].as_object();
assert!(resources.is_some()); assert!(resources.is_some());
if let std::option::Option::Some(resources) = resources { if let std::option::Option::Some(resources) = resources {
assert_eq!(resources.len(), 15); assert_eq!(resources.len(), 16);
assert!(resources.contains_key("../../config/composite.ksp-app-raw-transaction-ingest-desk.json"));
assert!(resources.contains_key("../../config/composite.ksp-app-store-desk.json")); assert!(resources.contains_key("../../config/composite.ksp-app-store-desk.json"));
assert!(resources.contains_key("../../config/std.store.json")); assert!(resources.contains_key("../../config/std.store.json"));
assert!(resources.contains_key("../../config/schemas/std.store.schema.json")); assert!(resources.contains_key("../../config/schemas/std.store.schema.json"));

View File

@@ -53,6 +53,7 @@
], ],
"resources": { "resources": {
"../../config/composite.ksp-app-backfill-desk.json": "config/composite.ksp-app-backfill-desk.json", "../../config/composite.ksp-app-backfill-desk.json": "config/composite.ksp-app-backfill-desk.json",
"../../config/composite.ksp-app-raw-transaction-ingest-desk.json": "config/composite.ksp-app-raw-transaction-ingest-desk.json",
"../../config/composite.ksp-app-solprices-desk.json": "config/composite.ksp-app-solprices-desk.json", "../../config/composite.ksp-app-solprices-desk.json": "config/composite.ksp-app-solprices-desk.json",
"../../config/composite.ksp-app-store-desk.json": "config/composite.ksp-app-store-desk.json", "../../config/composite.ksp-app-store-desk.json": "config/composite.ksp-app-store-desk.json",
"../../config/composite.ksp-app-wallet-desk.json": "config/composite.ksp-app-wallet-desk.json", "../../config/composite.ksp-app-wallet-desk.json": "config/composite.ksp-app-wallet-desk.json",

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-wallet-desk/tests/desktop_contract.rs // file: crates/ksp-app-wallet-desk/tests/desktop_contract.rs
// version: 33 // version: 34
//! Desktop build, shell and Config-status contract audits for Wallet Desk. //! Desktop build, shell and Config-status contract audits for Wallet Desk.
@@ -432,11 +432,15 @@ fn pre_018_packaged_runtime_bundles_config_resources_and_keeps_wallet_desk_versi
let resources = tauri.pointer("/bundle/resources").and_then(serde_json::Value::as_object); let resources = tauri.pointer("/bundle/resources").and_then(serde_json::Value::as_object);
assert!(resources.is_some(), "packaged Wallet Desk Config resources map must exist"); assert!(resources.is_some(), "packaged Wallet Desk Config resources map must exist");
if let std::option::Option::Some(resources) = resources { if let std::option::Option::Some(resources) = resources {
assert_eq!(resources.len(), 15); assert_eq!(resources.len(), 16);
assert_eq!( assert_eq!(
resources.get("../../config/composite.ksp-app-backfill-desk.json").and_then(serde_json::Value::as_str), resources.get("../../config/composite.ksp-app-backfill-desk.json").and_then(serde_json::Value::as_str),
std::option::Option::Some("config/composite.ksp-app-backfill-desk.json"), std::option::Option::Some("config/composite.ksp-app-backfill-desk.json"),
); );
assert_eq!(
resources.get("../../config/composite.ksp-app-raw-transaction-ingest-desk.json").and_then(serde_json::Value::as_str),
std::option::Option::Some("config/composite.ksp-app-raw-transaction-ingest-desk.json"),
);
assert_eq!( assert_eq!(
resources.get("../../config/composite.ksp-app-solprices-desk.json").and_then(serde_json::Value::as_str), resources.get("../../config/composite.ksp-app-solprices-desk.json").and_then(serde_json::Value::as_str),
std::option::Option::Some("config/composite.ksp-app-solprices-desk.json"), std::option::Option::Some("config/composite.ksp-app-solprices-desk.json"),

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-wallet-desk/tests/release_compliance.rs // file: crates/ksp-app-wallet-desk/tests/release_compliance.rs
// version: 12 // version: 13
//! Release-wide deterministic compliance canaries for Wallet Desk. //! Release-wide deterministic compliance canaries for Wallet Desk.
@@ -199,7 +199,7 @@ fn packaged_resources_include_only_registered_config_sources_and_schemas() {
std::option::Option::Some(value) => value, std::option::Option::Some(value) => value,
std::option::Option::None => return, std::option::Option::None => return,
}; };
assert_eq!(resources.len(), 15); assert_eq!(resources.len(), 16);
for (source, destination) in resources { for (source, destination) in resources {
let destination = destination.as_str(); let destination = destination.as_str();
assert!(destination.is_some(), "resource destination must be textual"); assert!(destination.is_some(), "resource destination must be textual");

View File

@@ -1,5 +1,5 @@
<!-- file: crates/ksp-config-lib/README.md --> <!-- file: crates/ksp-config-lib/README.md -->
<!-- version: 12 --> <!-- version: 13 -->
# ksp-config-lib # ksp-config-lib
@@ -35,8 +35,9 @@ La crate centralise les documents JSON, leurs schemas, les profils et compositio
Le registre par défaut connaît : Le registre par défaut connaît :
```text ```text
cfg.composite.ksp-app-backfill-desk -> config/composite.ksp-app-backfill-desk.json cfg.composite.ksp-app-backfill-desk -> config/composite.ksp-app-backfill-desk.json
cfg.composite.ksp-app-solprices-desk -> config/composite.ksp-app-solprices-desk.json cfg.composite.ksp-app-raw-transaction-ingest-desk -> config/composite.ksp-app-raw-transaction-ingest-desk.json
cfg.composite.ksp-app-solprices-desk -> config/composite.ksp-app-solprices-desk.json
cfg.composite.ksp-app-store-desk -> config/composite.ksp-app-store-desk.json cfg.composite.ksp-app-store-desk -> config/composite.ksp-app-store-desk.json
cfg.composite.ksp-app-wallet-desk -> config/composite.ksp-app-wallet-desk.json cfg.composite.ksp-app-wallet-desk -> config/composite.ksp-app-wallet-desk.json
cfg.std.logging -> config/std.logging.json cfg.std.logging -> config/std.logging.json
@@ -103,6 +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.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/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-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-solprices-desk.json`](../../config/composite.ksp-app-solprices-desk.json) — composition Logging/Off-chain Transport de SOL Prices Desk ; - [`../../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-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 ; - [`../../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 ;

View File

@@ -1,5 +1,5 @@
<!-- file: crates/ksp-config-lib/USAGE.md --> <!-- file: crates/ksp-config-lib/USAGE.md -->
<!-- version: 15 --> <!-- version: 16 -->
# Utilisation de ksp-config-lib # Utilisation de ksp-config-lib
@@ -28,6 +28,7 @@ Les arguments compris par Config sont :
```text ```text
--cfgpath=/path/to/config --cfgpath=/path/to/config
--schemapath=/path/to/schemas --schemapath=/path/to/schemas
--filemap=cfg.composite.ksp-app-raw-transaction-ingest-desk=my-raw-ingest-desk.json
--filemap=cfg.composite.ksp-app-solprices-desk=my-solprices-desk.json --filemap=cfg.composite.ksp-app-solprices-desk=my-solprices-desk.json
--filemap=cfg.composite.ksp-app-wallet-desk=my-wallet-desk.json --filemap=cfg.composite.ksp-app-wallet-desk=my-wallet-desk.json
--filemap=cfg.std.logging=my-logging.json --filemap=cfg.std.logging=my-logging.json
@@ -161,7 +162,9 @@ Le provider affiche `solana-yellowstone-grpc.publicnode.com:443` ; le profil ver
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. 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"`. 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 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 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. 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.
@@ -289,7 +292,7 @@ let component = match composite.component("wallet") {
let wallet = engine.resolve_wallet_config_profile(component.resolved(), &environment); let wallet = engine.resolve_wallet_config_profile(component.resolved(), &environment);
``` ```
La même forme existe pour Logging via `resolve_logging_config_profile`. Le composite `cfg.composite.ksp-app-solprices-desk` référence `logging` et `offchain_transport`. Le composite `cfg.composite.ksp-app-wallet-desk` référence `logging`, `offchain_transport`, `transport` et `wallet`; chaque application valide ses frontières de composition au bootstrap. La même forme existe pour Logging via `resolve_logging_config_profile`. Le composite `cfg.composite.ksp-app-raw-transaction-ingest-desk` référence exactement `logging`, `transport` et `store`; ses profils couplent explicitement un profil Transport et un profil Store du même réseau logique (`devnet`, `mainnet` ou `testnet`) afin que l'application n'assemble jamais arbitrairement deux réseaux. Le composite `cfg.composite.ksp-app-solprices-desk` référence `logging` et `offchain_transport`. Le composite `cfg.composite.ksp-app-wallet-desk` référence `logging`, `offchain_transport`, `transport` et `wallet`; chaque application valide ses frontières de composition au bootstrap.
## 6. Management de `std.logging.json` ## 6. Management de `std.logging.json`

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-config-lib/src/lib.rs // file: crates/ksp-config-lib/src/lib.rs
// version: 23 // version: 24
#![warn(missing_docs)] #![warn(missing_docs)]
#![deny(unreachable_pub)] #![deny(unreachable_pub)]
@@ -153,6 +153,8 @@ pub use self::registry::ConfigFileKind;
pub use self::registry::ConfigFileRegistry; pub use self::registry::ConfigFileRegistry;
/// Default physical filename for the Backfill Desk composite configuration document. /// Default physical filename for the Backfill Desk composite configuration document.
pub use self::registry::DEFAULT_COMPOSITE_KSP_APP_BACKFILL_DESK_FILENAME; pub use self::registry::DEFAULT_COMPOSITE_KSP_APP_BACKFILL_DESK_FILENAME;
/// Default physical filename for the Raw Transaction Ingest Desk composite configuration document.
pub use self::registry::DEFAULT_COMPOSITE_KSP_APP_RAW_TRANSACTION_INGEST_DESK_FILENAME;
/// Default physical filename for the SOL Prices Desk composite configuration document. /// Default physical filename for the SOL Prices Desk composite configuration document.
pub use self::registry::DEFAULT_COMPOSITE_KSP_APP_SOLPRICES_DESK_FILENAME; pub use self::registry::DEFAULT_COMPOSITE_KSP_APP_SOLPRICES_DESK_FILENAME;
/// Default physical filename for the Store Desk composite configuration document. /// Default physical filename for the Store Desk composite configuration document.
@@ -183,6 +185,8 @@ pub use self::registry::DEFAULT_STD_WALLET_FILENAME;
pub use self::registry::DEFAULT_STD_WALLET_SCHEMA_FILENAME; pub use self::registry::DEFAULT_STD_WALLET_SCHEMA_FILENAME;
/// Logical file identifier for the Backfill Desk composite configuration document. /// Logical file identifier for the Backfill Desk composite configuration document.
pub use self::registry::FILE_ID_COMPOSITE_KSP_APP_BACKFILL_DESK; pub use self::registry::FILE_ID_COMPOSITE_KSP_APP_BACKFILL_DESK;
/// Logical file identifier for the Raw Transaction Ingest Desk composite configuration document.
pub use self::registry::FILE_ID_COMPOSITE_KSP_APP_RAW_TRANSACTION_INGEST_DESK;
/// Logical file identifier for the SOL Prices Desk composite configuration document. /// Logical file identifier for the SOL Prices Desk composite configuration document.
pub use self::registry::FILE_ID_COMPOSITE_KSP_APP_SOLPRICES_DESK; pub use self::registry::FILE_ID_COMPOSITE_KSP_APP_SOLPRICES_DESK;
/// Logical file identifier for the Store Desk composite configuration document. /// Logical file identifier for the Store Desk composite configuration document.

View File

@@ -1,10 +1,12 @@
// file: crates/ksp-config-lib/src/registry.rs // file: crates/ksp-config-lib/src/registry.rs
// version: 15 // version: 16
/// Bootstrap argument used to replace a known Config filename mapping. /// Bootstrap argument used to replace a known Config filename mapping.
pub const ARG_FILE_MAP: &str = "--filemap"; pub const ARG_FILE_MAP: &str = "--filemap";
/// Default physical filename for the Backfill Desk composite configuration document. /// Default physical filename for the Backfill Desk composite configuration document.
pub const DEFAULT_COMPOSITE_KSP_APP_BACKFILL_DESK_FILENAME: &str = "composite.ksp-app-backfill-desk.json"; pub const DEFAULT_COMPOSITE_KSP_APP_BACKFILL_DESK_FILENAME: &str = "composite.ksp-app-backfill-desk.json";
/// Default physical filename for the Raw Transaction Ingest Desk composite configuration document.
pub const DEFAULT_COMPOSITE_KSP_APP_RAW_TRANSACTION_INGEST_DESK_FILENAME: &str = "composite.ksp-app-raw-transaction-ingest-desk.json";
/// Default physical filename for the SOL Prices Desk composite configuration document. /// Default physical filename for the SOL Prices Desk composite configuration document.
pub const DEFAULT_COMPOSITE_KSP_APP_SOLPRICES_DESK_FILENAME: &str = "composite.ksp-app-solprices-desk.json"; pub const DEFAULT_COMPOSITE_KSP_APP_SOLPRICES_DESK_FILENAME: &str = "composite.ksp-app-solprices-desk.json";
/// Default physical filename for the Store Desk composite configuration document. /// Default physical filename for the Store Desk composite configuration document.
@@ -35,6 +37,8 @@ pub const DEFAULT_STD_WALLET_FILENAME: &str = "std.wallet.json";
pub const DEFAULT_STD_WALLET_SCHEMA_FILENAME: &str = "std.wallet.schema.json"; pub const DEFAULT_STD_WALLET_SCHEMA_FILENAME: &str = "std.wallet.schema.json";
/// Logical file identifier for the Backfill Desk composite configuration document. /// Logical file identifier for the Backfill Desk composite configuration document.
pub const FILE_ID_COMPOSITE_KSP_APP_BACKFILL_DESK: &str = "cfg.composite.ksp-app-backfill-desk"; pub const FILE_ID_COMPOSITE_KSP_APP_BACKFILL_DESK: &str = "cfg.composite.ksp-app-backfill-desk";
/// Logical file identifier for the Raw Transaction Ingest Desk composite configuration document.
pub const FILE_ID_COMPOSITE_KSP_APP_RAW_TRANSACTION_INGEST_DESK: &str = "cfg.composite.ksp-app-raw-transaction-ingest-desk";
/// Logical file identifier for the SOL Prices Desk composite configuration document. /// Logical file identifier for the SOL Prices Desk composite configuration document.
pub const FILE_ID_COMPOSITE_KSP_APP_SOLPRICES_DESK: &str = "cfg.composite.ksp-app-solprices-desk"; pub const FILE_ID_COMPOSITE_KSP_APP_SOLPRICES_DESK: &str = "cfg.composite.ksp-app-solprices-desk";
/// Logical file identifier for the Store Desk composite configuration document. /// Logical file identifier for the Store Desk composite configuration document.
@@ -178,6 +182,16 @@ impl ConfigFileRegistry {
std::result::Result::Ok(value) => value, std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error), std::result::Result::Err(error) => return std::result::Result::Err(error),
}; };
let raw_transaction_ingest_composite = ConfigFileDescriptor::new(
FILE_ID_COMPOSITE_KSP_APP_RAW_TRANSACTION_INGEST_DESK,
ConfigFileKind::Config,
DEFAULT_COMPOSITE_KSP_APP_RAW_TRANSACTION_INGEST_DESK_FILENAME,
std::option::Option::Some(FILE_ID_SCHEMA_COMPOSITE),
);
let raw_transaction_ingest_composite = match raw_transaction_ingest_composite {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let solprices_composite = ConfigFileDescriptor::new( let solprices_composite = ConfigFileDescriptor::new(
FILE_ID_COMPOSITE_KSP_APP_SOLPRICES_DESK, FILE_ID_COMPOSITE_KSP_APP_SOLPRICES_DESK,
ConfigFileKind::Config, ConfigFileKind::Config,
@@ -300,6 +314,7 @@ impl ConfigFileRegistry {
}; };
return crate::build_registry([ return crate::build_registry([
backfill_composite, backfill_composite,
raw_transaction_ingest_composite,
solprices_composite, solprices_composite,
store_desk_composite, store_desk_composite,
wallet_composite, wallet_composite,

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-config-lib/src/transport.rs // file: crates/ksp-config-lib/src/transport.rs
// version: 5 // version: 6
/// Effective standard on-chain Transport configuration resolved from Config and mapped to HTTP plus optional WebSocket and Yellowstone gRPC runtime contracts. /// Effective standard on-chain Transport configuration resolved from Config and mapped to HTTP plus optional WebSocket and Yellowstone gRPC runtime contracts.
#[derive(Clone, Eq, PartialEq)] #[derive(Clone, Eq, PartialEq)]
@@ -259,6 +259,8 @@ struct EffectiveWsEndpointSource {
kind: String, kind: String,
url: String, url: String,
#[serde(default)] #[serde(default)]
capabilities: std::option::Option<std::vec::Vec<String>>,
#[serde(default)]
session: std::option::Option<EffectiveWsSessionOverrideSource>, session: std::option::Option<EffectiveWsSessionOverrideSource>,
} }
@@ -812,7 +814,7 @@ fn map_ws_endpoints(
std::result::Result::Ok(value) => value, std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error), std::result::Result::Err(error) => return std::result::Result::Err(error),
}; };
endpoints.push(ksp_onchain_transport_lib::WsEndpointSettings::new( let mut endpoint = ksp_onchain_transport_lib::WsEndpointSettings::new(
source.name, source.name,
source.enabled, source.enabled,
ksp_onchain_transport_lib::WsProviderName::new(source.provider), ksp_onchain_transport_lib::WsProviderName::new(source.provider),
@@ -820,7 +822,20 @@ fn map_ws_endpoints(
protocol, protocol,
url, url,
session, session,
)); );
if let std::option::Option::Some(capability_sources) = source.capabilities {
let mut capabilities = std::vec::Vec::<ksp_onchain_transport_lib::WsSubscriptionKind>::with_capacity(capability_sources.len());
for capability_source in capability_sources {
let capability = map_ws_subscription_capability(capability_source.as_str(), profile, endpoint_name.as_str());
let capability = match capability {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
capabilities.push(capability);
}
endpoint = endpoint.with_subscription_capabilities(capabilities);
}
endpoints.push(endpoint);
} }
return std::result::Result::Ok(endpoints); return std::result::Result::Ok(endpoints);
} }
@@ -841,6 +856,30 @@ fn map_ws_protocol_kind(
}; };
} }
fn map_ws_subscription_capability(
value: &str,
profile: &crate::ResolvedConfigProfile,
endpoint_name: &str,
) -> ksp_core_lib::Result<ksp_onchain_transport_lib::WsSubscriptionKind> {
return match value {
"account" => std::result::Result::Ok(ksp_onchain_transport_lib::WsSubscriptionKind::Account),
"block" => std::result::Result::Ok(ksp_onchain_transport_lib::WsSubscriptionKind::Block),
"logs" => std::result::Result::Ok(ksp_onchain_transport_lib::WsSubscriptionKind::Logs),
"program" => std::result::Result::Ok(ksp_onchain_transport_lib::WsSubscriptionKind::Program),
"root" => std::result::Result::Ok(ksp_onchain_transport_lib::WsSubscriptionKind::Root),
"signature" => std::result::Result::Ok(ksp_onchain_transport_lib::WsSubscriptionKind::Signature),
"slot" => std::result::Result::Ok(ksp_onchain_transport_lib::WsSubscriptionKind::Slot),
"slots_updates" => std::result::Result::Ok(ksp_onchain_transport_lib::WsSubscriptionKind::SlotsUpdates),
"vote" => std::result::Result::Ok(ksp_onchain_transport_lib::WsSubscriptionKind::Vote),
"helius_transaction" => std::result::Result::Ok(ksp_onchain_transport_lib::WsSubscriptionKind::HeliusTransaction),
_ => std::result::Result::Err(
effective_error(profile, "effective WebSocket subscription capability is unsupported")
.with_context("endpoint_name", endpoint_name)
.with_context("ws_capability", value),
),
};
}
fn map_ws_session_settings( fn map_ws_session_settings(
defaults: &EffectiveWsSessionSource, defaults: &EffectiveWsSessionSource,
overrides: std::option::Option<&EffectiveWsSessionOverrideSource>, overrides: std::option::Option<&EffectiveWsSessionOverrideSource>,

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-config-lib/tests/ownership.rs // file: crates/ksp-config-lib/tests/ownership.rs
// version: 9 // version: 10
//! Workspace ownership audits for KSP application configuration boundaries. //! Workspace ownership audits for KSP application configuration boundaries.
@@ -262,6 +262,7 @@ fn workspace_crates_do_not_hardcode_config_managed_physical_files() {
}; };
for token in [ for token in [
"\".env\"", "\".env\"",
"\"composite.ksp-app-raw-transaction-ingest-desk.json\"",
"\"composite.ksp-app-solprices-desk.json\"", "\"composite.ksp-app-solprices-desk.json\"",
"\"composite.ksp-app-wallet-desk.json\"", "\"composite.ksp-app-wallet-desk.json\"",
"\"composite.schema.json\"", "\"composite.schema.json\"",

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-config-lib/tests/public_api.rs // file: crates/ksp-config-lib/tests/public_api.rs
// version: 28 // version: 29
//! Integration tests for the public `ksp-config-lib` bootstrap, registry, JSON/profile/composite, environment-resolution, sensitivity, //! Integration tests for the public `ksp-config-lib` bootstrap, registry, JSON/profile/composite, environment-resolution, sensitivity,
//! Logging/Transport/Store adapters and management contracts. //! Logging/Transport/Store adapters and management contracts.
@@ -80,23 +80,24 @@ fn registry_descriptor_inventory_is_available_from_crate_root() {
assert!(registry.is_ok(), "public registry should remain constructible: {registry:?}"); assert!(registry.is_ok(), "public registry should remain constructible: {registry:?}");
if let std::result::Result::Ok(registry) = registry { if let std::result::Result::Ok(registry) = registry {
let descriptors: std::vec::Vec<&ksp_config_lib::ConfigFileDescriptor> = registry.descriptors().collect(); let descriptors: std::vec::Vec<&ksp_config_lib::ConfigFileDescriptor> = registry.descriptors().collect();
assert_eq!(descriptors.len(), 15); assert_eq!(descriptors.len(), 16);
assert_eq!(descriptors[0].file_id().as_str(), ksp_config_lib::FILE_ID_COMPOSITE_KSP_APP_BACKFILL_DESK); assert_eq!(descriptors[0].file_id().as_str(), ksp_config_lib::FILE_ID_COMPOSITE_KSP_APP_BACKFILL_DESK);
assert_eq!(descriptors[1].file_id().as_str(), ksp_config_lib::FILE_ID_COMPOSITE_KSP_APP_SOLPRICES_DESK); assert_eq!(descriptors[1].file_id().as_str(), ksp_config_lib::FILE_ID_COMPOSITE_KSP_APP_RAW_TRANSACTION_INGEST_DESK);
assert_eq!(descriptors[2].file_id().as_str(), ksp_config_lib::FILE_ID_COMPOSITE_KSP_APP_STORE_DESK); assert_eq!(descriptors[2].file_id().as_str(), ksp_config_lib::FILE_ID_COMPOSITE_KSP_APP_SOLPRICES_DESK);
assert_eq!(descriptors[3].file_id().as_str(), ksp_config_lib::FILE_ID_COMPOSITE_KSP_APP_WALLET_DESK); assert_eq!(descriptors[3].file_id().as_str(), ksp_config_lib::FILE_ID_COMPOSITE_KSP_APP_STORE_DESK);
assert_eq!(descriptors[4].file_id().as_str(), ksp_config_lib::FILE_ID_STD_LOGGING); assert_eq!(descriptors[4].file_id().as_str(), ksp_config_lib::FILE_ID_COMPOSITE_KSP_APP_WALLET_DESK);
assert_eq!(descriptors[5].file_id().as_str(), ksp_config_lib::FILE_ID_STD_OFFCHAIN_TRANSPORT); assert_eq!(descriptors[5].file_id().as_str(), ksp_config_lib::FILE_ID_STD_LOGGING);
assert_eq!(descriptors[6].file_id().as_str(), ksp_config_lib::FILE_ID_STD_STORE); assert_eq!(descriptors[6].file_id().as_str(), ksp_config_lib::FILE_ID_STD_OFFCHAIN_TRANSPORT);
assert_eq!(descriptors[7].file_id().as_str(), ksp_config_lib::FILE_ID_STD_TRANSPORT); assert_eq!(descriptors[7].file_id().as_str(), ksp_config_lib::FILE_ID_STD_STORE);
assert_eq!(descriptors[8].file_id().as_str(), ksp_config_lib::FILE_ID_STD_WALLET); assert_eq!(descriptors[8].file_id().as_str(), ksp_config_lib::FILE_ID_STD_TRANSPORT);
assert_eq!(descriptors[9].file_id().as_str(), ksp_config_lib::FILE_ID_SCHEMA_COMPOSITE); assert_eq!(descriptors[9].file_id().as_str(), ksp_config_lib::FILE_ID_STD_WALLET);
assert_eq!(descriptors[10].file_id().as_str(), ksp_config_lib::FILE_ID_SCHEMA_STD_LOGGING); assert_eq!(descriptors[10].file_id().as_str(), ksp_config_lib::FILE_ID_SCHEMA_COMPOSITE);
assert_eq!(descriptors[11].file_id().as_str(), ksp_config_lib::FILE_ID_SCHEMA_STD_OFFCHAIN_TRANSPORT); assert_eq!(descriptors[11].file_id().as_str(), ksp_config_lib::FILE_ID_SCHEMA_STD_LOGGING);
assert_eq!(descriptors[12].file_id().as_str(), ksp_config_lib::FILE_ID_SCHEMA_STD_STORE); assert_eq!(descriptors[12].file_id().as_str(), ksp_config_lib::FILE_ID_SCHEMA_STD_OFFCHAIN_TRANSPORT);
assert_eq!(descriptors[13].file_id().as_str(), ksp_config_lib::FILE_ID_SCHEMA_STD_TRANSPORT); assert_eq!(descriptors[13].file_id().as_str(), ksp_config_lib::FILE_ID_SCHEMA_STD_STORE);
assert_eq!(descriptors[14].file_id().as_str(), ksp_config_lib::FILE_ID_SCHEMA_STD_WALLET); assert_eq!(descriptors[14].file_id().as_str(), ksp_config_lib::FILE_ID_SCHEMA_STD_TRANSPORT);
let schema_file_id = descriptors[8].schema_file_id(); assert_eq!(descriptors[15].file_id().as_str(), ksp_config_lib::FILE_ID_SCHEMA_STD_WALLET);
let schema_file_id = descriptors[9].schema_file_id();
assert!(schema_file_id.is_some(), "public Wallet descriptor should preserve schema association"); assert!(schema_file_id.is_some(), "public Wallet descriptor should preserve schema association");
if let std::option::Option::Some(schema_file_id) = schema_file_id { if let std::option::Option::Some(schema_file_id) = schema_file_id {
assert_eq!(schema_file_id.as_str(), ksp_config_lib::FILE_ID_SCHEMA_STD_WALLET); assert_eq!(schema_file_id.as_str(), ksp_config_lib::FILE_ID_SCHEMA_STD_WALLET);
@@ -167,6 +168,8 @@ fn composite_schema_and_provenance_contracts_are_available_from_crate_root() {
} }
assert_eq!(ksp_config_lib::FILE_ID_COMPOSITE_KSP_APP_BACKFILL_DESK, "cfg.composite.ksp-app-backfill-desk"); assert_eq!(ksp_config_lib::FILE_ID_COMPOSITE_KSP_APP_BACKFILL_DESK, "cfg.composite.ksp-app-backfill-desk");
assert_eq!(ksp_config_lib::DEFAULT_COMPOSITE_KSP_APP_BACKFILL_DESK_FILENAME, "composite.ksp-app-backfill-desk.json"); assert_eq!(ksp_config_lib::DEFAULT_COMPOSITE_KSP_APP_BACKFILL_DESK_FILENAME, "composite.ksp-app-backfill-desk.json");
assert_eq!(ksp_config_lib::FILE_ID_COMPOSITE_KSP_APP_RAW_TRANSACTION_INGEST_DESK, "cfg.composite.ksp-app-raw-transaction-ingest-desk");
assert_eq!(ksp_config_lib::DEFAULT_COMPOSITE_KSP_APP_RAW_TRANSACTION_INGEST_DESK_FILENAME, "composite.ksp-app-raw-transaction-ingest-desk.json");
assert_eq!(ksp_config_lib::FILE_ID_COMPOSITE_KSP_APP_SOLPRICES_DESK, "cfg.composite.ksp-app-solprices-desk"); assert_eq!(ksp_config_lib::FILE_ID_COMPOSITE_KSP_APP_SOLPRICES_DESK, "cfg.composite.ksp-app-solprices-desk");
assert_eq!(ksp_config_lib::DEFAULT_COMPOSITE_KSP_APP_SOLPRICES_DESK_FILENAME, "composite.ksp-app-solprices-desk.json"); assert_eq!(ksp_config_lib::DEFAULT_COMPOSITE_KSP_APP_SOLPRICES_DESK_FILENAME, "composite.ksp-app-solprices-desk.json");
assert_eq!(ksp_config_lib::FILE_ID_COMPOSITE_KSP_APP_STORE_DESK, "cfg.composite.ksp-app-store-desk"); assert_eq!(ksp_config_lib::FILE_ID_COMPOSITE_KSP_APP_STORE_DESK, "cfg.composite.ksp-app-store-desk");

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-config-lib/unit_tests/composite.rs // file: crates/ksp-config-lib/unit_tests/composite.rs
// version: 4 // version: 5
const TEST_COMPOSITE_FILENAME: &str = "examples/composite.example.json"; const TEST_COMPOSITE_FILENAME: &str = "examples/composite.example.json";
const TEST_COMPOSITE_FILE_ID: &str = "cfg.composite.test"; const TEST_COMPOSITE_FILE_ID: &str = "cfg.composite.test";
@@ -128,6 +128,71 @@ fn duplicate_component_ids_are_rejected_inside_one_composite_profile() {
cleanup_fixture(&fixture); cleanup_fixture(&fixture);
} }
#[test]
fn v0_3_15_pre_003_raw_ingest_composite_keeps_transport_and_store_networks_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:?}");
let registry = crate::ConfigFileRegistry::defaults();
assert!(registry.is_ok(), "default Config registry should be valid: {registry:?}");
let file_id = crate::ConfigFileId::new(crate::FILE_ID_COMPOSITE_KSP_APP_RAW_TRANSACTION_INGEST_DESK);
assert!(file_id.is_ok(), "Raw Transaction Ingest Desk composite file_id should be valid: {file_id:?}");
let (bootstrap, registry, file_id) = match (bootstrap, registry, file_id) {
(std::result::Result::Ok(bootstrap), std::result::Result::Ok(registry), std::result::Result::Ok(file_id)) => (bootstrap, registry, file_id),
_ => return,
};
let engine = crate::ConfigDocumentEngine::new(bootstrap, registry);
let mut process = std::collections::BTreeMap::<String, String>::new();
process.insert("KSP_SECRET_HELIUS_API_KEY".to_owned(), "helius-composite-canary".to_owned());
process.insert("KSP_SECRET_ORBITFLARE_DEVNET_GRPC_X_TOKEN".to_owned(), "orbitflare-composite-canary".to_owned());
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"),
] {
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:?}");
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,
};
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::option::Option::Some(grpc) = transport.grpc_settings() {
assert!(grpc.endpoints().iter().all(|endpoint| return endpoint.cluster().as_str() == expected_network));
}
}
}
fn fixture_example_engine() -> ksp_core_lib::Result<crate::ConfigDocumentEngine> { fn fixture_example_engine() -> ksp_core_lib::Result<crate::ConfigDocumentEngine> {
let workspace = workspace_root(); let workspace = workspace_root();
let fixture_root = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("unit_tests/fixtures"); let fixture_root = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("unit_tests/fixtures");

View File

@@ -0,0 +1,146 @@
{
"format_version": 1,
"default_profile": "devnet_public",
"profiles": [
{
"profile_id": "devnet_public",
"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": "devnet"
}
]
},
{
"profile_id": "helius_devnet",
"documents": [
{
"component_id": "logging",
"file_id": "cfg.std.logging",
"profile_id": "local_dev"
},
{
"component_id": "transport",
"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",
"file_id": "cfg.std.transport",
"profile_id": "secret_test"
},
{
"component_id": "store",
"file_id": "cfg.std.store",
"profile_id": "devnet"
}
]
},
{
"profile_id": "mainnet_public",
"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": "helius_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_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",
"file_id": "cfg.std.transport",
"profile_id": "secret_test"
},
{
"component_id": "store",
"file_id": "cfg.std.store",
"profile_id": "testnet"
}
]
}
]
}

View File

@@ -70,7 +70,15 @@
"resubscribe": "never", "resubscribe": "never",
"notification_queue_capacity": 48, "notification_queue_capacity": 48,
"max_pending_requests": 24 "max_pending_requests": 24
} },
"capabilities": [
"account",
"logs",
"program",
"root",
"signature",
"slot"
]
}, },
{ {
"name": "fixture_helius_ws", "name": "fixture_helius_ws",
@@ -78,7 +86,17 @@
"provider": "helius", "provider": "helius",
"cluster": "mainnet", "cluster": "mainnet",
"kind": "helius_laserstream", "kind": "helius_laserstream",
"url": "wss://mainnet.helius-rpc.com/?api-key=${KSP_SECRET_HELIUS_API_KEY:-fixture-helius-key}" "url": "wss://mainnet.helius-rpc.com/?api-key=${KSP_SECRET_HELIUS_API_KEY:-fixture-helius-key}",
"capabilities": [
"account",
"logs",
"program",
"root",
"signature",
"slot",
"slots_updates",
"helius_transaction"
]
} }
] ]
}, },
@@ -119,7 +137,17 @@
"provider": "helius", "provider": "helius",
"cluster": "devnet", "cluster": "devnet",
"kind": "helius_laserstream", "kind": "helius_laserstream",
"url": "wss://devnet.helius-rpc.com/?api-key=${KSP_SECRET_HELIUS_API_KEY:-fixture-helius-key}" "url": "wss://devnet.helius-rpc.com/?api-key=${KSP_SECRET_HELIUS_API_KEY:-fixture-helius-key}",
"capabilities": [
"account",
"logs",
"program",
"root",
"signature",
"slot",
"slots_updates",
"helius_transaction"
]
} }
] ]
} }

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-config-lib/unit_tests/registry.rs // file: crates/ksp-config-lib/unit_tests/registry.rs
// version: 13 // version: 14
#[test] #[test]
fn descriptors_expose_complete_registry_in_deterministic_file_id_order() { fn descriptors_expose_complete_registry_in_deterministic_file_id_order() {
@@ -7,30 +7,33 @@ fn descriptors_expose_complete_registry_in_deterministic_file_id_order() {
assert!(registry.is_ok(), "default registry should be valid: {registry:?}"); assert!(registry.is_ok(), "default registry should be valid: {registry:?}");
if let std::result::Result::Ok(registry) = registry { if let std::result::Result::Ok(registry) = registry {
let descriptors: std::vec::Vec<&crate::ConfigFileDescriptor> = registry.descriptors().collect(); let descriptors: std::vec::Vec<&crate::ConfigFileDescriptor> = registry.descriptors().collect();
assert_eq!(descriptors.len(), 15); assert_eq!(descriptors.len(), 16);
assert_eq!(descriptors[0].file_id().as_str(), crate::FILE_ID_COMPOSITE_KSP_APP_BACKFILL_DESK); assert_eq!(descriptors[0].file_id().as_str(), crate::FILE_ID_COMPOSITE_KSP_APP_BACKFILL_DESK);
assert_eq!(descriptors[0].filename(), std::path::Path::new(crate::DEFAULT_COMPOSITE_KSP_APP_BACKFILL_DESK_FILENAME)); assert_eq!(descriptors[0].filename(), std::path::Path::new(crate::DEFAULT_COMPOSITE_KSP_APP_BACKFILL_DESK_FILENAME));
assert_eq!(descriptors[0].schema_file_id().map(crate::ConfigFileId::as_str), std::option::Option::Some(crate::FILE_ID_SCHEMA_COMPOSITE)); assert_eq!(descriptors[0].schema_file_id().map(crate::ConfigFileId::as_str), std::option::Option::Some(crate::FILE_ID_SCHEMA_COMPOSITE));
assert_eq!(descriptors[1].file_id().as_str(), crate::FILE_ID_COMPOSITE_KSP_APP_SOLPRICES_DESK); assert_eq!(descriptors[1].file_id().as_str(), crate::FILE_ID_COMPOSITE_KSP_APP_RAW_TRANSACTION_INGEST_DESK);
assert_eq!(descriptors[2].file_id().as_str(), crate::FILE_ID_COMPOSITE_KSP_APP_STORE_DESK); assert_eq!(descriptors[1].filename(), std::path::Path::new(crate::DEFAULT_COMPOSITE_KSP_APP_RAW_TRANSACTION_INGEST_DESK_FILENAME));
assert_eq!(descriptors[2].filename(), std::path::Path::new(crate::DEFAULT_COMPOSITE_KSP_APP_STORE_DESK_FILENAME)); assert_eq!(descriptors[1].schema_file_id().map(crate::ConfigFileId::as_str), std::option::Option::Some(crate::FILE_ID_SCHEMA_COMPOSITE));
assert_eq!(descriptors[2].schema_file_id().map(crate::ConfigFileId::as_str), std::option::Option::Some(crate::FILE_ID_SCHEMA_COMPOSITE)); assert_eq!(descriptors[2].file_id().as_str(), crate::FILE_ID_COMPOSITE_KSP_APP_SOLPRICES_DESK);
assert_eq!(descriptors[3].file_id().as_str(), crate::FILE_ID_COMPOSITE_KSP_APP_WALLET_DESK); assert_eq!(descriptors[3].file_id().as_str(), crate::FILE_ID_COMPOSITE_KSP_APP_STORE_DESK);
assert_eq!(descriptors[4].file_id().as_str(), crate::FILE_ID_STD_LOGGING); assert_eq!(descriptors[3].filename(), std::path::Path::new(crate::DEFAULT_COMPOSITE_KSP_APP_STORE_DESK_FILENAME));
assert_eq!(descriptors[5].file_id().as_str(), crate::FILE_ID_STD_OFFCHAIN_TRANSPORT); assert_eq!(descriptors[3].schema_file_id().map(crate::ConfigFileId::as_str), std::option::Option::Some(crate::FILE_ID_SCHEMA_COMPOSITE));
assert_eq!(descriptors[6].file_id().as_str(), crate::FILE_ID_STD_STORE); assert_eq!(descriptors[4].file_id().as_str(), crate::FILE_ID_COMPOSITE_KSP_APP_WALLET_DESK);
assert_eq!(descriptors[7].file_id().as_str(), crate::FILE_ID_STD_TRANSPORT); assert_eq!(descriptors[5].file_id().as_str(), crate::FILE_ID_STD_LOGGING);
assert_eq!(descriptors[8].file_id().as_str(), crate::FILE_ID_STD_WALLET); assert_eq!(descriptors[6].file_id().as_str(), crate::FILE_ID_STD_OFFCHAIN_TRANSPORT);
assert_eq!(descriptors[8].filename(), std::path::Path::new(crate::DEFAULT_STD_WALLET_FILENAME)); assert_eq!(descriptors[7].file_id().as_str(), crate::FILE_ID_STD_STORE);
assert_eq!(descriptors[8].schema_file_id().map(crate::ConfigFileId::as_str), std::option::Option::Some(crate::FILE_ID_SCHEMA_STD_WALLET)); assert_eq!(descriptors[8].file_id().as_str(), crate::FILE_ID_STD_TRANSPORT);
assert_eq!(descriptors[9].file_id().as_str(), crate::FILE_ID_SCHEMA_COMPOSITE); assert_eq!(descriptors[9].file_id().as_str(), crate::FILE_ID_STD_WALLET);
assert_eq!(descriptors[10].file_id().as_str(), crate::FILE_ID_SCHEMA_STD_LOGGING); assert_eq!(descriptors[9].filename(), std::path::Path::new(crate::DEFAULT_STD_WALLET_FILENAME));
assert_eq!(descriptors[11].file_id().as_str(), crate::FILE_ID_SCHEMA_STD_OFFCHAIN_TRANSPORT); assert_eq!(descriptors[9].schema_file_id().map(crate::ConfigFileId::as_str), std::option::Option::Some(crate::FILE_ID_SCHEMA_STD_WALLET));
assert_eq!(descriptors[12].file_id().as_str(), crate::FILE_ID_SCHEMA_STD_STORE); assert_eq!(descriptors[10].file_id().as_str(), crate::FILE_ID_SCHEMA_COMPOSITE);
assert_eq!(descriptors[13].file_id().as_str(), crate::FILE_ID_SCHEMA_STD_TRANSPORT); assert_eq!(descriptors[11].file_id().as_str(), crate::FILE_ID_SCHEMA_STD_LOGGING);
assert_eq!(descriptors[14].file_id().as_str(), crate::FILE_ID_SCHEMA_STD_WALLET); assert_eq!(descriptors[12].file_id().as_str(), crate::FILE_ID_SCHEMA_STD_OFFCHAIN_TRANSPORT);
assert!(descriptors[0..9].iter().all(|descriptor| return descriptor.kind() == crate::ConfigFileKind::Config)); assert_eq!(descriptors[13].file_id().as_str(), crate::FILE_ID_SCHEMA_STD_STORE);
assert!(descriptors[9..15].iter().all(|descriptor| return descriptor.kind() == crate::ConfigFileKind::Schema)); assert_eq!(descriptors[14].file_id().as_str(), crate::FILE_ID_SCHEMA_STD_TRANSPORT);
assert_eq!(descriptors[15].file_id().as_str(), crate::FILE_ID_SCHEMA_STD_WALLET);
assert!(descriptors[0..10].iter().all(|descriptor| return descriptor.kind() == crate::ConfigFileKind::Config));
assert!(descriptors[10..16].iter().all(|descriptor| return descriptor.kind() == crate::ConfigFileKind::Schema));
} }
} }
@@ -365,16 +368,18 @@ fn absolute_fixture_path() -> std::path::PathBuf {
} }
#[test] #[test]
fn defaults_register_generic_composite_schema_and_four_runtime_composites() { fn defaults_register_generic_composite_schema_and_five_runtime_composites() {
let registry = crate::ConfigFileRegistry::defaults(); let registry = crate::ConfigFileRegistry::defaults();
let schema_id = crate::ConfigFileId::new(crate::FILE_ID_SCHEMA_COMPOSITE); let schema_id = crate::ConfigFileId::new(crate::FILE_ID_SCHEMA_COMPOSITE);
let backfill_id = crate::ConfigFileId::new(crate::FILE_ID_COMPOSITE_KSP_APP_BACKFILL_DESK); let backfill_id = crate::ConfigFileId::new(crate::FILE_ID_COMPOSITE_KSP_APP_BACKFILL_DESK);
let raw_ingest_id = crate::ConfigFileId::new(crate::FILE_ID_COMPOSITE_KSP_APP_RAW_TRANSACTION_INGEST_DESK);
let solprices_id = crate::ConfigFileId::new(crate::FILE_ID_COMPOSITE_KSP_APP_SOLPRICES_DESK); let solprices_id = crate::ConfigFileId::new(crate::FILE_ID_COMPOSITE_KSP_APP_SOLPRICES_DESK);
let store_desk_id = crate::ConfigFileId::new(crate::FILE_ID_COMPOSITE_KSP_APP_STORE_DESK); let store_desk_id = crate::ConfigFileId::new(crate::FILE_ID_COMPOSITE_KSP_APP_STORE_DESK);
let wallet_id = crate::ConfigFileId::new(crate::FILE_ID_COMPOSITE_KSP_APP_WALLET_DESK); let wallet_id = crate::ConfigFileId::new(crate::FILE_ID_COMPOSITE_KSP_APP_WALLET_DESK);
assert!(registry.is_ok(), "default registry should be valid: {registry:?}"); assert!(registry.is_ok(), "default registry should be valid: {registry:?}");
assert!(schema_id.is_ok(), "composite schema file_id should be valid: {schema_id:?}"); assert!(schema_id.is_ok(), "composite schema file_id should be valid: {schema_id:?}");
assert!(backfill_id.is_ok(), "Backfill Desk composite runtime file_id should be valid: {backfill_id:?}"); assert!(backfill_id.is_ok(), "Backfill Desk composite runtime file_id should be valid: {backfill_id:?}");
assert!(raw_ingest_id.is_ok(), "Raw Transaction Ingest Desk composite runtime file_id should be valid: {raw_ingest_id:?}");
assert!(solprices_id.is_ok(), "SOL Prices Desk composite runtime file_id should be valid: {solprices_id:?}"); assert!(solprices_id.is_ok(), "SOL Prices Desk composite runtime file_id should be valid: {solprices_id:?}");
assert!(store_desk_id.is_ok(), "Store Desk composite runtime file_id should be valid: {store_desk_id:?}"); assert!(store_desk_id.is_ok(), "Store Desk composite runtime file_id should be valid: {store_desk_id:?}");
assert!(wallet_id.is_ok(), "Wallet Desk composite runtime file_id should be valid: {wallet_id:?}"); assert!(wallet_id.is_ok(), "Wallet Desk composite runtime file_id should be valid: {wallet_id:?}");
@@ -382,34 +387,41 @@ fn defaults_register_generic_composite_schema_and_four_runtime_composites() {
std::result::Result::Ok(registry), std::result::Result::Ok(registry),
std::result::Result::Ok(schema_id), std::result::Result::Ok(schema_id),
std::result::Result::Ok(backfill_id), std::result::Result::Ok(backfill_id),
std::result::Result::Ok(raw_ingest_id),
std::result::Result::Ok(solprices_id), std::result::Result::Ok(solprices_id),
std::result::Result::Ok(store_desk_id), std::result::Result::Ok(store_desk_id),
std::result::Result::Ok(wallet_id), std::result::Result::Ok(wallet_id),
) = (registry, schema_id, backfill_id, solprices_id, store_desk_id, wallet_id) ) = (registry, schema_id, backfill_id, raw_ingest_id, solprices_id, store_desk_id, wallet_id)
{ {
let schema = registry.descriptor(&schema_id); let schema = registry.descriptor(&schema_id);
let backfill = registry.descriptor(&backfill_id); let backfill = registry.descriptor(&backfill_id);
let raw_ingest = registry.descriptor(&raw_ingest_id);
let solprices = registry.descriptor(&solprices_id); let solprices = registry.descriptor(&solprices_id);
let store_desk = registry.descriptor(&store_desk_id); let store_desk = registry.descriptor(&store_desk_id);
let wallet = registry.descriptor(&wallet_id); let wallet = registry.descriptor(&wallet_id);
assert!(schema.is_ok(), "generic composite schema should be registered: {schema:?}"); assert!(schema.is_ok(), "generic composite schema should be registered: {schema:?}");
assert!(backfill.is_ok(), "Backfill Desk runtime composite should be registered: {backfill:?}"); assert!(backfill.is_ok(), "Backfill Desk runtime composite should be registered: {backfill:?}");
assert!(raw_ingest.is_ok(), "Raw Transaction Ingest Desk runtime composite should be registered: {raw_ingest:?}");
assert!(solprices.is_ok(), "SOL Prices Desk runtime composite should be registered: {solprices:?}"); assert!(solprices.is_ok(), "SOL Prices Desk runtime composite should be registered: {solprices:?}");
assert!(store_desk.is_ok(), "Store Desk runtime composite should be registered: {store_desk:?}"); assert!(store_desk.is_ok(), "Store Desk runtime composite should be registered: {store_desk:?}");
assert!(wallet.is_ok(), "Wallet Desk runtime composite should be registered: {wallet:?}"); assert!(wallet.is_ok(), "Wallet Desk runtime composite should be registered: {wallet:?}");
if let ( if let (
std::result::Result::Ok(schema), std::result::Result::Ok(schema),
std::result::Result::Ok(backfill), std::result::Result::Ok(backfill),
std::result::Result::Ok(raw_ingest),
std::result::Result::Ok(solprices), std::result::Result::Ok(solprices),
std::result::Result::Ok(store_desk), std::result::Result::Ok(store_desk),
std::result::Result::Ok(wallet), std::result::Result::Ok(wallet),
) = (schema, backfill, solprices, store_desk, wallet) ) = (schema, backfill, raw_ingest, solprices, store_desk, wallet)
{ {
assert_eq!(schema.kind(), crate::ConfigFileKind::Schema); assert_eq!(schema.kind(), crate::ConfigFileKind::Schema);
assert_eq!(schema.filename(), std::path::Path::new(crate::DEFAULT_COMPOSITE_SCHEMA_FILENAME)); assert_eq!(schema.filename(), std::path::Path::new(crate::DEFAULT_COMPOSITE_SCHEMA_FILENAME));
assert_eq!(backfill.kind(), crate::ConfigFileKind::Config); assert_eq!(backfill.kind(), crate::ConfigFileKind::Config);
assert_eq!(backfill.filename(), std::path::Path::new(crate::DEFAULT_COMPOSITE_KSP_APP_BACKFILL_DESK_FILENAME)); assert_eq!(backfill.filename(), std::path::Path::new(crate::DEFAULT_COMPOSITE_KSP_APP_BACKFILL_DESK_FILENAME));
assert_eq!(backfill.schema_file_id(), std::option::Option::Some(&schema_id)); assert_eq!(backfill.schema_file_id(), std::option::Option::Some(&schema_id));
assert_eq!(raw_ingest.kind(), crate::ConfigFileKind::Config);
assert_eq!(raw_ingest.filename(), std::path::Path::new(crate::DEFAULT_COMPOSITE_KSP_APP_RAW_TRANSACTION_INGEST_DESK_FILENAME));
assert_eq!(raw_ingest.schema_file_id(), std::option::Option::Some(&schema_id));
assert_eq!(solprices.kind(), crate::ConfigFileKind::Config); assert_eq!(solprices.kind(), crate::ConfigFileKind::Config);
assert_eq!(solprices.filename(), std::path::Path::new(crate::DEFAULT_COMPOSITE_KSP_APP_SOLPRICES_DESK_FILENAME)); assert_eq!(solprices.filename(), std::path::Path::new(crate::DEFAULT_COMPOSITE_KSP_APP_SOLPRICES_DESK_FILENAME));
assert_eq!(solprices.schema_file_id(), std::option::Option::Some(&schema_id)); assert_eq!(solprices.schema_file_id(), std::option::Option::Some(&schema_id));

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-config-lib/unit_tests/transport.rs // file: crates/ksp-config-lib/unit_tests/transport.rs
// version: 13 // version: 14
#[test] #[test]
fn fixture_transport_profile_maps_complete_runtime_contract() { fn fixture_transport_profile_maps_complete_runtime_contract() {
@@ -51,6 +51,19 @@ fn fixture_transport_profile_maps_complete_runtime_contract() {
assert_eq!(endpoint.provider().as_str(), "fixture-provider"); assert_eq!(endpoint.provider().as_str(), "fixture-provider");
assert_eq!(endpoint.cluster().as_str(), "fixture-cluster"); assert_eq!(endpoint.cluster().as_str(), "fixture-cluster");
assert_eq!(endpoint.protocol(), ksp_onchain_transport_lib::WsProtocolKind::SolanaStandard); assert_eq!(endpoint.protocol(), ksp_onchain_transport_lib::WsProtocolKind::SolanaStandard);
assert_eq!(
endpoint.subscription_capabilities(),
std::option::Option::Some(
&[
ksp_onchain_transport_lib::WsSubscriptionKind::Account,
ksp_onchain_transport_lib::WsSubscriptionKind::Logs,
ksp_onchain_transport_lib::WsSubscriptionKind::Program,
ksp_onchain_transport_lib::WsSubscriptionKind::Root,
ksp_onchain_transport_lib::WsSubscriptionKind::Signature,
ksp_onchain_transport_lib::WsSubscriptionKind::Slot,
][..],
)
);
assert_eq!(endpoint.url().as_str(), "wss://fallback.invalid"); assert_eq!(endpoint.url().as_str(), "wss://fallback.invalid");
assert_eq!(endpoint.session().command_timeout(), std::time::Duration::from_millis(4500)); assert_eq!(endpoint.session().command_timeout(), std::time::Duration::from_millis(4500));
assert_eq!(endpoint.session().close_timeout(), std::time::Duration::from_millis(4000)); assert_eq!(endpoint.session().close_timeout(), std::time::Duration::from_millis(4000));
@@ -70,6 +83,21 @@ fn fixture_transport_profile_maps_complete_runtime_contract() {
assert_eq!(helius.provider().as_str(), "helius"); assert_eq!(helius.provider().as_str(), "helius");
assert_eq!(helius.cluster().as_str(), "mainnet"); assert_eq!(helius.cluster().as_str(), "mainnet");
assert_eq!(helius.protocol(), ksp_onchain_transport_lib::WsProtocolKind::HeliusLaserStream); assert_eq!(helius.protocol(), ksp_onchain_transport_lib::WsProtocolKind::HeliusLaserStream);
assert_eq!(
helius.subscription_capabilities(),
std::option::Option::Some(
&[
ksp_onchain_transport_lib::WsSubscriptionKind::Account,
ksp_onchain_transport_lib::WsSubscriptionKind::Logs,
ksp_onchain_transport_lib::WsSubscriptionKind::Program,
ksp_onchain_transport_lib::WsSubscriptionKind::Root,
ksp_onchain_transport_lib::WsSubscriptionKind::Signature,
ksp_onchain_transport_lib::WsSubscriptionKind::Slot,
ksp_onchain_transport_lib::WsSubscriptionKind::SlotsUpdates,
ksp_onchain_transport_lib::WsSubscriptionKind::HeliusTransaction,
][..],
)
);
assert_eq!(helius.url().as_str(), "wss://mainnet.helius-rpc.com/?api-key=fixture-helius-key"); assert_eq!(helius.url().as_str(), "wss://mainnet.helius-rpc.com/?api-key=fixture-helius-key");
let _connect_future = ksp_onchain_transport_lib::HeliusLaserStreamWsSession::connect(helius.clone()); let _connect_future = ksp_onchain_transport_lib::HeliusLaserStreamWsSession::connect(helius.clone());
} }
@@ -117,6 +145,8 @@ fn committed_transport_document_maps_default_and_explicit_profiles() {
if let std::option::Option::Some(ws) = ws { if let std::option::Option::Some(ws) = ws {
assert_eq!(ws.endpoints()[0].url().as_str(), "wss://api.devnet.solana.com"); assert_eq!(ws.endpoints()[0].url().as_str(), "wss://api.devnet.solana.com");
assert_eq!(ws.endpoints()[0].protocol(), ksp_onchain_transport_lib::WsProtocolKind::SolanaStandard); 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));
} }
} }
if let std::result::Result::Ok(mainnet) = mainnet { if let std::result::Result::Ok(mainnet) = mainnet {
@@ -129,6 +159,8 @@ fn committed_transport_document_maps_default_and_explicit_profiles() {
assert!(ws.is_some(), "committed V3 Mainnet profile should expose WebSocket settings"); assert!(ws.is_some(), "committed V3 Mainnet profile should expose WebSocket settings");
if let std::option::Option::Some(ws) = ws { if let std::option::Option::Some(ws) = ws {
assert_eq!(ws.endpoints()[0].url().as_str(), "wss://api.mainnet.solana.com"); assert_eq!(ws.endpoints()[0].url().as_str(), "wss://api.mainnet.solana.com");
assert!(ws.endpoints()[0].supports_subscription(ksp_onchain_transport_lib::WsSubscriptionKind::Logs));
assert!(!ws.endpoints()[0].supports_subscription(ksp_onchain_transport_lib::WsSubscriptionKind::Block));
} }
} }
} }
@@ -529,6 +561,47 @@ fn helius_laserstream_mainnet_and_devnet_api_key_map_to_protocol_and_safe_redact
assert!(devnet_debug.contains(crate::REDACTED_CONFIG_VALUE)); assert!(devnet_debug.contains(crate::REDACTED_CONFIG_VALUE));
} }
#[test]
fn v0_3_15_pre_003_committed_helius_profiles_map_transaction_capability_without_block_or_vote() {
let engine = committed_engine();
let engine = match engine {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let canary = "helius-pre-003-api-key-canary";
let mut process = std::collections::BTreeMap::<String, String>::new();
process.insert("KSP_SECRET_HELIUS_API_KEY".to_owned(), canary.to_owned());
let environment = crate::ConfigEnvironment::from_maps(process, std::collections::BTreeMap::new());
for (profile_id, cluster, expected_url) in [
("helius_devnet", "devnet", "wss://devnet.helius-rpc.com/?api-key=helius-pre-003-api-key-canary"),
("helius_mainnet", "mainnet", "wss://mainnet.helius-rpc.com/?api-key=helius-pre-003-api-key-canary"),
] {
let resolved = engine.load_resolved_transport_config(std::option::Option::Some(profile_id), &environment);
assert!(resolved.is_ok(), "committed Helius profile {profile_id} should resolve: {resolved:?}");
let resolved = match resolved {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => continue,
};
assert_eq!(resolved.http_settings().endpoints()[0].cluster().as_str(), cluster);
let ws = resolved.ws_settings();
assert!(ws.is_some(), "committed Helius profile {profile_id} should expose WebSocket settings");
if let std::option::Option::Some(ws) = ws {
assert_eq!(ws.endpoints().len(), 1);
let endpoint = &ws.endpoints()[0];
assert_eq!(endpoint.provider().as_str(), "helius");
assert_eq!(endpoint.cluster().as_str(), cluster);
assert_eq!(endpoint.protocol(), ksp_onchain_transport_lib::WsProtocolKind::HeliusLaserStream);
assert_eq!(endpoint.url().as_str(), expected_url);
assert!(endpoint.supports_subscription(ksp_onchain_transport_lib::WsSubscriptionKind::Logs));
assert!(endpoint.supports_subscription(ksp_onchain_transport_lib::WsSubscriptionKind::HeliusTransaction));
assert!(!endpoint.supports_subscription(ksp_onchain_transport_lib::WsSubscriptionKind::Block));
assert!(!endpoint.supports_subscription(ksp_onchain_transport_lib::WsSubscriptionKind::Vote));
}
let debug = format!("{resolved:?}");
assert!(!debug.contains(canary));
}
}
#[test] #[test]
fn transport_secret_url_provenance_uses_process_and_process_beats_dotenv() { fn transport_secret_url_provenance_uses_process_and_process_beats_dotenv() {
let engine = fixture_engine(); let engine = fixture_engine();

333
deltas/0.3.15/pre.003.md Normal file
View File

@@ -0,0 +1,333 @@
<!-- file: deltas/0.3.15/pre.003.md -->
<!-- version: 1 -->
# Delta `0.3.15-pre.003` — mapping Config des capabilities WS et composite Raw Ingest Desk
## Base requise
```text
0.3.15-pre.002-fix.001 appliquée
workspace.package.version = 0.3.15-pre.2.fix.1
deltas/0.3.15/pre.002-fix.001.md présent
```
## Gate de la base
Le gate opérateur communiqué après `pre.002-fix.001` est propre :
```text
cargo fmt --all : PASS
cargo fmt --all -- --check : PASS
General Rust rule audit : clean
Rust export completeness audit : 0 candidate(s)
KSP workspace Rust rule audit : clean
Markdown table audit : clean (318 table(s), 187 file(s))
cargo check --workspace : PASS
cargo clippy --workspace --all-targets --all-features -- -D warnings : PASS
ksp-onchain-transport-lib unit tests : 395 passed / 0 failed
ksp-onchain-transport-lib public_api : 54 passed / 0 failed
ksp-onchain-transport-lib release_completeness : 47 passed / 0 failed
live smoke tests : ignored / opt-in uniquement
```
Ce gate qualifie la base directe de `pre.003` avant toute modification Config.
## Objectif
Implémenter strictement la tranche `pre.003` du plan `036` :
```text
porter les capabilities WS explicites dans le schema std.transport
mapper ces capabilities vers WsSubscriptionKind sans second enum runtime
committer des déclarations conservatrices sur les profils Transport existants
committer les profils Helius nécessaires à la future Desk sans inférence provider
ajouter le composite Raw Transaction Ingest Desk
forcer la cohérence Transport + Store par réseau dans ce composite
enregistrer le nouveau composite dans le registry Config
synchroniser les ressources Config packagées des Desk existantes
préserver l'absence de frontend et de runtime Worker dans cette tranche
```
L'enforcement par les constructeurs Worker reste exclusivement réservé à `pre.004`.
## Schema et mapping des capabilities WS
`std.transport.schema.json` accepte désormais sur chaque endpoint WebSocket le champ optionnel :
```text
capabilities: array unique borné à 10 entrées
```
Descripteurs admis :
```text
account
block
logs
program
root
signature
slot
slots_updates
vote
helius_transaction
```
Le mapping Config convertit chaque descripteur vers le `WsSubscriptionKind` Transport existant puis utilise `WsEndpointSettings::with_subscription_capabilities(...)`.
Aucun enum Config public supplémentaire n'est créé et aucune capability n'est dérivée du champ `provider`.
L'absence du champ reste backward-readable et conserve la sémantique fail-closed introduite par Transport en `pre.002`.
## Déclarations committed conservatrices
Les endpoints `solana_standard` committed déclarent exactement la surface stable utilisée par KSP :
```text
Account
Logs
Program
Root
Signature
Slot
```
Ils ne déclarent pas :
```text
Block
SlotsUpdates
Vote
HeliusTransaction
```
En particulier, `blockSubscribe` n'est jamais supposé disponible par simple appartenance au protocole Solana standard.
Les endpoints `helius_laserstream` committed déclarent :
```text
Account
Logs
Program
Root
Signature
Slot
SlotsUpdates
HeliusTransaction
```
Ils ne déclarent ni `Block` ni `Vote`.
## Profils Helius committed
Deux profils Transport sont ajoutés :
```text
helius_devnet
helius_mainnet
```
Chacun compose :
```text
HTTP public du même réseau pour hydration
WS HeliusLaserStream du même réseau
KSP_SECRET_HELIUS_API_KEY résolu uniquement côté Config/backend
capability HeliusTransaction explicitement déclarée
```
Le secret ne traverse aucune nouvelle projection publique ou frontend.
## Composite Raw Transaction Ingest Desk
Le nouveau document :
```text
config/composite.ksp-app-raw-transaction-ingest-desk.json
```
est enregistré sous :
```text
cfg.composite.ksp-app-raw-transaction-ingest-desk
```
Il utilise le schema composite générique existant et possède `devnet_public` comme profil par défaut.
Profils applicatifs committed :
```text
devnet_public -> Transport devnet_public + Store devnet
helius_devnet -> Transport helius_devnet + Store devnet
orbitflare_devnet -> Transport orbitflare_devnet + Store devnet
mainnet_public -> Transport mainnet_public + Store mainnet
helius_mainnet -> Transport helius_mainnet + Store mainnet
publicnode_mainnet -> Transport publicnode_mainnet + Store mainnet
publicnode_testnet -> Transport publicnode_testnet + Store testnet
```
Chaque profil compose également `cfg.std.logging/supertrace`.
`mainnet_backfill_pool` est volontairement absent : un profil explicitement façonné pour Backfill ne devient pas une dépendance implicite du Worker live. Cette séparation préserve les deux producteurs indépendants du même RAW Store.
## Registry et packaging Config
Le registry Config passe de 15 à 16 descripteurs avec le nouveau composite.
Les cinq Desk existantes utilisant le runtime Config packagé embarquent donc désormais 16 ressources :
```text
ksp-app-backfill-desk
ksp-app-config-desk
ksp-app-solprices-desk
ksp-app-store-desk
ksp-app-wallet-desk
```
Le nouveau composite est uniquement une ressource Config partagée à ce stade ; aucune nouvelle crate Desk n'est créée en `pre.003`.
Les canaris exacts de packaging, registry et inventaire Config sont recalibrés à 16 ressources/descripteurs.
## Tests et canaris ajoutés ou étendus
La tranche couvre notamment :
```text
mapping exact SolanaStandard des capabilities déclarées
mapping exact HeliusLaserStream des capabilities déclarées
profils committed standard : Logs oui / Block non
profils committed Helius : HeliusTransaction oui / Block et Vote non
redaction de KSP_SECRET_HELIUS_API_KEY dans les projections Debug
résolution des sept profils du composite Raw Ingest Desk
égalité de réseau Store / HTTP / WS / gRPC pour chaque profil applicatif
inventaire exact du registry public
présence du composite dans les ressources packagées des cinq Desk existantes
présence du composite dans l'inventaire Config Desk
```
## Documentation
`ksp-config-lib/README.md` et `USAGE.md` restent version-neutral et documentent :
```text
le nouveau composite Raw Ingest Desk
la déclaration explicite capabilities des endpoints WS
les descripteurs admis
l'absence d'inférence par provider
la surface conservatrice standard committed
les profils Helius committed et leur secret Config-owned
la cohérence Transport + Store du composite applicatif
```
Le plan `036` et la validation `032` sont réconciliés avec les faits de `pre.003` sans transformer le forecast en tableau.
## Fichiers ajoutés
```text
config/composite.ksp-app-raw-transaction-ingest-desk.json
crates/ksp-config-lib/unit_tests/fixtures/composite.ksp-app-raw-transaction-ingest-desk.json
deltas/0.3.15/pre.003.md
```
## Fichiers modifiés
```text
Cargo.toml
config/examples/std.transport.example.json
config/schemas/std.transport.schema.json
config/std.transport.json
crates/ksp-app-backfill-desk/tauri.conf.json
crates/ksp-app-backfill-desk/tests/desktop_contract.rs
crates/ksp-app-config-desk/tauri.conf.json
crates/ksp-app-config-desk/tests/desktop_contract.rs
crates/ksp-app-config-desk/unit_tests/profiles.rs
crates/ksp-app-solprices-desk/tauri.conf.json
crates/ksp-app-solprices-desk/tests/desktop_contract.rs
crates/ksp-app-store-desk/tauri.conf.json
crates/ksp-app-store-desk/tests/desktop_contract.rs
crates/ksp-app-wallet-desk/tauri.conf.json
crates/ksp-app-wallet-desk/tests/desktop_contract.rs
crates/ksp-app-wallet-desk/tests/release_compliance.rs
crates/ksp-config-lib/README.md
crates/ksp-config-lib/USAGE.md
crates/ksp-config-lib/src/lib.rs
crates/ksp-config-lib/src/registry.rs
crates/ksp-config-lib/src/transport.rs
crates/ksp-config-lib/tests/ownership.rs
crates/ksp-config-lib/tests/public_api.rs
crates/ksp-config-lib/unit_tests/composite.rs
crates/ksp-config-lib/unit_tests/fixtures/std.transport.json
crates/ksp-config-lib/unit_tests/registry.rs
crates/ksp-config-lib/unit_tests/transport.rs
docs/plans/036-V0_3_15_RAW_TRANSACTION_INGEST_DESK_PLAN.md
docs/validation/032-V0_3_15_RAW_TRANSACTION_INGEST_DESK.md
```
## Fichiers supprimés
```text
aucun
```
## Version Cargo
Cette tranche modifie Config/runtime mapping et packaging :
```text
header Cargo.toml : 596 -> 597
workspace.package.version : 0.3.15-pre.2.fix.1 -> 0.3.15-pre.3
```
Tous les crates workspace héritent de cette version.
## Validations exécutées dans l'environnement d'assemblage
```text
General Rust rule audit : clean
Rust export completeness audit : 0 candidate(s)
KSP workspace Rust rule audit : clean
Markdown table audit : clean
JSON parse audit : clean
std.transport JSON Schema validation : PASS
composite JSON Schema validation : PASS
capability declarations committed : PASS
composite Transport/Store network coupling statique : PASS
packaged Config resource inventory : 16/16 sur les cinq Desk existantes
versions de fichiers Rust modifiés : incrémentées exactement d'une unité
```
## Validations non exécutées dans l'environnement d'assemblage
La toolchain Cargo/Rust n'est pas disponible dans l'environnement d'assemblage. Le gate opérateur doit confirmer :
```bash
cargo fmt --all
cargo fmt --all -- --check
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.3.15
cargo check --workspace
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test -p ksp-config-lib --all-targets --all-features
cargo test --workspace --all-targets --all-features
```
## Décisions prises
```text
Config porte la déclaration de capability ; Transport reste propriétaire de sa sémantique typed
aucune capability n'est inférée depuis un provider name
les profils standard committed n'annoncent pas Block sans preuve explicite d'endpoint
les profils Helius committed n'annoncent ni Block ni Vote
les profils Helius peuvent hydrater via HTTP public du même réseau
le composite Raw Ingest couple toujours Transport et Store du même réseau
mainnet_backfill_pool reste hors composite Raw Ingest
aucun frontend et aucun Start/Stop Worker ne sont ouverts avant les tranches prévues
```
## Questions ouvertes
```text
aucune pour pre.003
```
La prochaine responsabilité reste `pre.004` : faire appliquer les capabilities WS déclarées par les constructeurs Worker avant toute I/O.

View File

@@ -1,5 +1,5 @@
<!-- file: docs/plans/036-V0_3_15_RAW_TRANSACTION_INGEST_DESK_PLAN.md --> <!-- file: docs/plans/036-V0_3_15_RAW_TRANSACTION_INGEST_DESK_PLAN.md -->
<!-- version: 2 --> <!-- version: 3 -->
# Plan v0.3.15 — Raw Transaction Ingest Desk # Plan v0.3.15 — Raw Transaction Ingest Desk
@@ -191,15 +191,16 @@ Aucun gap Store n'est identifié. L'application ne dépend jamais de `ksp-store-
## 6. Profils Transport réellement committed ## 6. Profils Transport réellement committed
| Profil/config | Réseau | Surfaces | Secret requis | Committed ? | Observation | | Profil/config | Réseau | Surfaces | Secret requis | Committed ? | Observation |
|-------------------------|------------------------------|---------------------------------------|-----------------------|----------------------|-------------------------------------------| |-------------------------|---------|---------------------------------------|-------------------|-------------|-----------------------------------------------------------------|
| `devnet_public` | devnet | HTTP + WS Solana standard | non | oui | pas de capability WS fine aujourdhui | | `devnet_public` | devnet | HTTP + WS Solana standard | non | oui | capabilities stables déclarées ; `Logs` oui ; `Block` non |
| `orbitflare_devnet` | devnet | HTTP + WS standard + Yellowstone gRPC | token gRPC secret | conditionnel | profil complet seulement si secret résolu | | `helius_devnet` | devnet | HTTP public + WS Helius | API key Helius | oui | `HeliusTransaction` oui ; `Block`/`Vote` non |
| `mainnet_public` | mainnet | HTTP + WS Solana standard | non | oui | pas de capability WS fine aujourdhui | | `orbitflare_devnet` | devnet | HTTP + WS standard + Yellowstone gRPC | token gRPC secret | oui | `Logs` déclaré ; gRPC composable seulement si secret résolu |
| `mainnet_backfill_pool` | mainnet | plusieurs HTTP + WS standard | selon endpoint/config | oui si profil résolu | priorités/rôles restent Transport-owned | | `mainnet_public` | mainnet | HTTP + WS Solana standard | non | oui | capabilities stables déclarées ; `Logs` oui ; `Block` non |
| `publicnode_mainnet` | mainnet | HTTP + WS standard + Yellowstone gRPC | x-token secret | conditionnel | gRPC provider-gated | | `helius_mainnet` | mainnet | HTTP public + WS Helius | API key Helius | oui | `HeliusTransaction` oui ; `Block`/`Vote` non |
| `publicnode_testnet` | testnet | HTTP + WS standard + Yellowstone gRPC | x-token secret | conditionnel | gRPC provider-gated | | `mainnet_backfill_pool` | mainnet | plusieurs HTTP + WS standard | selon endpoints | oui | `Logs` déclaré ; profil conservé hors composite Raw Ingest Desk |
| Helius exemple | mainnet/devnet selon exemple | WS `helius_laserstream` | API key secret | non committed | exemple != profil actif | | `publicnode_mainnet` | mainnet | HTTP + WS standard + Yellowstone gRPC | x-token secret | oui | `Logs` déclaré ; gRPC composable seulement si secret résolu |
| `publicnode_testnet` | testnet | HTTP + WS standard + Yellowstone gRPC | x-token secret | oui | `Logs` déclaré ; gRPC composable seulement si secret résolu |
Conséquences : Conséquences :
@@ -233,13 +234,13 @@ Le frontend reçoit uniquement la projection sûre de ce modèle. Les ressources
## 8. Matrice route / capability / Config / Worker ## 8. Matrice route / capability / Config / Worker
| Route V1 | Network | Required capability | Optional capability | Config evidence | Worker resource | Composable aujourdhui ? | Missing contract | Live-testable ? | | Route V1 | Network | Required capability | Optional capability | Config evidence | Worker resource | Composable aujourdhui ? | Missing contract | Live-testable ? |
|-------------------------------|------------------|----------------------------------------------------------------|-------------------------------------------------|-----------------------|-----------------------------------------------|---------------------------------------------------------|--------------------------------------------------|-----------------------------------------------| |-------------------------------|------------------|----------------------------------------------------------------|-------------------------------------------------|---------------------------------|-----------------------------------------------|-----------------------------------------------------------------------------------|------------------------------------------------|--------------------------------------|
| `yellowstone-hydrated` | réseau du profil | Yellowstone gRPC Subscribe + HTTP `getTransaction` même réseau | scan HTTP repair si rôle compatible | gRPC + HTTP role | `RawTransactionIngestYellowstoneSource` | oui sur profil complet et secrets résolus | aucun contrat essentiel | provider-gated selon profil | | `yellowstone-hydrated` | réseau du profil | Yellowstone gRPC Subscribe + HTTP `getTransaction` même réseau | scan HTTP repair si rôle compatible | gRPC + HTTP role | `RawTransactionIngestYellowstoneSource` | oui sur profil complet et secrets résolus | aucun contrat essentiel | provider-gated selon profil |
| `standard-logs-hydrated` | réseau du profil | WS Solana standard `logsSubscribe` + HTTP `getTransaction` | scan HTTP repair si rôle compatible | WS + HTTP role | `RawTransactionIngestStandardLogsSource` | non prouvable strictement avant capability WS explicite | Transport + Config + enforcement Worker minimaux | oui, notamment Devnet keyless après extension | | `standard-logs-hydrated` | réseau du profil | WS Solana standard `logsSubscribe` + HTTP `getTransaction` | scan HTTP repair si rôle compatible | `Logs` + HTTP role | `RawTransactionIngestStandardLogsSource` | oui côté Config sur profils déclarant `Logs` ; enforcement Worker en `pre.004` | enforcement Worker minimal | oui, notamment Devnet keyless |
| `standard-block-direct` | réseau du profil | WS Solana standard `blockSubscribe` explicitement déclaré | aucune hydration obligatoire | WS capability Block | `RawTransactionIngestStandardBlockSource` | non prouvable aujourdhui | Transport + Config + enforcement Worker minimaux | seulement endpoint prouvé compatible | | `standard-block-direct` | réseau du profil | WS Solana standard `blockSubscribe` explicitement déclaré | aucune hydration obligatoire | WS capability `Block` | `RawTransactionIngestStandardBlockSource` | non : aucun profil committed ne déclare `Block` | enforcement Worker minimal + endpoint qualifié | seulement endpoint prouvé compatible |
| `helius-transaction-hydrated` | réseau du profil | WS Helius `transactionSubscribe` + HTTP `getTransaction` | scan HTTP repair si rôle compatible | Helius WS + HTTP role | `RawTransactionIngestHeliusTransactionSource` | non dans les profils committed actuels | Config data/composite + capability WS explicite | provider-gated Helius | | `helius-transaction-hydrated` | réseau du profil | WS Helius `transactionSubscribe` + HTTP `getTransaction` | scan HTTP repair si rôle compatible | `HeliusTransaction` + HTTP role | `RawTransactionIngestHeliusTransactionSource` | oui côté Config sur `helius_*` si secret résolu ; enforcement Worker en `pre.004` | enforcement Worker minimal | provider-gated Helius |
| `http-block-polling` | réseau du profil | HTTP `getSlot` + `getBlocksWithLimit` + `getBlock` | `getTransaction` pour known-reference hydration | HTTP role | `RawTransactionIngestHttpBlockPollingSource` | oui lorsque le rôle résolu annonce les trois méthodes | aucun contrat essentiel | oui sur endpoint public compatible | | `http-block-polling` | réseau du profil | HTTP `getSlot` + `getBlocksWithLimit` + `getBlock` | `getTransaction` pour known-reference hydration | HTTP role | `RawTransactionIngestHttpBlockPollingSource` | oui lorsque le rôle résolu annonce les trois méthodes | aucun contrat essentiel | oui sur endpoint public compatible |
### 8.1 `yellowstone-hydrated` ### 8.1 `yellowstone-hydrated`
@@ -292,7 +293,7 @@ HTTP same-network avec getTransaction
commitment confirmed ou finalized commitment confirmed ou finalized
``` ```
Le profil committed stable n'expose actuellement pas cette ressource ; les exemples Helius ne sont pas convertis en disponibilité implicite. Les profils committed `helius_devnet` et `helius_mainnet` déclarent explicitement `HeliusTransaction` et restent conditionnels à la résolution de `KSP_SECRET_HELIUS_API_KEY`. Aucun profil Helius ne déclare `Block` ou `Vote`. Le provider name ne participe pas à cette preuve.
### 8.5 `http-block-polling` ### 8.5 `http-block-polling`
@@ -693,6 +694,8 @@ Budget cible : une tranche Transport.
Étendre le schema/mapping Config pour porter les capabilities WS déclarées et préparer les composites/profils Raw Transaction Ingest Desk cohérents Transport+Store. Aucun frontend. Étendre le schema/mapping Config pour porter les capabilities WS déclarées et préparer les composites/profils Raw Transaction Ingest Desk cohérents Transport+Store. Aucun frontend.
**État : implémenté.** Les profils WS committed déclarent désormais leurs capabilities explicitement, deux profils Helius sont committed, et le composite Raw Ingest Desk couple Transport+Store par réseau sans inclure le profil `mainnet_backfill_pool`.
Budget cible : une tranche Config. Budget cible : une tranche Config.
### `pre.004` — enforcement Worker des capabilities WS ### `pre.004` — enforcement Worker des capabilities WS

View File

@@ -1,5 +1,5 @@
<!-- file: docs/validation/032-V0_3_15_RAW_TRANSACTION_INGEST_DESK.md --> <!-- file: docs/validation/032-V0_3_15_RAW_TRANSACTION_INGEST_DESK.md -->
<!-- version: 3 --> <!-- version: 4 -->
# Validation v0.3.15 — Raw Transaction Ingest Desk # Validation v0.3.15 — Raw Transaction Ingest Desk
@@ -344,11 +344,11 @@ Le forecast durable est porté par la section 20 du plan `036` sous forme hiéra
### `pre.002` — Transport WS capability ### `pre.002` — Transport WS capability
Capability WS typed par subscription + tests, sans app. **Statut : réalisé après `pre.002-fix.001`.** Capability WS typed par subscription + tests, sans app. Le gate opérateur du fix passe `cargo check`, Clippy `-D warnings` et la suite Transport complète.
### `pre.003` — Config capability/composite ### `pre.003` — Config capability/composite
Schema/mapping capabilities WS + composite/profils Raw Ingest Desk. **Statut : réalisé côté assemblage ; gate Cargo opérateur requis.** Schema/mapping capabilities WS + composite/profils Raw Ingest Desk. Les profils standard committed déclarent la surface stable sans `Block`, les profils Helius déclarent explicitement `HeliusTransaction` sans `Block`/`Vote`, et les profils du composite Raw Ingest Desk couplent Transport+Store sur le même réseau.
### `pre.004` — Worker enforcement ### `pre.004` — Worker enforcement