v0.2.2-pre.007

This commit is contained in:
2026-08-18 09:48:11 +02:00
parent ecfb9500eb
commit b57f796187
16 changed files with 1020 additions and 78 deletions

View File

@@ -1,5 +1,5 @@
<!-- file: crates/ksp-onchain-transport-lib/README.md -->
<!-- version: 1 -->
<!-- version: 2 -->
# `ksp-onchain-transport-lib`
@@ -69,16 +69,16 @@ Le registre porte notamment :
- remplacement historique éventuel ;
- release de couverture typée KSP.
Les quatre wrappers typés de la foundation sont :
La surface typée candidate après `0.2.2-pre.007` contient **26 méthodes** :
```text
getBalance
getGenesisHash
getHealth
getVersion
0.2.1 foundation : 4
0.2.2 Accounts : 5
0.2.2 Tokens : 5
0.2.2 Cluster : 12
```
Les autres méthodes courantes peuvent déjà passer par l'exécuteur JSON-RPC standard générique lorsqu'un consumer fournit explicitement descriptor et paramètres JSON. Cette surface raw/générique **ne vaut pas couverture typée** : les wrappers et DTOs typés restants sont introduits selon la matrice HTTP KSP.
Les quatre canaris foundation restent `getBalance`, `getGenesisHash`, `getHealth` et `getVersion`. `0.2.2` ajoute les 22 wrappers Accounts/Tokens/Cluster affectés à `HttpRpcCoverageRelease::V0_2_2`. Les 26 méthodes Transactions/Blocks/Economics encore affectées à `0.2.3``0.2.4` peuvent déjà passer par l'exécuteur JSON-RPC standard générique lorsqu'un consumer fournit explicitement descriptor et paramètres JSON, mais cette surface raw/générique **ne vaut pas couverture typée**.
Les 14 méthodes historiques restent découvrables pour la compliance mais sont `Removed` et ne sont pas simulées comme appelables.
@@ -118,18 +118,22 @@ La configuration Logging de référence conserve un fichier dédié Transport à
Les tests par défaut sont déterministes et n'exigent pas Internet : fixtures JSON et serveur HTTP local couvrent requêtes, réponses, retry, 429, timeout, redaction et routing.
Un smoke Devnet live existe côté `ksp-config-lib` afin de tester la chaîne réelle :
Deux smokes Devnet opt-in sont séparés par responsabilité :
```text
Config -> std.transport/devnet_public -> HttpTransportPool
-> getHealth/getGenesisHash/getVersion/getBalance
Transport pur : settings programmatiques -> HttpTransportPool
-> getAccountInfo/getTokenSupply/getEpochInfo/getVoteAccounts
Composition historique : Config -> std.transport/devnet_public -> HttpTransportPool
-> getHealth/getGenesisHash/getVersion/getBalance
```
Il est `ignored` par défaut et doit être exécuté explicitement.
Les deux sont `ignored` par défaut. Le smoke Transport appartient durablement à cette crate ; le smoke cross-crates hébergé dans Config reste transitoire jusqu'à l'existence d'une surface KSP d'intégration/orchestration appropriée.
## Documentation
- [`USAGE.md`](USAGE.md) — consommation directe, Config -> Transport, API typed/raw et inspection runtime ;
- [`../../docs/plans/008-V0_2_1_ONCHAIN_HTTP_PLAN.md`](../../docs/plans/008-V0_2_1_ONCHAIN_HTTP_PLAN.md) — plan et matrice HTTP ;
- [`../../docs/validation/003-V0_2_1_ONCHAIN_HTTP.md`](../../docs/validation/003-V0_2_1_ONCHAIN_HTTP.md) — matrice de clôture ;
- [`USAGE.md`](USAGE.md) — consommation directe, Config -> Transport, API typed/raw, smokes et inspection runtime ;
- [`../../docs/plans/008-V0_2_1_ONCHAIN_HTTP_PLAN.md`](../../docs/plans/008-V0_2_1_ONCHAIN_HTTP_PLAN.md) — foundation HTTP stable ;
- [`../../docs/plans/009-V0_2_2_HTTP_ACCOUNTS_TOKENS_CLUSTER_PLAN.md`](../../docs/plans/009-V0_2_2_HTTP_ACCOUNTS_TOKENS_CLUSTER_PLAN.md) — extension typed Accounts/Tokens/Cluster ;
- [`../../docs/validation/004-V0_2_2_HTTP_ACCOUNTS_TOKENS_CLUSTER.md`](../../docs/validation/004-V0_2_2_HTTP_ACCOUNTS_TOKENS_CLUSTER.md) — matrice de clôture candidate `0.2.2` ;
- [`../../config/std.transport.json`](../../config/std.transport.json) — configuration standard HTTP.

View File

