Skip to content

Commit 40a9c37

Browse files
authored
feat(nns): Allow Governance to request rewards from Node Reward canister instead of registry (behind flag) (#4693)
# What This allows a test version of governance to rely on the new Node Rewards canister to calculate node rewards. In the normal case, it will continue to rely on Registry. We also fix an issue in the Node Reward canister, where a query was used when an update should have been used. Since this canister is not yet considered "live" and this API endpoint should not yet be relied upon, we are overriding the DIDC_CHECK on backwards compatibility. # Why This is part of the effort to allow node rewards to be calculated based on useful work.
1 parent 95ff572 commit 40a9c37

File tree

20 files changed

+378
-222
lines changed

20 files changed

+378
-222
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rs/nns/governance/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ DEPENDENCIES = [
6868
"//rs/nns/gtc_accounts",
6969
"//rs/nns/handlers/root/interface",
7070
"//rs/nns/sns-wasm",
71+
"//rs/node_rewards/canister/api",
7172
"//rs/protobuf",
7273
"//rs/registry/canister",
7374
"//rs/rust_canisters/http_types",

rs/nns/governance/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ ic-nns-gtc-accounts = { path = "../gtc_accounts" }
6565
ic-nns-governance-api = { path = "./api" }
6666
ic-nns-governance-init = { path = "./init" }
6767
ic-nns-handler-root-interface = { path = "../handlers/root/interface" }
68+
ic-node-rewards-canister-api = { path = "../../node_rewards/canister/api" }
6869
ic-protobuf = { path = "../../protobuf" }
6970
ic-sns-init = { path = "../../sns/init" } # This is just for a couple of PB definitions.
7071
ic-sns-root = { path = "../../sns/root" } # This is just for a couple of PB definitions.

rs/nns/governance/src/governance.rs

Lines changed: 122 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ use crate::{
6767
},
6868
},
6969
proposals::{call_canister::CallCanister, sum_weighted_voting_power},
70+
use_node_provider_reward_canister,
7071
};
7172
use async_trait::async_trait;
7273
use candid::{Decode, Encode};
@@ -91,8 +92,8 @@ use ic_nns_common::{
9192
};
9293
use ic_nns_constants::{
9394
CYCLES_MINTING_CANISTER_ID, GENESIS_TOKEN_CANISTER_ID, GOVERNANCE_CANISTER_ID,
94-
LIFELINE_CANISTER_ID, REGISTRY_CANISTER_ID, ROOT_CANISTER_ID, SNS_WASM_CANISTER_ID,
95-
SUBNET_RENTAL_CANISTER_ID,
95+
LIFELINE_CANISTER_ID, NODE_REWARDS_CANISTER_ID, REGISTRY_CANISTER_ID, ROOT_CANISTER_ID,
96+
SNS_WASM_CANISTER_ID, SUBNET_RENTAL_CANISTER_ID,
9697
};
9798
use ic_nns_governance_api::{
9899
pb::v1::{
@@ -105,6 +106,9 @@ use ic_nns_governance_api::{
105106
proposal_validation,
106107
subnet_rental::SubnetRentalRequest,
107108
};
109+
use ic_node_rewards_canister_api::monthly_rewards::{
110+
GetNodeProvidersMonthlyXdrRewardsRequest, GetNodeProvidersMonthlyXdrRewardsResponse,
111+
};
108112
use ic_protobuf::registry::dc::v1::AddOrRemoveDataCentersProposalPayload;
109113
use ic_sns_init::pb::v1::SnsInitPayload;
110114
use ic_sns_swap::pb::v1::{self as sns_swap_pb, Lifecycle, NeuronsFundParticipationConstraints};
@@ -120,6 +124,7 @@ use registry_canister::{
120124
};
121125
use rust_decimal::Decimal;
122126
use rust_decimal_macros::dec;
127+
use std::str::FromStr;
123128
use std::sync::Arc;
124129
use std::{
125130
borrow::Cow,
@@ -7721,8 +7726,15 @@ impl Governance {
77217726
let mut rewards = vec![];
77227727

77237728
// Maps node providers to their rewards in XDR
7724-
let xdr_permyriad_rewards: NodeProvidersMonthlyXdrRewards =
7725-
self.get_node_providers_monthly_xdr_rewards().await?;
7729+
let (reg_rewards, maybe_version) = self.get_node_providers_monthly_xdr_rewards().await?;
7730+
let registry_version = maybe_version.ok_or_else(|| {
7731+
GovernanceError::new_with_message(
7732+
ErrorType::External,
7733+
"Registry version was not available in the response to \
7734+
get_node_providers_monthly_xdr_rewards, which indicates a problem."
7735+
.to_string(),
7736+
)
7737+
})?;
77267738

77277739
// The average (last 30 days) conversion rate from 10,000ths of an XDR to 1 ICP
77287740
let icp_xdr_conversion_rate = self.get_average_icp_xdr_conversion_rate().await?.data;
@@ -7742,9 +7754,7 @@ impl Governance {
77427754
// `rewards`
77437755
for np in &self.heap_data.node_providers {
77447756
if let Some(np_id) = &np.id {
7745-
let np_id_str = np_id.to_string();
7746-
let xdr_permyriad_reward =
7747-
*xdr_permyriad_rewards.rewards.get(&np_id_str).unwrap_or(&0);
7757+
let xdr_permyriad_reward = *reg_rewards.get(np_id).unwrap_or(&0);
77487758

77497759
if let Some(reward_node_provider) =
77507760
get_node_provider_reward(np, xdr_permyriad_reward, xdr_permyriad_per_icp)
@@ -7759,8 +7769,6 @@ impl Governance {
77597769
xdr_permyriad_per_icp: icp_xdr_conversion_rate.xdr_permyriad_per_icp,
77607770
};
77617771

7762-
let registry_version = xdr_permyriad_rewards.registry_version.unwrap();
7763-
77647772
Ok(MonthlyNodeProviderRewards {
77657773
timestamp: self.env.now(),
77667774
rewards,
@@ -7774,8 +7782,83 @@ impl Governance {
77747782

77757783
/// A helper for the Registry's get_node_providers_monthly_xdr_rewards method
77767784
async fn get_node_providers_monthly_xdr_rewards(
7777-
&mut self,
7778-
) -> Result<NodeProvidersMonthlyXdrRewards, GovernanceError> {
7785+
&self,
7786+
) -> Result<(BTreeMap<PrincipalId, u64>, Option<u64>), GovernanceError> {
7787+
if use_node_provider_reward_canister() {
7788+
self.get_node_providers_monthly_xdr_rewards_from_node_provider_reward_canister()
7789+
.await
7790+
} else {
7791+
self.get_node_providers_monthly_xdr_rewards_from_registry()
7792+
.await
7793+
}
7794+
}
7795+
7796+
async fn get_node_providers_monthly_xdr_rewards_from_node_provider_reward_canister(
7797+
&self,
7798+
) -> Result<(BTreeMap<PrincipalId, u64>, Option<u64>), GovernanceError> {
7799+
let response: Vec<u8> = self.env.call_canister_method(
7800+
NODE_REWARDS_CANISTER_ID,
7801+
"get_node_providers_monthly_xdr_rewards",
7802+
Encode!(&GetNodeProvidersMonthlyXdrRewardsRequest {registry_version: None }).unwrap(),
7803+
).await
7804+
.map_err(|(code, msg)| {
7805+
GovernanceError::new_with_message(
7806+
ErrorType::External,
7807+
format!(
7808+
"Error calling 'get_node_providers_monthly_xdr_rewards': code: {:?}, message: {}",
7809+
code, msg
7810+
),
7811+
)
7812+
})?;
7813+
7814+
let response =
7815+
Decode!(&response, GetNodeProvidersMonthlyXdrRewardsResponse).map_err(|err| {
7816+
GovernanceError::new_with_message(
7817+
ErrorType::External,
7818+
format!(
7819+
"Cannot decode return type from get_node_providers_monthly_xdr_rewards \
7820+
as GetNodeProvidersMonthlyXdrRewardsResponse'. Error: {}",
7821+
err,
7822+
),
7823+
)
7824+
})?;
7825+
7826+
let GetNodeProvidersMonthlyXdrRewardsResponse { rewards, error } = response;
7827+
7828+
if let Some(err_msg) = error {
7829+
return Err(GovernanceError::new_with_message(
7830+
ErrorType::External,
7831+
format!(
7832+
"Error calling 'get_node_providers_monthly_xdr_rewards': {}",
7833+
err_msg
7834+
),
7835+
));
7836+
}
7837+
7838+
if let Some(rewards) = rewards {
7839+
let ic_node_rewards_canister_api::monthly_rewards::NodeProvidersMonthlyXdrRewards {
7840+
rewards,
7841+
registry_version,
7842+
} = rewards;
7843+
let rewards = rewards
7844+
.into_iter()
7845+
.map(|(principal, amount)| (PrincipalId::from(principal), amount))
7846+
.collect();
7847+
return Ok((rewards, registry_version));
7848+
}
7849+
7850+
Err(GovernanceError::new_with_message(
7851+
ErrorType::Unspecified,
7852+
"get_node_providers_monthly_xdr_rewards returned empty response, \
7853+
which should be impossible.",
7854+
))
7855+
}
7856+
7857+
/// A helper to get the node provider rewards from registry (instead of Node Provider Reward Canister)
7858+
/// This will be removed once the Node Provider Reward Canister is in use.
7859+
async fn get_node_providers_monthly_xdr_rewards_from_registry(
7860+
&self,
7861+
) -> Result<(BTreeMap<PrincipalId, u64>, Option<u64>), GovernanceError> {
77797862
let registry_response:
77807863
Vec<u8> = self
77817864
.env
@@ -7796,14 +7879,35 @@ impl Governance {
77967879
})?;
77977880

77987881
Decode!(&registry_response, Result<NodeProvidersMonthlyXdrRewards, String>)
7799-
.map_err(|err| GovernanceError::new_with_message(
7800-
ErrorType::External,
7801-
format!(
7802-
"Cannot decode return type from get_node_providers_monthly_xdr_rewards'. Error: {}",
7803-
err,
7804-
),
7805-
))?
7882+
.map_err(|err| {
7883+
GovernanceError::new_with_message(
7884+
ErrorType::External,
7885+
format!(
7886+
"Cannot decode return type from get_node_providers_monthly_xdr_rewards'. Error: {}",
7887+
err,
7888+
),
7889+
)
7890+
})?
78067891
.map_err(|msg| GovernanceError::new_with_message(ErrorType::External, msg))
7892+
.map(|response| {
7893+
let NodeProvidersMonthlyXdrRewards {
7894+
rewards,
7895+
registry_version,
7896+
} = response;
7897+
7898+
let rewards = rewards
7899+
.into_iter()
7900+
.map(|(principal_str, amount)| {
7901+
(
7902+
PrincipalId::from_str(&principal_str)
7903+
.expect("Could not get principal from string"),
7904+
amount,
7905+
)
7906+
})
7907+
.collect();
7908+
7909+
(rewards, registry_version)
7910+
})
78077911
}
78087912

78097913
/// A helper for the CMC's get_average_icp_xdr_conversion_rate method

rs/nns/governance/src/lib.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,9 @@ thread_local! {
212212

213213
static IS_DISBURSE_MATURITY_ENABLED: Cell<bool>
214214
= const { Cell::new(cfg!(not(any(feature = "canbench-rs", feature = "test")))) };
215+
216+
static USE_NODE_PROVIDER_REWARD_CANISTER: Cell<bool>
217+
= const { Cell::new(cfg!(feature = "test")) };
215218
}
216219

217220
thread_local! {
@@ -317,6 +320,20 @@ pub fn temporarily_disable_disburse_maturity() -> Temporary {
317320
Temporary::new(&IS_DISBURSE_MATURITY_ENABLED, false)
318321
}
319322

323+
pub fn use_node_provider_reward_canister() -> bool {
324+
USE_NODE_PROVIDER_REWARD_CANISTER.get()
325+
}
326+
327+
#[cfg(any(test, feature = "canbench-rs", feature = "test"))]
328+
pub fn temporarily_enable_node_provider_reward_canister() -> Temporary {
329+
Temporary::new(&USE_NODE_PROVIDER_REWARD_CANISTER, true)
330+
}
331+
332+
#[cfg(any(test, feature = "canbench-rs", feature = "test"))]
333+
pub fn temporarily_disable_node_provider_reward_canister() -> Temporary {
334+
Temporary::new(&USE_NODE_PROVIDER_REWARD_CANISTER, false)
335+
}
336+
320337
pub fn decoder_config() -> DecoderConfig {
321338
let mut config = DecoderConfig::new();
322339
config.set_skipping_quota(DEFAULT_SKIPPING_QUOTA);

rs/nns/governance/tests/fake.rs

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ use ic_nns_common::{
1313
types::UpdateIcpXdrConversionRatePayload,
1414
};
1515
use ic_nns_constants::{
16-
CYCLES_MINTING_CANISTER_ID, GOVERNANCE_CANISTER_ID, LEDGER_CANISTER_ID, REGISTRY_CANISTER_ID,
17-
SNS_WASM_CANISTER_ID,
16+
CYCLES_MINTING_CANISTER_ID, GOVERNANCE_CANISTER_ID, LEDGER_CANISTER_ID,
17+
NODE_REWARDS_CANISTER_ID, REGISTRY_CANISTER_ID, SNS_WASM_CANISTER_ID,
1818
};
1919
use ic_nns_governance::{
2020
governance::{Environment, Governance, HeapGrowthPotential, RngError},
@@ -23,14 +23,15 @@ use ic_nns_governance::{
2323
GovernanceError, ManageNeuron, Motion, NetworkEconomics, Neuron, NnsFunction, Proposal,
2424
Vote,
2525
},
26+
use_node_provider_reward_canister,
2627
};
2728
use ic_nns_governance_api::pb::v1::{manage_neuron_response, ManageNeuronResponse};
2829
use ic_sns_root::{GetSnsCanistersSummaryRequest, GetSnsCanistersSummaryResponse};
2930
use ic_sns_swap::pb::v1 as sns_swap_pb;
3031
use ic_sns_wasm::pb::v1::{DeployedSns, ListDeployedSnsesRequest, ListDeployedSnsesResponse};
3132
use icp_ledger::{AccountIdentifier, Subaccount, Tokens};
3233
use lazy_static::lazy_static;
33-
use maplit::hashmap;
34+
use maplit::{btreemap, hashmap};
3435
use rand::{RngCore, SeedableRng};
3536
use rand_chacha::ChaCha20Rng;
3637
use registry_canister::pb::v1::NodeProvidersMonthlyXdrRewards;
@@ -50,6 +51,7 @@ use ic_nns_governance::governance::tla::{
5051
};
5152
use ic_nns_governance::governance::RandomnessGenerator;
5253
use ic_nns_governance::{tla_log_request, tla_log_response};
54+
use ic_node_rewards_canister_api::monthly_rewards::GetNodeProvidersMonthlyXdrRewardsResponse;
5355

5456
lazy_static! {
5557
pub(crate) static ref SNS_ROOT_CANISTER_ID: PrincipalId = PrincipalId::new_user_test_id(213599);
@@ -566,17 +568,32 @@ impl Environment for FakeDriver {
566568
}
567569

568570
if method_name == "get_node_providers_monthly_xdr_rewards" {
569-
assert_eq!(PrincipalId::from(target), REGISTRY_CANISTER_ID.get());
570-
571-
return Ok(Encode!(&Ok::<NodeProvidersMonthlyXdrRewards, String>(
572-
NodeProvidersMonthlyXdrRewards {
573-
rewards: hashmap! {
574-
PrincipalId::new_user_test_id(1).to_string() => NODE_PROVIDER_REWARD,
575-
},
576-
registry_version: Some(5)
577-
}
578-
))
579-
.unwrap());
571+
if use_node_provider_reward_canister() {
572+
assert_eq!(PrincipalId::from(target), NODE_REWARDS_CANISTER_ID.get());
573+
574+
return Ok(Encode!(&GetNodeProvidersMonthlyXdrRewardsResponse {
575+
rewards: Some(ic_node_rewards_canister_api::monthly_rewards::NodeProvidersMonthlyXdrRewards {
576+
rewards: btreemap! {
577+
PrincipalId::new_user_test_id(1).0 => NODE_PROVIDER_REWARD,
578+
},
579+
registry_version: Some(5)
580+
}),
581+
error: None
582+
})
583+
.unwrap());
584+
} else {
585+
assert_eq!(PrincipalId::from(target), REGISTRY_CANISTER_ID.get());
586+
587+
return Ok(Encode!(&Ok::<NodeProvidersMonthlyXdrRewards, String>(
588+
NodeProvidersMonthlyXdrRewards {
589+
rewards: hashmap! {
590+
PrincipalId::new_user_test_id(1).to_string() => NODE_PROVIDER_REWARD,
591+
},
592+
registry_version: Some(5)
593+
}
594+
))
595+
.unwrap());
596+
}
580597
}
581598

582599
if method_name == "get_average_icp_xdr_conversion_rate" {

rs/nns/governance/unreleased_changelog.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ In general, upcoming/unreleased behavior changes are described here. For details
44
on the process that this file is part of, see
55
`rs/nervous_system/changelog_process.md`.
66

7-
87
# Next Upgrade Proposal
98

109
## Added
1110

11+
* Governance now gets node provider rewards from the Node Reward Canister in test builds.
12+
1213
## Changed
1314

1415
* The `_pb` methods now always panic.

rs/nns/handlers/root/impl/BUILD.bazel

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ rust_ic_test_suite(
198198
),
199199
aliases = ALIASES,
200200
data = [
201+
# Keep sorted.
201202
":root-canister",
202203
":upgrade-test-canister",
203204
"//rs/ledger_suite/icp/ledger:ledger-canister-wasm-notify-method",
@@ -206,6 +207,7 @@ rust_ic_test_suite(
206207
"//rs/nns/gtc:genesis-token-canister",
207208
"//rs/nns/handlers/lifeline/impl:lifeline_canister",
208209
"//rs/nns/sns-wasm:sns-wasm-canister",
210+
"//rs/node_rewards/canister:node-rewards-canister",
209211
"//rs/registry/canister:registry-canister",
210212
"//rs/universal_canister/impl:universal_canister.wasm.gz",
211213
],
@@ -216,6 +218,7 @@ rust_ic_test_suite(
216218
"GOVERNANCE_CANISTER_TEST_WASM_PATH": "$(rootpath //rs/nns/governance:governance-canister-test)",
217219
"LEDGER_CANISTER_NOTIFY_METHOD_WASM_PATH": "$(rootpath //rs/ledger_suite/icp/ledger:ledger-canister-wasm-notify-method)",
218220
"LIFELINE_WASM_PATH": "$(rootpath //rs/nns/handlers/lifeline/impl:lifeline_canister)",
221+
"NODE_REWARDS_CANISTER_WASM_PATH": "$(rootpath //rs/node_rewards/canister:node-rewards-canister)",
219222
"REGISTRY_CANISTER_WASM_PATH": "$(rootpath //rs/registry/canister:registry-canister)",
220223
"ROOT_CANISTER_WASM_PATH": "$(rootpath //rs/nns/handlers/root/impl:root-canister)",
221224
"SNS_WASM_CANISTER_WASM_PATH": "$(rootpath //rs/nns/sns-wasm:sns-wasm-canister)",

rs/nns/integration_tests/src/cycles_minting_canister.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -802,7 +802,7 @@ fn test_cmc_automatically_refunds_when_memo_is_garbage() {
802802
let assert_canister_statuses_fixed = |test_phase| {
803803
assert_eq!(
804804
btreemap! {
805-
btreemap! { "status".to_string() => "running".to_string() } => 11,
805+
btreemap! { "status".to_string() => "running".to_string() } => 17,
806806
btreemap! { "status".to_string() => "stopped".to_string() } => 0,
807807
btreemap! { "status".to_string() => "stopping".to_string() } => 0,
808808
},

0 commit comments

Comments
 (0)