From a7cc7a94e1c70fdb172a7a3ac53e177a4fc23bb0 Mon Sep 17 00:00:00 2001 From: Jason Zhu Date: Fri, 19 Dec 2025 05:58:40 +0000 Subject: [PATCH 1/2] Use hard coded DID file for uninstall_code NNS Function --- .../src/proposals/execute_nns_function.rs | 18 ++++++++ .../proposals/execute_nns_function_tests.rs | 46 ++++++++++++++++++- 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/rs/nns/governance/src/proposals/execute_nns_function.rs b/rs/nns/governance/src/proposals/execute_nns_function.rs index a1f58803d629..79f74f654e1f 100644 --- a/rs/nns/governance/src/proposals/execute_nns_function.rs +++ b/rs/nns/governance/src/proposals/execute_nns_function.rs @@ -17,6 +17,17 @@ use ic_nns_constants::{ }; use std::sync::Arc; +/// A partial Candid interface for the management canister (ic_00) that contains the necessary +/// methods for all the ExecuteNnsFunction proposals. Currently, only the uninstall_code method is +/// supported. +const PARTIAL_IC_00_CANDID: &str = r#"type uninstall_code_args = record { + canister_id : principal; + sender_canister_version : opt nat64; +}; +service ic : { + uninstall_code : (uninstall_code_args) -> (); +}"#; + #[derive(Debug, Clone, PartialEq)] pub struct ValidExecuteNnsFunction { pub nns_function: ValidNnsFunction, @@ -72,6 +83,13 @@ impl ValidExecuteNnsFunction { async fn get_candid_source(&self, env: Arc) -> Result { let (canister_id, _method_name) = self.nns_function.canister_and_function(); + + // The management canister (ic_00) doesn't expose candid:service metadata, so we return a + // hard-coded DID file for it. + if canister_id == CanisterId::ic_00() { + return Ok(PARTIAL_IC_00_CANDID.to_string()); + } + let request = CanisterMetadataRequest::new(canister_id, "candid:service".to_string()); let encoded_request = Encode!(&request).expect("Failed to encode payload"); let response = env diff --git a/rs/nns/governance/src/proposals/execute_nns_function_tests.rs b/rs/nns/governance/src/proposals/execute_nns_function_tests.rs index 535c973991e1..9cd2c81d3bc3 100644 --- a/rs/nns/governance/src/proposals/execute_nns_function_tests.rs +++ b/rs/nns/governance/src/proposals/execute_nns_function_tests.rs @@ -6,7 +6,7 @@ use crate::{ }, test_utils::{ExpectedCallCanisterMethodCallArguments, MockEnvironment}, }; -use candid::Encode; +use candid::{Encode, Nat}; use ic_base_types::CanisterId; use ic_management_canister_types_private::{CanisterMetadataRequest, CanisterMetadataResponse}; use ic_nns_constants::CYCLES_MINTING_CANISTER_ID; @@ -186,3 +186,47 @@ service : { }) ); } + +#[tokio::test] +async fn test_to_self_describing_uninstall_code() { + // Create the uninstall_code_args payload + #[derive(candid::CandidType)] + struct UninstallCodeArgs { + canister_id: CanisterId, + sender_canister_version: Option, + } + + let target_canister = CanisterId::from_u64(123); + let arg = UninstallCodeArgs { + canister_id: target_canister, + sender_canister_version: Some(42), + }; + let payload = Encode!(&arg).unwrap(); + + let execute_nns_function = ValidExecuteNnsFunction { + nns_function: ValidNnsFunction::UninstallCode, + payload, + }; + + // No canister_metadata call expected a hard-coded DID file is used instead. + let env = Arc::new(MockEnvironment::new(vec![], 0)); + + let proposal_action = ValidProposalAction::ExecuteNnsFunction(execute_nns_function); + let result = proposal_action.to_self_describing(env).await.unwrap(); + + assert_eq!(result.type_name, "Uninstall Code"); + assert!( + result + .type_description + .contains("Uninstall code of a canister") + ); + assert_eq!( + SelfDescribingValue::from(result.value.unwrap()), + SelfDescribingValue::Map(hashmap! { + "canister_id".to_string() => SelfDescribingValue::Text("2uzo4-eaaaa-aaaaa-aab5q-cai".to_string()), + "sender_canister_version".to_string() => SelfDescribingValue::Array(vec![ + SelfDescribingValue::Nat(Nat::from(42u64)), + ]), + }) + ); +} From 7d32f09718bc47c85cab3a2926fd5c3808b3d083 Mon Sep 17 00:00:00 2001 From: Jason Zhu Date: Tue, 6 Jan 2026 00:20:45 +0000 Subject: [PATCH 2/2] Address comments --- rs/nns/governance/src/proposals/execute_nns_function_tests.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rs/nns/governance/src/proposals/execute_nns_function_tests.rs b/rs/nns/governance/src/proposals/execute_nns_function_tests.rs index 9cd2c81d3bc3..b61dd819f30e 100644 --- a/rs/nns/governance/src/proposals/execute_nns_function_tests.rs +++ b/rs/nns/governance/src/proposals/execute_nns_function_tests.rs @@ -223,9 +223,9 @@ async fn test_to_self_describing_uninstall_code() { assert_eq!( SelfDescribingValue::from(result.value.unwrap()), SelfDescribingValue::Map(hashmap! { - "canister_id".to_string() => SelfDescribingValue::Text("2uzo4-eaaaa-aaaaa-aab5q-cai".to_string()), + "canister_id".to_string() => SelfDescribingValue::Text(target_canister.to_string()), "sender_canister_version".to_string() => SelfDescribingValue::Array(vec![ - SelfDescribingValue::Nat(Nat::from(42u64)), + SelfDescribingValue::Nat(Nat::from(42_u64)), ]), }) );