Skip to main content

ic_query/sns/report/model/reports/neurons/
diff.rs

1//! Module: sns::report::model::reports::neurons::diff
2//!
3//! Responsibility: local SNS reward-checkpoint reconciliation DTOs.
4//! Does not own: checkpoint collection, filesystem loading, or live source calls.
5//! Boundary: preserves joined raw maturity deltas, typed invalid reasons, and allocations.
6
7use super::SnsPolicyObservationStatus;
8use serde::{Deserialize, Serialize};
9
10///
11/// SnsRewardAllocationStatus
12///
13/// Reconciliation outcome for one pair of SNS reward checkpoints.
14///
15
16#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize)]
17#[serde(rename_all = "snake_case")]
18pub enum SnsRewardAllocationStatus {
19    /// Every invariant reconciled to one positive native reward distribution.
20    Valid,
21    /// Every invariant reconciled to a native distribution of zero.
22    NoAllocation,
23    /// At least one checkpoint, continuity, policy, or reconciliation invariant failed.
24    Invalid,
25}
26
27impl SnsRewardAllocationStatus {
28    /// Return the stable report label for this allocation status.
29    #[must_use]
30    pub const fn as_str(self) -> &'static str {
31        match self {
32            Self::Valid => "valid",
33            Self::NoAllocation => "no_allocation",
34            Self::Invalid => "invalid",
35        }
36    }
37}
38
39///
40/// SnsRewardDiffInvalidReasonKind
41///
42/// Stable category for one failed reward-diff invariant.
43///
44
45#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize)]
46#[serde(rename_all = "snake_case")]
47pub enum SnsRewardDiffInvalidReasonKind {
48    /// The earlier checkpoint failed pure raw-evidence validation.
49    BeforeCheckpointInvalid,
50    /// The later checkpoint failed pure raw-evidence validation.
51    AfterCheckpointInvalid,
52    /// A stable network or canister identity differs between checkpoints.
53    TargetMismatch,
54    /// The later collection started before the earlier collection completed.
55    CheckpointOrder,
56    /// A recomputed checkpoint policy is not observed satisfied.
57    PolicyNotObservedSatisfied,
58    /// Canonical reward-event timestamps are missing or not strictly increasing.
59    RewardEventOrder,
60    /// The later distribution did not occur after the earlier collection completed.
61    RewardEventCoverage,
62    /// Native reward round continuity does not describe the immediate next event.
63    RewardEventContinuity,
64    /// A neuron present before is absent after.
65    NeuronMissingAfter,
66    /// A later-only neuron cannot truthfully receive a synthetic zero before value.
67    NewNeuronCreationUnexplained,
68    /// A matched neuron changed its reported creation timestamp.
69    NeuronCreationTimestampChanged,
70    /// A neuron's combined maturity decreased.
71    NegativeMaturityDelta,
72    /// Aggregate before/after maturity does not reconcile to the native distribution.
73    AggregateReconciliation,
74    /// The sum of joined neuron deltas does not reconcile to the native distribution.
75    PerNeuronReconciliation,
76    /// Checked arithmetic could not represent a required value.
77    Arithmetic,
78}
79
80impl SnsRewardDiffInvalidReasonKind {
81    /// Return the stable report label for this invalid-reason category.
82    #[must_use]
83    pub const fn as_str(self) -> &'static str {
84        match self {
85            Self::BeforeCheckpointInvalid => "before_checkpoint_invalid",
86            Self::AfterCheckpointInvalid => "after_checkpoint_invalid",
87            Self::TargetMismatch => "target_mismatch",
88            Self::CheckpointOrder => "checkpoint_order",
89            Self::PolicyNotObservedSatisfied => "policy_not_observed_satisfied",
90            Self::RewardEventOrder => "reward_event_order",
91            Self::RewardEventCoverage => "reward_event_coverage",
92            Self::RewardEventContinuity => "reward_event_continuity",
93            Self::NeuronMissingAfter => "neuron_missing_after",
94            Self::NewNeuronCreationUnexplained => "new_neuron_creation_unexplained",
95            Self::NeuronCreationTimestampChanged => "neuron_creation_timestamp_changed",
96            Self::NegativeMaturityDelta => "negative_maturity_delta",
97            Self::AggregateReconciliation => "aggregate_reconciliation",
98            Self::PerNeuronReconciliation => "per_neuron_reconciliation",
99            Self::Arithmetic => "arithmetic",
100        }
101    }
102}
103
104///
105/// SnsRewardDiffInvalidReason
106///
107/// One typed failed invariant retained in an invalid reward diff.
108///
109
110#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
111#[serde(deny_unknown_fields)]
112pub struct SnsRewardDiffInvalidReason {
113    /// Stable machine-readable reason category.
114    pub kind: SnsRewardDiffInvalidReasonKind,
115    /// Full neuron identifier when the failure belongs to one joined row.
116    pub neuron_id: Option<String>,
117    /// Deterministic human-readable detail retaining compared raw values.
118    pub detail: String,
119}
120
121///
122/// SnsRewardDiffCheckpointRef
123///
124/// Stable identity and event position retained for one compared checkpoint.
125///
126
127#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
128#[serde(deny_unknown_fields)]
129pub struct SnsRewardDiffCheckpointRef {
130    /// Requested network identity.
131    pub network: String,
132    /// Stable SNS-W canister principal.
133    pub sns_wasm_canister_id: String,
134    /// Mutable SNS-W list position retained only as display metadata.
135    pub id: usize,
136    /// Mutable SNS name retained only as display metadata.
137    pub name: String,
138    /// Stable SNS Root canister principal.
139    pub root_canister_id: String,
140    /// Stable SNS Governance canister principal.
141    pub governance_canister_id: String,
142    /// Stable SNS ledger canister principal.
143    pub ledger_canister_id: String,
144    /// Stable SNS swap canister principal.
145    pub swap_canister_id: String,
146    /// Stable SNS index canister principal.
147    pub index_canister_id: String,
148    /// Explicit source endpoint retained as provenance, not target identity.
149    pub source_endpoint: String,
150    /// Collection completion timestamp from the checkpoint.
151    pub collection_completed_at_unix_secs: u64,
152    /// Canonical reward-event position.
153    pub reward_event_end_timestamp_seconds: Option<u64>,
154    /// Native timestamp at which the represented reward distribution actually ran.
155    pub reward_event_actual_timestamp_seconds: u64,
156    /// Deprecated native round retained as continuity evidence.
157    pub reward_event_round: u64,
158    /// Native number of rounds covered by the event when supplied.
159    pub rounds_since_last_distribution: Option<u64>,
160    /// Exact maturity distributed by this checkpoint's native reward event.
161    pub distributed_e8s_equivalent: u64,
162}
163
164///
165/// SnsRewardDiffRow
166///
167/// Joined raw maturity and policy evidence for one full SNS neuron identifier.
168///
169
170#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
171#[serde(deny_unknown_fields)]
172pub struct SnsRewardDiffRow {
173    /// Exact 32-byte neuron identifier as lowercase hexadecimal text.
174    pub neuron_id: String,
175    /// Earlier combined maturity, or no value for a supported new neuron.
176    pub before_combined_maturity_e8s_equivalent: Option<u64>,
177    /// Later combined maturity, or no value when the neuron disappeared.
178    pub after_combined_maturity_e8s_equivalent: Option<u64>,
179    /// Raw signed later-minus-earlier combined maturity delta.
180    pub maturity_delta_e8s_equivalent: i128,
181    /// Whether the neuron was first observed after the earlier checkpoint.
182    pub new_neuron: bool,
183    /// Whether an earlier neuron was absent from the later checkpoint.
184    pub missing_after: bool,
185    /// Earlier creation timestamp when the neuron existed.
186    pub before_created_timestamp_seconds: Option<u64>,
187    /// Later creation timestamp when the neuron existed.
188    pub after_created_timestamp_seconds: Option<u64>,
189    /// Earlier neuron-local mint-conversion observation when present.
190    pub before_maturity_mint_conversion_observed_disabled: Option<SnsPolicyObservationStatus>,
191    /// Later neuron-local mint-conversion observation when present.
192    pub after_maturity_mint_conversion_observed_disabled: Option<SnsPolicyObservationStatus>,
193    /// Earlier neuron-local manual-staking observation when present.
194    pub before_manual_maturity_staking_observed_disabled: Option<SnsPolicyObservationStatus>,
195    /// Later neuron-local manual-staking observation when present.
196    pub after_manual_maturity_staking_observed_disabled: Option<SnsPolicyObservationStatus>,
197    /// Earlier pending maturity-disbursement count.
198    pub before_pending_maturity_disbursement_count: Option<usize>,
199    /// Later pending maturity-disbursement count.
200    pub after_pending_maturity_disbursement_count: Option<usize>,
201    /// Whether raw permission, pending-disbursement, or auto-stake evidence changed.
202    pub policy_evidence_changed: bool,
203    /// Allocation numerator, populated only for a valid positive distribution.
204    pub allocation_numerator_e8s_equivalent: Option<u64>,
205    /// Shared allocation denominator, populated only for a valid positive distribution.
206    pub allocation_denominator_e8s_equivalent: Option<u64>,
207}
208
209///
210/// SnsRewardDiffReport
211///
212/// Pure local reconciliation of two untrusted SNS reward checkpoints.
213///
214
215#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
216#[serde(deny_unknown_fields)]
217pub struct SnsRewardDiffReport {
218    /// Reward-diff report schema version.
219    pub schema_version: u32,
220    /// Earlier checkpoint identity and event position.
221    pub before: SnsRewardDiffCheckpointRef,
222    /// Later checkpoint identity and event position.
223    pub after: SnsRewardDiffCheckpointRef,
224    /// Recomputed earlier aggregate combined maturity when representable.
225    pub aggregate_before_combined_maturity_e8s_equivalent: Option<u64>,
226    /// Recomputed later aggregate combined maturity when representable.
227    pub aggregate_after_combined_maturity_e8s_equivalent: Option<u64>,
228    /// Raw signed later-minus-earlier aggregate maturity delta when representable.
229    pub aggregate_maturity_delta_e8s_equivalent: Option<i128>,
230    /// Checked sum of every joined raw signed neuron delta when representable.
231    pub summed_neuron_maturity_delta_e8s_equivalent: Option<i128>,
232    /// Native distributed value against which both delta paths reconcile.
233    pub distributed_e8s_equivalent: u64,
234    /// Whether aggregate before/after maturity exactly matches the native distribution.
235    pub aggregate_reconciled: bool,
236    /// Whether the sum of joined neuron deltas exactly matches the native distribution.
237    pub per_neuron_reconciled: bool,
238    /// Recomputed earlier global maturity-conversion policy status when available.
239    pub before_policy_status: Option<SnsPolicyObservationStatus>,
240    /// Recomputed later global maturity-conversion policy status when available.
241    pub after_policy_status: Option<SnsPolicyObservationStatus>,
242    /// Typed allocation outcome.
243    pub allocation_status: SnsRewardAllocationStatus,
244    /// Every failed comparison and reconciliation invariant.
245    pub invalid_reasons: Vec<SnsRewardDiffInvalidReason>,
246    /// Canonically neuron-id-ordered joined rows.
247    pub rows: Vec<SnsRewardDiffRow>,
248    /// Always false because local JSON evidence has no content-authenticity proof.
249    pub checkpoint_content_authenticated: bool,
250}