From 26ee3b06f79214df91b5b3c316eff1c558d9cd9f Mon Sep 17 00:00:00 2001 From: Marcin Nowak-Liebiediew Date: Tue, 23 Jan 2024 15:09:00 +0100 Subject: [PATCH] rework --- e2e/assets/faucet/main.mo | 22 +-- e2e/tests-dfx/cycles-ledger.bash | 90 +---------- .../commands/cycles/redeem_faucet_coupon.rs | 149 ++---------------- 3 files changed, 33 insertions(+), 228 deletions(-) diff --git a/e2e/assets/faucet/main.mo b/e2e/assets/faucet/main.mo index 9d0d66bd47..8febbdc399 100644 --- a/e2e/assets/faucet/main.mo +++ b/e2e/assets/faucet/main.mo @@ -2,22 +2,25 @@ import Cycles "mo:base/ExperimentalCycles"; import Error "mo:base/Error"; import Principal "mo:base/Principal"; import Text "mo:base/Text"; +import Debug "mo:base/Debug"; + actor class Coupon() = self { type Management = actor { - deposit_cycles : ({canister_id : Principal}) -> async (); + deposit_cycles : ({canister_id : Principal}) -> async (); }; type CyclesLedger = actor { - deposit : ({code: Text; account: Account}) -> async (DepositResult); - }; - type DepositResult = { - balance : Nat; - block_index : Nat; + deposit : (DepositArgs) -> async (DepositResult); }; type Account = { owner : Principal; subaccount : ?Blob; }; + type DepositArgs = { + to : Account; + memo : ?Blob; + }; + type DepositResult = { balance : Nat; block_index : Nat }; // Uploading wasm is hard. This is much easier to handle. @@ -61,9 +64,10 @@ actor class Coupon() = self { let CyclesLedgerCanister : CyclesLedger = actor("um5iw-rqaaa-aaaaq-qaaba-cai"); var amount = 10000000000000; Cycles.add(amount); - return await CyclesLedgerCanister.deposit({ - code = code; - account = account + let result = await CyclesLedgerCanister.deposit({ + to = account; + memo = null }); + return result; }; }; diff --git a/e2e/tests-dfx/cycles-ledger.bash b/e2e/tests-dfx/cycles-ledger.bash index 359bc1a27a..444fcecdd5 100644 --- a/e2e/tests-dfx/cycles-ledger.bash +++ b/e2e/tests-dfx/cycles-ledger.bash @@ -525,7 +525,7 @@ current_time_nanoseconds() { # setup done dfx identity use alice - # shellcheck disable=SC2031 + # shellcheck disable=SC2031,SC2030 export DFX_DISABLE_AUTO_WALLET=1 assert_command dfx canister create --all --with-cycles 10T assert_command dfx cycles balance --precise @@ -546,47 +546,7 @@ current_time_nanoseconds() { assert_eq "22.379 TC (trillion cycles)." } -@test "redeem-faucet-coupon can set a new wallet and top up an existing one" { - dfx_new hello - install_asset faucet - dfx deploy - dfx ledger fabricate-cycles --canister faucet --t 1000 - - dfx identity new --storage-mode plaintext faucet_testing - dfx identity use faucet_testing - - # prepare wallet to hand out - dfx wallet balance # this creates a new wallet with user faucet_testing as controller - dfx canister call faucet set_wallet_to_hand_out "(principal \"$(dfx identity get-wallet)\")" # register the wallet as the wallet that the faucet will return - rm "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/wallets.json" # forget about the currently configured wallet - - # assert: no wallet configured - # shellcheck disable=SC2031 - export DFX_DISABLE_AUTO_WALLET=1 - assert_command_fail dfx wallet balance - assert_match "No wallet configured" - - assert_command dfx cycles redeem-faucet-coupon --new-cycles-wallet --faucet "$(dfx canister id faucet)" 'valid-coupon' - assert_match "Redeemed coupon valid-coupon for 100.000 TC .* to a new wallet" - - # only succeeds if wallet is correctly set - assert_command dfx wallet balance - # checking only balance before the dot, rest may fluctuate - # balance may be 99.??? TC if cycles accounting is done, or 100.000 TC if not - assert_match "99\.|100\." - - unset DFX_DISABLE_AUTO_WALLET - - assert_command dfx cycles redeem-faucet-coupon --faucet "$(dfx canister id faucet)" 'another-valid-coupon' - assert_match "Redeemed coupon code another-valid-coupon for 10.000 TC .* to the existing wallet" - - assert_command dfx wallet balance - # checking only balance before the dot, rest may fluctuate - # balance may be 109.??? TC if cycles accounting is done, or 110.000 TC if not - assert_match "109\.|110\." -} - -@test "redeem-faucet-coupon without --new-cycles-wallet redeems into the cycles ledger when no wallet exists" { +@test "redeem-faucet-coupon without redeems into the cycles ledger" { assert_command deploy_cycles_ledger dfx_new hello install_asset faucet @@ -596,50 +556,6 @@ current_time_nanoseconds() { dfx identity new --storage-mode plaintext no_wallet_identity dfx identity use no_wallet_identity - # ensure no wallet is set for the identity - # rm "$DFX_CONFIG_ROOT/.config/dfx/identity/no_wallet_identity/wallets.json" - - # redeem a faucet coupon without specifying --new-cycles-wallet assert_command dfx cycles redeem-faucet-coupon --faucet "$(dfx canister id faucet)" 'valid-coupon' - assert_match "Redeemed coupon code valid-coupon for .* TC .* to the cycles ledger.", -} - -@test "redeem-faucet-coupon without specifying --new-cycles-wallet redeems into existing wallet" { - dfx_new hello - install_asset faucet - dfx deploy - dfx ledger fabricate-cycles --canister faucet --t 1000 - - dfx identity new --storage-mode plaintext wallet_identity - dfx identity use wallet_identity - - # create a new wallet for wallet_identity - assert_command dfx wallet balance - - # ensure a wallet is configured for the identity - assert_command dfx identity get-wallet - - # redeem a faucet coupon without specifying --new-cycles-wallet - assert_command dfx cycles redeem-faucet-coupon --faucet "$(dfx canister id faucet)" 'valid-coupon' - assert_match "Redeemed coupon code valid-coupon for .* TC .* to the existing wallet .*", -} - -@test "redeem-faucet-coupon with --new-cycles-wallet fails when wallet already exists" { - dfx_new hello - install_asset faucet - dfx deploy - dfx ledger fabricate-cycles --canister faucet --t 1000 - - dfx identity new --storage-mode plaintext existing_wallet_identity - dfx identity use existing_wallet_identity - - # create a new wallet for existing_wallet_identity - assert_command dfx wallet balance - - # ensure a wallet is configured for the identity - assert_command dfx identity get-wallet - - # try to redeem a faucet coupon with the --new-cycles-wallet flag set to 'true' - assert_command_fail dfx cycles redeem-faucet-coupon --faucet "$(dfx canister id faucet)" --new-cycles-wallet 'valid-coupon' - assert_match "A cycles wallet already exists for the current identity." + assert_match "Redeemed coupon 'valid-coupon' to the cycles ledger, current balance: .* TC .* for identity '$(dfx identity get-principal)'" } diff --git a/src/dfx/src/commands/cycles/redeem_faucet_coupon.rs b/src/dfx/src/commands/cycles/redeem_faucet_coupon.rs index 239e0c5be9..c637d01895 100644 --- a/src/dfx/src/commands/cycles/redeem_faucet_coupon.rs +++ b/src/dfx/src/commands/cycles/redeem_faucet_coupon.rs @@ -1,15 +1,11 @@ -use crate::commands::wallet::get_wallet; -use crate::lib::diagnosis::DiagnosedError; use crate::lib::environment::Environment; use crate::lib::error::DfxResult; -use crate::lib::identity::wallet::{set_wallet_id, GetOrCreateWalletCanisterError}; use crate::lib::root_key::fetch_root_key_if_needed; use crate::util::{format_as_trillions, pretty_thousand_separators}; use anyhow::{anyhow, bail, Context}; use candid::{encode_args, CandidType, Decode, Deserialize, Principal}; use clap::Parser; -use ic_agent::Agent; -use ic_utils::interfaces::WalletCanister; +use icrc_ledger_types::icrc1::account::Account; use slog::{info, warn}; pub const DEFAULT_FAUCET_PRINCIPAL: Principal = @@ -24,10 +20,6 @@ pub struct RedeemFaucetCouponOpts { /// Alternative faucet address. If not set, this uses the DFINITY faucet. #[arg(long)] faucet: Option, - - /// Redeem coupon to a new cycles wallet, creates a if the identity does not have one, otherwise returns an error. - #[arg(long, default_value = "false")] - new_cycles_wallet: bool, } pub async fn exec(env: &dyn Environment, opts: RedeemFaucetCouponOpts) -> DfxResult { @@ -48,125 +40,6 @@ pub async fn exec(env: &dyn Environment, opts: RedeemFaucetCouponOpts) -> DfxRes } info!(log, "Redeeming coupon. This may take up to 30 seconds..."); - let wallet = get_wallet(env) - .await - .map_err(|e| e.downcast::()); - let coupon_code = opts.coupon_code; - match wallet { - Ok(_) if opts.new_cycles_wallet => { - bail!("A cycles wallet already exists for the current identity. Use the wallet to redeem the coupon."); - } - // identity already has a wallet - faucet should top up the wallet - Ok(wallet_canister) => { - let redeemed_cycles = - redeem_to_existing_wallet(agent, &wallet_canister, &faucet_principal, &coupon_code) - .await?; - info!( - log, - "Redeemed coupon code {coupon_code} for {} TC (trillion cycles) to the existing wallet {}", - pretty_thousand_separators(format_as_trillions(redeemed_cycles)), - wallet_canister.canister_id_() - ); - } - // identity has no wallet yet - faucet will provide one - Err(Ok(GetOrCreateWalletCanisterError::NoWalletConfigured { .. })) - if opts.new_cycles_wallet => - { - let (redeemed_cycles, new_wallet_address) = - create_wallet_and_redeem(agent, env, &faucet_principal, &coupon_code).await?; - info!( - log, - "Redeemed coupon {coupon_code} for {} TC (trillion cycles) to a new wallet {new_wallet_address}.", - pretty_thousand_separators(format_as_trillions(redeemed_cycles)) - ); - } - Err(_) if opts.new_cycles_wallet => { - bail!("Failed to create a new cycles wallet."); - } - // identity has no wallet yet - faucet will redeem the coupon to the cycles ledger - Err(_) => { - let redeemed_cycles = - redeem_to_cycles_ledger(agent, env, &faucet_principal, &coupon_code).await?; - info!( - log, - "Redeemed coupon code {coupon_code} for {} TC (trillion cycles) to the cycles ledger.", - pretty_thousand_separators(format_as_trillions(redeemed_cycles)) - ); - } - }; - - Ok(()) -} - -async fn redeem_to_existing_wallet( - agent: &Agent, - wallet_canister: &WalletCanister<'_>, - faucet_principal: &Principal, - coupon_code: &str, -) -> DfxResult { - let wallet_principal = wallet_canister.canister_id_(); - let response = agent - .update(&faucet_principal, "redeem_to_wallet") - .with_arg( - encode_args((coupon_code, wallet_principal)) - .context("Failed to serialize redeem_to_wallet arguments.")?, - ) - .call_and_wait() - .await - .context("Failed redeem_to_wallet call.")?; - let redeemed_cycles = - Decode!(&response, u128).context("Failed to decode redeem_to_wallet response.")?; - Ok(redeemed_cycles) -} - -async fn create_wallet_and_redeem( - agent: &Agent, - env: &dyn Environment, - faucet_principal: &Principal, - coupon_code: &str, -) -> DfxResult<(u128, Principal)> { - let identity = env - .get_selected_identity() - .with_context(|| anyhow!("No identity selected."))?; - let response = agent - .update(&faucet_principal, "redeem") - .with_arg(encode_args((coupon_code,)).context("Failed to serialize 'redeem' arguments.")?) - .call_and_wait() - .await - .context("Failed 'redeem' call.")?; - let new_wallet_address = - Decode!(&response, Principal).context("Failed to decode 'redeem' response.")?; - set_wallet_id(env.get_network_descriptor(), &identity, new_wallet_address) - .with_context(|| { - DiagnosedError::new( - format!( - "dfx failed while trying to set your new wallet, '{}'", - &new_wallet_address - ), - format!("Please save your new wallet's ID '{}' and set the wallet manually afterwards using 'dfx identity set-wallet'.", &new_wallet_address), - ) - })?; - let redeemed_cycles = WalletCanister::create(agent, new_wallet_address.clone()) - .await - .unwrap() - .wallet_balance() - .await - .unwrap() - .amount; - Ok((redeemed_cycles, new_wallet_address)) -} - -async fn redeem_to_cycles_ledger( - agent: &Agent, - env: &dyn Environment, - faucet_principal: &Principal, - coupon_code: &str, -) -> DfxResult { - #[derive(CandidType, Deserialize)] - struct Account { - owner: Principal, - subaccount: Option>, - } let identity = env .get_selected_identity_principal() .with_context(|| anyhow!("No identity selected."))?; @@ -174,7 +47,7 @@ async fn redeem_to_cycles_ledger( .update(&faucet_principal, "redeem_to_cycles_ledger") .with_arg( encode_args(( - coupon_code, + opts.coupon_code.clone(), Account { owner: identity, subaccount: None, @@ -185,10 +58,22 @@ async fn redeem_to_cycles_ledger( .call_and_wait() .await .context("Failed 'redeem_to_cycles_ledger' call.")?; - let result = Decode!(&response, (u128, u128)) + #[derive(CandidType, Deserialize)] + struct DepositResponse { + balance: u128, + block_index: u128, + } + let result = Decode!(&response, DepositResponse) .context("Failed to decode 'redeem_to_cycles_ledger' response.")?; - let redeemed_cycles = result.0; - Ok(redeemed_cycles) + let redeemed_cycles = result.balance; + info!( + log, + "Redeemed coupon '{}' to the cycles ledger, current balance: {} TC (trillions of cycles) for identity '{}'.", + opts.coupon_code.clone(), + pretty_thousand_separators(format_as_trillions(redeemed_cycles)), + identity, + ); + Ok(()) } #[cfg(test)]