Skip to content

Commit

Permalink
feat(sns-cli): Add sns-lint command
Browse files Browse the repository at this point in the history
  • Loading branch information
anchpop committed Sep 27, 2024
1 parent a34cbd9 commit 8df67f0
Show file tree
Hide file tree
Showing 16 changed files with 186 additions and 35 deletions.
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/nervous_system/agent/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ DEPENDENCIES = [
"//rs/nns/constants",
"//rs/nns/sns-wasm",
"//rs/sns/governance",
"//rs/sns/root",
"//rs/types/base_types",
"@crate_index//:anyhow",
"@crate_index//:candid",
Expand Down
1 change: 1 addition & 0 deletions rs/nervous_system/agent/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ ic-nns-constants = { path = "../../nns/constants" }
ic-sns-wasm = { path = "../../nns/sns-wasm" }
ic-sns-governance = { path = "../../sns/governance" }
pocket-ic = { path = "../../../packages/pocket-ic" }
ic-sns-root = { path = "../../sns/root" }
serde = { workspace = true }
tempfile = { workspace = true }
thiserror = { workspace = true }
Expand Down
2 changes: 1 addition & 1 deletion rs/nervous_system/agent/src/nns/sns_wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use tokio::process::Command;

use crate::CallCanisters;

pub async fn query_sns_upgrade_steps<C: CallCanisters>(
pub async fn query_mainline_sns_upgrade_steps<C: CallCanisters>(
agent: &C,
) -> Result<ListUpgradeStepsResponse, C::Error> {
let request = ListUpgradeStepsRequest {
Expand Down
14 changes: 13 additions & 1 deletion rs/nervous_system/agent/src/sns/governance.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use crate::CallCanisters;
use ic_base_types::PrincipalId;
use ic_sns_governance::pb::v1::{GetMetadataRequest, GetMetadataResponse};
use ic_sns_governance::pb::v1::{
GetMetadataRequest, GetMetadataResponse, GetRunningSnsVersionRequest,
GetRunningSnsVersionResponse,
};
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, Deserialize, Serialize)]
Expand All @@ -15,4 +18,13 @@ impl GovernanceCanister {
) -> Result<GetMetadataResponse, C::Error> {
agent.call(self.canister_id, GetMetadataRequest {}).await
}

pub async fn version<C: CallCanisters>(
&self,
agent: &C,
) -> Result<GetRunningSnsVersionResponse, C::Error> {
agent
.call(self.canister_id, GetRunningSnsVersionRequest {})
.await
}
}
23 changes: 23 additions & 0 deletions rs/nervous_system/agent/src/sns/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@ pub mod ledger;
pub mod root;
pub mod swap;

use anyhow::Result;
use ic_nns_constants::SNS_WASM_CANISTER_ID;
use ic_sns_wasm::pb::v1::{ListUpgradeStepsRequest, ListUpgradeStepsResponse, SnsVersion};
use serde::{Deserialize, Serialize};

use crate::CallCanisters;

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Sns {
pub ledger: ledger::LedgerCanister,
Expand All @@ -14,6 +20,23 @@ pub struct Sns {
pub root: root::RootCanister,
}

impl Sns {
pub async fn remaining_upgrade_steps<C: CallCanisters>(
&self,
agent: &C,
) -> Result<ListUpgradeStepsResponse, C::Error> {
let version = self.governance.version(agent).await?;
let list_upgrade_steps_request = ListUpgradeStepsRequest {
limit: 0,
sns_governance_canister_id: Some(self.governance.canister_id),
starting_at: version.deployed_version.map(SnsVersion::from),
};
agent
.call(SNS_WASM_CANISTER_ID, list_upgrade_steps_request)
.await
}
}

impl TryFrom<ic_sns_wasm::pb::v1::DeployedSns> for Sns {
type Error = String;

Expand Down
19 changes: 19 additions & 0 deletions rs/nervous_system/agent/src/sns/root.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,26 @@
use ic_base_types::PrincipalId;
use ic_sns_root::{GetSnsCanistersSummaryRequest, GetSnsCanistersSummaryResponse};
use serde::{Deserialize, Serialize};

use crate::CallCanisters;

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct RootCanister {
pub canister_id: PrincipalId,
}

impl RootCanister {
pub async fn sns_canisters_summary<C: CallCanisters>(
&self,
agent: &C,
) -> Result<GetSnsCanistersSummaryResponse, C::Error> {
agent
.call(
self.canister_id,
GetSnsCanistersSummaryRequest {
update_canister_list: None,
},
)
.await
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ async fn main() -> Result<()> {
.into_iter()
.collect::<Result<Vec<CanisterUpdate>>>()?;

let sns_upgrade_steps = sns_wasm::query_sns_upgrade_steps(&agent).await?;
let sns_upgrade_steps = sns_wasm::query_mainline_sns_upgrade_steps(&agent).await?;
let latest_sns_version = &sns_upgrade_steps
.steps
.last()
Expand Down
5 changes: 4 additions & 1 deletion rs/sns/cli/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
deploy::DirectSnsDeployerForTests, init_config_file::InitConfigFileArgs,
deploy::DirectSnsDeployerForTests, health::HealthArgs, init_config_file::InitConfigFileArgs,
neuron_id_to_candid_subaccount::NeuronIdToCandidSubaccountArgs,
prepare_canisters::PrepareCanistersArgs, propose::ProposeArgs,
};
Expand Down Expand Up @@ -30,6 +30,7 @@ use std::{
use tempfile::NamedTempFile;

pub mod deploy;
pub mod health;
pub mod init_config_file;
pub mod list;
pub mod neuron_id_to_candid_subaccount;
Expand Down Expand Up @@ -77,6 +78,8 @@ pub enum SubCommand {
NeuronIdToCandidSubaccount(NeuronIdToCandidSubaccountArgs),
/// List SNSes
List(list::ListArgs),
/// Check SNSes for warnings and errors.
Health(HealthArgs),
}

impl CliArgs {
Expand Down
32 changes: 5 additions & 27 deletions rs/sns/cli/src/list.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
use crate::table::{as_table, TableRow};
use crate::utils::{get_snses_with_metadata, SnsWithMetadata};
use anyhow::Result;
use clap::Parser;
use futures::{stream, StreamExt};
use ic_agent::Agent;
use ic_nervous_system_agent::{nns::sns_wasm, sns::Sns};
use itertools::Itertools;
use serde::{Deserialize, Serialize};
use ic_nervous_system_agent::nns::sns_wasm;

#[derive(Debug, Parser)]
pub struct ListArgs {
Expand All @@ -14,12 +12,6 @@ pub struct ListArgs {
json: bool,
}

#[derive(Debug, Serialize, Deserialize)]
struct SnsWithMetadata {
name: String,
sns: Sns,
}

impl TableRow for SnsWithMetadata {
fn column_names() -> Vec<&'static str> {
vec!["name", "ledger", "governance", "index", "swap", "root"]
Expand All @@ -38,24 +30,10 @@ impl TableRow for SnsWithMetadata {
}

pub async fn exec(args: ListArgs, agent: &Agent) -> Result<()> {
eprintln!("Listing SNSes...");

let snses = sns_wasm::list_deployed_snses(agent).await?;
let snses_with_metadata = stream::iter(snses)
.map(|sns| async move {
let metadata = sns.governance.metadata(agent).await?;
Ok((sns, metadata))
})
.buffer_unordered(10) // Do up to 10 requests at a time in parallel
.collect::<Vec<anyhow::Result<_>>>()
.await;
let snses_with_metadata = snses_with_metadata
.into_iter()
.filter_map(Result::ok)
.map(|(sns, metadata)| {
let name = metadata.name.unwrap_or_else(|| "Unknown".to_string());
SnsWithMetadata { name, sns }
})
.sorted_by(|a, b| a.name.cmp(&b.name))
.collect::<Vec<_>>();
let snses_with_metadata = get_snses_with_metadata(agent, snses).await;

let output = if args.json {
serde_json::to_string(&snses_with_metadata).unwrap()
Expand Down
3 changes: 2 additions & 1 deletion rs/sns/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use anyhow::{bail, Result};
use clap::Parser;

use ic_sns_cli::{
add_sns_wasm_for_tests, deploy_testflight, init_config_file, list,
add_sns_wasm_for_tests, deploy_testflight, health, init_config_file, list,
neuron_id_to_candid_subaccount, prepare_canisters, propose, CliArgs, SubCommand,
};

Expand All @@ -27,5 +27,6 @@ async fn main() -> Result<()> {
SubCommand::Propose(args) => propose::exec(args),
SubCommand::NeuronIdToCandidSubaccount(args) => neuron_id_to_candid_subaccount::exec(args),
SubCommand::List(args) => list::exec(args, &agent).await,
SubCommand::Health(args) => health::exec(args, &agent).await,
}
}
33 changes: 33 additions & 0 deletions rs/sns/cli/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
use anyhow::{anyhow, Result};
use futures::{stream, StreamExt};
use ic_agent::Agent;
use ic_nervous_system_agent::sns::Sns;
use itertools::Itertools;
use serde::{Deserialize, Serialize};

fn get_agent(ic_url: &str) -> Result<Agent> {
Agent::builder()
Expand All @@ -13,3 +17,32 @@ pub fn get_mainnet_agent() -> Result<Agent> {
let ic_url = "https://ic0.app/";
get_agent(ic_url)
}

#[derive(Debug, Serialize, Deserialize)]
pub(crate) struct SnsWithMetadata {
pub(crate) name: String,
pub(crate) sns: Sns,
}

pub(crate) async fn get_snses_with_metadata(
agent: &Agent,
snses: Vec<Sns>,
) -> Vec<SnsWithMetadata> {
let snses_with_metadata = stream::iter(snses)
.map(|sns| async move {
let metadata = sns.governance.metadata(agent).await?;
Ok((sns, metadata))
})
.buffer_unordered(10) // Do up to 10 requests at a time in parallel
.collect::<Vec<anyhow::Result<_>>>()
.await;
snses_with_metadata
.into_iter()
.filter_map(Result::ok)
.map(|(sns, metadata)| {
let name = metadata.name.unwrap_or_else(|| "Unknown".to_string());
SnsWithMetadata { name, sns }
})
.sorted_by(|a, b| a.name.cmp(&b.name))
.collect::<Vec<_>>()
}
12 changes: 9 additions & 3 deletions rs/sns/governance/src/request_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ use crate::pb::v1::{
ClaimSwapNeuronsRequest, ClaimSwapNeuronsResponse, FailStuckUpgradeInProgressRequest,
FailStuckUpgradeInProgressResponse, GetMaturityModulationRequest,
GetMaturityModulationResponse, GetMetadataRequest, GetMetadataResponse, GetMode,
GetModeResponse, GetNeuronResponse, GetProposalResponse, GetSnsInitializationParametersRequest,
GetSnsInitializationParametersResponse, ListNeuronsResponse, ListProposalsResponse,
ManageNeuronResponse,
GetModeResponse, GetNeuronResponse, GetProposalResponse, GetRunningSnsVersionResponse,
GetSnsInitializationParametersRequest, GetSnsInitializationParametersResponse,
ListNeuronsResponse, ListProposalsResponse, ManageNeuronResponse,
};

impl Request for ClaimSwapNeuronsRequest {
Expand Down Expand Up @@ -74,3 +74,9 @@ impl Request for crate::pb::v1::ManageNeuron {
const METHOD: &'static str = "manage_neuron";
const UPDATE: bool = true;
}

impl Request for crate::pb::v1::GetRunningSnsVersionRequest {
type Response = GetRunningSnsVersionResponse;
const METHOD: &'static str = "get_running_sns_version";
const UPDATE: bool = true;
}
35 changes: 35 additions & 0 deletions rs/sns/root/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@ use std::{
fmt::Write,
thread::LocalKey,
};
use types::SnsCanisterType;

pub use icrc_ledger_types::icrc3::archive::ArchiveInfo;
pub mod logs;
pub mod pb;
mod request_impls;
pub mod types;

// The number of dapp canisters that can be registered with the SNS Root
Expand Down Expand Up @@ -119,6 +121,39 @@ impl GetSnsCanistersSummaryResponse {
}
}

impl IntoIterator for GetSnsCanistersSummaryResponse {
type Item = (Option<CanisterSummary>, SnsCanisterType);

// Using Box<dyn Iterator<...>> because the type is very long otherwise.
// But this could be changed to a more specific type.
type IntoIter = Box<dyn Iterator<Item = Self::Item>>;

fn into_iter(self) -> Self::IntoIter {
let canisters = [
(self.root, SnsCanisterType::Root),
(self.governance, SnsCanisterType::Governance),
(self.ledger, SnsCanisterType::Ledger),
(self.swap, SnsCanisterType::Swap),
(self.index, SnsCanisterType::Index),
];

Box::new(
canisters
.into_iter()
.chain(
self.dapps
.into_iter()
.map(|d| (Some(d), SnsCanisterType::Dapp)),
)
.chain(
self.archives
.into_iter()
.map(|a| (Some(a), SnsCanisterType::Archive)),
),
)
}
}

#[derive(Clone, Eq, PartialEq, Debug, Default, candid::CandidType, candid::Deserialize)]
pub struct CanisterSummary {
pub canister_id: Option<PrincipalId>,
Expand Down
8 changes: 8 additions & 0 deletions rs/sns/root/src/request_impls.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use crate::{GetSnsCanistersSummaryRequest, GetSnsCanistersSummaryResponse};
use ic_nervous_system_clients::Request;

impl Request for GetSnsCanistersSummaryRequest {
type Response = GetSnsCanistersSummaryResponse;
const METHOD: &'static str = "get_sns_canisters_summary";
const UPDATE: bool = true;
}
30 changes: 30 additions & 0 deletions rs/sns/root/src/types.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
use std::fmt::{self, Display, Formatter};

use async_trait::async_trait;
use ic_base_types::CanisterId;
use serde::{Deserialize, Serialize};

/// A general trait for the environment in which governance is running.
#[async_trait]
pub trait Environment: Send + Sync {
Expand All @@ -20,3 +24,29 @@ pub trait Environment: Send + Sync {
arg: Vec<u8>,
) -> Result</* reply: */ Vec<u8>, (/* error_code: */ i32, /* message: */ String)>;
}

/// Different from the SnsCanisterType in SNS-W because it includes Dap
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
pub enum SnsCanisterType {
Root,
Governance,
Ledger,
Swap,
Archive,
Index,
Dapp,
}

impl Display for SnsCanisterType {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
SnsCanisterType::Root => write!(f, "root"),
SnsCanisterType::Governance => write!(f, "governance"),
SnsCanisterType::Ledger => write!(f, "ledger"),
SnsCanisterType::Swap => write!(f, "swap"),
SnsCanisterType::Dapp => write!(f, "dapp"),
SnsCanisterType::Archive => write!(f, "archive"),
SnsCanisterType::Index => write!(f, "index"),
}
}
}

0 comments on commit 8df67f0

Please sign in to comment.