@@ -1,5 +1,5 @@
<!-- file: crates/ksp-onchain-transport-lib/USAGE.md -->
<!-- version: 1 -->
<!-- version: 2 -->
# Utilisation de `ksp-onchain-transport-lib`
@@ -86,17 +86,17 @@ let balance = pool
.await;
```
Les types de retour associés sont :
Les quatre canaris `0.2.1` restent disponibles. `0.2.2` ajoute les wrappers typés Accounts, Tokens et Cluster. Exemples représentatifs :
```text
SolanaNodeHealth
SolanaGenesisHash
SolanaNodeVersion
GetBalanceResult
SolanaRpcContext
```rust
let account = pool
.get_account_info(&role, &ksp_core_lib::PRGIDPK_SOLANA_SYSTEM, None)
.await;
let epoch = pool.get_epoch_info(&role, None).await;
let vote_accounts = pool.get_vote_accounts(&role, None).await;
```
`GetBalanceResult::value()` renvoie les lamports et `context()` fournit le slot/API version retournés par Solana.
La surface candidate contient 26 wrappers typés au total : 4 foundation + 5 Accounts + 5 Tokens + 12 Cluster. Les DTOs Transport conservent les `null`, options et formes wire : données Account encodées/`jsonParsed`, `TokenAmount.uiAmount`, contexte RPC, nodes, epoch, leader schedule et vote accounts. Aucun décodage Program/SPL métier n'est effectué ici.
## 4. Exécution JSON-RPC standard générique
@@ -153,12 +153,22 @@ Ne jamais journaliser l'URL complète, un token provider, un body massif, une tr
La configuration standard route les événements `info` de Transport vers un fichier dédié. Pour une investigation temporaire, élever uniquement ce target/sink à `debug` ou `trace`, puis revenir à `info` avant clôture du développement.
## 9. Smoke Devnet opt-in
## 9. Smokes Devnet opt-in
Le smoke live est volontairement hors des tests par défaut :
Le smoke **Transport pur** construit ses settings programmatiquement et exerce un sous-ensemble représentatif de `0.2.2` :
```bash
cargo test -p ksp-onchain-transport-lib --test transport_devnet_smoke -- --ignored --nocapture
```
Il appelle `getAccountInfo`, `getTokenSupply`, `getEpochInfo` et `getVoteAccounts`. Le mint utilisé par `getTokenSupply` est celui de l'exemple Devnet de la documentation officielle courante ; si cet état externe disparaît, le smoke doit être réaudité plutôt que transformé en test déterministe.
Le smoke historique de **composition Config -> Transport** reste également disponible :
```bash
cargo test -p ksp-config-lib --test transport_devnet_smoke -- --ignored --nocapture
```
Il charge le profil Config `devnet_public`, construit le pool puis appelle les quatre wrappers typés. Les endpoints publics Solana étant rate-limités et non destinés à la production, un échec réseau externe n'est pas interprété comme un échec déterministe de la suite locale.
Il valide le profil committé `devnet_public` et les quatre canaris foundation. Il reste transitoirement hébergé dans Config : les futurs smokes cross-crates ne doivent pas faire de Config leur destination générale et devront migrer vers une surface d'intégration/orchestration dédiée lorsqu'elle existera.
Les endpoints publics Solana sont rate-limités et non destinés à la production. Un échec réseau externe n'est pas assimilé automatiquement à une régression locale ; les fixtures HTTP locales restent les gates reproductibles.

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-onchain-transport-lib/tests/release_completeness.rs
// version: 5
// version: 6
//! Release-level completeness canaries for the `0.2.1` HTTP foundation contract.
@@ -143,3 +143,54 @@ fn release_pre_006_completes_cluster_subset_exactly_and_retry_safe() {
assert_eq!(actual, expected);
assert_eq!(actual.len(), 12);
}
#[test]
fn release_pre_007_v0_2_2_exact_set_is_complete_without_advancing_future_releases() {
let mut expected = std::vec![
"getAccountInfo",
"getClusterNodes",
"getEpochInfo",
"getEpochSchedule",
"getHighestSnapshotSlot",
"getIdentity",
"getLargestAccounts",
"getLeaderSchedule",
"getMaxRetransmitSlot",
"getMaxShredInsertSlot",
"getMinimumBalanceForRentExemption",
"getMultipleAccounts",
"getProgramAccounts",
"getSlot",
"getSlotLeader",
"getSlotLeaders",
"getTokenAccountBalance",
"getTokenAccountsByDelegate",
"getTokenAccountsByOwner",
"getTokenLargestAccounts",
"getTokenSupply",
"getVoteAccounts",
];
let mut actual = std::vec::Vec::new();
let mut future_transactions = 0_usize;
let mut future_blocks_economics = 0_usize;
for descriptor in ksp_onchain_transport_lib::current_http_rpc_methods() {
match descriptor.coverage_release() {
ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_2 => {
actual.push(descriptor.method());
assert_eq!(descriptor.documentation_status(), ksp_onchain_transport_lib::RpcDocumentationStatus::Stable);
assert_eq!(descriptor.runtime_status(), ksp_onchain_transport_lib::RpcRuntimeStatus::Supported);
assert_eq!(descriptor.operation_kind(), ksp_onchain_transport_lib::RpcOperationKind::Read);
assert_eq!(descriptor.transport_retry_class(), ksp_onchain_transport_lib::TransportRetryClass::RetrySafe);
},
ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_3 => future_transactions += 1,
ksp_onchain_transport_lib::HttpRpcCoverageRelease::V0_2_4 => future_blocks_economics += 1,
_ => {},
}
}
actual.sort_unstable();
expected.sort_unstable();
assert_eq!(actual, expected);
assert_eq!(actual.len(), 22);
assert_eq!(future_transactions, 11);
assert_eq!(future_blocks_economics, 15);
}

