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