Skip to content
Draft
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
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

88 changes: 88 additions & 0 deletions src/commands/make_proposal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
use std::{fs, path::PathBuf};

use crate::{
lib::{
governance_canister_id,
signing::{sign_ingress_with_request_status_query, IngressWithRequestId},
AuthInfo, ROLE_NNS_GOVERNANCE,
},
AnyhowResult,
};
use anyhow::Error;
use candid::{CandidType, Decode, Encode, TypeEnv};
use candid_parser::parse_idl_args;
use clap::Parser;
use ic_nns_common::pb::v1::NeuronId;
use ic_nns_governance::pb::v1::{
manage_neuron::{self, NeuronIdOrSubaccount},
ManageNeuron, Proposal,
};

use super::neuron_manage::parse_neuron_id;

/// Creates an NNS proposal for others to vote on.
#[derive(Parser)]
pub struct MakeProposalOpts {
/// The id of the neuron making the proposal.
#[arg(value_parser = parse_neuron_id)]
proposer_neuron_id: u64,

/// The proposal to be submitted. The proposal must be formatted as a string
/// wrapped candid record.
///
/// For example:
/// '(record {
/// title=opt "Known Neuron Proposal";
/// url="http://example.com";
/// summary="A proposal to become a named neuron";
/// action=opt variant {
/// RegisterKnownNeuron = record {
/// id=opt record { id=773; };
/// known_neuron_data=opt record { name="Me!" };
/// }
/// };
/// })'
#[arg(long)]
proposal: Option<String>,

/// Path to a file containing the proposal. The proposal must be the binary encoding of
/// the proposal candid record.
#[arg(
long,
conflicts_with = "proposal",
required_unless_present = "proposal"
)]
proposal_path: Option<PathBuf>,
}

pub fn exec(auth: &AuthInfo, opts: MakeProposalOpts) -> AnyhowResult<Vec<IngressWithRequestId>> {
let neuron_id = opts.proposer_neuron_id;

let proposal = if let Some(proposal) = opts.proposal {
parse_nns_proposal_from_candid_string(proposal)?
} else {
Decode!(&fs::read(opts.proposal_path.unwrap())?, Proposal)?
};

let args = Encode!(&ManageNeuron {
id: None,
neuron_id_or_subaccount: Some(NeuronIdOrSubaccount::NeuronId(NeuronId { id: neuron_id })),
command: Some(manage_neuron::Command::MakeProposal(Box::new(proposal)))
})?;

let msg = sign_ingress_with_request_status_query(
auth,
governance_canister_id(),
ROLE_NNS_GOVERNANCE,
"manage_neuron",
args,
)?;

Ok(vec![msg])
}

