From 1f48dec30fe1f5fa7b7d768a62dbf367b0581bec Mon Sep 17 00:00:00 2001 From: beulah7717108-eng Date: Sun, 30 Aug 2026 17:54:11 +0000 Subject: [PATCH] refactor: remove orphaned history_snapshot module NormalizedSnapshot and normalize_history were documented as the way to summarize history "for dashboard telemetry", but no contract method returned them and the type was not #[contracttype], so it could not cross the contract boundary. The documented dashboard path did not exist and nothing it exposed was actually obtainable on-chain. The dashboard surface is now explicitly get_stats (cumulative aggregate) with get_severity_telemetry (per-severity weekly windows); the module, its lib.rs registration, and its ownership-list entries are removed and the get_stats docs name the supported aggregate so no dashboard path points at an unexposed type. Closes #467 --- CHANGELOG.md | 1 + apexchainx_calculator/src/history_snapshot.rs | 148 ------------------ apexchainx_calculator/src/lib.rs | 7 +- docs/MODULE_OWNERSHIP.md | 5 +- 4 files changed, 9 insertions(+), 152 deletions(-) delete mode 100644 apexchainx_calculator/src/history_snapshot.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ee7a69..ee2e4de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ## [Unreleased] ### Changed +- **Removed the orphaned `history_snapshot` module.** `NormalizedSnapshot`/`normalize_history` were documented as a dashboard aggregate but were never exposed by a contract method and were not `#[contracttype]`, so no backend could obtain them. The dashboard surface is now explicitly `get_stats` (cumulative aggregate) alongside `get_severity_telemetry` (per-severity weekly windows); the module, its `lib.rs` registration, and its ownership list entries were removed (#467) - **Fuzz targets now assert the contract's documented semantics, not just panic-freedom.** `compute_result` and `validate_config` previously ran the function and let libFuzzer watch for a crash; on code guarded throughout by `checked_mul`/`checked_neg` that finds essentially nothing, and a semantic regression (e.g. treating `mttr == threshold` as a violation) would have left the nightly job green. Both targets now compare every input against `apexchainx_calculator::spec` and fail on any disagreement. Each target header states what it asserts and what it does not; `docs/FUZZING_GUARANTEES.md` states the suite-wide guarantees and the policy for resolving an implementation-vs-documentation conflict - **`ts/historyPagination.ts` capped pages at 50 where the contract caps at 200** (`history::MAX_PAGE_SIZE`, #409), so a backend paging with `limit = 200` received 50 entries and — because the mirror also derived `hasMore` from the returned length — could conclude history had ended. It also coerced `limit = 0` up to 1, returning an entry where the contract returns an empty page, and reported `hasMore: false` where the contract reports `true`. The helper now imports the contract-generated `MAX_PAGE_SIZE` and mirrors `end = min(offset + limit, total)` / `hasMore = end < total` exactly - **`ts/configVersionHash.ts` computed an unrelated hash.** It ran djb2 over a canonical JSON serialisation of a snapshot whose fields (`penaltyBps`, `rewardBps`) do not exist on the contract, so a backend comparing it against `get_config_version_hash` would have seen a mismatch on every call. It now reproduces the contract's polynomial rolling hash exactly, in `BigInt` `u64` arithmetic, and is asserted equal to a contract-recorded value diff --git a/apexchainx_calculator/src/history_snapshot.rs b/apexchainx_calculator/src/history_snapshot.rs deleted file mode 100644 index 7824706..0000000 --- a/apexchainx_calculator/src/history_snapshot.rs +++ /dev/null @@ -1,148 +0,0 @@ -//! SLA history snapshot analysis and normalization utilities. -//! -//! This module provides analytical utilities for inspecting the on-chain SLA -//! calculation history. The `NormalizedSnapshot` struct summarizes the history -//! in a form that backend consumers can use for dashboards and alerting. -//! -//! # Usage -//! -//! ```ignore -//! let history = contract.get_history(); -//! let snapshot = normalize_history(&history); -//! println!("Total entries: {}", snapshot.count); -//! println!("Has violations: {}", snapshot.has_violations); -//! ``` -//! -//! The normalization is deterministic: identical history inputs always produce -//! identical snapshot outputs. - -use crate::SLAResult; -use soroban_sdk::{symbol_short, Vec}; - -/// Summarised view of SLA calculation history. -/// -/// Provides a lightweight aggregate of the full history without exposing -/// individual record details. Suitable for dashboard telemetry. -pub struct NormalizedSnapshot { - /// Total number of SLA calculation entries in the history. - pub count: u32, - /// Whether any entry has a "viol" (violated) status. - pub has_violations: bool, - /// Whether any entry has a "rew" (reward) payment type. - pub has_rewards: bool, -} - -/// Scans the full history and produces a `NormalizedSnapshot`. -/// -/// Iterates through all history entries once, checking each for violation -/// status and reward payment type. Runtime is O(n) in the history size. -pub fn normalize_history(history: &Vec) -> NormalizedSnapshot { - let mut has_violations = false; - let mut has_rewards = false; - - for i in 0..history.len() { - let entry = history.get(i).unwrap(); - if entry.status == symbol_short!("viol") { - has_violations = true; - } - if entry.payment_type == symbol_short!("rew") { - has_rewards = true; - } - } - - NormalizedSnapshot { - count: history.len(), - has_violations, - has_rewards, - } -} - -#[cfg(test)] -mod tests { - use super::normalize_history; - use crate::{SLACalculatorContract, SLACalculatorContractClient}; - use soroban_sdk::{symbol_short, testutils::Address as _, Address, Env}; - - fn setup() -> (Env, SLACalculatorContractClient<'static>, Address, Address) { - let env = Env::default(); - env.mock_all_auths(); - let contract_id = env.register_contract(None, SLACalculatorContract); - let client = SLACalculatorContractClient::new(&env, &contract_id); - let admin = Address::generate(&env); - let operator = Address::generate(&env); - client.initialize(&admin, &operator); - (env, client, admin, operator) - } - - #[test] - fn test_history_snapshot_is_deterministic() { - let (_env, client, _admin, operator) = setup(); - client.calculate_sla(&operator, &symbol_short!("OUT1"), &symbol_short!("high"), &10); - client.calculate_sla(&operator, &symbol_short!("OUT2"), &symbol_short!("high"), &10); - let stats = client.get_stats(); - assert_eq!(stats.total_calculations, 2); - } - - #[test] - #[should_panic] - fn test_stranger_cannot_calculate_sla() { - let (env, client, _admin, _operator) = setup(); - let stranger = Address::generate(&env); - // stranger does not hold the operator role - client.calculate_sla(&stranger, &symbol_short!("U_OUT"), &symbol_short!("high"), &10); - } - - /// Empty history: both flags false, count zero. - #[test] - fn test_normalize_history_empty() { - let (_env, client, _admin, _operator) = setup(); - let history = client.get_history(); - let snap = normalize_history(&history); - assert_eq!(snap.count, 0); - assert!(!snap.has_violations); - assert!(!snap.has_rewards); - } - - /// Only SLA-met (reward) entries: has_rewards true, has_violations false. - #[test] - fn test_normalize_history_has_rewards_only() { - let (_env, client, _admin, operator) = setup(); - // high threshold = 30 min; MTTR 10 < 30 → met + rew - client.calculate_sla(&operator, &symbol_short!("R1"), &symbol_short!("high"), &10); - client.calculate_sla(&operator, &symbol_short!("R2"), &symbol_short!("high"), &5); - let history = client.get_history(); - let snap = normalize_history(&history); - assert_eq!(snap.count, 2); - assert!(snap.has_rewards); - assert!(!snap.has_violations); - } - - /// Only SLA-violated (penalty) entries: has_violations true, has_rewards false. - #[test] - fn test_normalize_history_has_violations_only() { - let (_env, client, _admin, operator) = setup(); - // high threshold = 30 min; MTTR 60 > 30 → viol + pen - client.calculate_sla(&operator, &symbol_short!("V1"), &symbol_short!("high"), &60); - client.calculate_sla(&operator, &symbol_short!("V2"), &symbol_short!("high"), &90); - let history = client.get_history(); - let snap = normalize_history(&history); - assert_eq!(snap.count, 2); - assert!(snap.has_violations); - assert!(!snap.has_rewards); - } - - /// Mixed history: both flags true. - #[test] - fn test_normalize_history_has_both_flags() { - let (_env, client, _admin, operator) = setup(); - // met entry - client.calculate_sla(&operator, &symbol_short!("M1"), &symbol_short!("high"), &10); - // violated entry - client.calculate_sla(&operator, &symbol_short!("V1"), &symbol_short!("high"), &60); - let history = client.get_history(); - let snap = normalize_history(&history); - assert_eq!(snap.count, 2); - assert!(snap.has_rewards); - assert!(snap.has_violations); - } -} diff --git a/apexchainx_calculator/src/lib.rs b/apexchainx_calculator/src/lib.rs index fdc6b1b..308f96e 100644 --- a/apexchainx_calculator/src/lib.rs +++ b/apexchainx_calculator/src/lib.rs @@ -49,7 +49,6 @@ mod event_schema; pub mod fuzz_spec; pub mod governance; pub mod history; -pub mod history_snapshot; pub mod metadata; pub mod metrics; /// Parity checker: compares current `compute_result` against the locked-in @@ -2099,6 +2098,12 @@ impl SLACalculatorContract { // ------------------------------------------------------------------- /// Returns the cumulative SLA performance statistics. + /// + /// This is the contract's dashboard aggregate: together with + /// `get_severity_telemetry` (per-severity weekly windows) it is the + /// supported surface for dashboard telemetry. Cumulative totals here + /// subsume the windowed view; consumers that need a windowed summary + /// should read `get_severity_telemetry` rather than re-scan history. pub fn get_stats(env: Env) -> Result { Self::check_version(&env)?; env.storage() diff --git a/docs/MODULE_OWNERSHIP.md b/docs/MODULE_OWNERSHIP.md index 1dabcbc..590c289 100644 --- a/docs/MODULE_OWNERSHIP.md +++ b/docs/MODULE_OWNERSHIP.md @@ -29,7 +29,7 @@ reviewers and merge bottlenecks are reduced. | **Contract Core** | Contract Core reviewers | `apexchainx_calculator/src/lib.rs`, `calculation.rs`, `config.rs`, core types and entrypoints | | **Contract Governance** | Contract Core reviewers | `governance.rs`, `config_freeze.rs`, role management, pause/unpause | | **Contract Infrastructure** | Contract Core reviewers | `storage_version.rs`, `version_negotiation.rs`, `deployment_policy.rs`, `cross_contract_safety.rs` | -| **Contract Data Layer** | Contract Core reviewers | `history.rs`, `history_snapshot.rs`, `config_metadata.rs`, `config_bundle.rs`, `metadata.rs` | +| **Contract Data Layer** | Contract Core reviewers | `history.rs`, `config_metadata.rs`, `config_bundle.rs`, `metadata.rs` | | **Event System** | Contract Core reviewers | `event.rs`, `event_schema.rs`, `event_correlation.rs`, event test modules | | **Audit & Telemetry** | Contract Core reviewers | `audit_state.rs`, `error_responses.rs` | | **Testing** | Contract Core reviewers | `tests.rs`, `fuzz_tests.rs`, fuzz targets, property tests | @@ -62,7 +62,6 @@ All paths are relative to `apexchainx_calculator/src/`. | `config_freeze.rs` | Contract Governance | `freeze_config`, `unfreeze_config`, `is_config_frozen` | **Medium** | | `metadata.rs` | Contract Governance | `pause`, `unpause`, `is_paused`, `get_pause_info`, `require_not_paused` | **High** | | `history.rs` | Contract Data Layer | `get_history`, `prune_history`, `prune_history_by_age`, `get_history_page`, `get_history_page_with_meta`, `get_history_by_outage`, `get_latest_by_outage`, `get_config_count`, `set_retention_limit`, `get_retention_limit` | **High** | -| `history_snapshot.rs` | Contract Data Layer | `normalize_history` | **Medium** | | `config_metadata.rs` | Contract Data Layer | `record_config_update`, `get_last_config_update` | **Medium** | | `config_bundle.rs` | Contract Data Layer | (composed types for `get_config_bundle`) | **Low** | | `audit_state.rs` | Audit & Telemetry | (composed types for `get_full_audit_state`) | **Low** | @@ -257,7 +256,7 @@ All paths relative to `.github/workflows/`. | `calculation.rs`, `config.rs` | 1 reviewer from Contract Core | | `governance.rs`, `config_freeze.rs` | 1 reviewer from Contract Governance | | `storage_version.rs`, `version_negotiation.rs`, `cross_contract_safety.rs` | 1 reviewer from Contract Infrastructure | -| `history.rs`, `history_snapshot.rs`, `config_metadata.rs` | 1 reviewer from Contract Data Layer | +| `history.rs`, `config_metadata.rs` | 1 reviewer from Contract Data Layer | | `event.rs`, `event_schema.rs`, `event_correlation.rs` | 1 reviewer from Event System | | Any `.github/workflows/*.yml` | 1 reviewer from DevOps | | Any `docs/*.md` | 1 reviewer from Docs |