From 6b204fdcb1cba8d96d3fbae11f94cd1d57d49766 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 20 Apr 2023 15:04:46 +0200 Subject: [PATCH] Ckbtc install (#121) # Motivation We would like to have e2e tests for ckbtc functionality in the nns-dapp repo. # Changes - Add a CKBTC deployment script - This is the script from the nns-dapp repo, with variables such as network taken from command line flags. - Include ckbtc canisters in the stock snapshot # Tests * The ckbtc deployment contains a self-test. Additionally, there is a set of tests for the self-test itself. --------- Co-authored-by: Max Murphy-Skvorzov Co-authored-by: Formatting Committer --- .github/workflows/checks.yml | 33 +++++++-- bin/dfx-canister-check-wasm-hash | 44 ++++++++++++ bin/dfx-ckbtc-deploy | 111 +++++++++++++++++++++++++++++++ bin/dfx-ckbtc-deploy.test | 73 ++++++++++++++++++++ bin/dfx-snapshot-stock-make | 4 ++ 5 files changed, 260 insertions(+), 5 deletions(-) create mode 100755 bin/dfx-canister-check-wasm-hash create mode 100755 bin/dfx-ckbtc-deploy create mode 100755 bin/dfx-ckbtc-deploy.test diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 74ed22d1..c9e5ca6e 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -44,12 +44,35 @@ jobs: uses: ludeeus/action-shellcheck@master env: SHELLCHECK_OPTS: -e SC1090 -e SC2119 -e SC1091 -e SC2121 -e SC2155 -e SC2094 -e SC2015 + clap-checks: + needs: formatting + name: Clap checks + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v3 - name: Clap works run: ./bin/clap.test + ckbtc-checks: + needs: formatting + name: CKBTC tools + runs-on: ubuntu-20.04 + steps: + - name: Install apt-dependencies + run: sudo apt-get update && sudo apt-get install moreutils -yy && command -v more + - uses: actions/checkout@v3 + - name: Install dfx + run: ./bin/dfx-software-dfx-install --version "$(jq -r .dfx dfx.json)" - name: Import ckbtc works run: | - set -euxo pipefail - echo "This modifies files, so make sure the state is clean before and after" - git clean -dfx - bin/dfx-ckbtc-import.test - git clean -dfx + set -euxo pipefail + echo "This modifies files, so make sure the state is clean before and after" + git clean -dfx + bin/dfx-ckbtc-import.test + git clean -dfx + - name: Deploy ckbtc works + run: | + set -euxo pipefail + echo "This modifies files, so make sure the state is clean before and after" + git clean -dfx + bin/dfx-ckbtc-deploy.test + git clean -dfx diff --git a/bin/dfx-canister-check-wasm-hash b/bin/dfx-canister-check-wasm-hash new file mode 100755 index 00000000..7a557e91 --- /dev/null +++ b/bin/dfx-canister-check-wasm-hash @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +set -euo pipefail +SOURCE_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" +PATH="$SOURCE_DIR:$PATH" + +print_help() { + cat <<-EOF + + Checks that a deployed canister's hash matches a local WASM. + If a deployment succeeded, the wasms must match. + + This will not detect whether the canister arguments were as expected. + EOF +} + +# Source the clap.bash file --------------------------------------------------- +source "$SOURCE_DIR/clap.bash" +# Define options +clap.define short=n long=network desc="The dfx network to use" variable=DFX_NETWORK default="local" +clap.define short=c long=canister desc="The canister name or ID" variable=DFX_CANISTER +clap.define short=w long=wasm desc="The path to the local wasm file" variable=DFX_WASM +# Source the output file ---------------------------------------------------------- +source "$(clap.build)" + +# If the location of the wasm was not supplied, get the default location specified in dfx.json +DFX_WASM="${DFX_WASM:-$(n="$DFX_CANISTER" jq -r '.canisters[env.n].wasm' dfx.json)}" +test -e "${DFX_WASM:-}" || { + echo "ERROR: Wasm not found at '${DFX_WASM:-}'" + echo " Please check and specify the correct path." + exit 1 +} >&2 + +build_hash="$(sha256sum "$DFX_WASM" | awk '{print $1}')" +deployed_hash="$(dfx canister info "$DFX_CANISTER" --network "$DFX_NETWORK" | awk '/Module hash/{print $3}')" +if [[ "0x$build_hash" == "$deployed_hash" ]]; then + echo "Check passed: Installed $DFX_CANISTER matches local wasm." +else + { + echo "ERROR: Deployed $DFX_CANISTER hash does not match." + echo "Local build: 0x$build_hash" + echo "Deployed: $deployed_hash" + exit 1 + } >&2 +fi diff --git a/bin/dfx-ckbtc-deploy b/bin/dfx-ckbtc-deploy new file mode 100755 index 00000000..70fcf7b9 --- /dev/null +++ b/bin/dfx-ckbtc-deploy @@ -0,0 +1,111 @@ +#!/usr/bin/env bash +# Original source: https://github.com/dfinity/ckBTC-Minter-Frontend/blob/master/local_deploy.sh +# Via: https://github.com/dfinity/nns-dapp/blob/main/scripts/ckbtc/deploy-ckbtc +set -euo pipefail +SOURCE_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" +PATH="$SOURCE_DIR:$PATH" +. "$SOURCE_DIR/versions.bash" + +print_help() { + cat <<-EOF + + Installs ckbtc canisters. + + EOF +} + +# Source the clap.bash file --------------------------------------------------- +source "$SOURCE_DIR/clap.bash" +# Define options +clap.define short=n long=network desc="The dfx network to use" variable=DFX_NETWORK default="local" +clap.define short=p long=prefix desc="Prefix for the local canister names" variable=LOCAL_PREFIX default="ckbtc_" +clap.define short=y long=yes desc="Deploy even if there are existing ckbtc canisters." variable=DFX_YES nargs=0 +clap.define short=c long=check desc="Check that the canisters are present and correct." variable=DFX_CHECK nargs=0 +clap.define short=m long=mode desc="Canister install mode." variable=DFX_MODE default="reinstall" +# Source the output file ---------------------------------------------------------- +source "$(clap.build)" + +: Check preconditions. This is always run. +"${SOURCE_DIR}/dfx-ckbtc-import" --check --prefix "$LOCAL_PREFIX" || { + echo "ERROR: ckbtc canister data and files are not all present and correct." + echo " Please run:" + echo " dfx-ckbtc-import --prefix '$LOCAL_PREFIX'" + echo + exit 1 +} >&2 + +deploy_ckbtc() { + dfx canister create "${LOCAL_PREFIX}ledger" --network "$DFX_NETWORK" || echo "${LOCAL_PREFIX}ledger canister already exists" + dfx canister create "${LOCAL_PREFIX}minter" --network "$DFX_NETWORK" || echo "${LOCAL_PREFIX}minter canister already exists" + + MINTERID="$(dfx canister id "${LOCAL_PREFIX}minter" --network "$DFX_NETWORK")" + echo "$MINTERID" + LEDGERID="$(dfx canister id "${LOCAL_PREFIX}ledger" --network "$DFX_NETWORK")" + echo "$LEDGERID" + + # echo "Step 2: deploying minter canister..." + dfx deploy "${LOCAL_PREFIX}minter" --network "$DFX_NETWORK" --argument "(variant { + Init = record { + btc_network = variant { Regtest }; + ledger_id = principal \"$LEDGERID\"; + ecdsa_key_name = \"dfx_test_key\"; + retrieve_btc_min_amount = 0; + max_time_in_queue_nanos = 420_000_000_000; + min_confirmations = opt 12; + mode = variant { GeneralAvailability }; + kyt_fee = opt 13_333; + kyt_principal = null; + } +})" --mode="${DFX_MODE}" ${DFX_YES:+--yes} --upgrade-unchanged + + echo "Step 3: deploying ledger canister..." + PRINCIPAL="$(dfx identity get-principal)" + dfx deploy "${LOCAL_PREFIX}ledger" --network "$DFX_NETWORK" --argument "(variant { + Init = record { + token_symbol = \"ckBTC\"; + token_name = \"Token ckBTC\"; + minting_account = record { owner = principal \"$MINTERID\" }; + transfer_fee = 11_500; + metadata = vec {}; + initial_balances = vec { record { record { owner = principal \"$PRINCIPAL\"; }; 10_000_000; }; }; + archive_options = record { + num_blocks_to_archive = 10_000; + trigger_threshold = 20_000; + controller_id = principal \"$PRINCIPAL\"; + cycles_for_archive_creation = opt 4_000_000_000_000; + }; + } +})" --mode="$DFX_MODE" ${DFX_YES:+--yes} --upgrade-unchanged + + echo "Step 4: deploying index canister..." + dfx deploy "${LOCAL_PREFIX}index" --network "$DFX_NETWORK" --argument "(record { ledger_id = principal \"$LEDGERID\" })" --mode="${DFX_MODE}" ${DFX_YES:+--yes} --upgrade-unchanged + + # Example to mint ckBTC + + # BTCADDRESS="$(dfx canister call "${LOCAL_PREFIX}minter" get_btc_address '(record {subaccount=null;})')" + # dfx canister call "${LOCAL_PREFIX}minter" update_balance '(record {subaccount=null;})' + # WITHDRAWALADDRESS="$(dfx canister call "${LOCAL_PREFIX}minter" get_withdrawal_account)" + # echo $BTCADDRESS + # echo $WITHDRAWALADDRESS + # + # cleaned_output=$(echo $WITHDRAWALADDRESS | sed -re 's/^\(|, \)$//g') + # + # dfx canister call "${LOCAL_PREFIX}ledger" icrc1_transfer "(record {from=null; to=$cleaned_output; amount=1000000; fee=null; memo=null; created_at_time=null;})" + # + # Execute the command to get the input string and save the result + # dfx canister call "${LOCAL_PREFIX}minter" retrieve_btc '(record {fee = null; address="bcrt1qu9za0uzzd3kjjecgv7waqq0ynn8dl8l538q0xl"; amount=10000})' + + echo "Step 5: transfer ckBTC to principal..." + # record { owner= principal “”;} + dfx canister call "${LOCAL_PREFIX}ledger" --network "$DFX_NETWORK" icrc1_transfer "(record {from=null; to=record { owner= principal \"73avq-yvrvj-kuzxq-kttlj-nkaz4-tecy6-biuud-3ymeg-guvci-naire-uqe\";}; amount=1000000; fee=null; memo=null; created_at_time=null;})" +} + +check_ckbtc_deployment() { + for canister in "${LOCAL_PREFIX}ledger" "${LOCAL_PREFIX}minter"; do + : "Verify that the deployed canister matches the local wasm" + dfx-canister-check-wasm-hash --canister "$canister" + done +} + +[[ "${DFX_CHECK:-}" == "true" ]] || deploy_ckbtc +check_ckbtc_deployment diff --git a/bin/dfx-ckbtc-deploy.test b/bin/dfx-ckbtc-deploy.test new file mode 100755 index 00000000..2ab76903 --- /dev/null +++ b/bin/dfx-ckbtc-deploy.test @@ -0,0 +1,73 @@ +#!/usr/bin/env bash +set -euo pipefail +SOURCE_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")" +PATH="$SOURCE_DIR:$PATH" + +( + printf "\n\n===================================\n" + echo "Should fail if ckbtc canisters have not been imported" + git checkout dfx.json + error_log="$(mktemp ,test_log.XXXXXXXX)" + if dfx-ckbtc-deploy --check 2>"$error_log"; then + echo "ERROR: Should fail if ckbtc canisters are not in dfx.json" + exit 1 + fi + grep 'ckbtc canister data and files are not all present and correct' "$error_log" || { + echo "The check should complain about ckbtc canisters missing in dfx.json" + exit 1 + } + rm "$error_log" +) +( + printf "\n\n===================================\n" + echo "Should fail if ckbtc canisters have not been created" + dfx-network-stop + dfx start --clean --background + git checkout dfx.json + dfx-ckbtc-import + error_log="$(mktemp ,test_log.XXXXXXXX)" + if dfx-ckbtc-deploy --check 2>"$error_log"; then + echo "ERROR: Should fail if ckbtc canisters are not in dfx.json" + fi + grep 'Failed to determine id for canister' "$error_log" || { + echo "The check should complain about missing canisters" + exit 1 + } + rm "$error_log" + dfx-network-stop +) +( + printf "\n\n===================================\n" + echo "Should fail if ckbtc canisters have not been populated" + dfx-network-stop + dfx start --clean --background + git checkout dfx.json + dfx-ckbtc-import + dfx canister create "ckbtc_ledger" --no-wallet + dfx canister create "ckbtc_minter" --no-wallet + error_log="$(mktemp ,test_log.XXXXXXXX)" + if dfx-ckbtc-deploy --check 2>"$error_log"; then + echo "ERROR: Should fail if ckbtc canisters have the incorrect or no hash" + fi + grep 'ERROR: Deployed ckbtc_ledger hash does not match.' "$error_log" || { + echo "ERROR: The check should complain about missing canisters." + echo "Actual error output:" + sed 's/^/ /g' "$error_log" + exit 1 + } + rm "$error_log" + dfx-network-stop +) +( + printf "\n\n===================================\n" + echo "Should succeed if canisters have been deployed" + dfx-network-stop + dfx start --clean --background + git checkout dfx.json + dfx-ckbtc-import + dfx-ckbtc-deploy --yes + dfx-ckbtc-deploy --check + dfx-network-stop +) + +printf "\n\n\nckbtc tests passed.\n" diff --git a/bin/dfx-snapshot-stock-make b/bin/dfx-snapshot-stock-make index 04450b71..93bfbe9b 100755 --- a/bin/dfx-snapshot-stock-make +++ b/bin/dfx-snapshot-stock-make @@ -20,6 +20,10 @@ source "$(clap.build)" : Set up SNS state and create one finalized SNS dfx-sns-demo +: Set up ckbtc canisters +dfx-ckbtc-import --prefix ckbtc_ +dfx-ckbtc-deploy --prefix ckbtc_ --yes + : "Wait for a checkpoint" dfx-network-wait-for-checkpoint --timeout 600