Where can I find the wasm.gz
file for the NNS governance canister, and what are the required initialization parameters to spin up that canister locally?
Follow this format to get the latest wasm.gz
file of any IC canister:
https://download.dfinity.systems/ic/$IC_VERSION/canisters/$CANISTER_NAME_IN_THE_LIST
With:
IC_VERSION
as the latest release commit that can be found on the IC Data Dashboard releases
CANISTER_NAME_IN_THE_LIST
as the canister name in this list.
In this case, the last IC version is bb76748d1d225c08d88037e99ca9a066f97de496
and the canister name is governance-canister.wasm.gz
:
https://download.dfinity.systems/ic/bb76748d1d225c08d88037e99ca9a066f97de496/canisters/governance-canister.wasm.gz
For initialization parameters, maybe @jasonzhu or @daniel-wong can help?
Not sure about the exact use case, but perhaps you can use the dfx nns extension (documentation) if the goal is to run a governance canister on a local replica. It fetches all the NNS WASMs and install them on the local replica with reasonable init args.
Iām aware of this command, thatās not the goal. Also I donāt use dfx here.
Alright, then it would be very helpful to specify the use case and the environment.
Without knowing more, if I take āthe required initialization parametersā literally, the init args is a candid-serialized blob and it should be possible to build it using the init library of NNS Governance (init.rs) and then serialize it with candid.
Yeah I know can either retro-engineer the did file or source code on my spare time to understand what parameters are required or not but, given that there is absolutely zero documentation on the website, I was hoping for some guidance on what parameters are required and what are their expected values.
Anyway I guess those are the one I need to provide.
type Governance = record {
default_followees : vec record { int32; Followees };
making_sns_proposal : opt MakingSnsProposal;
most_recent_monthly_node_provider_rewards : opt MostRecentMonthlyNodeProviderRewards;
maturity_modulation_last_updated_at_timestamp_seconds : opt nat64;
wait_for_quiet_threshold_seconds : nat64;
metrics : opt GovernanceCachedMetrics;
node_providers : vec NodeProvider;
cached_daily_maturity_modulation_basis_points : opt int32;
economics : opt NetworkEconomics;
spawning_neurons : opt bool;
latest_reward_event : opt RewardEvent;
to_claim_transfers : vec NeuronStakeTransfer;
short_voting_period_seconds : nat64;
migrations : opt Migrations;
proposals : vec record { nat64; ProposalData };
in_flight_commands : vec record { nat64; NeuronInFlightCommand };
neurons : vec record { nat64; Neuron };
genesis_timestamp_seconds : nat64;
};
Reject text: Canister rrkah-fqaaa-aaaaa-aaaaq-cai trapped explicitly: Panicked at āCouldnāt initialize canister.: DecodeError { description: āinvalid wire type: EndGroup (expected LengthDelimited)ā, stack: [(āGovernanceā, āeconomicsā)] }ā, rs/nns/governance/canister/canister.rs:292:6
import {init} from '../declarations/governance.idl';
const E8S_PER_ICP = 100_000_000n;
const DEFAULT_TRANSFER_FEE = 10_000n;
const economics: NetworkEconomics = {
neuron_minimum_stake_e8s: E8S_PER_ICP, // 1 ICP
max_proposals_to_keep_per_topic: 100,
neuron_management_fee_per_proposal_e8s: 1_000_000n, // 0.01 ICP
reject_cost_e8s: E8S_PER_ICP, // 1 ICP
transaction_fee_e8s: DEFAULT_TRANSFER_FEE,
neuron_spawn_dissolve_delay_seconds: BigInt(24 * 60 * 60* 7), // 7 days
minimum_icp_xdr_rate: 100n, // 1 XDR
maximum_node_provider_rewards_e8s: BigInt(1_000_000 * 100_000_000,) // 1M ICP
}
const sourceArg: Governance = {
default_followees: [],
making_sns_proposal: [],
most_recent_monthly_node_provider_rewards: [],
maturity_modulation_last_updated_at_timestamp_seconds: [],
wait_for_quiet_threshold_seconds: BigInt(60 * 60 * 24 * 4), // 4 days
metrics: [],
node_providers: [],
cached_daily_maturity_modulation_basis_points: [],
economics: [economics],
spawning_neurons: [],
latest_reward_event: [],
to_claim_transfers: [],
short_voting_period_seconds: BigInt(60 * 60 * 12), // 12 hours
migrations: [],
proposals: [],
in_flight_commands: [],
neurons: [],
genesis_timestamp_seconds: 1n
};
const arg = IDL.encode(init({IDL}), [sourceArg]);
Bumped to last release git hash e790c6636115482db53ca3daa2f1900202ab04cf.
juno-satellite-1 | Reject code: 5
juno-satellite-1 | Reject text: Canister rrkah-fqaaa-aaaaa-aaaaq-cai trapped explicitly: Panicked at āCouldnāt initialize canister.: DecodeError { description: āinvalid wire type: EndGroup (expected LengthDelimited)ā, stack: [(āGovernanceā, āeconomicsā)] }ā, rs/nns/governance/canister/canister.rs:317:6
I donāt know the heck is happening when the payload is decoded here.
Is the provided governance did file (https://raw.githubusercontent.com/dfinity/ic/e790c6636115482db53ca3daa2f1900202ab04cf/rs/nns/governance/canister/governance.did
) encoded correctly?
In the did file the service is declared as following:
service : (Governance) -> {
But the error says
stack: [("Governance", "economics")]
[Canister rrkah-fqaaa-aaaaa-aaaaq-cai] Error deserializing canister state in initialization: failed to decode Protobuf message: Governance.economics: invalid wire type: EndGroup (expected LengthDelimited).
Wait, you cannot initialize that freaking canister with Candid but, only protobuf?
6 hours spent, still not the right parameters. Love itā¦
// Source: https://github.com/dfinity/ic/blob/e90838a1687f8e0869d85343aac2845d883f74ff/rs/nns/governance/src/governance.rs#L231
const E8S_PER_ICP = 100_000_000n;
const DEFAULT_TRANSFER_FEE = 10_000n;
const neurons_fund_economics: NeuronsFundEconomics = {
maximum_icp_xdr_rate: [
{
basis_points: [1_000_000n] // // 1:100
}
],
minimum_icp_xdr_rate: [
{
basis_points: [10_000n] // 1:1
}
],
neurons_fund_matched_funding_curve_coefficients: [
{
contribution_threshold_xdr: [
{
human_readable: ['75_000.0']
}
],
one_third_participation_milestone_xdr: [
{
human_readable: ['225_000.0']
}
],
full_participation_milestone_xdr: [
{
human_readable: ['375_000.0']
}
]
}
],
max_theoretical_neurons_fund_participation_amount_xdr: [
{
human_readable: ['750_000.0']
}
]
};
const economics: NetworkEconomics = {
neuron_minimum_stake_e8s: E8S_PER_ICP, // 1 ICP
max_proposals_to_keep_per_topic: 100,
neuron_management_fee_per_proposal_e8s: 1_000_000n, // 0.01 ICP
reject_cost_e8s: E8S_PER_ICP, // 1 ICP
transaction_fee_e8s: DEFAULT_TRANSFER_FEE,
neuron_spawn_dissolve_delay_seconds: BigInt(24 * 60 * 60 * 7), // 7 days
minimum_icp_xdr_rate: 100n, // 1 XDR
maximum_node_provider_rewards_e8s: BigInt(1_000_000 * 100_000_000), // 1M ICP,
neurons_fund_economics: [neurons_fund_economics]
};
const sourceArg: Governance = {
default_followees: [],
making_sns_proposal: [],
most_recent_monthly_node_provider_rewards: [],
maturity_modulation_last_updated_at_timestamp_seconds: [],
wait_for_quiet_threshold_seconds: BigInt(60 * 60 * 24 * 4), // 4 days
metrics: [],
node_providers: [],
cached_daily_maturity_modulation_basis_points: [],
economics: [economics],
spawning_neurons: [false],
latest_reward_event: [],
to_claim_transfers: [],
short_voting_period_seconds: BigInt(60 * 60 * 12), // 12 hours
migrations: [],
proposals: [],
in_flight_commands: [],
neurons: [],
genesis_timestamp_seconds: 1n,
neuron_management_voting_period_seconds: [],
restore_aging_summary: [],
topic_followee_index: [],
xdr_conversion_rate: [
{
xdr_permyriad_per_icp: [10_000n],
timestamp_seconds: [1n]
}
]
};
// Type definitions generated by Candid are not clean enough.
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
const arg = IDL.encode(init({IDL}), [sourceArg]);
=>
2024-05-10 11:53:25.462486500 UTC: [Canister rrkah-fqaaa-aaaaa-aaaaq-cai] Error deserializing canister state in initialization: failed to decode Protobuf message: Governance.economics: invalid wire type: EndGroup (expected LengthDelimited).
2024-05-10 11:53:25.462486500 UTC: [Canister rrkah-fqaaa-aaaaa-aaaaq-cai] Panicked at āCouldnāt initialize canister.: DecodeError { description: āinvalid wire type: EndGroup (expected LengthDelimited)ā, stack: [(āGovernanceā, āeconomicsā)] }ā, rs/nns/governance/canister/canister.rs:317:6
May 10 11:53:28.441 INFO s:s4n6d-5ipoi-2loaq-copwn-cgb2w-zzskv-wfsfm-3dnwk-rseqo-h56ts-7qe/n:lagdo-6ghtj-kxszl-7pa7f-wdf34-lault-uxqf2-qp4ae-wzow6-nf3lz-oae/ic_execution_environment/install Executing (canister_init) on canister rrkah-fqaaa-aaaaa-aaaaq-cai consumed 77203 instructions. 191841644797 instructions are left.
May 10 11:53:28.442 INFO s:s4n6d-5ipoi-2loaq-copwn-cgb2w-zzskv-wfsfm-3dnwk-rseqo-h56ts-7qe/n:lagdo-6ghtj-kxszl-7pa7f-wdf34-lault-uxqf2-qp4ae-wzow6-nf3lz-oae/ic_execution_environment/execution_environment Finished executing install_code message on canister CanisterId(rrkah-fqaaa-aaaaa-aaaaq-cai) after 1.7273262919999999 with error: Hypervisor(CanisterId(rrkah-fqaaa-aaaaa-aaaaq-cai), CalledTrap(āPanicked at āCouldnāt initialize canister.: DecodeError { description: "invalid wire type: EndGroup (expected LengthDelimited)", stack: [("Governance", "economics")] }ā, rs/nns/governance/canister/canister.rs:317:6ā)), instructions consumed 8158355203
May 10 11:53:28.478 INFO s:s4n6d-5ipoi-2loaq-copwn-cgb2w-zzskv-wfsfm-3dnwk-rseqo-h56ts-7qe/n:lagdo-6ghtj-kxszl-7pa7f-wdf34-lault-uxqf2-qp4ae-wzow6-nf3lz-oae/ic_execution_environment/execution_environment Start executing install_code message on canister CanisterId(jx5yt-yyaaa-aaaal-abzbq-cai)
Request ID: 13e7d96cee60518788403af8e532c61c44785aa42157b54590072815adcfc017
Reject code: 5
Reject text: Canister rrkah-fqaaa-aaaaa-aaaaq-cai trapped explicitly: Panicked at āCouldnāt initialize canister.: DecodeError { description: āinvalid wire type: EndGroup (expected LengthDelimited)ā, stack: [(āGovernanceā, āeconomicsā)] }ā, rs/nns/governance/canister/canister.rs:317:6
Given that there seem to be some expected tag order in the NNS canister I sorted the parameters, no luck neither.
const E8S_PER_ICP = 100_000_000n;
const DEFAULT_TRANSFER_FEE = 10_000n;
const neurons_fund_economics: NeuronsFundEconomics = {
max_theoretical_neurons_fund_participation_amount_xdr: [
{
human_readable: ['750_000.0']
}
],
neurons_fund_matched_funding_curve_coefficients: [
{
contribution_threshold_xdr: [
{
human_readable: ['75_000.0']
}
],
one_third_participation_milestone_xdr: [
{
human_readable: ['225_000.0']
}
],
full_participation_milestone_xdr: [
{
human_readable: ['375_000.0']
}
]
}
],
minimum_icp_xdr_rate: [
{
basis_points: [10_000n] // 1:1
}
],
maximum_icp_xdr_rate: [
{
basis_points: [1_000_000n] // // 1:100
}
]
};
const economics: NetworkEconomics = {
reject_cost_e8s: E8S_PER_ICP, // 1 ICP
neuron_minimum_stake_e8s: E8S_PER_ICP, // 1 ICP
neuron_management_fee_per_proposal_e8s: 1_000_000n, // 0.01 ICP
minimum_icp_xdr_rate: 100n, // 1 XDR
neuron_spawn_dissolve_delay_seconds: BigInt(24 * 60 * 60 * 7), // 7 days
maximum_node_provider_rewards_e8s: BigInt(1_000_000 * 100_000_000), // 1M ICP,
transaction_fee_e8s: DEFAULT_TRANSFER_FEE,
max_proposals_to_keep_per_topic: 100,
neurons_fund_economics: [neurons_fund_economics]
};
const sourceArg: Governance = {
neurons: [],
proposals: [],
to_claim_transfers: [],
wait_for_quiet_threshold_seconds: BigInt(60 * 60 * 24 * 4), // 4 days
economics: [economics],
latest_reward_event: [],
in_flight_commands: [],
genesis_timestamp_seconds: 1n,
node_providers: [],
default_followees: [],
short_voting_period_seconds: BigInt(60 * 60 * 12), // 12 hours
neuron_management_voting_period_seconds: [],
metrics: [],
most_recent_monthly_node_provider_rewards: [],
cached_daily_maturity_modulation_basis_points: [],
maturity_modulation_last_updated_at_timestamp_seconds: [],
spawning_neurons: [false],
making_sns_proposal: [],
migrations: [],
topic_followee_index: [],
xdr_conversion_rate: [
{
timestamp_seconds: [1n],
xdr_permyriad_per_icp: [10_000n]
}
],
restore_aging_summary: []
};
Now I see, you are trying to pass the init args from another language, and thatās why you cannot use the nns-init library.
Wait, you cannot initialize that freaking canister with Candid but, only protobuf?
After a closer look, yes. Apologies for what I said earlier. My impression came from the fact that we use candid::export_service!
to generate governance.did
from the canister code and in most cases it was accurate in terms of the consistency between the interface and what the canister actually expects. It turns out this is the biggest lie the governance.did
has been telling - this is prost_message::decode
which is protobuf wire format, and the ic-nns-init library I mentioned earlier is also encoding as protobuf here
Would you be able to use governance.proto to generate ts?
I was hoping for some guidance on what parameters are required and what are their expected values.
Your example above seems to provide enough for at least get it running.
There seems to be a relatively easy path towards sanity here, since those canister init methods (Governance and GTC) will not be used on mainnet anymore (and no post_upgrade arguments), we can change them without affecting mainnet, for better initialization for testing and development. On the other hand, you seem to be the first one running into this. Could you help provide some explanation on why you canāt use the dfx extension? This will help a lot in justifying the work, and help us prioritize. Thanks!
Thanks for the answer. So, did I understand it correctly that Protobuf is a must for installing the Governance canister?
Not that I know, I donāt think didc supports protobuf right?
Does the foundation provides another tool to converts Protobuf with JS/TS?
Update: There is @dfinity/nns-proto
but not sure its definition is up-to-date and there seems to be no Governance.encode
feature, Iāve got check further.
I donāt think I should provide any motivation or context about my feature unless itās acceptable to consider dfx as a gatekeeper for accessing or developing on the Internet Computer?
But if you want some spontaneous thoughts:
- Protobuf is rarely used for any other canisters; therefore, there is a discrepancy in the Governance canister regarding the state of the art of development on the IC.
- Having to use Protobuf makes it hard or impossible to install the Governance canister with any tool that uses JavaScript ā for example, my custom feature, but I can also think at trying to incorporate that canister with PocketIC using PicJS.
It looks like in ic-js there are some scripts to generate js and ts scripts from proto so if I would manage to update the proto files and all dependencies, I might be able to generate something.
How it can be used afterwards thatās another questionā¦
It seems I was finally able to deploy the Governance canister locally it took only the all dayā¦
So, if anyone is ever looking to do the same, please note that PROTOBUF is required - i.e., you cannot encode your arguments with Candid to spin up an NNS Governance canister.
I had to update the @dfinity/nns-proto
library (PR +79,445 ā8,353) and then rewrite a chunk of code in my feature (PR). I think we originally planned to abandon this library given that it is not used anymore as it was primarily meant for the hardware wallet which now also uses Candid. So, Iāll speak with my colleagues to either release a new version, or Iāll just fork it.
Besides that, letās now continue with the next issue : Exchange rate canister required to deploy CMC
If anyone ever interested, thatās what you need to build the protobuf parameters for a NNS Governance canister:
import type {Identity} from '@dfinity/agent';
import {
Decimal,
Governance,
NetworkEconomics,
Neuron,
NeuronsFundEconomics,
Percentage,
PrincipalId
} from '@dfinity/nns-proto';
import {
NeuronsFundMatchedFundingCurveCoefficients,
XdrConversionRate
} from '@dfinity/nns-proto/dist/proto/governance_pb';
import {NeuronId} from '@dfinity/nns-proto/dist/proto/nns_common_pb';
import {neuronSubaccount} from '@dfinity/sns';
import {MAIN_IDENTITY_KEY} from '../../constants/constants';
import type {ModuleInstallParams} from '../../types/module';
import {NEURON_ID} from './governance.constants';
export const prepareGovernanceArgs = ({
identities
}: Pick<ModuleInstallParams, 'identities'>): Governance => {
const {[MAIN_IDENTITY_KEY]: identity} = identities;
// Source: https://github.com/dfinity/ic/blob/e90838a1687f8e0869d85343aac2845d883f74ff/rs/nns/governance/src/governance.rs#L231
const E8S_PER_ICP = 100_000_000n;
const DEFAULT_TRANSFER_FEE = 10_000n;
const fund = new NeuronsFundEconomics();
const decimal = (value: string): Decimal => {
const dev = new Decimal();
dev.setHumanReadable(value);
return dev;
};
fund.setMaxTheoreticalNeuronsFundParticipationAmountXdr(decimal('750_000.0'));
const efficients = new NeuronsFundMatchedFundingCurveCoefficients();
efficients.setContributionThresholdXdr(decimal('75_000.0'));
efficients.setOneThirdParticipationMilestoneXdr(decimal('225_000.0'));
efficients.setFullParticipationMilestoneXdr(decimal('375_000.0'));
fund.setNeuronsFundMatchedFundingCurveCoefficients(efficients);
const percentage = (value: number): Percentage => {
const dev = new Percentage();
dev.setBasisPoints(value);
return dev;
};
fund.setMinimumIcpXdrRate(percentage(10_000)); // 1:1
fund.setMaximumIcpXdrRate(percentage(1_000_000)); // 1:100
const eco = new NetworkEconomics();
eco.setRejectCostE8s(Number(E8S_PER_ICP)); // 1 ICP
eco.setNeuronMinimumStakeE8s(Number(E8S_PER_ICP)); // 1 ICP
eco.setNeuronManagementFeePerProposalE8s(1_000_000); // 0.01 ICP
eco.setMinimumIcpXdrRate(100); // 1 XDR
eco.setNeuronSpawnDissolveDelaySeconds(24 * 60 * 60 * 7); // 7 days
eco.setMaximumNodeProviderRewardsE8s(1_000_000 * 100_000_000); // 1M ICP
eco.setTransactionFeeE8s(Number(DEFAULT_TRANSFER_FEE));
eco.setMaxProposalsToKeepPerTopic(100);
eco.setNeuronsFundEconomics(fund);
const xdr = new XdrConversionRate();
xdr.setTimestampSeconds(1);
xdr.setXdrPermyriadPerIcp(10_000);
const neuron = prepareNeuron({identity});
const gov = new Governance();
gov.getNeuronsMap().set(neuron.getId()?.getId() ?? NEURON_ID, neuron);
gov.setWaitForQuietThresholdSeconds(60 * 60 * 24 * 4); // 4 days
gov.setEconomics(eco);
gov.setGenesisTimestampSeconds(0);
gov.setShortVotingPeriodSeconds(60 * 60 * 12); // 12 hours
gov.setXdrConversionRate(xdr);
return gov;
};
const prepareNeuron = ({identity}: {identity: Identity}): Neuron => {
const id = new NeuronId();
id.setId(NEURON_ID);
const subAccount = neuronSubaccount({
index: 0,
controller: identity.getPrincipal()
}) as Uint8Array;
const principalId = new PrincipalId();
principalId.setSerializedId(identity.getPrincipal().toUint8Array());
const neuron = new Neuron();
neuron.setId(id);
neuron.setAccount(subAccount);
neuron.setController(principalId);
neuron.setHotKeysList([principalId]);
neuron.setCachedNeuronStakeE8s(1_000_000 * 100_000_000); // 1M ICP
neuron.setCreatedTimestampSeconds(0);
neuron.setAgingSinceTimestampSeconds(0);
neuron.setKycVerified(true);
neuron.setMaturityE8sEquivalent(0);
neuron.setNotForProfit(true);
neuron.setDissolveDelaySeconds(24 * 60 * 60 * 365 * 8); // 8 * 365 days
return neuron;
};
we can not use nns to test and mock, because we can not get the admin or provide tokens
maybe I can start part of nns canisters ? like I can deploy icp ledger by myself, then I can init some ICP(test) to local identity. and then I can test my product