fn parse_nns_proposal_from_candid_string(proposal_candid: String) -> AnyhowResult<Proposal> {
let args = parse_idl_args(&proposal_candid)?;
let args: Vec<u8> = args.to_bytes_with_types(&TypeEnv::default(), &[Proposal::ty()])?;
Decode!(args.as_slice(), Proposal).map_err(Error::msg)
}
6 changes: 6 additions & 0 deletions src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ mod get_neuron_info;
mod get_proposal_info;
mod list_neurons;
mod list_proposals;
mod make_proposal;
mod neuron_manage;
mod neuron_stake;
mod public;
Expand All @@ -37,6 +38,7 @@ pub enum Command {
ListNeurons(list_neurons::ListNeuronsOpts),
ListProposals(list_proposals::ListProposalsOpts),
GetProposalInfo(get_proposal_info::GetProposalInfoOpts),
MakeProposal(make_proposal::MakeProposalOpts),
GetNeuronInfo(get_neuron_info::GetNeuronInfoOpts),
AccountBalance(account_balance::AccountBalanceOpts),
UpdateNodeProvider(update_node_provider::UpdateNodeProviderOpts),
Expand Down Expand Up @@ -79,6 +81,10 @@ pub fn dispatch(auth: &AuthInfo, cmd: Command, fetch_root_key: bool, qr: bool) -
Command::GetProposalInfo(opts) => {
get_proposal_info::exec(opts, fetch_root_key)?;
}
Command::MakeProposal(opts) => {
let out = make_proposal::exec(auth, opts)?;
print_vec(qr, &out)?;
}
Command::GetNeuronInfo(opts) => {
get_neuron_info::exec(opts, fetch_root_key)?;
}
Expand Down
6 changes: 3 additions & 3 deletions src/commands/neuron_manage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ Cannot use --ledger with these flags. This version of quill only supports the fo
let mut msgs = Vec::new();

let id = NeuronId {
id: parse_neuron_id(opts.neuron_id)?,
id: parse_neuron_id(&opts.neuron_id)?,
};
let id = Some(NeuronIdOrSubaccount::NeuronId(id));
if opts.add_hot_key.is_some() {
Expand Down Expand Up @@ -316,7 +316,7 @@ Cannot use --ledger with these flags. This version of quill only supports the fo
id: None,
command: Some(Command::Merge(Merge {
source_neuron_id: Some(NeuronId {
id: parse_neuron_id(neuron_id)?
id: parse_neuron_id(&neuron_id)?
}),
})),
neuron_id_or_subaccount: id.clone(),
Expand Down Expand Up @@ -448,7 +448,7 @@ Cannot use --ledger with these flags. This version of quill only supports the fo
Ok(generated)
}

fn parse_neuron_id(id: String) -> AnyhowResult<u64> {
pub fn parse_neuron_id(id: &str) -> AnyhowResult<u64> {
id.replace('_', "")
.parse()
.context("Failed to parse the neuron id")
Expand Down
4 changes: 2 additions & 2 deletions src/commands/sns/make_proposal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ pub fn exec(
let governance_canister_id = sns_canister_ids.governance_canister_id;

let proposal = if let Some(proposal) = opts.proposal {
parse_proposal_from_candid_string(proposal)?
parse_sns_proposal_from_candid_string(proposal)?
} else {
Decode!(&fs::read(opts.proposal_path.unwrap())?, Proposal)?
};
Expand All @@ -83,7 +83,7 @@ pub fn exec(
Ok(vec![msg])
}

fn parse_proposal_from_candid_string(proposal_candid: String) -> AnyhowResult<Proposal> {
fn parse_sns_proposal_from_candid_string(proposal_candid: String) -> AnyhowResult<Proposal> {
let args = parse_idl_args(&proposal_candid)?;
let args: Vec<u8> = args.to_bytes_with_types(&TypeEnv::default(), &[Proposal::ty()])?;
Decode!(args.as_slice(), Proposal).map_err(Error::msg)
Expand Down
6 changes: 3 additions & 3 deletions src/lib/format/nns_governance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use sha2::{Digest, Sha256};

use crate::lib::{
e8s_to_tokens,
format::{format_duration_seconds, format_t_cycles, format_timestamp_seconds},
format::{format_duration_seconds, format_timestamp_seconds},
get_default_role, get_idl_string, AnyhowResult,
};

Expand Down Expand Up @@ -971,8 +971,8 @@ fn display_canister_settings(settings: CanisterSettings) -> AnyhowResult<String>
if let Some(freezing) = settings.freezing_threshold {
writeln!(
fmt,
"Freezing threshold: {} cycles",
format_t_cycles(freezing.into())
"Freezing threshold: {}",
format_duration_seconds(freezing),
)?;
}
if let Some(memory) = settings.memory_allocation {
Expand Down
32 changes: 32 additions & 0 deletions tests/output/default/make_proposal/known_neuron.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
Sending message with

Call type: update
Sender: fdsgv-62ihb-nbiqv-xgic5-iefsv-3cscz-tmbzv-63qd5-vh43v-dqfrt-pae
Canister id: rrkah-fqaaa-aaaaa-aaaaq-cai
Method name: manage_neuron
Arguments: (
record {
id = null;
command = opt variant {
MakeProposal = record {
title = opt "Known Neuron Proposal";
url = "http://example.com";
summary = "A proposal to become a named neuron";
action = opt variant {
RegisterKnownNeuron = record {
id = opt record {
id = 773 : nat64;
};
known_neuron_data = opt record {
name = "Me!";
};
}
};
}
};
neuron_id_or_subaccount = opt variant {
NeuronId = record { id = 2_313_380_519_530_470_538 : nat64 }
};
},
)

2 changes: 1 addition & 1 deletion tests/output/neuron_manage.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{ledger_compatible, quill_send, OutputExt, ALICE, PRINCIPAL};

const NEURON_ID: &str = "2313380519530470538";
pub const NEURON_ID: &str = "2313380519530470538";

// uncomment tests on next ledger app update
ledger_compatible![
Expand Down
25 changes: 23 additions & 2 deletions tests/output/root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use std::io::Write;
use tempfile::NamedTempFile;

use crate::{
escape_p, ledger_compatible, quill, quill_authed, quill_query, quill_query_authed, quill_send,
OutputExt,
escape_p, ledger_compatible, neuron_manage::NEURON_ID, quill, quill_authed, quill_query,
quill_query_authed, quill_send, OutputExt,
};

// Uncomment tests on next ledger app update
Expand Down Expand Up @@ -195,3 +195,24 @@ fn ledger_fail_early() {
quill("neuron-manage 1 --ledger --join-community-fund")
.diff_err("ledger_incompatible/by_flag.txt");
}

#[test]
fn make_proposal() {
let proposal = r#"(record{
title=opt "Known Neuron Proposal";
url="http://example.com";
summary="A proposal to become a named neuron";
action=opt variant {
RegisterKnownNeuron = record {
id=opt record { id=773; };
known_neuron_data=opt record {
name="Me!";
};
}
};
})"#;
quill_send(&format!(
"make-proposal {NEURON_ID} --proposal '{proposal}'"
))
.diff("make_proposal/known_neuron.txt");
}
Loading