From b4c2e0046517347ca8f6b9f21b643d364140e1c4 Mon Sep 17 00:00:00 2001 From: Severin Siffert Date: Tue, 19 Apr 2022 13:21:04 +0200 Subject: [PATCH 1/4] feat: add local-top-up command --- CHANGELOG.adoc | 4 + e2e/tests-dfx/local_top_up.bash | 44 ++++++++++ src/dfx/src/commands/canister/local_top_up.rs | 82 +++++++++++++++++++ src/dfx/src/commands/canister/mod.rs | 3 + src/dfx/src/lib/operations/canister/mod.rs | 31 +++++++ src/dfx/src/lib/root_key.rs | 21 +++++ 6 files changed, 185 insertions(+) create mode 100644 e2e/tests-dfx/local_top_up.bash create mode 100644 src/dfx/src/commands/canister/local_top_up.rs diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index a5359c2710..389db38e10 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -53,6 +53,10 @@ Example how to specify the subnet type: } ---- +=== feat: Introduce command for local cycles top-up + +`dfx canister local-top-up ` can be used during local development to create cycles out of thin air and add them to a canister. Instead of supplying a canister name or id it is also possible to use `--all` to add the cycles to every canister in the current project. When no amount is supplied, the command uses 10T cycles as default. Using this command with `--network ic` will result in an error. + === feat: Private keys can be stored in encrypted format `dfx identity new` and `dfx identity import` now ask you for a password to encrypt the private key (PEM file) when it is stored on disk. diff --git a/e2e/tests-dfx/local_top_up.bash b/e2e/tests-dfx/local_top_up.bash new file mode 100644 index 0000000000..640c2d6ac6 --- /dev/null +++ b/e2e/tests-dfx/local_top_up.bash @@ -0,0 +1,44 @@ +#!/usr/bin/env bats + +load ../utils/_ + +setup() { + standard_setup + + dfx_new hello +} + +teardown() { + dfx_stop + + standard_teardown +} + +@test "canister local-top-up works with default amount" { + install_asset greet + dfx_start + dfx deploy + # default amount is 10 trillion cycles, which results in an amount like 13_899_071_239_420 + assert_command dfx canister local-top-up "$(dfx canister id hello)" + # bash does not accept \d, use [0-9] instead + assert_match 'updated balance: [0-9]{2}(_[0-9]{3}){4} cycles' + assert_command dfx canister local-top-up --all + assert_match 'updated balance: [0-9]{2}(_[0-9]{3}){4} cycles' +} + +@test "canister local-top-up works with specific amount" { + install_asset greet + dfx_start + dfx deploy + # adding 100 trillion cycles, which results in an amount like 103_899_071_239_420 + assert_command dfx canister local-top-up "$(dfx canister id hello)" 100000000000000 + assert_match 'updated balance: [0-9]{3}(_[0-9]{3}){4} cycles' + assert_command dfx canister local-top-up hello 100000000000000 + assert_match 'updated balance: [0-9]{3}(_[0-9]{3}){4} cycles' +} + +@test "canister local-top-up fails on real IC" { + install_asset greet + assert_command_fail dfx canister --network ic local-top-up --all + assert_match "Cannot run this on the real IC." +} diff --git a/src/dfx/src/commands/canister/local_top_up.rs b/src/dfx/src/commands/canister/local_top_up.rs new file mode 100644 index 0000000000..522f2f55af --- /dev/null +++ b/src/dfx/src/commands/canister/local_top_up.rs @@ -0,0 +1,82 @@ +use crate::lib::environment::Environment; +use crate::lib::error::DfxResult; +use crate::lib::identity::identity_utils::CallSender; +use crate::lib::models::canister_id_store::CanisterIdStore; +use crate::lib::operations::canister; +use crate::lib::root_key::fetch_root_key_or_anyhow; +use crate::util::clap::validators::cycle_amount_validator; +use crate::util::expiry_duration; + +use clap::Parser; +use ic_types::Principal; +use slog::info; +use std::time::Duration; + +/// Local development only: Mint cycles and deposit them into the specified canister(s). +#[derive(Parser)] +pub struct LocalTopUpOpts { + /// Specifies the name or id of the canister to receive the cycles deposit. + /// You must specify either a canister name/id or the --all option. + canister: Option, + + /// Specifies the amount of cycles to add. + #[clap(validator(cycle_amount_validator), default_value = "10000000000000")] + cycles: String, + + /// Deposit cycles to all of the canisters configured in the dfx.json file. + #[clap(long, required_unless_present("canister"))] + all: bool, +} + +async fn deposit_minted_cycles( + env: &dyn Environment, + canister: &str, + timeout: Duration, + call_sender: &CallSender, + cycles: u128, +) -> DfxResult { + let log = env.get_logger(); + let canister_id_store = CanisterIdStore::for_env(env)?; + let canister_id = + Principal::from_text(canister).or_else(|_| canister_id_store.get(canister))?; + + info!(log, "Depositing {} cycles onto {}", cycles, canister,); + + canister::provisional_deposit_cycles(env, canister_id, timeout, call_sender, cycles).await?; + + let status = canister::get_canister_status(env, canister_id, timeout, call_sender).await?; + + info!( + log, + "Deposited {} cycles, updated balance: {} cycles", cycles, status.cycles + ); + + Ok(()) +} + +pub async fn exec( + env: &dyn Environment, + opts: LocalTopUpOpts, + call_sender: &CallSender, +) -> DfxResult { + // amount has been validated by cycle_amount_validator + let cycles = opts.cycles.parse::().unwrap(); + + fetch_root_key_or_anyhow(env).await?; + + let timeout = expiry_duration(); + + if let Some(canister) = opts.canister.as_deref() { + deposit_minted_cycles(env, canister, timeout, call_sender, cycles).await + } else if opts.all { + let config = env.get_config_or_anyhow()?; + if let Some(canisters) = &config.get_config().canisters { + for canister in canisters.keys() { + deposit_minted_cycles(env, canister, timeout, call_sender, cycles).await?; + } + } + Ok(()) + } else { + unreachable!() + } +} diff --git a/src/dfx/src/commands/canister/mod.rs b/src/dfx/src/commands/canister/mod.rs index c290070f95..9a8cd77e8f 100644 --- a/src/dfx/src/commands/canister/mod.rs +++ b/src/dfx/src/commands/canister/mod.rs @@ -13,6 +13,7 @@ mod deposit_cycles; mod id; mod info; mod install; +mod local_top_up; mod request_status; mod send; mod sign; @@ -51,6 +52,7 @@ enum SubCommand { Id(id::CanisterIdOpts), Info(info::InfoOpts), Install(install::CanisterInstallOpts), + LocalTopUp(local_top_up::LocalTopUpOpts), RequestStatus(request_status::RequestStatusOpts), Send(send::CanisterSendOpts), Sign(sign::CanisterSignOpts), @@ -75,6 +77,7 @@ pub fn exec(env: &dyn Environment, opts: CanisterOpts) -> DfxResult { SubCommand::Id(v) => id::exec(&agent_env, v).await, SubCommand::Install(v) => install::exec(&agent_env, v, &call_sender).await, SubCommand::Info(v) => info::exec(&agent_env, v).await, + SubCommand::LocalTopUp(v) => local_top_up::exec(&agent_env, v, &call_sender).await, SubCommand::RequestStatus(v) => request_status::exec(&agent_env, v).await, SubCommand::Send(v) => send::exec(&agent_env, v, &call_sender).await, SubCommand::Sign(v) => sign::exec(&agent_env, v, &call_sender).await, diff --git a/src/dfx/src/lib/operations/canister/mod.rs b/src/dfx/src/lib/operations/canister/mod.rs index 9308ab0e5f..8535f540db 100644 --- a/src/dfx/src/lib/operations/canister/mod.rs +++ b/src/dfx/src/lib/operations/canister/mod.rs @@ -258,6 +258,37 @@ pub async fn deposit_cycles( Ok(()) } +/// Can only run this locally, not on the real IC. +/// Conjures cycles from nothing and deposits them in the selected canister. +pub async fn provisional_deposit_cycles( + env: &dyn Environment, + canister_id: Principal, + timeout: Duration, + call_sender: &CallSender, + cycles: u128, +) -> DfxResult { + #[derive(CandidType)] + struct In { + canister_id: Principal, + amount: u128, + } + let _: () = do_management_call( + env, + canister_id, + MgmtMethod::ProvisionalTopUpCanister.as_ref(), + In { + canister_id, + amount: cycles, + }, + timeout, + call_sender, + 0, + ) + .await?; + + Ok(()) +} + pub fn get_local_cid_and_candid_path( env: &dyn Environment, canister_name: &str, diff --git a/src/dfx/src/lib/root_key.rs b/src/dfx/src/lib/root_key.rs index b5d909fb2b..c2ef274067 100644 --- a/src/dfx/src/lib/root_key.rs +++ b/src/dfx/src/lib/root_key.rs @@ -17,3 +17,24 @@ pub async fn fetch_root_key_if_needed(env: &dyn Environment) -> DfxResult { } Ok(()) } + +/// Fetches the root key of the local network. +/// Returns an error if attempted to run on the real IC. +pub async fn fetch_root_key_or_anyhow(env: &dyn Environment) -> DfxResult { + let agent = env + .get_agent() + .ok_or_else(|| anyhow!("Cannot get HTTP client from environment."))?; + + if !env + .get_network_descriptor() + .expect("no network descriptor") + .is_ic + { + agent.fetch_root_key().await?; + Ok(()) + } else { + Err(anyhow!( + "This command only runs on local instances. Cannot run this on the real IC." + )) + } +} From 3273e51aedc25c9223b67150de50eb33582c9cc2 Mon Sep 17 00:00:00 2001 From: Severin Siffert Date: Wed, 20 Apr 2022 08:50:37 +0200 Subject: [PATCH 2/4] move to dfx ledger fabricate-cycles --- CHANGELOG.adoc | 2 +- ...ocal_top_up.bash => fabricate_cycles.bash} | 16 +++++++------- src/dfx/src/commands/canister/mod.rs | 3 --- .../fabricate_cycles.rs} | 21 ++++++++----------- src/dfx/src/commands/ledger/mod.rs | 3 +++ 5 files changed, 21 insertions(+), 24 deletions(-) rename e2e/tests-dfx/{local_top_up.bash => fabricate_cycles.bash} (60%) rename src/dfx/src/commands/{canister/local_top_up.rs => ledger/fabricate_cycles.rs} (76%) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 389db38e10..d80d005121 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -55,7 +55,7 @@ Example how to specify the subnet type: === feat: Introduce command for local cycles top-up -`dfx canister local-top-up ` can be used during local development to create cycles out of thin air and add them to a canister. Instead of supplying a canister name or id it is also possible to use `--all` to add the cycles to every canister in the current project. When no amount is supplied, the command uses 10T cycles as default. Using this command with `--network ic` will result in an error. +`dfx ledger fabricate-cycles ` can be used during local development to create cycles out of thin air and add them to a canister. Instead of supplying a canister name or id it is also possible to use `--all` to add the cycles to every canister in the current project. When no amount is supplied, the command uses 10T cycles as default. Using this command with `--network ic` will result in an error. === feat: Private keys can be stored in encrypted format diff --git a/e2e/tests-dfx/local_top_up.bash b/e2e/tests-dfx/fabricate_cycles.bash similarity index 60% rename from e2e/tests-dfx/local_top_up.bash rename to e2e/tests-dfx/fabricate_cycles.bash index 640c2d6ac6..d6e54e617f 100644 --- a/e2e/tests-dfx/local_top_up.bash +++ b/e2e/tests-dfx/fabricate_cycles.bash @@ -14,31 +14,31 @@ teardown() { standard_teardown } -@test "canister local-top-up works with default amount" { +@test "ledger fabricate-cycles works with default amount" { install_asset greet dfx_start dfx deploy # default amount is 10 trillion cycles, which results in an amount like 13_899_071_239_420 - assert_command dfx canister local-top-up "$(dfx canister id hello)" + assert_command dfx ledger fabricate-cycles "$(dfx canister id hello)" # bash does not accept \d, use [0-9] instead assert_match 'updated balance: [0-9]{2}(_[0-9]{3}){4} cycles' - assert_command dfx canister local-top-up --all + assert_command dfx ledger fabricate-cycles --all assert_match 'updated balance: [0-9]{2}(_[0-9]{3}){4} cycles' } -@test "canister local-top-up works with specific amount" { +@test "ledger fabricate-cycles works with specific amount" { install_asset greet dfx_start dfx deploy # adding 100 trillion cycles, which results in an amount like 103_899_071_239_420 - assert_command dfx canister local-top-up "$(dfx canister id hello)" 100000000000000 + assert_command dfx ledger fabricate-cycles "$(dfx canister id hello)" 100000000000000 assert_match 'updated balance: [0-9]{3}(_[0-9]{3}){4} cycles' - assert_command dfx canister local-top-up hello 100000000000000 + assert_command dfx ledger fabricate-cycles hello 100000000000000 assert_match 'updated balance: [0-9]{3}(_[0-9]{3}){4} cycles' } -@test "canister local-top-up fails on real IC" { +@test "ledger fabricate-cycles fails on real IC" { install_asset greet - assert_command_fail dfx canister --network ic local-top-up --all + assert_command_fail dfx ledger --network ic fabricate-cycles --all assert_match "Cannot run this on the real IC." } diff --git a/src/dfx/src/commands/canister/mod.rs b/src/dfx/src/commands/canister/mod.rs index 9a8cd77e8f..c290070f95 100644 --- a/src/dfx/src/commands/canister/mod.rs +++ b/src/dfx/src/commands/canister/mod.rs @@ -13,7 +13,6 @@ mod deposit_cycles; mod id; mod info; mod install; -mod local_top_up; mod request_status; mod send; mod sign; @@ -52,7 +51,6 @@ enum SubCommand { Id(id::CanisterIdOpts), Info(info::InfoOpts), Install(install::CanisterInstallOpts), - LocalTopUp(local_top_up::LocalTopUpOpts), RequestStatus(request_status::RequestStatusOpts), Send(send::CanisterSendOpts), Sign(sign::CanisterSignOpts), @@ -77,7 +75,6 @@ pub fn exec(env: &dyn Environment, opts: CanisterOpts) -> DfxResult { SubCommand::Id(v) => id::exec(&agent_env, v).await, SubCommand::Install(v) => install::exec(&agent_env, v, &call_sender).await, SubCommand::Info(v) => info::exec(&agent_env, v).await, - SubCommand::LocalTopUp(v) => local_top_up::exec(&agent_env, v, &call_sender).await, SubCommand::RequestStatus(v) => request_status::exec(&agent_env, v).await, SubCommand::Send(v) => send::exec(&agent_env, v, &call_sender).await, SubCommand::Sign(v) => sign::exec(&agent_env, v, &call_sender).await, diff --git a/src/dfx/src/commands/canister/local_top_up.rs b/src/dfx/src/commands/ledger/fabricate_cycles.rs similarity index 76% rename from src/dfx/src/commands/canister/local_top_up.rs rename to src/dfx/src/commands/ledger/fabricate_cycles.rs index 522f2f55af..6f8dc5abc7 100644 --- a/src/dfx/src/commands/canister/local_top_up.rs +++ b/src/dfx/src/commands/ledger/fabricate_cycles.rs @@ -12,14 +12,14 @@ use ic_types::Principal; use slog::info; use std::time::Duration; -/// Local development only: Mint cycles and deposit them into the specified canister(s). +/// Local development only: Fabricate cycles out of thin air and deposit them into the specified canister(s). #[derive(Parser)] -pub struct LocalTopUpOpts { +pub struct FabricateCyclesOpts { /// Specifies the name or id of the canister to receive the cycles deposit. /// You must specify either a canister name/id or the --all option. canister: Option, - /// Specifies the amount of cycles to add. + /// Specifies the amount of cycles to fabricate. #[clap(validator(cycle_amount_validator), default_value = "10000000000000")] cycles: String, @@ -40,7 +40,7 @@ async fn deposit_minted_cycles( let canister_id = Principal::from_text(canister).or_else(|_| canister_id_store.get(canister))?; - info!(log, "Depositing {} cycles onto {}", cycles, canister,); + info!(log, "Fabricating {} cycles onto {}", cycles, canister,); canister::provisional_deposit_cycles(env, canister_id, timeout, call_sender, cycles).await?; @@ -48,17 +48,13 @@ async fn deposit_minted_cycles( info!( log, - "Deposited {} cycles, updated balance: {} cycles", cycles, status.cycles + "Fabricated {} cycles, updated balance: {} cycles", cycles, status.cycles ); Ok(()) } -pub async fn exec( - env: &dyn Environment, - opts: LocalTopUpOpts, - call_sender: &CallSender, -) -> DfxResult { +pub async fn exec(env: &dyn Environment, opts: FabricateCyclesOpts) -> DfxResult { // amount has been validated by cycle_amount_validator let cycles = opts.cycles.parse::().unwrap(); @@ -67,12 +63,13 @@ pub async fn exec( let timeout = expiry_duration(); if let Some(canister) = opts.canister.as_deref() { - deposit_minted_cycles(env, canister, timeout, call_sender, cycles).await + deposit_minted_cycles(env, canister, timeout, &CallSender::SelectedId, cycles).await } else if opts.all { let config = env.get_config_or_anyhow()?; if let Some(canisters) = &config.get_config().canisters { for canister in canisters.keys() { - deposit_minted_cycles(env, canister, timeout, call_sender, cycles).await?; + deposit_minted_cycles(env, canister, timeout, &CallSender::SelectedId, cycles) + .await?; } } Ok(()) diff --git a/src/dfx/src/commands/ledger/mod.rs b/src/dfx/src/commands/ledger/mod.rs index 0e2eff37c5..1f09ec6210 100644 --- a/src/dfx/src/commands/ledger/mod.rs +++ b/src/dfx/src/commands/ledger/mod.rs @@ -28,6 +28,7 @@ const NOTIFY_METHOD: &str = "notify_dfx"; mod account_id; mod balance; mod create_canister; +mod fabricate_cycles; mod notify; mod top_up; mod transfer; @@ -49,6 +50,7 @@ enum SubCommand { AccountId(account_id::AccountIdOpts), Balance(balance::BalanceOpts), CreateCanister(create_canister::CreateCanisterOpts), + FabricateCycles(fabricate_cycles::FabricateCyclesOpts), Notify(notify::NotifyOpts), TopUp(top_up::TopUpOpts), Transfer(transfer::TransferOpts), @@ -62,6 +64,7 @@ pub fn exec(env: &dyn Environment, opts: LedgerOpts) -> DfxResult { SubCommand::AccountId(v) => account_id::exec(&agent_env, v).await, SubCommand::Balance(v) => balance::exec(&agent_env, v).await, SubCommand::CreateCanister(v) => create_canister::exec(&agent_env, v).await, + SubCommand::FabricateCycles(v) => fabricate_cycles::exec(&agent_env, v).await, SubCommand::Notify(v) => notify::exec(&agent_env, v).await, SubCommand::TopUp(v) => top_up::exec(&agent_env, v).await, SubCommand::Transfer(v) => transfer::exec(&agent_env, v).await, From 0d208bf774e119e983e32a6dcd4e069beaef5116 Mon Sep 17 00:00:00 2001 From: Severin Siffert Date: Wed, 20 Apr 2022 09:19:19 +0200 Subject: [PATCH 3/4] canister is a flag, not an argument --- e2e/tests-dfx/fabricate_cycles.bash | 6 +++--- src/dfx/src/commands/ledger/fabricate_cycles.rs | 9 +++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/e2e/tests-dfx/fabricate_cycles.bash b/e2e/tests-dfx/fabricate_cycles.bash index d6e54e617f..00b2e6a00a 100644 --- a/e2e/tests-dfx/fabricate_cycles.bash +++ b/e2e/tests-dfx/fabricate_cycles.bash @@ -19,7 +19,7 @@ teardown() { dfx_start dfx deploy # default amount is 10 trillion cycles, which results in an amount like 13_899_071_239_420 - assert_command dfx ledger fabricate-cycles "$(dfx canister id hello)" + assert_command dfx ledger fabricate-cycles --canister "$(dfx canister id hello)" # bash does not accept \d, use [0-9] instead assert_match 'updated balance: [0-9]{2}(_[0-9]{3}){4} cycles' assert_command dfx ledger fabricate-cycles --all @@ -31,9 +31,9 @@ teardown() { dfx_start dfx deploy # adding 100 trillion cycles, which results in an amount like 103_899_071_239_420 - assert_command dfx ledger fabricate-cycles "$(dfx canister id hello)" 100000000000000 + assert_command dfx ledger fabricate-cycles --canister "$(dfx canister id hello)" 100000000000000 assert_match 'updated balance: [0-9]{3}(_[0-9]{3}){4} cycles' - assert_command dfx ledger fabricate-cycles hello 100000000000000 + assert_command dfx ledger fabricate-cycles --canister hello 100000000000000 assert_match 'updated balance: [0-9]{3}(_[0-9]{3}){4} cycles' } diff --git a/src/dfx/src/commands/ledger/fabricate_cycles.rs b/src/dfx/src/commands/ledger/fabricate_cycles.rs index 6f8dc5abc7..f2ecf23740 100644 --- a/src/dfx/src/commands/ledger/fabricate_cycles.rs +++ b/src/dfx/src/commands/ledger/fabricate_cycles.rs @@ -15,14 +15,15 @@ use std::time::Duration; /// Local development only: Fabricate cycles out of thin air and deposit them into the specified canister(s). #[derive(Parser)] pub struct FabricateCyclesOpts { - /// Specifies the name or id of the canister to receive the cycles deposit. - /// You must specify either a canister name/id or the --all option. - canister: Option, - /// Specifies the amount of cycles to fabricate. #[clap(validator(cycle_amount_validator), default_value = "10000000000000")] cycles: String, + /// Specifies the name or id of the canister to receive the cycles deposit. + /// You must specify either a canister name/id or the --all option. + #[clap(long)] + canister: Option, + /// Deposit cycles to all of the canisters configured in the dfx.json file. #[clap(long, required_unless_present("canister"))] all: bool, From 35616eace888c4da71b84f09c442d304f1277282 Mon Sep 17 00:00:00 2001 From: Severin Siffert Date: Wed, 20 Apr 2022 09:46:32 +0200 Subject: [PATCH 4/4] add t flag --- e2e/tests-dfx/fabricate_cycles.bash | 4 +-- .../src/commands/ledger/fabricate_cycles.rs | 34 ++++++++++++++++--- src/dfx/src/util/clap/validators.rs | 7 ++++ 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/e2e/tests-dfx/fabricate_cycles.bash b/e2e/tests-dfx/fabricate_cycles.bash index 00b2e6a00a..0887bba691 100644 --- a/e2e/tests-dfx/fabricate_cycles.bash +++ b/e2e/tests-dfx/fabricate_cycles.bash @@ -31,9 +31,9 @@ teardown() { dfx_start dfx deploy # adding 100 trillion cycles, which results in an amount like 103_899_071_239_420 - assert_command dfx ledger fabricate-cycles --canister "$(dfx canister id hello)" 100000000000000 + assert_command dfx ledger fabricate-cycles --canister "$(dfx canister id hello)" --amount 100000000000000 assert_match 'updated balance: [0-9]{3}(_[0-9]{3}){4} cycles' - assert_command dfx ledger fabricate-cycles --canister hello 100000000000000 + assert_command dfx ledger fabricate-cycles --canister hello --t 100 assert_match 'updated balance: [0-9]{3}(_[0-9]{3}){4} cycles' } diff --git a/src/dfx/src/commands/ledger/fabricate_cycles.rs b/src/dfx/src/commands/ledger/fabricate_cycles.rs index f2ecf23740..761a492a53 100644 --- a/src/dfx/src/commands/ledger/fabricate_cycles.rs +++ b/src/dfx/src/commands/ledger/fabricate_cycles.rs @@ -4,7 +4,7 @@ use crate::lib::identity::identity_utils::CallSender; use crate::lib::models::canister_id_store::CanisterIdStore; use crate::lib::operations::canister; use crate::lib::root_key::fetch_root_key_or_anyhow; -use crate::util::clap::validators::cycle_amount_validator; +use crate::util::clap::validators::{cycle_amount_validator, trillion_cycle_amount_validator}; use crate::util::expiry_duration; use clap::Parser; @@ -12,12 +12,22 @@ use ic_types::Principal; use slog::info; use std::time::Duration; +const DEFAULT_CYCLES_TO_FABRICATE: u128 = 10_000_000_000_000_u128; + /// Local development only: Fabricate cycles out of thin air and deposit them into the specified canister(s). #[derive(Parser)] pub struct FabricateCyclesOpts { - /// Specifies the amount of cycles to fabricate. - #[clap(validator(cycle_amount_validator), default_value = "10000000000000")] - cycles: String, + /// Specifies the amount of cycles to fabricate. Defaults to 10T cycles. + #[clap(long, validator(cycle_amount_validator), conflicts_with("t"))] + amount: Option, + + /// Specifies the amount of trillion cycles to fabricate. Defaults to 10T cycles. + #[clap( + long, + validator(trillion_cycle_amount_validator), + conflicts_with("amount") + )] + t: Option, /// Specifies the name or id of the canister to receive the cycles deposit. /// You must specify either a canister name/id or the --all option. @@ -57,7 +67,7 @@ async fn deposit_minted_cycles( pub async fn exec(env: &dyn Environment, opts: FabricateCyclesOpts) -> DfxResult { // amount has been validated by cycle_amount_validator - let cycles = opts.cycles.parse::().unwrap(); + let cycles = cycles_to_fabricate(&opts); fetch_root_key_or_anyhow(env).await?; @@ -78,3 +88,17 @@ pub async fn exec(env: &dyn Environment, opts: FabricateCyclesOpts) -> DfxResult unreachable!() } } + +fn cycles_to_fabricate(opts: &FabricateCyclesOpts) -> u128 { + if let Some(cycles_str) = &opts.amount { + //cycles_str is validated by cycle_amount_validator + cycles_str.parse::().unwrap() + } else if let Some(t_cycles_str) = &opts.t { + //cycles_str is validated by trillion_cycle_amount_validator + format!("{}000000000000", t_cycles_str) + .parse::() + .unwrap() + } else { + DEFAULT_CYCLES_TO_FABRICATE + } +} diff --git a/src/dfx/src/util/clap/validators.rs b/src/dfx/src/util/clap/validators.rs index a30c6ec40e..85c78cc014 100644 --- a/src/dfx/src/util/clap/validators.rs +++ b/src/dfx/src/util/clap/validators.rs @@ -44,6 +44,13 @@ pub fn cycle_amount_validator(cycles: &str) -> Result<(), String> { Err("Must be a non negative amount.".to_string()) } +pub fn trillion_cycle_amount_validator(cycles: &str) -> Result<(), String> { + if format!("{}000000000000", cycles).parse::().is_ok() { + return Ok(()); + } + Err("Must be a non negative amount.".to_string()) +} + pub fn compute_allocation_validator(compute_allocation: &str) -> Result<(), String> { if let Ok(num) = compute_allocation.parse::() { if num <= 100 {