diff --git a/Cargo.toml b/Cargo.toml index 8fe87e1..3513c09 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ # file: Cargo.toml -# version: 623 +# version: 624 [workspace] resolver = "3" members = ["crates/ksp-app-backfill-desk", "crates/ksp-app-config-desk", "crates/ksp-app-raw-transaction-ingest-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", "crates/ksp-worker-raw-transaction-ingest-lib"] [workspace.package] -version = "0.3.15-pre.12.fix.1" +version = "0.3.15-pre.12.fix.2" edition = "2024" license = "MIT" repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project" diff --git a/crates/ksp-app-raw-transaction-ingest-desk/src/route_runtime.rs b/crates/ksp-app-raw-transaction-ingest-desk/src/route_runtime.rs index 6d539c2..c3d9d5f 100644 --- a/crates/ksp-app-raw-transaction-ingest-desk/src/route_runtime.rs +++ b/crates/ksp-app-raw-transaction-ingest-desk/src/route_runtime.rs @@ -1,5 +1,5 @@ // file: crates/ksp-app-raw-transaction-ingest-desk/src/route_runtime.rs -// version: 7 +// version: 8 //! Multi-route Store and independent Worker lifecycle owned by Raw Transaction Ingest Desk. @@ -544,12 +544,35 @@ impl crate::RouteRuntimeLaunch { /// Waits for this Worker terminal state, removes only its route and closes Store only after the final same-network route terminates. pub(crate) async fn monitor(self) { let terminal = self.handle.wait_terminal().await; + let snapshot = self.handle.snapshot_source().current(); + let worker = snapshot.worker_snapshot(); + let snapshot_fault = worker.state().fault_code(); + let snapshot_fault_domain = snapshot_fault.map_or("none", |code| return code.domain()); + let snapshot_fault_code = snapshot_fault.map_or("none", |code| return code.code()); let terminal_state = match terminal { std::result::Result::Ok(state) => { ksp_logging_lib::info!( target: crate::TRACING_TARGET, domain = crate::TRACING_DOMAIN_ROUTE_RUNTIME, + profile_id = self.acknowledgement.profile_id.as_str(), + network = self.acknowledgement.network.as_str(), + route_id = self.acknowledgement.route_id.as_str(), + commitment = self.acknowledgement.commitment.as_str(), worker_state = state.code(), + worker_health = worker.health().code(), + worker_activity = worker.activity().code(), + fault_domain = snapshot_fault_domain, + fault_code = snapshot_fault_code, + source_failure_total = snapshot.source_failure_total(), + store_failure_total = snapshot.store_failure_total(), + content_conflict_total = snapshot.content_conflict_total(), + source_active = snapshot.source_active(), + source_reconnecting = snapshot.source_reconnecting(), + source_failed = snapshot.source_failed(), + open_gap_count = snapshot.open_gap_count(), + continuity_has_open_gaps = snapshot.continuity_has_open_gaps(), + future_target_coverage = snapshot.future_target_coverage(), + failed_source_losses_reconciled = snapshot.failed_source_losses_reconciled(), "Raw Transaction Ingest Desk route Worker reached terminal state" ); project_worker_state(state) @@ -558,8 +581,18 @@ impl crate::RouteRuntimeLaunch { ksp_logging_lib::warn!( target: crate::TRACING_TARGET, domain = crate::TRACING_DOMAIN_ROUTE_RUNTIME, + profile_id = self.acknowledgement.profile_id.as_str(), + network = self.acknowledgement.network.as_str(), + route_id = self.acknowledgement.route_id.as_str(), + commitment = self.acknowledgement.commitment.as_str(), error_domain = error.code().domain(), error_code = error.code().code(), + worker_health = worker.health().code(), + snapshot_fault_domain = snapshot_fault_domain, + snapshot_fault_code = snapshot_fault_code, + source_failure_total = snapshot.source_failure_total(), + store_failure_total = snapshot.store_failure_total(), + content_conflict_total = snapshot.content_conflict_total(), "Raw Transaction Ingest Desk terminal Worker wait failed" ); crate::RawIngestRouteState::Faulted @@ -567,7 +600,6 @@ impl crate::RouteRuntimeLaunch { }; let mut terminal = self.acknowledgement; terminal.state = terminal_state; - let snapshot = self.handle.snapshot_source().current(); let monitoring = crate::project_route_monitoring(&terminal, &snapshot); let monitoring = match monitoring { std::result::Result::Ok(value) => std::option::Option::Some(value), diff --git a/crates/ksp-app-raw-transaction-ingest-desk/tests/desktop_contract.rs b/crates/ksp-app-raw-transaction-ingest-desk/tests/desktop_contract.rs index 7024755..d88dfb4 100644 --- a/crates/ksp-app-raw-transaction-ingest-desk/tests/desktop_contract.rs +++ b/crates/ksp-app-raw-transaction-ingest-desk/tests/desktop_contract.rs @@ -1,5 +1,5 @@ // file: crates/ksp-app-raw-transaction-ingest-desk/tests/desktop_contract.rs -// version: 11 +// version: 12 //! Desktop scaffold contract canaries for Raw Transaction Ingest Desk. @@ -308,3 +308,42 @@ fn pre_012_window_shutdown_and_start_stop_races_are_bounded_backend_owned() { assert!(!app_state.contains("std::mem::drop(generation)")); assert!(app_state.contains("self.route_runtime.ensure_start_admission_open()")); } + +#[test] +fn pre_012_fix_002_terminal_fault_logging_keeps_safe_route_identity_and_worker_cause_counters() { + let runtime = read_text(app_root().join("src/route_runtime.rs").as_path()); + let start = runtime.find("pub(crate) async fn monitor(self)"); + let start = match start { + std::option::Option::Some(value) => value, + std::option::Option::None => { + assert!(false, "missing RouteRuntimeLaunch::monitor"); + return; + }, + }; + let remainder = &runtime[start..]; + let end = remainder.find("/// Starts one exact route Worker"); + let end = match end { + std::option::Option::Some(value) => value, + std::option::Option::None => { + assert!(false, "missing route runtime monitor end marker"); + return; + }, + }; + let monitor = &remainder[..end]; + for required in [ + "worker_health = worker.health().code()", + "fault_domain = snapshot_fault_domain", + "fault_code = snapshot_fault_code", + "source_failure_total = snapshot.source_failure_total()", + "store_failure_total = snapshot.store_failure_total()", + "content_conflict_total = snapshot.content_conflict_total()", + "open_gap_count = snapshot.open_gap_count()", + "future_target_coverage = snapshot.future_target_coverage()", + "failed_source_losses_reconciled = snapshot.failed_source_losses_reconciled()", + ] { + assert!(monitor.contains(required), "missing pre.012-fix.002 terminal diagnostic marker {required}"); + } + for forbidden in ["endpoint_url", "connection_uri", "api_key", "source_key", "worker_id", "signature", "canonical_bytes", "wire_bytes"] { + assert!(!monitor.contains(forbidden), "terminal diagnostic surface exposes forbidden marker {forbidden}"); + } +} diff --git a/deltas/0.3.15/pre.012-fix.002.md b/deltas/0.3.15/pre.012-fix.002.md new file mode 100644 index 0000000..f8c41b5 --- /dev/null +++ b/deltas/0.3.15/pre.012-fix.002.md @@ -0,0 +1,71 @@ + + + +# Delta `0.3.15-pre.012-fix.002` + +## Base + +```text +base archive : ksp-general-0.3.15-pre.012-fix.001.zip +base SHA-256 : c6b9925fe0c04b10d43283291c29c4576df9e052ab242fdc8bcd80114b784753 +base version : 0.3.15-pre.12.fix.1 +``` + +## Motif du fix + +Le gate opérateur de `pre.012-fix.001` est propre côté compilation/tests, mais le live Mainnet montre `yellowstone-hydrated` devenant terminal `faulted` pendant que `http-block-polling` reste productif. Le log terminal du Desk ne conservait que le lifecycle final et ne permettait pas de distinguer de façon sûre un `source_failed`, un `store_failure`, un `content_conflict` ou un défaut interne de projection/continuité. + +## Correction + +`RouteRuntimeLaunch::monitor` lit désormais le snapshot terminal déjà détenu par le Worker avant cleanup et journalise seulement des informations source-neutres et bornées : identité logique route, lifecycle/health/activity, `fault_domain`/`fault_code`, compteurs source/Store/conflit, cardinalités source/gap et indicateurs de continuité. + +Aucune décision métier n'est modifiée. Un Worker `Faulted` reste fail-closed ; ce fix rend sa cause observable avant de décider d'un éventuel correctif comportemental. + +## Version + +```text +workspace.package.version : 0.3.15-pre.12.fix.2 +root Cargo header counter : 624 +``` + +## Frontières + +Aucun changement de comportement pour : + +```text +Config / Transport / Store +Worker / Common RAW +acquisition / replay / repair +Start / Stop / shutdown +monitoring IPC / frontend +politique health / fault +``` + +## Sécurité + +Le diagnostic terminal interdit toujours endpoint URL/URI, credentials, API keys, source keys, WorkerId, signatures, canonical/wire bytes et payloads RAW. + +## Validation d'assemblage + +Exécuté dans l'environnement d'assemblage : audits statiques Rust/Markdown disponibles, canari de surface terminale, inventaire du delta, intégrité ZIP et reconstruction byte-exact. + +Cargo/rustfmt ne sont pas disponibles dans cet environnement ; les gates Cargo restent à confirmer par l'opérateur. + +## Inventaire exact du delta + +Ajout : + +```text +deltas/0.3.15/pre.012-fix.002.md +``` + +Modifications : + +```text +Cargo.toml +crates/ksp-app-raw-transaction-ingest-desk/src/route_runtime.rs +crates/ksp-app-raw-transaction-ingest-desk/tests/desktop_contract.rs +docs/validation/032-V0_3_15_RAW_TRANSACTION_INGEST_DESK.md +``` + +Suppressions : aucune. diff --git a/docs/validation/032-V0_3_15_RAW_TRANSACTION_INGEST_DESK.md b/docs/validation/032-V0_3_15_RAW_TRANSACTION_INGEST_DESK.md index f78e47b..8505734 100644 --- a/docs/validation/032-V0_3_15_RAW_TRANSACTION_INGEST_DESK.md +++ b/docs/validation/032-V0_3_15_RAW_TRANSACTION_INGEST_DESK.md @@ -1,5 +1,5 @@ - + # Validation v0.3.15 — Raw Transaction Ingest Desk @@ -804,3 +804,10 @@ Un canari de compilation `pre_012_fix_001_start_route_future_is_send_for_tauri_c Non-claims : aucune modification de route, Config, Transport, Store, Worker, Common RAW, monitoring, shutdown ou sécurité IPC. +### `pre.012-fix.002` — fault terminal diagnostiquable sans payload sensible + +Le gate opérateur de `pre.012-fix.001` compile et teste proprement le Desk, y compris le canari `Future + Send`. Le live Mainnet révèle toutefois un Worker Yellowstone devenant terminal `faulted` alors que le Worker HTTP Block Polling continue à produire des `getBlock` HTTP 200. Aucun `WARN` source/Transport n'est émis avant le terminal, et le log Desk historique ne conservait que `worker_state="faulted"`; le `fault_domain`/`fault_code` déjà présents dans le snapshot Worker et dans le DTO monitoring étaient donc perdus côté logs opérateur. + +Le fix ajoute uniquement une projection de diagnostic terminale source-neutre et bornée dans `RouteRuntimeLaunch::monitor`. Le log terminal porte désormais l'identité logique sûre de route, `worker_state`, `worker_health`, `worker_activity`, `fault_domain`, `fault_code`, les compteurs `source_failure_total`, `store_failure_total`, `content_conflict_total`, les cardinalités source/gap et les booléens de continuité. Aucun endpoint, credential, source key, WorkerId, signature, payload ou octet RAW n'est journalisé. + +Ce fix ne change volontairement aucune politique fail-closed : il ne transforme pas un `Faulted` en succès et ne suppose pas que l'incident observé est un conflit Store ou une panne source. Le prochain live doit permettre de classer exactement la cause avant toute correction comportementale.