Skip to content

calculate_sla_view can disagree with calculate_sla on duplicates: the audit path returns a result for inputs the mutating path would reject as DuplicateOutageInput #512

Description

@usmanimamu17-create

Problem

calculate_sla_view (src/lib.rs) shares compute_result with the mutating path but skips the duplicate-detection state machine:

pub fn calculate_sla_view(env, outage_id, severity, mttr_minutes) -> Result<SLAResult, SLAError> {
    Self::check_version(&env)?;
    let cfg = Self::load_config(&env, &severity)?;
    let config_version_hash = Self::compute_config_version_hash(&env)?;
    Self::compute_result(outage_id, mttr_minutes, &cfg, config_version_hash, env.ledger().timestamp())
}

The mutating path would return DuplicateOutageInput for the same (outage_id, mttr) under an unchanged hash with a conflicting prior entry; the view returns a new result with no awareness of the stored entry.

Consequences:

  • The audit view certifies submissions the mutating path rejects: a backend using calculate_sla_view to pre-validate a submission (a natural use of the "view-only" endpoint) sees a valid result; the actual calculate_sla then fails with DuplicateOutageInput — the pre-validation gave false confidence.
  • The two paths encode different semantics for the same inputs: the doc says the view "recalculates SLA deterministically" and shares the computation; but the decision semantics (replay vs. conflict vs. new generation) exist only in the mutating path, so the view is not a preview of the mutating path.
  • The replay case diverges too: for a genuinely idempotent resubmission, calculate_sla returns the stored result (with its original recorded_at), while the view computes a fresh result with the current timestamp — even in the agreeing case the outputs differ.

Root cause

The view was implemented as "pure computation" without the duplicate/replay policy; the docs describe it as an audit path without defining its relationship to the state machine.

Why this is architecturally hard

  1. Making the view agree requires it to read history and apply the duplicate/replay/conflict logic read-only — turning it into a non-trivial duplicate of calculate_sla's Phase 3 (the very duplication the codebase already struggles with — companion issue), or extracting the policy into a shared pure function both paths call.
  2. Alternatively the view's contract can be redefined as "pure math, ignoring history" and the docs corrected — but then its value as a pre-validation tool is explicitly off-limits, and consumers must know not to use it that way.
  3. Any change must keep calculate_sla_view read-only (no storage writes, no events — a documented property) while adding the history read.

Acceptance criteria

  • calculate_sla_view either applies the duplicate/replay policy (read-only) or is documented as math-only with the divergence explicit.
  • A test covers the same inputs through both paths (conflict case and replay case).
  • The view remains side-effect-free.

Out of scope

The replay-uses-current-config defect (companion issue in batch 02) and the computation duplication (companion issue in batch 01).

Getting started

just test

Good first files to read: apexchainx_calculator/src/lib.rs (calculate_sla_view, calculate_sla), apexchainx_calculator/src/calculation.rs.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardStellar WaveIssues in the Stellar wave programThird CampaignCampaign: Third Campaignarea/calculationImported campaign issue labelpriority/highImported campaign issue label

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions