ksp-onchain-transport-lib
ksp-onchain-transport-lib est la bibliothèque KSP propriétaire du transport on-chain Solana. Sa première surface est le transport HTTP JSON-RPC ; les extensions WebSocket et gRPC sont introduites séparément lorsque leur 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 explicitement livrés par KSP ;
- 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 :
ksp-config-lib
-> ksp-onchain-transport-lib
-> ksp-core-lib
-> ksp-logging-lib
-> reqwest / tokio / serde
-> tokio-tungstenite / futures-util
La direction inverse est interdite :
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 :
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 release stable 0.2.4 complète la surface typée des 52 méthodes courantes :
0.2.1 foundation : 4
0.2.2 Accounts/Tokens/Cluster : 22
0.2.3 Transactions : 11
0.2.4 Blocks/Economics : 15
total : 52
0.2.4 ajoute les dix wrappers Blocks et les cinq wrappers Economics. Les points sensibles restent 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. Le réaudit final 0.2.4 agrège le réaudit 37/37 de 0.2.3 avec les 15 nouveaux wrappers et porte la preuve stable à 52/52.
Les 14 méthodes historiques restent découvrables pour la compliance mais sont Removed et ne sont pas simulées comme appelables.
Foundation WebSocket 0.2.7-pre.004
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
mpscborné ; - maintient une map bornée de requests JSON-RPC en attente ;
- publie
WsSessionSnapshotvia un état compactwatch; - 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
Pingreçus et tolère lesPong; le lifecycle completClose/shutdown a été durci ensuite enpre.005.
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 subscriptions et le mapping remote/local sont matérialisés en pre.006, reconnect/resubscribe est actif depuis pre.007 et le backpressure borné par subscription est effectif depuis pre.008.
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.
Durcissement 0.2.7-pre.005
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é. Depuis pre.007, 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 0.2.7-pre.006
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) jusqu'aux wrappers standard des tranches pre.009+. Le handle public WsSubscription<T> expose uniquement :
id()etkind();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 0.2.7-pre.007
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é 0.2.7-pre.008
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.
Résilience
L'admission est calculée par couple endpoint/rôle. Le pool applique :
- rôle et capability ;
- priorité croissante ;
- round-robin dans le meilleur tier ;
- RPS/burst ;
- concurrence ;
- cooldown rate-limit ;
- fallback vers les pairs puis les tiers inférieurs ;
- 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 :
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.
Deux smokes Devnet opt-in sont séparés par responsabilité :
Transport pur : settings programmatiques -> HttpTransportPool
-> Accounts/Tokens/Cluster représentatifs
-> trois reads Transactions
-> getBlockHeight
-> getInflationRate/getStakeMinimumDelegation
Composition historique : Config -> std.transport/devnet_public -> HttpTransportPool
-> getHealth/getGenesisHash/getVersion/getBalance
Le smoke 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.
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— consommation directe, Config -> Transport, API typed/raw, smokes et inspection runtime ;../../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— extension typed Accounts/Tokens/Cluster ;../../docs/validation/004-V0_2_2_HTTP_ACCOUNTS_TOKENS_CLUSTER.md— matrice finale validée0.2.2;../../docs/plans/010-V0_2_3_HTTP_TRANSACTIONS_PLAN.md— plan historique clôturé de0.2.3;../../docs/validation/006-V0_2_3_HTTP_TRANSACTIONS.md— matrice finale validée0.2.3;../../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— matrice finale validée52/52 + 14/14et auditKSP-TRANSPORT-007global ;../../config/std.transport.json— configuration standard HTTP + WebSocket V2, avec lecture backward V1 HTTP-only.