Skip to content

Commit e878738

Browse files
feat(governance): expose get_maturity_modulation query endpoint (#9879)
## Why PR #9847 computes Mission 70 maturity modulation locally and stores it in canister state, but the computed value is not yet consumed by spawning or disbursement (those still rely on CMC). This PR exposes the locally computed value through a new `get_maturity_modulation` query endpoint so it can be observed and validated against the existing CMC-based modulation before the switchover happens in a follow-up PR. External tools and dashboards can also use this endpoint to read the current maturity modulation value. https://dfinity.atlassian.net/browse/NNS1-4319 ## What - Add MaturityModulation, GetMaturityModulationRequest, and GetMaturityModulationResponse types to the ic-nns-governance-api crate - Add Governance::get_maturity_modulation() method - Add #[query] fn get_maturity_modulation endpoint to the governance canister - Add corresponding types and service entry to governance.did - Add pb::api From conversion for MaturityModulation ### PR Chain - ⬅️ Previous: #9847
1 parent cb41874 commit e878738

9 files changed

Lines changed: 158 additions & 11 deletions

File tree

rs/nns/governance/api/src/types.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4741,3 +4741,23 @@ pub struct CreatedNeuron {
47414741
}
47424742

47434743
pub type CreateNeuronResponse = Result<CreatedNeuron, GovernanceError>;
4744+
4745+
#[derive(
4746+
candid::CandidType, candid::Deserialize, serde::Serialize, Debug, Clone, PartialEq, Default,
4747+
)]
4748+
pub struct MaturityModulation {
4749+
pub current_value_permyriad: Option<i32>,
4750+
pub updated_at_timestamp_seconds: Option<u64>,
4751+
}
4752+
4753+
#[derive(
4754+
candid::CandidType, candid::Deserialize, serde::Serialize, Debug, Clone, PartialEq, Default,
4755+
)]
4756+
pub struct GetMaturityModulationRequest {}
4757+
4758+
#[derive(
4759+
candid::CandidType, candid::Deserialize, serde::Serialize, Debug, Clone, PartialEq, Default,
4760+
)]
4761+
pub struct GetMaturityModulationResponse {
4762+
pub maturity_modulation: Option<MaturityModulation>,
4763+
}

