From 435126f67af0fee89862f969b7c8e5702a538b70 Mon Sep 17 00:00:00 2001 From: SinuS Von SifriduS Date: Sat, 22 Aug 2026 20:08:55 +0200 Subject: [PATCH] v0.2.7-pre.006-fix.001 --- Cargo.toml | 4 +- .../src/ws_session.rs | 4 +- .../unit_tests/ws_session.rs | 11 +- deltas/0.2.7/pre.006-fix.001.md | 131 ++++++++++++++++++ 4 files changed, 145 insertions(+), 5 deletions(-) create mode 100644 deltas/0.2.7/pre.006-fix.001.md diff --git a/Cargo.toml b/Cargo.toml index 8ee304e..8d6af5e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ # file: Cargo.toml -# version: 199 +# version: 200 [workspace] resolver = "3" members = ["crates/ksp-app-config-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-logging-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-wallet-lib"] [workspace.package] -version = "0.2.7-pre.6" +version = "0.2.7-pre.6.fix.1" edition = "2024" license = "MIT" repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project" diff --git a/crates/ksp-onchain-transport-lib/src/ws_session.rs b/crates/ksp-onchain-transport-lib/src/ws_session.rs index ed6e4a7..5b00b9c 100644 --- a/crates/ksp-onchain-transport-lib/src/ws_session.rs +++ b/crates/ksp-onchain-transport-lib/src/ws_session.rs @@ -1,5 +1,5 @@ // file: crates/ksp-onchain-transport-lib/src/ws_session.rs -// version: 4 +// version: 5 use futures_util::SinkExt; // rust-rules: trait-import use futures_util::StreamExt; // rust-rules: trait-import @@ -531,7 +531,7 @@ where return WsActorIoOutcome::Continue; }, }; - let (state_tx, state_rx) = tokio::sync::watch::channel(crate::WsSubscriptionState::Requested); + let (state_tx, _) = tokio::sync::watch::channel(crate::WsSubscriptionState::Requested); subscriptions.insert( subscription_id.get(), crate::WsSubscriptionRuntime { diff --git a/crates/ksp-onchain-transport-lib/unit_tests/ws_session.rs b/crates/ksp-onchain-transport-lib/unit_tests/ws_session.rs index 6fd4214..7cce4c6 100644 --- a/crates/ksp-onchain-transport-lib/unit_tests/ws_session.rs +++ b/crates/ksp-onchain-transport-lib/unit_tests/ws_session.rs @@ -1,5 +1,5 @@ // file: crates/ksp-onchain-transport-lib/unit_tests/ws_session.rs -// version: 3 +// version: 4 use futures_util::SinkExt; // rust-rules: trait-import use futures_util::StreamExt; // rust-rules: trait-import @@ -493,6 +493,8 @@ async fn websocket_unknown_remote_subscription_notification_is_ignored_without_a send_result(&mut websocket, &subscribe, serde_json::json!(3)).await; send_notification(&mut websocket, "rootNotification", 999, serde_json::json!(100)).await; send_notification(&mut websocket, "rootNotification", 3, serde_json::json!(101)).await; + let request = read_request(&mut websocket).await; + send_result(&mut websocket, &request, serde_json::json!(true)).await; }); let session = crate::WsSession::connect(local_endpoint(url.as_str())).await.expect("client handshake must succeed"); let mut subscription = session @@ -504,6 +506,9 @@ async fn websocket_unknown_remote_subscription_notification_is_ignored_without_a let notification = subscription.recv().await.expect("valid notification must exist").expect("valid notification must decode"); assert_eq!(notification, serde_json::json!(101)); assert_eq!(subscription.state(), crate::WsSubscriptionState::Active); + assert_eq!(session.state(), crate::WsSessionState::Active); + let result = session.execute_json_rpc("afterUnknownRemote", std::vec::Vec::new()).await.expect("physical session must remain usable"); + assert_eq!(result, serde_json::json!(true)); server.await.expect("local server task must complete"); } @@ -543,6 +548,8 @@ async fn websocket_typed_notification_decode_failure_fails_only_one_subscription let subscribe = read_request(&mut websocket).await; send_result(&mut websocket, &subscribe, serde_json::json!(8)).await; send_notification(&mut websocket, "rootNotification", 8, serde_json::json!("not-a-slot")).await; + let request = read_request(&mut websocket).await; + send_result(&mut websocket, &request, serde_json::json!(true)).await; }); let session = crate::WsSession::connect(local_endpoint(url.as_str())).await.expect("client handshake must succeed"); let mut subscription = session @@ -560,5 +567,7 @@ async fn websocket_typed_notification_decode_failure_fails_only_one_subscription assert_eq!(error.code(), crate::ERROR_CODE_INVALID_RESPONSE); wait_for_subscription_state(&subscription, crate::WsSubscriptionState::Failed).await; assert_eq!(session.state(), crate::WsSessionState::Active); + let result = session.execute_json_rpc("afterDecodeFailure", std::vec::Vec::new()).await.expect("physical session must remain usable"); + assert_eq!(result, serde_json::json!(true)); server.await.expect("local server task must complete"); } diff --git a/deltas/0.2.7/pre.006-fix.001.md b/deltas/0.2.7/pre.006-fix.001.md new file mode 100644 index 0000000..cdd08b4 --- /dev/null +++ b/deltas/0.2.7/pre.006-fix.001.md @@ -0,0 +1,131 @@ + + + +# Delta `0.2.7-pre.006-fix.001` — synchronisation des canaris d'isolation WS + warning watch + +## 1. Base requise + +```text +0.2.7-pre.006 appliqué +workspace.package.version = 0.2.7-pre.6 +``` + +Le checkpoint opérateur de `pre.006` a établi : + +```text +cargo fmt --all exécuté +python3 scripts/audit_rust_workspace_rules.py clean +cargo check --workspace réussi avec 1 warning +cargo clippy --workspace --all-targets réussi avec le même warning +cargo test -p ksp-onchain-transport-lib 273 réussis, 2 échoués +``` + +Les deux échecs concernent les canaris : + +```text +unknown remote subscription id -> session attendue Active +typed notification decode failure -> session attendue Active +``` + +## 2. Diagnostic + +Les chemins runtime concernés dans `handle_subscription_notification` renvoient déjà `WsActorIoOutcome::Continue` : + +- un remote subscription ID inconnu ou stale est ignoré sans modifier la session ; +- une erreur du decoder typed ferme uniquement la subscription locale en `Failed`. + +Le `Failed` observé par les deux tests provenait ensuite du fixture serveur local : après l'envoi des notifications, la tâche serveur se terminait immédiatement et abandonnait le socket sans Close frame. Le contrat de session acquis classe correctement cette fin physique sans Close comme une défaillance de connexion. + +Le test mélangeait donc deux événements distincts : l'anomalie logique à isoler et une déconnexion physique ultérieure. + +## 3. Correctif des canaris + +Les deux fixtures maintiennent désormais le peer local ouvert après l'anomalie et attendent une requête JSON-RPC témoin : + +```text +afterUnknownRemote +afterDecodeFailure +``` + +Le caller vérifie successivement : + +1. le résultat attendu de la notification ou de l'erreur typed ; +2. l'état logique attendu de la subscription ; +3. `WsSessionState::Active` ; +4. la réussite d'une nouvelle requête JSON-RPC sur la même session physique. + +Le serveur peut seulement terminer ensuite. + +Cette synchronisation supprime la course du fixture et prouve plus fortement la propriété recherchée : la session reste réellement utilisable après une anomalie locale de subscription. + +## 4. Warning `state_rx` + +Le receiver initial créé uniquement pour construire le `watch::Sender` n'est pas consommé par l'actor. Le binding inutile : + +```text +state_rx +``` + +est supprimé. Le handle public continue à recevoir son propre `watch::Receiver` lors de l'ACK subscribe via `state_tx.subscribe()`. + +Aucune lifecycle state, API ou politique de notification n'est modifiée. + +## 5. Signal technique + +Le fix modifie du Rust et des tests. Conformément aux règles de version KSP : + +```text +livraison = 0.2.7-pre.006-fix.001 +workspace.package.version = 0.2.7-pre.6.fix.1 +commit = v0.2.7-pre.006-fix.001 +``` + +Aucun tag prerelease. + +## 6. Fichiers modifiés + +```text +Cargo.toml +crates/ksp-onchain-transport-lib/src/ws_session.rs +crates/ksp-onchain-transport-lib/unit_tests/ws_session.rs +``` + +## 7. Fichier ajouté + +```text +deltas/0.2.7/pre.006-fix.001.md +``` + +## 8. Fichiers supprimés + +Aucun. + +## 9. Invariants préservés + +Le fix ne change pas : + +- `WsSession` ou `WsSubscription` publics ; +- les IDs locaux ou remote ; +- le mapping remote vers local ; +- les neuf familles standard ; +- la capacité des channels ; +- les règles unsubscribe ; +- la classification d'une vraie perte physique comme `Failed` ; +- les frontières de dépendances ; +- la façade de logging KSP ; +- le scope reconnect/resubscribe réservé à `pre.007`. + +## 10. Validation sandbox + +Le sandbox ne permet pas de revendiquer les gates Cargo opérateur. Les validations statiques disponibles sont exécutées sur le workspace reconstruit avec ce fix. + +## 11. Validation opérateur attendue + +```bash +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 +```