diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 738ae32f12..9204a1c175 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -53,9 +53,9 @@ jobs: matrix: # We build a dynamic-linked linux binary because otherwise HSM support fails with: # Error: IO: Dynamic loading not supported - os: [macos-12, ubuntu-20.04, ubuntu-22.04, windows-2022] + os: [macos-13-large, ubuntu-20.04, ubuntu-22.04, windows-2022] include: - - os: macos-12 + - os: macos-13-large target: x86_64-apple-darwin binary_path: target/x86_64-apple-darwin/release/dfx - os: ubuntu-20.04 @@ -109,7 +109,7 @@ jobs: strategy: fail-fast: false matrix: - os: [macos-12, ubuntu-20.04, ubuntu-22.04] + os: [macos-13-large, ubuntu-20.04, ubuntu-22.04] steps: - uses: actions/checkout@v4 - name: Download dfx binary @@ -182,7 +182,7 @@ jobs: strategy: fail-fast: false matrix: - os: [macos-12, ubuntu-20.04, ubuntu-22.04] + os: [macos-13-large, ubuntu-20.04, ubuntu-22.04] steps: - name: Checking out repo uses: actions/checkout@v4 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 06c4dbba6a..25a2c23b2b 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -41,7 +41,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ ubuntu-latest, macos-12, windows-latest ] + os: [ ubuntu-latest, macos-13-large, windows-latest ] steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/prepare-dfx-assets.yml b/.github/workflows/prepare-dfx-assets.yml index 73a7aa4171..52a768be7a 100644 --- a/.github/workflows/prepare-dfx-assets.yml +++ b/.github/workflows/prepare-dfx-assets.yml @@ -24,7 +24,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ ubuntu-latest, macos-12 ] + os: [ ubuntu-latest, macos-13-large ] steps: - uses: actions/checkout@v4 - uses: actions/cache@v4 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 3b235fc519..e95094effb 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -31,7 +31,7 @@ jobs: # Error: IO: Dynamic loading not supported target: [ x86_64-apple-darwin, x86_64-unknown-linux-gnu ] include: - - os: macos-12 + - os: macos-13-large target: x86_64-apple-darwin binary_path: target/x86_64-apple-darwin/release name: x86_64-darwin diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 22ee0a7f90..18b6987b41 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -43,7 +43,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ ubuntu-latest, macos-12 ] + os: [ ubuntu-latest, macos-13-large ] steps: - uses: actions/checkout@v4 - uses: actions/cache@v4 diff --git a/CHANGELOG.md b/CHANGELOG.md index 33f20a0441..fbb10fe5bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,90 @@ # UNRELEASED +### feat: all commands will use the DFX_NETWORK from the environment + +If `DFX_NETWORK` is set in the environment, all commands will use that network by default. +The `--network` parameter will take precedence if provided. + +### fix: dfx generate now honors the --network parameter +This fixes an issue where `dfx deploy --playground` would fail if the project +had not been previously built for the local network. + +### feat: facade pull ICP, ckBTC, ckETH ledger canisters + +The ledger canisters can be pulled even though they are not really "pullable". +The metadata like wasm_url and init_guide are hardcoded inside `dfx deps pull` logic. + +- ICP ledger: `ryjl3-tyaaa-aaaaa-aaaba-cai` +- ckBTC ledger: `mxzaz-hqaaa-aaaar-qaada-cai` +- ckETH ledger: `ss2fx-dyaaa-aaaar-qacoq-cai` + +### chore: update agent version in frontend templates, and include `resolve.dedupe` in Vite config + +### chore: improve error message when trying to use the local replica when it is not running + +### Frontend canister + +Allow setting permissions lists in init arguments just like in upgrade arguments. + +- Module hash: 2c24b5e1584890a7965011d5d1d827aca68c489c9a6308475730420fa53372e8 +- https://github.com/dfinity/sdk/pull/3965 + +### Candid UI + +- Module hash: f45db224b40fac516c877e3108dc809d4b22fa42d05ee8dfa5002536a3a3daed +- Bump agent-js to fix error code + +### chore!: improve the messages for the subcommands of `dfx cycles`. + +If users run subcommands of `dfx cycles` without the `--ic` flag, show below messages to indicate what to do next. +``` +Error explanation: +Cycles ledger with canister ID 'um5iw-rqaaa-aaaaq-qaaba-cai' is not installed. +How to resolve the error: +Run the command with '--ic' flag if you want to manage the cycles on the mainnet. +``` + +### chore: improve `dfx start` messages. + +For `dfx start`, show below messages to users to indicate what to do next. +``` +Success! The dfx server is running. +You must open a new terminal to continue developing. If you'd prefer to stop, quit with 'Ctrl-C'. +``` + +## Dependencies + +### Motoko + +Updated Motoko to [0.13.3](https://github.com/dfinity/motoko/releases/tag/0.13.3) + +### Replica + +Updated replica to elected commit a62848817cec7ae50618a87a526c85d020283fd9. +This incorporates the following executed proposals: + +- [134036](https://dashboard.internetcomputer.org/proposal/134036) +- [134035](https://dashboard.internetcomputer.org/proposal/134035) +- [134034](https://dashboard.internetcomputer.org/proposal/134034) +- [134032](https://dashboard.internetcomputer.org/proposal/134032) +- [133939](https://dashboard.internetcomputer.org/proposal/133939) +- [133953](https://dashboard.internetcomputer.org/proposal/133953) +- [133952](https://dashboard.internetcomputer.org/proposal/133952) +- [133951](https://dashboard.internetcomputer.org/proposal/133951) +- [133950](https://dashboard.internetcomputer.org/proposal/133950) +- [133902](https://dashboard.internetcomputer.org/proposal/133902) +- [133901](https://dashboard.internetcomputer.org/proposal/133901) +- [133900](https://dashboard.internetcomputer.org/proposal/133900) +- [133798](https://dashboard.internetcomputer.org/proposal/133798) +- [133799](https://dashboard.internetcomputer.org/proposal/133799) +- [133800](https://dashboard.internetcomputer.org/proposal/133800) +- [133457](https://dashboard.internetcomputer.org/proposal/133457) +- [133450](https://dashboard.internetcomputer.org/proposal/133450) +- [133443](https://dashboard.internetcomputer.org/proposal/133443) +- [133397](https://dashboard.internetcomputer.org/proposal/133397) +- [133396](https://dashboard.internetcomputer.org/proposal/133396) + # 0.24.2 ### feat: all commands will use the DFX_NETWORK from the environment diff --git a/Cargo.lock b/Cargo.lock index 57a1f1e8ef..bf50fb84af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1548,9 +1548,9 @@ dependencies = [ "hex", "humantime", "hyper-rustls 0.24.2", - "ic-agent 0.38.0", + "ic-agent", "ic-asset", - "ic-cdk 0.13.5", + "ic-cdk", "ic-identity-hsm", "ic-utils 0.38.0", "ic-wasm", @@ -1608,7 +1608,7 @@ dependencies = [ [[package]] name = "dfx-core" -version = "0.1.0" +version = "0.1.1" dependencies = [ "aes-gcm", "argon2", @@ -1626,7 +1626,7 @@ dependencies = [ "handlebars", "hex", "humantime-serde", - "ic-agent 0.38.0", + "ic-agent", "ic-identity-hsm", "ic-utils 0.38.0", "itertools 0.10.5", @@ -2512,17 +2512,6 @@ dependencies = [ "http 1.1.0", ] -[[package]] -name = "http-body-to-bytes" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17a08236c6f51c2ee95d840f45acf8fa9e339390e00b4ef640857b2f2a534d70" -dependencies = [ - "bytes", - "http-body 1.0.0", - "http-body-util", -] - [[package]] name = "http-body-util" version = "0.1.1" @@ -2633,7 +2622,7 @@ dependencies = [ "hyper 1.3.1", "hyper-util", "rustls 0.23.7", - "rustls-native-certs", + "rustls-native-certs 0.7.0", "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", @@ -2684,53 +2673,6 @@ dependencies = [ "cc", ] -[[package]] -name = "ic-agent" -version = "0.37.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fd3fdf5e5c4f4a9fe5ca612f0febd22dcb161d2f2b75b0142326732be5e4978" -dependencies = [ - "async-lock 3.3.0", - "backoff", - "cached 0.52.0", - "candid", - "ed25519-consensus", - "futures-util", - "hex", - "http 1.1.0", - "http-body 1.0.0", - "http-body-to-bytes", - "http-body-util", - "hyper 1.3.1", - "hyper-rustls 0.27.2", - "hyper-util", - "ic-certification 2.5.0", - "ic-transport-types 0.37.1", - "ic-verify-bls-signature", - "k256 0.13.3", - "leb128", - "p256", - "pem 3.0.4", - "pkcs8 0.10.2", - "rand", - "rangemap", - "reqwest", - "ring 0.17.8", - "rustls-webpki 0.102.3", - "sec1 0.7.3", - "serde", - "serde_bytes", - "serde_cbor", - "serde_repr", - "sha2 0.10.8", - "simple_asn1", - "thiserror", - "time", - "tokio", - "tower", - "url", -] - [[package]] name = "ic-agent" version = "0.38.0" @@ -2746,7 +2688,7 @@ dependencies = [ "hex", "http 1.1.0", "http-body 1.0.0", - "ic-certification 2.5.0", + "ic-certification 2.6.0", "ic-transport-types 0.38.0", "ic-verify-bls-signature", "k256 0.13.3", @@ -2786,7 +2728,7 @@ dependencies = [ "futures-intrusive", "globset", "hex", - "ic-agent 0.38.0", + "ic-agent", "ic-utils 0.38.0", "itertools 0.10.5", "json5", @@ -2856,7 +2798,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07bc917070b8fc4bd88e3199746372e44d507f54c93a9b191787e1caefca1eba" dependencies = [ "candid", - "ic-certification 2.5.0", + "ic-certification 2.6.0", "leb128", "nom", "thiserror", @@ -2869,21 +2811,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b1da6a25b045f9da3c9459c0cb2b0700ac368ee16382975a17185a23b9c18ab" dependencies = [ "candid", - "ic-cdk-macros 0.13.2", - "ic0 0.21.1", - "serde", - "serde_bytes", -] - -[[package]] -name = "ic-cdk" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8ecacd682fa05a985253592963306cb9799622d7b1cce4b1edb89c6ec85be1" -dependencies = [ - "candid", - "ic-cdk-macros 0.16.0", - "ic0 0.23.0", + "ic-cdk-macros", + "ic0", "serde", "serde_bytes", ] @@ -2898,24 +2827,10 @@ dependencies = [ "proc-macro2", "quote", "serde", - "serde_tokenstream 0.1.7", + "serde_tokenstream", "syn 1.0.109", ] -[[package]] -name = "ic-cdk-macros" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d4d857135deef20cc7ea8f3869a30cd9cfeb1392b3a81043790b2cd82adc3e0" -dependencies = [ - "candid", - "proc-macro2", - "quote", - "serde", - "serde_tokenstream 0.2.2", - "syn 2.0.61", -] - [[package]] name = "ic-certificate-verification" version = "2.4.0" @@ -2925,7 +2840,7 @@ dependencies = [ "cached 0.47.0", "candid", "ic-cbor", - "ic-certification 2.5.0", + "ic-certification 2.6.0", "lazy_static", "leb128", "miracl_core_bls12381", @@ -2948,9 +2863,9 @@ dependencies = [ [[package]] name = "ic-certification" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20052ce9255fbe2de7041a4f6996fddd095ba1f31ae83b6c0ccdee5be6e7bbcf" +checksum = "e64ee3d8b6e81b51f245716d3e0badb63c283c00f3c9fb5d5219afc30b5bf821" dependencies = [ "hex", "serde", @@ -2991,8 +2906,8 @@ dependencies = [ "candid", "candid_parser", "hex", - "ic-cdk 0.13.5", - "ic-certification 2.5.0", + "ic-cdk", + "ic-certification 2.6.0", "ic-certification-testing", "ic-crypto-tree-hash", "ic-http-certification", @@ -3161,7 +3076,7 @@ name = "ic-frontend-canister" version = "0.2.5" dependencies = [ "candid", - "ic-cdk 0.13.5", + "ic-cdk", "ic-certified-assets", ] @@ -3173,7 +3088,7 @@ checksum = "7ddb96501529c2380e087fa9f4552fd0d416f5784bb1e48142d746e9b3d6ae13" dependencies = [ "candid", "http 0.2.12", - "ic-certification 2.5.0", + "ic-certification 2.6.0", "ic-representation-independent-hash", "serde", "thiserror", @@ -3206,7 +3121,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe9975ba49e50ccf10d8268b168b9a35da165904f56725a333e8294d7e5f1d92" dependencies = [ "hex", - "ic-agent 0.38.0", + "ic-agent", "pkcs11", "sha2 0.10.8", "simple_asn1", @@ -3251,7 +3166,7 @@ dependencies = [ "http 0.2.12", "ic-cbor", "ic-certificate-verification", - "ic-certification 2.5.0", + "ic-certification 2.6.0", "ic-http-certification", "ic-representation-independent-hash", "leb128", @@ -3308,7 +3223,7 @@ checksum = "875dc4704780383112e8e8b5063a1b98de114321d0c7d3e7f635dcf360a57fba" dependencies = [ "candid", "hex", - "ic-certification 2.5.0", + "ic-certification 2.6.0", "leb128", "serde", "serde_bytes", @@ -3325,7 +3240,7 @@ checksum = "dc1834f2f91324b7263e0a05b95d385fd109b51efec991f053112d8cd2503d6e" dependencies = [ "candid", "hex", - "ic-certification 2.5.0", + "ic-certification 2.6.0", "leb128", "serde", "serde_bytes", @@ -3396,7 +3311,7 @@ dependencies = [ "async-trait", "candid", "futures-util", - "ic-agent 0.38.0", + "ic-agent", "once_cell", "semver", "serde", @@ -3448,12 +3363,6 @@ version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a54b5297861c651551676e8c43df805dad175cc33bc97dbd992edbbb85dcbcdf" -[[package]] -name = "ic0" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de254dd67bbd58073e23dc1c8553ba12fa1dc610a19de94ad2bbcd0460c067f" - [[package]] name = "ic_bls12_381" version = "0.8.0" @@ -3524,7 +3433,7 @@ dependencies = [ "clap", "delay", "humantime", - "ic-agent 0.38.0", + "ic-agent", "ic-asset", "ic-utils 0.38.0", "libflate 1.4.0", @@ -4675,13 +4584,12 @@ dependencies = [ [[package]] name = "pocket-ic" version = "5.0.0" -source = "git+https://github.com/dfinity/ic?rev=3c76b9142f67da01393d9280c705f88b6e522a93#3c76b9142f67da01393d9280c705f88b6e522a93" +source = "git+https://github.com/dfinity/ic?rev=a62848817cec7ae50618a87a526c85d020283fd9#a62848817cec7ae50618a87a526c85d020283fd9" dependencies = [ "base64 0.13.1", "candid", "hex", - "ic-agent 0.37.1", - "ic-cdk 0.16.0", + "ic-certification 2.6.0", "ic-transport-types 0.37.1", "reqwest", "schemars", @@ -4691,11 +4599,14 @@ dependencies = [ "serde_json", "sha2 0.10.8", "slog", + "strum 0.26.3", + "strum_macros 0.26.4", "thiserror", "tokio", "tracing", "tracing-appender", "tracing-subscriber", + "wslpath", ] [[package]] @@ -5145,9 +5056,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.7" +version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" +checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" dependencies = [ "base64 0.22.1", "bytes", @@ -5171,7 +5082,7 @@ dependencies = [ "pin-project-lite", "quinn", "rustls 0.23.7", - "rustls-native-certs", + "rustls-native-certs 0.8.0", "rustls-pemfile", "rustls-pki-types", "serde", @@ -5386,6 +5297,19 @@ dependencies = [ "security-framework", ] +[[package]] +name = "rustls-native-certs" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcaf18a4f2be7326cd874a5fa579fae794320a0f388d365dca7e480e55f83f8a" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "rustls-pki-types", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pemfile" version = "2.1.2" @@ -5725,18 +5649,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "serde_tokenstream" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64060d864397305347a78851c51588fd283767e7e7589829e8121d65512340f1" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "syn 2.0.61", -] - [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -6050,6 +5962,9 @@ name = "strum" version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros 0.26.4", +] [[package]] name = "strum_macros" @@ -6399,9 +6314,9 @@ dependencies = [ [[package]] name = "tokio-socks" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51165dfa029d2a65969413a6cc96f354b86b464498702f174a4efa13608fd8c0" +checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f" dependencies = [ "either", "futures-util", @@ -7220,6 +7135,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dab7ac864710bdea6594becbea5b5050333cf34fefb0dc319567eb347950d4" +[[package]] +name = "wslpath" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04a2ecdf2cc4d33a6a93d71bcfbc00bb1f635cdb8029a2cc0709204a045ec7a3" + [[package]] name = "wyz" version = "0.5.1" diff --git a/docs/cli-reference/dfx-identity.mdx b/docs/cli-reference/dfx-identity.mdx index cd596c4da5..9eac471822 100644 --- a/docs/cli-reference/dfx-identity.mdx +++ b/docs/cli-reference/dfx-identity.mdx @@ -136,7 +136,7 @@ dfx identity export alice >generated-id.pem ## dfx identity import -Use the `dfx identity import` command to create a user identity by importing the user’s key information or security certificate from a PEM file. +Use the `dfx identity import` command to create a user identity by importing the user’s key information or security certificate from a PEM file or seed phrase file. *Password policy*: If an identity is imported using `--storage-mode password-protected`, the following requirements apply to the password: - The password needs to be longer than 8 characters. diff --git a/docs/design/asset-canister-interface.md b/docs/design/asset-canister-interface.md index a761376823..ea23bbb94c 100644 --- a/docs/design/asset-canister-interface.md +++ b/docs/design/asset-canister-interface.md @@ -163,7 +163,13 @@ The size of any chunk cannot exceed the message ingress limit. ```candid service: (asset_canister_args: variant { - Init: record {}; + Init: record { + set_permissions: opt record { + prepare: vec principal; + commit: vec principal; + manage_permissions: vec principal; + }; + }; Upgrade: record { set_permissions: opt record { prepare: vec principal; @@ -179,7 +185,7 @@ The methods `init` and `post_upgrade` are called automatically by the system aft Both methods take the same argument type by definition. Therefore, to be able to have different arguments for the two cases, an enum is used to make the distinction. If `init` is called with the `Upgrade` variant or if `post_upgrade` is called with the `Init` variant the asset canister traps and thereby reverts the code changes. -In `Upgrade`, the field `set_permissions` can be used to (re)set the list of principals with the listed permissions. +In both variants, the field `set_permissions` can be used to (re)set the list of principals with the listed permissions. If `set_permissions` that is not `null`, then all permissions are set to the newly provided list of principals and the previous lists of principals are discarded. ### Method: `get` diff --git a/e2e/tests-dfx/assetscanister.bash b/e2e/tests-dfx/assetscanister.bash index 5de6817e58..d92f26758b 100644 --- a/e2e/tests-dfx/assetscanister.bash +++ b/e2e/tests-dfx/assetscanister.bash @@ -1988,6 +1988,38 @@ WARN: { assert_command dfx deploy } +@test "set permissions through init argument" { + dfx_start + dfx deploy + + dfx identity new alice --storage-mode plaintext + ALICE="$(dfx --identity alice identity get-principal)" + + dfx canister install e2e_project_frontend --mode reinstall --yes --argument "(opt variant { + Init = record { + set_permissions = opt record { + prepare = vec { + principal \"${ALICE}\"; + }; + commit = vec { + principal \"$(dfx identity get-principal)\"; + principal \"aaaaa-aa\"; + }; + manage_permissions = vec { + principal \"$(dfx identity get-principal)\"; + }; + } + } + })" + assert_command dfx canister call e2e_project_frontend list_permitted '(record { permission = variant { Prepare }; })' + assert_match "${ALICE}" + assert_command dfx canister call e2e_project_frontend list_permitted '(record { permission = variant { Commit }; })' + assert_match "$(dfx identity get-principal)" + assert_match '"aaaaa-aa"' + assert_command dfx canister call e2e_project_frontend list_permitted '(record { permission = variant { ManagePermissions }; })' + assert_match "$(dfx identity get-principal)" +} + @test "set permissions through upgrade argument" { dfx_start dfx deploy diff --git a/e2e/tests-dfx/call.bash b/e2e/tests-dfx/call.bash index 0454cb68ad..8122d77868 100644 --- a/e2e/tests-dfx/call.bash +++ b/e2e/tests-dfx/call.bash @@ -199,6 +199,7 @@ teardown() { dfx canister create --all dfx build dfx canister install hello_backend + [[ "$USE_POCKETIC" ]] && dfx ledger fabricate-cycles --t 9999999 --canister hello_backend assert_command dfx canister call hello_backend recurse 100 } diff --git a/e2e/tests-dfx/cycles-ledger.bash b/e2e/tests-dfx/cycles-ledger.bash index 1b36725427..481923ec32 100644 --- a/e2e/tests-dfx/cycles-ledger.bash +++ b/e2e/tests-dfx/cycles-ledger.bash @@ -117,6 +117,13 @@ current_time_nanoseconds() { assert_eq "2.900 TC (trillion cycles)." } +@test "balance without cycles ledger fails as expected" { + dfx_start + + assert_command_fail dfx cycles balance + assert_contains "Cycles ledger with canister ID 'um5iw-rqaaa-aaaaq-qaaba-cai' is not installed." +} + @test "transfer" { start_and_install_nns diff --git a/e2e/tests-dfx/deps.bash b/e2e/tests-dfx/deps.bash index 21d19fc55d..1cedfd5532 100644 --- a/e2e/tests-dfx/deps.bash +++ b/e2e/tests-dfx/deps.bash @@ -673,3 +673,172 @@ Installing canister: $CANISTER_ID_C (dep_c)" # this command will fail if the pulled.json is not correct assert_command dfx deps init } + +@test "dfx deps can facade pull ICP ledger" { + use_test_specific_cache_root # dfx deps pull will download files to cache + + dfx_new + jq '.canisters.e2e_project_backend.dependencies=["icp_ledger"]' dfx.json | sponge dfx.json + jq '.canisters.icp_ledger.type="pull"' dfx.json | sponge dfx.json + jq '.canisters.icp_ledger.id="ryjl3-tyaaa-aaaaa-aaaba-cai"' dfx.json | sponge dfx.json + + dfx_start + assert_command dfx deps pull --network local + assert_contains "Using facade dependencies for canister ryjl3-tyaaa-aaaaa-aaaba-cai." + + dfx identity new --storage-mode plaintext minter + assert_command_fail dfx deps init icp_ledger + assert_contains "1. Create a 'minter' identity: dfx identity new minter +2. Run the following multi-line command:" + + assert_command dfx deps init ryjl3-tyaaa-aaaaa-aaaba-cai --argument "(variant { + Init = record { + minting_account = \"$(dfx --identity minter ledger account-id)\"; + initial_values = vec {}; + send_whitelist = vec {}; + transfer_fee = opt record { e8s = 10_000 : nat64; }; + token_symbol = opt \"LICP\"; + token_name = opt \"Local ICP\"; + } +})" + + assert_command dfx deps deploy + + # Can mint tokens (transfer from minting_account) + assert_command dfx --identity minter canister call icp_ledger icrc1_transfer "( + record { + to = record { + owner = principal \"$(dfx --identity default identity get-principal)\"; + }; + amount = 1_000_000 : nat; + }, +)" + + assert_command dfx canister call icp_ledger icrc1_balance_of "( + record { + owner = principal \"$(dfx --identity default identity get-principal)\"; + }, +)" + assert_eq "(1_000_000 : nat)" +} + +@test "dfx deps can facade pull ckBTC ledger" { + [[ "$USE_POCKETIC" ]] && skip "skipped for pocketic which doesn't have ckBTC subnet" + + use_test_specific_cache_root # dfx deps pull will download files to cache + + dfx_new + jq '.canisters.e2e_project_backend.dependencies=["ckbtc_ledger"]' dfx.json | sponge dfx.json + jq '.canisters.ckbtc_ledger.type="pull"' dfx.json | sponge dfx.json + jq '.canisters.ckbtc_ledger.id="mxzaz-hqaaa-aaaar-qaada-cai"' dfx.json | sponge dfx.json + + dfx_start + assert_command dfx deps pull --network local + assert_contains "Using facade dependencies for canister mxzaz-hqaaa-aaaar-qaada-cai." + + dfx identity new --storage-mode plaintext minter + assert_command_fail dfx deps init ckbtc_ledger + assert_contains "1. Create a 'minter' identity: dfx identity new minter +2. Run the following multi-line command:" + + assert_command dfx deps init mxzaz-hqaaa-aaaar-qaada-cai --argument "(variant { + Init = record { + minting_account = record { owner = principal \"$(dfx --identity minter identity get-principal)\"; }; + transfer_fee = 10; + token_symbol = \"ckBTC\"; + token_name = \"ckBTC\"; + metadata = vec {}; + initial_balances = vec {}; + max_memo_length = opt 80; + archive_options = record { + num_blocks_to_archive = 1000; + trigger_threshold = 2000; + max_message_size_bytes = null; + cycles_for_archive_creation = opt 100_000_000_000_000; + node_max_memory_size_bytes = opt 3_221_225_472; + controller_id = principal \"2vxsx-fae\" + } + } +})" + + assert_command dfx deps deploy + + # Can mint tokens (transfer from minting_account) + assert_command dfx --identity minter canister call ckbtc_ledger icrc1_transfer "( + record { + to = record { + owner = principal \"$(dfx --identity default identity get-principal)\"; + }; + amount = 1_000_000 : nat; + }, +)" + + assert_command dfx canister call ckbtc_ledger icrc1_balance_of "( + record { + owner = principal \"$(dfx --identity default identity get-principal)\"; + }, +)" + assert_eq "(1_000_000 : nat)" +} + + +@test "dfx deps can facade pull ckETH ledger" { + [[ "$USE_POCKETIC" ]] && skip "skipped for pocketic which doesn't have ckETH subnet" + + use_test_specific_cache_root # dfx deps pull will download files to cache + + dfx_new + jq '.canisters.e2e_project_backend.dependencies=["cketh_ledger"]' dfx.json | sponge dfx.json + jq '.canisters.cketh_ledger.type="pull"' dfx.json | sponge dfx.json + jq '.canisters.cketh_ledger.id="ss2fx-dyaaa-aaaar-qacoq-cai"' dfx.json | sponge dfx.json + + dfx_start + assert_command dfx deps pull --network local + assert_contains "Using facade dependencies for canister ss2fx-dyaaa-aaaar-qacoq-cai." + + dfx identity new --storage-mode plaintext minter + assert_command_fail dfx deps init cketh_ledger + assert_contains "1. Create a 'minter' identity: dfx identity new minter +2. Run the following multi-line command:" + + assert_command dfx deps init ss2fx-dyaaa-aaaar-qacoq-cai --argument "(variant { + Init = record { + minting_account = record { owner = principal \"$(dfx --identity minter identity get-principal)\"; }; + decimals = opt 18; + max_memo_length = opt 80; + transfer_fee = 2_000_000_000_000; + token_symbol = \"ckETH\"; + token_name = \"ckETH\"; + feature_flags = opt record { icrc2 = true }; + metadata = vec {}; + initial_balances = vec {}; + archive_options = record { + num_blocks_to_archive = 1000; + trigger_threshold = 2000; + max_message_size_bytes = null; + cycles_for_archive_creation = opt 100_000_000_000_000; + node_max_memory_size_bytes = opt 3_221_225_472; + controller_id = principal \"2vxsx-fae\" + } + } +})" + + assert_command dfx deps deploy + + # Can mint tokens (transfer from minting_account) + assert_command dfx --identity minter canister call cketh_ledger icrc1_transfer "( + record { + to = record { + owner = principal \"$(dfx --identity default identity get-principal)\"; + }; + amount = 1_000_000 : nat; + }, +)" + + assert_command dfx canister call cketh_ledger icrc1_balance_of "( + record { + owner = principal \"$(dfx --identity default identity get-principal)\"; + }, +)" + assert_eq "(1_000_000 : nat)" +} diff --git a/e2e/tests-dfx/error_context.bash b/e2e/tests-dfx/error_context.bash index 622116ae47..a09aaf365d 100644 --- a/e2e/tests-dfx/error_context.bash +++ b/e2e/tests-dfx/error_context.bash @@ -211,3 +211,13 @@ teardown() { assert_contains "it did not contain a function that dfx was looking for" assert_contains "dfx identity set-wallet --identity " } + +@test "Local replica not running has nice error messages" { + dfx_new + assert_command_fail dfx ping local + assert_contains "You are trying to connect to the local replica but dfx cannot connect to it." + assert_command_fail dfx deploy + assert_contains "You are trying to connect to the local replica but dfx cannot connect to it." + assert_command_fail dfx canister call um5iw-rqaaa-aaaaq-qaaba-cai some_method + assert_contains "You are trying to connect to the local replica but dfx cannot connect to it." +} diff --git a/e2e/tests-dfx/playground.bash b/e2e/tests-dfx/playground.bash index f71d8d4ff2..77cd0b866e 100644 --- a/e2e/tests-dfx/playground.bash +++ b/e2e/tests-dfx/playground.bash @@ -27,6 +27,7 @@ setup_playground() { dfx_start dfx deploy backend dfx ledger fabricate-cycles --t 9999999 --canister backend + PLAYGROUND_CANISTER_ID=$(dfx canister id backend) export PLAYGROUND_CANISTER_ID echo "PLAYGROUND_CANISTER_ID is $PLAYGROUND_CANISTER_ID" @@ -45,6 +46,10 @@ setup_playground() { } @test "canister lifecycle" { + assert_command dfx canister create --all --playground + [[ "$USE_POCKETIC" ]] && dfx ledger fabricate-cycles --t 9999999 --canister hello_backend --playground + [[ "$USE_POCKETIC" ]] && dfx ledger fabricate-cycles --t 9999999 --canister hello_frontend --playground + assert_command dfx deploy --playground assert_command dfx canister --playground call hello_backend greet '("player")' assert_match "Hello, player!" @@ -101,6 +106,10 @@ setup_playground() { # If the hashes didn't match then the playground would attempt to # instrument the asset canister during upload which would run into execution limits. @test "playground-installed asset canister is same wasm as normal asset canister" { + assert_command dfx canister create --all --playground + [[ "$USE_POCKETIC" ]] && dfx ledger fabricate-cycles --t 9999999 --canister hello_backend --playground + [[ "$USE_POCKETIC" ]] && dfx ledger fabricate-cycles --t 9999999 --canister hello_frontend --playground + assert_command dfx deploy --playground PLAYGROUND_HASH=$(dfx canister --playground info hello_frontend | grep hash) echo "PLAYGROUND_HASH: ${PLAYGROUND_HASH}" diff --git a/e2e/tests-dfx/print.bash b/e2e/tests-dfx/print.bash deleted file mode 100644 index 7a1349921a..0000000000 --- a/e2e/tests-dfx/print.bash +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bats - -load ../utils/_ - -setup() { - standard_setup - - dfx_new -} - -teardown() { - dfx_stop - - standard_teardown -} - -@test "print_mo" { - install_asset print - dfx_start 2>stderr.txt - dfx canister create --all - dfx build - dfx canister install e2e_project - dfx canister call e2e_project hello - sleep 2 - run tail -2 stderr.txt - assert_match "Hello, World! from DFINITY" -} diff --git a/e2e/tests-dfx/start.bash b/e2e/tests-dfx/start.bash index 3b6fe8207c..417a84f180 100644 --- a/e2e/tests-dfx/start.bash +++ b/e2e/tests-dfx/start.bash @@ -21,6 +21,14 @@ teardown() { assert_command diff "$NETWORK_ID_PATH" "$NETWORK_ID_BY_SETTINGS_DIGEST_PATH" } +@test "start and stop" { + dfx_start + dfx_stop + + dfx_start + dfx_stop +} + @test "start and stop with different options" { [[ "$USE_POCKETIC" ]] && skip "skipped for pocketic: clean required" dfx_start --artificial-delay 101 @@ -60,7 +68,8 @@ teardown() { } @test "start and stop outside project" { - dfx_start + assert_command dfx_start + assert_contains "Success! The dfx server is running in the background." mkdir subdir cd subdir || exit 1 diff --git a/e2e/tests-dfx/usage.bash b/e2e/tests-dfx/usage.bash index cbfaeb11d1..0ece968beb 100644 --- a/e2e/tests-dfx/usage.bash +++ b/e2e/tests-dfx/usage.bash @@ -60,3 +60,15 @@ teardown() { assert_match "$(dfx identity get-principal --identity alice)" } +@test "print_mo" { + dfx_new + install_asset print + dfx_start 2>stderr.txt + dfx canister create --all + dfx build + dfx canister install e2e_project + dfx canister call e2e_project hello + sleep 2 + run tail -2 stderr.txt + assert_match "Hello, World! from DFINITY" +} diff --git a/nix/sources.json b/nix/sources.json index 5c1a7c1a7a..79f8cf4d8b 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -2,71 +2,71 @@ "canister_sandbox-x86_64-darwin": { "builtin": false, "description": "The canister_sandbox binary. It must be updated together with the replica binary.", - "rev": "3c76b9142f67da01393d9280c705f88b6e522a93", - "sha256": "0y7zs1njdvzs971r0l1wjmbgwa4alc99mlqssd6lg67548g8pbdn", + "rev": "a62848817cec7ae50618a87a526c85d020283fd9", + "sha256": "1kncgldpv8sqkw34c6qyza0s09zz7zk3n0qc76fj3jvmz4is99ar", "type": "file", - "url": "https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-darwin/canister_sandbox.gz", + "url": "https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-darwin/canister_sandbox.gz", "url_template": "https://download.dfinity.systems/ic//binaries/x86_64-darwin/canister_sandbox.gz" }, "canister_sandbox-x86_64-linux": { "builtin": false, "description": "The canister_sandbox binary. It must be updated together with the replica binary.", - "rev": "3c76b9142f67da01393d9280c705f88b6e522a93", - "sha256": "1rb3r9h2pfqsvbdr716s7k9gh3by3c1ap5ab6jgg0qci6fyh5ls0", + "rev": "a62848817cec7ae50618a87a526c85d020283fd9", + "sha256": "1mww6b9y1ynsa04wlys1ckkfvvw6irbk5va12gvjwsmyhw92crmp", "type": "file", - "url": "https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-linux/canister_sandbox.gz", + "url": "https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-linux/canister_sandbox.gz", "url_template": "https://download.dfinity.systems/ic//binaries/x86_64-linux/canister_sandbox.gz" }, "compiler_sandbox-x86_64-darwin": { "builtin": false, "description": "The compiler_sandbox binary. It must be updated together with the replica binary.", - "rev": "3c76b9142f67da01393d9280c705f88b6e522a93", - "sha256": "028vy5pz7m17rx10iqyj0qsnlns5qq6zxmplx0jc0gg9f8hdlmfd", + "rev": "a62848817cec7ae50618a87a526c85d020283fd9", + "sha256": "1z7nnsk6k94ial5pdn0rnvwba38q4d1b2vdhmi94b1qd8cq1slda", "type": "file", - "url": "https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-darwin/compiler_sandbox.gz", + "url": "https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-darwin/compiler_sandbox.gz", "url_template": "https://download.dfinity.systems/ic//binaries/x86_64-darwin/compiler_sandbox.gz" }, "compiler_sandbox-x86_64-linux": { "builtin": false, "description": "The compiler_sandbox binary. It must be updated together with the replica binary.", - "rev": "3c76b9142f67da01393d9280c705f88b6e522a93", - "sha256": "0shc1l0k8b23rabln81vzfj17ah71dqji1dw44hhbm835gfz1sf9", + "rev": "a62848817cec7ae50618a87a526c85d020283fd9", + "sha256": "0rmlybqsqc87nx0xknfxvz5qkz2wn7xx65m4gk1l39cdwx3ab9xq", "type": "file", - "url": "https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-linux/compiler_sandbox.gz", + "url": "https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-linux/compiler_sandbox.gz", "url_template": "https://download.dfinity.systems/ic//binaries/x86_64-linux/compiler_sandbox.gz" }, "ic-admin-x86_64-darwin": { "builtin": false, "description": "The ic-admin binary.", - "rev": "3c76b9142f67da01393d9280c705f88b6e522a93", - "sha256": "1r4rsmvj408l1cyb9ih59xf0dr0z708s9qwnlby0a1lcyfarbd0c", + "rev": "a62848817cec7ae50618a87a526c85d020283fd9", + "sha256": "0rmg487g393lh6dnb4isnamb81r9ifkp96n1jyysbkiw2n179a67", "type": "file", - "url": "https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-darwin/ic-admin.gz", + "url": "https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-darwin/ic-admin.gz", "url_template": "https://download.dfinity.systems/ic//binaries/x86_64-darwin/ic-admin.gz" }, "ic-admin-x86_64-linux": { "builtin": false, "description": "The ic-admin binary.", - "rev": "3c76b9142f67da01393d9280c705f88b6e522a93", - "sha256": "1q96fmqd0l5vqmvp0a9cm2rw3bgjr1xhvmyib00n5gks9ijivrgd", + "rev": "a62848817cec7ae50618a87a526c85d020283fd9", + "sha256": "050sg2swz66ppyvzd3bvx5f7csc4ymjbs42jbqrvskcnhmw4v0a2", "type": "file", - "url": "https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-linux/ic-admin.gz", + "url": "https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-linux/ic-admin.gz", "url_template": "https://download.dfinity.systems/ic//binaries/x86_64-linux/ic-admin.gz" }, "ic-btc-adapter-x86_64-darwin": { "builtin": false, - "rev": "3c76b9142f67da01393d9280c705f88b6e522a93", - "sha256": "11z81llj7dk3p2xq4cdikic3gbpb25f4j1087fhdhwpsbjlrxiba", + "rev": "a62848817cec7ae50618a87a526c85d020283fd9", + "sha256": "17p501lixydsqk3b0x6sddc4nsisnd5zlcaxl9wfni9ji7pns00b", "type": "file", - "url": "https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-darwin/ic-btc-adapter.gz", + "url": "https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-darwin/ic-btc-adapter.gz", "url_template": "https://download.dfinity.systems/ic//binaries/x86_64-darwin/ic-btc-adapter.gz" }, "ic-btc-adapter-x86_64-linux": { "builtin": false, - "rev": "3c76b9142f67da01393d9280c705f88b6e522a93", - "sha256": "1s4m93ngbqpp5ab1qgb2g51hnk94jj7zsi9i7zl1psb9dgnkh5nz", + "rev": "a62848817cec7ae50618a87a526c85d020283fd9", + "sha256": "1fpgvswpj209khpz644c9rvbs2ky5xrw892jpvmxgp9ppyd4gpa0", "type": "file", - "url": "https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-linux/ic-btc-adapter.gz", + "url": "https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-linux/ic-btc-adapter.gz", "url_template": "https://download.dfinity.systems/ic//binaries/x86_64-linux/ic-btc-adapter.gz" }, "ic-btc-canister": { @@ -78,146 +78,146 @@ }, "ic-https-outcalls-adapter-x86_64-darwin": { "builtin": false, - "rev": "3c76b9142f67da01393d9280c705f88b6e522a93", - "sha256": "1vskby9drh0jwyxafbnk11qj3pmf8p24hkcalr5d2m9s5lpigjhi", + "rev": "a62848817cec7ae50618a87a526c85d020283fd9", + "sha256": "1zaqlgm5yxhm2plgf83cwjzfapj73dq3bamrk5l2b15dsjx8zf5l", "type": "file", - "url": "https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-darwin/ic-https-outcalls-adapter.gz", + "url": "https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-darwin/ic-https-outcalls-adapter.gz", "url_template": "https://download.dfinity.systems/ic//binaries/x86_64-darwin/ic-https-outcalls-adapter.gz" }, "ic-https-outcalls-adapter-x86_64-linux": { "builtin": false, - "rev": "3c76b9142f67da01393d9280c705f88b6e522a93", - "sha256": "1jcxr8lzgr77wm1d2ksfhv44nchqmx4dcgh2r0hbn4ck2zls1w6i", + "rev": "a62848817cec7ae50618a87a526c85d020283fd9", + "sha256": "135529wzk5rh3x960vgp493vrcgw3ga5n6rnxa4iw9jiijb9ylvj", "type": "file", - "url": "https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-linux/ic-https-outcalls-adapter.gz", + "url": "https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-linux/ic-https-outcalls-adapter.gz", "url_template": "https://download.dfinity.systems/ic//binaries/x86_64-linux/ic-https-outcalls-adapter.gz" }, "ic-nns-init-x86_64-darwin": { "builtin": false, "description": "The ic-nns-init binary.", - "rev": "3c76b9142f67da01393d9280c705f88b6e522a93", - "sha256": "0pwm70vx0xbnv3kv4x64nszdihrx3kd7kwz9kid82v5xlpmlda0i", + "rev": "a62848817cec7ae50618a87a526c85d020283fd9", + "sha256": "1jc5pa4l1fzmvgyhzw73jpi50dh97girgbmkn656cgaxvbfg9jxw", "type": "file", - "url": "https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-darwin/ic-nns-init.gz", + "url": "https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-darwin/ic-nns-init.gz", "url_template": "https://download.dfinity.systems/ic//binaries/x86_64-darwin/ic-nns-init.gz" }, "ic-nns-init-x86_64-linux": { "builtin": false, "description": "The ic-nns-init binary.", - "rev": "3c76b9142f67da01393d9280c705f88b6e522a93", - "sha256": "00gvrh7iiv8wilhc39sarcaf5f5dsr5rcd5wy4rh8as7k39ykjki", + "rev": "a62848817cec7ae50618a87a526c85d020283fd9", + "sha256": "0ha7wi72nyzp3rcb8af2zmmq78lxglmggi42fqv3nzx74f90mf3i", "type": "file", - "url": "https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-linux/ic-nns-init.gz", + "url": "https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-linux/ic-nns-init.gz", "url_template": "https://download.dfinity.systems/ic//binaries/x86_64-linux/ic-nns-init.gz" }, "ic-starter-x86_64-darwin": { "builtin": false, - "rev": "3c76b9142f67da01393d9280c705f88b6e522a93", - "sha256": "1w1r21g5wbh0klzm59y30rx46z4izxblvp7jckddmqjsp98wwshm", + "rev": "a62848817cec7ae50618a87a526c85d020283fd9", + "sha256": "0k4zhx3vmzyv5isy08q65kaigks9ywj65lw7wzff3gl2wnkxi6gr", "type": "file", - "url": "https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-darwin/ic-starter.gz", + "url": "https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-darwin/ic-starter.gz", "url_template": "https://download.dfinity.systems/ic//binaries/x86_64-darwin/ic-starter.gz" }, "ic-starter-x86_64-linux": { "builtin": false, - "rev": "3c76b9142f67da01393d9280c705f88b6e522a93", - "sha256": "1g0bvajccdaj3sjh9y28b7g6jlh841xs5n0xi76ffhzvxhkfvc1s", + "rev": "a62848817cec7ae50618a87a526c85d020283fd9", + "sha256": "08n4nns6zn4072aww45ll7f96sknxhs7k5hlbciklnr5gnzrfd51", "type": "file", - "url": "https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-linux/ic-starter.gz", + "url": "https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-linux/ic-starter.gz", "url_template": "https://download.dfinity.systems/ic//binaries/x86_64-linux/ic-starter.gz" }, "motoko-base": { "builtin": false, "description": "The Motoko base library", "owner": "dfinity", - "sha256": "0qq8dlzjy4dkp5jn2ly21aw8n024fmkvhisca2qswhsqbg2wwv3x", + "sha256": "0i2jrys9qj6w776f3nrifhvs7s4rrhwlnqmd7dlawd0qk8ns4zk9", "type": "tarball", - "url": "https://github.com/dfinity/motoko/releases/download/0.13.2/motoko-base-library.tar.gz", + "url": "https://github.com/dfinity/motoko/releases/download/0.13.3/motoko-base-library.tar.gz", "url_template": "https://github.com/dfinity/motoko/releases/download//motoko-base-library.tar.gz", - "version": "0.13.2" + "version": "0.13.3" }, "motoko-x86_64-darwin": { "builtin": false, - "sha256": "1pb5jn7nr5s18hqpcvzc4szkdnrk2h417d6dpwnrrx8l3fqvx1m3", + "sha256": "0c4njswid1z3aw298y2vd7sx618ijb8ixgjsh99vlazm9yai5wi1", "type": "file", - "url": "https://github.com/dfinity/motoko/releases/download/0.13.2/motoko-Darwin-x86_64-0.13.2.tar.gz", + "url": "https://github.com/dfinity/motoko/releases/download/0.13.3/motoko-Darwin-x86_64-0.13.3.tar.gz", "url_template": "https://github.com/dfinity/motoko/releases/download//motoko-Darwin-x86_64-.tar.gz", - "version": "0.13.2" + "version": "0.13.3" }, "motoko-x86_64-linux": { "builtin": false, - "sha256": "1i1adh7a5yn10a8k1sjnl8bp8k2sxfmmy5smy2phj28lzwm374j9", + "sha256": "110hx0da5bspv4ida3xwf2mpiwmn6axq8cnnq0vinzfrajb5r2qm", "type": "file", - "url": "https://github.com/dfinity/motoko/releases/download/0.13.2/motoko-Linux-x86_64-0.13.2.tar.gz", + "url": "https://github.com/dfinity/motoko/releases/download/0.13.3/motoko-Linux-x86_64-0.13.3.tar.gz", "url_template": "https://github.com/dfinity/motoko/releases/download//motoko-Linux-x86_64-.tar.gz", - "version": "0.13.2" + "version": "0.13.3" }, "pocket-ic-x86_64-darwin": { - "rev": "3c76b9142f67da01393d9280c705f88b6e522a93", - "sha256": "0ybw08pghwgcw3lqxgd41q0jc4qdyga2grcnfnqbiynrjrbyx8w9", + "rev": "a62848817cec7ae50618a87a526c85d020283fd9", + "sha256": "1xlsd1lkv22raw0z9n02li6ghg6vpb30hygr8s1g0fvchzhsnizx", "type": "file", - "url": "https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-darwin/pocket-ic.gz", + "url": "https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-darwin/pocket-ic.gz", "url_template": "https://download.dfinity.systems/ic//binaries/x86_64-darwin/pocket-ic.gz" }, "pocket-ic-x86_64-linux": { - "rev": "3c76b9142f67da01393d9280c705f88b6e522a93", - "sha256": "0a5s0vvyshg59h139lsxpikrvicml8az1rp5m1cwimh7djjc9acv", + "rev": "a62848817cec7ae50618a87a526c85d020283fd9", + "sha256": "16ch04yghl21pr8lk9mcdrvw1f7l8rbpbslh34bsn96xjshy453v", "type": "file", - "url": "https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-linux/pocket-ic.gz", + "url": "https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-linux/pocket-ic.gz", "url_template": "https://download.dfinity.systems/ic//binaries/x86_64-linux/pocket-ic.gz" }, "replica-x86_64-darwin": { "builtin": false, "description": "The replica binary. It must be updated together with the canister_sandbox binary.", - "rev": "3c76b9142f67da01393d9280c705f88b6e522a93", - "sha256": "1i4ab9yjamip4ys969qy7q32qhh4dn7dzdljwwzbis78vh4crrzr", + "rev": "a62848817cec7ae50618a87a526c85d020283fd9", + "sha256": "0whfgqrjbbp6alwdk8w478ifypbfccg83d6mx5325vzym6hqn35l", "type": "file", - "url": "https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-darwin/replica.gz", + "url": "https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-darwin/replica.gz", "url_template": "https://download.dfinity.systems/ic//binaries/x86_64-darwin/replica.gz" }, "replica-x86_64-linux": { "builtin": false, "description": "The replica binary. It must be updated together with the canister_sandbox binary.", - "rev": "3c76b9142f67da01393d9280c705f88b6e522a93", - "sha256": "0vg22spwl8kz0dmknj7v88hgndv1qlvqby6ayb5hs8446rn73psh", + "rev": "a62848817cec7ae50618a87a526c85d020283fd9", + "sha256": "1m2ghvf8p87mhvns8bphnkhplfsad499s3r11c1q9lbgbnzld07i", "type": "file", - "url": "https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-linux/replica.gz", + "url": "https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-linux/replica.gz", "url_template": "https://download.dfinity.systems/ic//binaries/x86_64-linux/replica.gz" }, "sandbox_launcher-x86_64-darwin": { "builtin": false, "description": "The sandbox_launcher binary. It must be updated together with the replica binary.", - "rev": "3c76b9142f67da01393d9280c705f88b6e522a93", - "sha256": "01bzyx8faysx8mslvkiwcq21f3c5fbnhc73f703s22aav292afxv", + "rev": "a62848817cec7ae50618a87a526c85d020283fd9", + "sha256": "05sb7ipkjy3vlz4dyja3z3wr8mqpg999mlyrbxrki4hili2xjy5s", "type": "file", - "url": "https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-darwin/sandbox_launcher.gz", + "url": "https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-darwin/sandbox_launcher.gz", "url_template": "https://download.dfinity.systems/ic//binaries/x86_64-darwin/sandbox_launcher.gz" }, "sandbox_launcher-x86_64-linux": { "builtin": false, "description": "The sandbox_launcher binary. It must be updated together with the replica binary.", - "rev": "3c76b9142f67da01393d9280c705f88b6e522a93", - "sha256": "128igib3yv995lznapd6x73lsnhnwpi0gf4vixia3413kr22kd1g", + "rev": "a62848817cec7ae50618a87a526c85d020283fd9", + "sha256": "11ljnvnx1c55bkb0g23iyxwhp8w9j8x528gpb4q8sf7ganyq2fb6", "type": "file", - "url": "https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-linux/sandbox_launcher.gz", + "url": "https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-linux/sandbox_launcher.gz", "url_template": "https://download.dfinity.systems/ic//binaries/x86_64-linux/sandbox_launcher.gz" }, "sns-x86_64-darwin": { "builtin": false, "description": "The sns binary.", - "rev": "3c76b9142f67da01393d9280c705f88b6e522a93", - "sha256": "08grpjrxn7zzs3xvvfnds4h1vpwi4k7zlhzvs2jjk4ma064x0rdp", + "rev": "a62848817cec7ae50618a87a526c85d020283fd9", + "sha256": "1nfrwn4gn81bw5ba3dkkfibfpmr0c0hmaf7ksicphjlxvqpi9xjl", "type": "file", - "url": "https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-darwin/sns.gz", + "url": "https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-darwin/sns.gz", "url_template": "https://download.dfinity.systems/ic//binaries/x86_64-darwin/sns.gz" }, "sns-x86_64-linux": { "builtin": false, "description": "The sns binary.", - "rev": "3c76b9142f67da01393d9280c705f88b6e522a93", - "sha256": "11lna989cdz371xy9ipgahdv4dsj4i9l44zagzjyvgq6rylfivi0", + "rev": "a62848817cec7ae50618a87a526c85d020283fd9", + "sha256": "0ra40c1apbykwk1xbs3w3rqzgimz5lnk24wzdf5ajkbla0hqqpxz", "type": "file", - "url": "https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-linux/sns.gz", + "url": "https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-linux/sns.gz", "url_template": "https://download.dfinity.systems/ic//binaries/x86_64-linux/sns.gz" } } diff --git a/public/manifest.json b/public/manifest.json index 48cfb36127..9505c2a475 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -1,6 +1,6 @@ { "tags": { - "latest": "0.24.0" + "latest": "0.24.1" }, "versions": [ "0.5.0", @@ -73,7 +73,7 @@ "0.22.0", "0.23.0", "0.24.0", - "0.24.2-beta.0", + "0.24.1", "0.24.2" ] } diff --git a/scripts/test-uis.py b/scripts/test-uis.py index 41876fdb92..a203a15fc2 100644 --- a/scripts/test-uis.py +++ b/scripts/test-uis.py @@ -32,51 +32,21 @@ ("Error", "/index.js"), ("Invalid asm.js: Unexpected token", "/index.js"), ("Expected to find result for path [object Object], but instead found nothing.", "/index.js"), + ("If you want to use Internet Identity, please provide a URL to your local Internet Identity service using the `ii` query parameter", "/index.js"), ( """ -Error: Server returned an error: - Code: 404 (Not Found) - Body: Custom section name not found. - - at j.readState (http://localhost:4943/index.js:2:11709) - at async http://localhost:4943/index.js:2:97683 - at async Promise.all (index 0) - at async Module.UA (http://localhost:4943/index.js:2:98732) - at async Object.getNames (http://localhost:4943/index.js:2:266156) - at async http://localhost:4943/index.js:2:275479""".strip(), +AgentError: Call failed: + Canister: aaaaa-aa + Method: fetch_canister_logs (query) + "Status": "rejected" + "Code": "CanisterReject" + "Message": "IC0406: Caller 2vxsx-fae is not allowed to query ic00 method fetch_canister_logs" + """.strip(), "/index.js", - ), - ( - """ -Error: Server returned an error: - Code: 404 (Not Found) - Body: Custom section name not found.""".strip(), - "/index.js", - ), + ) ] _CANDID_UI_ERRORS_TO_IGNORE = [ - ("Error", "/index.js"), - ("Failed to load resource: the server responded with a status of 404 (Not Found)", "/read_state"), - ( - "Error: Please provide a URL to your local Internet Identity service using the `ii` query parameter", - "/index.js", - ), - ( - """ -Error: Please provide a URL to your local Internet Identity service using the `ii` query parameter - at http://localhost:4943/index.js:2:300040 - at t.renderAuth (http://localhost:4943/index.js:2:301237) - at async http://localhost:4943/index.js:2:314291""".strip(), - "/index.js", - ), - ( - """ -Error: Please provide a URL to your local Internet Identity service using the `ii` query parameter - at http://127.0.0.1:4943/index.js:2:300040 - at t.renderAuth (http://127.0.0.1:4943/index.js:2:301237) - at async http://127.0.0.1:4943/index.js:2:314291""".strip(), - "/index.js", - ), + ("Error", "/index.js") ] # `page.route` does not support additional function parameters _FRONTEND_URL = None @@ -161,7 +131,7 @@ def _check_console_logs(console_logs): for actual_text, endpoint in ( _CANDID_UI_ERRORS_TO_IGNORE if log.type == "error" else _CANDID_UI_WARNINGS_TO_IGNORE ): - if actual_text == log.text.strip() and endpoint in url: + if log.text.strip().startswith(actual_text) and endpoint in url: logging.warning( f'Found {log.type}, but it was expected (log.type="{actual_text}", endpoint="{endpoint}")' ) diff --git a/scripts/update-replica.sh b/scripts/update-replica.sh index d509843dc5..5660bad82f 100755 --- a/scripts/update-replica.sh +++ b/scripts/update-replica.sh @@ -34,6 +34,10 @@ niv update sandbox_launcher-x86_64-linux -a rev="$SHA" niv update sns-x86_64-darwin -a rev="$SHA" niv update sns-x86_64-linux -a rev="$SHA" +# pocket-ic client needs to be upgraded to the same SHA as the pocket-ic server +perl -i.bak -pe "s/(pocket-ic = {[^}]*rev = \")[a-f0-9]+(\")/\1$SHA\2/" src/dfx/Cargo.toml +cargo update -p pocket-ic # refresh the lock file + echo "Writing asset sources" ./scripts/write-dfx-asset-sources.sh diff --git a/scripts/workflows/e2e-matrix.py b/scripts/workflows/e2e-matrix.py index f65d6a832d..a039189045 100755 --- a/scripts/workflows/e2e-matrix.py +++ b/scripts/workflows/e2e-matrix.py @@ -16,7 +16,7 @@ def test_scripts(prefix): matrix = { "test": test, "backend": ["pocketic", "replica"], - "os": ["macos-12", "ubuntu-20.04"], + "os": ["macos-13-large", "ubuntu-20.04"], "exclude": [ { "backend": "pocketic", @@ -25,10 +25,6 @@ def test_scripts(prefix): { "backend": "pocketic", "test": "dfx/canister_http_adapter" - }, - { - "backend": "pocketic", - "test": "dfx/canister_logs" } ] } diff --git a/scripts/workflows/provision-darwin.sh b/scripts/workflows/provision-darwin.sh index c85ea49273..279c4cc1b1 100755 --- a/scripts/workflows/provision-darwin.sh +++ b/scripts/workflows/provision-darwin.sh @@ -20,7 +20,7 @@ if [ "$E2E_TEST" = "tests-dfx/bitcoin.bash" ]; then brew fetch --retry bitcoin brew install bitcoin fi -if [ "$E2E_TEST" = "tests-dfx/build_rust.bash" ]; then +if [ "$E2E_TEST" = "tests-dfx/build_rust.bash" ] && command -v cargo-audit &>/dev/null; then cargo uninstall cargo-audit fi if [ "$E2E_TEST" = "tests-dfx/certificate.bash" ]; then diff --git a/src/canisters/frontend/ic-certified-assets/assets.did b/src/canisters/frontend/ic-certified-assets/assets.did index ccf075ffc4..f94499065d 100644 --- a/src/canisters/frontend/ic-certified-assets/assets.did +++ b/src/canisters/frontend/ic-certified-assets/assets.did @@ -144,7 +144,9 @@ type AssetCanisterArgs = variant { Upgrade: UpgradeArgs; }; -type InitArgs = record {}; +type InitArgs = record { + set_permissions: opt SetPermissions; +}; type UpgradeArgs = record { set_permissions: opt SetPermissions; diff --git a/src/canisters/frontend/ic-certified-assets/src/lib.rs b/src/canisters/frontend/ic-certified-assets/src/lib.rs index 44847a1e1c..4b96ef1b44 100644 --- a/src/canisters/frontend/ic-certified-assets/src/lib.rs +++ b/src/canisters/frontend/ic-certified-assets/src/lib.rs @@ -423,16 +423,23 @@ fn is_controller() -> Result<(), String> { } pub fn init(args: Option) { - if let Some(upgrade_arg) = args { - let AssetCanisterArgs::Init(InitArgs {}) = upgrade_arg else { - ic_cdk::trap("Cannot initialize the canister with an Upgrade argument. Please provide an Init argument.") - }; - } STATE.with(|s| { let mut s = s.borrow_mut(); s.clear(); s.grant_permission(caller(), &Permission::Commit); }); + + if let Some(upgrade_arg) = args { + let AssetCanisterArgs::Init(init_args) = upgrade_arg else { + ic_cdk::trap("Cannot initialize the canister with an Upgrade argument. Please provide an Init argument.") + }; + STATE.with(|s| { + let mut state = s.borrow_mut(); + if let Some(set_permissions) = init_args.set_permissions { + state.set_permissions(set_permissions); + } + }); + } } pub fn pre_upgrade() -> StableState { diff --git a/src/canisters/frontend/ic-certified-assets/src/types.rs b/src/canisters/frontend/ic-certified-assets/src/types.rs index a164dc12c5..5274b9b06b 100644 --- a/src/canisters/frontend/ic-certified-assets/src/types.rs +++ b/src/canisters/frontend/ic-certified-assets/src/types.rs @@ -207,7 +207,9 @@ pub enum AssetCanisterArgs { } #[derive(Clone, Debug, CandidType, Deserialize)] -pub struct InitArgs {} +pub struct InitArgs { + pub set_permissions: Option, +} #[derive(Clone, Debug, CandidType, Deserialize)] pub struct UpgradeArgs { diff --git a/src/dfx-core/CHANGELOG.md b/src/dfx-core/CHANGELOG.md new file mode 100644 index 0000000000..8caf055ffc --- /dev/null +++ b/src/dfx-core/CHANGELOG.md @@ -0,0 +1,19 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [0.1.1] - 2024-11-08 + +### Added + + - get_version_from_cache_path function to get the version from the cache path + - DfxInterfaceBuilder: with_extension_manager and with_extension_manager_from_cache_path methods + +## [0.1.0] - 2024-09-01 + +Initial Version diff --git a/src/dfx-core/Cargo.toml b/src/dfx-core/Cargo.toml index 5bbfec0a45..577b717fbf 100644 --- a/src/dfx-core/Cargo.toml +++ b/src/dfx-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dfx-core" -version = "0.1.0" +version = "0.1.1" authors.workspace = true edition.workspace = true repository.workspace = true diff --git a/src/dfx-core/src/config/cache.rs b/src/dfx-core/src/config/cache.rs index 11c813f810..13e39a2b61 100644 --- a/src/dfx-core/src/config/cache.rs +++ b/src/dfx-core/src/config/cache.rs @@ -2,13 +2,13 @@ use crate::config::directories::project_dirs; use crate::error::cache::{ DeleteCacheError, EnsureCacheVersionsDirError, GetBinaryCommandPathError, GetCacheRootError, - IsCacheInstalledError, ListCacheVersionsError, + GetVersionFromCachePathError, IsCacheInstalledError, ListCacheVersionsError, }; #[cfg(not(windows))] use crate::foundation::get_user_home; use crate::fs::composite::ensure_dir_exists; use semver::Version; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; pub trait Cache { fn version_str(&self) -> String; @@ -50,6 +50,21 @@ pub fn get_cache_path_for_version(v: &str) -> Result Ok(p) } +pub fn get_version_from_cache_path( + cache_path: &Path, +) -> Result { + let version = cache_path + .file_name() + .ok_or(GetVersionFromCachePathError::NoCachePathFilename( + cache_path.to_path_buf(), + ))? + .to_str() + .ok_or(GetVersionFromCachePathError::CachePathFilenameNotUtf8( + cache_path.to_path_buf(), + ))?; + Ok(Version::parse(version)?) +} + /// Return the binary cache root. It constructs it if not present /// already. pub fn ensure_cache_versions_dir() -> Result { diff --git a/src/dfx-core/src/config/model/replica_config.rs b/src/dfx-core/src/config/model/replica_config.rs index ed4164b56d..510f0420e3 100644 --- a/src/dfx-core/src/config/model/replica_config.rs +++ b/src/dfx-core/src/config/model/replica_config.rs @@ -222,7 +222,8 @@ impl<'a> CachedConfig<'a> { } } pub fn can_share_state(&self, other: &Self) -> bool { - self == other + // effective canister id does not matter for ability to share state + self.replica_rev == other.replica_rev && self.config == other.config } pub fn get_effective_canister_id(&self) -> Option { self.effective_canister_id diff --git a/src/dfx-core/src/error/cache.rs b/src/dfx-core/src/error/cache.rs index f74e684eb0..b431c4eecc 100644 --- a/src/dfx-core/src/error/cache.rs +++ b/src/dfx-core/src/error/cache.rs @@ -5,6 +5,7 @@ use crate::error::fs::{ }; use crate::error::get_current_exe::GetCurrentExeError; use crate::error::get_user_home::GetUserHomeError; +use std::path::PathBuf; use thiserror::Error; #[derive(Error, Debug)] @@ -34,6 +35,18 @@ pub enum EnsureCacheVersionsDirError { GetCacheRoot(#[from] GetCacheRootError), } +#[derive(Error, Debug)] +pub enum GetVersionFromCachePathError { + #[error("no filename in cache path '{0}'")] + NoCachePathFilename(PathBuf), + + #[error("filename in cache path '{0}' is not valid UTF-8")] + CachePathFilenameNotUtf8(PathBuf), + + #[error("cannot parse version from cache path filename")] + ParseVersion(#[from] semver::Error), +} + #[derive(Error, Debug)] pub enum GetCacheRootError { #[error(transparent)] diff --git a/src/dfx-core/src/error/interface.rs b/src/dfx-core/src/error/interface.rs new file mode 100644 index 0000000000..79a7929949 --- /dev/null +++ b/src/dfx-core/src/error/interface.rs @@ -0,0 +1,12 @@ +use crate::error::cache::GetVersionFromCachePathError; +use crate::error::extension::NewExtensionManagerError; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum NewExtensionManagerFromCachePathError { + #[error(transparent)] + GetVersionFromCachePath(#[from] GetVersionFromCachePathError), + + #[error(transparent)] + NewExtensionManager(#[from] NewExtensionManagerError), +} diff --git a/src/dfx-core/src/error/mod.rs b/src/dfx-core/src/error/mod.rs index 5d720b50ce..ff13e4e49e 100644 --- a/src/dfx-core/src/error/mod.rs +++ b/src/dfx-core/src/error/mod.rs @@ -12,6 +12,7 @@ pub mod fs; pub mod get_current_exe; pub mod get_user_home; pub mod identity; +pub mod interface; pub mod keyring; pub mod load_dfx_config; pub mod load_networks_config; diff --git a/src/dfx-core/src/interface/builder.rs b/src/dfx-core/src/interface/builder.rs index 20776e6552..e7390e2c93 100644 --- a/src/dfx-core/src/interface/builder.rs +++ b/src/dfx-core/src/interface/builder.rs @@ -1,12 +1,16 @@ use crate::{ + config::cache::get_version_from_cache_path, config::model::{ dfinity::{Config, NetworksConfig}, network_descriptor::NetworkDescriptor, }, error::{ builder::{BuildAgentError, BuildDfxInterfaceError, BuildIdentityError}, + extension::NewExtensionManagerError, + interface::NewExtensionManagerFromCachePathError, network_config::NetworkConfigError, }, + extension::manager::ExtensionManager, identity::{identity_manager::InitializeIdentity, IdentityManager}, network::{ provider::{create_network_descriptor, LocalBindDetermination}, @@ -16,6 +20,8 @@ use crate::{ }; use ic_agent::{agent::route_provider::RoundRobinRouteProvider, Agent, Identity}; use reqwest::Client; +use semver::Version; +use std::path::Path; use std::sync::Arc; #[derive(PartialEq)] @@ -42,14 +48,17 @@ pub struct DfxInterfaceBuilder { /// There is no need to set this for the local network, where the root key is fetched by default. /// This would typically be set for a testnet, or an alias for the local network. force_fetch_root_key_insecure_non_mainnet_only: bool, + + extension_manager: Option, } impl DfxInterfaceBuilder { - pub(crate) fn new() -> Self { + pub fn new() -> Self { Self { identity: IdentityPicker::Selected, network: NetworkPicker::Local, force_fetch_root_key_insecure_non_mainnet_only: false, + extension_manager: None, } } @@ -77,6 +86,26 @@ impl DfxInterfaceBuilder { self.with_network(NetworkPicker::Named(name.to_string())) } + pub fn with_extension_manager( + self, + version: Version, + ) -> Result { + let extension_manager = Some(ExtensionManager::new(&version)?); + Ok(Self { + extension_manager, + ..self + }) + } + + pub fn with_extension_manager_from_cache_path( + self, + cache_path: &Path, + ) -> Result { + let version = get_version_from_cache_path(cache_path)?; + + Ok(self.with_extension_manager(version)?) + } + pub fn with_force_fetch_root_key_insecure_non_mainnet_only(self) -> Self { Self { force_fetch_root_key_insecure_non_mainnet_only: true, @@ -88,7 +117,7 @@ impl DfxInterfaceBuilder { let fetch_root_key = self.network == NetworkPicker::Local || self.force_fetch_root_key_insecure_non_mainnet_only; let networks_config = NetworksConfig::new()?; - let config = Config::from_current_dir(None)?.map(Arc::new); + let config = Config::from_current_dir(self.extension_manager.as_ref())?.map(Arc::new); let network_descriptor = self.build_network_descriptor(config.clone(), &networks_config)?; let identity = self.build_identity()?; let agent = self.build_agent(identity.clone(), &network_descriptor)?; diff --git a/src/dfx/Cargo.toml b/src/dfx/Cargo.toml index 9cb2e5db88..91ef990def 100644 --- a/src/dfx/Cargo.toml +++ b/src/dfx/Cargo.toml @@ -127,7 +127,7 @@ ci_info = "0.14" junction = "1.0.0" [target.'cfg(unix)'.dependencies] -pocket-ic = { git = "https://github.com/dfinity/ic", rev = "3c76b9142f67da01393d9280c705f88b6e522a93" } +pocket-ic = { git = "https://github.com/dfinity/ic", rev = "a62848817cec7ae50618a87a526c85d020283fd9" } [dev-dependencies] env_logger = "0.10" diff --git a/src/dfx/assets/dfx-asset-sources.toml b/src/dfx/assets/dfx-asset-sources.toml index 7c044d44be..1940059a57 100644 --- a/src/dfx/assets/dfx-asset-sources.toml +++ b/src/dfx/assets/dfx-asset-sources.toml @@ -1,119 +1,119 @@ # generated by write-dfx-asset-sources.sh -replica-rev = '3c76b9142f67da01393d9280c705f88b6e522a93' +replica-rev = 'a62848817cec7ae50618a87a526c85d020283fd9' [x86_64-darwin.ic-admin] -url = 'https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-darwin/ic-admin.gz' -sha256 = '0cb49595f38c0605fca296e3a411381fe4065c4f05c6b43c0b14012277d599e4' +url = 'https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-darwin/ic-admin.gz' +sha256 = 'c7a87482153ccea5bd97c19a74a78b2907b4aab23a92659b8174a4f10e22af66' [x86_64-darwin.ic-btc-adapter] -url = 'https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-darwin/ic-btc-adapter.gz' -sha256 = '6ac59ea95cfa72d8a03b0804495c11ebae37589cb13182bbb863b623290de887' +url = 'https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-darwin/ic-btc-adapter.gz' +sha256 = '0b006def893245eb78a25d31fa4bb33a6a4b586bda74b0c6c4baf91e6900e59e' [x86_64-darwin.ic-https-outcalls-adapter] -url = 'https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-darwin/ic-https-outcalls-adapter.gz' -sha256 = '11ca172f2d3a55d14aa68a4d48c445aede217108d32ea7bae712c0dc925f53ef' +url = 'https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-darwin/ic-https-outcalls-adapter.gz' +sha256 = 'b4b88fbad4ad84256899b9aa35701b475ee5bee46c20f7e81515765feaa358fd' [x86_64-darwin.ic-nns-init] -url = 'https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-darwin/ic-nns-init.gz' -sha256 = '11a846eba5bd6c815a9ce9f379da1c3dc3d8beb6c474b2e7d87675d03738955f' +url = 'https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-darwin/ic-nns-init.gz' +sha256 = 'bccbf4dcda5d3d668ab1b3ae97e33b093650e295e3f00ffddbf5bb4089ba85c9' [x86_64-darwin.ic-starter] -url = 'https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-darwin/ic-starter.gz' -sha256 = '156ace51ba5ae2dada64f2dc4d57ff917c437a06c3a7523f9d002e5e5e1039f0' +url = 'https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-darwin/ic-starter.gz' +sha256 = 'f999d8a7e582bee1dce787d36224f749cf17d52c0623e0752cdbffba47879f4c' [x86_64-darwin.motoko] -url = 'https://github.com/dfinity/motoko/releases/download/0.13.2/motoko-Darwin-x86_64-0.13.2.tar.gz' -sha256 = 'a386beb11b14f59c2dbfcdb413081433db36bf26ec6f76314441976c8f9565dd' +url = 'https://github.com/dfinity/motoko/releases/download/0.13.3/motoko-Darwin-x86_64-0.13.3.tar.gz' +sha256 = '21f212954ff52bba53825abe1ed1921105d3f5695b78940457e38716b9969630' # The replica, canister_sandbox and compiler_sandbox binaries must have the same revision. [x86_64-darwin.replica] -url = 'https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-darwin/replica.gz' -sha256 = 'f9e7cc08dce8e8b83ee792b6df8e6d04422c063e1e2793b4273756257d5a8ac4' +url = 'https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-darwin/replica.gz' +sha256 = 'b40c8ba1a9feef2246e9d5b4811e636e5def223a84a3d93855e6ae25337e0e72' # The replica, canister_sandbox and compiler_sandbox binaries must have the same revision. [x86_64-darwin.canister_sandbox] -url = 'https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-darwin/canister_sandbox.gz' -sha256 = 'b6ad8b1e22e598474dd31ad39a12a38a28fe56953c5090c349faef266dd0ff78' +url = 'https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-darwin/canister_sandbox.gz' +sha256 = '59a5a423f975cb219d390c033be63fff27a081fa1e1b46069f58a37d1b7dccce' # The replica, canister_sandbox and compiler_sandbox binaries must have the same revision. [x86_64-darwin.compiler_sandbox] -url = 'https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-darwin/compiler_sandbox.gz' -sha256 = 'cd55da2072e93dc024e8f4d6fe0dc6455b6a3506d2e30842cf27d4f36ff11b09' +url = 'https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-darwin/compiler_sandbox.gz' +sha256 = 'aa511d30430d874552acb06db14223180db5f8b619d8760b5591a469a6b6f6fc' [x86_64-darwin.sandbox_launcher] -url = 'https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-darwin/sandbox_launcher.gz' -sha256 = 'bb3b2592d84a09a107386e1c06ed72850d1704663cce4d75455d7be550f77f05' +url = 'https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-darwin/sandbox_launcher.gz' +sha256 = 'ba78d945a4119238735fd9d39a527a175794f9f84349dfc8a77b78396f3c4b17' [x86_64-darwin.sns] -url = 'https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-darwin/sns.gz' -sha256 = 'b765d08901aa9229a5d0fb43facf2491df1d20d1cdbabdfbd0ff1fdbb3bcf921' +url = 'https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-darwin/sns.gz' +sha256 = '54f6142fde9d4a7859d4f33855216020d7eb567473b6a156e12b20fb88e5d9d9' [x86_64-darwin.pocket-ic] -url = 'https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-darwin/pocket-ic.gz' -sha256 = '89a3ee5796d9fab8b07596e527d4f30d1326010ea4bd8ee9e0ec71f82e027c79' +url = 'https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-darwin/pocket-ic.gz' +sha256 = 'fd47abe1876c3bf08246f97908c6badb3cf84ca402d8f4015759883d69689af6' [x86_64-darwin.motoko-base] -url = 'https://github.com/dfinity/motoko/releases/download/0.13.2/motoko-base-library.tar.gz' -sha256 = 'd456d015745af15ce8ee731ce184163b8f2385a1ba45741393da667f08a376db' +url = 'https://github.com/dfinity/motoko/releases/download/0.13.3/motoko-base-library.tar.gz' +sha256 = 'e48cb7ea6685f5afcfc189f7f3292ca015dda5e1b4c19fb30c7f1211f8733adc' [x86_64-darwin.ic-btc-canister] url = 'https://github.com/dfinity/bitcoin-canister/releases/download/release%2F2023-10-13/ic-btc-canister.wasm.gz' sha256 = '09f5647a45ff6d5d05b2b0ed48613fb2365b5fe6573ba0e901509c39fb9564ac' [x86_64-linux.ic-admin] -url = 'https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-linux/ic-admin.gz' -sha256 = 'ede51d654c7abe620158d1d70d7bc8f2adc1b3a82c297077c5bb50d0707526e1' +url = 'https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-linux/ic-admin.gz' +sha256 = '42814d7885964dbd335e5210bd64f58469765ce97b8df6b7bfd798cfb5781a14' [x86_64-linux.ic-btc-adapter] -url = 'https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-linux/ic-btc-adapter.gz' -sha256 = 'df1638ed6b69e91be83f3145fd8f94244d0b4379623d1c962af7e2f5ec4895e8' +url = 'https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-linux/ic-btc-adapter.gz' +sha256 = '40dd479abf37ddd7ebbe5224c4732f7e0abd764e8c10f32f9c090879b9deefba' [x86_64-linux.ic-https-outcalls-adapter] -url = 'https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-linux/ic-https-outcalls-adapter.gz' -sha256 = 'd1f0a0e9179311bb20c8023ed648af18324bc8864e4fd142e5e7e4f729ca9dc9' +url = 'https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-linux/ic-https-outcalls-adapter.gz' +sha256 = '72539f968c51261e89ea361b5bd41bfcb1bc4722f76d60521f3097f97912a58c' [x86_64-linux.ic-nns-init] -url = 'https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-linux/ic-nns-init.gz' -sha256 = '71cae9d398472b0433f1bc34964bd6adb8e214cb4aa7c1208d1ced180fccfb01' +url = 'https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-linux/ic-nns-init.gz' +sha256 = '71b80a9223a77f3b367682c4f72a7d9da2836bfdc229b4581ef77b2b4ee44741' [x86_64-linux.ic-starter] -url = 'https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-linux/ic-starter.gz' -sha256 = '3ab0ed26ecfb43e7cc891dd8a27b20085269de5948f804a51e5235c6a4da0bbc' +url = 'https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-linux/ic-starter.gz' +sha256 = 'a13497bf7d255b3a235b14967934ec766a93dca1b410ce953880d86fb4b5c422' [x86_64-linux.motoko] -url = 'https://github.com/dfinity/motoko/releases/download/0.13.2/motoko-Linux-x86_64-0.13.2.tar.gz' -sha256 = '4992332aff140909aff055175fabeb5a4c7417a256ea309102c1faa20e6c2ac4' +url = 'https://github.com/dfinity/motoko/releases/download/0.13.3/motoko-Linux-x86_64-0.13.3.tar.gz' +sha256 = '158b5c9654d97d1b37c0d63284bb32b6f278ab70bc0fd522d957afa21ae81084' # The replica, canister_sandbox and compiler_sandbox binaries must have the same revision. [x86_64-linux.replica] -url = 'https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-linux/replica.gz' -sha256 = '50df716c3684200dcbf2caf88537c56137fb2042fb483b6b037f22caaf16e26d' +url = 'https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-linux/replica.gz' +sha256 = 'f18046bf5d6fd184030b210f9d12694a3b7ae1b4f02ea4ed86f5a08bdc864fd4' # The replica, canister_sandbox and compiler_sandbox binaries must have the same revision. [x86_64-linux.canister_sandbox] -url = 'https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-linux/canister_sandbox.gz' -sha256 = '40d302bd339161f09e344b95ab021b7e0df8d23cda8493dbda1abb2b60ca63e5' +url = 'https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-linux/canister_sandbox.gz' +sha256 = 'b766261287be6a2ef71341ed32578e86efede664417bca0950dafae0d3329cd7' # The replica, canister_sandbox and compiler_sandbox binaries must have the same revision. [x86_64-linux.compiler_sandbox] -url = 'https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-linux/compiler_sandbox.gz' -sha256 = 'c9e9f0dd2b03d5052121bc8528710b07aa13a4fb3b204b97ca432c34010d0c6a' +url = 'https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-linux/compiler_sandbox.gz' +sha256 = 'b8a7a546e78da541c37ca416d3fbb15cfc89cbdfddd9d941b70731acf1f2b466' [x86_64-linux.sandbox_launcher] -url = 'https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-linux/sandbox_launcher.gz' -sha256 = '2fb429449e2390a1628f9bb807e2e5165a4dc7e9a65d653f2d296d3f567c1189' +url = 'https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-linux/sandbox_launcher.gz' +sha256 = '663981bd55ef388d3059f721513a9289a30b79f7718807d65ca5b0d0edb69286' [x86_64-linux.sns] -url = 'https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-linux/sns.gz' -sha256 = '20eee8a8cf06bfede57fea134253245237b21b54efc6e47b38e3379650529686' +url = 'https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-linux/sns.gz' +sha256 = 'bf5f8c2150744da98a6b9f13312d2dbfc6f7711e7ce8d5c3e4d3afab02034465' [x86_64-linux.pocket-ic] -url = 'https://download.dfinity.systems/ic/3c76b9142f67da01393d9280c705f88b6e522a93/binaries/x86_64-linux/pocket-ic.gz' -sha256 = '9ba9c4a46c07d6c859a8e5e6f015a295c59d67bc5dd334024ce541edf706ba28' +url = 'https://download.dfinity.systems/ic/a62848817cec7ae50618a87a526c85d020283fd9/binaries/x86_64-linux/pocket-ic.gz' +sha256 = '7b14e2a196dd24ab171990ea755746f4b8c0776eaca64951be4150f83c019099' [x86_64-linux.motoko-base] -url = 'https://github.com/dfinity/motoko/releases/download/0.13.2/motoko-base-library.tar.gz' -sha256 = 'd456d015745af15ce8ee731ce184163b8f2385a1ba45741393da667f08a376db' +url = 'https://github.com/dfinity/motoko/releases/download/0.13.3/motoko-base-library.tar.gz' +sha256 = 'e48cb7ea6685f5afcfc189f7f3292ca015dda5e1b4c19fb30c7f1211f8733adc' [x86_64-linux.ic-btc-canister] url = 'https://github.com/dfinity/bitcoin-canister/releases/download/release%2F2023-10-13/ic-btc-canister.wasm.gz' diff --git a/src/dfx/assets/project_templates/react/src/__frontend_name__/package.json b/src/dfx/assets/project_templates/react/src/__frontend_name__/package.json index 04d9cd02c5..6c1c237ecd 100644 --- a/src/dfx/assets/project_templates/react/src/__frontend_name__/package.json +++ b/src/dfx/assets/project_templates/react/src/__frontend_name__/package.json @@ -13,9 +13,9 @@ "dependencies": { "react": "^18.2.0", "react-dom": "^18.2.0", - "@dfinity/agent": "^1.4.0", - "@dfinity/candid": "^1.4.0", - "@dfinity/principal": "^1.4.0" + "@dfinity/agent": "^2.1.3", + "@dfinity/candid": "^2.1.3", + "@dfinity/principal": "^2.1.3" }, "devDependencies": { "@types/react": "^18.2.14", diff --git a/src/dfx/assets/project_templates/react/src/__frontend_name__/vite.config.js b/src/dfx/assets/project_templates/react/src/__frontend_name__/vite.config.js index 670b0347b6..9755aa9919 100644 --- a/src/dfx/assets/project_templates/react/src/__frontend_name__/vite.config.js +++ b/src/dfx/assets/project_templates/react/src/__frontend_name__/vite.config.js @@ -39,5 +39,6 @@ export default defineConfig({ ), }, ], + dedupe: ['@dfinity/agent'], }, }); diff --git a/src/dfx/assets/project_templates/svelte/src/__frontend_name__/package.json b/src/dfx/assets/project_templates/svelte/src/__frontend_name__/package.json index 69b8edd5af..85cf87751c 100644 --- a/src/dfx/assets/project_templates/svelte/src/__frontend_name__/package.json +++ b/src/dfx/assets/project_templates/svelte/src/__frontend_name__/package.json @@ -11,9 +11,9 @@ "format": "prettier --write \"src/**/*.{json,js,jsx,ts,tsx,css,scss}\"" }, "dependencies": { - "@dfinity/agent": "^1.4.0", - "@dfinity/candid": "^1.4.0", - "@dfinity/principal": "^1.4.0" + "@dfinity/agent": "^2.1.3", + "@dfinity/candid": "^2.1.3", + "@dfinity/principal": "^2.1.3" }, "devDependencies": { "@sveltejs/adapter-static": "^3.0.4", diff --git a/src/dfx/assets/project_templates/svelte/src/__frontend_name__/vite.config.js b/src/dfx/assets/project_templates/svelte/src/__frontend_name__/vite.config.js index 51010df640..70d491d6cd 100644 --- a/src/dfx/assets/project_templates/svelte/src/__frontend_name__/vite.config.js +++ b/src/dfx/assets/project_templates/svelte/src/__frontend_name__/vite.config.js @@ -39,5 +39,6 @@ export default defineConfig({ ), }, ], + dedupe: ['@dfinity/agent'], }, }); diff --git a/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/package.json b/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/package.json index e96aeb2059..36d9b2fef0 100644 --- a/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/package.json +++ b/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/package.json @@ -21,9 +21,9 @@ "vitest": "^2.0.5" }, "dependencies": { - "@dfinity/agent": "^1.4.0", - "@dfinity/candid": "^1.4.0", - "@dfinity/principal": "^1.4.0", + "@dfinity/agent": "^2.1.3", + "@dfinity/candid": "^2.1.3", + "@dfinity/principal": "^2.1.3", "lit-html": "^2.8.0" } } diff --git a/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/vite.config.js b/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/vite.config.js index 18ceb500c7..9d6f44396e 100644 --- a/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/vite.config.js +++ b/src/dfx/assets/project_templates/vanilla_js/src/__frontend_name__/vite.config.js @@ -38,5 +38,6 @@ export default defineConfig({ ), }, ], + dedupe: ['@dfinity/agent'], }, }); diff --git a/src/dfx/assets/project_templates/vue/src/__frontend_name__/package.json b/src/dfx/assets/project_templates/vue/src/__frontend_name__/package.json index daa41481a1..8367abd0d5 100644 --- a/src/dfx/assets/project_templates/vue/src/__frontend_name__/package.json +++ b/src/dfx/assets/project_templates/vue/src/__frontend_name__/package.json @@ -23,8 +23,8 @@ "dependencies": { "pinia": "^2.1.6", "vue": "^3.3.4", - "@dfinity/agent": "^1.4.0", - "@dfinity/candid": "^1.4.0", - "@dfinity/principal": "^1.4.0" + "@dfinity/agent": "^2.1.3", + "@dfinity/candid": "^2.1.3", + "@dfinity/principal": "^2.1.3" } } \ No newline at end of file diff --git a/src/dfx/assets/project_templates/vue/src/__frontend_name__/vite.config.js b/src/dfx/assets/project_templates/vue/src/__frontend_name__/vite.config.js index 014c37999a..92afec3511 100644 --- a/src/dfx/assets/project_templates/vue/src/__frontend_name__/vite.config.js +++ b/src/dfx/assets/project_templates/vue/src/__frontend_name__/vite.config.js @@ -34,6 +34,7 @@ export default defineConfig({ alias: [ { find: 'declarations', replacement: fileURLToPath(new URL('../declarations', import.meta.url)) }, { find: '@', replacement: fileURLToPath(new URL('./src', import.meta.url)) }, - ] + ], + dedupe: ['@dfinity/agent'], } }); diff --git a/src/dfx/src/actors/mod.rs b/src/dfx/src/actors/mod.rs index 721f35a73f..4cc8b1c839 100644 --- a/src/dfx/src/actors/mod.rs +++ b/src/dfx/src/actors/mod.rs @@ -13,6 +13,7 @@ use dfx_core::config::model::replica_config::ReplicaConfig; use fn_error_context::context; use pocketic_proxy::signals::PortReadySubscribe; use pocketic_proxy::{PocketIcProxy, PocketIcProxyConfig}; +use post_start::PostStart; use std::fs; use std::path::PathBuf; @@ -22,6 +23,7 @@ pub mod btc_adapter; pub mod canister_http_adapter; pub mod pocketic; pub mod pocketic_proxy; +pub mod post_start; pub mod replica; mod shutdown; pub mod shutdown_controller; @@ -214,3 +216,17 @@ pub fn start_pocketic_actor( }; Ok(pocketic::PocketIc::new(actor_config).start()) } + +#[context("Failed to start PostStart actor.")] +pub fn start_post_start_actor( + env: &dyn Environment, + background: bool, + pocketic_proxy: Option>, +) -> DfxResult> { + let config = post_start::Config { + logger: env.get_logger().clone(), + background, + pocketic_proxy, + }; + Ok(PostStart::new(config).start()) +} diff --git a/src/dfx/src/actors/pocketic_proxy.rs b/src/dfx/src/actors/pocketic_proxy.rs index ba86f0d580..b41a04454f 100644 --- a/src/dfx/src/actors/pocketic_proxy.rs +++ b/src/dfx/src/actors/pocketic_proxy.rs @@ -1,4 +1,5 @@ use crate::actors::pocketic_proxy::signals::{PortReadySignal, PortReadySubscribe}; +use crate::actors::post_start::signals::{PocketIcProxyReadySignal, PocketIcProxyReadySubscribe}; use crate::actors::shutdown::{wait_for_child_or_receiver, ChildOrReceiver}; use crate::actors::shutdown_controller::signals::outbound::Shutdown; use crate::actors::shutdown_controller::signals::ShutdownSubscribe; @@ -70,6 +71,9 @@ pub struct PocketIcProxy { stop_sender: Option>, thread_join: Option>, + + /// Ready Signal subscribers. + ready_subscribers: Vec>, } impl PocketIcProxy { @@ -81,10 +85,11 @@ impl PocketIcProxy { stop_sender: None, thread_join: None, logger, + ready_subscribers: Vec::new(), } } - fn start_pocketic_proxy(&mut self, replica_url: Url) -> DfxResult { + fn start_pocketic_proxy(&mut self, replica_url: Url, addr: Addr) -> DfxResult { let logger = self.logger.clone(); let config = &self.config.pocketic_proxy_config; let pocketic_proxy_path = self.config.pocketic_proxy_path.clone(); @@ -100,6 +105,7 @@ impl PocketIcProxy { pocketic_proxy_path, pocketic_proxy_pid_path, pocketic_proxy_port_path, + addr, receiver, config.verbose, config.domains.clone(), @@ -164,7 +170,7 @@ impl Actor for PocketIcProxy { .do_send(ShutdownSubscribe(ctx.address().recipient::())); if let Some(replica_url) = &self.config.pocketic_proxy_config.replica_url { - self.start_pocketic_proxy(replica_url.clone()) + self.start_pocketic_proxy(replica_url.clone(), ctx.address()) .expect("Could not start PocketIC HTTP gateway"); } } @@ -179,7 +185,7 @@ impl Actor for PocketIcProxy { impl Handler for PocketIcProxy { type Result = (); - fn handle(&mut self, msg: PortReadySignal, _ctx: &mut Self::Context) { + fn handle(&mut self, msg: PortReadySignal, ctx: &mut Self::Context) { debug!( self.logger, "replica ready on {}, so re/starting HTTP gateway", msg.url @@ -189,11 +195,29 @@ impl Handler for PocketIcProxy { let replica_url = Url::parse(&msg.url).unwrap(); - self.start_pocketic_proxy(replica_url) + self.start_pocketic_proxy(replica_url, ctx.address()) .expect("Could not start PocketIC HTTP gateway"); } } +impl Handler for PocketIcProxy { + type Result = (); + + fn handle(&mut self, msg: PocketIcProxyReadySubscribe, _ctx: &mut Self::Context) { + self.ready_subscribers.push(msg.0); + } +} + +impl Handler for PocketIcProxy { + type Result = (); + + fn handle(&mut self, _msg: PocketIcProxyReadySignal, _ctx: &mut Self::Context) { + for sub in &self.ready_subscribers { + sub.do_send(PocketIcProxyReadySignal); + } + } +} + impl Handler for PocketIcProxy { type Result = ResponseActFuture>; @@ -217,6 +241,7 @@ fn pocketic_proxy_start_thread( pocketic_proxy_path: PathBuf, pocketic_proxy_pid_path: PathBuf, pocketic_proxy_port_path: PathBuf, + addr: Addr, receiver: Receiver<()>, verbose: bool, domains: Option>, @@ -235,6 +260,7 @@ fn pocketic_proxy_start_thread( cmd.args(["--port-file".as_ref(), pocketic_proxy_port_path.as_os_str()]); cmd.stdout(std::process::Stdio::inherit()); cmd.stderr(std::process::Stdio::inherit()); + let _ = std::fs::remove_file(&pocketic_proxy_port_path); let last_start = std::time::Instant::now(); debug!(logger, "Starting pocket-ic gateway..."); let mut child = cmd.spawn().expect("Could not start pocket-ic gateway."); @@ -277,6 +303,9 @@ fn pocketic_proxy_start_thread( } info!(logger, "Replica API running on {address}"); + // Send PocketIcProxyReadySignal to PocketIcProxy. + addr.do_send(PocketIcProxyReadySignal); + // This waits for the child to stop, or the receiver to receive a message. // We don't restart pocket-ic if done = true. match wait_for_child_or_receiver(&mut child, &receiver) { diff --git a/src/dfx/src/actors/post_start.rs b/src/dfx/src/actors/post_start.rs new file mode 100644 index 0000000000..35222e6e57 --- /dev/null +++ b/src/dfx/src/actors/post_start.rs @@ -0,0 +1,62 @@ +use crate::actors::pocketic_proxy::PocketIcProxy; +use crate::actors::post_start::signals::{PocketIcProxyReadySignal, PocketIcProxyReadySubscribe}; +use actix::{Actor, Addr, AsyncContext, Context, Handler}; +use slog::{info, Logger}; + +pub mod signals { + use actix::prelude::*; + + #[derive(Message)] + #[rtype(result = "()")] + pub struct PocketIcProxyReadySignal; + + #[derive(Message)] + #[rtype(result = "()")] + pub struct PocketIcProxyReadySubscribe(pub Recipient); +} + +pub struct Config { + pub logger: Logger, + pub background: bool, + pub pocketic_proxy: Option>, +} + +pub struct PostStart { + config: Config, +} + +impl PostStart { + pub fn new(config: Config) -> Self { + Self { config } + } +} + +impl Actor for PostStart { + type Context = Context; + + fn started(&mut self, ctx: &mut Self::Context) { + // Register the PostStart recipent to PocketIcProxy. + if let Some(pocketic_proxy) = &self.config.pocketic_proxy { + pocketic_proxy.do_send(PocketIcProxyReadySubscribe(ctx.address().recipient())); + } + } +} + +impl Handler for PostStart { + type Result = (); + + fn handle(&mut self, _msg: PocketIcProxyReadySignal, _ctx: &mut Self::Context) -> Self::Result { + let logger = &self.config.logger; + if self.config.background { + info!( + logger, + "Success! The dfx server is running in the background." + ) + } else { + info!( + logger, + "Success! The dfx server is running.\nYou must open a new terminal to continue developing. If you'd prefer to stop, quit with 'Ctrl-C'." + ) + } + } +} diff --git a/src/dfx/src/commands/deps/pull.rs b/src/dfx/src/commands/deps/pull.rs index 3da9b7d9d8..cf42888104 100644 --- a/src/dfx/src/commands/deps/pull.rs +++ b/src/dfx/src/commands/deps/pull.rs @@ -1,31 +1,15 @@ use crate::lib::agent::create_anonymous_agent_environment; -use crate::lib::deps::{ - get_candid_path_in_project, get_pull_canisters_in_config, get_pulled_canister_dir, - get_pulled_service_candid_path, get_pulled_wasm_path, save_pulled_json, +use crate::lib::deps::pull::{ + copy_service_candid_to_project, download_all_and_generate_pulled_json, resolve_all_dependencies, }; -use crate::lib::deps::{PulledCanister, PulledJson}; +use crate::lib::deps::{get_pull_canisters_in_config, save_pulled_json}; use crate::lib::environment::Environment; use crate::lib::error::DfxResult; -use crate::lib::metadata::dfx::DfxMetadata; -use crate::lib::metadata::names::{CANDID_ARGS, CANDID_SERVICE, DFX}; use crate::lib::network::network_opt::NetworkOpt; use crate::lib::root_key::fetch_root_key_if_needed; -use crate::lib::state_tree::canister_info::read_state_tree_canister_module_hash; -use crate::lib::wasm::file::{decompress_bytes, read_wasm_module}; -use crate::util::download_file; -use anyhow::{anyhow, bail, Context}; -use candid::Principal; +use anyhow::anyhow; use clap::Parser; -use dfx_core::config::model::dfinity::Pullable; -use dfx_core::fs::composite::{ensure_dir_exists, ensure_parent_dir_exists}; -use fn_error_context::context; -use ic_agent::{Agent, AgentError}; -use ic_wasm::metadata::get_metadata; -use sha2::{Digest, Sha256}; -use slog::{error, info, trace, warn, Logger}; -use std::collections::{BTreeMap, BTreeSet, VecDeque}; -use std::io::Write; -use std::path::Path; +use slog::info; /// Pull canisters upon which the project depends. /// This command connects to the "ic" mainnet by default. @@ -74,289 +58,3 @@ pub async fn exec(env: &dyn Environment, opts: DepsPullOpts) -> DfxResult { save_pulled_json(&project_root, &pulled_json)?; Ok(()) } - -async fn resolve_all_dependencies( - agent: &Agent, - logger: &Logger, - pull_canisters_in_config: &BTreeMap, -) -> DfxResult> { - let mut canisters_to_resolve: VecDeque = - pull_canisters_in_config.values().cloned().collect(); - let mut checked = BTreeSet::new(); - while let Some(canister_id) = canisters_to_resolve.pop_front() { - if !checked.contains(&canister_id) { - checked.insert(canister_id); - let dependencies = get_dependencies(agent, logger, &canister_id).await?; - canisters_to_resolve.extend(dependencies.iter()); - } - } - let all_dependencies = checked.into_iter().collect::>(); - let mut message = String::new(); - message.push_str(&format!("Found {} dependencies:", all_dependencies.len())); - for id in &all_dependencies { - message.push('\n'); - message.push_str(&id.to_text()); - } - info!(logger, "{}", message); - Ok(all_dependencies) -} - -#[context("Failed to get dependencies of canister {canister_id}.")] -async fn get_dependencies( - agent: &Agent, - logger: &Logger, - canister_id: &Principal, -) -> DfxResult> { - info!(logger, "Fetching dependencies of canister {canister_id}..."); - let dfx_metadata = fetch_dfx_metadata(agent, canister_id).await?; - let dependencies = dfx_metadata.get_pullable()?.dependencies.clone(); - Ok(dependencies) -} - -async fn download_all_and_generate_pulled_json( - agent: &Agent, - logger: &Logger, - all_dependencies: &[Principal], -) -> DfxResult { - let mut any_download_fail = false; - let mut pulled_json = PulledJson::default(); - for canister_id in all_dependencies { - match download_and_generate_pulled_canister(agent, logger, *canister_id).await { - Ok(pulled_canister) => { - pulled_json.canisters.insert(*canister_id, pulled_canister); - } - Err(e) => { - error!(logger, "Failed to pull canister {canister_id}.\n{e}"); - any_download_fail = true; - } - } - } - - if any_download_fail { - bail!("Failed when pulling canisters."); - } - Ok(pulled_json) -} - -// Download canister wasm, then extract metadata from it to build a PulledCanister -async fn download_and_generate_pulled_canister( - agent: &Agent, - logger: &Logger, - canister_id: Principal, -) -> DfxResult { - info!(logger, "Pulling canister {canister_id}..."); - - let mut pulled_canister = PulledCanister::default(); - - let dfx_metadata = fetch_dfx_metadata(agent, &canister_id).await?; - let pullable = dfx_metadata.get_pullable()?; - - let hash_on_chain = get_hash_on_chain(agent, logger, canister_id, pullable).await?; - pulled_canister.wasm_hash = hex::encode(&hash_on_chain); - - // skip download if cache hit - let mut cache_hit = false; - - for gzip in [false, true] { - let path = get_pulled_wasm_path(&canister_id, gzip)?; - if path.exists() { - let bytes = dfx_core::fs::read(&path)?; - let hash_cache = Sha256::digest(bytes); - if hash_cache.as_slice() == hash_on_chain { - cache_hit = true; - pulled_canister.gzip = gzip; - pulled_canister.wasm_hash_download = hex::encode(hash_cache); - trace!(logger, "The canister wasm was found in the cache."); - } - break; - } - } - - if !cache_hit { - // delete files from previous pull - let pulled_canister_dir = get_pulled_canister_dir(&canister_id)?; - if pulled_canister_dir.exists() { - dfx_core::fs::remove_dir_all(&pulled_canister_dir)?; - } - dfx_core::fs::create_dir_all(&pulled_canister_dir)?; - - // lookup `wasm_url` in dfx metadata - let wasm_url = reqwest::Url::parse(&pullable.wasm_url)?; - - // download - let content = download_file(&wasm_url).await?; - - // hash check - let hash_download = Sha256::digest(&content); - pulled_canister.wasm_hash_download = hex::encode(hash_download); - - let gzip = decompress_bytes(&content).is_ok(); - pulled_canister.gzip = gzip; - let wasm_path = get_pulled_wasm_path(&canister_id, gzip)?; - - write_to_tempfile_then_rename(&content, &wasm_path)?; - } - - let wasm_path = get_pulled_wasm_path(&canister_id, pulled_canister.gzip)?; - - // extract `candid:service` and save as candid file in shared cache - let module = read_wasm_module(&wasm_path)?; - let candid_service = get_metadata_as_string(&module, CANDID_SERVICE, &wasm_path)?; - let service_candid_path = get_pulled_service_candid_path(&canister_id)?; - write_to_tempfile_then_rename(candid_service.as_bytes(), &service_candid_path)?; - - // extract `candid:args` - let candid_args = get_metadata_as_string(&module, CANDID_ARGS, &wasm_path)?; - pulled_canister.candid_args = candid_args; - - // extract `dfx` - let dfx_metadata_str = get_metadata_as_string(&module, DFX, &wasm_path)?; - let dfx_metadata: DfxMetadata = serde_json::from_str(&dfx_metadata_str)?; - let pullable = dfx_metadata.get_pullable()?; - pulled_canister.dependencies = pullable.dependencies.clone(); - pulled_canister.init_guide = pullable.init_guide.clone(); - pulled_canister.init_arg = pullable.init_arg.clone(); - - Ok(pulled_canister) -} - -async fn fetch_dfx_metadata(agent: &Agent, canister_id: &Principal) -> DfxResult { - match fetch_metadata(agent, canister_id, DFX).await? { - Some(dfx_metadata_raw) => { - let dfx_metadata_str = String::from_utf8(dfx_metadata_raw)?; - let dfx_metadata: DfxMetadata = serde_json::from_str(&dfx_metadata_str)?; - Ok(dfx_metadata) - } - None => { - bail!("`{DFX}` metadata not found in canister {canister_id}."); - } - } -} - -#[context("Failed to fetch metadata {metadata} of canister {canister_id}.")] -async fn fetch_metadata( - agent: &Agent, - canister_id: &Principal, - metadata: &str, -) -> DfxResult>> { - match agent - .read_state_canister_metadata(*canister_id, metadata) - .await - { - Ok(data) => Ok(Some(data)), - Err(agent_error) => match agent_error { - // replica returns such error - AgentError::HttpError(ref e) => { - let status = e.status; - let content = String::from_utf8(e.content.clone())?; - if status == 404 - && content.starts_with(&format!("Custom section {metadata} not found")) - { - Ok(None) - } else { - bail!(agent_error); - } - } - // ic-ref returns such error when the canister doesn't define the metadata - AgentError::LookupPathAbsent(_) => Ok(None), - _ => { - bail!(agent_error) - } - }, - } -} - -// Get expected hash of the canister wasm. -// If `wasm_hash` is specified in dfx metadata, use it. -// If `wasm_hash_url` is specified in dfx metadata, download the hash from the url. -// Otherwise, get the hash of the on chain canister. -async fn get_hash_on_chain( - agent: &Agent, - logger: &Logger, - canister_id: Principal, - pullable: &Pullable, -) -> DfxResult> { - if pullable.wasm_hash.is_some() && pullable.wasm_hash_url.is_some() { - warn!(logger, "Canister {canister_id} specified both `wasm_hash` and `wasm_hash_url`. `wasm_hash` will be used."); - }; - if let Some(wasm_hash_str) = &pullable.wasm_hash { - trace!( - logger, - "Canister {canister_id} specified a custom hash: {wasm_hash_str}" - ); - Ok(hex::decode(wasm_hash_str) - .with_context(|| format!("Failed to decode {wasm_hash_str} as sha256 hash."))?) - } else if let Some(wasm_hash_url) = &pullable.wasm_hash_url { - trace!( - logger, - "Canister {canister_id} specified a custom hash via url: {wasm_hash_url}" - ); - let wasm_hash_url = reqwest::Url::parse(wasm_hash_url) - .with_context(|| format!("{wasm_hash_url} is not a valid URL."))?; - let wasm_hash_content = download_file(&wasm_hash_url) - .await - .with_context(|| format!("Failed to download wasm_hash from {wasm_hash_url}."))?; - let wasm_hash_str = String::from_utf8(wasm_hash_content) - .with_context(|| format!("Content from {wasm_hash_url} is not valid text."))?; - // The content might contain the file name (usually from tools like shasum or sha256sum). - // We only need the hash part. - let wasm_hash_encoded = wasm_hash_str - .split_whitespace() - .next() - .with_context(|| format!("Content from {wasm_hash_url} is empty."))?; - Ok(hex::decode(wasm_hash_encoded) - .with_context(|| format!("Failed to decode {wasm_hash_encoded} as sha256 hash."))?) - } else { - match read_state_tree_canister_module_hash(agent, canister_id).await? { - Some(hash_on_chain) => Ok(hash_on_chain), - None => { - bail!( - "Canister {canister_id} doesn't have module hash. Perhaps it's not installed." - ); - } - } - } -} - -#[context("Failed to write to a tempfile then rename it to {}", path.display())] -fn write_to_tempfile_then_rename(content: &[u8], path: &Path) -> DfxResult { - assert!(path.is_absolute()); - let dir = dfx_core::fs::parent(path)?; - ensure_dir_exists(&dir)?; - let mut f = tempfile::NamedTempFile::new_in(&dir) - .with_context(|| format!("Failed to create a NamedTempFile in {dir:?}"))?; - f.write_all(content) - .with_context(|| format!("Failed to write the NamedTempFile at {:?}", f.path()))?; - dfx_core::fs::rename(f.path(), path)?; - Ok(()) -} - -#[context("Failed to copy candid path of pull dependency {name}")] -pub fn copy_service_candid_to_project( - project_root: &Path, - name: &str, - canister_id: &Principal, -) -> DfxResult { - let service_candid_path = get_pulled_service_candid_path(canister_id)?; - let path_in_project = get_candid_path_in_project(project_root, canister_id); - ensure_parent_dir_exists(&path_in_project)?; - dfx_core::fs::copy(&service_candid_path, &path_in_project)?; - dfx_core::fs::set_permissions_readwrite(&path_in_project)?; - Ok(()) -} - -fn get_metadata_as_string( - module: &walrus::Module, - section: &str, - wasm_path: &Path, -) -> DfxResult { - let metadata_bytes = get_metadata(module, section) - .with_context(|| format!("Failed to get {} metadata from {:?}", section, wasm_path))?; - let metadata = String::from_utf8(metadata_bytes.to_vec()).with_context(|| { - format!( - "Failed to read {} metadata from {:?} as UTF-8 text", - section, wasm_path - ) - })?; - Ok(metadata) -} diff --git a/src/dfx/src/commands/identity/import.rs b/src/dfx/src/commands/identity/import.rs index 4a2d374979..832a4ff5d5 100644 --- a/src/dfx/src/commands/identity/import.rs +++ b/src/dfx/src/commands/identity/import.rs @@ -9,7 +9,7 @@ use std::fs; use std::path::PathBuf; use std::str::FromStr; -/// Creates a new identity from a PEM file. +/// Creates a new identity from a PEM file or seed phrase file. #[derive(Parser)] pub struct ImportOpts { /// The identity to create. diff --git a/src/dfx/src/commands/start.rs b/src/dfx/src/commands/start.rs index 4e556cbecd..909058dfa4 100644 --- a/src/dfx/src/commands/start.rs +++ b/src/dfx/src/commands/start.rs @@ -1,7 +1,8 @@ use crate::actors::pocketic_proxy::{signals::PortReadySubscribe, PocketIcProxyConfig}; use crate::actors::{ start_btc_adapter_actor, start_canister_http_adapter_actor, start_pocketic_actor, - start_pocketic_proxy_actor, start_replica_actor, start_shutdown_controller, + start_pocketic_proxy_actor, start_post_start_actor, start_replica_actor, + start_shutdown_controller, }; use crate::config::dfx_version_str; use crate::error_invalid_argument; @@ -49,6 +50,10 @@ pub struct StartOpts { #[arg(long)] background: bool, + /// Indicates if the actual dfx process is running in the background. + #[arg(long, env = "DFX_RUNNING_IN_BACKGROUND", hide = true)] + running_in_background: bool, + /// Cleans the state of the current project. #[arg(long)] clean: bool, @@ -138,6 +143,7 @@ pub fn exec( StartOpts { host, background, + running_in_background, clean, force, bitcoin_node, @@ -413,7 +419,10 @@ pub fn exec( pocketic_proxy_pid_file_path, pocketic_proxy_port_file_path, )?; - Ok::<_, Error>(proxy) + + let post_start = start_post_start_actor(env, running_in_background, Some(proxy))?; + + Ok::<_, Error>(post_start) })?; system.run()?; @@ -574,7 +583,8 @@ fn send_background() -> DfxResult<()> { .skip(1) .filter(|a| !a.eq("--background")) .filter(|a| !a.eq("--clean")), - ); + ) + .env("DFX_RUNNING_IN_BACKGROUND", "true"); // Set the `DFX_RUNNING_IN_BACKGROUND` environment variable which will be used by the second start. cmd.spawn().context("Failed to spawn child process.")?; Ok(()) diff --git a/src/dfx/src/lib/canister_info/rust.rs b/src/dfx/src/lib/canister_info/rust.rs index a3b7266c26..22ce219e04 100644 --- a/src/dfx/src/lib/canister_info/rust.rs +++ b/src/dfx/src/lib/canister_info/rust.rs @@ -64,7 +64,7 @@ impl CanisterInfoFactory for RustCanisterInfo { }; let mut candidate_targets = package_info.targets.iter().filter(|x| { x.crate_types.iter().any(|c| { - (c == "cdylib" && x.name == crate_name.replace('-', "_")) + (c == "cdylib" && x.name.replace('-', "_") == crate_name.replace('-', "_")) || (c == "bin" && x.name == crate_name) }) }); diff --git a/src/dfx/src/lib/deps/mod.rs b/src/dfx/src/lib/deps/mod.rs index 456220ccda..90e7ba973d 100644 --- a/src/dfx/src/lib/deps/mod.rs +++ b/src/dfx/src/lib/deps/mod.rs @@ -15,13 +15,14 @@ use std::{ }; pub mod deploy; +pub mod pull; #[derive(Serialize, Deserialize, Default)] pub struct PulledJson { pub canisters: BTreeMap, } -#[derive(Serialize, Deserialize, Default)] +#[derive(Serialize, Deserialize, Default, Clone)] pub struct PulledCanister { /// Name of `type: pull` in dfx.json. Omitted if indirect dependency. #[serde(skip_serializing_if = "Option::is_none")] diff --git a/src/dfx/src/lib/deps/pull/facade.rs b/src/dfx/src/lib/deps/pull/facade.rs new file mode 100644 index 0000000000..aeae315eef --- /dev/null +++ b/src/dfx/src/lib/deps/pull/facade.rs @@ -0,0 +1,190 @@ +use candid::{pretty::candid::pp_args, Principal}; +use candid_parser::utils::{instantiate_candid, CandidSource}; +use dfx_core::config::cache::get_cache_root; +use dfx_core::fs::{composite::ensure_parent_dir_exists, read, read_to_string, write}; +use sha2::{Digest, Sha256}; +use std::collections::HashMap; + +use super::{super::PulledCanister, write_to_tempfile_then_rename}; +use crate::lib::deps::{ + get_pulled_canister_dir, get_pulled_service_candid_path, get_pulled_wasm_path, +}; +use crate::lib::error::DfxResult; +use crate::util::{download_file, download_file_to_path}; + +static IC_REV: &str = "1eeb4d74deb00bd52739cbd6f37ce1dc72e0c76e"; + +#[derive(Debug)] +struct Facade { + wasm_url: String, + candid_url: String, + dependencies: Vec, + init_guide: String, +} + +lazy_static::lazy_static! { + static ref ICP_LEDGER: Principal=Principal::from_text("ryjl3-tyaaa-aaaaa-aaaba-cai").unwrap(); + static ref CKBTC_LEDGER: Principal=Principal::from_text("mxzaz-hqaaa-aaaar-qaada-cai").unwrap(); + static ref CKETH_LEDGER: Principal=Principal::from_text("ss2fx-dyaaa-aaaar-qacoq-cai").unwrap(); + static ref FACADE: HashMap = { + let mut m = HashMap::new(); + // https://internetcomputer.org/docs/current/developer-docs/defi/tokens/ledger/setup/icp_ledger_setup + m.insert( + *ICP_LEDGER, + Facade { + wasm_url: format!("https://download.dfinity.systems/ic/{IC_REV}/canisters/ledger-canister.wasm.gz"), + candid_url: format!("https://raw.githubusercontent.com/dfinity/ic/{IC_REV}/rs/ledger_suite/icp/ledger.did"), + dependencies:vec![], + init_guide: r#" +1. Create a 'minter' identity: dfx identity new minter +2. Run the following multi-line command: + +dfx deps init ryjl3-tyaaa-aaaaa-aaaba-cai --argument "(variant { + Init = record { + minting_account = \"$(dfx --identity minter ledger account-id)\"; + initial_values = vec {}; + send_whitelist = vec {}; + transfer_fee = opt record { e8s = 10_000 : nat64; }; + token_symbol = opt \"LICP\"; + token_name = opt \"Local ICP\"; + } +})" +"#.to_string(), + } + ); + // https://github.com/dfinity/ic/blob/master/rs/bitcoin/ckbtc/mainnet/README.md#installing-the-ledger-mxzaz-hqaaa-aaaar-qaada-cai + m.insert( + *CKBTC_LEDGER, + Facade { + wasm_url: format!("https://download.dfinity.systems/ic/{IC_REV}/canisters/ic-icrc1-ledger.wasm.gz"), + candid_url: format!("https://raw.githubusercontent.com/dfinity/ic/{IC_REV}/rs/ledger_suite/icrc1/ledger/ledger.did"), + dependencies:vec![], + init_guide: r#" +1. Create a 'minter' identity: dfx identity new minter +2. Run the following multi-line command: + +dfx deps init mxzaz-hqaaa-aaaar-qaada-cai --argument "(variant { + Init = record { + minting_account = record { owner = principal \"$(dfx --identity minter identity get-principal)\"; }; + transfer_fee = 10; + token_symbol = \"ckBTC\"; + token_name = \"ckBTC\"; + metadata = vec {}; + initial_balances = vec {}; + max_memo_length = opt 80; + archive_options = record { + num_blocks_to_archive = 1000; + trigger_threshold = 2000; + max_message_size_bytes = null; + cycles_for_archive_creation = opt 100_000_000_000_000; + node_max_memory_size_bytes = opt 3_221_225_472; + controller_id = principal \"2vxsx-fae\" + } + } +})" +"#.to_string(), + } + ); + // https://github.com/dfinity/ic/blob/master/rs/ethereum/cketh/mainnet/README.md#installing-the-ledger + m.insert( + *CKETH_LEDGER, + Facade { + wasm_url: format!("https://download.dfinity.systems/ic/{IC_REV}/canisters/ic-icrc1-ledger-u256.wasm.gz"), + candid_url: format!("https://raw.githubusercontent.com/dfinity/ic/{IC_REV}/rs/ledger_suite/icrc1/ledger/ledger.did"), + dependencies:vec![], + init_guide: r#" +1. Create a 'minter' identity: dfx identity new minter +2. Run the following multi-line command: + +dfx deps init ss2fx-dyaaa-aaaar-qacoq-cai --argument "(variant { + Init = record { + minting_account = record { owner = principal \"$(dfx --identity minter identity get-principal)\"; }; + decimals = opt 18; + max_memo_length = opt 80; + transfer_fee = 2_000_000_000_000; + token_symbol = \"ckETH\"; + token_name = \"ckETH\"; + feature_flags = opt record { icrc2 = true }; + metadata = vec {}; + initial_balances = vec {}; + archive_options = record { + num_blocks_to_archive = 1000; + trigger_threshold = 2000; + max_message_size_bytes = null; + cycles_for_archive_creation = opt 100_000_000_000_000; + node_max_memory_size_bytes = opt 3_221_225_472; + controller_id = principal \"2vxsx-fae\" + } + } +})" +"#.to_string(), + } + ); + m + }; +} + +pub(super) fn facade_dependencies(canister_id: &Principal) -> Option> { + FACADE + .get(canister_id) + .map(|facade| facade.dependencies.clone()) +} + +pub(super) async fn facade_download(canister_id: &Principal) -> DfxResult> { + if let Some(facade) = FACADE.get(canister_id) { + let mut pulled_canister = PulledCanister { + dependencies: facade.dependencies.clone(), + init_guide: facade.init_guide.clone(), + gzip: facade.wasm_url.ends_with(".gz"), + ..Default::default() + }; + let ic_rev_path = get_cache_root()? + .join("pulled") + .join(".facade") + .join(canister_id.to_text()); + let wasm_path = get_pulled_wasm_path(canister_id, pulled_canister.gzip)?; + let service_candid_path = get_pulled_service_candid_path(canister_id)?; + let mut cache_hit = false; + if ic_rev_path.exists() && wasm_path.exists() && service_candid_path.exists() { + let ic_rev = read_to_string(&ic_rev_path)?; + if ic_rev == IC_REV { + cache_hit = true; + } + } + if !cache_hit { + // delete files from previous pull + let pulled_canister_dir = get_pulled_canister_dir(canister_id)?; + if pulled_canister_dir.exists() { + dfx_core::fs::remove_dir_all(&pulled_canister_dir)?; + } + dfx_core::fs::create_dir_all(&pulled_canister_dir)?; + // download wasm and candid + let wasm_url = reqwest::Url::parse(&facade.wasm_url)?; + download_file_to_path(&wasm_url, &wasm_path).await?; + let candid_url = reqwest::Url::parse(&facade.candid_url)?; + let candid_bytes = download_file(&candid_url).await?; + let candid_service = String::from_utf8(candid_bytes)?; + write_to_tempfile_then_rename(candid_service.as_bytes(), &service_candid_path)?; + // write ic_rev for cache logic + ensure_parent_dir_exists(&ic_rev_path)?; + write(&ic_rev_path, IC_REV)?; + } + + // wasm_hash + let wasm_content = read(&wasm_path)?; + let wasm_hash = Sha256::digest(wasm_content).to_vec(); + pulled_canister.wasm_hash = hex::encode(&wasm_hash); + pulled_canister.wasm_hash_download = hex::encode(&wasm_hash); + + // candid_args + let candid_service = read_to_string(&service_candid_path)?; + let candid_source = CandidSource::Text(&candid_service); + let (args, _service) = instantiate_candid(candid_source)?; + let candid_args = pp_args(&args).pretty(80).to_string(); + pulled_canister.candid_args = candid_args; + + Ok(Some(pulled_canister)) + } else { + Ok(None) + } +} diff --git a/src/dfx/src/lib/deps/pull/mod.rs b/src/dfx/src/lib/deps/pull/mod.rs new file mode 100644 index 0000000000..5358eec4ee --- /dev/null +++ b/src/dfx/src/lib/deps/pull/mod.rs @@ -0,0 +1,322 @@ +use super::{ + get_candid_path_in_project, get_pulled_canister_dir, get_pulled_service_candid_path, + get_pulled_wasm_path, PulledCanister, PulledJson, +}; +use crate::lib::error::DfxResult; +use crate::lib::metadata::dfx::DfxMetadata; +use crate::lib::metadata::names::{CANDID_ARGS, CANDID_SERVICE, DFX}; +use crate::lib::state_tree::canister_info::read_state_tree_canister_module_hash; +use crate::lib::wasm::file::{decompress_bytes, read_wasm_module}; +use crate::util::download_file; +use anyhow::{bail, Context}; +use candid::Principal; +use dfx_core::config::model::dfinity::Pullable; +use dfx_core::fs::composite::{ensure_dir_exists, ensure_parent_dir_exists}; +use fn_error_context::context; +use ic_agent::{Agent, AgentError}; +use ic_wasm::metadata::get_metadata; +use sha2::{Digest, Sha256}; +use slog::{error, info, trace, warn, Logger}; +use std::collections::{BTreeMap, BTreeSet, VecDeque}; +use std::io::Write; +use std::path::Path; + +mod facade; +use facade::{facade_dependencies, facade_download}; + +pub async fn resolve_all_dependencies( + agent: &Agent, + logger: &Logger, + pull_canisters_in_config: &BTreeMap, +) -> DfxResult> { + let mut canisters_to_resolve: VecDeque = + pull_canisters_in_config.values().cloned().collect(); + let mut checked = BTreeSet::new(); + while let Some(canister_id) = canisters_to_resolve.pop_front() { + if !checked.contains(&canister_id) { + checked.insert(canister_id); + let dependencies = if let Some(deps) = facade_dependencies(&canister_id) { + info!( + logger, + "Using facade dependencies for canister {canister_id}." + ); + deps + } else { + get_dependencies(agent, logger, &canister_id).await? + }; + canisters_to_resolve.extend(dependencies.iter()); + } + } + let all_dependencies = checked.into_iter().collect::>(); + let mut message = String::new(); + message.push_str(&format!("Found {} dependencies:", all_dependencies.len())); + for id in &all_dependencies { + message.push('\n'); + message.push_str(&id.to_text()); + } + info!(logger, "{}", message); + Ok(all_dependencies) +} + +#[context("Failed to get dependencies of canister {canister_id}.")] +async fn get_dependencies( + agent: &Agent, + logger: &Logger, + canister_id: &Principal, +) -> DfxResult> { + info!(logger, "Fetching dependencies of canister {canister_id}..."); + let dfx_metadata = fetch_dfx_metadata(agent, canister_id).await?; + let dependencies = dfx_metadata.get_pullable()?.dependencies.clone(); + Ok(dependencies) +} + +pub async fn download_all_and_generate_pulled_json( + agent: &Agent, + logger: &Logger, + all_dependencies: &[Principal], +) -> DfxResult { + let mut any_download_fail = false; + let mut pulled_json = PulledJson::default(); + for canister_id in all_dependencies { + match download_and_generate_pulled_canister(agent, logger, *canister_id).await { + Ok(pulled_canister) => { + pulled_json.canisters.insert(*canister_id, pulled_canister); + } + Err(e) => { + error!(logger, "Failed to pull canister {canister_id}.\n{e}"); + any_download_fail = true; + } + } + } + + if any_download_fail { + bail!("Failed when pulling canisters."); + } + Ok(pulled_json) +} + +// Download canister wasm, then extract metadata from it to build a PulledCanister +async fn download_and_generate_pulled_canister( + agent: &Agent, + logger: &Logger, + canister_id: Principal, +) -> DfxResult { + info!(logger, "Pulling canister {canister_id}..."); + if let Some(pulled_canister) = facade_download(&canister_id).await? { + return Ok(pulled_canister); + } + + let mut pulled_canister = PulledCanister::default(); + + let dfx_metadata = fetch_dfx_metadata(agent, &canister_id).await?; + let pullable = dfx_metadata.get_pullable()?; + + let hash_on_chain = get_hash_on_chain(agent, logger, canister_id, pullable).await?; + pulled_canister.wasm_hash = hex::encode(&hash_on_chain); + + // skip download if cache hit + let mut cache_hit = false; + + for gzip in [false, true] { + let path = get_pulled_wasm_path(&canister_id, gzip)?; + if path.exists() { + let bytes = dfx_core::fs::read(&path)?; + let hash_cache = Sha256::digest(bytes); + if hash_cache.as_slice() == hash_on_chain { + cache_hit = true; + pulled_canister.gzip = gzip; + pulled_canister.wasm_hash_download = hex::encode(hash_cache); + trace!(logger, "The canister wasm was found in the cache."); + } + break; + } + } + + if !cache_hit { + // delete files from previous pull + let pulled_canister_dir = get_pulled_canister_dir(&canister_id)?; + if pulled_canister_dir.exists() { + dfx_core::fs::remove_dir_all(&pulled_canister_dir)?; + } + dfx_core::fs::create_dir_all(&pulled_canister_dir)?; + + // lookup `wasm_url` in dfx metadata + let wasm_url = reqwest::Url::parse(&pullable.wasm_url)?; + + // download + let content = download_file(&wasm_url).await?; + + // hash check + let hash_download = Sha256::digest(&content); + pulled_canister.wasm_hash_download = hex::encode(hash_download); + + let gzip = decompress_bytes(&content).is_ok(); + pulled_canister.gzip = gzip; + let wasm_path = get_pulled_wasm_path(&canister_id, gzip)?; + + write_to_tempfile_then_rename(&content, &wasm_path)?; + } + + let wasm_path = get_pulled_wasm_path(&canister_id, pulled_canister.gzip)?; + + // extract `candid:service` and save as candid file in shared cache + let module = read_wasm_module(&wasm_path)?; + let candid_service = get_metadata_as_string(&module, CANDID_SERVICE, &wasm_path)?; + let service_candid_path = get_pulled_service_candid_path(&canister_id)?; + write_to_tempfile_then_rename(candid_service.as_bytes(), &service_candid_path)?; + + // extract `candid:args` + let candid_args = get_metadata_as_string(&module, CANDID_ARGS, &wasm_path)?; + pulled_canister.candid_args = candid_args; + + // extract `dfx` + let dfx_metadata_str = get_metadata_as_string(&module, DFX, &wasm_path)?; + let dfx_metadata: DfxMetadata = serde_json::from_str(&dfx_metadata_str)?; + let pullable = dfx_metadata.get_pullable()?; + pulled_canister.dependencies = pullable.dependencies.clone(); + pulled_canister.init_guide = pullable.init_guide.clone(); + pulled_canister.init_arg = pullable.init_arg.clone(); + + Ok(pulled_canister) +} + +async fn fetch_dfx_metadata(agent: &Agent, canister_id: &Principal) -> DfxResult { + match fetch_metadata(agent, canister_id, DFX).await? { + Some(dfx_metadata_raw) => { + let dfx_metadata_str = String::from_utf8(dfx_metadata_raw)?; + let dfx_metadata: DfxMetadata = serde_json::from_str(&dfx_metadata_str)?; + Ok(dfx_metadata) + } + None => { + bail!("`{DFX}` metadata not found in canister {canister_id}."); + } + } +} + +#[context("Failed to fetch metadata {metadata} of canister {canister_id}.")] +async fn fetch_metadata( + agent: &Agent, + canister_id: &Principal, + metadata: &str, +) -> DfxResult>> { + match agent + .read_state_canister_metadata(*canister_id, metadata) + .await + { + Ok(data) => Ok(Some(data)), + Err(agent_error) => match agent_error { + // replica returns such error + AgentError::HttpError(ref e) => { + let status = e.status; + let content = String::from_utf8(e.content.clone())?; + if status == 404 + && content.starts_with(&format!("Custom section {metadata} not found")) + { + Ok(None) + } else { + bail!(agent_error); + } + } + // ic-ref returns such error when the canister doesn't define the metadata + AgentError::LookupPathAbsent(_) => Ok(None), + _ => { + bail!(agent_error) + } + }, + } +} + +// Get expected hash of the canister wasm. +// If `wasm_hash` is specified in dfx metadata, use it. +// If `wasm_hash_url` is specified in dfx metadata, download the hash from the url. +// Otherwise, get the hash of the on chain canister. +async fn get_hash_on_chain( + agent: &Agent, + logger: &Logger, + canister_id: Principal, + pullable: &Pullable, +) -> DfxResult> { + if pullable.wasm_hash.is_some() && pullable.wasm_hash_url.is_some() { + warn!(logger, "Canister {canister_id} specified both `wasm_hash` and `wasm_hash_url`. `wasm_hash` will be used."); + }; + if let Some(wasm_hash_str) = &pullable.wasm_hash { + trace!( + logger, + "Canister {canister_id} specified a custom hash: {wasm_hash_str}" + ); + Ok(hex::decode(wasm_hash_str) + .with_context(|| format!("Failed to decode {wasm_hash_str} as sha256 hash."))?) + } else if let Some(wasm_hash_url) = &pullable.wasm_hash_url { + trace!( + logger, + "Canister {canister_id} specified a custom hash via url: {wasm_hash_url}" + ); + let wasm_hash_url = reqwest::Url::parse(wasm_hash_url) + .with_context(|| format!("{wasm_hash_url} is not a valid URL."))?; + let wasm_hash_content = download_file(&wasm_hash_url) + .await + .with_context(|| format!("Failed to download wasm_hash from {wasm_hash_url}."))?; + let wasm_hash_str = String::from_utf8(wasm_hash_content) + .with_context(|| format!("Content from {wasm_hash_url} is not valid text."))?; + // The content might contain the file name (usually from tools like shasum or sha256sum). + // We only need the hash part. + let wasm_hash_encoded = wasm_hash_str + .split_whitespace() + .next() + .with_context(|| format!("Content from {wasm_hash_url} is empty."))?; + Ok(hex::decode(wasm_hash_encoded) + .with_context(|| format!("Failed to decode {wasm_hash_encoded} as sha256 hash."))?) + } else { + match read_state_tree_canister_module_hash(agent, canister_id).await? { + Some(hash_on_chain) => Ok(hash_on_chain), + None => { + bail!( + "Canister {canister_id} doesn't have module hash. Perhaps it's not installed." + ); + } + } + } +} + +#[context("Failed to write to a tempfile then rename it to {}", path.display())] +fn write_to_tempfile_then_rename(content: &[u8], path: &Path) -> DfxResult { + assert!(path.is_absolute()); + let dir = dfx_core::fs::parent(path)?; + ensure_dir_exists(&dir)?; + let mut f = tempfile::NamedTempFile::new_in(&dir) + .with_context(|| format!("Failed to create a NamedTempFile in {dir:?}"))?; + f.write_all(content) + .with_context(|| format!("Failed to write the NamedTempFile at {:?}", f.path()))?; + dfx_core::fs::rename(f.path(), path)?; + Ok(()) +} + +#[context("Failed to copy candid path of pull dependency {name}")] +pub fn copy_service_candid_to_project( + project_root: &Path, + name: &str, + canister_id: &Principal, +) -> DfxResult { + let service_candid_path = get_pulled_service_candid_path(canister_id)?; + let path_in_project = get_candid_path_in_project(project_root, canister_id); + ensure_parent_dir_exists(&path_in_project)?; + dfx_core::fs::copy(&service_candid_path, &path_in_project)?; + dfx_core::fs::set_permissions_readwrite(&path_in_project)?; + Ok(()) +} + +fn get_metadata_as_string( + module: &walrus::Module, + section: &str, + wasm_path: &Path, +) -> DfxResult { + let metadata_bytes = get_metadata(module, section) + .with_context(|| format!("Failed to get {} metadata from {:?}", section, wasm_path))?; + let metadata = String::from_utf8(metadata_bytes.to_vec()).with_context(|| { + format!( + "Failed to read {} metadata from {:?} as UTF-8 text", + section, wasm_path + ) + })?; + Ok(metadata) +} diff --git a/src/dfx/src/lib/diagnosis.rs b/src/dfx/src/lib/diagnosis.rs index eb121f14ab..e59a810da7 100644 --- a/src/dfx/src/lib/diagnosis.rs +++ b/src/dfx/src/lib/diagnosis.rs @@ -1,5 +1,6 @@ use crate::lib::error_code; use anyhow::Error as AnyhowError; +use dfx_core::error::root_key::FetchRootKeyError; use ic_agent::agent::{RejectCode, RejectResponse}; use ic_agent::AgentError; use ic_asset::error::{GatherAssetDescriptorsError, SyncError, UploadContentError}; @@ -53,6 +54,13 @@ pub fn diagnose(err: &AnyhowError) -> Diagnosis { } else if *agent_err == AgentError::CertificateNotAuthorized() { return subnet_not_authorized(); } + if cycles_ledger_not_found(err) { + return diagnose_cycles_ledger_not_found(); + } + } + + if local_replica_not_running(err) { + return diagnose_local_replica_not_running(); } if let Some(sync_error) = err.downcast_ref::() { @@ -64,6 +72,32 @@ pub fn diagnose(err: &AnyhowError) -> Diagnosis { NULL_DIAGNOSIS } +fn local_replica_not_running(err: &AnyhowError) -> bool { + let maybe_agent_error = { + if let Some(FetchRootKeyError::AgentError(agent_error)) = + err.downcast_ref::() + { + Some(agent_error) + } else { + err.downcast_ref::() + } + }; + if let Some(AgentError::TransportError(transport_error)) = maybe_agent_error { + transport_error.is_connect() + && transport_error + .url() + .and_then(|url| url.host()) + .map(|host| match host { + url::Host::Domain(domain) => domain == "localhost", + url::Host::Ipv4(ipv4_addr) => ipv4_addr.is_loopback(), + url::Host::Ipv6(ipv6_addr) => ipv6_addr.is_loopback(), + }) + .unwrap_or(false) + } else { + false + } +} + fn not_a_controller(err: &AgentError) -> bool { // Newer replicas include the error code in the reject response. if matches!( @@ -119,6 +153,17 @@ The most common way this error is solved is by running 'dfx canister update-sett ) } +fn diagnose_local_replica_not_running() -> Diagnosis { + let error_explanation = + "You are trying to connect to the local replica but dfx cannot connect to it."; + let action_suggestion = + "Target a different network or run 'dfx start' to start the local replica."; + ( + Some(error_explanation.to_string()), + Some(action_suggestion.to_string()), + ) +} + fn subnet_not_authorized() -> Diagnosis { let action_suggestion = "If you are connecting to a node directly instead of a boundary node, try using --provisional-create-canister-effective-canister-id with a canister id in the subnet's canister range. First non-root subnet: 5v3p4-iyaaa-aaaaa-qaaaa-cai, second non-root subnet: jrlun-jiaaa-aaaab-aaaaa-cai"; (None, Some(action_suggestion.to_string())) @@ -187,3 +232,17 @@ If you're using a local replica and configuring a wallet was a mistake, you can recreate the replica with `dfx stop && dfx start --clean` to start over."; (Some(explanation.to_string()), Some(suggestion.to_string())) } + +fn cycles_ledger_not_found(err: &AnyhowError) -> bool { + err.to_string() + .contains("Canister um5iw-rqaaa-aaaaq-qaaba-cai not found") +} + +fn diagnose_cycles_ledger_not_found() -> Diagnosis { + let explanation = + "Cycles ledger with canister ID 'um5iw-rqaaa-aaaaq-qaaba-cai' is not installed."; + let suggestion = + "Run the command with '--ic' flag if you want to manage the cycles on the mainnet."; + + (Some(explanation.to_string()), Some(suggestion.to_string())) +} diff --git a/src/distributed/assetstorage.did b/src/distributed/assetstorage.did index ccf075ffc4..f94499065d 100644 --- a/src/distributed/assetstorage.did +++ b/src/distributed/assetstorage.did @@ -144,7 +144,9 @@ type AssetCanisterArgs = variant { Upgrade: UpgradeArgs; }; -type InitArgs = record {}; +type InitArgs = record { + set_permissions: opt SetPermissions; +}; type UpgradeArgs = record { set_permissions: opt SetPermissions; diff --git a/src/distributed/assetstorage.wasm.gz b/src/distributed/assetstorage.wasm.gz index 7f2c64e536..b0e0ec09f2 100755 Binary files a/src/distributed/assetstorage.wasm.gz and b/src/distributed/assetstorage.wasm.gz differ diff --git a/src/distributed/ui.wasm b/src/distributed/ui.wasm index 908ef62dc1..b56e2fb430 100644 Binary files a/src/distributed/ui.wasm and b/src/distributed/ui.wasm differ