Skip to content

Commit

Permalink
feat(dre): listing proposals (#372)
Browse files Browse the repository at this point in the history
* adding proposal subcommand

* adding basic listing of upstream proposals

* Github Action: Bazel Repin

* clippy fix

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
  • Loading branch information
NikolaMilosa and github-actions[bot] committed May 13, 2024
1 parent c427b48 commit 44261a2
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 9 deletions.
6 changes: 5 additions & 1 deletion Cargo.Bazel.lock
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"checksum": "e6da14722080ba8a0b4069a6eb6deaec83172a87557d77964c9e84894b72a822",
"checksum": "a23efd2a9223be99e707de45f95191617e14ea319d026e8ae5eff0ec08ad9243",
"crates": {
"actix-codec 0.5.2": {
"name": "actix-codec",
Expand Down Expand Up @@ -11744,6 +11744,10 @@
"id": "ic-interfaces-registry 0.9.0",
"target": "ic_interfaces_registry"
},
{
"id": "ic-nns-common 0.9.0",
"target": "ic_nns_common"
},
{
"id": "ic-nns-constants 0.9.0",
"target": "ic_nns_constants"
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions rs/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ ic-management-backend = { workspace = true }
ic-management-types = { workspace = true }
ic-nns-constants = { workspace = true }
ic-nns-governance = { workspace = true }
ic-nns-common = { workspace = true }
ic-protobuf = { workspace = true }
ic-registry-keys = { workspace = true }
ic-registry-local-registry = { workspace = true }
Expand Down
63 changes: 63 additions & 0 deletions rs/cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,9 @@ pub enum Commands {
#[clap(long, default_value = None, required = true)]
summary: Option<String>,
},

/// Proposal Listing
Proposals(proposals::Cmd),
}

impl Default for Commands {
Expand Down Expand Up @@ -423,3 +426,63 @@ pub mod nodes {
},
}
}

pub mod proposals {
use super::*;

#[derive(Parser, Clone)]
pub struct Cmd {
#[clap(subcommand)]
pub subcommand: Commands,
}

#[derive(Subcommand, Clone)]
pub enum Commands {
/// Get list of pending proposals
Pending,

/// Get list of filtered proposals
List {
/// Limit on the number of \[ProposalInfo\] to return. If no value is
/// specified, or if a value greater than 100 is specified, 100
/// will be used.
#[clap(long, default_value = "100")]
limit: u32,
/// If specified, only return proposals that are strictly earlier than
/// the specified proposal according to the proposal ID. If not
/// specified, start with the most recent proposal.
#[clap(long)]
before_proposal: Option<u64>,
/// Exclude proposals with a topic in this list. This is particularly
/// useful to exclude proposals on the topics TOPIC_EXCHANGE_RATE and
/// TOPIC_KYC which most users are not likely to be interested in
/// seeing.
#[clap(long)]
exclude_topic: Vec<i32>,
/// Include proposals that have a reward status in this list (see
/// \[ProposalRewardStatus\] for more information). If this list is
/// empty, no restriction is applied. For example, many users listing
/// proposals will only be interested in proposals for which they can
/// receive voting rewards, i.e., with reward status
/// PROPOSAL_REWARD_STATUS_ACCEPT_VOTES.
#[clap(long)]
include_reward_status: Vec<i32>,
/// Include proposals that have a status in this list (see
/// \[ProposalStatus\] for more information). If this list is empty, no
/// restriction is applied.
#[clap(long)]
include_status: Vec<i32>,
/// Include all ManageNeuron proposals regardless of the visibility of the
/// proposal to the caller principal. Note that exclude_topic is still
/// respected even when this option is set to true.
#[clap(long)]
include_all_manage_neuron_proposals: Option<bool>,
/// Omits "large fields" from the response. Currently only omits the
/// `logo` and `token_logo` field of CreateServiceNervousSystem proposals. This
/// is useful to improve download times and to ensure that the response to the
/// request doesn't exceed the message size limit.
#[clap(long)]
omit_large_fields: Option<bool>,
},
}
}
31 changes: 30 additions & 1 deletion rs/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ use dre::general::{get_node_metrics_history, vote_on_proposals};
use dre::operations::hostos_rollout::{NodeGroupUpdate, NumberOfNodes};
use dre::{cli, ic_admin, local_unused_port, registry_dump, runner};
use ic_base_types::CanisterId;
use ic_canisters::governance::governance_canister_version;
use ic_canisters::governance::{governance_canister_version, GovernanceCanisterWrapper};
use ic_canisters::CanisterClient;
use ic_management_backend::endpoints;
use ic_management_types::requests::NodesRemoveRequest;
use ic_management_types::{Artifact, MinNakamotoCoefficients, NodeFeature};
use ic_nns_common::pb::v1::ProposalId;
use ic_nns_governance::pb::v1::ListProposalInfo;
use log::{info, warn};
use serde_json::Value;
use std::collections::BTreeMap;
Expand Down Expand Up @@ -327,6 +330,32 @@ async fn async_main() -> Result<(), anyhow::Error> {
..Default::default()
}, cli_opts.simulate).await
}
cli::Commands::Proposals(p) => match &p.subcommand {
cli::proposals::Commands::Pending => {
let nns_url = target_network.get_nns_urls().first().expect("Should have at least one NNS URL");
let client = GovernanceCanisterWrapper::from(CanisterClient::from_anonymous(nns_url)?);
let proposals = client.get_pending_proposals().await?;
let proposals = serde_json::to_string(&proposals).map_err(|e| anyhow::anyhow!("Couldn't serialize to string: {:?}", e))?;
println!("{}", proposals);
Ok(())
}
cli::proposals::Commands::List { limit, before_proposal, exclude_topic, include_reward_status, include_status, include_all_manage_neuron_proposals, omit_large_fields } => {
let nns_url = target_network.get_nns_urls().first().expect("Should have at least one NNS URL");
let client = GovernanceCanisterWrapper::from(CanisterClient::from_anonymous(nns_url)?);
let proposals = client.list_proposals(ListProposalInfo {
before_proposal: before_proposal.as_ref().map(|p| ProposalId { id: *p }),
exclude_topic: exclude_topic.clone(),
include_all_manage_neuron_proposals: *include_all_manage_neuron_proposals,
include_reward_status: include_reward_status.clone(),
include_status: include_status.clone(),
limit: *limit,
omit_large_fields: *omit_large_fields
}).await?;
let proposals = serde_json::to_string(&proposals).map_err(|e| anyhow::anyhow!("Couldn't serialize to string: {:?}", e))?;
println!("{}", proposals);
Ok(())
},
},
}
})
.await;
Expand Down
19 changes: 19 additions & 0 deletions rs/ic-canisters/src/governance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use ic_nns_common::pb::v1::NeuronId;
use ic_nns_common::pb::v1::ProposalId;
use ic_nns_constants::GOVERNANCE_CANISTER_ID;
use ic_nns_governance::pb::v1::manage_neuron::RegisterVote;
use ic_nns_governance::pb::v1::ListProposalInfo;
use ic_nns_governance::pb::v1::ListProposalInfoResponse;
use ic_nns_governance::pb::v1::ManageNeuron;
use ic_nns_governance::pb::v1::ManageNeuronResponse;
use ic_nns_governance::pb::v1::ProposalInfo;
Expand Down Expand Up @@ -141,4 +143,21 @@ impl GovernanceCanisterWrapper {
Err(err) => Err(anyhow::anyhow!("Error executing update: {}", err)),
}
}

