Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

## DFX

### feat: Add dfx sns deploy

This allows users to deploy a set of SNS canisters.

### fix: `cargo run -p dfx -- --version` prints correct version

Expand Down
49 changes: 46 additions & 3 deletions docs/cli-reference/dfx-sns.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ Depending on the `dfx sns` subcommand you specify, additional arguments, options

| Command | Description |
|-------------------------------------|-------------------------------------------------------------------------------|
| [`create`](#_dfx_sns_create) | Creates an SNS configuration template |
| [`create`](#_dfx_sns_create) | Creates an SNS configuration template. |
| [`validate`](#_dfx_sns_validate) | Checks whether the sns config file is valid. |
| [`deploy`](#_dfx_sns_deploy) | Deploys SNS canisters according to the local config. |
| `help` | Displays usage information message for a specified subcommand. |

To view usage information for a specific subcommand, specify the subcommand and the `--help` flag. For example, to see usage information for `dfx sns validate`, you can run the following command:
Expand Down Expand Up @@ -74,6 +75,48 @@ You can use the following optional flags with the `dfx sns validate` command.
You can use the `dfx sns validate` command to verify that a configuration template is valid. It is not; it needs details such as token name:

``` bash
dfx sns create
dfx sns validate
dfx sns config create
```
Fill in the blank fields, then:
``` bash
dfx sns config validate
```

## dfx sns deploy

Use the `dfx sns deploy` command to create SNS canisters according to the local configuration file.

Note: Deploying SNS canisters does not require a proposal, however there is a hefty fee. Please don't create canisters on mainnet until you have tested your configuration locally and are sure that you are happy with it.

### Basic usage

``` bash
dfx sns deploy
```

### Flags

You can use the following optional flags with the `dfx sns deploy` command.

| Flag | Description |
|-------------------|-------------------------------|
| `-h`, `--help` | Displays usage information. |
| `-V`, `--version` | Displays version information. |

### Examples

Create an SNS on the local testnet:
``` bash
dfx sns config create
```
Fill in the blank fields, then:
``` bash
dfx sns config validate
dfx sns deploy
```
You can now verify that the sns canisters have been created. E.g.:
```
dfx canister info sns_root
dfx canister info sns_ledger
```

File renamed without changes
34 changes: 30 additions & 4 deletions e2e/tests-dfx/sns.bash
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,42 @@ SNS_CONFIG_FILE_NAME="sns.yml"

@test "sns config validate approves a valid configuration" {
dfx_new
cp "${BATS_TEST_DIRNAME}/../assets/sns/valid_sns_init_config.yaml" "$SNS_CONFIG_FILE_NAME"
cp "${BATS_TEST_DIRNAME}/../assets/sns/logo.svg" .
install_asset sns/valid
assert_command dfx sns config validate
assert_match 'SNS config file is valid'
}

@test "sns config validate identifies a missing key" {
dfx_new
grep -v token_name "${BATS_TEST_DIRNAME}/../assets/sns/valid_sns_init_config.yaml" > "$SNS_CONFIG_FILE_NAME"
cp "${BATS_TEST_DIRNAME}/../assets/sns/logo.svg" .
install_asset sns/valid
grep -v token_name "${SNS_CONFIG_FILE_NAME}" | sponge "$SNS_CONFIG_FILE_NAME"
assert_command_fail dfx sns config validate
assert_match token.name
}

@test "sns deploy exists" {
dfx sns deploy --help
}

@test "sns deploy fails without config file" {
dfx_new
rm -f sns.yml # Is not expected to be present anyway
assert_command_fail dfx sns deploy
assert_match
}

@test "sns deploy succeeds" {
dfx_new
install_shared_asset subnet_type/shared_network_settings/system
dfx start --clean --background --host 127.0.0.1:8080
sleep 1
dfx nns install
# There are no entries for "local" upstream yet, so we need a network mapping.
dfx nns import --network-mapping local=mainnet
# This canister ID is not included upstream .. yet.
jq '.canisters["nns-sns-wasm"].remote.id.local="qjdve-lqaaa-aaaaa-aaaeq-cai"' dfx.json | sponge dfx.json
ls candid
cat dfx.json
install_asset sns/valid
dfx sns deploy
}
2 changes: 2 additions & 0 deletions src/dfx/src/commands/nns/import.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//! Code for the command line: `dfx nns import`
use crate::lib::error::DfxResult;
use crate::lib::info::replica_rev;
use crate::lib::project::import::import_canister_definitions;
Expand All @@ -19,6 +20,7 @@ pub struct ImportOpts {
network_mapping: Vec<String>,
}

/// Executes `dfx nns import`
pub async fn exec(env: &dyn Environment, opts: ImportOpts) -> DfxResult {
let config = env.get_config_or_anyhow()?;
let mut config = config.as_ref().clone();
Expand Down
2 changes: 2 additions & 0 deletions src/dfx/src/commands/nns/install.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//! Code for the command line: `dfx nns install`
use crate::lib::error::DfxResult;
use crate::Environment;
use anyhow::anyhow;
Expand All @@ -23,6 +24,7 @@ use clap::Parser;
#[clap(about)]
pub struct InstallOpts {}

/// Executes `dfx nns install`.
pub async fn exec(env: &dyn Environment, _opts: InstallOpts) -> DfxResult {
let agent = env
.get_agent()
Expand Down
10 changes: 9 additions & 1 deletion src/dfx/src/commands/nns/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Code for the command line `dfx nns`.
#![warn(clippy::missing_docs_in_private_items)]
use crate::lib::environment::Environment;
use crate::lib::error::DfxResult;
use crate::lib::provider::create_agent_environment;
Expand All @@ -9,25 +11,31 @@ use tokio::runtime::Runtime;
mod import;
mod install;

/// NNS commands.
/// Options for `dfx nns` and its subcommands.
#[derive(Parser)]
#[clap(name("nns"))]
pub struct NnsOpts {
/// `dfx nns` subcommand arguments.
#[clap(subcommand)]
subcmd: SubCommand,

/// An argument to choose the network from those specified in dfx.json.
#[clap(flatten)]
network: NetworkOpt,
}

/// Command line options for subcommands of `dfx nns`.
#[derive(Parser)]
enum SubCommand {
/// Options for importing NNS API definitions and canister IDs.
#[clap(hide(true))]
Import(import::ImportOpts),
/// Options for installing an NNS.
#[clap(hide(true))]
Install(install::InstallOpts),
}

/// Executes `dfx nns` and its subcommands.
pub fn exec(env: &dyn Environment, opts: NnsOpts) -> DfxResult {
let env = create_agent_environment(env, opts.network.network)?;
let runtime = Runtime::new().expect("Unable to create a runtime");
Expand Down
2 changes: 2 additions & 0 deletions src/dfx/src/commands/sns/config/create.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//! Code for executing `dfx sns config create`
use crate::lib::error::DfxResult;
use crate::Environment;

Expand All @@ -9,6 +10,7 @@ use clap::Parser;
#[derive(Parser)]
pub struct CreateOpts {}

/// Executes `dfx sns config create`
pub fn exec(env: &dyn Environment, _opts: CreateOpts) -> DfxResult {
let config = env.get_config_or_anyhow()?;
let path = config.get_project_root().join(sns::CONFIG_FILE_NAME);
Expand Down
9 changes: 8 additions & 1 deletion src/dfx/src/commands/sns/config/mod.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,33 @@
//! Code for the command line `dfx sns config`.
use crate::lib::{environment::Environment, error::DfxResult};
use clap::Parser;

mod create;
mod validate;

/// Command line options for `sdx sns config`.
#[derive(Parser)]
pub struct ConfigOpts {}

/// SNS config commands.
/// SNS config command line arguments.
#[derive(Parser)]
#[clap(name("config"))]
pub struct SnsConfigOpts {
/// `dfx sns config` subcommand arguments.
#[clap(subcommand)]
subcmd: SubCommand,
}

/// Command line options for `sdx sns` subcommands.
#[derive(Parser)]
enum SubCommand {
/// Command line options for creating an SNS configuration.
Create(create::CreateOpts),
/// Command line options for validating an SNS configuration.
Validate(validate::ValidateOpts),
}

/// Executes `dfx sns config` and its subcommands.
pub fn exec(env: &dyn Environment, opts: SnsConfigOpts) -> DfxResult {
match opts.subcmd {
SubCommand::Create(v) => create::exec(env, v),
Expand Down
2 changes: 2 additions & 0 deletions src/dfx/src/commands/sns/config/validate.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//! Code for executing `dfx sns config validate`
use crate::lib::error::DfxResult;
use crate::Environment;

Expand All @@ -9,6 +10,7 @@ use clap::Parser;
#[derive(Parser)]
pub struct ValidateOpts {}

/// Executes `dfx sns config validate`
pub fn exec(env: &dyn Environment, _opts: ValidateOpts) -> DfxResult {
let config = env.get_config_or_anyhow()?;
let path = config.get_project_root().join(sns::CONFIG_FILE_NAME);
Expand Down
24 changes: 24 additions & 0 deletions src/dfx/src/commands/sns/deploy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//! Code for the command line `dfx sns deploy`.
use crate::lib::error::DfxResult;
use crate::Environment;

use crate::lib::sns;
use crate::lib::sns::deploy::deploy_sns;
use clap::Parser;

/// Creates an SNS on a network.
///
/// # Arguments
/// - `env` - The execution environment, including the network to deploy to and connection credentials.
/// - `opts` - Deployment options.
#[derive(Parser)]
pub struct DeployOpts {}

/// Executes the command line `dfx sns deploy`.
pub fn exec(env: &dyn Environment, _opts: DeployOpts) -> DfxResult {
let config = env.get_config_or_anyhow()?;
let path = config.get_project_root().join(sns::CONFIG_FILE_NAME);

deploy_sns(env, &path)?;
Ok(())
}
2 changes: 2 additions & 0 deletions src/dfx/src/commands/sns/import.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//! Code for the command line `dfx sns import`
use crate::lib::error::DfxResult;
use crate::lib::project::import::import_canister_definitions;
use crate::lib::project::network_mappings::get_network_mappings;
Expand All @@ -19,6 +20,7 @@ pub struct SnsImportOpts {
network_mapping: Vec<String>,
}

/// Executes the command line `dfx sns import`.
pub fn exec(env: &dyn Environment, opts: SnsImportOpts) -> DfxResult {
let config = env.get_config_or_anyhow()?;
let mut config = config.as_ref().clone();
Expand Down
14 changes: 13 additions & 1 deletion src/dfx/src/commands/sns/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Command line interface for `dfx sns`.
#![warn(clippy::missing_docs_in_private_items)]
use crate::{
commands::sns::config::SnsConfigOpts,
commands::sns::import::SnsImportOpts,
Expand All @@ -7,27 +9,37 @@ use crate::{
use clap::Parser;

mod config;
mod deploy;
mod import;

/// SNS commands.
/// Options for `dfx sns`.
#[derive(Parser)]
#[clap(name("sns"))]
pub struct SnsOpts {
/// Arguments and flags for subcommands.
#[clap(subcommand)]
subcmd: SubCommand,
}

/// Subcommands of `dfx sns`
#[derive(Parser)]
enum SubCommand {
/// Subcommands for working with configuration.
#[clap(hide(true))]
Config(SnsConfigOpts),
/// Subcommand for creating an SNS.
#[clap(hide(true))]
Deploy(deploy::DeployOpts),
/// Subcommand for importing sns API definitions and canister IDs.
#[clap(hide(true))]
Import(SnsImportOpts),
}

/// Executes `dfx sns` and its subcommands.
pub fn exec(env: &dyn Environment, cmd: SnsOpts) -> DfxResult {
match cmd.subcmd {
SubCommand::Config(v) => config::exec(env, v),
SubCommand::Import(v) => import::exec(env, v),
SubCommand::Deploy(v) => deploy::exec(env, v),
}
}
3 changes: 3 additions & 0 deletions src/dfx/src/lib/nns/install_nns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,9 @@ async fn get_subnet_id(agent: &Agent) -> anyhow::Result<Principal> {
/// The NNS canisters use the very first few canister IDs; they must be available.
#[context("Failed to verify that the network is empty; dfx nns install must be run just after dfx start --clean")]
async fn verify_nns_canister_ids_are_available(agent: &Agent) -> anyhow::Result<()> {
/// Checks that the canister is unused on the given network.
///
/// The network is queried directly; local state such as canister_ids.json has no effect on this function.
async fn verify_canister_id_is_available(
agent: &Agent,
canister_id: &str,
Expand Down
2 changes: 2 additions & 0 deletions src/dfx/src/lib/nns/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
//! Code for managing the network nervous system.
#![warn(clippy::missing_docs_in_private_items)]
pub mod install_nns;
2 changes: 2 additions & 0 deletions src/dfx/src/lib/sns/create_config.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//! Code for creating SNS configurations
use anyhow::{anyhow, Context};
use fn_error_context::context;
use std::path::Path;
Expand All @@ -6,6 +7,7 @@ use std::process::{self, Command};
use crate::lib::error::DfxResult;
use crate::Environment;

/// Ceates an SNS configuration template.
#[context("Failed to create sns config at {}.", path.display())]
pub fn create_config(env: &dyn Environment, path: &Path) -> DfxResult {
let cli_name = "sns";
Expand Down
19 changes: 19 additions & 0 deletions src/dfx/src/lib/sns/deploy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//! Code for creating an SNS.
use fn_error_context::context;
use std::ffi::OsString;
use std::path::Path;

use crate::lib::error::DfxResult;
use crate::lib::sns::sns_cli::call_sns_cli;
use crate::Environment;

/// Creates an SNS. This requires funds but no proposal.
#[context("Failed to deploy SNS with config: {}", path.display())]
pub fn deploy_sns(env: &dyn Environment, path: &Path) -> DfxResult<String> {
let args = vec![
OsString::from("deploy"),
OsString::from("--init-config-file"),
OsString::from(path),
];
call_sns_cli(env, &args).map(|stdout| format!("Deployed SNS: {}\n{}", path.display(), stdout))
}
4 changes: 4 additions & 0 deletions src/dfx/src/lib/sns/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
//! Code for decentralizing dapps
#![warn(clippy::missing_docs_in_private_items)]
pub mod create_config;
pub mod deploy;
pub mod sns_cli;
pub mod validate_config;

/// The default location of an SNS configuration file.
pub const CONFIG_FILE_NAME: &str = "sns.yml";
Loading