From d4f4237723c4872f58dca5f9f801040c0dcc653e Mon Sep 17 00:00:00 2001 From: SinuS Von SifriduS Date: Sun, 6 Sep 2026 23:00:44 +0200 Subject: [PATCH] v0.3.10-pre.005-fix.002 --- Cargo.toml | 4 +- .../ksp-job-backfill-lib/tests/hardening.rs | 4 +- .../tests/ws_raw_parity.rs | 131 ++++++++++++++---- deltas/0.3.10/pre.005-fix.002.md | 101 ++++++++++++++ ...031-V0_3_10_RAW_TRANSACTION_INGEST_PLAN.md | 4 +- .../027-V0_3_10_RAW_TRANSACTION_INGEST.md | 55 +++++++- 6 files changed, 264 insertions(+), 35 deletions(-) create mode 100644 deltas/0.3.10/pre.005-fix.002.md diff --git a/Cargo.toml b/Cargo.toml index 56ec8af..ed75d5e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ # file: Cargo.toml -# version: 497 +# version: 498 [workspace] resolver = "3" members = ["crates/ksp-app-backfill-desk", "crates/ksp-app-config-desk", "crates/ksp-app-solprices-desk", "crates/ksp-app-store-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-interface-lib", "crates/ksp-job-api", "crates/ksp-job-backfill-lib", "crates/ksp-logging-lib", "crates/ksp-offchain-transport-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-program-api", "crates/ksp-raw-transaction-lib", "crates/ksp-store-api", "crates/ksp-store-lib", "crates/ksp-store-postgres-lib", "crates/ksp-wallet-lib", "crates/ksp-worker-api"] [workspace.package] -version = "0.3.10-pre.5.fix.1" +version = "0.3.10-pre.5.fix.2" edition = "2024" license = "MIT" repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project" diff --git a/crates/ksp-job-backfill-lib/tests/hardening.rs b/crates/ksp-job-backfill-lib/tests/hardening.rs index 160aef9..aba448a 100644 --- a/crates/ksp-job-backfill-lib/tests/hardening.rs +++ b/crates/ksp-job-backfill-lib/tests/hardening.rs @@ -1,5 +1,5 @@ // file: crates/ksp-job-backfill-lib/tests/hardening.rs -// version: 5 +// version: 6 //! Adversarial, security, visibility and external-boundary hardening canaries for `pre.010`. @@ -275,7 +275,7 @@ fn pre_010_manifest_dependency_surface_is_exact_and_backend_neutral() { "tokio", ]) ); - assert_eq!(dev, std::collections::BTreeSet::from(["tokio"])); + assert_eq!(dev, std::collections::BTreeSet::from(["tokio", "tokio-tungstenite"])); assert!(manifest.contains("ksp-store-lib = { path = \"../ksp-store-lib\", default-features = false }")); assert!(manifest.contains("tokio = { workspace = true, features = [\"macros\", \"sync\"] }")); assert!(!manifest.contains("ksp-store-postgres-lib")); diff --git a/crates/ksp-job-backfill-lib/tests/ws_raw_parity.rs b/crates/ksp-job-backfill-lib/tests/ws_raw_parity.rs index bdf7cb6..b0e82a2 100644 --- a/crates/ksp-job-backfill-lib/tests/ws_raw_parity.rs +++ b/crates/ksp-job-backfill-lib/tests/ws_raw_parity.rs @@ -1,5 +1,5 @@ // file: crates/ksp-job-backfill-lib/tests/ws_raw_parity.rs -// version: 2 +// version: 3 //! Cross-layer parity canaries qualifying standard `blockSubscribe` and Helius `transactionSubscribe` for RAW v1 ingestion. @@ -179,7 +179,7 @@ fn raw_from_block_transaction( }, _ => return std::option::Option::None, }; - let material = ksp_raw_transaction_lib::RawTransactionMaterial::binary_base64_with_embedded_signature( + let material = match ksp_raw_transaction_lib::RawTransactionMaterial::binary_base64_with_embedded_signature( network, slot, block_time, @@ -187,8 +187,10 @@ fn raw_from_block_transaction( map_meta(transaction.meta()), map_version(transaction.version()), ksp_raw_transaction_lib::RawTransactionWireField::Value(transaction_index), - ) - .ok()?; + ) { + Ok(material) => material, + Err(_) => return std::option::Option::None, + }; return ksp_raw_transaction_lib::canonicalize_raw_transaction(material).ok(); } @@ -202,7 +204,7 @@ fn raw_from_confirmed_transaction( }, _ => return std::option::Option::None, }; - let material = ksp_raw_transaction_lib::RawTransactionMaterial::binary_base64_with_embedded_signature( + let material = match ksp_raw_transaction_lib::RawTransactionMaterial::binary_base64_with_embedded_signature( network, transaction.slot(), transaction.block_time(), @@ -214,8 +216,10 @@ fn raw_from_confirmed_transaction( ksp_onchain_transport_lib::SolanaWireField::Null => ksp_raw_transaction_lib::RawTransactionWireField::Null, ksp_onchain_transport_lib::SolanaWireField::Value(value) => ksp_raw_transaction_lib::RawTransactionWireField::Value(*value), }, - ) - .ok()?; + ) { + Ok(material) => material, + Err(_) => return std::option::Option::None, + }; return ksp_raw_transaction_lib::canonicalize_raw_transaction(material).ok(); } @@ -223,19 +227,50 @@ fn raw_from_helius_full( network: ksp_store_lib::RawNetworkId, notification: &ksp_onchain_transport_lib::HeliusFullTransactionNotification, ) -> std::option::Option { - let object = notification.transaction().as_object()?; - let encoded = object.get("transaction")?.as_array()?; - if encoded.len() != 2 || encoded.get(1)?.as_str()? != "base64" { + let object = match notification.transaction().as_object() { + std::option::Option::Some(object) => object, + std::option::Option::None => return std::option::Option::None, + }; + let encoded = match object.get("transaction") { + std::option::Option::Some(value) => match value.as_array() { + std::option::Option::Some(encoded) => encoded, + std::option::Option::None => return std::option::Option::None, + }, + std::option::Option::None => return std::option::Option::None, + }; + if encoded.len() != 2 { return std::option::Option::None; } - let data = encoded.first()?.as_str()?; + let encoding = match encoded.get(1) { + std::option::Option::Some(value) => match value.as_str() { + std::option::Option::Some(encoding) => encoding, + std::option::Option::None => return std::option::Option::None, + }, + std::option::Option::None => return std::option::Option::None, + }; + if encoding != "base64" { + return std::option::Option::None; + } + let data = match encoded.first() { + std::option::Option::Some(value) => match value.as_str() { + std::option::Option::Some(data) => data, + std::option::Option::None => return std::option::Option::None, + }, + std::option::Option::None => return std::option::Option::None, + }; let meta = match object.get("meta") { std::option::Option::None => ksp_raw_transaction_lib::RawTransactionWireField::Omitted, std::option::Option::Some(serde_json::Value::Null) => ksp_raw_transaction_lib::RawTransactionWireField::Null, std::option::Option::Some(value) => ksp_raw_transaction_lib::RawTransactionWireField::Value(value.clone()), }; - let signature = ksp_raw_transaction_lib::parse_raw_transaction_signature(notification.signature()).ok()?; - let transaction_index = u32::try_from(notification.transaction_index()).ok()?; + let signature = match ksp_raw_transaction_lib::parse_raw_transaction_signature(notification.signature()) { + Ok(signature) => signature, + Err(_) => return std::option::Option::None, + }; + let transaction_index = match u32::try_from(notification.transaction_index()) { + Ok(transaction_index) => transaction_index, + Err(_) => return std::option::Option::None, + }; let material = ksp_raw_transaction_lib::RawTransactionMaterial::binary_base64( network, signature, @@ -276,14 +311,25 @@ async fn pre_005_standard_block_subscribe_full_base64_matches_http_get_block_raw let address = listener.local_addr().expect("WS parity listener address must resolve"); let ws_block = serde_json::from_str::(HTTP_BODY).expect("HTTP parity JSON must parse")["result"].clone(); let server = tokio::spawn(async move { - let (stream, _) = listener.accept().await.map_err(|_| return "WS parity server accept failed".to_owned())?; - let mut websocket = tokio_tungstenite::accept_async(stream).await.map_err(|_| return "WS parity handshake failed".to_owned())?; - let subscribe = read_ws_request(&mut websocket).await?; + let (stream, _) = match listener.accept().await { + Ok(accepted) => accepted, + Err(_) => return Err("WS parity server accept failed".to_owned()), + }; + let mut websocket = match tokio_tungstenite::accept_async(stream).await { + Ok(websocket) => websocket, + Err(_) => return Err("WS parity handshake failed".to_owned()), + }; + let subscribe = match read_ws_request(&mut websocket).await { + Ok(subscribe) => subscribe, + Err(error) => return Err(error), + }; if subscribe.get("method") != std::option::Option::Some(&serde_json::json!("blockSubscribe")) { return Err("WS parity server received unexpected subscribe method".to_owned()); } - send_ws_result(&mut websocket, &subscribe, serde_json::json!(501)).await?; - send_ws_json( + if let Err(error) = send_ws_result(&mut websocket, &subscribe, serde_json::json!(501)).await { + return Err(error); + } + if let Err(error) = send_ws_json( &mut websocket, serde_json::json!({ "jsonrpc":"2.0", @@ -291,12 +337,20 @@ async fn pre_005_standard_block_subscribe_full_base64_matches_http_get_block_raw "params":{"subscription":501,"result":{"context":{"slot":FIXTURE_SLOT},"value":{"slot":FIXTURE_SLOT,"block":ws_block,"err":null}}} }), ) - .await?; - let unsubscribe = read_ws_request(&mut websocket).await?; + .await + { + return Err(error); + } + let unsubscribe = match read_ws_request(&mut websocket).await { + Ok(unsubscribe) => unsubscribe, + Err(error) => return Err(error), + }; if unsubscribe.get("method") != std::option::Option::Some(&serde_json::json!("blockUnsubscribe")) { return Err("WS parity server received unexpected unsubscribe method".to_owned()); } - send_ws_result(&mut websocket, &unsubscribe, serde_json::json!(true)).await?; + if let Err(error) = send_ws_result(&mut websocket, &unsubscribe, serde_json::json!(true)).await { + return Err(error); + } wait_for_ws_close(&mut websocket).await; return Ok::<(), std::string::String>(()); }); @@ -364,14 +418,25 @@ async fn pre_005_helius_full_base64_without_block_time_and_version_requires_http let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.expect("Helius parity listener must bind"); let address = listener.local_addr().expect("Helius parity listener address must resolve"); let server = tokio::spawn(async move { - let (stream, _) = listener.accept().await.map_err(|_| return "Helius parity server accept failed".to_owned())?; - let mut websocket = tokio_tungstenite::accept_async(stream).await.map_err(|_| return "Helius parity handshake failed".to_owned())?; - let subscribe = read_ws_request(&mut websocket).await?; + let (stream, _) = match listener.accept().await { + Ok(accepted) => accepted, + Err(_) => return Err("Helius parity server accept failed".to_owned()), + }; + let mut websocket = match tokio_tungstenite::accept_async(stream).await { + Ok(websocket) => websocket, + Err(_) => return Err("Helius parity handshake failed".to_owned()), + }; + let subscribe = match read_ws_request(&mut websocket).await { + Ok(subscribe) => subscribe, + Err(error) => return Err(error), + }; if subscribe.get("method") != std::option::Option::Some(&serde_json::json!("transactionSubscribe")) { return Err("Helius parity server received unexpected subscribe method".to_owned()); } - send_ws_result(&mut websocket, &subscribe, serde_json::json!(601)).await?; - send_ws_json( + if let Err(error) = send_ws_result(&mut websocket, &subscribe, serde_json::json!(601)).await { + return Err(error); + } + if let Err(error) = send_ws_json( &mut websocket, serde_json::json!({ "jsonrpc":"2.0", @@ -387,12 +452,20 @@ async fn pre_005_helius_full_base64_without_block_time_and_version_requires_http } }), ) - .await?; - let unsubscribe = read_ws_request(&mut websocket).await?; + .await + { + return Err(error); + } + let unsubscribe = match read_ws_request(&mut websocket).await { + Ok(unsubscribe) => unsubscribe, + Err(error) => return Err(error), + }; if unsubscribe.get("method") != std::option::Option::Some(&serde_json::json!("transactionUnsubscribe")) { return Err("Helius parity server received unexpected unsubscribe method".to_owned()); } - send_ws_result(&mut websocket, &unsubscribe, serde_json::json!(true)).await?; + if let Err(error) = send_ws_result(&mut websocket, &unsubscribe, serde_json::json!(true)).await { + return Err(error); + } wait_for_ws_close(&mut websocket).await; return Ok::<(), std::string::String>(()); }); diff --git a/deltas/0.3.10/pre.005-fix.002.md b/deltas/0.3.10/pre.005-fix.002.md new file mode 100644 index 0000000..5133ac5 --- /dev/null +++ b/deltas/0.3.10/pre.005-fix.002.md @@ -0,0 +1,101 @@ + + + +# Delta `0.3.10-pre.005-fix.002` — conformité Clippy du canari WS + inventaire dev exact + +## Base requise + +```text +0.3.10-pre.005-fix.001 +``` + +## Motif du fix + +Le second rejeu opérateur confirme `cargo check --workspace`, la common RAW et la suite Transport complète, mais révèle deux défauts test-only : + +```text +25 clippy::question_mark_used dans crates/ksp-job-backfill-lib/tests/ws_raw_parity.rs +1 canari hardening encore calibré sur dev-dependencies = {tokio} +``` + +Le manifest `pre.005` contient légitimement `tokio-tungstenite` en dev-only pour les serveurs WebSocket déterministes ; `tests/dependency_boundary.rs` verrouille déjà qu’il ne migre pas vers les dépendances de production. + +## Corrections + +`ws_raw_parity.rs` remplace tous les `?` par du contrôle de flux explicite : + +```text +Option -> match + retour None +Result -> match + retour Err/None +appel async Result -> if let Err(error) + retour Err(error) +``` + +Aucun `#[allow(...)]`, aucun changement de golden et aucune modification du scénario positif `blockSubscribe` ou négatif Helius ne sont introduits. + +`hardening.rs` attend désormais exactement : + +```text +dev-dependencies = {tokio, tokio-tungstenite} +``` + +L’inventaire des dépendances normales reste inchangé. + +## Invariants inchangés + +```text +blockSubscribe standard confirmed/full/base64 legacy-v0 -> golden RAW-direct positif +Helius transactionSubscribe full/base64 -> signal + hydration HTTP +RAW v1 bytes/hash contracts +TR-C2 Worker-owned +tokio-tungstenite strictement dev-only +aucun code de production modifié +aucune dépendance ni feature modifiée par ce fix +``` + +## Version technique + +```text +workspace.package.version = 0.3.10-pre.5.fix.2 +Cargo.toml header = 498 +workspace members = 20 +``` + +## Fichiers modifiés + +```text +Cargo.toml +crates/ksp-job-backfill-lib/tests/hardening.rs +crates/ksp-job-backfill-lib/tests/ws_raw_parity.rs +docs/plans/031-V0_3_10_RAW_TRANSACTION_INGEST_PLAN.md +docs/validation/027-V0_3_10_RAW_TRANSACTION_INGEST.md +``` + +## Fichier ajouté + +```text +deltas/0.3.10/pre.005-fix.002.md +``` + +## Audits d’assemblage + +```text +General Rust rule audit: clean +Rust export completeness audit: 0 candidate(s) +KSP workspace Rust rule audit: clean +Markdown table audit: clean (339 table(s), 761 file(s)) +``` + +## Gate opérateur à rejouer + +```bash +cargo fmt --all +python3 scripts/audit_rust_workspace_rules.py +python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas +cargo check --workspace +cargo clippy --workspace --all-targets --all-features -- -D warnings +cargo test -p ksp-raw-transaction-lib +cargo test -p ksp-onchain-transport-lib +cargo test -p ksp-job-backfill-lib +``` + +Les arbres Cargo n’ont pas besoin d’être rejoués pour ce fix : aucun manifest de crate, aucune dépendance et aucune feature ne change. diff --git a/docs/plans/031-V0_3_10_RAW_TRANSACTION_INGEST_PLAN.md b/docs/plans/031-V0_3_10_RAW_TRANSACTION_INGEST_PLAN.md index bc3bc7a..ef7f8f5 100644 --- a/docs/plans/031-V0_3_10_RAW_TRANSACTION_INGEST_PLAN.md +++ b/docs/plans/031-V0_3_10_RAW_TRANSACTION_INGEST_PLAN.md @@ -1,5 +1,5 @@ - + # Plan v0.3.10 — RAW Transaction commune + Worker d’ingestion multi-source @@ -1076,6 +1076,8 @@ Helius `transactionSubscribe` full/base64 conserve la signature, le slot, le tra Le premier rejeu opérateur de `pre.005` le 2026-09-06 valide les audits statiques, `cargo check --workspace`, la common RAW, les 387 tests Transport et les arbres normal/dev/features, mais `cargo clippy --workspace --all-targets --all-features -- -D warnings` ainsi que `cargo test -p ksp-job-backfill-lib` s’arrêtent sur une unique erreur de compilation du nouveau canari : `RawTransaction::block_time().unix_millis()` retourne `u64`, tandis que `FIXTURE_BLOCK_TIME * 1_000` était inféré en `i64`. `pre.005-fix.001` corrige uniquement cette constante de comparaison en millisecondes `u64`; aucun code de production, golden, contrat RAW/WS ni dépendance n’est modifié. +Le second rejeu opérateur de `pre.005-fix.001` révèle ensuite deux gardes de test supplémentaires : Clippy strict refuse 25 usages de l’opérateur `?` dans les helpers/canaris de `ws_raw_parity.rs`, et le canari historique `pre_010_manifest_dependency_surface_is_exact_and_backend_neutral` attend encore uniquement `tokio` dans les dev-dependencies alors que `pre.005` a ajouté `tokio-tungstenite` en dev-only. `pre.005-fix.002` remplace les `?` par des `match`/`if let Err` explicites et recalibre uniquement le set dev attendu vers `tokio` + `tokio-tungstenite`. Aucun code de production, dépendance, feature, golden ou décision de qualification WS/Helius n’est modifié. + ### `pre.006` — parité Yellowstone transactions/blocks Fermer les adapters structurés Yellowstone transaction/block, Transaction V1, meta et cross-source parity. Aucune persistance directe si le gate byte-identical échoue. diff --git a/docs/validation/027-V0_3_10_RAW_TRANSACTION_INGEST.md b/docs/validation/027-V0_3_10_RAW_TRANSACTION_INGEST.md index efd36a5..fe441d8 100644 --- a/docs/validation/027-V0_3_10_RAW_TRANSACTION_INGEST.md +++ b/docs/validation/027-V0_3_10_RAW_TRANSACTION_INGEST.md @@ -1,5 +1,5 @@ - + # Validation v0.3.10 — RAW Transaction commune + Worker d’ingestion @@ -889,3 +889,56 @@ Rust export completeness audit: 0 candidate(s) KSP workspace Rust rule audit: clean Markdown table audit: clean (339 table(s), 760 file(s)) ``` + +### 13.9 Second rejeu opérateur de `pre.005-fix.001` et fix Clippy/hardening + +Le rejeu communiqué le 2026-09-06 confirme d’abord : + +```text +cargo fmt --all : exécuté +General Rust rule audit: clean +Rust export completeness audit: 0 candidate(s) +KSP workspace Rust rule audit: clean +Markdown table audit: clean (339 table(s), 760 file(s)) +cargo check --workspace : PASS +cargo test -p ksp-raw-transaction-lib : PASS, 12 unit + 9 intégration, 0 échec +cargo test -p ksp-onchain-transport-lib : PASS, 387 unit + 51 public API + 43 release completeness + 4 doctests, 0 échec ; smokes live ignorés comme prévu +``` + +Deux défauts restent strictement confinés aux nouveaux canaris de `pre.005` : + +```text +cargo clippy --workspace --all-targets --all-features -- -D warnings + -> FAIL : 25 clippy::question_mark_used dans tests/ws_raw_parity.rs + +cargo test -p ksp-job-backfill-lib + -> 51 unit PASS + -> dependency_boundary 3/3 PASS + -> hardening 8/9 PASS + -> FAIL : inventaire dev attendu {tokio} alors que le manifest réel contient {tokio, tokio-tungstenite} +``` + +`pre.005-fix.002` corrige uniquement ces gardes : + +```text +ws_raw_parity.rs + ? sur Option/Result -> match explicite ou if let Err(...) + aucun #[allow(...)] + aucun changement de scénario/golden + +hardening.rs + dev attendu -> {tokio, tokio-tungstenite} + inventaire normal inchangé +``` + +Le fix ne modifie ni `crates/ksp-job-backfill-lib/Cargo.toml`, ni aucune dépendance/feature, ni le code de production. Les arbres Cargo déjà validés pour `pre.005` restent donc applicables et n’ont pas besoin d’être rejoués pour ce fix. + +L’assemblage local de `pre.005-fix.002` obtient : + +```text +General Rust rule audit: clean +Rust export completeness audit: 0 candidate(s) +KSP workspace Rust rule audit: clean +Markdown table audit: clean (339 table(s), 761 file(s)) +``` +