Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions rs/nns/governance/api/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,13 @@ pub struct NeuronInfo {
pub deciding_voting_power: Option<u64>,
/// See analogous field in Neuron.
pub potential_voting_power: Option<u64>,

/// Base value (in e8s) used for the "8-year gang" dissolve delay bonus.
/// For neurons that had the maximum dissolve delay of 8 years before the
/// maximum dissolve delay was reduced, this is set to the total staked value
/// net of fees (including staked maturity) captured at the time of migration.
/// For all other neurons, this is 0.
pub eight_year_gang_bonus_base_e8s: Option<u64>,
}

impl NeuronInfo {
Expand Down Expand Up @@ -328,6 +335,13 @@ pub struct Neuron {
/// Per NNS policy, this is opt. Nevertheless, it will never be null.
pub potential_voting_power: Option<u64>,

/// Base value (in e8s) used for the "8-year gang" dissolve delay bonus.
/// For neurons that had the maximum dissolve delay of 8 years before the
/// maximum dissolve delay was reduced, this is set to the total staked value
/// net of fees (including staked maturity) captured at the time of migration.
/// For all other neurons, this is 0.
pub eight_year_gang_bonus_base_e8s: Option<u64>,

/// The maturity disbursements in progress for this neuron.
pub maturity_disbursements_in_progress: Option<Vec<MaturityDisbursement>>,
}
Expand Down
8 changes: 8 additions & 0 deletions rs/nns/governance/canister/governance.did
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,12 @@ type Neuron = record {
// Per NNS policy, this is opt. Nevertheless, it will never be null.
potential_voting_power : opt nat64;

// Base value (in e8s) used for the "8-year gang" dissolve delay bonus. For neurons that had the
// maximum dissolve delay of 8 years before the maximum was reduced, this is set to the total
// staked value net of fees (including staked maturity) captured at the time of migration.
// For all other neurons, this is 0.
eight_year_gang_bonus_base_e8s : opt nat64;

// The maturity disbursements in progress, i.e. the disbursements that are initiated but not
// finalized. The finalization happens 7 days after the disbursement is initiated.
maturity_disbursements_in_progress : opt vec MaturityDisbursement;
Expand Down Expand Up @@ -871,6 +877,8 @@ type NeuronInfo = record {
voting_power_refreshed_timestamp_seconds : opt nat64;
deciding_voting_power : opt nat64;
potential_voting_power : opt nat64;
// See analogous field in Neuron.
eight_year_gang_bonus_base_e8s : opt nat64;
};

type NeuronStakeTransfer = record {
Expand Down
7 changes: 5 additions & 2 deletions rs/nns/governance/src/neuron/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -894,6 +894,7 @@ impl Neuron {
deciding_voting_power: Some(deciding_voting_power),
potential_voting_power: Some(potential_voting_power),
voting_power: potential_voting_power,
eight_year_gang_bonus_base_e8s: Some(self.eight_year_gang_bonus_base_e8s),
}
}

Expand Down Expand Up @@ -1174,6 +1175,7 @@ impl TryFrom<api::Neuron> for Neuron {
neuron_type,
visibility,
voting_power_refreshed_timestamp_seconds,
eight_year_gang_bonus_base_e8s,

// We do not allow these fields to be initialized by the user.
deciding_voting_power: _,
Expand Down Expand Up @@ -1245,7 +1247,7 @@ impl TryFrom<api::Neuron> for Neuron {
// Step 6: some fields that are not set by the API type.
let recent_ballots_next_entry_index = None;
let maturity_disbursements_in_progress = vec![];
let eight_year_gang_bonus_base_e8s = 0;
let eight_year_gang_bonus_base_e8s = eight_year_gang_bonus_base_e8s.unwrap_or(0);

// Step 7: build the neuron.
Ok(Neuron {
Expand Down Expand Up @@ -1314,11 +1316,11 @@ impl Neuron {
neuron_type,
voting_power_refreshed_timestamp_seconds,
maturity_disbursements_in_progress,
eight_year_gang_bonus_base_e8s,

// Not used.
visibility: _,
recent_ballots_next_entry_index: _,
eight_year_gang_bonus_base_e8s: _,
} = self;

let id = Some(id);
Expand Down Expand Up @@ -1380,6 +1382,7 @@ impl Neuron {

potential_voting_power,
deciding_voting_power,
eight_year_gang_bonus_base_e8s: Some(eight_year_gang_bonus_base_e8s),
}
}
}
Expand Down
83 changes: 81 additions & 2 deletions rs/nns/governance/src/neuron/types/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,15 @@ fn test_neuron_into_api() {
)
.build();

original_neuron.eight_year_gang_bonus_base_e8s = 500_000_000;

// Add ballots.
for i in 0..10 {
let proposal_id = Some(ProposalId { id: 123_000 + i });

original_neuron.recent_ballots.push(pb::BallotInfo {
proposal_id,
vote: Vote::No as i32,
vote: pb::Vote::No as i32,
});
}
original_neuron.recent_ballots_next_entry_index = Some(3);
Expand Down Expand Up @@ -116,6 +118,7 @@ fn test_neuron_into_api() {
neuron_type: None,
potential_voting_power,
deciding_voting_power,
eight_year_gang_bonus_base_e8s: Some(500_000_000),
maturity_disbursements_in_progress: Some(vec![]),
},
);
Expand All @@ -134,13 +137,89 @@ fn test_neuron_into_api() {

api::BallotInfo {
proposal_id,
vote: Vote::No as i32,
vote: pb::Vote::No as i32,
}
})
.collect::<Vec<_>>(),
);
}

