Files
khadhroony-solana-project/deltas/0.2.7/pre.006.md
2026-08-22 19:56:20 +02:00

8.6 KiB

Delta 0.2.7-pre.006 — registry subscriptions + IDs locaux + moteur typed générique

1. Base requise

0.2.7-pre.005 appliqué
workspace.package.version = 0.2.7-pre.5

Le checkpoint opérateur reçu avant cette tranche est vert : cargo fmt --all, audit Python, cargo check --workspace, cargo clippy --workspace --all-targets, cargo test -p ksp-onchain-transport-lib et cargo test --workspace. Les 269 tests unitaires Transport de pre.005 passent.

2. Objectif

Cette tranche ajoute le registry de subscriptions au même actor que le socket et la pending map JSON-RPC, sans avancer sur reconnect/resubscribe.

Objectifs matérialisés :

1 session physique
 -> 0..N subscriptions logiques
 -> WsSubscriptionId local stable
 -> remote subscription id interne/transient
 -> channel de notifications typed et bounded

La création générique de subscription reste crate-private jusqu'aux wrappers standard publics des lots pre.009+.

3. Signal technique

Cette prerelease modifie le runtime Rust, les tests et la documentation. Conformément aux règles KSP :

livraison                 = 0.2.7-pre.006
workspace.package.version = 0.2.7-pre.6
commit                    = v0.2.7-pre.006

Aucun tag prerelease.

4. Registry actor-owned

Le même actor possède maintenant :

socket physique
request counter
pending JSON-RPC map
subscription local counter
subscription registry local
remote_subscription_id -> WsSubscriptionId
safe snapshots

Le registry local est ordonné par local ID. Les IDs locaux sont assignés par l'actor et ne dépendent jamais du remote ID retourné par Solana.

Le remote ID reste absent de l'API publique, de Debug, des snapshots et des logs. Seul remote_bound: bool reste projeté par WsSubscriptionSnapshot.

5. Binding subscribe atomique

Le subscribe n'est pas implémenté comme :

execute_json_rpc
puis register local

car une notification pourrait alors arriver entre les deux opérations.

pre.006 ajoute une command actor Subscribe. Le pending request conserve le local ID et le dispatcher typed. Lorsque la réponse *Subscribe est reçue :

  1. la réponse JSON-RPC est validée ;
  2. le remote ID numérique est validé ;
  3. l'unicité du remote ID actif est vérifiée ;
  4. le remote ID est lié au local ID ;
  5. la subscription passe Requested -> Active ;
  6. seulement ensuite le handle typed est rendu au caller.

Le prochain message socket ne peut donc pas être dispatché avant la mise à jour du mapping actor-owned.

6. WsSubscription<T>

Nouvelle surface publique commune :

WsSubscription<T>
  id()                  -> WsSubscriptionId
  kind()                -> WsSubscriptionKind
  state()               -> WsSubscriptionState
  recv().await          -> Option<Result<T>>
  unsubscribe().await   -> Result<bool>

Le receiver de notifications est borné par notification_queue_capacity.

La création WsSession::subscribe_typed(...) reste pub(crate) : elle sera consommée par les wrappers typed standard et ne crée pas de surface raw provider-extension publique.

7. Dispatch notifications

Pour une notification Solana standard :

JSON-RPC notification
 -> params.subscription remote u64
 -> remote_to_local
 -> WsSubscriptionRuntime
 -> validation notification method
 -> decoder typed
 -> bounded typed channel

Le mapping exact famille/méthodes est centralisé sur WsSubscriptionKind pour les neuf familles :

account
block
logs
program
root
signature
slot
slotsUpdates
vote

Chaque famille possède son triplet exact *Subscribe, *Unsubscribe, *Notification.

8. Anomalies isolées

Politique matérialisée dans cette tranche :

  • remote ID inconnu/stale : safe drop + diagnostic sûr, session inchangée ;
  • notification method incompatible avec la famille enregistrée : subscription Failed, session reste Active ;
  • typed decoder failure : erreur livrée au channel lorsque possible, subscription Failed, session reste Active ;
  • queue typed déjà pleine : aucune perte silencieuse considérée normale, subscription terminale ; le compteur/cleanup adversarial complet est finalisé en pre.008 ;
  • malformed JSON / structure JSON-RPC invalide : reste une anomalie de session selon le contrat acquis.

Aucun reconnect n'est déclenché par une erreur RPC applicative ou une erreur typed locale.

9. Unsubscribe