rs/nns/governance/canister/canister.rs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,15 @@ use ic_nns_governance::{
2929
use ic_nns_governance_api::test_api::TimeWarp;
3030
use ic_nns_governance_api::{
3131
ClaimOrRefreshNeuronFromAccount, ClaimOrRefreshNeuronFromAccountResponse, CreateNeuronRequest,
32-
CreateNeuronResponse, GetNeuronIndexRequest, GetNeuronsFundAuditInfoRequest,
33-
GetNeuronsFundAuditInfoResponse, GetPendingProposalsRequest, Governance as ApiGovernanceProto,
34-
GovernanceError, ListKnownNeuronsResponse, ListNeuronVotesRequest, ListNeuronVotesResponse,
35-
ListNeurons, ListNeuronsResponse, ListNodeProviderRewardsRequest,
36-
ListNodeProviderRewardsResponse, ListNodeProvidersResponse, ListProposalInfoRequest,
37-
ListProposalInfoResponse, ManageNeuronCommandRequest, ManageNeuronRequest,
38-
ManageNeuronResponse, MonthlyNodeProviderRewards, NetworkEconomics, Neuron, NeuronIndexData,
39-
NeuronInfo, NodeProvider, Proposal, ProposalInfo, RestoreAgingSummary, RewardEvent,
32+
CreateNeuronResponse, GetMaturityModulationRequest, GetMaturityModulationResponse,
33+
GetNeuronIndexRequest, GetNeuronsFundAuditInfoRequest, GetNeuronsFundAuditInfoResponse,
34+
GetPendingProposalsRequest, Governance as ApiGovernanceProto, GovernanceError,
35+
ListKnownNeuronsResponse, ListNeuronVotesRequest, ListNeuronVotesResponse, ListNeurons,
36+
ListNeuronsResponse, ListNodeProviderRewardsRequest, ListNodeProviderRewardsResponse,
37+
ListNodeProvidersResponse, ListProposalInfoRequest, ListProposalInfoResponse,
38+
ManageNeuronCommandRequest, ManageNeuronRequest, ManageNeuronResponse,
39+
MonthlyNodeProviderRewards, NetworkEconomics, Neuron, NeuronIndexData, NeuronInfo,
40+
NodeProvider, Proposal, ProposalInfo, RestoreAgingSummary, RewardEvent,
4041
SettleCommunityFundParticipation, SettleNeuronsFundParticipationRequest,
4142
SettleNeuronsFundParticipationResponse, UpdateNodeProvider, Vote,
4243
claim_or_refresh_neuron_from_account_response::Result as ClaimOrRefreshNeuronFromAccountResponseResult,
@@ -577,6 +578,15 @@ fn get_restore_aging_summary() -> RestoreAgingSummary {
577578
RestoreAgingSummary::from(response)
578579
}
579580

581+
/// Returns the current maturity modulation, as defined by Mission 70.
582+
#[query]
583+
fn get_maturity_modulation(
584+
_request: GetMaturityModulationRequest,
585+
) -> GetMaturityModulationResponse {
586+
debug_log("get_maturity_modulation");
587+
with_governance(|governance| governance.get_maturity_modulation())
588+
}
589+
580590
#[query]
581591
fn list_neuron_votes(request: ListNeuronVotesRequest) -> ListNeuronVotesResponse {
582592
with_governance(|governance| {

rs/nns/governance/canister/governance.did

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,17 @@ type GetNeuronsFundAuditInfoRequest = record {
334334
nns_proposal_id : opt ProposalId;
335335
};
336336

337+
type MaturityModulation = record {
338+
current_value_permyriad : opt int32;
339+
updated_at_timestamp_seconds : opt nat64;
340+
};
341+
342+
type GetMaturityModulationRequest = record {};
343+
344+
type GetMaturityModulationResponse = record {
345+
maturity_modulation : opt MaturityModulation;
346+
};
347+
337348
type GetNeuronsFundAuditInfoResponse = record {
338349
result : opt Result_6;
339350
};
@@ -1599,6 +1610,9 @@ service : (Governance) -> {
15991610
Result_2,
16001611
) query;
16011612
get_latest_reward_event : () -> (RewardEvent) query;
1613+
get_maturity_modulation : (GetMaturityModulationRequest) -> (
1614+
GetMaturityModulationResponse,
1615+
) query;
16021616
get_metrics : () -> (Result_3) query;
16031617
get_monthly_node_provider_rewards : () -> (Result_4);
16041618
get_most_recent_monthly_node_provider_rewards : () -> (

rs/nns/governance/src/governance.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,9 @@ use ic_nns_constants::{
108108
};
109109
use ic_nns_governance_api::{
110110
self as api, CreateServiceNervousSystem as ApiCreateServiceNervousSystem,
111-
GetNeuronIndexRequest, GetPendingProposalsRequest, ListNeuronVotesRequest, ListNeurons,
112-
ListNeuronsResponse, ListProposalInfoRequest, ListProposalInfoResponse, ManageNeuronResponse,
111+
GetMaturityModulationResponse, GetNeuronIndexRequest, GetPendingProposalsRequest,
112+
ListNeuronVotesRequest, ListNeurons, ListNeuronsResponse, ListProposalInfoRequest,
113+
ListProposalInfoResponse, ManageNeuronResponse, MaturityModulation as ApiMaturityModulation,
113114
NeuronIndexData, NeuronInfo, NeuronVote, NeuronVotes, ProposalInfo,
114115
manage_neuron_response::{self, StakeMaturityResponse},
115116
proposal_validation::{
@@ -8030,6 +8031,16 @@ impl Governance {
80308031
pub fn get_restore_aging_summary(&self) -> Option<RestoreAgingSummary> {
80318032
self.heap_data.restore_aging_summary.clone()
80328033
}
8034+
8035+
/// Returns the current maturity modulation, as defined by Mission 70.
8036+
pub fn get_maturity_modulation(&self) -> GetMaturityModulationResponse {
8037+
GetMaturityModulationResponse {
8038+
maturity_modulation: self
8039+
.heap_data
8040+
.maturity_modulation
8041+
.map(ApiMaturityModulation::from),
8042+
}
8043+
}
80338044
}
80348045

80358046
impl From<NeuronSubsetMetrics> for NeuronSubsetMetricsPb {
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
use crate::governance::{
2+
Governance,
3+
tests::{MockEnvironment, StubCMC, StubIcpLedger},
4+
};
5+
use crate::pb::v1::MaturityModulation;
6+
use crate::test_utils::MockRandomness;
7+
use ic_nervous_system_common::ONE_DAY_SECONDS;
8+
use ic_nns_governance_api::{
9+
GetMaturityModulationResponse, MaturityModulation as ApiMaturityModulation,
10+
};
11+
use std::sync::Arc;
12+
13+
fn make_governance() -> Governance {
14+
Governance::new(
15+
Default::default(),
16+
Arc::new(MockEnvironment::new(vec![], 0)),
17+
Arc::new(StubIcpLedger {}),
18+
Arc::new(StubCMC {}),
19+
Box::new(MockRandomness::new()),
20+
)
21+
}
22+
23+
#[test]
24+
fn returns_none_when_unset() {
25+
let governance = make_governance();
26+
27+
let response = governance.get_maturity_modulation();
28+
29+
assert_eq!(
30+
response,
31+
GetMaturityModulationResponse {
32+
maturity_modulation: None
33+
}
34+
);
35+
}
36+
37+
#[test]
38+
fn converts_days_since_epoch_to_seconds() {
39+
let mut governance = make_governance();
40+
governance.heap_data.maturity_modulation = Some(MaturityModulation {
41+
current_value_permyriad: Some(123),
42+
updated_at_days_since_epoch: Some(20_000),
43+
});
44+
45+
let response = governance.get_maturity_modulation();
46+
47+
assert_eq!(
48+
response,
49+
GetMaturityModulationResponse {
50+
maturity_modulation: Some(ApiMaturityModulation {
51+
current_value_permyriad: Some(123),
52+
updated_at_timestamp_seconds: Some(20_000 * ONE_DAY_SECONDS),
53+
}),
54+
}
55+
);
56+
}
57+
58+
#[test]
59+
fn updated_at_returns_none_when_days_overflow() {
60+
let mut governance = make_governance();
61+
governance.heap_data.maturity_modulation = Some(MaturityModulation {
62+
current_value_permyriad: Some(123),
63+
updated_at_days_since_epoch: Some(u64::MAX),
64+
});
65+
66+
let response = governance.get_maturity_modulation();
67+
68+
assert_eq!(
69+
response,
70+
GetMaturityModulationResponse {
71+
maturity_modulation: Some(ApiMaturityModulation {
72+
current_value_permyriad: Some(123),
73+
updated_at_timestamp_seconds: None,
74+
}),
75+
}
76+
);
77+
}

rs/nns/governance/src/governance/tests/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use lazy_static::lazy_static;
2424
use maplit::hashmap;
2525
use std::{convert::TryFrom, time::Duration};
2626

27+
mod get_maturity_modulation;
2728
mod get_neuron_index;
2829
mod list_neurons;
2930
mod list_proposals;

rs/nns/governance/src/pb/conversions/mod.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::pb::{
44
};
55

66
use ic_crypto_sha2::Sha256;
7+
use ic_nervous_system_common::ONE_DAY_SECONDS;
78
use ic_nns_governance_api as api;
89
use ic_nns_governance_conversions::{
910
convert_guest_launch_measurements_from_api_to_pb,
@@ -4076,3 +4077,14 @@ impl From<api::TakeCanisterSnapshot> for pb::TakeCanisterSnapshot {
40764077
}
40774078
}
40784079
}
4080+
4081+
impl From<pb::MaturityModulation> for api::MaturityModulation {
4082+
fn from(item: pb::MaturityModulation) -> Self {
4083+
Self {
4084+
current_value_permyriad: item.current_value_permyriad,
4085+
updated_at_timestamp_seconds: item
4086+
.updated_at_days_since_epoch
4087+
.and_then(|days| days.checked_mul(ONE_DAY_SECONDS)),
4088+
}
4089+
}
4090+
}

rs/nns/governance/unreleased_changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ on the process that this file is part of, see
1111

1212
* Daily timer task that fetches ICP/XDR rates from the Exchange Rate Canister, maintains a 365-day price history in Governance state, and computes Mission 70 maturity modulation locally. The computed value is not yet consumed by spawning or disbursement; that switchover will happen in a follow-up PR.
1313

14+
* `get_maturity_modulation` query endpoint that returns the current Mission 70 maturity modulation value, including `current_value_permyriad` and `updated_at_timestamp_seconds`.
15+
1416
* Expose `staked_maturity_e8s_equivalent` on `NeuronInfo`, so external callers
1517
can read staked maturity from `get_neuron_info` / `list_neurons` responses.
1618

rs/nns/nns.bzl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ CANISTER_NAME_TO_MAX_COMPRESSED_WASM_SIZE_E5_BYTES = {
2222
"cycles-minting-canister.wasm.gz": 6,
2323
"genesis-token-canister.wasm.gz": 3,
2424
"governance-canister.wasm.gz": 22,
25-
"governance-canister_test.wasm.gz": 23,
25+
"governance-canister_test.wasm.gz": 24,
2626
"registry-canister.wasm.gz": 16,
2727
"root-canister.wasm.gz": 5,
2828
}

0 commit comments

Comments
 (0)