328 lines
25 KiB
Markdown
328 lines
25 KiB
Markdown
<!-- file: crates/ksp-onchain-transport-lib/README.md -->
|
||
<!-- version: 21 -->
|
||
|
||
# `ksp-onchain-transport-lib`
|
||
|
||
`ksp-onchain-transport-lib` est la bibliothèque KSP propriétaire du transport on-chain Solana. Elle fournit le transport HTTP JSON-RPC complet, le moteur WebSocket Solana standard et la foundation Yellowstone gRPC standard/provider-neutral. Les extensions provider-specific restent ajoutées séparément lorsqu’une release les cible.
|
||
|
||
## Responsabilités
|
||
|
||
La crate possède :
|
||
|
||
- les settings runtime HTTP publics ;
|
||
- les endpoints nommés et leurs metadata provider/cluster ;
|
||
- les rôles, capabilities/request kinds et priorités ;
|
||
- la sélection/fairness/fallback du pool ;
|
||
- les limites RPS/burst/concurrence et le cooldown ;
|
||
- les deadlines et timeouts ;
|
||
- le retry/backoff borné et la règle no-resend après dispatch ambigu ;
|
||
- les enveloppes JSON-RPC 2.0 et leur validation ;
|
||
- le registre audité des méthodes Solana HTTP ;
|
||
- l'exécution générique des méthodes standard supportées ;
|
||
- les wrappers typés HTTP et WebSocket explicitement livrés par KSP ;
|
||
- les sessions physiques WebSocket, subscriptions logiques, reconnect/resubscribe et backpressure bornés ;
|
||
- les settings, channels, unary et sessions `Subscribe` Yellowstone gRPC standard ;
|
||
- le reconnect/replay Yellowstone prudent avec observabilité de gaps/duplicates sans promesse lossless ;
|
||
- les snapshots runtime sûrs ;
|
||
- l'observabilité Transport via `ksp-logging-lib`.
|
||
|
||
La crate ne possède ni documents Config, ni persistence Store, ni modèles Program/métier.
|
||
|
||
## Frontières de dépendances
|
||
|
||
La direction autorisée est :
|
||
|
||
```text
|
||
ksp-config-lib
|
||
-> ksp-onchain-transport-lib
|
||
-> ksp-core-lib
|
||
-> ksp-logging-lib
|
||
-> reqwest / tokio / serde
|
||
-> tokio-tungstenite / futures-util
|
||
-> tonic / tonic-prost / yellowstone-grpc-proto
|
||
```
|
||
|
||
La direction inverse est interdite :
|
||
|
||
```text
|
||
ksp-onchain-transport-lib -X-> ksp-config-lib
|
||
ksp-onchain-transport-lib -X-> Store
|
||
ksp-onchain-transport-lib -X-> Program
|
||
ksp-onchain-transport-lib -X-> tracing direct
|
||
```
|
||
|
||
`ksp-config-lib` peut donc charger `std.transport.json` et construire `HttpTransportSettings`, tandis que Transport reste directement utilisable par un consumer qui fournit lui-même ses settings.
|
||
|
||
## Surface HTTP standard
|
||
|
||
Le registre KSP conserve deux inventaires distincts :
|
||
|
||
```text
|
||
52 méthodes HTTP courantes
|
||
14 méthodes historiques Deprecated / runtime Removed
|
||
```
|
||
|
||
Le registre porte notamment :
|
||
|
||
- catégorie ;
|
||
- request kind ;
|
||
- statut documentaire ;
|
||
- statut runtime ;
|
||
- forme de requête stable/legacy ;
|
||
- type d'opération ;
|
||
- classe de retry ;
|
||
- remplacement historique éventuel ;
|
||
- release de couverture typée KSP.
|
||
|
||
La surface HTTP typée couvre les **52 méthodes courantes** :
|
||
|
||
```text
|
||
foundation : 4
|
||
Accounts/Tokens/Cluster : 22
|
||
Transactions : 11
|
||
Blocks/Economics : 15
|
||
total : 52
|
||
```
|
||
|
||
Les dix wrappers Blocks et les cinq wrappers Economics couvrent notamment `getBlock` moderne + bare encoding legacy deprecated, les quatre `transactionDetails`, les versions transaction numériques génériques, `numRewardPartitions`, `commissionBps`, les overloads de `getBlocks`, les ranges de production, les valeurs d'inflation/minimum de délégation fournies par le runtime et les `null` positionnels de `getInflationReward`.
|
||
|
||
`KSP-TRANSPORT-007` impose qu'un wrapper typé couvre toutes les possibilités RPC supportées retenues par l'audit : paramètres/options, overloads et formes legacy encore supportées, contraintes déterministes utiles et variantes de réponse pertinentes sans perte. Les canaris de release portent la preuve globale à **52/52** méthodes courantes typées.
|
||
|
||
Les 14 méthodes historiques restent découvrables pour la compliance mais sont `Removed` et ne sont pas simulées comme appelables.
|
||
|
||
## Moteur WebSocket standard
|
||
|
||
La première session physique WebSocket est matérialisée sans introduire de pool/scheduler automatique ni de registry de subscriptions anticipé.
|
||
|
||
`WsSession::connect(WsEndpointSettings)` :
|
||
|
||
- ouvre exactement une connexion physique pour un appel ;
|
||
- confie le socket à une tâche actor unique ;
|
||
- sérialise les commandes internes par un canal `mpsc` borné ;
|
||
- maintient une map bornée de requests JSON-RPC en attente ;
|
||
- publie `WsSessionSnapshot` via un état compact `watch` ;
|
||
- applique aux sockets les plafonds KSP de message, frame et write buffer ;
|
||
- ne projette jamais l'URL dans `Debug`, snapshot, erreurs KSP ou logs ;
|
||
- répond aux `Ping` reçus et tolère les `Pong`; le lifecycle `Close`/shutdown est borné et explicite.
|
||
|
||
Le chemin JSON-RPC générique reste `pub(crate)`. Il sert de primitive au moteur typed de subscriptions et **ne constitue pas une API publique raw provider-extension**. Le registry de subscriptions et le mapping remote/local, le reconnect/resubscribe et le backpressure borné par subscription font partie du moteur standard.
|
||
|
||
Les tests déterministes utilisent un serveur WebSocket local et prouvent le handshake, le round-trip JSON-RPC, le dispatch de réponses hors ordre, l'isolation des erreurs RPC applicatives, deux sessions physiques distinctes sur la même URL et la redaction des erreurs de connexion.
|
||
|
||
### Limites, control frames et shutdown
|
||
|
||
La session physique dispose désormais de `WsSession::close().await`. Le signal de shutdown est distinct de la command queue, passe l'état en `Closing`, annule les requests JSON-RPC en attente, envoie un Close WebSocket best-effort sous `close_timeout`, puis publie `Closed`. Un peer qui ne répond pas au Close ne peut donc pas bloquer indéfiniment le shutdown.
|
||
|
||
Les limites `max_message_size`, `max_frame_size`, `max_write_buffer_size` et `max_pending_requests` sont couvertes par des fixtures adversariales locales. Les requests outbound qui dépassent les bornes message/frame sont rejetées avant écriture ; les frames/messages inbound surdimensionnés sont rejetés par Tungstenite avant parse JSON. Les timeouts pending libèrent leur capacité sans faire tomber une session encore saine.
|
||
|
||
Ping/Pong/Close sont traités comme control frames : le Pong automatique Tungstenite est flushé et aucun heartbeat applicatif périodique n'est ajouté. Un Close distant inattendu, EOF, erreur I/O/TLS/WebSocket ou violation protocolaire structurelle entre dans le reconnect borné ; `Closed` reste réservé au shutdown local explicite ou à la disparition des handles.
|
||
|
||
### Registry de subscriptions
|
||
|
||
Le même actor possède maintenant le registre des subscriptions logiques, sans exposer les IDs numériques distants. Chaque subscription reçoit un `WsSubscriptionId` local stable, et le mapping `remote_subscription_id -> WsSubscriptionId` reste strictement runtime/interne.
|
||
|
||
La création générique typed reste `pub(crate)` et n'est jamais exposée comme API raw provider-extension. Les wrappers standard publics l’utilisent derrière leurs DTOs et paramètres typés. Le handle public `WsSubscription<T>` expose uniquement :
|
||
|
||
- `id()` et `kind()` ;
|
||
- `state()` ;
|
||
- `recv()` sur un canal typed borné ;
|
||
- `unsubscribe()` qui conserve le booléen retourné par l'unsubscribe Solana standard.
|
||
|
||
L'ACK de subscribe est traité atomiquement dans l'actor : le remote ID est lié au local ID avant que la notification suivante puisse être dispatchée. Les notifications inconnues/stale sont ignorées avec un diagnostic sûr. Un mismatch de méthode de notification ou un échec de décodage typed termine uniquement la subscription concernée ; la session physique reste `Active`.
|
||
|
||
### Reconnect et resubscribe
|
||
|
||
Une perte de connexion physique invalide immédiatement les remote subscription IDs et incrémente `continuity_gap_count`. Le runtime utilise `WsReconnectSettings` pour appliquer un nombre fini de tentatives avec backoff exponentiel borné et sans jitter. Le shutdown surveille les phases de backoff et de handshake et interrompt la reprise sans reconnecter uniquement pour nettoyer des subscriptions.
|
||
|
||
Avec `WsResubscribePolicy::ActiveSubscriptions`, les subscriptions encore désirées passent en `Resubscribing` et sont restaurées dans l'ordre croissant de leur `WsSubscriptionId`. Les paramètres de subscribe conservés par l'actor sont rejoués, puis chaque nouvel ACK remappe un remote ID sans changer l'identité locale. Le retour à `Active` ne se produit qu'après la fin de cette restauration, ce qui réinitialise alors le budget de reconnect.
|
||
|
||
Avec `WsResubscribePolicy::Never`, la session physique peut se reconnecter mais les subscriptions précédentes deviennent terminales. Une cancellation locale reçue pendant reconnect gagne toujours : elle retire la subscription de la restauration ; si un ACK distant arrive après cette cancellation, l'actor envoie un unsubscribe best-effort du nouvel ID sans réactiver le handle local.
|
||
|
||
Le compteur de continuity gaps est un signal d'observabilité, pas une garantie de livraison. Transport n'ajoute aucun backfill HTTP et ne promet aucune continuité lossless pendant l'intervalle de déconnexion.
|
||
|
||
### Backpressure et libération de capacité
|
||
|
||
Chaque subscription dispose de sa propre queue typed bornée par `notification_queue_capacity`. Le runtime ne droppe jamais silencieusement une notification lorsque cette queue est pleine : il incrémente `WsSessionSnapshot::overflow_count()`, fait passer uniquement le handle lent à `Failed`, publie `ERROR_CODE_WS_BACKPRESSURE_OVERFLOW` via `WsSubscription::terminal_error_code()` et programme un `*Unsubscribe` distant best-effort. Les autres subscriptions et la session physique restent utilisables.
|
||
|
||
Les autres terminaisons en échec publient également un code KSP sûr sur le handle : erreur protocolaire, timeout, erreur RPC applicative ou perte physique terminale. Les fermetures normales et les unsubscriptions réussis conservent `terminal_error_code() == None`. Aucun payload distant, remote subscription ID ou endpoint URL n'est projeté dans cette cause.
|
||
|
||
`max_active_subscriptions` reste une limite d'admission distincte du compteur d'overflow de notifications : un rejet de création ne l'incrémente pas. Lorsqu'une subscription est fermée, échoue ou que son receiver est abandonné puis détecté sur la notification suivante, son entrée runtime et son binding distant sont nettoyés et la capacité locale redevient réutilisable.
|
||
|
||
Les fixtures adversariales prouvent l'isolation d'un consumer lent, la survie d'une subscription saine, le cleanup distant best-effort, la réutilisation de capacité après unsubscribe ou abandon du receiver et la conservation du compteur d'overflow à travers les snapshots. Aucune promesse de livraison lossless n'est ajoutée.
|
||
|
||
### Wrappers stables : account, program et logs
|
||
|
||
Les trois premiers wrappers WebSocket standards sont publics sur `WsSession` :
|
||
|
||
```text
|
||
account_subscribe -> WsSubscription<SolanaRpcResponse<SolanaAccount>>
|
||
program_subscribe -> WsSubscription<SolanaProgramNotification>
|
||
logs_subscribe -> WsSubscription<SolanaRpcResponse<SolanaLogsNotification>>
|
||
```
|
||
|
||
`SolanaAccountSubscribeConfig` expose uniquement les options réellement effectives du PubSub audité : `encoding`, `dataSlice` et `commitment`. `minContextSlot` reste volontairement absent car le handler Agave ciblé l'ignore pour `accountSubscribe`; KSP ne transforme donc pas un champ partagé mais inopérant en promesse WebSocket.
|
||
|
||
`SolanaProgramSubscribeConfig` réutilise les encodings, slices et commitments account, ajoute les filtres programme et conserve `withContext`. Le décodeur `SolanaProgramNotification` accepte aussi bien le keyed account non contexté que la forme `RpcResponse` contextée afin de préserver les deux formes retenues par l'audit sans perte. `sortResults`, présent sur la surface HTTP `getProgramAccounts`, n'est pas exposé ici car le handler PubSub audité ne le consomme pas.
|
||
|
||
`SolanaLogsSubscribeFilter` rend les trois filtres upstream explicites : `All`, `AllWithVotes` et `Mentions(Pubkey)`. La variante `Mentions` encode par construction exactement une adresse. `SolanaLogsNotification` conserve la signature opaque, le `err` nullable et l'ordre des messages `logs`, enveloppés dans `SolanaRpcResponse`.
|
||
|
||
L'unsubscribe de ces trois familles passe toujours par `WsSubscription::unsubscribe()`: le caller ne voit ni ne fournit l'ID serveur. Les paramètres initiaux restent conservés par l’actor pour le resubscribe déterministe, et toutes les règles de backpressure/terminal error s’appliquent sans branche spéciale aux DTOs publics.
|
||
|
||
### Wrappers stables : signature, slot et root
|
||
|
||
Le second lot stable complète les subscriptions standard non instables :
|
||
|
||
```text
|
||
signature_subscribe -> WsSubscription<SolanaRpcResponse<SolanaSignatureNotification>>
|
||
slot_subscribe -> WsSubscription<SolanaSlotNotification>
|
||
root_subscribe -> WsSubscription<u64>
|
||
```
|
||
|
||
`SolanaSignatureSubscribeConfig` conserve séparément `commitment` et `enableReceivedNotification`, y compris la différence entre option omise et booléen explicitement faux. `SolanaSignatureNotification` représente les deux formes wire : `ReceivedSignature` pour l'événement précoce optionnel et `Processed { err }` pour la notification terminale. Après livraison de `Processed`, l'actor ferme localement la subscription avec `terminal_error_code() == None`, retire son binding et ne la remet jamais dans le set de resubscribe, conformément au caractère one-shot du serveur Solana.
|
||
|
||
Une cancellation effectuée avant cette terminaison continue d'utiliser `WsSubscription::unsubscribe()` et émet `signatureUnsubscribe` avec le remote ID détenu uniquement par l'actor. Après la notification terminale, `unsubscribe()` devient local-only et retourne `false`, puisque la subscription est déjà fermée côté serveur et côté KSP.
|
||
|
||
`slot_subscribe()` et `root_subscribe()` n'acceptent aucun paramètre. `SolanaSlotNotification` conserve exactement `slot`, `parent` et `root`; `root_subscribe()` délivre directement le root `u64`. Ces deux subscriptions restent continues et utilisent donc le reconnect/resubscribe standard.
|
||
|
||
### Wrappers unstable : block, slotsUpdates et vote
|
||
|
||
Les trois familles standard restantes complètent désormais l'inventaire **9/9 subscribe + 9/9 unsubscribe via handles** :
|
||
|
||
```text
|
||
block_subscribe -> WsSubscription<SolanaRpcResponse<SolanaBlockNotification>>
|
||
slots_updates_subscribe -> WsSubscription<SolanaSlotUpdate>
|
||
vote_subscribe -> WsSubscription<SolanaVoteNotification>
|
||
```
|
||
|
||
Ces familles restent explicitement **unstable**. Le moteur commun `subscribe_typed_with_completion` émet un warning KSP centralisé pour `Block`, `SlotsUpdates` et `Vote` avant l'ouverture logique, sans recopier filtres, pubkeys, payloads ou URL dans les logs.
|
||
|
||
`SolanaBlockSubscribeConfig` conserve `commitment`, `encoding`, `transactionDetails`, `maxSupportedTransactionVersion` et `showRewards`. Le filtre représente `All` ou `MentionsAccountOrProgram(Pubkey)`. Un commitment `processed` explicitement fourni est rejeté avant I/O ; la notification réutilise `SolanaConfirmedBlock` pour le block nullable et conserve l'erreur publication nullable sans interprétation métier. Le numéro de version transaction supporté reste un `u8` générique et n'est pas durci à `0`.
|
||
|
||
`SolanaSlotUpdate` représente les sept variantes courantes `firstShredReceived`, `completed`, `createdBank`, `frozen`, `dead`, `optimisticConfirmation` et `root`. Une variante upstream inconnue devient `Unknown { update_type, raw }` au lieu de faire tomber la session. Le `raw` reste borné par `max_message_size_bytes` avant le parse JSON.
|
||
|
||
`SolanaVoteNotification` conserve `votePubkey`, `slots`, `hash`, `timestamp` et `signature`. Le timestamp reste optionnel : omission et `null` deviennent `None`, tandis qu'une valeur `i64` est préservée. Transport ne transforme pas ces votes gossip pre-consensus en vérité ledger.
|
||
|
||
## Helius LaserStream WebSocket
|
||
|
||
La façade `HeliusLaserStreamWsSession` utilise le même actor physique `WsSession` mais expose uniquement la surface provider actuellement retenue par l’audit Helius :
|
||
|
||
```text
|
||
standard réutilisé : account / logs / program / root / signature / slot / slotsUpdates
|
||
extension Helius : transactionSubscribe / transactionUnsubscribe
|
||
absent Helius : block / vote
|
||
```
|
||
|
||
`slotsUpdates` reste **unstable** et conserve le warning centralisé du moteur standard. `block` et `vote` restent absents de la façade Helius même s’ils existent sur la façade Solana standard. `transactionSubscribe` reste provider-specific et n’est jamais ajouté à `SolanaStandardWsSession`.
|
||
|
||
Les endpoints Helius mainnet/devnet utilisent un `api-key` dans l’URL. KSP recommande de les construire via `ksp-config-lib` et `KSP_SECRET_HELIUS_API_KEY`; la valeur réelle atteint Transport mais les projections sûres, `Debug`, snapshots, erreurs et diagnostics n’exposent pas le credential. Transport ne lit jamais l’environnement et ne dépend jamais de Config.
|
||
|
||
Pour Helius, l’actor envoie automatiquement un control frame WebSocket `Ping` toutes les 60 secondes sur une session active. Cette policy est provider-owned, non configurable et ne s’applique pas aux sessions `SolanaStandard`. Une perte physique suit le reconnect/resubscribe borné déjà décrit; aucun replay/lossless n’est promis par la couche WebSocket.
|
||
|
||
LaserStream **gRPC** reste un backend distinct, hors de cette façade, de `WsProtocolKind` et de la Config WebSocket `helius_laserstream`.
|
||
|
||
|
||
## Yellowstone gRPC standard
|
||
|
||
La foundation `0.2.9` ajoute un troisième backend réseau distinct de HTTP et WebSocket. Le moteur est KSP-owned : `yellowstone-grpc-proto` fournit le wire publié, tandis que Tonic reste encapsulé derrière les types crate-root KSP. Aucun client Tonic brut ni type protobuf upstream n’est réexporté.
|
||
|
||
La surface publique principale comprend :
|
||
|
||
```text
|
||
YellowstoneGrpcEndpointUrl / YellowstoneGrpcEndpointSettings
|
||
YellowstoneGrpcSessionSettings / YellowstoneGrpcReconnectSettings
|
||
YellowstoneGrpcTransportSettings
|
||
YellowstoneGrpcChannel
|
||
SolanaYellowstoneGrpcUnaryClient
|
||
YellowstoneSubscribeRequest
|
||
SolanaYellowstoneGrpcSubscribeSession
|
||
YellowstoneGrpcSubscribeSnapshot
|
||
```
|
||
|
||
Les sept unary standards retenus sont `SubscribeReplayInfo`, `Ping`, `GetLatestBlockhash`, `GetBlockHeight`, `GetSlot`, `IsBlockhashValid` et `GetVersion`. `Subscribe` couvre accounts, slots, transactions, transaction status, blocks, block metadata et entries, avec `commitment`, `accounts_data_slice`, `ping` et `from_slot`. `SubscribeDeshred` reste hors scope de la foundation standard.
|
||
|
||
Le stream bidirectionnel est borné : request/update queues, tailles inbound/outbound, half-close, close timeout et reconnect budget sont explicites. Après reconnect, KSP rejoue la dernière requête complète acceptée et avance prudemment `from_slot` selon le dernier slot observé et `SubscribeReplayInfo.first_available`. Les compteurs de gap et duplicate sont de l’observabilité ; ils ne constituent jamais une garantie exactly-once ou lossless.
|
||
|
||
Config Transport V3 peut mapper des `grpc_endpoints` vers ces settings sans inverser la dépendance. `protocol = solana_yellowstone` décrit le wire standard, tandis que `provider` reste un descripteur distinct. Le profil committé `publicnode_mainnet` utilise le standard sans credential ni façade PublicNode spécifique.
|
||
|
||
Un smoke live opt-in vérifie directement PublicNode Mainnet via `GetVersion` puis `GetSlot`. Le hostname PublicNode Testnet Yellowstone n’est pas versionné tant qu’une source officielle exploitable ne l’a pas confirmé ; KSP ne déduit pas un endpoint à partir d’une convention de nommage.
|
||
|
||
## Résilience
|
||
|
||
L'admission est calculée par couple endpoint/rôle. Le pool applique :
|
||
|
||
1. rôle et capability ;
|
||
2. priorité croissante ;
|
||
3. round-robin dans le meilleur tier ;
|
||
4. RPS/burst ;
|
||
5. concurrence ;
|
||
6. cooldown rate-limit ;
|
||
7. fallback vers les pairs puis les tiers inférieurs ;
|
||
8. deadline commune à l'opération et ses retries.
|
||
|
||
Les retries ne sont autorisés que lorsque la metadata de méthode et l'état de dispatch les rendent sûrs. `WriteSubmission / NeverAfterDispatch` interdit tout resend automatique après un dispatch ambigu.
|
||
|
||
Les erreurs JSON-RPC applicatives ne sont pas transformées en retries transport génériques.
|
||
|
||
## Sécurité et diagnostics
|
||
|
||
Les URLs d'endpoint peuvent contenir des credentials. Elles ne sont donc pas exposées par les `Debug`, snapshots ou logs ordinaires.
|
||
|
||
Les `reqwest::Error` attachées comme source sont neutralisées avec `without_url()` avant exposition dans le contrat d'erreur KSP.
|
||
|
||
Le target de tracing est possédé explicitement par :
|
||
|
||
```text
|
||
src/constants.rs
|
||
TRACING_TARGET = "ksp-onchain-transport-lib"
|
||
```
|
||
|
||
La configuration Logging de référence conserve un fichier dédié Transport à niveau `info`. Un niveau `debug`/`trace` ciblé peut être réactivé temporairement via Config lors d'un développement ou diagnostic explicite.
|
||
|
||
## Tests
|
||
|
||
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.
|
||
|
||
Quatre smokes réseau opt-in sont séparés par responsabilité :
|
||
|
||
```text
|
||
Transport HTTP pur : settings programmatiques -> HttpTransportPool
|
||
-> Accounts/Tokens/Cluster représentatifs
|
||
-> trois reads Transactions
|
||
-> getBlockHeight
|
||
-> getInflationRate/getStakeMinimumDelegation
|
||
|
||
Transport WebSocket pur : settings programmatiques -> WsSession
|
||
-> slotSubscribe
|
||
-> une slotNotification sous timeout
|
||
-> slotUnsubscribe
|
||
-> close
|
||
|
||
Composition historique : Config -> std.transport/devnet_public -> HttpTransportPool
|
||
-> getHealth/getGenesisHash/getVersion/getBalance
|
||
|
||
Transport Yellowstone gRPC : settings programmatiques -> PublicNode Mainnet
|
||
-> TLS -> GetVersion -> GetSlot confirmed
|
||
```
|
||
|
||
Le smoke HTTP Transport utilise pour sa branche Token la forme Devnet documentée `getTokenAccountsByOwner(owner, { programId }, { commitment: finalized, encoding: jsonParsed })`. L'owner est une Pubkey ordinaire de l'exemple officiel ; aucune présence de token account n'est exigée, donc une liste vide reste valide.
|
||
|
||
Le smoke WebSocket Transport cible uniquement la famille stable `slotSubscribe` sur l'endpoint public Devnet `wss://api.devnet.solana.com`. Il borne connexion, attente de notification, unsubscribe et fermeture ; il ne transforme aucune famille unstable en gate live.
|
||
|
||
Les quatre tests sont `ignored` par défaut. Les trois smokes Transport appartiennent 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. Un rate-limit, refus externe ou incident Devnet n'est pas assimilé automatiquement à une régression locale.
|
||
|
||
Aucun smoke Helius live supplémentaire n’est committé en `0.2.8-pre.010`. Un tel test devrait à la fois obtenir `KSP_SECRET_HELIUS_API_KEY` via Config et exercer Transport ; l’ajouter dans Transport violerait l’ownership environnement/secret, tandis que l’ajouter dans Config étendrait l’exception cross-crates que le projet veut au contraire résorber. La première surface KSP d’intégration/orchestration dédiée devra héberger ce smoke. Le scénario live recommandé est alors `helius_devnet -> HeliusLaserStreamWsSession -> slotSubscribe -> notification -> unsubscribe -> close`; `transactionSubscribe` reste un smoke optionnel dépendant des droits provider et ne devient pas un gate stable de release.
|
||
|
||
## Documentation
|
||
|
||
- [`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 finale validée Accounts/Tokens/Cluster ;
|
||
- [`../../docs/plans/010-V0_2_3_HTTP_TRANSACTIONS_PLAN.md`](../../docs/plans/010-V0_2_3_HTTP_TRANSACTIONS_PLAN.md) — plan historique clôturé Transactions ;
|
||
- [`../../docs/validation/006-V0_2_3_HTTP_TRANSACTIONS.md`](../../docs/validation/006-V0_2_3_HTTP_TRANSACTIONS.md) — matrice finale validée Transactions ;
|
||
- [`../../docs/plans/011-V0_2_4_HTTP_BLOCKS_ECONOMICS_PLAN.md`](../../docs/plans/011-V0_2_4_HTTP_BLOCKS_ECONOMICS_PLAN.md) — plan Blocks/Economics et compliance HTTP finale ;
|
||
- [`../../docs/validation/007-V0_2_4_HTTP_FINAL_COMPLIANCE.md`](../../docs/validation/007-V0_2_4_HTTP_FINAL_COMPLIANCE.md) — matrice finale validée `52/52 + 14/14` et audit `KSP-TRANSPORT-007` global ;
|
||
- [`../../docs/plans/016-V0_2_9_YELLOWSTONE_GRPC_PLAN.md`](../../docs/plans/016-V0_2_9_YELLOWSTONE_GRPC_PLAN.md) — plan Yellowstone gRPC standard/provider-neutral et PublicNode ;
|
||
- [`../../docs/validation/012-V0_2_9_YELLOWSTONE_GRPC.md`](../../docs/validation/012-V0_2_9_YELLOWSTONE_GRPC.md) — matrice de compliance Yellowstone ;
|
||
- [`../../config/std.transport.json`](../../config/std.transport.json) — configuration standard Transport V3 HTTP + WebSocket + gRPC, avec lecture backward V1/V2.
|