#[test]
fn test_get_neuron_info() {
let controller = PrincipalId::new_user_test_id(42);
let dissolve_delay_seconds = TWELVE_MONTHS_SECONDS;
let aging_since = 100_000_000;
let dissolve_state_and_age = DissolveStateAndAge::NotDissolving {
dissolve_delay_seconds,
aging_since_timestamp_seconds: aging_since,
};

let recent_ballots = vec![
pb::BallotInfo {
proposal_id: Some(ProposalId { id: 1 }),
vote: pb::Vote::Yes as i32,
},
pb::BallotInfo {
proposal_id: Some(ProposalId { id: 2 }),
vote: pb::Vote::No as i32,
},
];

let known_neuron_data = Some(pb::KnownNeuronData {
name: "test neuron".to_string(),
description: Some("a known neuron".to_string()),
..Default::default()
});

let neuron = NeuronBuilder::new_for_test(99, dissolve_state_and_age)
.with_controller(controller)
.with_cached_neuron_stake_e8s(10 * E8)
.with_eight_year_gang_bonus_base_e8s(3 * E8)
.with_recent_ballots(recent_ballots)
.with_known_neuron_data(known_neuron_data.clone())
.with_joined_community_fund_timestamp_seconds(Some(50_000_000))
.with_neuron_type(Some(pb::NeuronType::Seed as i32))
.build();

let potential_voting_power = neuron.potential_voting_power(NOW);
let deciding_voting_power = neuron.deciding_voting_power(&VotingPowerEconomics::DEFAULT, NOW);

// multi_query=false so known_neuron_data is included; requester=controller so full info shown.
let observed = neuron.get_neuron_info(&VotingPowerEconomics::DEFAULT, NOW, controller, false);

assert_eq!(
observed,
NeuronInfo {
id: Some(NeuronId { id: 99 }),
retrieved_at_timestamp_seconds: NOW,
state: NeuronState::NotDissolving as i32,
age_seconds: NOW - aging_since,
dissolve_delay_seconds,
recent_ballots: vec![
api::BallotInfo {
proposal_id: Some(ProposalId { id: 2 }),
vote: pb::Vote::No as i32,
},
api::BallotInfo {
proposal_id: Some(ProposalId { id: 1 }),
vote: pb::Vote::Yes as i32,
},
],
voting_power: potential_voting_power,
created_timestamp_seconds: 0,
stake_e8s: 10 * E8,
joined_community_fund_timestamp_seconds: Some(50_000_000),
known_neuron_data: known_neuron_data.map(api::KnownNeuronData::from),
neuron_type: Some(pb::NeuronType::Seed as i32),
visibility: Some(Visibility::Public as i32),
voting_power_refreshed_timestamp_seconds: Some(0),
deciding_voting_power: Some(deciding_voting_power),
potential_voting_power: Some(potential_voting_power),
eight_year_gang_bonus_base_e8s: Some(3 * E8),
},
);
}

#[test]
fn test_dissolve_state_and_age_conversion() {
let test_cases = vec![
Expand Down
2 changes: 2 additions & 0 deletions rs/nns/governance/tests/governance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4436,6 +4436,7 @@ fn create_mature_neuron(dissolved: bool) -> (fake::FakeDriver, Governance, api::
voting_power_refreshed_timestamp_seconds: Some(START_TIMESTAMP_SECONDS),
deciding_voting_power: Some(expected_voting_power),
potential_voting_power: Some(expected_voting_power),
eight_year_gang_bonus_base_e8s: Some(0),
maturity_disbursements_in_progress: Some(vec![]),
..Default::default()
}
Expand Down Expand Up @@ -9639,6 +9640,7 @@ fn test_include_public_neurons_in_full_neurons() {
voting_power_refreshed_timestamp_seconds: Some(START_TIMESTAMP_SECONDS),
deciding_voting_power: Some(20 * E8),
potential_voting_power: Some(20 * E8),
eight_year_gang_bonus_base_e8s: Some(0),
maturity_disbursements_in_progress: Some(vec![]),

..Default::default()
Expand Down
8 changes: 8 additions & 0 deletions rs/nns/governance/unreleased_changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ on the process that this file is part of, see
## Added

- Proposal type `DeleteSubnet`, currently limited to CloudEngine subnets.
- Tag neurons that have the maximum dissolve delay of 8 years with their bonus base
(`eight_year_gang_bonus_base_e8s`), in preparation for the dissolve delay bonus
grandfathering when the maximum dissolve delay is reduced to 2 years.
- Expose data that will be used to determine the bonus that "8 year gang" neurons
will receive, starting in the near future. This data consists of the staked amount
in neurons with 8 year dissolve delay at the beginning of Mission 70. This will be
used in the near future to determine voting power (and consequently, voting rewards),
once other aspects of voting power/rewards are in production.

## Changed

Expand Down
Loading