WsSubscription<T>::unsubscribe() ne reçoit jamais le remote ID du caller.

L'actor :

  1. marque la subscription Cancelling ;
  2. retire immédiatement le remote ID du mapping de dispatch local ;
  3. construit le *Unsubscribe exact avec le remote ID interne ;
  4. valide la réponse booléenne ;
  5. publie Closed et ferme le channel local.

Le booléen standard Solana est préservé au caller afin de ne pas perdre une variante de réponse pertinente.

Les races unsubscribe/reconnect et le principe « local cancellation wins » sous reconnexion restent le scope explicite de pre.007.

10. Tests déterministes

Ajouts principaux :

subscribe slot -> remote binding -> notification typed -> unsubscribe exact
2 familles -> 2 IDs locaux ordonnés + remote IDs indépendants
unknown remote ID -> notification suivante valide toujours dispatchée
notification method mismatch -> seule la subscription échoue
typed decode error -> erreur receiver + seule la subscription échoue
triplets exacts des 9 familles standard
public API canary WsSubscription<T>

Le serveur reste exclusivement local et déterministe. Aucun réseau Solana réel n'est requis.

11. Logging et sécurité

Toutes les émissions passent exclusivement par ksp-logging-lib avec :

TRACING_TARGET = "ksp-onchain-transport-lib"

Les diagnostics n'exposent que :

session_id
subscription_id local
subscription_kind
endpoint logique
counts/états sûrs

Aucun remote subscription ID n'est journalisé comme identité métier, et aucune URL, credential ou notification brute n'est loggée.

12. Fichiers ajoutés

crates/ksp-onchain-transport-lib/src/ws_subscription.rs
deltas/0.2.7/pre.006.md

13. Fichiers modifiés

Cargo.toml
crates/ksp-onchain-transport-lib/README.md
crates/ksp-onchain-transport-lib/USAGE.md
crates/ksp-onchain-transport-lib/src/lib.rs
crates/ksp-onchain-transport-lib/src/ws_lifecycle.rs
crates/ksp-onchain-transport-lib/src/ws_session.rs
crates/ksp-onchain-transport-lib/tests/public_api.rs
crates/ksp-onchain-transport-lib/unit_tests/ws_lifecycle.rs
crates/ksp-onchain-transport-lib/unit_tests/ws_session.rs
docs/plans/014-V0_2_7_ONCHAIN_WEBSOCKET_PLAN.md
docs/validation/010-V0_2_7_ONCHAIN_WEBSOCKET.md

14. Fichiers supprimés

Aucun.

ROADMAP.md et CHANGELOG.md restent inchangés pendant la série prerelease.

15. Validation exécutée dans le sandbox

python3 scripts/audit_rust_workspace_rules.py
General Rust rule audit: clean
Rust export completeness audit: 0 candidate(s)
KSP workspace Rust rule audit: clean

Contrôles statiques supplémentaires :

workspace.package.version = 0.2.7-pre.6
tracing direct             = absent
question-mark runtime      = absent
remote ID public           = absent
lignes Rust > 160 ajoutées = absentes

16. Validation non exécutée dans le sandbox

Cargo/rustfmt ne sont pas disponibles dans le sandbox de génération. Aucun résultat local n'est revendiqué pour :

cargo fmt --all
cargo check --workspace
cargo clippy --workspace --all-targets
cargo test -p ksp-onchain-transport-lib
cargo test --workspace

17. Gates opérateur avant commit

cargo fmt --all
python3 scripts/audit_rust_workspace_rules.py
cargo check --workspace
cargo clippy --workspace --all-targets
cargo test -p ksp-onchain-transport-lib
cargo test --workspace

Si le checkpoint est vert :

commit = v0.2.7-pre.006

La tranche suivante est 0.2.7-pre.007 : reconnect borné, resubscribe déterministe, continuity gap et races unsubscribe/reconnect.

18. Décisions / questions ouvertes

Décisions :

  • le registry et le mapping remote/local appartiennent exclusivement à l'actor ;
  • le local ID est stable et le remote ID est transient/interne ;
  • la création générique typed reste crate-private ;
  • le handle typed public possède le receiver et l'unsubscribe ;
  • une anomalie typed connue ne doit pas faire tomber la session physique ;
  • aucune promesse lossless n'est introduite.

Questions laissées aux tranches suivantes :

  • pre.007 finalise la restauration déterministe après reconnexion et les races cancellation/ACK ;
  • pre.008 finalise overflow counters, cleanup best-effort et leak/lifecycle adversarial sous slow consumer.