pub async fn list_proposals(&self, contract: ListProposalInfo) -> anyhow::Result<Vec<ProposalInfo>> {
let args = Encode! { &contract }?;
match self
.client
.agent
.execute_query(&GOVERNANCE_CANISTER_ID, "list_proposals", args)
.await
{
Ok(Some(response)) => match Decode!(response.as_slice(), ListProposalInfoResponse) {
Ok(response) => Ok(response.proposal_info),
Err(e) => Err(anyhow::anyhow!("Error deserializing response: {:?}", e)),
},
Ok(None) => Ok(vec![]),
Err(e) => Err(anyhow::anyhow!("Error executing query: {}", e)),
}
}
}
23 changes: 16 additions & 7 deletions rs/ic-canisters/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use candid::CandidType;
use ic_agent::agent::http_transport::ReqwestTransport;
use ic_agent::identity::AnonymousIdentity;
use ic_agent::identity::BasicIdentity;
use ic_agent::identity::Secp256k1Identity;
use ic_agent::Agent;
Expand Down Expand Up @@ -51,6 +52,12 @@ impl CanisterClient {
agent: CanisterClientAgent::new(nns_url.clone(), sender),
})
}

pub fn from_anonymous(nns_url: &Url) -> anyhow::Result<Self> {
Ok(Self {
agent: CanisterClientAgent::new(nns_url.clone(), Sender::Anonymous),
})
}
}

pub struct IcAgentCanisterClient {
Expand All @@ -66,18 +73,20 @@ impl IcAgentCanisterClient {
.map_err(|e| anyhow::anyhow!("Couldn't load identity: {:?}", e))?;
Box::new(identity)
};
Ok(Self {
agent: Agent::builder()
.with_identity(identity)
.with_transport(ReqwestTransport::create(url)?)
.with_verify_query_signatures(false)
.build()?,
})
Self::build_agent(url, identity)
}

pub fn from_hsm(pin: String, slot: u64, key_id: String, url: Url, lock: Option<Mutex<()>>) -> anyhow::Result<Self> {
let pin_fn = || Ok(pin);
let identity = ParallelHardwareIdentity::new(pkcs11_lib_path()?, slot as usize, &key_id, pin_fn, lock)?;
Self::build_agent(url, Box::new(identity))
}

pub fn from_anonymous(url: Url) -> anyhow::Result<Self> {
Self::build_agent(url, Box::new(AnonymousIdentity))
}

fn build_agent(url: Url, identity: Box<dyn Identity>) -> anyhow::Result<Self> {
Ok(Self {
agent: Agent::builder()
.with_identity(identity)
Expand Down

0 comments on commit 44261a2

Please sign in to comment.