View File

@@ -0,0 +1,84 @@
// file: crates/ksp-onchain-transport-lib/tests/transport_devnet_smoke.rs
// version: 1
//! Opt-in live Devnet smoke for the pure Transport Accounts/Tokens/Cluster path.
fn devnet_pool() -> ksp_onchain_transport_lib::HttpTransportPool {
let role = ksp_onchain_transport_lib::HttpRoleName::new("default");
let role_settings = ksp_onchain_transport_lib::HttpEndpointRoleSettings::new(
role,
true,
std::vec![ksp_onchain_transport_lib::HttpRequestKind::wildcard()],
0,
ksp_onchain_transport_lib::HttpRoleLimits::new(
std::option::Option::None,
std::option::Option::None,
std::option::Option::None,
std::option::Option::None,
),
);
let endpoint_url =
ksp_onchain_transport_lib::HttpEndpointUrl::parse("https://api.devnet.solana.com").expect("official Solana Devnet endpoint URL must remain valid");
let endpoint = ksp_onchain_transport_lib::HttpEndpointSettings::new(
"solana_devnet_public",
true,
ksp_onchain_transport_lib::HttpProviderName::new("solana-public"),
ksp_onchain_transport_lib::HttpClusterName::new("devnet"),
endpoint_url,
std::time::Duration::from_secs(5),
std::time::Duration::from_secs(20),
std::option::Option::Some(4),
std::vec![role_settings],
);
let settings = ksp_onchain_transport_lib::HttpTransportSettings::new(
std::vec![endpoint],
ksp_onchain_transport_lib::HttpRetrySettings::new(1, std::time::Duration::from_millis(250), std::time::Duration::from_secs(2)),
);
return ksp_onchain_transport_lib::HttpTransportPool::new(settings).expect("programmatic Devnet Transport settings must construct a pool");
}
#[tokio::test(flavor = "current_thread")]
#[ignore = "opt-in live Solana Devnet smoke; performs external network requests"]
async fn programmatic_devnet_transport_reaches_accounts_tokens_and_cluster_wrappers() {
let pool = devnet_pool();
let role = ksp_onchain_transport_lib::HttpRoleName::new("default");
let account_config = ksp_onchain_transport_lib::SolanaAccountInfoConfig::new(
std::option::Option::Some(ksp_onchain_transport_lib::SolanaAccountEncoding::Base64),
std::option::Option::None,
std::option::Option::Some(ksp_onchain_transport_lib::SolanaCommitment::Finalized),
std::option::Option::None,
);
let account = pool
.get_account_info(&role, &ksp_core_lib::PRGIDPK_SOLANA_SYSTEM, std::option::Option::Some(&account_config))
.await
.expect("Devnet getAccountInfo smoke must succeed");
assert!(account.context().slot() > 0);
assert!(account.value().is_some());
// Current official getTokenSupply documentation uses this Devnet mint in its example.
let token_mint = "3wyAj7Rt1TWVPZVteFJPLa26JmLvdb1CAKEFZm3NY75E"
.parse::<ksp_core_lib::Pubkey>()
.expect("official Devnet token mint example must remain a valid Pubkey literal");
let token_supply = pool
.get_token_supply(&role, &token_mint, std::option::Option::None)
.await
.expect("Devnet getTokenSupply smoke must succeed for the documented mint");
assert!(token_supply.context().slot() > 0);
assert!(!token_supply.value().amount().is_empty());
let epoch = pool
.get_epoch_info(
&role,
std::option::Option::Some(&ksp_onchain_transport_lib::SolanaContextConfig::new(
std::option::Option::Some(ksp_onchain_transport_lib::SolanaCommitment::Finalized),
std::option::Option::None,
)),
)
.await
.expect("Devnet getEpochInfo smoke must succeed");
assert!(epoch.absolute_slot() > 0);
let vote_accounts = pool.get_vote_accounts(&role, std::option::Option::None).await.expect("Devnet getVoteAccounts smoke must succeed");
assert!(!vote_accounts.current().is_empty() || !vote_accounts.delinquent().is_empty());
}