Skip to content

Commit b4b0230

Browse files
feat(governance): clear voting power snapshots on Mission 70 activation (#9731)
## Summary - Add `VotingPowerSnapshots::clear()` method that empties both stable BTreeMaps - Call it during governance initialization when the Mission 70 flag is enabled - Add a log line when a voting power spike is detected for observability - Add unit test for `clear()` verifying both maps are emptied ## Why When Mission 70 activates, voting power distribution changes significantly (convex dissolve delay bonus, reduced max dissolve delay). Stale snapshots from before activation would cause false spike detections, since the new voting power totals will differ substantially from the old ones. ## Test plan - [x] Unit test added for `VotingPowerSnapshots::clear()` verifying both maps are emptied --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 516edbc commit b4b0230

File tree

3 files changed

+48
-1
lines changed

3 files changed

+48
-1
lines changed

rs/nns/governance/src/governance.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use crate::{
88
validate_merge_neurons_before_commit,
99
},
1010
split_neuron::{SplitNeuronEffect, calculate_split_neuron_effect},
11+
voting_power_snapshots::VotingPowerSnapshots,
1112
},
1213
heap_governance_data::{
1314
HeapGovernanceData, XdrConversionRate, initialize_governance, reassemble_governance_proto,
@@ -1355,6 +1356,8 @@ impl Governance {
13551356
governance.heap_data.neuron_id_to_pre_clamp_dissolve_state = governance
13561357
.neuron_store
13571358
.clamp_dissolve_delay_for_all_neurons_or_panic(now);
1359+
1360+
VOTING_POWER_SNAPSHOTS.with_borrow_mut(VotingPowerSnapshots::clear);
13581361
}
13591362

13601363
governance

rs/nns/governance/src/governance/voting_power_snapshots.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::{
55
pb::v1::{Ballot, NeuronIdToVotingPowerMap, VotingPowerTotal},
66
};
77

8-
use ic_cdk::eprintln;
8+
use ic_cdk::{eprintln, println};
99
use ic_nervous_system_common::ONE_MONTH_SECONDS;
1010
use ic_stable_structures::{
1111
DefaultMemoryImpl, StableBTreeMap, Storable, memory_manager::VirtualMemory, storable::Bound,
@@ -202,6 +202,14 @@ impl VotingPowerSnapshots {
202202
voting_power_map,
203203
totals_with_minimum_total_potential_voting_power,
204204
));
205+
println!(
206+
"{}Voting power spike detected at timestamp {}, total potential voting power: {}, \
207+
minimum total potential voting power: {}",
208+
LOG_PREFIX,
209+
timestamp_with_minimum_total_potential_voting_power,
210+
total_potential_voting_power,
211+
totals_with_minimum_total_potential_voting_power.total_potential_voting_power
212+
);
205213
Some((
206214
timestamp_with_minimum_total_potential_voting_power,
207215
previous_voting_power_snapshot,
@@ -214,6 +222,13 @@ impl VotingPowerSnapshots {
214222
.last_key_value()
215223
.map(|(timestamp, _)| timestamp)
216224
}
225+
226+
/// Clears the voting power snapshots.
227+
// TODO(NNS1-4323): Remove this method after Mission 70 is fully deployed.
228+
pub(crate) fn clear(&mut self) {
229+
self.neuron_id_to_voting_power_maps.clear_new();
230+
self.voting_power_totals.clear_new();
231+
}
217232
}
218233

219234
impl Storable for NeuronIdToVotingPowerMap {

rs/nns/governance/src/governance/voting_power_snapshots_tests.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,32 @@ fn test_record_voting_power_snapshot() {
113113
None,
114114
);
115115
}
116+
117+
// TODO(NNS1-4323): Remove this test after Mission 70 is fully deployed.
118+
#[test]
119+
fn test_clear() {
120+
let memory_manager = MemoryManager::init(DefaultMemoryImpl::default());
121+
let mut snapshots = VotingPowerSnapshots::new(
122+
memory_manager.get(MemoryId::new(0)),
123+
memory_manager.get(MemoryId::new(1)),
124+
);
125+
126+
// Record a few snapshots.
127+
for i in 1..=3 {
128+
snapshots.record_voting_power_snapshot(i, voting_power_snapshot(vec![90, 10], 100 + i));
129+
}
130+
131+
// Verify that snapshots are present.
132+
assert_eq!(snapshots.latest_snapshot_timestamp_seconds(), Some(3));
133+
134+
// Clear the snapshots.
135+
snapshots.clear();
136+
137+
// Verify that everything is empty.
138+
assert_eq!(snapshots.latest_snapshot_timestamp_seconds(), None);
139+
assert_eq!(
140+
snapshots.previous_ballots_if_voting_power_spike_detected(u64::MAX, 10),
141+
None
142+
);
143+
assert!(!snapshots.is_latest_snapshot_a_spike(10));
144+
}

0 commit comments

Comments
 (0)