diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1b6bdcb8c..85b29b06c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,11 @@ concurrency: jobs: build: name: cargo build - runs-on: ubuntu-24.04 + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-24.04, macos-13-large] + fail-fast: false steps: - name: Checkout repository uses: actions/checkout@v4 @@ -34,8 +38,8 @@ jobs: ${{ runner.os }}- - name: Run builds run: | - cargo build --workspace --exclude ic-cdk-e2e-tests --exclude candid-extractor --target wasm32-unknown-unknown - cargo build --workspace --exclude ic-cdk-e2e-tests --exclude candid-extractor --target wasm32-unknown-unknown --release + cargo build --workspace --exclude ic-cdk-e2e-tests --target wasm32-unknown-unknown + cargo build --workspace --exclude ic-cdk-e2e-tests --target wasm32-unknown-unknown --release cargo build --example=work test: @@ -44,6 +48,7 @@ jobs: strategy: matrix: os: [ubuntu-24.04, macos-13-large] + fail-fast: false steps: - name: Checkout repository uses: actions/checkout@v4 @@ -66,9 +71,43 @@ jobs: - name: Run tests run: | # https://github.com/rust-lang/cargo/issues/6669 we have to run ALL tests with two commands - cargo test --all-targets + cargo test --all-targets --no-fail-fast cargo test --doc + wasm64: + name: wasm64 e2e + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-24.04, macos-13-large] + fail-fast: false + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Cache + uses: actions/cache@v4 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-wasm64-${{ hashFiles('Cargo.lock', 'rust-toolchain.toml') }} + restore-keys: | + ${{ runner.os }}-wasm64- + ${{ runner.os }}- + - name: Download pocket-ic + run: | + bash scripts/download_pocket_ic.sh + - uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + toolchain: nightly + components: rust-src + - name: Run tests + run: + WASM64=1 cargo test --package ic-cdk-e2e-tests --no-fail-fast + fmt: name: cargo fmt runs-on: ubuntu-24.04 @@ -141,7 +180,7 @@ jobs: aggregate: name: ci:required if: ${{ always() }} - needs: [build, test, fmt, clippy, doc] + needs: [build, test, wasm64, fmt, clippy, doc] runs-on: ubuntu-24.04 steps: - name: check build result @@ -150,6 +189,9 @@ jobs: - name: check test result if: ${{ needs.test.result != 'success' }} run: exit 1 + - name: check wasm64 result + if: ${{ needs.wasm64.result != 'success' }} + run: exit 1 - name: check fmt result if: ${{ needs.fmt.result != 'success' }} run: exit 1 diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml deleted file mode 100644 index 807da5464..000000000 --- a/.github/workflows/examples.yml +++ /dev/null @@ -1,132 +0,0 @@ -name: Examples - -on: - push: - branches: - - main - pull_request: - schedule: - - cron: "0 4 * * 0,3" # 4 a.m. UTC every Sun and Wed, keep actions-cache available - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -env: - ic-wasm-version: 0.4.0 - -jobs: - build-candid-extractor: - runs-on: ubuntu-24.04 - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Cache - uses: actions/cache@v4 - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - target/ - key: ${{ runner.os }}-candid-extractor-${{ hashFiles('Cargo.lock', 'rust-toolchain.toml') }} - restore-keys: | - ${{ runner.os }}-candid-extractor- - ${{ runner.os }}- - - - name: Build candid-extractor - run: cargo build -p candid-extractor --release - - - uses: actions/upload-artifact@v4 - with: - name: candid-extractor - path: target/release/candid-extractor - - test: - runs-on: ubuntu-24.04 - needs: build-candid-extractor - strategy: - fail-fast: false - matrix: - project-name: - [asset_storage, chess, counter, management_canister, print, profile] - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - submodules: "recursive" - - - name: Cache - uses: actions/cache@v4 - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - examples/${{ matrix.project-name }}/target/ - key: ${{ runner.os }}-${{ matrix.project-name }}-${{ hashFiles('Cargo.lock', 'rust-toolchain.toml') }} - restore-keys: | - ${{ runner.os }}-${{ matrix.project-name }}- - ${{ runner.os }}- - - - name: Install ic-wasm - # might already in cache - run: | - wget https://github.com/dfinity/ic-wasm/releases/download/${{env.ic-wasm-version }}/ic-wasm-linux64 - chmod 755 ic-wasm-linux64 - mv ic-wasm-linux64 /usr/local/bin/ic-wasm - - - name: Download candid-extractor - uses: actions/download-artifact@v4 - with: - name: candid-extractor - - - name: Install candid-extractor - run: | - chmod 755 candid-extractor - mv candid-extractor /usr/local/bin/candid-extractor - - - name: Install DFX - uses: dfinity/setup-dfx@main - - - name: Install bitcoin - if: ${{ matrix.project-name == 'management_canister' }} - run: | - CWD=$(pwd) - cd "$(mktemp -d)" - BITCOIN_CORE_VERSION=22.0 - BITCOIN_CORE_FILENAME="bitcoin-$BITCOIN_CORE_VERSION-x86_64-linux-gnu.tar.gz" - BITCOIN_CORE_TARBALL_SHA="59ebd25dd82a51638b7a6bb914586201e67db67b919b2a1ff08925a7936d1b16" - wget -nv "https://bitcoin.org/bin/bitcoin-core-$BITCOIN_CORE_VERSION/$BITCOIN_CORE_FILENAME" - echo "$BITCOIN_CORE_TARBALL_SHA $BITCOIN_CORE_FILENAME" | shasum -c - tar xzf "$BITCOIN_CORE_FILENAME" - cd "bitcoin-$BITCOIN_CORE_VERSION/bin" - sudo install -m 0755 -o root -g root -t /usr/local/bin * - cd "$CWD" - - - name: Run Tests - shell: bash - run: | - ./examples/bats/bats-core/bin/bats -r examples/${{ matrix.project-name }} - env: - RUST_BACKTRACE: 1 - - - name: cargo fmt # no clippy because build.rs using ic-cdk-bindgen would fail - run: | - pushd examples/${{ matrix.project-name }} - cargo fmt --check - popd - - aggregate: - name: examples:required - if: ${{ always() }} - needs: test - runs-on: ubuntu-24.04 - steps: - - name: Check step result directly - if: ${{ needs.test.result != 'success' }} - run: exit 1 diff --git a/.github/workflows/release-candid-extractor.yml b/.github/workflows/release-candid-extractor.yml deleted file mode 100644 index 621a263f5..000000000 --- a/.github/workflows/release-candid-extractor.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: Release candid-extractor -on: - push: - tags: - - "candid-extractor-*" - -jobs: - release: - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - include: - - os: ubuntu-24.04 - binstall_pkg: candid-extractor-x86_64-unknown-linux-gnu.tar.gz - - os: macos-12 - binstall_pkg: candid-extractor-x86_64-apple-darwin.tar.gz - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Cache - uses: actions/cache@v4 - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - target/ - key: ${{ runner.os }}-candid-extractor-${{ hashFiles('Cargo.lock', 'rust-toolchain.toml') }} - restore-keys: | - ${{ runner.os }}-candid-extractor- - ${{ runner.os }}- - - name: Build - run: | - cargo build -p candid-extractor --release --locked - - - name: Bundle - run: | - cp target/release/candid-extractor . - chmod +x candid-extractor - tar -cvzf ${{ matrix.binstall_pkg }} candid-extractor - - - name: Upload release - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: ${{ matrix.binstall_pkg }} - asset_name: ${{ matrix.binstall_pkg }} - tag: ${{ github.ref }} diff --git a/.gitignore b/.gitignore index 5a787c2e5..1a89a87b0 100644 --- a/.gitignore +++ b/.gitignore @@ -8,15 +8,12 @@ target/ # DFX .dfx/ -# Include root Cargo.toml to build candid-extractor with `--locked` -# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries -# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +# The root Cargo.lock is ignored because the workspace only has library crates. +# The candid-extractor has its own Cargo.lock file which is tracked to build the binary. +Cargo.lock examples/*/Cargo.lock -# These are backup files generated by rustfmt -**/*.rs.bk -.DS_Store - +# The pocket-ic server binary installed by scripts/download_pocket_ic.sh e2e-tests/pocket-ic # Generated bindings diff --git a/.gitmodules b/.gitmodules index ef90ac626..e69de29bb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +0,0 @@ -[submodule "examples/bats/bats-core"] - path = examples/bats/bats-core - url = https://github.com/bats-core/bats-core.git -[submodule "examples/bats/bats-support"] - path = examples/bats/bats-support - url = https://github.com/bats-core/bats-support.git -[submodule "examples/bats/bats-assert"] - path = examples/bats/bats-assert - url = https://github.com/bats-core/bats-assert.git diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index 6dcb36591..000000000 --- a/Cargo.lock +++ /dev/null @@ -1,4046 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "addr2line" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" -dependencies = [ - "gimli 0.28.1", -] - -[[package]] -name = "addr2line" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" -dependencies = [ - "gimli 0.31.1", -] - -[[package]] -name = "adler2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" - -[[package]] -name = "ahash" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - -[[package]] -name = "anstream" -version = "0.6.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" - -[[package]] -name = "anstyle-parse" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" -dependencies = [ - "anstyle", - "windows-sys 0.59.0", -] - -[[package]] -name = "anyhow" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" - -[[package]] -name = "arbitrary" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" - -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - -[[package]] -name = "ascii-canvas" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" -dependencies = [ - "term", -] - -[[package]] -name = "async-trait" -version = "0.1.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.90", -] - -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - -[[package]] -name = "autocfg" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" - -[[package]] -name = "backoff" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" -dependencies = [ - "getrandom", - "instant", - "rand", -] - -[[package]] -name = "backtrace" -version = "0.3.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" -dependencies = [ - "addr2line 0.24.2", - "cfg-if", - "libc", - "miniz_oxide", - "object 0.36.5", - "rustc-demangle", - "windows-targets", -] - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "beef" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "binread" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16598dfc8e6578e9b597d9910ba2e73618385dc9f4b1d43dd92c349d6be6418f" -dependencies = [ - "binread_derive", - "lazy_static", - "rustversion", -] - -[[package]] -name = "binread_derive" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d9672209df1714ee804b1f4d4f68c8eb2a90b1f7a07acf472f88ce198ef1fed" -dependencies = [ - "either", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "bit-set" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - -[[package]] -name = "bitflags" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "bumpalo" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bytes" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" - -[[package]] -name = "camino" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" -dependencies = [ - "serde", -] - -[[package]] -name = "candid" -version = "0.10.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c30ee7f886f296b6422c0ff017e89dd4f831521dfdcc76f3f71aae1ce817222" -dependencies = [ - "anyhow", - "binread", - "byteorder", - "candid_derive", - "hex", - "ic_principal", - "leb128", - "num-bigint", - "num-traits", - "paste", - "pretty", - "serde", - "serde_bytes", - "stacker", - "thiserror 1.0.69", -] - -[[package]] -name = "candid-extractor" -version = "0.1.4" -dependencies = [ - "anyhow", - "clap", - "quote", - "syn 2.0.90", - "wasmtime", -] - -[[package]] -name = "candid_derive" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3de398570c386726e7a59d9887b68763c481477f9a043fb998a2e09d428df1a9" -dependencies = [ - "lazy_static", - "proc-macro2", - "quote", - "syn 2.0.90", -] - -[[package]] -name = "candid_parser" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48a3da76f989cd350b7342c64c6c6008341bb6186f6832ef04e56dc50ba0fd76" -dependencies = [ - "anyhow", - "candid", - "codespan-reporting", - "convert_case", - "hex", - "lalrpop", - "lalrpop-util", - "logos", - "num-bigint", - "pretty", - "thiserror 1.0.69", -] - -[[package]] -name = "cargo-platform" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo_metadata" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" -dependencies = [ - "camino", - "cargo-platform", - "semver", - "serde", - "serde_json", - "thiserror 1.0.69", -] - -[[package]] -name = "cc" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f34d93e62b03caf570cccc334cbc6c2fceca82f39211051345108adcba3eebdc" -dependencies = [ - "jobserver", - "libc", - "shlex", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - -[[package]] -name = "clap" -version = "4.5.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69371e34337c4c984bbe322360c2547210bf632eb2814bbe78a6e87a2935bd2b" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.5.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e24c1b4099818523236a8ca881d2b45db98dadfb4625cf6608c12069fcbbde1" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "syn 2.0.90", -] - -[[package]] -name = "clap_lex" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" - -[[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width 0.1.14", -] - -[[package]] -name = "colorchoice" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" - -[[package]] -name = "convert_case" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "core-foundation" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "cpp_demangle" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96e58d342ad113c2b878f16d5d034c03be492ae460cdbc02b7f0f2284d310c7d" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "cpufeatures" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" -dependencies = [ - "libc", -] - -[[package]] -name = "cranelift-bforest" -version = "0.106.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b57d4f3ffc28bbd6ef1ca7b50b20126717232f97487efe027d135d9d87eb29c" -dependencies = [ - "cranelift-entity", -] - -[[package]] -name = "cranelift-codegen" -version = "0.106.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1f7d0ac7fd53f2c29db3ff9a063f6ff5a8be2abaa8f6942aceb6e1521e70df7" -dependencies = [ - "bumpalo", - "cranelift-bforest", - "cranelift-codegen-meta", - "cranelift-codegen-shared", - "cranelift-control", - "cranelift-entity", - "cranelift-isle", - "gimli 0.28.1", - "hashbrown 0.14.5", - "log", - "regalloc2", - "smallvec", - "target-lexicon", -] - -[[package]] -name = "cranelift-codegen-meta" -version = "0.106.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b40bf21460a600178956cb7fd900a7408c6587fbb988a8063f7215361801a1da" -dependencies = [ - "cranelift-codegen-shared", -] - -[[package]] -name = "cranelift-codegen-shared" -version = "0.106.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d792ecc1243b7ebec4a7f77d9ed428ef27456eeb1f8c780587a6f5c38841be19" - -[[package]] -name = "cranelift-control" -version = "0.106.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea2808043df964b73ad7582e09afbbe06a31f3fb9db834d53e74b4e16facaeb" -dependencies = [ - "arbitrary", -] - -[[package]] -name = "cranelift-entity" -version = "0.106.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1930946836da6f514da87625cd1a0331f3908e0de454628c24a0b97b130c4d4" -dependencies = [ - "serde", - "serde_derive", -] - -[[package]] -name = "cranelift-frontend" -version = "0.106.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5482a5fcdf98f2f31b21093643bdcfe9030866b8be6481117022e7f52baa0f2b" -dependencies = [ - "cranelift-codegen", - "log", - "smallvec", - "target-lexicon", -] - -[[package]] -name = "cranelift-isle" -version = "0.106.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f6e1869b6053383bdb356900e42e33555b4c9ebee05699469b7c53cdafc82ea" - -[[package]] -name = "cranelift-native" -version = "0.106.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a91446e8045f1c4bc164b7bba68e2419c623904580d4b730877a663c6da38964" -dependencies = [ - "cranelift-codegen", - "libc", - "target-lexicon", -] - -[[package]] -name = "cranelift-wasm" -version = "0.106.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8b17979b862d3b0d52de6ae3294ffe4d86c36027b56ad0443a7c8c8f921d14f" -dependencies = [ - "cranelift-codegen", - "cranelift-entity", - "cranelift-frontend", - "itertools 0.12.1", - "log", - "smallvec", - "wasmparser 0.201.0", - "wasmtime-types", -] - -[[package]] -name = "crc32fast" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "data-encoding" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" - -[[package]] -name = "debugid" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" -dependencies = [ - "uuid", -] - -[[package]] -name = "deranged" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" -dependencies = [ - "powerfmt", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] - -[[package]] -name = "directories-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" -dependencies = [ - "cfg-if", - "dirs-sys-next", -] - -[[package]] -name = "dirs-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if", - "dirs-sys-next", -] - -[[package]] -name = "dirs-sys-next" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.90", -] - -[[package]] -name = "dyn-clone" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" - -[[package]] -name = "either" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" - -[[package]] -name = "ena" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d248bdd43ce613d87415282f69b9bb99d947d290b10962dd6c56233312c2ad5" -dependencies = [ - "log", -] - -[[package]] -name = "encoding_rs" -version = "0.8.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "erased-serde" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c138974f9d5e7fe373eb04df7cae98833802ae4b11c24ac7039a21d5af4b26c" -dependencies = [ - "serde", -] - -[[package]] -name = "errno" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" -dependencies = [ - "libc", - "windows-sys 0.59.0", -] - -[[package]] -name = "escargot" -version = "0.5.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05a3ac187a16b5382fef8c69fd1bad123c67b7cf3932240a2d43dcdd32cded88" -dependencies = [ - "log", - "once_cell", - "serde", - "serde_json", -] - -[[package]] -name = "fallible-iterator" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" - -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "form_urlencoded" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "futures" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" - -[[package]] -name = "futures-executor" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" - -[[package]] -name = "futures-macro" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.90", -] - -[[package]] -name = "futures-sink" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" - -[[package]] -name = "futures-task" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" - -[[package]] -name = "futures-util" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] - -[[package]] -name = "fxprof-processed-profile" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27d12c0aed7f1e24276a241aadc4cb8ea9f83000f34bc062b7cc2d51e3b0fabd" -dependencies = [ - "bitflags", - "debugid", - "fxhash", - "serde", - "serde_json", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] - -[[package]] -name = "gimli" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" -dependencies = [ - "fallible-iterator", - "indexmap", - "stable_deref_trait", -] - -[[package]] -name = "gimli" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" - -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - -[[package]] -name = "h2" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "half" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" - -[[package]] -name = "hashbrown" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" -dependencies = [ - "ahash", -] - -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", -] - -[[package]] -name = "hashbrown" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -dependencies = [ - "serde", -] - -[[package]] -name = "http" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" -dependencies = [ - "bytes", - "http", -] - -[[package]] -name = "http-body-util" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" -dependencies = [ - "bytes", - "futures-util", - "http", - "http-body", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" - -[[package]] -name = "hyper" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "itoa", - "pin-project-lite", - "smallvec", - "tokio", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.27.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" -dependencies = [ - "futures-util", - "http", - "hyper", - "hyper-util", - "rustls", - "rustls-native-certs", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tower-service", - "webpki-roots", -] - -[[package]] -name = "hyper-util" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "hyper", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", -] - -[[package]] -name = "ic-cdk" -version = "0.17.1" -dependencies = [ - "anyhow", - "candid", - "ic-cdk-macros", - "ic0", - "rstest", - "serde", - "serde_bytes", - "slotmap", - "trybuild", -] - -[[package]] -name = "ic-cdk-bindgen" -version = "0.1.3" -dependencies = [ - "candid", - "candid_parser", - "convert_case", - "pretty", -] - -[[package]] -name = "ic-cdk-e2e-tests" -version = "0.1.0" -dependencies = [ - "candid", - "cargo_metadata", - "escargot", - "futures", - "hex", - "ic-cdk", - "ic-cdk-timers", - "lazy_static", - "pocket-ic", - "serde_bytes", - "sha2", -] - -[[package]] -name = "ic-cdk-macros" -version = "0.17.1" -dependencies = [ - "candid", - "proc-macro2", - "quote", - "serde", - "serde_tokenstream", - "syn 2.0.90", -] - -[[package]] -name = "ic-cdk-timers" -version = "0.11.0" -dependencies = [ - "futures", - "ic-cdk", - "ic0", - "serde", - "serde_bytes", - "slotmap", -] - -[[package]] -name = "ic-certification" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64ee3d8b6e81b51f245716d3e0badb63c283c00f3c9fb5d5219afc30b5bf821" -dependencies = [ - "hex", - "serde", - "serde_bytes", - "sha2", -] - -[[package]] -name = "ic-certified-map" -version = "0.4.0" -dependencies = [ - "bincode", - "candid", - "hex", - "ic-cdk", - "serde", - "serde_bytes", - "serde_cbor", - "sha2", -] - -[[package]] -name = "ic-ledger-types" -version = "0.14.0" -dependencies = [ - "candid", - "crc32fast", - "hex", - "ic-cdk", - "serde", - "serde_bytes", - "sha2", -] - -[[package]] -name = "ic-transport-types" -version = "0.37.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875dc4704780383112e8e8b5063a1b98de114321d0c7d3e7f635dcf360a57fba" -dependencies = [ - "candid", - "hex", - "ic-certification", - "leb128", - "serde", - "serde_bytes", - "serde_repr", - "sha2", - "thiserror 1.0.69", -] - -[[package]] -name = "ic0" -version = "0.23.0" -dependencies = [ - "quote", - "syn 2.0.90", -] - -[[package]] -name = "ic_principal" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1762deb6f7c8d8c2bdee4b6c5a47b60195b74e9b5280faa5ba29692f8e17429c" -dependencies = [ - "arbitrary", - "crc32fast", - "data-encoding", - "serde", - "sha2", - "thiserror 1.0.69", -] - -[[package]] -name = "icu_collections" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locid" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" - -[[package]] -name = "icu_normalizer" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "utf16_iter", - "utf8_iter", - "write16", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" - -[[package]] -name = "icu_properties" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_locid_transform", - "icu_properties_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" - -[[package]] -name = "icu_provider" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_provider_macros", - "stable_deref_trait", - "tinystr", - "writeable", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.90", -] - -[[package]] -name = "id-arena" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" - -[[package]] -name = "idna" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "indexmap" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" -dependencies = [ - "equivalent", - "hashbrown 0.15.2", - "serde", -] - -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "ipnet" -version = "2.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" - -[[package]] -name = "is_terminal_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" - -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" - -[[package]] -name = "ittapi" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b996fe614c41395cdaedf3cf408a9534851090959d90d54a535f675550b64b1" -dependencies = [ - "anyhow", - "ittapi-sys", - "log", -] - -[[package]] -name = "ittapi-sys" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52f5385394064fa2c886205dba02598013ce83d3e92d33dbdc0c52fe0e7bf4fc" -dependencies = [ - "cc", -] - -[[package]] -name = "jobserver" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" -dependencies = [ - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a865e038f7f6ed956f788f0d7d60c541fff74c7bd74272c5d4cf15c63743e705" -dependencies = [ - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "lalrpop" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cb077ad656299f160924eb2912aa147d7339ea7d69e1b5517326fdcec3c1ca" -dependencies = [ - "ascii-canvas", - "bit-set", - "ena", - "itertools 0.11.0", - "lalrpop-util", - "petgraph", - "pico-args", - "regex", - "regex-syntax 0.8.5", - "string_cache", - "term", - "tiny-keccak", - "unicode-xid", - "walkdir", -] - -[[package]] -name = "lalrpop-util" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" -dependencies = [ - "regex-automata 0.4.9", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "leb128" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" - -[[package]] -name = "libc" -version = "0.2.167" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" - -[[package]] -name = "libredox" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" -dependencies = [ - "bitflags", - "libc", -] - -[[package]] -name = "linux-raw-sys" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" - -[[package]] -name = "litemap" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" - -[[package]] -name = "lock_api" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" - -[[package]] -name = "logos" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c000ca4d908ff18ac99b93a062cb8958d331c3220719c52e77cb19cc6ac5d2c1" -dependencies = [ - "logos-derive", -] - -[[package]] -name = "logos-codegen" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc487311295e0002e452025d6b580b77bb17286de87b57138f3b5db711cded68" -dependencies = [ - "beef", - "fnv", - "proc-macro2", - "quote", - "regex-syntax 0.6.29", - "syn 2.0.90", -] - -[[package]] -name = "logos-derive" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbfc0d229f1f42d790440136d941afd806bc9e949e2bcb8faa813b0f00d1267e" -dependencies = [ - "logos-codegen", -] - -[[package]] -name = "mach" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" -dependencies = [ - "libc", -] - -[[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata 0.1.10", -] - -[[package]] -name = "memchr" -version = "2.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" - -[[package]] -name = "memfd" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" -dependencies = [ - "rustix", -] - -[[package]] -name = "memoffset" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" -dependencies = [ - "autocfg", -] - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "mime_guess" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" -dependencies = [ - "mime", - "unicase", -] - -[[package]] -name = "miniz_oxide" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" -dependencies = [ - "adler2", -] - -[[package]] -name = "mio" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" -dependencies = [ - "libc", - "wasi", - "windows-sys 0.52.0", -] - -[[package]] -name = "new_debug_unreachable" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" - -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - -[[package]] -name = "num-bigint" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" -dependencies = [ - "num-integer", - "num-traits", - "serde", -] - -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - -[[package]] -name = "object" -version = "0.32.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" -dependencies = [ - "crc32fast", - "hashbrown 0.14.5", - "indexmap", - "memchr", -] - -[[package]] -name = "object" -version = "0.36.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - -[[package]] -name = "parking_lot" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets", -] - -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - -[[package]] -name = "percent-encoding" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" - -[[package]] -name = "petgraph" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" -dependencies = [ - "fixedbitset", - "indexmap", -] - -[[package]] -name = "phf_shared" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" -dependencies = [ - "siphasher", -] - -[[package]] -name = "pico-args" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" - -[[package]] -name = "pin-project-lite" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkg-config" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" - -[[package]] -name = "pocket-ic" -version = "6.0.0" -source = "git+https://github.com/dfinity/ic?tag=release-2024-11-28_03-15-base#2d8611eb4efa8e69c4dd567546c1c353a545e0a6" -dependencies = [ - "backoff", - "base64 0.13.1", - "candid", - "hex", - "ic-certification", - "ic-transport-types", - "reqwest", - "schemars", - "serde", - "serde_bytes", - "serde_cbor", - "serde_json", - "sha2", - "slog", - "strum", - "strum_macros", - "thiserror 2.0.4", - "tokio", - "tracing", - "tracing-appender", - "tracing-subscriber", - "wslpath", -] - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "ppv-lite86" -version = "0.2.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "precomputed-hash" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" - -[[package]] -name = "pretty" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b55c4d17d994b637e2f4daf6e5dc5d660d209d5642377d675d7a1c3ab69fa579" -dependencies = [ - "arrayvec", - "typed-arena", - "unicode-width 0.1.14", -] - -[[package]] -name = "proc-macro2" -version = "1.0.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "psm" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200b9ff220857e53e184257720a14553b2f4aa02577d2ed9842d45d4b9654810" -dependencies = [ - "cc", -] - -[[package]] -name = "quinn" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef" -dependencies = [ - "bytes", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash 2.1.0", - "rustls", - "socket2", - "thiserror 2.0.4", - "tokio", - "tracing", -] - -[[package]] -name = "quinn-proto" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d" -dependencies = [ - "bytes", - "getrandom", - "rand", - "ring", - "rustc-hash 2.1.0", - "rustls", - "rustls-pki-types", - "slab", - "thiserror 2.0.4", - "tinyvec", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-udp" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5a626c6807713b15cac82a6acaccd6043c9a5408c24baae07611fec3f243da" -dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2", - "tracing", - "windows-sys 0.59.0", -] - -[[package]] -name = "quote" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rayon" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - -[[package]] -name = "redox_syscall" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" -dependencies = [ - "bitflags", -] - -[[package]] -name = "redox_users" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" -dependencies = [ - "getrandom", - "libredox", - "thiserror 1.0.69", -] - -[[package]] -name = "regalloc2" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad156d539c879b7a24a363a2016d77961786e71f48f2e2fc8302a92abd2429a6" -dependencies = [ - "hashbrown 0.13.2", - "log", - "rustc-hash 1.1.0", - "slice-group-by", - "smallvec", -] - -[[package]] -name = "regex" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata 0.4.9", - "regex-syntax 0.8.5", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", -] - -[[package]] -name = "regex-automata" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax 0.8.5", -] - -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" - -[[package]] -name = "reqwest" -version = "0.12.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" -dependencies = [ - "base64 0.22.1", - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-util", - "ipnet", - "js-sys", - "log", - "mime", - "mime_guess", - "once_cell", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls", - "rustls-native-certs", - "rustls-pemfile", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tokio-rustls", - "tokio-socks", - "tokio-util", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams", - "web-sys", - "webpki-roots", - "windows-registry", -] - -[[package]] -name = "ring" -version = "0.17.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" -dependencies = [ - "cc", - "cfg-if", - "getrandom", - "libc", - "spin", - "untrusted", - "windows-sys 0.52.0", -] - -[[package]] -name = "rstest" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d912f35156a3f99a66ee3e11ac2e0b3f34ac85a07e05263d05a7e2c8810d616f" -dependencies = [ - "cfg-if", - "proc-macro2", - "quote", - "rustc_version", - "syn 1.0.109", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "rustc-hash" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "0.38.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustls" -version = "0.23.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1" -dependencies = [ - "once_cell", - "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-native-certs" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" -dependencies = [ - "openssl-probe", - "rustls-pki-types", - "schannel", - "security-framework", -] - -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "rustls-pki-types" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" -dependencies = [ - "web-time", -] - -[[package]] -name = "rustls-webpki" -version = "0.102.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", -] - -[[package]] -name = "rustversion" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" - -[[package]] -name = "ryu" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "schannel" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "schemars" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92" -dependencies = [ - "dyn-clone", - "schemars_derive", - "serde", - "serde_json", -] - -[[package]] -name = "schemars_derive" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e" -dependencies = [ - "proc-macro2", - "quote", - "serde_derive_internals", - "syn 2.0.90", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "security-framework" -version = "3.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1415a607e92bec364ea2cf9264646dcce0f91e6d65281bd6f2819cca3bf39c8" -dependencies = [ - "bitflags", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" -dependencies = [ - "serde", -] - -[[package]] -name = "serde" -version = "1.0.215" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_bytes" -version = "0.11.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_cbor" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" -dependencies = [ - "half", - "serde", -] - -[[package]] -name = "serde_derive" -version = "1.0.215" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.90", -] - -[[package]] -name = "serde_derive_internals" -version = "0.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.90", -] - -[[package]] -name = "serde_json" -version = "1.0.133" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" -dependencies = [ - "itoa", - "memchr", - "ryu", - "serde", -] - -[[package]] -name = "serde_repr" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.90", -] - -[[package]] -name = "serde_spanned" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" -dependencies = [ - "serde", -] - -[[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.90", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sha2" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "signal-hook-registry" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" -dependencies = [ - "libc", -] - -[[package]] -name = "siphasher" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" - -[[package]] -name = "slab" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] - -[[package]] -name = "slice-group-by" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" - -[[package]] -name = "slog" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8347046d4ebd943127157b94d63abb990fcf729dc4e9978927fdf4ac3c998d06" -dependencies = [ - "erased-serde", -] - -[[package]] -name = "slotmap" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" -dependencies = [ - "version_check", -] - -[[package]] -name = "smallvec" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" - -[[package]] -name = "socket2" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - -[[package]] -name = "sptr" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a" - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "stacker" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799c883d55abdb5e98af1a7b3f23b9b6de8ecada0ecac058672d7635eb48ca7b" -dependencies = [ - "cc", - "cfg-if", - "libc", - "psm", - "windows-sys 0.59.0", -] - -[[package]] -name = "string_cache" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" -dependencies = [ - "new_debug_unreachable", - "once_cell", - "parking_lot", - "phf_shared", - "precomputed-hash", -] - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "strum" -version = "0.26.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.90", -] - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.90" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "sync_wrapper" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" -dependencies = [ - "futures-core", -] - -[[package]] -name = "synstructure" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.90", -] - -[[package]] -name = "target-lexicon" -version = "0.12.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" - -[[package]] -name = "target-triple" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42a4d50cdb458045afc8131fd91b64904da29548bcb63c7236e0844936c13078" - -[[package]] -name = "term" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" -dependencies = [ - "dirs-next", - "rustversion", - "winapi", -] - -[[package]] -name = "termcolor" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f49a1853cf82743e3b7950f77e0f4d622ca36cf4317cba00c767838bac8d490" -dependencies = [ - "thiserror-impl 2.0.4", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.90", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8381894bb3efe0c4acac3ded651301ceee58a15d47c2e34885ed1908ad667061" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.90", -] - -[[package]] -name = "thread_local" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" -dependencies = [ - "cfg-if", - "once_cell", -] - -[[package]] -name = "time" -version = "0.3.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" - -[[package]] -name = "time-macros" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - -[[package]] -name = "tinystr" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tinyvec" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" -dependencies = [ - "backtrace", - "bytes", - "libc", - "mio", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.52.0", -] - -[[package]] -name = "tokio-macros" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.90", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" -dependencies = [ - "rustls", - "rustls-pki-types", - "tokio", -] - -[[package]] -name = "tokio-socks" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f" -dependencies = [ - "either", - "futures-util", - "thiserror 1.0.69", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "toml" -version = "0.8.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - -[[package]] -name = "toml_datetime" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.22.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" -dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime", - "winnow", -] - -[[package]] -name = "tower-service" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" - -[[package]] -name = "tracing" -version = "0.1.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" -dependencies = [ - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-appender" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" -dependencies = [ - "crossbeam-channel", - "thiserror 1.0.69", - "time", - "tracing-subscriber", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.90", -] - -[[package]] -name = "tracing-core" -version = "0.1.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-serde" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1" -dependencies = [ - "serde", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex", - "serde", - "serde_json", - "sharded-slab", - "smallvec", - "thread_local", - "time", - "tracing", - "tracing-core", - "tracing-log", - "tracing-serde", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - -[[package]] -name = "trybuild" -version = "1.0.101" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dcd332a5496c026f1e14b7f3d2b7bd98e509660c04239c58b0ba38a12daded4" -dependencies = [ - "glob", - "serde", - "serde_derive", - "serde_json", - "target-triple", - "termcolor", - "toml", -] - -[[package]] -name = "typed-arena" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" - -[[package]] -name = "typenum" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" - -[[package]] -name = "unicase" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" - -[[package]] -name = "unicode-ident" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" - -[[package]] -name = "unicode-segmentation" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" - -[[package]] -name = "unicode-width" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" - -[[package]] -name = "unicode-width" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" - -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "url" -version = "2.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] - -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - -[[package]] -name = "uuid" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" - -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.97" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d15e63b4482863c109d70a7b8706c1e364eb6ea449b201a76c5b89cedcec2d5c" -dependencies = [ - "cfg-if", - "once_cell", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.97" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d36ef12e3aaca16ddd3f67922bc63e48e953f126de60bd33ccc0101ef9998cd" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.90", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dfaf8f50e5f293737ee323940c7d8b08a66a95a419223d9f41610ca08b0833d" -dependencies = [ - "cfg-if", - "js-sys", - "once_cell", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.97" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "705440e08b42d3e4b36de7d66c944be628d579796b8090bfa3471478a2260051" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.97" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98c9ae5a76e46f4deecd0f0255cc223cfa18dc9b261213b8aa0c7b36f61b3f1d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.90", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.97" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ee99da9c5ba11bd675621338ef6fa52296b76b83305e9b6e5c77d4c286d6d49" - -[[package]] -name = "wasm-encoder" -version = "0.201.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9c7d2731df60006819b013f64ccc2019691deccf6e11a1804bc850cd6748f1a" -dependencies = [ - "leb128", -] - -[[package]] -name = "wasm-encoder" -version = "0.221.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17a3bd88f2155da63a1f2fcb8a56377a24f0b6dfed12733bb5f544e86f690c5" -dependencies = [ - "leb128", - "wasmparser 0.221.2", -] - -[[package]] -name = "wasm-streams" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" -dependencies = [ - "futures-util", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "wasmparser" -version = "0.201.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84e5df6dba6c0d7fafc63a450f1738451ed7a0b52295d83e868218fa286bf708" -dependencies = [ - "bitflags", - "indexmap", - "semver", -] - -[[package]] -name = "wasmparser" -version = "0.221.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9845c470a2e10b61dd42c385839cdd6496363ed63b5c9e420b5488b77bd22083" -dependencies = [ - "bitflags", - "indexmap", - "semver", -] - -[[package]] -name = "wasmprinter" -version = "0.201.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a67e66da702706ba08729a78e3c0079085f6bfcb1a62e4799e97bbf728c2c265" -dependencies = [ - "anyhow", - "wasmparser 0.201.0", -] - -[[package]] -name = "wasmtime" -version = "19.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e300c0e3f19dc9064e3b17ce661088646c70dbdde36aab46470ed68ba58db7d" -dependencies = [ - "addr2line 0.21.0", - "anyhow", - "async-trait", - "bincode", - "bumpalo", - "cfg-if", - "encoding_rs", - "fxprof-processed-profile", - "gimli 0.28.1", - "indexmap", - "ittapi", - "libc", - "log", - "object 0.32.2", - "once_cell", - "paste", - "rayon", - "rustix", - "semver", - "serde", - "serde_derive", - "serde_json", - "target-lexicon", - "wasm-encoder 0.201.0", - "wasmparser 0.201.0", - "wasmtime-cache", - "wasmtime-component-macro", - "wasmtime-component-util", - "wasmtime-cranelift", - "wasmtime-environ", - "wasmtime-fiber", - "wasmtime-jit-debug", - "wasmtime-jit-icache-coherence", - "wasmtime-runtime", - "wasmtime-slab", - "wasmtime-winch", - "wat", - "windows-sys 0.52.0", -] - -[[package]] -name = "wasmtime-asm-macros" -version = "19.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "110aa598e02a136fb095ca70fa96367fc16bab55256a131e66f9b58f16c73daf" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "wasmtime-cache" -version = "19.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4e660537b0ac2fc76917fb0cc9d403d2448b6983a84e59c51f7fea7b7dae024" -dependencies = [ - "anyhow", - "base64 0.21.7", - "bincode", - "directories-next", - "log", - "rustix", - "serde", - "serde_derive", - "sha2", - "toml", - "windows-sys 0.52.0", - "zstd", -] - -[[package]] -name = "wasmtime-component-macro" -version = "19.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "091f32ce586251ac4d07019388fb665b010d9518ffe47be1ddbabb162eed6007" -dependencies = [ - "anyhow", - "proc-macro2", - "quote", - "syn 2.0.90", - "wasmtime-component-util", - "wasmtime-wit-bindgen", - "wit-parser", -] - -[[package]] -name = "wasmtime-component-util" -version = "19.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd17dc1ebc0b28fd24b6b9d07638f55b82ae908918ff08fd221f8b0fefa9125" - -[[package]] -name = "wasmtime-cranelift" -version = "19.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e923262451a4b5b39fe02f69f1338d56356db470e289ea1887346b9c7f592738" -dependencies = [ - "anyhow", - "cfg-if", - "cranelift-codegen", - "cranelift-control", - "cranelift-entity", - "cranelift-frontend", - "cranelift-native", - "cranelift-wasm", - "gimli 0.28.1", - "log", - "object 0.32.2", - "target-lexicon", - "thiserror 1.0.69", - "wasmparser 0.201.0", - "wasmtime-cranelift-shared", - "wasmtime-environ", - "wasmtime-versioned-export-macros", -] - -[[package]] -name = "wasmtime-cranelift-shared" -version = "19.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "508898cbbea0df81a5d29cfc1c7c72431a1bc4c9e89fd9514b4c868474c05c7a" -dependencies = [ - "anyhow", - "cranelift-codegen", - "cranelift-control", - "cranelift-native", - "gimli 0.28.1", - "object 0.32.2", - "target-lexicon", - "wasmtime-environ", -] - -[[package]] -name = "wasmtime-environ" -version = "19.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7e3f2aa72dbb64c19708646e1ff97650f34e254598b82bad5578ea9c80edd30" -dependencies = [ - "anyhow", - "bincode", - "cpp_demangle", - "cranelift-entity", - "gimli 0.28.1", - "indexmap", - "log", - "object 0.32.2", - "rustc-demangle", - "serde", - "serde_derive", - "target-lexicon", - "thiserror 1.0.69", - "wasm-encoder 0.201.0", - "wasmparser 0.201.0", - "wasmprinter", - "wasmtime-component-util", - "wasmtime-types", -] - -[[package]] -name = "wasmtime-fiber" -version = "19.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9235b643527bcbac808216ed342e1fba324c95f14a62762acfa6f2e6ca5edbd6" -dependencies = [ - "anyhow", - "cc", - "cfg-if", - "rustix", - "wasmtime-asm-macros", - "wasmtime-versioned-export-macros", - "windows-sys 0.52.0", -] - -[[package]] -name = "wasmtime-jit-debug" -version = "19.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92de34217bf7f0464262adf391a9950eba440f9dfc7d3b0e3209302875c6f65f" -dependencies = [ - "object 0.32.2", - "once_cell", - "rustix", - "wasmtime-versioned-export-macros", -] - -[[package]] -name = "wasmtime-jit-icache-coherence" -version = "19.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c22ca2ef4d87b23d400660373453e274b2251bc2d674e3102497f690135e04b0" -dependencies = [ - "cfg-if", - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "wasmtime-runtime" -version = "19.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1806ee242ca4fd183309b7406e4e83ae7739b7569f395d56700de7c7ef9f5eb8" -dependencies = [ - "anyhow", - "cc", - "cfg-if", - "encoding_rs", - "indexmap", - "libc", - "log", - "mach", - "memfd", - "memoffset", - "paste", - "psm", - "rustix", - "sptr", - "wasm-encoder 0.201.0", - "wasmtime-asm-macros", - "wasmtime-environ", - "wasmtime-fiber", - "wasmtime-jit-debug", - "wasmtime-versioned-export-macros", - "wasmtime-wmemcheck", - "windows-sys 0.52.0", -] - -[[package]] -name = "wasmtime-slab" -version = "19.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20c58bef9ce877fd06acb58f08d003af17cb05cc51225b455e999fbad8e584c0" - -[[package]] -name = "wasmtime-types" -version = "19.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cebe297aa063136d9d2e5b347c1528868aa43c2c8d0e1eb0eec144567e38fe0f" -dependencies = [ - "cranelift-entity", - "serde", - "serde_derive", - "thiserror 1.0.69", - "wasmparser 0.201.0", -] - -[[package]] -name = "wasmtime-versioned-export-macros" -version = "19.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffaafa5c12355b1a9ee068e9295d50c4ca0a400c721950cdae4f5b54391a2da5" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.90", -] - -[[package]] -name = "wasmtime-winch" -version = "19.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d618b4e90d3f259b1b77411ce573c9f74aade561957102132e169918aabdc863" -dependencies = [ - "anyhow", - "cranelift-codegen", - "gimli 0.28.1", - "object 0.32.2", - "target-lexicon", - "wasmparser 0.201.0", - "wasmtime-cranelift-shared", - "wasmtime-environ", - "winch-codegen", -] - -[[package]] -name = "wasmtime-wit-bindgen" -version = "19.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c7a253c8505edd7493603e548bff3af937b0b7dbf2b498bd5ff2131b651af72" -dependencies = [ - "anyhow", - "heck 0.4.1", - "indexmap", - "wit-parser", -] - -[[package]] -name = "wasmtime-wmemcheck" -version = "19.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9a8c62e9df8322b2166d2a6f096fbec195ddb093748fd74170dcf25ef596769" - -[[package]] -name = "wast" -version = "221.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc4470b9de917ba199157d1f0ae104f2ae362be728c43e68c571c7715bd629e" -dependencies = [ - "bumpalo", - "leb128", - "memchr", - "unicode-width 0.2.0", - "wasm-encoder 0.221.2", -] - -[[package]] -name = "wat" -version = "1.221.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b1f3c6d82af47286494c6caea1d332037f5cbeeac82bbf5ef59cb8c201c466e" -dependencies = [ - "wast", -] - -[[package]] -name = "web-sys" -version = "0.3.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a98bc3c33f0fe7e59ad7cd041b89034fa82a7c2d4365ca538dda6cdaf513863c" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "web-time" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki-roots" -version = "0.26.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "winch-codegen" -version = "0.17.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d15869abc9e3bb29c017c003dbe007a08e9910e8ff9023a962aa13c1b2ee6af" -dependencies = [ - "anyhow", - "cranelift-codegen", - "gimli 0.28.1", - "regalloc2", - "smallvec", - "target-lexicon", - "wasmparser 0.201.0", - "wasmtime-environ", -] - -[[package]] -name = "windows-registry" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" -dependencies = [ - "windows-result", - "windows-strings", - "windows-targets", -] - -[[package]] -name = "windows-result" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-strings" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" -dependencies = [ - "windows-result", - "windows-targets", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "winnow" -version = "0.6.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" -dependencies = [ - "memchr", -] - -[[package]] -name = "wit-parser" -version = "0.201.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "196d3ecfc4b759a8573bf86a9b3f8996b304b3732e4c7de81655f875f6efdca6" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser 0.201.0", -] - -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - -[[package]] -name = "writeable" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" - -[[package]] -name = "wslpath" -version = "0.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04a2ecdf2cc4d33a6a93d71bcfbc00bb1f635cdb8029a2cc0709204a045ec7a3" - -[[package]] -name = "yoke" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" -dependencies = [ - "serde", - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.90", - "synstructure", -] - -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "byteorder", - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.90", -] - -[[package]] -name = "zerofrom" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.90", - "synstructure", -] - -[[package]] -name = "zeroize" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" - -[[package]] -name = "zerovec" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.90", -] - -[[package]] -name = "zstd" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" -dependencies = [ - "zstd-safe", -] - -[[package]] -name = "zstd-safe" -version = "7.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" -dependencies = [ - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "2.0.13+zstd.1.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" -dependencies = [ - "cc", - "pkg-config", -] diff --git a/Cargo.toml b/Cargo.toml index b62810485..9c83ffd22 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,15 @@ [workspace] -members = ["src/*", "library/*", "e2e-tests"] +members = [ + "ic-cdk", + "ic-cdk-bindgen", + "ic-cdk-macros", + "ic-cdk-timers", + "ic0", + "library/*", + "e2e-tests", +] resolver = "2" +exclude = ["candid-extractor"] [workspace.package] authors = ["DFINITY Stiftung "] @@ -20,9 +29,9 @@ lto = true opt-level = 'z' [workspace.dependencies] -ic0 = { path = "src/ic0", version = "0.23.0" } -ic-cdk = { path = "src/ic-cdk", version = "0.17.1" } -ic-cdk-timers = { path = "src/ic-cdk-timers", version = "0.11.0" } +ic0 = { path = "ic0", version = "0.24.0-alpha.1" } +ic-cdk = { path = "ic-cdk", version = "0.18.0-alpha.1" } +ic-cdk-timers = { path = "ic-cdk-timers", version = "0.12.0-alpha.1" } candid = "0.10.4" candid_parser = "0.1.4" diff --git a/docs/antora.yml b/docs/antora.yml deleted file mode 100644 index 6f27a6f84..000000000 --- a/docs/antora.yml +++ /dev/null @@ -1,2 +0,0 @@ -name: docs -version: 'master' \ No newline at end of file diff --git a/docs/modules/rust-guide/examples/counter-tutorial/counter.did b/docs/modules/rust-guide/examples/counter-tutorial/counter.did deleted file mode 100644 index 478ee08b3..000000000 --- a/docs/modules/rust-guide/examples/counter-tutorial/counter.did +++ /dev/null @@ -1,5 +0,0 @@ -service : { - "increment": () -> (); - "get": () -> (nat) query; - "set": (nat) -> (); -} diff --git a/docs/modules/rust-guide/examples/counter-tutorial/counter.rs b/docs/modules/rust-guide/examples/counter-tutorial/counter.rs deleted file mode 100644 index cbfa47f8e..000000000 --- a/docs/modules/rust-guide/examples/counter-tutorial/counter.rs +++ /dev/null @@ -1,29 +0,0 @@ -use ic_cdk::{export::candid, init, query, update}; - -static mut COUNTER: Option = None; - -#[init] -fn init() { - unsafe { - COUNTER = Some(candid::Nat::from(0)); - } -} - -#[update] -fn increment() -> () { - unsafe { - COUNTER.as_mut().unwrap().0 += 1u64; - } -} - -#[query] -fn get() -> candid::Nat { - unsafe { COUNTER.as_mut().unwrap().clone() } -} - -#[update] -fn set(input: candid::Nat) -> () { - unsafe { - COUNTER.as_mut().unwrap().0 = input.0; - } -} diff --git a/docs/modules/rust-guide/examples/hello-dfx.json b/docs/modules/rust-guide/examples/hello-dfx.json deleted file mode 100644 index c10d3b5cb..000000000 --- a/docs/modules/rust-guide/examples/hello-dfx.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "canisters": { - "rust_hello": { - "candid": "src/rust_hello/rust_hello.did", - "package": "rust_hello", - "type": "rust" - }, - "rust_hello_assets": { - "dependencies": [ - "rust_hello" - ], - "frontend": { - "entrypoint": "src/rust_hello_assets/src/index.html" - }, - "source": [ - "src/rust_hello_assets/assets", - "dist/rust_hello_assets/" - ], - "type": "assets" - } - }, - "defaults": { - "build": { - "args": "", - "packtool": "" - } - }, - "networks": { - "local": { - "bind": "127.0.0.1:8000", - "type": "ephemeral" - } - }, - "version": 1 -} \ No newline at end of file diff --git a/docs/modules/rust-guide/examples/intercanister-tutorial/profile-interoperation.rs b/docs/modules/rust-guide/examples/intercanister-tutorial/profile-interoperation.rs deleted file mode 100644 index 09973919e..000000000 --- a/docs/modules/rust-guide/examples/intercanister-tutorial/profile-interoperation.rs +++ /dev/null @@ -1,24 +0,0 @@ -use ic_cdk::{import, update}; - -#[import(canister = "rust_profile")] -struct ProfileCanister; - -#[update(name = "getSelf")] -async fn get_self() -> Box { - ProfileCanister::getSelf().await -} - -#[update] -async fn get(name: String) -> Box { - ProfileCanister::get(name).await -} - -#[update] -async fn update(profile: Profile) { - ProfileCanister::update(profile).await -} - -#[update] -async fn search(text: String) -> Option> { - ProfileCanister::search(text).await -} diff --git a/docs/modules/rust-guide/examples/intercanister-tutorial/profile_inter.did b/docs/modules/rust-guide/examples/intercanister-tutorial/profile_inter.did deleted file mode 100644 index 7c01cd5a0..000000000 --- a/docs/modules/rust-guide/examples/intercanister-tutorial/profile_inter.did +++ /dev/null @@ -1,13 +0,0 @@ -type Profile_2 = record { - "name": text; - "description": text; - "keywords": vec text; -}; -type Profile = Profile_2; - -service : { - "getSelf": () -> (Profile_2); - "get": (text) -> (Profile_2); - "update": (Profile_2) -> (); - "search": (text) -> (opt Profile_2); -} \ No newline at end of file diff --git a/docs/modules/rust-guide/examples/intercanister-tutorial/rust_profile2_dfx.json b/docs/modules/rust-guide/examples/intercanister-tutorial/rust_profile2_dfx.json deleted file mode 100644 index c9d1d569f..000000000 --- a/docs/modules/rust-guide/examples/intercanister-tutorial/rust_profile2_dfx.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "canisters": { - "rust_profile": { - "type": "custom", - "candid": "src/rust_profile/profile.did", - "wasm": "target/wasm32-unknown-unknown/debug/rust_profile.wasm", - "build": "cargo build --target wasm32-unknown-unknown --package rust_profile" - }, - "profile_interoperation": { - "type": "custom", - "candid": "src/profile_interoperation/profile_inter.did", - "wasm": "target/wasm32-unknown-unknown/debug/profile_interoperation.wasm", - "build": "cargo build --target wasm32-unknown-unknown --package profile_interoperation", - "dependencies": [ - "rust_profile" - ] - } - }, - "defaults": { - "build": { - "packtool": "" - } - }, - "networks": { - "local": { - "bind": "127.0.0.1:8000", - "type": "ephemeral" - } - }, - "version": 1 -} \ No newline at end of file diff --git a/docs/modules/rust-guide/examples/mul-deps/deps-main.rs b/docs/modules/rust-guide/examples/mul-deps/deps-main.rs deleted file mode 100644 index 34f2dc9a8..000000000 --- a/docs/modules/rust-guide/examples/mul-deps/deps-main.rs +++ /dev/null @@ -1,9 +0,0 @@ -use ic_cdk::{import, update, export::candid}; - -#[import(canister = "multiply_deps")] -struct CounterCanister; - -#[update] -async fn read() -> candid::Nat { - CounterCanister::read().await.0 -} diff --git a/docs/modules/rust-guide/examples/mul-deps/mul-deps-dfx.json b/docs/modules/rust-guide/examples/mul-deps/mul-deps-dfx.json deleted file mode 100644 index 1f2e10595..000000000 --- a/docs/modules/rust-guide/examples/mul-deps/mul-deps-dfx.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "canisters": { - "multiply_deps": { - "main": "src/multiply_deps/main.mo", - "type": "motoko" - }, - "rust_deps": { - "candid": "src/rust_deps/rust_deps.did", - "package": "rust_deps", - "type": "rust", - "dependencies": [ - "multiply_deps" - ] - } - }, - "networks": { - "local": { - "bind": "127.0.0.1:8000", - "type": "ephemeral" - } - }, - "version": 1 -} \ No newline at end of file diff --git a/docs/modules/rust-guide/examples/mul-deps/mul-deps.mo b/docs/modules/rust-guide/examples/mul-deps/mul-deps.mo deleted file mode 100644 index d427e71f0..000000000 --- a/docs/modules/rust-guide/examples/mul-deps/mul-deps.mo +++ /dev/null @@ -1,11 +0,0 @@ -actor Multiply { - - var cell : Nat = 1; - - public func mul(n:Nat) : async Nat { cell *= n*3; cell }; - - public query func read() : async Nat { - cell - }; -} - diff --git a/docs/modules/rust-guide/examples/profile-tutorial/profile.did b/docs/modules/rust-guide/examples/profile-tutorial/profile.did deleted file mode 100644 index e4e3eb05d..000000000 --- a/docs/modules/rust-guide/examples/profile-tutorial/profile.did +++ /dev/null @@ -1,13 +0,0 @@ -type Profile_2 = record { - "name": text; - "description": text; - "keywords": vec text; -}; -type Profile = Profile_2; - -service : { - "getSelf": () -> (Profile_2) query; - "get": (text) -> (Profile_2) query; - "update": (Profile_2) -> (); - "search": (text) -> (opt Profile_2) query; -} diff --git a/docs/modules/rust-guide/examples/profile-tutorial/profile.rs b/docs/modules/rust-guide/examples/profile-tutorial/profile.rs deleted file mode 100644 index a0e98132a..000000000 --- a/docs/modules/rust-guide/examples/profile-tutorial/profile.rs +++ /dev/null @@ -1,84 +0,0 @@ -use ic_cdk::{ - call::{self, ManualReply}, - export::{ - candid::{CandidType, Deserialize}, - Principal, - }, - query, update, -}; -use std::cell::RefCell; -use std::collections::BTreeMap; - -type IdStore = BTreeMap; -type ProfileStore = BTreeMap; - -#[derive(Clone, Debug, Default, CandidType, Deserialize)] -struct Profile { - pub name: String, - pub description: String, - pub keywords: Vec, -} - -thread_local! { - static PROFILE_STORE: RefCell = RefCell::default(); - static ID_STORE: RefCell = RefCell::default(); -} - -#[query(name = "getSelf")] -fn get_self() -> Profile { - let id = ic_cdk::api::caller(); - PROFILE_STORE.with(|profile_store| { - profile_store - .borrow() - .get(&id) - .cloned() - .unwrap_or_else(|| Profile::default()) - }) -} - -#[query] -fn get(name: String) -> Profile { - ID_STORE.with(|id_store| { - PROFILE_STORE.with(|profile_store| { - id_store - .borrow() - .get(&name) - .and_then(|id| profile_store.borrow().get(id).cloned()) - .unwrap_or_else(|| Profile::default()) - }) - }) -} - -#[update] -fn update(profile: Profile) { - let principal_id = ic_cdk::api::caller(); - ID_STORE.with(|id_store| { - id_store - .borrow_mut() - .insert(profile.name.clone(), principal_id); - }); - PROFILE_STORE.with(|profile_store| { - profile_store.borrow_mut().insert(principal_id, profile); - }); -} - -#[query(manual_reply = true)] -fn search(text: String) -> ManualReply> { - let text = text.to_lowercase(); - PROFILE_STORE.with(|profile_store| { - for (_, p) in profile_store.borrow().iter() { - if p.name.to_lowercase().contains(&text) || p.description.to_lowercase().contains(&text) - { - return ManualReply::one(Some(p)); - } - - for x in p.keywords.iter() { - if x.to_lowercase() == text { - return ManualReply::one(Some(p)); - } - } - } - }); - - ManualReply::one(None::) -} diff --git a/docs/modules/rust-guide/examples/rust_counter_dfx.json b/docs/modules/rust-guide/examples/rust_counter_dfx.json deleted file mode 100644 index 9afa20bf1..000000000 --- a/docs/modules/rust-guide/examples/rust_counter_dfx.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "canisters": { - "rust_counter": { - "build": "cargo build --target wasm32-unknown-unknown --package rust_counter --release", - "candid": "src/rust_counter/src/counter.did", - "wasm": "target/wasm32-unknown-unknown/release/rust_counter.wasm", - "type": "custom" - } - }, - "networks": { - "local": { - "bind": "127.0.0.1:8000", - "type": "ephemeral" - } - }, - "version": 1 -} diff --git a/docs/modules/rust-guide/images/Rust-building-blocks.svg b/docs/modules/rust-guide/images/Rust-building-blocks.svg deleted file mode 100644 index e8d8af2e6..000000000 --- a/docs/modules/rust-guide/images/Rust-building-blocks.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/docs/modules/rust-guide/pages/multiply-dependency.adoc b/docs/modules/rust-guide/pages/multiply-dependency.adoc deleted file mode 100644 index bf9158c50..000000000 --- a/docs/modules/rust-guide/pages/multiply-dependency.adoc +++ /dev/null @@ -1,316 +0,0 @@ -= Basic dependency -ifdef::env-github,env-browser[:outfilesuffix:.adoc] -:toc-title: TUTORIAL - SIMPLE DEPENDENCY -:toclevels: 3 -:native: Motoko -:lang: Rust -:platform: Internet Computer platform -:LEE: local canister execution environment -:IC: Internet Computer blockchain -:company-id: DFINITY -:sdk-short-name: DFINITY Canister SDK -:cdk-short-name: DFINITY Rust CDK -:sdk-long-name: DFINITY Canister Software Development Kit (SDK) -:cdk-long-name: DFINITY Canister Development Kit (CDK) for Rust - -One common approach to dapp design is to calculate or store data in one canister smart contract – or canister for short – that you can then use in another canister. -This ability to share and use functions defined in different canister smart contracts, even if the underlying smart contracts are written in different languages, is an important strategy for building dapps to run the {IC}. -This tutorial provides a basic introduction to how you can write functions in one language—in the example, {native}—then use the data in another—in this case, {lang}. - -For this tutorial, both canister smart contracts are in the same project. - -* The {native} canister creates an actor with a `+cell+` variable to contain the current value that results from an operation. -* The `+mul+` function takes a natural number as input, multiplies the input value by three and stores the result in the `+cell+` variable. -* The {lang} canister provides a simple `+read+` function that returns the current value of the `+cell+` variable. Notice that the function making inter-canister call should be a `+update+` method even though the method to be called is `+query+`. - -== Before you begin - -Before you start your project, verify the following: - -* You have an internet connection and access to a shell terminal on your local macOS or Linux computer. - -* You have downloaded and installed the Rust programming language and Cargo as described in the link:https://doc.rust-lang.org/book/ch01-01-installation.html[Rust installation instructions] for your operating system. - -+ -[source,bash] ----- -curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh ----- -+ -The Rust tool chain must be at version 1.46.0, or later. - -* You have downloaded and installed the {sdk-long-name} package as described in link:../quickstart/local-quickstart{outfilesuffix}#download-and-install[Download and install]. - -* You have `+cmake+` installed. For example, use Homebrew with the following command: -+ -[source,bash] ----- -brew install cmake ----- -For instructions on how to install Homebrew, see the link:https://docs.brew.sh/Installation[Homebrew Documentation]. - -* You have stopped any {LEE} processes running on your computer. - -If you aren’t sure how to open a new terminal shell on your local computer, run commands in a terminal, or how to check for and install packages, see link:../quickstart/newcomers{outfilesuffix}[Preliminary steps for newcomers]. -If you are comfortable meeting the prerequisites without instructions, continue to <>. - -This tutorial takes approximately 20 minutes to complete. - -== Create a new project - -To create a new project directory for this tutorial: - -[arabic] -. Open a terminal shell on your local computer, if you don’t already have one open. -. Create a new project by running the following command: -+ -[source,bash] ----- -dfx new --type=rust rust_deps ----- -. Change to your project directory by running the following command: -+ -[source,bash] ----- -cd rust_deps ----- - -== Modify the default project - -To complete this tutorial, you'll need to complete the following steps: - -* <> -* <> -* <> -* <> - -=== Edit the default canister settings - -Because this sample project is going to consist of two canisters-the {native} canister and the {lang} canister—you need to modify the default `+dfx.json+` configuration file to include information for building both the {native} canister and a {lang} canister. - -To modify the `+dfx.json+` configuration file: - -. Check that you are still in the root directory for your project, if needed. -. Open the `+dfx.json+` configuration file in a text editor. -. Insert a new section before the `+canisters.rust_deps+` settings with settings for building a {native} program. -+ -For example, in the `+canisters+` section, add a new `+multiply_deps+` key with settings like these: -+ -[source,json] ----- -"multiply_deps": { - "main": "src/multiply_deps/main.mo", - "type": "motoko" -} ----- -. Add a `+dependencies+` setting to the `+rust_deps+` . -+ -The `+dependencies+` setting enables you to import functions from one canisters for use in another canister. For this tutorial, we want to import a function from the `+multiply_deps+` canister—written in {native}—and use it from the `+rust_deps+` canister written in Rust. -Then the `+rust_deps` field will look like: -+ -[source,json] ----- -"rust_deps": { - "candid": "src/rust_deps/rust_deps.did", - "package": "rust_deps", - "type": "rust", - "dependencies": [ - "multiply_deps" - ] -} ----- -+ -. Remove all of the `+rust_deps_assets+` configuration settings from the file. -+ -The sample dapp for this tutorial doesn't use any front-end assets, so you can remove those settings from the configuration file. -+ -You can also remove the `+defaults+` and `+dfx+` version settings. -+ -For example, your configuration file might look like this after you modify the settings: -+ -.... -include::example$mul-deps/mul-deps-dfx.json[] -.... -. Save your change and close the `+dfx.json+` file to continue. - -=== Implement Motoko canister smart contract - -The next step is to create a file at `+src/multiply_deps/main.mo+` with code that implements the `+mul+` and `+read+` functions. - -To write the {native} source code: - -. Check that you are still in the root directory for your project, if needed. -. Create the directory for the {native} canister. -+ -[source,bash] ----- -mkdir multiply_deps ----- -+ -. Create and open the `+src/multiply_deps/main.mo+` file in a text editor. -. Copy and paste the following sample code into the `+main.mo+` file: -+ -[source,motoko] ----- -include::example$mul-deps/mul-deps.mo[] ----- -. Save your changes and close the file to continue. - -=== Replace the default Rust canister smart contract - -Now that we have the {native} canister that the {lang} canister depends upon, let's add the {lang} canister to the project. - -To replace the default {lang} canister: - -. Check that you are still in the root directory for your project, if needed. -. Open the template `+src/rust_deps/lib.rs+` file in a text editor and delete the existing content. -+ -The next step is to write a Rust program that imports the {native} canister and implements the `+read+` function. -. Copy and paste the following sample code into the `+lib.rs+` file: -+ -[source,bash] ----- -include::example$mul-deps/deps-main.rs[] ----- -. Save your changes and close the `+src/rust_deps/lib.rs+` file to continue. - -=== Update interface description file - -Candid is an interface description language (IDL) for interacting with canisters running on the {IC}. -Candid files provide a language-independent description of a canister's interfaces including the names, parameters, and result formats and data types for each function a canister defines. - -By adding Candid files to your project, you can ensure that data is properly converted from its definition in Rust to run safely on the {IC}. - -To see details about the Candid interface description language syntax, see the link:../candid-guide/candid-intro{outfilesuffix}[_Candid Guide_] or the https://docs.rs/candid/[Candid crate documentation]. - -To update the Candid file for this tutorial: - -. Check that you are still in the root directory for your project, if needed. -. Open the `+src/rust_deps/rust_deps.did+` file in a text editor. -. Copy and paste the following `+service+` definition for the `+read+` function: -+ -[source,did] ----- -service : { - "read": () -> (nat); -} ----- -. Save your changes and close the `+deps.did+` file to continue. - -== Start the {LEE} - -Before you can build the project, you need to connect to the {LEE} in your development environment or the {IC} mainnet. - -To start the network locally: - -. Check that you are still in the root directory for your project, if needed. -. Start the {LEE} on your computer in the background by running the following command: -+ -[source,bash] ----- -dfx start --clean --background ----- -+ -Depending on your platform and local security settings, you might see a warning displayed. -If you are prompted to allow or deny incoming network connections, click *Allow*. - -== Register, build, and deploy your project - -After you connect to the {LEE} running in your development environment, you can register, build, and deploy your project locally. - -To register, build, and deploy: - -. Check that you are still in root directory for your project directory, if needed. -. Register, build, and deploy the canisters specified in the `+dfx.json+` file by running the following command: -+ -[source,bash] ----- -dfx deploy ----- -+ -The `+dfx deploy+` command output displays information about each of the operations it performs similar to the following excerpt: -+ -.... -Creating a wallet canister on the local network. -The wallet canister on the "local" network for user "default" is "rwlgt-iiaaa-aaaaa-aaaaa-cai" -Deploying all canisters. -Creating canisters... -Creating canister "multiply_deps"... -"multiply_deps" canister created with canister id: "rrkah-fqaaa-aaaaa-aaaaq-cai" -Creating canister "rust_deps"... -"rust_deps" canister created with canister id: "ryjl3-tyaaa-aaaaa-aaaba-cai" -Building canisters... -Executing: "cargo" "build" "--target" "wasm32-unknown-unknown" "--release" "-p" "rust_deps" -... - Finished release [optimized] target(s) in 5.26s -Executing: ic-cdk-optimizer -o target/wasm32-unknown-unknown/release/rust_deps.wasm target/wasm32-unknown-unknown/release/rust_deps.wasm -Installing canisters... -Creating UI canister on the local network. -The UI canister on the "local" network is "r7inp-6aaaa-aaaaa-aaabq-cai" -Installing code for canister multiply_deps, with canister_id rrkah-fqaaa-aaaaa-aaaaq-cai -Installing code for canister rust_deps, with canister_id ryjl3-tyaaa-aaaaa-aaaba-cai -Deployed canisters. -.... - -== Call functions on the deployed canister - -After successfully deploying the canister, you can test the canister by invoking the functions it provides. - -For this tutorial: - -* Call the `+mul+` function to multiply the value of the `+cell+` variable by three each time it is called. -* Call the `+read+` function to return the current value of the `+cell+` variable. - -To test the deployed canister: - -. Call the `+read+` function from the {native} canister, which reads the current value of the `+cell+` variable on the deployed canister: -+ -[source,bash] ----- -dfx canister call multiply_deps read ----- -+ -The command returns the current value of the `+cell+` variable as one: -+ -.... -(1 : nat) -.... -. Call the `+mul+` function to multiply the input argument by three by running the following command: -+ -[source,bash] ----- -dfx canister call multiply_deps mul '(3)' ----- -+ -The command returns the new value of the `+cell+` variable: -+ -.... -(9 : nat) -.... -. Call the `+read+` function using the `+rust_deps+` canister that imports functions from the `+multiply_deps+` canister: -+ -[source,bash] ----- -dfx canister call rust_deps read ----- -+ -The command returns the current value of the `+cell+` variable: -+ -.... -(9 : nat) -.... - -== Stop the {LEE} - -After you finish experimenting with your dapp, you can stop the {LEE} so that it doesn’t continue running in the background. - -To stop the {LEE}: - -. In the terminal that displays network operations, press Control-C to interrupt the {LEE} process. - -. Stop the {LEE} by running the following command: -+ -[source,bash] ----- -dfx stop ----- diff --git a/docs/modules/rust-guide/pages/rust-counter.adoc b/docs/modules/rust-guide/pages/rust-counter.adoc deleted file mode 100644 index caba6c8b9..000000000 --- a/docs/modules/rust-guide/pages/rust-counter.adoc +++ /dev/null @@ -1,274 +0,0 @@ -= Incrementing a counter -ifdef::env-github,env-browser[:outfilesuffix:.adoc] -:toc: -:toc: right -:toc-title: TUTORIAL - SIMPLE PERSISTENCE -:toclevels: 3 -:proglang: Rust -:platform: Internet Computer platform -:LEE: local canister execution environment -:IC: Internet Computer blockchain -:company-id: DFINITY -:sdk-short-name: DFINITY Canister SDK -:sdk-long-name: DFINITY Canister Software Development Kit (SDK) -:cdk-short-name: DFINITY Rust CDK -:cdk-long-name: DFINITY Canister Development Kit (CDK) for Rust - -In this tutorial, you are going to write a dapp that provides a few basic functions to increment a counter and illustrates the persistence of a value. - -For this tutorial, the dapp declares a `+COUNTER+` as a mutable variable to contain a natural number that represents the current value of the counter. -This dapp supports the following functions: - -* The `+increment+` function updates the current value, incrementing by 1 with no return value. -* The `+get+` function is a simple query that returns the current value of the counter. -* The `+set+` function updates the current value to the numeric value you specify as an argument. - -This tutorial provides a simple example of how you can increment a counter by calling functions on a deployed canister smart contract. -By calling the function to increment a value multiple times, you can verify that the variable state—that is, the value of the variable between calls—persists. - -Like the other sample dapps, this tutorial demonstrates a simple, but realistic, workflow in which you perform the following steps: - -* Create a new project. -* Write a dapp that compiles into a WebAssembly module. -* Deploy the canister on the {LEE}. -* Invoke the canister smart contract methods to increment then read the value of a counter. - -[[before-you-begin]] -== Before you begin - -Before you start your project, verify the following: - -* You have an internet connection and access to a shell terminal on your local macOS or Linux computer. - -* You have downloaded and installed the Rust programming language and Cargo as described in the link:https://doc.rust-lang.org/book/ch01-01-installation.html[Rust installation instructions] for your operating system. - -+ -[source,bash] ----- -curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh ----- -+ -The Rust tool chain must be at version 1.46.0, or later. - -* You have downloaded and installed the {sdk-long-name} package as described in link:../quickstart/local-quickstart{outfilesuffix}#download-and-install[Download and install]. - -* You have `+cmake+` installed. For example, use Homebrew with the following command: -+ -[source,bash] ----- -brew install cmake ----- -For instructions on how to install Homebrew, see the link:https://docs.brew.sh/Installation[Homebrew Documentation]. - -* You have stopped any {LEE} processes running on your computer. - -If you aren’t sure how to open a new terminal shell on your local computer, run commands in a terminal, or how to check for and install packages, see link:../quickstart/newcomers{outfilesuffix}[Preliminary steps for newcomers]. -If you are comfortable meeting the prerequisites without instructions, continue to <>. - -This tutorial takes approximately 20 minutes to complete. - -== Create a new project - -To create a new project directory for this tutorial: - -[arabic] -. Open a terminal shell on your local computer, if you don’t already have one open. -. Create a new project by running the following command: -+ -[source,bash] ----- -dfx new --type=rust rust_counter ----- -+ -. Change to your project directory by running the following command: -+ -[source,bash] ----- -cd rust_counter ----- - -== Modify the default project - -In the link:rust-quickstart{outfilesuffix}[Hello, World! Rust CDK Quick Start], you went through the files in a default project with Rust type canister. - -To complete this tutorial, you'll need to complete the following steps: - -* <> -* <> - -=== Replace the default dapp - -Now that you have the files in place for your Rust dapp, we can replace the template `+lib.rs+` dapp with the Rust dapp we want to deploy on the {IC}. - -To replace the default dapp: - -. Check that you are still in the root directory for your project, if needed. -. Open the template `+src/rust_counter/lib.rs+` file in a text editor and delete the existing content. -+ -The next step is to write a Rust dapp that declares the `+COUNTER+` variable and implements the `+increment+`, `+get+`, and `+set+` functions. -. Copy and paste the following sample code into the `+lib.rs+` file: -+ -[source,motoko] ----- -include::example$counter-tutorial/counter.rs[] ----- -. Save your changes and close the `+lib.rs+` file to continue. - -=== Update interface description file - -Candid is an interface description language (IDL) for interacting with canisters running on the Internet Computer. -Candid files provide a language-independent description of a canister's interfaces including the names, parameters, and result formats and data types for each function a canister defines. - -By adding Candid files to your project, you can ensure that data is properly converted from its definition in Rust to run safely on the {IC}. - -To see details about the Candid interface description language syntax, see the link:../candid-guide/candid-intro{outfilesuffix}[_Candid Guide_] or the https://docs.rs/candid/[Candid crate documentation]. - -To update the Candid file for this tutorial: - -. Check that you are still in the root directory for your project, if needed. -. Open the `+src/rust_counter/rust_counter.did+` file in a text editor, then copy and paste the following `+service+` definition for the `+increment+`, `+get+`, and `+set+` functions: -+ -[source,did] ----- -service : { - "increment": () -> (); - "get": () -> (nat) query; - "set": (nat) -> (); -} ----- -. Save your changes and close the `+rust_counter.did+` file to continue. - -== Start the {LEE} - -Before you can build the `+rust_counter+` project, you need to connect to the {LEE} running in your development environment or the decentralized {IC} mainnet. - -To start the {LEE}: - -. Check that you are still in the root directory for your project, if needed. -. Start the {LEE} on your computer in the background by running the following command: -+ -[source,bash] ----- -dfx start --background ----- -+ -Depending on your platform and local security settings, you might see a warning displayed. -If you are prompted to allow or deny incoming network connections, click *Allow*. - -== Register, build, and deploy your project - -After you connect to the {LEE} running in your development environment, you can register, build, and deploy your project locally. - -To register, build, and deploy: - -. Check that you are still in root directory for your project directory, if needed. -. Register, build, and deploy the canisters specified in the `+dfx.json+` file by running the following command: -+ -[source,bash] ----- -dfx deploy ----- -+ -The `+dfx deploy+` command output displays information about each of the operations it performs similar to the following excerpt: -+ -.... -Creating a wallet canister on the local network. -The wallet canister on the "local" network for user "default" is "rwlgt-iiaaa-aaaaa-aaaaa-cai" -Deploying all canisters. -Creating canisters... -Creating canister "rust_counter"... -"rust_counter" canister created with canister id: "rrkah-fqaaa-aaaaa-aaaaq-cai" -Creating canister "rust_counter_assets"... -"rust_counter_assets" canister created with canister id: "ryjl3-tyaaa-aaaaa-aaaba-cai" -Building canisters... -Executing: "cargo" "build" "--target" "wasm32-unknown-unknown" "--release" "-p" "rust_counter" -... - Finished release [optimized] target(s) in 53.36s -Building frontend... -Installing canisters... -Creating UI canister on the local network. -The UI canister on the "local" network is "r7inp-6aaaa-aaaaa-aaabq-cai" -Installing code for canister rust_counter, with canister_id rrkah-fqaaa-aaaaa-aaaaq-cai -... -Deployed canisters. -.... - -== Call functions and test the dapp - -After successfully deploying the canister, you can test the canister by invoking the functions it provides. -For this tutorial: - -* Call the `+get+` function to query the value of the counter. -* Call the `+increment+` function to increment the counter each time it is called. -* Call the `+set+` function to pass an argument to update the counter to an arbitrary value you specify. - -To test the dapp: - -[arabic] -. Call the `+get+` function to read the current value of the `+COUNTER+` variable by running the following command: -+ -[source,bash] ----- -dfx canister call rust_counter get ----- -+ -The command returns the current value of the `+COUNTER+` variable as zero: -+ -.... -(0 : nat) -.... -[arabic] -. Call the `+increment+` function to increment the value of the `+COUNTER+` variable by one: -+ -[source,bash] ----- -dfx canister call rust_counter increment ----- -+ -This command increments the value of the variable—changing its state—but does not return the result. -. Rerun the command to call the `+get+` function to see the current value of the `+COUNTER+` variable: -+ -[source,bash] ----- -dfx canister call rust_counter get ----- -+ -The command returns the updated value of the `+COUNTER+` variable as one: -+ -.... -(1 : nat) -.... -. Run additional commands to experiment with call the functions and using different values. -+ -For example, try commands similar to the following to set and return the counter value: -+ -[source,bash] ----- -dfx canister call rust_counter set '(987)' -dfx canister call rust_counter get ----- -+ -Returns the current value of 987. -+ -[source,bash] ----- -dfx canister call rust_counter increment -dfx canister call rust_counter get ----- -+ -Returns the incremented value of 988. - -== Stop the {LEE} - -After you finish experimenting with your dapp, you can stop the local Internet Computer network so that it doesn’t continue running in the background. - -To stop the {LEE}: - -. In the terminal that displays network operations, press Control-C to interrupt the {LEE} process. - -. Stop the {LEE} by running the following command: -+ -[source,bash] ----- -dfx stop ----- diff --git a/docs/modules/rust-guide/pages/rust-intro.adoc b/docs/modules/rust-guide/pages/rust-intro.adoc deleted file mode 100644 index dad04b8e8..000000000 --- a/docs/modules/rust-guide/pages/rust-intro.adoc +++ /dev/null @@ -1,36 +0,0 @@ -= Introduction to working with Rust -:cdk-short-name: DFINITY Rust CDK -:cdk-long-name: DFINITY Canister Development Kit (CDK) for Rust -:cdk: Rust Canister Development Kit (CDK) -:IC: Internet Computer blockchain - -Rust is a powerful and type sound modern programming language with an active developer community. -Because Rust compiles to WebAssembly, it offers a rich development environment for writing dapps to run on the {IC}. -To help pave the way for writing dapps in Rust that can be deployed on the {IC}, {company-id} provides some tools to simplify the process. - -Collectively, these tools are referred to as the {cdk-long-name} and consist of the following main libraries: - -[width="100%",cols="<20%,<80%",options="header"] -|=== -|Package |Description - -|`+ic-types+` |The `+ic-types+` crate defines the types used to communicate with the decentralized {IC}, and when building dapps to be deployed as canister smart contracts on the {IC}. - -|`+ic-agent+` |The `+ic-agent+` library enables direct communication with the {IC}. - -|`+ic-utils+` |The `+ic-utils+` library provides utilities for managing calls and dapps deployed as canister smart contracts. - - -|`+ic-cdk+` |The `+ic-cdk+` provides the core methods that enable Rust programs to interact with the {IC} system API. This library serves as the runtime core of the Rust CDK. - -|`+ic-cdk-macros+` |The `+ic-cdk-macros+` library defines the procedural macros that facilitate building operation endpoints and APIs. This library includes macros for `+update+`, `+query+`, `+import+` and other important operations. - -|`+ic-cdk-timers+` |The `+ic-cdk-timers+` library implements multiple and periodic timers. - -|`+ic-cdk-optimizer+` |The `+ic-cdk-optimizer+` is a helper library used to reduce the size of WebAssembly modules. - -|=== - -The following diagram provides a simplified view of the {cdk} building blocks from the lowest to highest level of abstraction. - -image:Rust-building-blocks.svg[] diff --git a/docs/modules/rust-guide/pages/rust-optimize.adoc b/docs/modules/rust-guide/pages/rust-optimize.adoc deleted file mode 100644 index 6b825b7cc..000000000 --- a/docs/modules/rust-guide/pages/rust-optimize.adoc +++ /dev/null @@ -1,69 +0,0 @@ -= Optimize a Rust program -:proglang: Rust -:platform: Internet Computer platform -:LEE: local canister excecution environment -:IC: Internet Computer blockchain -:company-id: DFINITY -:cdk-short-name: DFINITY Rust CDK -:sdk-short-name: DFINITY Canister SDK -:cdk-long-name: DFINITY Canister Development Kit (CDK) for Rust -:sdk-long-name: DFINITY Canister Software Development Kit (SDK) -ifdef::env-github,env-browser[:outfilesuffix:.adoc] - -Compiling Rust to WebAssembly often increases the file size significantly. The {cdk-short-name} includes a helper library—`+ic-cdk-optimizer+`—that you can use to reduce the size of Rust-based canister smart contracts before deploying them on the {IC} mainnet. - -[[before-you-begin]] -== Before you begin - -Before you optimize your program, verify the following: - -* You have an internet connection and access to a shell terminal on your local macOS or Linux computer. - -* You have downloaded and installed the Rust programming language and Cargo as described in the link:https://doc.rust-lang.org/book/ch01-01-installation.html[Rust installation instructions] for your operating system. - -+ -[source,bash] ----- -curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh ----- -+ -The Rust tool chain must be at version 1.46.0, or later. - -* You have downloaded and installed the {sdk-long-name} package as described in link:../../quickstart/quickstart{outfilesuffix}#download-and-install[Download and install]. - -* You have `+cmake+` installed. For example, use Homebrew with the following command: -+ -[source,bash] ----- -brew install cmake ----- -For instructions on how to install Homebrew, see the link:https://docs.brew.sh/Installation[Homebrew Documentation]. - -* You have successfully compiled your dapp to a WebAssembly module (WASM) and deployed it on the {LEE}. - -== Install and run the optimizer - -To optimize a canister that resulted from compiling a Rust dapp: - -. Check that you are still in root directory for your project directory, if needed. -. Install the `+ic-cdk-optimizer+` crate, if you have not previously installed it, by running the following command: -+ -[source,bash] ----- -cargo install ic-cdk-optimizer ----- -+ -This package optimizes your Rust code to reduce the size of the WebAssembly output to ensure your dapp can be uploaded to the {IC} mainnet as a canister. -. Create a release directory within the `+src+` directory for your program by running a command similar to the following: -+ -[source,bash,subs=quotes] ----- -mkdir -p src/_rust-canister_/target/wasm32-unknown-unknown/release/ ----- -. Optimize the code within the `+target+` directory by running a command similar to the following: -+ -[source,bash,subs-quotes] ----- -ic-cdk-optimizer target/wasm32-unknown-unknown/release/_rust_canister_.wasm -o target/wasm32-unknown-unknown/release/_rust_canister_-opt.wasm ----- - diff --git a/docs/modules/rust-guide/pages/rust-profile.adoc b/docs/modules/rust-guide/pages/rust-profile.adoc deleted file mode 100644 index 7f2519fe2..000000000 --- a/docs/modules/rust-guide/pages/rust-profile.adoc +++ /dev/null @@ -1,378 +0,0 @@ -= Adding and searching simple records -ifdef::env-github,env-browser[:outfilesuffix:.adoc] -:toc-title: TUTORIAL - SAMPLE PROFILE -:proglang: Rust -:platform: Internet Computer platform -:LEE: local execution environment -:IC: Internet Computer blockchain -:company-id: DFINITY -:sdk-short-name: DFINITY Canister SDK -:cdk-short-name: DFINITY Rust CDK -:sdk-long-name: DFINITY Canister Software Development Kit (SDK) -:cdk-long-name: DFINITY Canister Development Kit (CDK) for Rust - -In this tutorial, you are going to write a dapp that provides a few basic functions to add and retrieve simple profile records that consist of a name, description, and an array of keywords. - -This program supports the following functions: - -* The `+update+` function enables you to add a profile that consists of a `+name+`, a `+description+`, and `+keywords+`. -* The `+getSelf+` function returns the profile for the principal associated with the function caller. -* The `+get+` function performs a simple query to return the profile matching the `+name+` value passed to it. -For this function, the name specified must match the `+name+` field exactly to return the record. -* The `+search+` function performs a more complex query to return the profile matching all or part of the text specified in any profile field. For example, the `+search+` function can return a profile containing a specific keyword or that matches only part of a name or description. - -This tutorial provides a simple example of how you can use the Rust CDK interfaces and macros to simplify writing dapps in Rust for the {IC}. - -This tutorial demonstrates: -* How to represent slightly more complex data—in the form of a profile as a `+record+` and an `+array+` of keywords—using the Candid interface description language. -* How to write a simple search function with partial string matching. -* How profiles are associated with a specific principal. - -== Before you begin - -Before you start your project, verify the following: - -* You have an internet connection and access to a shell terminal on your local macOS or Linux computer. - -* You have downloaded and installed the Rust programming language and Cargo as described in the link:https://doc.rust-lang.org/book/ch01-01-installation.html[Rust installation instructions] for your operating system. - -+ -[source,bash] ----- -curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh ----- -+ -The Rust tool chain must be at version 1.46.0, or later. - -* You have downloaded and installed the {sdk-long-name} package as described in link:../../quickstart/quickstart{outfilesuffix}#download-and-install[Download and install]. - -* You have `+cmake+` installed. For example, use Homebrew with the following command: -+ -[source,bash] ----- -brew install cmake ----- -For instructions on how to install Homebrew, see the link:https://docs.brew.sh/Installation[Homebrew Documentation]. - -* You have stopped any {LEE} processes running on your computer. - -If you aren’t sure how to open a new terminal shell on your local computer, run commands in a terminal, or how to check for and install packages, see link:../quickstart/newcomers{outfilesuffix}[Preliminary steps for newcomers]. -If you are comfortable meeting the prerequisites without instructions, continue to <>. - -This tutorial takes approximately 20 minutes to complete. - -== Create a new project - -To create a new project directory for this tutorial: - -[arabic] -. Open a terminal shell on your local computer, if you don’t already have one open. -. Create a new project by running the following command: -+ -[source,bash] ----- -dfx new --type=rust rust_profile ----- -. Change to your project directory by running the following command: -+ -[source,bash] ----- -cd rust_profile ----- - -== Modify the default project - -In the link:rust-quickstart{outfilesuffix}[Hello, World! Rust CDK Quick Start], you went through the files in a default project with Rust type canister. - -To complete this tutorial, you'll need to complete the following steps: - -* <> -* <> - -=== Replace the default dapp - -Now that you have the files in place for your Rust dapp, we can replace the template `+lib.rs+` dapp with the Rust dapp we want to deploy on the {IC}. - -To replace the default program: - -. Check that you are still in the root directory for your project, if needed. -. Open the `+src/rust_profile/Cargo.toml+` file in a text editor and add `+serde+` to dependencies. -+ -[source,toml] ----- -[dependencies] -ic-cdk = "0.5" -ic-cdk-macros = "0.5" -serde = "1.0" ----- -+ -. Open the template `+src/rust_profile/lib.rs+` file in a text editor and delete the existing content. -+ -The next step is to add a Rust program that implements the `+getSelf+`, `+update+`, `+get+`, and `+search+` functions. -. Copy and paste the following sample code into the `+profile.rs+` file: -+ -[source,motoko] ----- -include::example$profile-tutorial/profile.rs[] ----- -. Save your changes and close the file to continue. - -== Update interface description file - -Candid is an interface description language (IDL) for interacting with canister smart contracts running on the Internet Computer. -Candid files provide a language-independent description of a canister smart contract's interfaces including the names, parameters, and result formats and data types for each function a canister defines. - -By adding Candid files to your project, you can ensure that data is properly converted from its definition in Rust to run safely on the {IC}. - -To see details about the Candid interface description language syntax, see the link:../candid-guide/candid-intro{outfilesuffix}[_Candid Guide_] or the https://docs.rs/candid/[Candid crate documentation]. - -To update Candid file for this tutorial: - -. Check that you are still in the root directory for your project, if needed. -. Open the `+src/rust_profile/rust_profile.did+`file in a text editor. -. Copy and paste the following `+type+` declaration and `+service+` definition for the `+getSelf+`, `+update+`, `+get+`, and `+search+` functions: -+ -[source,did] ----- -include::example$profile-tutorial/profile.did[] ----- -. Save your changes and close the file to continue. - -== Start the {LEE} - -Before you can build the `+rust_profile+` project, you need to connect to the {LEE} running in your development environment or the decentralized {IC} mainnet. - -To start {LEE}: - -. Check that you are still in the root directory for your project, if needed. -. Start the {LEE} on your computer in the background by running the following command: -+ -[source,bash] ----- -dfx start --background --clean ----- -+ -Depending on your platform and local security settings, you might see a warning displayed. -If you are prompted to allow or deny incoming network connections, click *Allow*. - -== Register, build, and deploy your project - -After you connect to the {LEE} running in your development environment, you can register, build, and deploy your project locally. - -To register, build, and deploy: - -. Check that you are still in root directory for your project directory, if needed. -. Register, build, and deploy the canisters specified in the `+dfx.json+` file by running the following command: -+ -[source,bash] ----- -dfx deploy ----- -+ -The `+dfx deploy+` command output displays information about each of the operations it performs similar to the following excerpt: -+ -.... -Creating a wallet canister on the local network. -The wallet canister on the "local" network for user "default" is "rwlgt-iiaaa-aaaaa-aaaaa-cai" -Deploying all canisters. -Creating canisters... -Creating canister "rust_profile"... -"rust_profile" canister created with canister id: "rrkah-fqaaa-aaaaa-aaaaq-cai" -Creating canister "rust_profile_assets"... -"rust_profile_assets" canister created with canister id: "ryjl3-tyaaa-aaaaa-aaaba-cai" -Building canisters... -Executing: "cargo" "build" "--target" "wasm32-unknown-unknown" "--release" "-p" "rust_profile" -... - Finished release [optimized] target(s) in 6.31s -Building frontend... -Installing canisters... -Creating UI canister on the local network. -The UI canister on the "local" network is "r7inp-6aaaa-aaaaa-aaabq-cai" -Installing code for canister rust_profile, with canister_id rrkah-fqaaa-aaaaa-aaaaq-cai -... -Deployed canisters. -.... - -== Call functions on the deployed canister - -After successfully deploying the canister, you can test the canister by calling the functions it provides. - -For this tutorial: - -* Call the `+update+` function to add a profile. -* Call the `+getSelf+` function to display the profile for the principal identity. -* Call the `+search+` function to look up the profile using a keyword. - -To test the deployed canister: - -. Call the `+update+` function to create a profile record by running the following command: -+ -[source,bash] ----- -dfx canister call rust_profile update '(record {name = "Luxi"; description = "mountain dog"; keywords = vec {"scars"; "toast"}})' ----- -. Call the `+getSelf+` function to retrieve a profile record by running the following command: -+ -[source,bash] ----- -dfx canister call rust_profile getSelf ----- -+ -The command returns the profile you used the `+update+` function to add. -For example: -+ -.... -( record { - name = "Luxi"; - description = "mountain dog"; - keywords = vec { "scars"; "toast" }; - }, -) -.... -+ -In its current form, the dapp only stores and returns one profile. -If you run the following command to add a second profile using the `+update+` function, the command replaces the `+Luxi+` profile with the `+Dupree+` profile: -+ -[source,bash] ----- -dfx canister call rust_profile update '(record {name = "Dupree"; description = "black dog"; keywords = vec {"funny tail"; "white nose"}})' ----- -+ -You can use the `+get+`, `+getSelf+`, and `+search+` functions, but they will only return results for the `+Dupree+` profile. -. Run the following command to call the `+search+` function: -+ -[source,bash] ----- -dfx canister call rust_profile search '("black")'; ----- -+ -This command finds the matching profile using the `+description+` and returns the profile: -+ -.... -( - opt record { - name = "Dupree"; - description = "black dog"; - keywords = vec { "funny tail"; "white nose" }; - }, -.... - -== Adding profiles for new identities - -In its current form, the dapp only stores one profile—the one associated with the principal invoking the commands. To test that the `+get+`, `+getSelf+`, and `+search+` functions do what we want them to, we need to add some new identities that can have different profiles. - -To add identities for testing: - -. Create a new user identity by running the following command: -+ -[source,bash] ----- -dfx identity new Miles ----- -+ -.... -Creating identity: "Miles". -Created identity: "Miles". -.... - -. Call the `+update+` function to add a profile for the new identity. -+ -[source,bash] ----- -dfx --identity Miles canister call rust_profile update '(record {name = "Miles"; description = "Great Dane"; keywords = vec {"Boston"; "mantle"; "three-legged"}})' ----- -. Call the `+getSelf+` function to view the profile associated with the `+default+` user identity. -+ -[source,bash] ----- -dfx canister call rust_profile getSelf ----- -+ -The command displays the profile currently associated with the default identity, in this example, the Dupree profile: -+ -.... -( - record { - name = "Dupree"; - description = "black dog"; - keywords = vec { "funny tail"; "white nose" }; - }, -) -.... -. Call the `+getSelf+` function using the `+Miles+` user identity by running the following command: -+ -[source,bash] ----- -dfx --identity Miles canister call rust_profile getSelf ----- -+ -The command displays the profile currently associated with the Miles identity, in this example: -+ -.... -( - record { - name = "Miles"; - description = "Great Dane"; - keywords = vec { "Boston"; "mantle"; "three-legged" }; - }, -) -.... -. Call the `+search+` function using part of the description or a keyword to further test the whether the correct profile is returned. -+ -For example, to verify the `+Miles+` profile is returned, you might run the following command: -+ -[source,bash] ----- -dfx canister call rust_profile search '("Great")' ----- -+ -The command returns the `+Miles+` profile: -+ -.... -( - opt record { - name = "Miles"; - description = "Great Dane"; - keywords = vec { "Boston"; "mantle"; "three-legged" }; - }, -) -.... -. Call the `+search+` function to further test the whether the correct profile is returned. -+ -For example, to verify the `+Dupree+` profile is returned, you might run the following command: -+ -[source,bash] ----- -dfx canister call rust_profile search '("black")' ----- -+ -The command returns the `+Dupree+` profile: -+ -.... -( - opt record { - name = "Dupree"; - description = "black dog"; - keywords = vec { "funny tail"; "white nose" }; - }, -) -.... - -== Extending the sample dapp - -This sample dapp only stores one profile for each unique user identity. If you were to extend this dapp by adding a second method for linking social connections to each users profile, you would be well on your way to recreating the LinkedUp sample application using Rust. - -== Stop the {LEE} - -After you finish experimenting with your program, you can stop the {LEE} so that it doesn’t continue running in the background. - -To stop the {LEE}: - -. In the terminal that displays network operations, press Control-C to interrupt the {LEE} process. - -. Stop the {LEE} by running the following command: -+ -[source,bash] ----- -dfx stop ----- diff --git a/docs/modules/rust-guide/pages/rust-quickstart.adoc b/docs/modules/rust-guide/pages/rust-quickstart.adoc deleted file mode 100644 index 9f2fa7ded..000000000 --- a/docs/modules/rust-guide/pages/rust-quickstart.adoc +++ /dev/null @@ -1,295 +0,0 @@ -= Hello, World! Rust CDK Quick Start -:experimental: -// Define unicode for Apple Command key. -:commandkey: ⌘ -:toclevels: 3 -:proglang: Rust -:platform: Internet Computer platform -:LEE: local execution environment -:IC: Internet Computer blockchain -:company-id: DFINITY -:cdk-short-name: DFINITY Rust CDK -:sdk-short-name: DFINITY Canister SDK -:cdk-long-name: DFINITY Canister Development Kit (CDK) for Rust -:sdk-long-name: DFINITY Canister Software Development Kit (SDK) -ifdef::env-github,env-browser[:outfilesuffix:.adoc] - -[[quick-start-intro]] -The {cdk-long-name} provides tools, sample code, and documentation to help you create dapps to run on the decentralized {IC} mainnet. -This _Hello, World! Rust CDK Quick Start_ assumes that you are installing the {cdk-short-name} for the first time. - -To help you get started, this tutorial illustrates how to modify the traditional "Hello World" first dapp to use the {cdk-short-name}. -This simple dapp has just one function that prints text to a terminal, but it provides a good model for understanding the workflow when writing dapps in Rust that you want to deploy on the {IC}. - -[[before-you-begin]] -== Before you begin - -Before you start your project, verify the following: - -* You have an internet connection and access to a shell terminal on your local macOS or Linux computer. - -* You have downloaded and installed the Rust programming language and Cargo as described in the link:https://doc.rust-lang.org/book/ch01-01-installation.html[Rust installation instructions] for your operating system. - -+ -[source,bash] ----- -curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh ----- -+ -The Rust tool chain must be at version 1.46.0, or later. - -* You have downloaded and installed the {sdk-long-name} package as described in link:../quickstart/local-quickstart{outfilesuffix}#download-and-install[Download and install]. - -* You have `+cmake+` installed. For example, use Homebrew with the following command: -+ -[source,bash] ----- -brew install cmake ----- -+ -For instructions on how to install Homebrew, see the link:https://docs.brew.sh/Installation[Homebrew Documentation]. - -* You have stopped any {LEE} processes running on your computer. - -If you aren’t sure how to open a new terminal shell on your local computer, run commands in a terminal, or how to check for and install packages, see link:../quickstart/newcomers{outfilesuffix}[Preliminary steps for newcomers]. -If you are comfortable meeting the prerequisites without instructions, continue to <>. - -[[create-a-new-project]] -== Create a new project - -Applications for the {IC} start as **projects**. -You can create new projects for the {IC} using the {sdk-short-name}. -Because you are building this project to be deployed on the {IC}, this tutorial focuses on how to create, build, and deploy a Rust program by using the `+dfx+` parent command and its subcommands. - -To create a new project using the {sdk-short-name}: - -. Open a terminal shell on your local computer, if you don’t already have one open. -. Create a new project with rust canister named `+rust_hello+` by running the following command: -+ -[source,bash] ----- -dfx new --type=rust rust_hello ----- -+ -The `+dfx new --type=rust rust_hello+` command creates a new `+rust_hello+` project directory, template files, and a new `+rust_hello+` Git repository for your project. -. Change to your project directory by running the following command: -+ -[source,bash] ----- -cd rust_hello ----- - -== Take a look at the project - -The project is ready to be compiled and deployed to the {IC}. -Before that, let's go through the project files. - -=== `+dfx.json+` - -One of the template files included in your project directory is a default `+dfx.json+` configuration file. -This file contains settings required to build a project for the {IC} much like the `+Cargo.toml+` file provides build and package management configuration details for Rust programs. - -The configuration file should look like this: - -.... -include::example$hello-dfx.json[] -.... - -Notice that under the `+canisters+` key, you have some default settings for the `+rust_hello+` canister. - -. `+"type": "rust"+` specifies that `+rust_hello+` is a `+rust+` type canister. - -. `+"candid": "src/rust_hello/rust_hello.did"+` specifies the location of the Candid interface description file to use for the canister. - -. `+"package": "rust_hello"+` specifies the package name of the Rust crate. It should be the same as in the crate `+Cargo.toml+` file. - -=== `+Cargo.toml+` - -In the root directory, there is a `+Cargo.toml+` file. - -It defines a Rust workspace by specifying paths to each Rust crate. -A Rust type canister is just a Rust crate compiled to WebAssembly. -Here we have one member at `+src/rust_hello+` which is the only Rust canister. -[source,toml] ----- -[workspace] -members = [ - "src/rust_hello", -] ----- - -=== `+src/rust_hello/+` - -Now we are in the Rust canister. As any standard Rust crate, it has a `+Cargo.toml+` file which configures the details to build the Rust crate. - -==== `+src/rust_hello/Cargo.toml+` - -[source,toml] ----- -[package] -name = "rust_hello" -version = "0.1.0" -edition = "2018" - -[lib] -path = "lib.rs" -crate-type = ["cdylib"] - -[dependencies] -ic-cdk = "0.5" -ic-cdk-macros = "0.5" ----- - -Notice the `+crate-type = ["cdylib"]+` line which is necessary to compile this rust program into WebAssembly module. - -==== `+src/rust_hello/lib.rs+` - -The default project has a simple `+greet+` function that uses the {cdk-short-name} `+query+` macro. - -[source,rust] ----- -#[ic_cdk::query] -fn greet(name: String) -> String { - format!("Hello, {}!", name) -} ----- - -==== `+src/rust_hello/rust_hello.did+` - -Candid is an interface description language (IDL) for interacting with canisters running on the Internet Computer. -Candid files provide a language-independent description of a canister's interfaces including the names, parameters, and result formats and data types for each function a canister defines. - -By adding Candid files to your project, you can ensure that data is properly converted from its definition in Rust to run safely on the {IC}. - -To see details about the Candid interface description language syntax, see the link:../candid-guide/candid-intro{outfilesuffix}[_Candid Guide_] or the https://docs.rs/candid/[Candid crate documentation]. - -[source,did] ----- -service : { - "greet": (text) -> (text) query; -} ----- - -This definition specifies that the `+greet+` function is a `+query+` method which takes `+text+` data as input and returns `+text+` data. - -[[start-the-local-network]] -== Start the {LEE} - -Before you can build your project, you need to connect to the {LEE} running in your development environment or the decentralized {IC} mainnet. - -To start the {LEE}: - -[arabic] -. Check that you are still in the root directory for your project, if needed. -. Start the {LEE} on your computer in the background by running the following command: -+ -[source,bash] ----- -dfx start --background ----- -+ -Depending on your platform and local security settings, you might see a warning displayed. -If you are prompted to allow or deny incoming network connections, click *Allow*. - -[[register-ids]] -== Register, build, and deploy your project - -After you connect to the {LEE} running in your development environment, you can register, build, and deploy your project locally. - -To register, build, and deploy: - -. Check that you are still in root directory for your project directory, if needed. -. Make sure you have `wasm32-unknown-unknown` installed by running the command: -+ -[source,bash] -rustup target add wasm32-unknown-unknown -+ -. Register, build, and deploy the canisters specified in the `+dfx.json+` file by running the following command: -+ -[source,bash] ----- -dfx deploy ----- -+ -The `+dfx deploy+` command output displays information about each of the operations it performs similar to the following excerpt: -+ -.... -Creating a wallet canister on the local network. -The wallet canister on the "local" network for user "default" is "rwlgt-iiaaa-aaaaa-aaaaa-cai" -Deploying all canisters. -Creating canisters... -Creating canister "rust_hello"... -"rust_hello" canister created with canister id: "rrkah-fqaaa-aaaaa-aaaaq-cai" -Creating canister "rust_hello_assets"... -"rust_hello_assets" canister created with canister id: "ryjl3-tyaaa-aaaaa-aaaba-cai" -Building canisters... -Executing: "cargo" "build" "--target" "wasm32-unknown-unknown" "--release" "-p" "rust_hello" - Updating crates.io index - Compiling unicode-xid v0.2.2 - ... -Building frontend... -Installing canisters... -Creating UI canister on the local network. -The UI canister on the "local" network is "r7inp-6aaaa-aaaaa-aaabq-cai" -Installing code for canister rust_hello, with canister_id rrkah-fqaaa-aaaaa-aaaaq-cai -... -Deployed canisters. -.... - -//// -== Optimize and test the dapp - -At this point, you have a dapp compiled to WebAssembly that has not been optimized for deployment on the {IC}. - -To optimize and test the resulting canister: - -. Check that you are still in root directory for your project directory, if needed. -. Install the `+ic-cdk-optimizer+` crate by running the following command: -+ -[source,bash] ----- -cargo install ic-cdk-optimizer ----- -+ -This package optimizes your Rust code to reduce the size of the WebAssembly output to ensure your dapp can be uploaded as a canister. -. Create a release directory within the `+src/rust_hello+` directory by running the following command: -+ -[source,bash] ----- -mkdir -p src/rust_hello/target/wasm32-unknown-unknown/release/ ----- -. Optimize the code within the `+target/rust_hello+` directory by running the following command: -+ -[source,bash] ----- -ic-cdk-optimizer target/wasm32-unknown-unknown/release/rust_hello.wasm -o target/wasm32-unknown-unknown/release/rust_hello-opt.wasm ----- -//// - -== Test the dapp - -To test the deployed dapp locally: - -. Check that you are still in root directory for your project directory, if needed. -. Call the `+greet+` function in the dapp by running the following command: -+ -[source,bash] ----- -dfx canister call rust_hello greet world ----- -. Verify that the call to the `+rust_hello+` canister `+greet+` function returns a text message `+("Hello, world!")+`. - -== Stop the {LEE} - -After testing the application, you can stop the {LEE} so that it doesn’t continue running in the background. - -To stop the {LEE}: - -. In the terminal that displays network operations, press Control-C to interrupt the {LEE} process. - -. Stop the {LEE} running on your computer by running the following command: -+ -[source,bash] ----- -dfx stop ----- diff --git a/docs/modules/rust-guide/rust-nav.adoc b/docs/modules/rust-guide/rust-nav.adoc deleted file mode 100644 index 4d4c58506..000000000 --- a/docs/modules/rust-guide/rust-nav.adoc +++ /dev/null @@ -1,7 +0,0 @@ -* xref:rust-intro.adoc[Working with Rust] -** xref:rust-quickstart.adoc[Hello, World! Quick Start] -** xref:rust-counter.adoc[Simple counter tutorial] -** xref:multiply-dependency.adoc[Basic dependency tutorial] -** xref:rust-profile.adoc[Profile tutorial] -//** xref:rust-asset-storage.adoc[Asset storage tutorial] -//** xref:rust-chess-autonomous.adoc[Autonomous game tutorial] diff --git a/e2e-tests/Cargo.toml b/e2e-tests/Cargo.toml index 481978daa..f3a59c821 100644 --- a/e2e-tests/Cargo.toml +++ b/e2e-tests/Cargo.toml @@ -10,47 +10,15 @@ publish = false [dependencies] candid.workspace = true -cargo_metadata = "0.18" escargot = { version = "0.5.7", features = ["print"] } -ic-cdk.workspace = true +futures.workspace = true +ic-cdk = { workspace = true, features = ["transform-closure"] } ic-cdk-timers.workspace = true lazy_static = "1.4.0" serde_bytes.workspace = true -futures.workspace = true sha2.workspace = true -[[bin]] -name = "simple-kv-store" -path = "canisters/simple_kv_store.rs" - -[[bin]] -name = "async" -path = "canisters/async.rs" - -[[bin]] -name = "reverse" -path = "canisters/reverse.rs" - -[[bin]] -name = "api-call" -path = "canisters/api_call.rs" - -[[bin]] -name = "timers" -path = "canisters/timers.rs" - -[[bin]] -name = "canister_info" -path = "canisters/canister_info.rs" - -[[bin]] -name = "management_caller" -path = "canisters/management_caller.rs" - -[[bin]] -name = "chunk" -path = "canisters/chunk.rs" - [dev-dependencies] +cargo_metadata = "0.19" hex.workspace = true pocket-ic = { git = "https://github.com/dfinity/ic", tag = "release-2024-11-28_03-15-base" } diff --git a/e2e-tests/canisters/management_caller.rs b/e2e-tests/canisters/management_caller.rs deleted file mode 100644 index ed7b87206..000000000 --- a/e2e-tests/canisters/management_caller.rs +++ /dev/null @@ -1,188 +0,0 @@ -use ic_cdk::*; - -/// Some management canister "main" methods are tested with other e2e canisters: -/// - canister_info.rs -/// - chunk.rs -mod main { - use super::*; - use candid::Principal; - use ic_cdk::api::management_canister::main::*; - #[update] - async fn execute_main_methods() { - let arg = CreateCanisterArgument { - settings: Some(CanisterSettings { - controllers: Some(vec![ic_cdk::id()]), - // There is no canister in the subnet, so we can set it to 100. - compute_allocation: Some(1u8.into()), - // Though the upper limit is 256TiB, the actual subnet may have less memory resource (e.g. local replica). - // Here we set it to 10KiB for testing. - memory_allocation: Some(10000u16.into()), - freezing_threshold: Some(u64::MAX.into()), - reserved_cycles_limit: Some(u128::MAX.into()), - log_visibility: Some(LogVisibility::Public), - wasm_memory_limit: Some((2u64.pow(48) - 1).into()), - }), - }; - let canister_id = create_canister(arg, 200_000_000_000_000_000_000_000_000u128) - .await - .unwrap() - .0 - .canister_id; - - let canister_id_record = CanisterIdRecord { canister_id }; - let response = canister_status(canister_id_record).await.unwrap().0; - assert_eq!(response.status, CanisterStatusType::Running); - assert_eq!(response.reserved_cycles.0, 0u128.into()); - let definite_canister_setting = response.settings; - assert_eq!(definite_canister_setting.controllers, vec![ic_cdk::id()]); - assert_eq!(definite_canister_setting.compute_allocation, 1u8); - assert_eq!(definite_canister_setting.memory_allocation, 10000u16); - assert_eq!(definite_canister_setting.freezing_threshold, u64::MAX); - assert_eq!(definite_canister_setting.reserved_cycles_limit, u128::MAX); - assert_eq!( - definite_canister_setting.log_visibility, - LogVisibility::Public - ); - assert_eq!( - definite_canister_setting.wasm_memory_limit, - 2u64.pow(48) - 1 - ); - - let arg = UpdateSettingsArgument { - canister_id, - settings: CanisterSettings::default(), - }; - update_settings(arg).await.unwrap(); - - let arg = InstallCodeArgument { - mode: CanisterInstallMode::Install, - canister_id, - // A minimal valid wasm module - // wat2wasm "(module)" - wasm_module: b"\x00asm\x01\x00\x00\x00".to_vec(), - arg: vec![], - }; - install_code(arg).await.unwrap(); - - uninstall_code(canister_id_record).await.unwrap(); - start_canister(canister_id_record).await.unwrap(); - stop_canister(canister_id_record).await.unwrap(); - deposit_cycles(canister_id_record, 1_000_000_000_000u128) - .await - .unwrap(); - delete_canister(canister_id_record).await.unwrap(); - let response = raw_rand().await.unwrap().0; - assert_eq!(response.len(), 32); - } - - #[update] - async fn execute_subnet_info(subnet_id: Principal) { - let arg = SubnetInfoArgs { subnet_id }; - let response = subnet_info(arg).await.unwrap().0; - assert!(!response.replica_version.is_empty()); - } -} - -mod provisional { - use super::*; - use api::management_canister::main::LogVisibility; - use ic_cdk::api::management_canister::provisional::*; - - #[update] - async fn execute_provisional_methods() { - let settings = CanisterSettings { - controllers: Some(vec![ic_cdk::caller()]), - compute_allocation: Some(50u8.into()), - memory_allocation: Some(10000u16.into()), - freezing_threshold: Some(10000u16.into()), - reserved_cycles_limit: Some(10000u16.into()), - log_visibility: Some(LogVisibility::Public), - wasm_memory_limit: Some(10000u16.into()), - }; - let arg = ProvisionalCreateCanisterWithCyclesArgument { - amount: Some(10_000_000_000_000u64.into()), - settings: Some(settings), - }; - let canister_id = provisional_create_canister_with_cycles(arg) - .await - .unwrap() - .0 - .canister_id; - - let arg = ProvisionalTopUpCanisterArgument { - canister_id, - amount: 1_000_000_000u64.into(), - }; - provisional_top_up_canister(arg).await.unwrap(); - } -} - -mod snapshot { - use super::*; - use ic_cdk::api::management_canister::main::*; - - #[update] - async fn execute_snapshot_methods() { - let arg = CreateCanisterArgument::default(); - let canister_id = create_canister(arg, 2_000_000_000_000u128) - .await - .unwrap() - .0 - .canister_id; - - // Cannot take a snapshot of a canister that is empty. - // So we install a minimal wasm module. - let arg = InstallCodeArgument { - mode: CanisterInstallMode::Install, - canister_id, - // A minimal valid wasm module - // wat2wasm "(module)" - wasm_module: b"\x00asm\x01\x00\x00\x00".to_vec(), - arg: vec![], - }; - install_code(arg).await.unwrap(); - - let arg = TakeCanisterSnapshotArgs { - canister_id, - replace_snapshot: None, - }; - let snapshot = take_canister_snapshot(arg).await.unwrap().0; - - let arg = LoadCanisterSnapshotArgs { - canister_id, - snapshot_id: snapshot.id.clone(), - sender_canister_version: None, - }; - assert!(load_canister_snapshot(arg).await.is_ok()); - - let canister_id_record = CanisterIdRecord { canister_id }; - let snapshots = list_canister_snapshots(canister_id_record).await.unwrap().0; - assert_eq!(snapshots.len(), 1); - assert_eq!(snapshots[0].id, snapshot.id); - - let arg = DeleteCanisterSnapshotArgs { - canister_id, - snapshot_id: snapshot.id.clone(), - }; - assert!(delete_canister_snapshot(arg).await.is_ok()); - - let arg = CanisterInfoRequest { - canister_id, - num_requested_changes: Some(1), - }; - let canister_info_response = canister_info(arg).await.unwrap().0; - assert_eq!(canister_info_response.total_num_changes, 3); - assert_eq!(canister_info_response.recent_changes.len(), 1); - if let CanisterChange { - details: CanisterChangeDetails::LoadSnapshot(load_snapshot_record), - .. - } = &canister_info_response.recent_changes[0] - { - assert_eq!(load_snapshot_record.snapshot_id, snapshot.id); - } else { - panic!("Expected the most recent change to be LoadSnapshot"); - } - } -} - -fn main() {} diff --git a/e2e-tests/canisters/reverse.rs b/e2e-tests/canisters/reverse.rs deleted file mode 100644 index 5afa9ce12..000000000 --- a/e2e-tests/canisters/reverse.rs +++ /dev/null @@ -1,14 +0,0 @@ -use ic_cdk::api::call::{arg_data_raw, arg_data_raw_size, reply_raw}; - -#[export_name = "canister_query reverse"] -fn reverse() { - let arg_bytes: Vec = arg_data_raw(); - assert_eq!(arg_bytes.len(), arg_data_raw_size()); - reply_raw(arg_bytes.into_iter().rev().collect::>().as_ref()); -} -#[export_name = "canister_update empty_call"] -fn empty_call() { - reply_raw(&[]); -} - -fn main() {} diff --git a/e2e-tests/src/bin/api.rs b/e2e-tests/src/bin/api.rs new file mode 100644 index 000000000..9abe2f033 --- /dev/null +++ b/e2e-tests/src/bin/api.rs @@ -0,0 +1,158 @@ +use candid::Principal; +use ic_cdk::api::*; + +#[export_name = "canister_update call_msg_arg_data"] +fn call_msg_arg_data() { + assert_eq!(msg_arg_data(), vec![42]); + msg_reply(vec![]); +} + +#[export_name = "canister_update call_msg_caller"] +fn call_msg_caller() { + assert_eq!(msg_caller(), Principal::anonymous()); + msg_reply(vec![]); +} + +#[export_name = "canister_update call_msg_reply"] +fn call_msg_reply() { + msg_reply(vec![42]); +} + +#[export_name = "canister_update call_msg_reject"] +fn call_msg_reject() { + msg_reject("e2e test reject"); +} + +#[export_name = "canister_update call_msg_cycles_available"] +fn call_msg_cycles_available() { + assert_eq!(msg_cycles_available(), 0); + msg_reply(vec![]); +} + +#[export_name = "canister_update call_msg_cycles_accept"] +fn call_msg_cycles_accept() { + // The available cycles are 0, so the actual cycles accepted are 0. + assert_eq!(msg_cycles_accept(1000), 0); + msg_reply(vec![]); +} + +#[export_name = "canister_update call_cycles_burn"] +fn call_cycles_burn() { + assert_eq!(cycles_burn(1000), 1000); + msg_reply(vec![]); +} + +#[export_name = "canister_update call_canister_self"] +fn call_canister_self() { + let self_id = canister_self(); + // The sender sended canister ID + let data = msg_arg_data(); + assert_eq!(self_id.as_slice(), data); + msg_reply(vec![]); +} + +#[export_name = "canister_update call_canister_cycle_balance"] +fn call_canister_cycle_balance() { + assert!(canister_cycle_balance() > 0); + msg_reply(vec![]); +} + +#[export_name = "canister_update call_canister_status"] +fn call_canister_status() { + assert_eq!(canister_status(), CanisterStatusCode::Running); + assert_eq!(canister_status(), 1); + msg_reply(vec![]); +} + +#[export_name = "canister_update call_canister_version"] +fn call_canister_version() { + assert!(canister_version() > 0); + msg_reply(vec![]); +} + +#[export_name = "canister_inspect_message"] +fn inspect_message() { + assert!(msg_method_name().starts_with("call_")); + accept_message(); +} + +#[export_name = "canister_update call_stable"] +fn call_stable() { + assert_eq!(stable_size(), 0); + assert_eq!(stable_grow(1), 0); + let data = vec![42]; + stable_write(0, &data); + let mut read_buf = vec![0]; + stable_read(0, &mut read_buf); + assert_eq!(read_buf, data); + msg_reply(vec![]); +} + +#[export_name = "canister_update call_certified_data_set"] +fn call_certified_data_set() { + certified_data_set(vec![42]); + msg_reply(vec![]); +} + +#[export_name = "canister_query call_data_certificate"] +fn call_data_certificate() { + assert!(data_certificate().is_some()); + msg_reply(vec![]); +} + +#[export_name = "canister_update call_time"] +fn call_time() { + assert!(time() > 0); + msg_reply(vec![]); +} + +#[export_name = "canister_update call_performance_counter"] +fn call_performance_counter() { + let t0 = PerformanceCounterType::InstructionCounter; + assert_eq!(t0, 0); + let ic0 = performance_counter(0); + let ic1 = performance_counter(t0); + let ic2 = instruction_counter(); + assert!(ic0 < ic1); + assert!(ic1 < ic2); + + let t1 = PerformanceCounterType::CallContextInstructionCounter; + assert_eq!(t1, 1); + let ccic0 = performance_counter(1); + let ccic1 = performance_counter(t1); + let ccic2 = call_context_instruction_counter(); + assert!(ccic0 < ccic1); + assert!(ccic1 < ccic2); + msg_reply(vec![]); +} + +#[export_name = "canister_update call_is_controller"] +fn call_is_controller() { + // The canister was created by the anonymous principal. + assert!(is_controller(&Principal::anonymous())); + msg_reply(vec![]); +} + +/// This entry point will be called by both update and query calls. +/// The query call will return 0, and the update call will return 1. +#[export_name = "canister_query call_in_replicated_execution"] +fn call_in_replicated_execution() { + let res = match in_replicated_execution() { + true => 1, + false => 0, + }; + msg_reply(vec![res]); +} + +#[export_name = "canister_update call_debug_print"] +fn call_debug_print() { + debug_print("Hello, world!"); + msg_reply(vec![]); +} + +#[export_name = "canister_update call_trap"] +fn call_trap() { + trap("It's a trap!"); +} + +fn main() {} diff --git a/e2e-tests/canisters/api_call.rs b/e2e-tests/src/bin/api_call.rs similarity index 100% rename from e2e-tests/canisters/api_call.rs rename to e2e-tests/src/bin/api_call.rs diff --git a/e2e-tests/canisters/async.rs b/e2e-tests/src/bin/async.rs similarity index 59% rename from e2e-tests/canisters/async.rs rename to e2e-tests/src/bin/async.rs index 73873dd71..1c4571b2c 100644 --- a/e2e-tests/canisters/async.rs +++ b/e2e-tests/src/bin/async.rs @@ -1,4 +1,5 @@ use candid::Principal; +use ic_cdk::call::{Call, CallError, CallResult, SendableCall}; use ic_cdk::{query, update}; use lazy_static::lazy_static; use std::sync::RwLock; @@ -31,9 +32,11 @@ async fn panic_after_async() { let value = *lock; // Do not drop the lock before the await point. - let _: (u64,) = ic_cdk::call(ic_cdk::api::id(), "inc", (value,)) + let _: u64 = Call::new(ic_cdk::api::canister_self(), "inc") + .with_arg(value) + .call() .await - .expect("failed to call self"); + .unwrap(); ic_cdk::api::trap("Goodbye, cruel world.") } @@ -47,9 +50,10 @@ async fn panic_twice() { } async fn async_then_panic() { - let _: (u64,) = ic_cdk::call(ic_cdk::api::id(), "on_notify", ()) + let _: u64 = Call::new(ic_cdk::api::canister_self(), "on_notify") + .call() .await - .expect("Failed to call self"); + .unwrap(); panic!(); } @@ -65,12 +69,14 @@ fn on_notify() { #[update] fn notify(whom: Principal, method: String) { - ic_cdk::notify(whom, method.as_str(), ()).unwrap_or_else(|reject| { - ic_cdk::api::trap(&format!( - "failed to notify (callee={}, method={}): {:?}", - whom, method, reject - )) - }); + Call::new(whom, method.as_str()) + .call_oneway() + .unwrap_or_else(|reject| { + ic_cdk::api::trap(format!( + "failed to notify (callee={}, method={}): {:?}", + whom, method, reject + )) + }); } #[query] @@ -80,24 +86,31 @@ fn greet(name: String) -> String { #[query(composite = true)] async fn greet_self(greeter: Principal) -> String { - let (greeting,) = ic_cdk::api::call::call(greeter, "greet", ("myself",)) + Call::new(greeter, "greet") + .with_arg("myself") + .call() .await - .unwrap(); - greeting + .unwrap() } #[update] async fn invalid_reply_payload_does_not_trap() -> String { // We're decoding an integer instead of a string, decoding must fail. - let result: Result<(u64,), _> = - ic_cdk::call(ic_cdk::api::id(), "greet", ("World".to_string(),)).await; + let result: CallResult = Call::new(ic_cdk::api::canister_self(), "greet") + .with_arg("World") + .call() + .await; match result { - Ok((_n,)) => ic_cdk::api::trap("expected the decoding to fail"), - Err((err_code, _)) => format!( - "handled decoding error gracefully with code {}", - err_code as i32 - ), + Ok(_) => ic_cdk::api::trap("expected the decoding to fail"), + Err(e) => match e { + CallError::CandidDecodeFailed(candid_err) => { + format!("handled decoding error gracefully with candid error: {candid_err}") + } + other_err => ic_cdk::api::trap(format!( + "expected a CandidDecodeFailed error, got {other_err}" + )), + }, } } diff --git a/e2e-tests/src/bin/call.rs b/e2e-tests/src/bin/call.rs new file mode 100644 index 000000000..8fe0b91a2 --- /dev/null +++ b/e2e-tests/src/bin/call.rs @@ -0,0 +1,243 @@ +use candid::Encode; +use ic_cdk::api::canister_self; +use ic_cdk::call::{Call, ConfigurableCall, DecoderConfig, SendableCall}; +use ic_cdk::update; + +/// A simple endpoint that takes empty arguments. +#[update] +async fn foo() -> u32 { + 0 +} + +/// `Call::new(...)` can be configured and called. +#[update] +async fn call_foo() { + let n = 0u32; + let bytes = Encode!(&n).unwrap(); + + let res: u32 = Call::new(canister_self(), "foo").call().await.unwrap(); + assert_eq!(res, n); + let res: (u32,) = Call::new(canister_self(), "foo") + .call_tuple() + .await + .unwrap(); + assert_eq!(res.0, n); + let res = Call::new(canister_self(), "foo").call_raw().await.unwrap(); + assert_eq!(res, bytes); + Call::new(canister_self(), "foo").call_oneway().unwrap(); + + let res: (u32,) = Call::new(canister_self(), "foo") + .with_guaranteed_response() + .call_tuple() + .await + .unwrap(); + assert_eq!(res.0, n); + let res: (u32,) = Call::new(canister_self(), "foo") + .change_timeout(5) + .call_tuple() + .await + .unwrap(); + assert_eq!(res.0, n); + let res: (u32,) = Call::new(canister_self(), "foo") + .with_cycles(1000) + .call_tuple() + .await + .unwrap(); + assert_eq!(res.0, n); + let decoder_config = DecoderConfig::default(); + + let res: (u32,) = Call::new(canister_self(), "foo") + .with_decoder_config(decoder_config) + .call_tuple() + .await + .unwrap(); + assert_eq!(res.0, n); +} + +/// A simple endpoint that takes a single `u32` argument and returns it. +#[update] +async fn echo(arg: u32) -> u32 { + arg +} + +/// `Call::new(...).with_arg(...)` can be configured and called. +#[update] +async fn call_echo_with_arg() { + let n = 1u32; + let bytes = Encode!(&n).unwrap(); + // call* + let res: u32 = Call::new(canister_self(), "echo") + .with_arg(n) + .call() + .await + .unwrap(); + assert_eq!(res, n); + let res: (u32,) = Call::new(canister_self(), "echo") + .with_arg(n) + .call_tuple() + .await + .unwrap(); + assert_eq!(res.0, n); + let res = Call::new(canister_self(), "echo") + .with_arg(n) + .call_raw() + .await + .unwrap(); + assert_eq!(res, bytes); + Call::new(canister_self(), "echo") + .with_arg(n) + .call_oneway() + .unwrap(); + // with* + let res: (u32,) = Call::new(canister_self(), "echo") + .with_arg(n) + .with_guaranteed_response() + .call_tuple() + .await + .unwrap(); + assert_eq!(res.0, n); + let res: (u32,) = Call::new(canister_self(), "echo") + .with_arg(n) + .change_timeout(5) + .call_tuple() + .await + .unwrap(); + assert_eq!(res.0, n); + let res: (u32,) = Call::new(canister_self(), "echo") + .with_arg(n) + .with_cycles(1000) + .call_tuple() + .await + .unwrap(); + assert_eq!(res.0, n); + let decoder_config = DecoderConfig::default(); + let res: (u32,) = Call::new(canister_self(), "echo") + .with_arg(n) + .with_decoder_config(decoder_config) + .call_tuple() + .await + .unwrap(); + assert_eq!(res.0, n); +} + +/// `Call::new(...).with_args(...)` can be configured and called. +#[update] +async fn call_echo_with_args() { + let n = 1u32; + let bytes = Encode!(&n).unwrap(); + // call* + let res: u32 = Call::new(canister_self(), "echo") + .with_args((n,)) + .call() + .await + .unwrap(); + assert_eq!(res, n); + let res: (u32,) = Call::new(canister_self(), "echo") + .with_args((n,)) + .call_tuple() + .await + .unwrap(); + assert_eq!(res.0, n); + let res = Call::new(canister_self(), "echo") + .with_args((n,)) + .call_raw() + .await + .unwrap(); + assert_eq!(res, bytes); + Call::new(canister_self(), "echo") + .with_args((n,)) + .call_oneway() + .unwrap(); + // with* + let res: (u32,) = Call::new(canister_self(), "echo") + .with_args((n,)) + .with_guaranteed_response() + .call_tuple() + .await + .unwrap(); + assert_eq!(res.0, n); + let res: (u32,) = Call::new(canister_self(), "echo") + .with_args((n,)) + .change_timeout(5) + .call_tuple() + .await + .unwrap(); + assert_eq!(res.0, n); + let res: (u32,) = Call::new(canister_self(), "echo") + .with_args((n,)) + .with_cycles(1000) + .call_tuple() + .await + .unwrap(); + assert_eq!(res.0, n); + let decoder_config = DecoderConfig::default(); + let res: (u32,) = Call::new(canister_self(), "echo") + .with_args((n,)) + .with_decoder_config(decoder_config) + .call_tuple() + .await + .unwrap(); + assert_eq!(res.0, n); +} + +/// Call::new(...).with_raw_args(...) can be configured and called. +#[update] +async fn call_echo_with_raw_args() { + let n = 1u32; + let bytes: Vec = Encode!(&n).unwrap(); + // call* + let res: u32 = Call::new(canister_self(), "echo") + .with_raw_args(&bytes) + .call() + .await + .unwrap(); + assert_eq!(res, n); + let res: (u32,) = Call::new(canister_self(), "echo") + .with_raw_args(&bytes) + .call_tuple() + .await + .unwrap(); + assert_eq!(res.0, n); + let res = Call::new(canister_self(), "echo") + .with_raw_args(&bytes) + .call_raw() + .await + .unwrap(); + assert_eq!(res, bytes); + Call::new(canister_self(), "echo") + .with_raw_args(&bytes) + .call_oneway() + .unwrap(); + // with* + let res: (u32,) = Call::new(canister_self(), "echo") + .with_raw_args(&bytes) + .with_guaranteed_response() + .call_tuple() + .await + .unwrap(); + assert_eq!(res.0, n); + let res: (u32,) = Call::new(canister_self(), "echo") + .with_raw_args(&bytes) + .change_timeout(5) + .call_tuple() + .await + .unwrap(); + assert_eq!(res.0, n); + let res: (u32,) = Call::new(canister_self(), "echo") + .with_raw_args(&bytes) + .with_cycles(1000) + .call_tuple() + .await + .unwrap(); + assert_eq!(res.0, n); + let decoder_config = DecoderConfig::default(); + let res: (u32,) = Call::new(canister_self(), "echo") + .with_raw_args(&bytes) + .with_decoder_config(decoder_config) + .call_tuple() + .await + .unwrap(); + assert_eq!(res.0, n); +} + +fn main() {} diff --git a/e2e-tests/canisters/canister_info.rs b/e2e-tests/src/bin/canister_info.rs similarity index 55% rename from e2e-tests/canisters/canister_info.rs rename to e2e-tests/src/bin/canister_info.rs index 9a4bc2078..0d0568883 100644 --- a/e2e-tests/canisters/canister_info.rs +++ b/e2e-tests/src/bin/canister_info.rs @@ -1,68 +1,66 @@ use candid::Principal; -use ic_cdk::api::management_canister::main::{ +use ic_cdk::management_canister::{ canister_info, create_canister, install_code, uninstall_code, update_settings, - CanisterIdRecord, CanisterInfoRequest, CanisterInfoResponse, + CanisterInfoArgs, CanisterInfoResult, CanisterInstallMode::{Install, Reinstall, Upgrade}, - CanisterSettings, CreateCanisterArgument, InstallCodeArgument, UpdateSettingsArgument, + CanisterSettings, CreateCanisterArgs, InstallCodeArgs, UninstallCodeArgs, UpdateSettingsArgs, }; #[ic_cdk::update] -async fn info(canister_id: Principal) -> CanisterInfoResponse { - let request = CanisterInfoRequest { +async fn info(canister_id: Principal) -> CanisterInfoResult { + let request = CanisterInfoArgs { canister_id, num_requested_changes: Some(20), }; - canister_info(request).await.unwrap().0 + canister_info(request).await.unwrap() } #[ic_cdk::update] async fn canister_lifecycle() -> Principal { - let canister_id = create_canister(CreateCanisterArgument { settings: None }, 1_000_000_000_000) + let canister_id = create_canister(CreateCanisterArgs { settings: None }, 1_000_000_000_000) .await .unwrap() - .0; - install_code(InstallCodeArgument { + .canister_id; + install_code(InstallCodeArgs { mode: Install, arg: vec![], wasm_module: vec![0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00], - canister_id: canister_id.canister_id, - }) - .await - .unwrap(); - uninstall_code(CanisterIdRecord { - canister_id: canister_id.canister_id, + canister_id, }) .await .unwrap(); - install_code(InstallCodeArgument { + uninstall_code(UninstallCodeArgs { canister_id }) + .await + .unwrap(); + install_code(InstallCodeArgs { mode: Install, arg: vec![], wasm_module: vec![0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00], - canister_id: canister_id.canister_id, + canister_id, }) .await .unwrap(); - install_code(InstallCodeArgument { + install_code(InstallCodeArgs { mode: Reinstall, arg: vec![], wasm_module: vec![0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00], - canister_id: canister_id.canister_id, + canister_id, }) .await .unwrap(); - install_code(InstallCodeArgument { + install_code(InstallCodeArgs { mode: Upgrade(None), arg: vec![], wasm_module: vec![0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00], - canister_id: canister_id.canister_id, + canister_id, }) .await .unwrap(); - update_settings(UpdateSettingsArgument { + update_settings(UpdateSettingsArgs { settings: CanisterSettings { controllers: Some(vec![ - ic_cdk::id(), - canister_id.canister_id, + ic_cdk::api::canister_self(), + canister_id, Principal::anonymous(), ]), compute_allocation: None, @@ -72,11 +70,11 @@ async fn canister_lifecycle() -> Principal { log_visibility: None, wasm_memory_limit: None, }, - canister_id: canister_id.canister_id, + canister_id, }) .await .unwrap(); - canister_id.canister_id + canister_id } fn main() {} diff --git a/e2e-tests/canisters/chunk.rs b/e2e-tests/src/bin/chunk.rs similarity index 69% rename from e2e-tests/canisters/chunk.rs rename to e2e-tests/src/bin/chunk.rs index 9f387a6bd..2380b0ead 100644 --- a/e2e-tests/canisters/chunk.rs +++ b/e2e-tests/src/bin/chunk.rs @@ -1,42 +1,41 @@ use candid::Principal; -use ic_cdk::api::management_canister::main::{ +use ic_cdk::management_canister::{ clear_chunk_store, create_canister, install_chunked_code, stored_chunks, upload_chunk, - CanisterInstallMode, ChunkHash, ClearChunkStoreArgument, CreateCanisterArgument, - InstallChunkedCodeArgument, StoredChunksArgument, UploadChunkArgument, + CanisterInstallMode, ChunkHash, ClearChunkStoreArgs, CreateCanisterArgs, + InstallChunkedCodeArgs, StoredChunksArgs, UploadChunkArgs, }; use ic_cdk::update; #[update] async fn call_create_canister() -> Principal { - let arg = CreateCanisterArgument::default(); + let arg = CreateCanisterArgs::default(); create_canister(arg, 1_000_000_000_000u128) .await .unwrap() - .0 .canister_id } #[update] async fn call_upload_chunk(canister_id: Principal, chunk: Vec) -> Vec { - let arg = UploadChunkArgument { + let arg = UploadChunkArgs { canister_id, chunk: chunk.to_vec(), }; - upload_chunk(arg).await.unwrap().0.hash + upload_chunk(arg).await.unwrap().hash } #[update] async fn call_stored_chunks(canister_id: Principal) -> Vec> { - let arg = StoredChunksArgument { canister_id }; - let hashes = stored_chunks(arg).await.unwrap().0; + let arg = StoredChunksArgs { canister_id }; + let hashes = stored_chunks(arg).await.unwrap(); hashes.into_iter().map(|v| v.hash).collect() } #[update] async fn call_clear_chunk_store(canister_id: Principal) { - let arg = ClearChunkStoreArgument { canister_id }; + let arg = ClearChunkStoreArgs { canister_id }; clear_chunk_store(arg).await.unwrap(); } @@ -50,7 +49,7 @@ async fn call_install_chunked_code( .iter() .map(|v| ChunkHash { hash: v.clone() }) .collect(); - let arg = InstallChunkedCodeArgument { + let arg = InstallChunkedCodeArgs { mode: CanisterInstallMode::Install, target_canister: canister_id, store_canister: None, diff --git a/e2e-tests/src/bin/http_request.rs b/e2e-tests/src/bin/http_request.rs new file mode 100644 index 000000000..fa738adb6 --- /dev/null +++ b/e2e-tests/src/bin/http_request.rs @@ -0,0 +1,152 @@ +use ic_cdk::management_canister::{ + http_request, http_request_with_closure, HttpHeader, HttpMethod, HttpRequestArgs, + HttpRequestResult, TransformArgs, TransformContext, +}; +use ic_cdk::{query, update}; + +/// The formula to calculate the cost of a request. +fn cycles_cost(args: &HttpRequestArgs) -> u128 { + const N: u128 = 13; + let request_bytes_len = (args.url.len() + + args + .headers + .iter() + .map(|h| h.name.len() + h.value.len()) + .sum::() + + args.body.as_ref().map(|b| b.len()).unwrap_or(0) + + args + .transform + .as_ref() + .map(|t| t.context.len() + t.function.0.method.len()) + .unwrap_or(0)) as u128; + let response_bytes_len = args.max_response_bytes.unwrap_or(2_000_000) as u128; + (3_000_000 + 60_000 * N) * N + 400 * N * request_bytes_len + 800 * N * response_bytes_len +} + +/// All fields are Some except transform. +#[update] +async fn get_without_transform() { + let args = HttpRequestArgs { + url: "https://example.com".to_string(), + method: HttpMethod::GET, + headers: vec![HttpHeader { + name: "request_header_name".to_string(), + value: "request_header_value".to_string(), + }], + body: Some(vec![1]), + max_response_bytes: Some(100_000), + transform: None, + }; + let cycles = cycles_cost(&args); + let res = http_request(args, cycles).await.unwrap(); + assert_eq!(res.status, 200u32); + assert_eq!( + res.headers, + vec![HttpHeader { + name: "response_header_name".to_string(), + value: "response_header_value".to_string(), + }] + ); + assert_eq!(res.body, vec![42]); +} + +/// Method is POST. +#[update] +async fn post() { + let args = HttpRequestArgs { + url: "https://example.com".to_string(), + method: HttpMethod::POST, + ..Default::default() + }; + let cycles = cycles_cost(&args); + http_request(args, cycles).await.unwrap(); +} + +/// Method is HEAD. +#[update] +async fn head() { + let args = HttpRequestArgs { + url: "https://example.com".to_string(), + method: HttpMethod::HEAD, + ..Default::default() + }; + let cycles = cycles_cost(&args); + http_request(args, cycles).await.unwrap(); +} + +/// The standard way to define a transform function. +/// +/// It is a query endpoint that takes a TransformArgs and returns an HttpRequestResult. +#[query] +fn transform(args: TransformArgs) -> HttpRequestResult { + let mut body = args.response.body; + body.push(args.context[0]); + HttpRequestResult { + status: args.response.status, + headers: args.response.headers, + body, + } +} + +/// Set the transform field with the name of the transform query endpoint. +#[update] +async fn get_with_transform() { + let args = HttpRequestArgs { + url: "https://example.com".to_string(), + method: HttpMethod::GET, + transform: Some(TransformContext::from_name( + "transform".to_string(), + vec![42], + )), + ..Default::default() + }; + let cycles = cycles_cost(&args); + let res = http_request(args, cycles).await.unwrap(); + assert_eq!(res.status, 200u32); + assert_eq!( + res.headers, + vec![HttpHeader { + name: "response_header_name".to_string(), + value: "response_header_value".to_string(), + }] + ); + // The first 42 is from the response body, the second 42 is from the transform context. + assert_eq!(res.body, vec![42, 42]); +} + +/// Set the transform field with a closure. +#[update] +async fn get_with_transform_closure() { + let transform = |args: HttpRequestResult| { + let mut body = args.body; + body.push(42); + HttpRequestResult { + status: args.status, + headers: args.headers, + body, + } + }; + let args = HttpRequestArgs { + url: "https://example.com".to_string(), + method: HttpMethod::GET, + transform: None, + ..Default::default() + }; + // The transform closure takes 40 bytes. + let cycles = cycles_cost(&args) + 40 * 400 * 13; + let res = http_request_with_closure(args.clone(), cycles, transform) + .await + .unwrap(); + assert_eq!(res.status, 200u32); + assert_eq!( + res.headers, + vec![HttpHeader { + name: "response_header_name".to_string(), + value: "response_header_value".to_string(), + }] + ); + // The first 42 is from the response body, the second 42 is from the transform closure. + assert_eq!(res.body, vec![42, 42]); +} + +fn main() {} diff --git a/e2e-tests/src/bin/management_canister.rs b/e2e-tests/src/bin/management_canister.rs new file mode 100644 index 000000000..8e25925b5 --- /dev/null +++ b/e2e-tests/src/bin/management_canister.rs @@ -0,0 +1,280 @@ +use candid::Principal; +use ic_cdk::api::canister_self; +use ic_cdk::management_canister::*; +use ic_cdk::update; +use sha2::Digest; + +#[update] +async fn basic() { + // create_canister + let self_id = canister_self(); + let arg = CreateCanisterArgs { + settings: Some(CanisterSettings { + controllers: Some(vec![self_id]), + compute_allocation: Some(0u8.into()), + memory_allocation: Some(0u8.into()), + freezing_threshold: Some(0u8.into()), + reserved_cycles_limit: Some(0u8.into()), + log_visibility: Some(LogVisibility::Public), + wasm_memory_limit: Some(0u8.into()), + }), + }; + // 500 B is the minimum cycles required to create a canister. + // Here we set 1 T cycles for other operations below. + let canister_id = create_canister(arg, 1_000_000_000_000u128) + .await + .unwrap() + .canister_id; + + // canister_status + let arg = CanisterStatusArgs { canister_id }; + let result = canister_status(arg).await.unwrap(); + assert_eq!(result.status, CanisterStatusType::Running); + assert_eq!(result.reserved_cycles.0, 0u128.into()); + let definite_canister_setting = result.settings; + assert_eq!(definite_canister_setting.controllers, vec![self_id]); + assert_eq!(definite_canister_setting.compute_allocation, 0u8); + assert_eq!(definite_canister_setting.memory_allocation, 0u8); + assert_eq!(definite_canister_setting.freezing_threshold, 0u8); + assert_eq!(definite_canister_setting.reserved_cycles_limit, 0u8); + assert_eq!( + definite_canister_setting.log_visibility, + LogVisibility::Public + ); + assert_eq!(definite_canister_setting.wasm_memory_limit, 0u8); + + // update_settings + let arg = UpdateSettingsArgs { + canister_id, + settings: CanisterSettings { + freezing_threshold: Some(0u16.into()), + log_visibility: Some(LogVisibility::AllowedViewers(vec![self_id])), + ..Default::default() + }, + }; + update_settings(arg).await.unwrap(); + + // install_code + let arg = InstallCodeArgs { + mode: CanisterInstallMode::Install, + canister_id, + // A minimal valid wasm module + // wat2wasm "(module)" + wasm_module: b"\x00asm\x01\x00\x00\x00".to_vec(), + arg: vec![], + }; + install_code(arg).await.unwrap(); + + // uninstall_code + let arg = UninstallCodeArgs { canister_id }; + uninstall_code(arg).await.unwrap(); + + // start_canister + let arg = StartCanisterArgs { canister_id }; + start_canister(arg).await.unwrap(); + + // stop_canister + let arg = StopCanisterArgs { canister_id }; + stop_canister(arg).await.unwrap(); + + // deposit_cycles + let arg = DepositCyclesArgs { canister_id }; + deposit_cycles(arg, 1_000_000_000_000u128).await.unwrap(); + + // delete_canister + let arg = DeleteCanisterArgs { canister_id }; + delete_canister(arg).await.unwrap(); + + // raw_rand + let bytes = raw_rand().await.unwrap(); + assert_eq!(bytes.len(), 32); +} + +#[update] +async fn ecdsa() { + // ecdsa_public_key + let key_id = EcdsaKeyId { + curve: EcdsaCurve::Secp256k1, + name: "test_key_1".to_string(), + }; + let derivation_path = vec![]; + let arg = EcdsaPublicKeyArgs { + canister_id: None, + derivation_path: derivation_path.clone(), + key_id: key_id.clone(), + }; + let EcdsaPublicKeyResult { + public_key, + chain_code, + } = ecdsa_public_key(arg).await.unwrap(); + assert_eq!(public_key.len(), 33); + assert_eq!(chain_code.len(), 32); + + let message = "hello world"; + let message_hash = sha2::Sha256::digest(message).to_vec(); + let arg = SignWithEcdsaArgs { + message_hash, + derivation_path, + key_id, + }; + let SignWithEcdsaResult { signature } = sign_with_ecdsa(arg).await.unwrap(); + assert_eq!(signature.len(), 64); +} + +#[update] +async fn schnorr() { + // schnorr_public_key + let key_id = SchnorrKeyId { + algorithm: SchnorrAlgorithm::Bip340secp256k1, + name: "test_key_1".to_string(), + }; + let derivation_path = vec![]; + let arg = SchnorrPublicKeyArgs { + canister_id: None, + derivation_path: derivation_path.clone(), + key_id: key_id.clone(), + }; + let SchnorrPublicKeyResult { + public_key, + chain_code, + } = schnorr_public_key(arg).await.unwrap(); + assert_eq!(public_key.len(), 33); + assert_eq!(chain_code.len(), 32); + let arg = SchnorrPublicKeyArgs { + canister_id: None, + derivation_path: derivation_path.clone(), + key_id: SchnorrKeyId { + algorithm: SchnorrAlgorithm::Ed25519, + name: "test_key_1".to_string(), + }, + }; + let SchnorrPublicKeyResult { + public_key, + chain_code, + } = schnorr_public_key(arg).await.unwrap(); + assert_eq!(public_key.len(), 32); + assert_eq!(chain_code.len(), 32); + + // sign_with_schnorr + let message = "hello world".into(); + let arg = SignWithSchnorrArgs { + message, + derivation_path, + key_id, + }; + let SignWithSchnorrResult { signature } = sign_with_schnorr(arg).await.unwrap(); + assert_eq!(signature.len(), 64); +} + +#[update] +async fn metrics(subnet_id: Principal) { + // node_metrics_history + let arg = NodeMetricsHistoryArgs { + subnet_id, + start_at_timestamp_nanos: 0, + }; + let result = node_metrics_history(arg).await.unwrap(); + for record in result { + assert!(record.timestamp_nanos > 0); + assert!(!record.node_metrics.is_empty()); + } +} + +#[update] +async fn subnet(subnet_id: Principal) { + // subnet_info + let arg = SubnetInfoArgs { subnet_id }; + let result = subnet_info(arg).await.unwrap(); + assert!(!result.replica_version.is_empty()); +} + +#[update] +async fn provisional() { + // provisional_create_canister_with_cycles + let settings = CanisterSettings { + log_visibility: Some(LogVisibility::Controllers), + ..Default::default() + }; + let arg = ProvisionalCreateCanisterWithCyclesArgs { + amount: Some(10_000_000_000_000u64.into()), + settings: Some(settings), + }; + let canister_id = provisional_create_canister_with_cycles(arg) + .await + .unwrap() + .canister_id; + + // provisional_top_up_canister + let arg = ProvisionalTopUpCanisterArgs { + canister_id, + amount: 1_000_000_000u64.into(), + }; + provisional_top_up_canister(arg).await.unwrap(); +} + +#[update] +async fn snapshots() { + let arg = CreateCanisterArgs::default(); + let canister_id = create_canister(arg, 2_000_000_000_000u128) + .await + .unwrap() + .canister_id; + + // Cannot take a snapshot of a canister that is empty. + // So we install a minimal wasm module. + let arg = InstallCodeArgs { + mode: CanisterInstallMode::Install, + canister_id, + // A minimal valid wasm module + // wat2wasm "(module)" + wasm_module: b"\x00asm\x01\x00\x00\x00".to_vec(), + arg: vec![], + }; + install_code(arg).await.unwrap(); + + // take_canister_snapshot + let arg = TakeCanisterSnapshotArgs { + canister_id, + replace_snapshot: None, + }; + let snapshot = take_canister_snapshot(arg).await.unwrap(); + + // load_canister_snapshot + let arg = LoadCanisterSnapshotArgs { + canister_id, + snapshot_id: snapshot.id.clone(), + }; + assert!(load_canister_snapshot(arg).await.is_ok()); + + // list_canister_snapshots + let args = ListCanisterSnapshotsArgs { canister_id }; + let snapshots = list_canister_snapshots(args).await.unwrap(); + assert_eq!(snapshots.len(), 1); + assert_eq!(snapshots[0].id, snapshot.id); + + // delete_canister_snapshot + let arg = DeleteCanisterSnapshotArgs { + canister_id, + snapshot_id: snapshot.id.clone(), + }; + assert!(delete_canister_snapshot(arg).await.is_ok()); + + // check the above snapshot operations are recorded in the canister's history. + let arg = CanisterInfoArgs { + canister_id, + num_requested_changes: Some(1), + }; + let canister_info_result = canister_info(arg).await.unwrap(); + assert_eq!(canister_info_result.total_num_changes, 3); + assert_eq!(canister_info_result.recent_changes.len(), 1); + if let CanisterChange { + details: CanisterChangeDetails::LoadSnapshot(load_snapshot_record), + .. + } = &canister_info_result.recent_changes[0] + { + assert_eq!(load_snapshot_record.snapshot_id, snapshot.id); + } else { + panic!("Expected the most recent change to be LoadSnapshot"); + } +} +fn main() {} diff --git a/e2e-tests/canisters/simple_kv_store.rs b/e2e-tests/src/bin/simple_kv_store.rs similarity index 100% rename from e2e-tests/canisters/simple_kv_store.rs rename to e2e-tests/src/bin/simple_kv_store.rs diff --git a/e2e-tests/canisters/timers.rs b/e2e-tests/src/bin/timers.rs similarity index 96% rename from e2e-tests/canisters/timers.rs rename to e2e-tests/src/bin/timers.rs index 03433aaa8..bc912569a 100644 --- a/e2e-tests/canisters/timers.rs +++ b/e2e-tests/src/bin/timers.rs @@ -93,8 +93,8 @@ fn add_event(event: &'static str) { } #[update] -fn set_global_timer(timestamp: u64) -> u64 { - ic_cdk::api::set_global_timer(timestamp) +fn global_timer_set(timestamp: u64) -> u64 { + ic_cdk::api::global_timer_set(timestamp) } fn main() {} diff --git a/e2e-tests/src/lib.rs b/e2e-tests/src/lib.rs deleted file mode 100644 index 5187448a0..000000000 --- a/e2e-tests/src/lib.rs +++ /dev/null @@ -1,41 +0,0 @@ -use cargo_metadata::MetadataCommand; -use escargot::CargoBuild; -use std::path::PathBuf; - -/// Builds a canister with the specified name from the current -/// package and returns the WebAssembly module. -pub fn cargo_build_canister(bin_name: &str) -> Vec { - let dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()); - - let cargo_toml_path = dir.join("Cargo.toml"); - - let target_dir = MetadataCommand::new() - .manifest_path(&cargo_toml_path) - .no_deps() - .exec() - .expect("failed to run cargo metadata") - .target_directory; - - // We use a different target path to stop the native cargo build - // cache being invalidated every time we run this function - let wasm_target_dir = target_dir.join("canister-build"); - - let cargo_build = CargoBuild::new() - .target("wasm32-unknown-unknown") - .bin(bin_name) - .args(["--profile", "canister-release"]) - .manifest_path(&cargo_toml_path) - .target_dir(wasm_target_dir); - - let binary = cargo_build - .run() - .expect("Cargo failed to compile the wasm binary"); - - std::fs::read(binary.path()).unwrap_or_else(|e| { - panic!( - "failed to read compiled Wasm file from {}: {}", - binary.path().display(), - e - ) - }) -} diff --git a/e2e-tests/tests/api.rs b/e2e-tests/tests/api.rs new file mode 100644 index 000000000..5b3e4af1c --- /dev/null +++ b/e2e-tests/tests/api.rs @@ -0,0 +1,115 @@ +use candid::Principal; +use pocket_ic::{ErrorCode, UserError, WasmResult}; + +mod test_utilities; +use test_utilities::{cargo_build_canister, pocket_ic}; + +#[test] +fn call_api() { + let pic = pocket_ic(); + let wasm = cargo_build_canister("api"); + let canister_id = pic.create_canister(); + pic.add_cycles(canister_id, 100_000_000_000_000); + pic.install_canister(canister_id, wasm, vec![], None); + let sender = Principal::anonymous(); + let res = pic + .update_call(canister_id, sender, "call_msg_arg_data", vec![42]) + .unwrap(); + assert_eq!(res, WasmResult::Reply(vec![])); + let res = pic + .update_call(canister_id, sender, "call_msg_caller", vec![]) + .unwrap(); + assert_eq!(res, WasmResult::Reply(vec![])); + // `msg_reject_code` and `msg_reject_msg` can't be tested here. + // They are invoked in the reply/reject callback of inter-canister calls. + // So the `call.rs` test covers them. + let res = pic + .update_call(canister_id, sender, "call_msg_reply", vec![]) + .unwrap(); + assert_eq!(res, WasmResult::Reply(vec![42])); + let res = pic + .update_call(canister_id, sender, "call_msg_reject", vec![]) + .unwrap(); + assert_eq!(res, WasmResult::Reject("e2e test reject".to_string())); + let res = pic + .update_call(canister_id, sender, "call_msg_cycles_available", vec![]) + .unwrap(); + assert_eq!(res, WasmResult::Reply(vec![])); + // `msg_cycles_refunded` can't be tested here. + // It can only be called in the reply/reject callback of inter-canister calls. + // TODO: Find a way to test it. + let res = pic + .update_call(canister_id, sender, "call_msg_cycles_accept", vec![]) + .unwrap(); + assert_eq!(res, WasmResult::Reply(vec![])); + let res = pic + .update_call(canister_id, sender, "call_cycles_burn", vec![]) + .unwrap(); + assert_eq!(res, WasmResult::Reply(vec![])); + let res = pic + .update_call( + canister_id, + sender, + "call_canister_self", + canister_id.as_slice().to_vec(), + ) + .unwrap(); + assert_eq!(res, WasmResult::Reply(vec![])); + let res = pic + .update_call(canister_id, sender, "call_canister_cycle_balance", vec![]) + .unwrap(); + assert_eq!(res, WasmResult::Reply(vec![])); + let res = pic + .update_call(canister_id, sender, "call_canister_status", vec![]) + .unwrap(); + assert_eq!(res, WasmResult::Reply(vec![])); + let res = pic + .update_call(canister_id, sender, "call_canister_version", vec![]) + .unwrap(); + assert_eq!(res, WasmResult::Reply(vec![])); + // `msg_method_name` and `accept_message` are invoked in the inspect_message entry point. + // Every calls above/below execute the inspect_message entry point. + // So these two API bindings are tested implicitly. + let res = pic + .update_call(canister_id, sender, "call_stable", vec![]) + .unwrap(); + assert_eq!(res, WasmResult::Reply(vec![])); + let res = pic + .update_call(canister_id, sender, "call_certified_data_set", vec![]) + .unwrap(); + assert_eq!(res, WasmResult::Reply(vec![])); + let res = pic + .query_call(canister_id, sender, "call_data_certificate", vec![]) + .unwrap(); + assert_eq!(res, WasmResult::Reply(vec![])); + let res = pic + .update_call(canister_id, sender, "call_time", vec![]) + .unwrap(); + assert_eq!(res, WasmResult::Reply(vec![])); + // `global_timer_set` is tested in `timers.rs`. + let res = pic + .update_call(canister_id, sender, "call_performance_counter", vec![]) + .unwrap(); + assert_eq!(res, WasmResult::Reply(vec![])); + let res = pic + .update_call(canister_id, sender, "call_is_controller", vec![]) + .unwrap(); + assert_eq!(res, WasmResult::Reply(vec![])); + let res = pic + .update_call(canister_id, sender, "call_in_replicated_execution", vec![]) + .unwrap(); + assert_eq!(res, WasmResult::Reply(vec![1])); + let res = pic + .query_call(canister_id, sender, "call_in_replicated_execution", vec![]) + .unwrap(); + assert_eq!(res, WasmResult::Reply(vec![0])); + let res = pic + .update_call(canister_id, sender, "call_debug_print", vec![]) + .unwrap(); + assert_eq!(res, WasmResult::Reply(vec![])); + let UserError { code, description } = pic + .update_call(canister_id, sender, "call_trap", vec![]) + .unwrap_err(); + assert_eq!(code, ErrorCode::CanisterCalledTrap); + assert!(description.contains("It's a trap!")); +} diff --git a/e2e-tests/tests/api_call.rs b/e2e-tests/tests/api_call.rs new file mode 100644 index 000000000..d605d687d --- /dev/null +++ b/e2e-tests/tests/api_call.rs @@ -0,0 +1,85 @@ +use candid::{Encode, Principal}; +use pocket_ic::common::rest::RawEffectivePrincipal; +use pocket_ic::{call_candid, query_candid, WasmResult}; + +mod test_utilities; +use test_utilities::{cargo_build_canister, pocket_ic}; + +#[test] +fn call_apis() { + let pic = pocket_ic(); + let wasm = cargo_build_canister("api_call"); + let canister_id = pic.create_canister(); + pic.add_cycles(canister_id, 2_000_000_000_000); + pic.install_canister(canister_id, wasm, vec![], None); + let (result,): (u64,) = query_candid(&pic, canister_id, "instruction_counter", ()) + .expect("failed to query instruction_counter"); + assert!(result > 0); + + let result = pic + .query_call( + canister_id, + Principal::anonymous(), + "manual_reject", + Encode!().unwrap(), + ) + .unwrap(); + assert_eq!(result, WasmResult::Reject("manual reject".to_string())); + + let (result,): (bool,) = call_candid( + &pic, + canister_id, + RawEffectivePrincipal::None, + "update_is_replicated", + (), + ) + .expect("Failed to call update_is_replicated"); + assert!(result); + + let (result,): (bool,) = query_candid(&pic, canister_id, "query_is_not_replicated", ()) + .expect("Failed to call query_is_not_replicated"); + assert!(!result); +} + +#[test] +fn cycles_burn() { + let pic = pocket_ic(); + let wasm = cargo_build_canister("api_call"); + let canister_id = pic.create_canister(); + pic.add_cycles(canister_id, 2_000_000_000_000); + + pic.install_canister(canister_id, wasm, vec![], None); + eprintln!("Canister installed."); + let balance1 = pic.cycle_balance(canister_id); + eprintln!("Balance 1: {balance1}"); + + // Scenario 1: burn less than balance + let attempted1 = 1000u128; + let (burned,): (u128,) = call_candid( + &pic, + canister_id, + RawEffectivePrincipal::None, + "cycles_burn", + (attempted1,), + ) + .expect("Error calling cycles_burn"); + eprintln!("Attempted to burn {attempted1}, actually burned {burned}"); + assert_eq!(burned, attempted1); + let balance2 = pic.cycle_balance(canister_id); + eprintln!("Balance 2: {balance2}"); + + // Scenario 2: burn more than balance + let attempted2 = balance2 + 1; + let (burned,): (u128,) = call_candid( + &pic, + canister_id, + RawEffectivePrincipal::None, + "cycles_burn", + (attempted2,), + ) + .expect("Error calling cycles_burn"); + eprintln!("Attempted to burn {attempted2}, actually burned {burned}"); + assert!(burned < balance2); // restrained by reserved_balance and freezing_limit + let balance3 = pic.cycle_balance(canister_id); + eprintln!("Balance 3: {balance3}"); +} diff --git a/e2e-tests/tests/async.rs b/e2e-tests/tests/async.rs new file mode 100644 index 000000000..d341974b7 --- /dev/null +++ b/e2e-tests/tests/async.rs @@ -0,0 +1,131 @@ +use pocket_ic::common::rest::RawEffectivePrincipal; +use pocket_ic::{call_candid, query_candid, CallError, ErrorCode}; + +mod test_utilities; +use test_utilities::{cargo_build_canister, pocket_ic}; + +#[test] +fn panic_after_async_frees_resources() { + let pic = pocket_ic(); + let wasm = cargo_build_canister("async"); + let canister_id = pic.create_canister(); + pic.add_cycles(canister_id, 2_000_000_000_000); + pic.install_canister(canister_id, wasm, vec![], None); + + for i in 1..3 { + match call_candid( + &pic, + canister_id, + RawEffectivePrincipal::None, + "panic_after_async", + (), + ) { + Ok(()) => (), + Err(CallError::Reject(msg)) => panic!("unexpected reject: {}", msg), + Err(CallError::UserError(e)) => { + println!("Got a user error as expected: {}", e); + + assert_eq!(e.code, ErrorCode::CanisterCalledTrap); + let expected_message = "Goodbye, cruel world."; + assert!( + e.description.contains(expected_message), + "Expected the user error to contain '{}', got: {}", + expected_message, + e.description + ); + } + } + + let (n,): (u64,) = call_candid( + &pic, + canister_id, + RawEffectivePrincipal::None, + "invocation_count", + (), + ) + .unwrap(); + + assert_eq!(i, n, "expected the invocation count to be {}, got {}", i, n); + } + + let (message,): (String,) = call_candid( + &pic, + canister_id, + RawEffectivePrincipal::None, + "invalid_reply_payload_does_not_trap", + (), + ) + .unwrap(); + assert!(message.contains("handled decoding error gracefully")); + + let err = call_candid::<_, ()>( + &pic, + canister_id, + RawEffectivePrincipal::None, + "panic_twice", + (), + ) + .expect_err("failed to panic"); + assert!( + matches!(err, CallError::UserError(u) if u.description.contains("Call already trapped")) + ); + let _: (u64,) = call_candid( + &pic, + canister_id, + RawEffectivePrincipal::None, + "notifications_received", + (), + ) + .unwrap(); + let _: (u64,) = call_candid( + &pic, + canister_id, + RawEffectivePrincipal::None, + "invocation_count", + (), + ) + .unwrap(); +} + +#[test] +fn notify_calls() { + let pic = pocket_ic(); + let wasm = cargo_build_canister("async"); + let sender_id = pic.create_canister(); + pic.add_cycles(sender_id, 2_000_000_000_000); + pic.install_canister(sender_id, wasm.clone(), vec![], None); + let receiver_id = pic.create_canister(); + pic.add_cycles(receiver_id, 2_000_000_000_000); + pic.install_canister(receiver_id, wasm, vec![], None); + + let (n,): (u64,) = query_candid(&pic, receiver_id, "notifications_received", ()).unwrap(); + assert_eq!(n, 0); + + let () = call_candid( + &pic, + sender_id, + RawEffectivePrincipal::None, + "notify", + (receiver_id, "on_notify"), + ) + .unwrap(); + + let (n,): (u64,) = query_candid(&pic, receiver_id, "notifications_received", ()).unwrap(); + assert_eq!(n, 1); +} + +#[test] +fn test_composite_query() { + let pic = pocket_ic(); + let wasm = cargo_build_canister("async"); + let sender_id = pic.create_canister(); + pic.add_cycles(sender_id, 2_000_000_000_000); + pic.install_canister(sender_id, wasm.clone(), vec![], None); + let receiver_id = pic.create_canister(); + pic.add_cycles(receiver_id, 2_000_000_000_000); + pic.install_canister(receiver_id, wasm, vec![], None); + + let (greeting,): (String,) = + query_candid(&pic, sender_id, "greet_self", (receiver_id,)).unwrap(); + assert_eq!(greeting, "Hello, myself"); +} diff --git a/e2e-tests/tests/call.rs b/e2e-tests/tests/call.rs new file mode 100644 index 000000000..a1c5a6fb3 --- /dev/null +++ b/e2e-tests/tests/call.rs @@ -0,0 +1,46 @@ +use pocket_ic::call_candid; +use pocket_ic::common::rest::RawEffectivePrincipal; + +mod test_utilities; +use test_utilities::{cargo_build_canister, pocket_ic}; + +#[test] +fn call() { + let pic = pocket_ic(); + let wasm = cargo_build_canister("call"); + let canister_id = pic.create_canister(); + pic.add_cycles(canister_id, 100_000_000_000_000); + pic.install_canister(canister_id, wasm, vec![], None); + let _: () = call_candid( + &pic, + canister_id, + RawEffectivePrincipal::None, + "call_foo", + (), + ) + .unwrap(); + let _: () = call_candid( + &pic, + canister_id, + RawEffectivePrincipal::None, + "call_echo_with_arg", + (), + ) + .unwrap(); + let _: () = call_candid( + &pic, + canister_id, + RawEffectivePrincipal::None, + "call_echo_with_args", + (), + ) + .unwrap(); + let _: () = call_candid( + &pic, + canister_id, + RawEffectivePrincipal::None, + "call_echo_with_raw_args", + (), + ) + .unwrap(); +} diff --git a/e2e-tests/tests/canister_info.rs b/e2e-tests/tests/canister_info.rs new file mode 100644 index 000000000..e96ac88c5 --- /dev/null +++ b/e2e-tests/tests/canister_info.rs @@ -0,0 +1,203 @@ +use candid::Principal; +use ic_cdk::management_canister::{ + CanisterChange, CanisterChangeDetails, CanisterChangeOrigin, CanisterInfoResult, + CanisterInstallMode, + CodeDeploymentMode::{Install, Reinstall, Upgrade}, + CodeDeploymentRecord, ControllersChangeRecord, CreationRecord, FromCanisterRecord, + FromUserRecord, InstallCodeArgs, UninstallCodeArgs, +}; +use pocket_ic::common::rest::RawEffectivePrincipal; +use pocket_ic::{call_candid, call_candid_as}; +use std::time::UNIX_EPOCH; + +mod test_utilities; +use test_utilities::{cargo_build_canister, pocket_ic}; + +#[test] +fn test_canister_info() { + let pic = pocket_ic(); + let wasm = cargo_build_canister("canister_info"); + // As of PocketIC server v5.0.0 and client v4.0.0, the first canister creation happens at (time0+4). + // Each operation advances the Pic by 2 nanos, except for the last operation which advances only by 1 nano. + let time0: u64 = pic + .get_time() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos() + .try_into() + .unwrap(); + let canister_id = pic.create_canister(); + pic.add_cycles(canister_id, 2_000_000_000_000); + pic.install_canister(canister_id, wasm, vec![], None); + + let new_canister: (Principal,) = call_candid( + &pic, + canister_id, + RawEffectivePrincipal::None, + "canister_lifecycle", + (), + ) + .expect("Error calling canister_lifecycle"); + + let () = call_candid_as( + &pic, + Principal::management_canister(), + RawEffectivePrincipal::None, + Principal::anonymous(), + "uninstall_code", + (UninstallCodeArgs { + canister_id: new_canister.0, + },), + ) + .expect("Error calling uninstall_code"); + let () = call_candid_as( + &pic, + Principal::management_canister(), + RawEffectivePrincipal::None, + Principal::anonymous(), + "install_code", + (InstallCodeArgs { + mode: CanisterInstallMode::Install, + arg: vec![], + wasm_module: vec![0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00], + canister_id: new_canister.0, + },), + ) + .expect("Error calling install_code"); + + let info: (CanisterInfoResult,) = call_candid( + &pic, + canister_id, + RawEffectivePrincipal::None, + "info", + (new_canister.0,), + ) + .expect("Error calling canister_info"); + + assert_eq!( + info.0, + CanisterInfoResult { + total_num_changes: 9, + recent_changes: vec![ + CanisterChange { + timestamp_nanos: time0 + 4, + canister_version: 0, + origin: CanisterChangeOrigin::FromCanister(FromCanisterRecord { + canister_id, + canister_version: Some(1) + }), + details: CanisterChangeDetails::Creation(CreationRecord { + controllers: vec![canister_id] + }), + }, + CanisterChange { + timestamp_nanos: time0 + 6, + canister_version: 1, + origin: CanisterChangeOrigin::FromCanister(FromCanisterRecord { + canister_id, + canister_version: Some(2) + }), + details: CanisterChangeDetails::CodeDeployment(CodeDeploymentRecord { + mode: Install, + module_hash: hex::decode( + "93a44bbb96c751218e4c00d479e4c14358122a389acca16205b1e4d0dc5f9476" + ) + .unwrap(), + }), + }, + CanisterChange { + timestamp_nanos: time0 + 8, + canister_version: 2, + origin: CanisterChangeOrigin::FromCanister(FromCanisterRecord { + canister_id, + canister_version: Some(3) + }), + details: CanisterChangeDetails::CodeUninstall, + }, + CanisterChange { + timestamp_nanos: time0 + 10, + canister_version: 3, + origin: CanisterChangeOrigin::FromCanister(FromCanisterRecord { + canister_id, + canister_version: Some(4) + }), + details: CanisterChangeDetails::CodeDeployment(CodeDeploymentRecord { + mode: Install, + module_hash: hex::decode( + "93a44bbb96c751218e4c00d479e4c14358122a389acca16205b1e4d0dc5f9476" + ) + .unwrap(), + }), + }, + CanisterChange { + timestamp_nanos: time0 + 12, + canister_version: 4, + origin: CanisterChangeOrigin::FromCanister(FromCanisterRecord { + canister_id, + canister_version: Some(5) + }), + details: CanisterChangeDetails::CodeDeployment(CodeDeploymentRecord { + mode: Reinstall, + module_hash: hex::decode( + "93a44bbb96c751218e4c00d479e4c14358122a389acca16205b1e4d0dc5f9476" + ) + .unwrap(), + }), + }, + CanisterChange { + timestamp_nanos: time0 + 14, + canister_version: 5, + origin: CanisterChangeOrigin::FromCanister(FromCanisterRecord { + canister_id, + canister_version: Some(6) + }), + details: CanisterChangeDetails::CodeDeployment(CodeDeploymentRecord { + mode: Upgrade, + module_hash: hex::decode( + "93a44bbb96c751218e4c00d479e4c14358122a389acca16205b1e4d0dc5f9476" + ) + .unwrap(), + }), + }, + CanisterChange { + timestamp_nanos: time0 + 16, + canister_version: 6, + origin: CanisterChangeOrigin::FromCanister(FromCanisterRecord { + canister_id, + canister_version: Some(7) + }), + details: CanisterChangeDetails::ControllersChange(ControllersChangeRecord { + controllers: vec![Principal::anonymous(), canister_id, new_canister.0] + }), + }, + CanisterChange { + timestamp_nanos: time0 + 18, + canister_version: 7, + origin: CanisterChangeOrigin::FromUser(FromUserRecord { + user_id: Principal::anonymous(), + }), + details: CanisterChangeDetails::CodeUninstall, + }, + CanisterChange { + timestamp_nanos: time0 + 19, + canister_version: 8, + origin: CanisterChangeOrigin::FromUser(FromUserRecord { + user_id: Principal::anonymous(), + }), + details: CanisterChangeDetails::CodeDeployment(CodeDeploymentRecord { + mode: Install, + module_hash: hex::decode( + "93a44bbb96c751218e4c00d479e4c14358122a389acca16205b1e4d0dc5f9476" + ) + .unwrap(), + }), + }, + ], + module_hash: Some( + hex::decode("93a44bbb96c751218e4c00d479e4c14358122a389acca16205b1e4d0dc5f9476") + .unwrap() + ), + controllers: vec![Principal::anonymous(), canister_id, new_canister.0], + } + ); +} diff --git a/e2e-tests/tests/chunk.rs b/e2e-tests/tests/chunk.rs new file mode 100644 index 000000000..7dce16253 --- /dev/null +++ b/e2e-tests/tests/chunk.rs @@ -0,0 +1,94 @@ +use candid::Principal; +use pocket_ic::call_candid; +use pocket_ic::common::rest::RawEffectivePrincipal; +use sha2::Digest; + +mod test_utilities; +use test_utilities::{cargo_build_canister, pocket_ic}; + +#[test] +fn test_chunk() { + let pic = pocket_ic(); + let wasm = cargo_build_canister("chunk"); + let canister_id = pic.create_canister(); + pic.add_cycles(canister_id, 100_000_000_000_000); + pic.install_canister(canister_id, wasm, vec![], None); + let (target_canister_id,): (Principal,) = call_candid( + &pic, + canister_id, + RawEffectivePrincipal::None, + "call_create_canister", + (), + ) + .unwrap(); + + let wasm_module = b"\x00asm\x01\x00\x00\x00".to_vec(); + let wasm_module_hash = sha2::Sha256::digest(&wasm_module).to_vec(); + let chunk1 = wasm_module[..4].to_vec(); + let chunk2 = wasm_module[4..].to_vec(); + let hash1_expected = sha2::Sha256::digest(&chunk1).to_vec(); + let hash2_expected = sha2::Sha256::digest(&chunk2).to_vec(); + + let (hash1_return,): (Vec,) = call_candid( + &pic, + canister_id, + RawEffectivePrincipal::None, + "call_upload_chunk", + (target_canister_id, chunk1.clone()), + ) + .unwrap(); + assert_eq!(&hash1_return, &hash1_expected); + + let () = call_candid( + &pic, + canister_id, + RawEffectivePrincipal::None, + "call_clear_chunk_store", + (target_canister_id,), + ) + .unwrap(); + + let (_hash1_return,): (Vec,) = call_candid( + &pic, + canister_id, + RawEffectivePrincipal::None, + "call_upload_chunk", + (target_canister_id, chunk1), + ) + .unwrap(); + let (_hash2_return,): (Vec,) = call_candid( + &pic, + canister_id, + RawEffectivePrincipal::None, + "call_upload_chunk", + (target_canister_id, chunk2), + ) + .unwrap(); + + let (hashes,): (Vec>,) = call_candid( + &pic, + canister_id, + RawEffectivePrincipal::None, + "call_stored_chunks", + (target_canister_id,), + ) + .unwrap(); + // the hashes returned are not guaranteed to be in order + assert_eq!(hashes.len(), 2); + assert!(hashes.contains(&hash1_expected)); + assert!(hashes.contains(&hash2_expected)); + + let () = call_candid( + &pic, + canister_id, + RawEffectivePrincipal::None, + "call_install_chunked_code", + ( + target_canister_id, + // the order of the hashes matters + vec![hash1_expected, hash2_expected], + wasm_module_hash, + ), + ) + .unwrap(); +} diff --git a/e2e-tests/tests/e2e.rs b/e2e-tests/tests/e2e.rs deleted file mode 100644 index bccfe9fa5..000000000 --- a/e2e-tests/tests/e2e.rs +++ /dev/null @@ -1,647 +0,0 @@ -use std::time::Duration; -use std::time::SystemTime; -use std::time::UNIX_EPOCH; - -use candid::utils::ArgumentDecoder; -use candid::utils::ArgumentEncoder; -use candid::{Encode, Principal}; -use ic_cdk::api::management_canister::main::{ - CanisterChange, CanisterChangeDetails, CanisterChangeOrigin, CanisterIdRecord, - CanisterInfoResponse, CanisterInstallMode, - CodeDeploymentMode::{Install, Reinstall, Upgrade}, - CodeDeploymentRecord, ControllersChangeRecord, CreationRecord, FromCanisterRecord, - FromUserRecord, InstallCodeArgument, -}; -use ic_cdk_e2e_tests::cargo_build_canister; -use pocket_ic::common::rest::RawEffectivePrincipal; -use pocket_ic::PocketIcBuilder; -use pocket_ic::{call_candid_as, query_candid, CallError, ErrorCode, PocketIc, WasmResult}; - -use serde_bytes::ByteBuf; -use sha2::Digest; - -// 2T cycles -const INIT_CYCLES: u128 = 2_000_000_000_000; - -/// wrapper around `pocket_ic::call_candid` that uses None as the effective principal. -fn call_candid( - env: &PocketIc, - canister_id: Principal, - method: &str, - input: Input, -) -> Result -where - Input: ArgumentEncoder, - Output: for<'a> ArgumentDecoder<'a>, -{ - pocket_ic::call_candid(env, canister_id, RawEffectivePrincipal::None, method, input) -} - -/// Checks that a canister that uses [`ic_cdk::storage::stable_store`] -/// and [`ic_cdk::storage::stable_restore`] functions can keep its data -/// across upgrades. -#[test] -fn test_storage_roundtrip() { - let pic = PocketIc::new(); - let wasm = cargo_build_canister("simple-kv-store"); - let canister_id = pic.create_canister(); - pic.add_cycles(canister_id, INIT_CYCLES); - pic.install_canister(canister_id, wasm.clone(), vec![], None); - - let () = call_candid(&pic, canister_id, "insert", (&"candid", &b"did")) - .expect("failed to insert 'candid'"); - - pic.upgrade_canister(canister_id, wasm, vec![], None) - .expect("failed to upgrade the simple-kv-store canister"); - - let (result,): (Option,) = - query_candid(&pic, canister_id, "lookup", (&"candid",)).expect("failed to lookup 'candid'"); - assert_eq!(result, Some(ByteBuf::from(b"did".to_vec()))); -} - -#[test] -fn test_panic_after_async_frees_resources() { - let pic: PocketIc = PocketIc::new(); - let wasm = cargo_build_canister("async"); - let canister_id = pic.create_canister(); - pic.add_cycles(canister_id, INIT_CYCLES); - pic.install_canister(canister_id, wasm, vec![], None); - - for i in 1..3 { - match call_candid(&pic, canister_id, "panic_after_async", ()) { - Ok(()) => (), - Err(CallError::Reject(msg)) => panic!("unexpected reject: {}", msg), - Err(CallError::UserError(e)) => { - println!("Got a user error as expected: {}", e); - - assert_eq!(e.code, ErrorCode::CanisterCalledTrap); - let expected_message = "Goodbye, cruel world."; - assert!( - e.description.contains(expected_message), - "Expected the user error to contain '{}', got: {}", - expected_message, - e.description - ); - } - } - - let (n,): (u64,) = call_candid(&pic, canister_id, "invocation_count", ()) - .expect("failed to call invocation_count"); - - assert_eq!(i, n, "expected the invocation count to be {}, got {}", i, n); - } - - let (message,): (String,) = - call_candid(&pic, canister_id, "invalid_reply_payload_does_not_trap", ()) - .expect("call failed"); - assert_eq!(&message, "handled decoding error gracefully with code 5"); - - let err = - call_candid::<_, ()>(&pic, canister_id, "panic_twice", ()).expect_err("failed to panic"); - assert!( - matches!(err, CallError::UserError(u) if u.description.contains("Call already trapped")) - ); - let _: (u64,) = call_candid(&pic, canister_id, "notifications_received", ()) - .expect("failed to call unrelated function afterwards"); - let _: (u64,) = - call_candid(&pic, canister_id, "invocation_count", ()).expect("failed to recover lock"); -} - -#[test] -fn test_raw_api() { - let pic = PocketIc::new(); - let wasm = cargo_build_canister("reverse"); - let canister_id = pic.create_canister(); - pic.add_cycles(canister_id, INIT_CYCLES); - pic.install_canister(canister_id, wasm, vec![], None); - - let result = pic - .query_call( - canister_id, - Principal::anonymous(), - "reverse", - vec![1, 2, 3, 4], - ) - .unwrap(); - assert_eq!(result, WasmResult::Reply(vec![4, 3, 2, 1])); - - let result = pic - .update_call( - canister_id, - Principal::anonymous(), - "empty_call", - Default::default(), - ) - .unwrap(); - assert_eq!(result, WasmResult::Reply(Default::default())); -} - -#[test] -fn test_notify_calls() { - let pic = PocketIc::new(); - let wasm = cargo_build_canister("async"); - let sender_id = pic.create_canister(); - pic.add_cycles(sender_id, INIT_CYCLES); - pic.install_canister(sender_id, wasm.clone(), vec![], None); - let receiver_id = pic.create_canister(); - pic.add_cycles(receiver_id, INIT_CYCLES); - pic.install_canister(receiver_id, wasm, vec![], None); - - let (n,): (u64,) = query_candid(&pic, receiver_id, "notifications_received", ()) - .expect("failed to query 'notifications_received'"); - assert_eq!(n, 0); - - let () = call_candid(&pic, sender_id, "notify", (receiver_id, "on_notify")) - .expect("failed to call 'notify'"); - - let (n,): (u64,) = query_candid(&pic, receiver_id, "notifications_received", ()) - .expect("failed to query 'notifications_received'"); - assert_eq!(n, 1); -} - -// Composite queries are not enabled yet. -#[test] -#[ignore] -fn test_composite_query() { - let pic = PocketIc::new(); - let wasm = cargo_build_canister("async"); - let sender_id = pic.create_canister(); - pic.add_cycles(sender_id, INIT_CYCLES); - pic.install_canister(sender_id, wasm.clone(), vec![], None); - let receiver_id = pic.create_canister(); - pic.add_cycles(receiver_id, INIT_CYCLES); - pic.install_canister(receiver_id, wasm, vec![], None); - - let (greeting,): (String,) = query_candid(&pic, sender_id, "greet_self", (receiver_id,)) - .expect("failed to query 'greet_self'"); - assert_eq!(greeting, "Hello, myself"); -} - -#[test] -fn test_api_call() { - let pic = PocketIc::new(); - let wasm = cargo_build_canister("api-call"); - let canister_id = pic.create_canister(); - pic.add_cycles(canister_id, INIT_CYCLES); - pic.install_canister(canister_id, wasm, vec![], None); - let (result,): (u64,) = query_candid(&pic, canister_id, "instruction_counter", ()) - .expect("failed to query instruction_counter"); - assert!(result > 0); - - let result = pic - .query_call( - canister_id, - Principal::anonymous(), - "manual_reject", - Encode!().unwrap(), - ) - .unwrap(); - assert_eq!(result, WasmResult::Reject("manual reject".to_string())); - - let (result,): (bool,) = call_candid(&pic, canister_id, "update_is_replicated", ()) - .expect("Failed to call update_is_replicated"); - assert!(result); - - let (result,): (bool,) = query_candid(&pic, canister_id, "query_is_not_replicated", ()) - .expect("Failed to call query_is_not_replicated"); - assert!(!result); -} - -#[test] -fn test_timers() { - let pic = PocketIc::new(); - let wasm = cargo_build_canister("timers"); - let canister_id = pic.create_canister(); - pic.add_cycles(canister_id, INIT_CYCLES); - pic.install_canister(canister_id, wasm, vec![], None); - - call_candid::<(), ()>(&pic, canister_id, "schedule", ()).expect("Failed to call schedule"); - advance_seconds(&pic, 5); - - call_candid::<_, ()>(&pic, canister_id, "schedule_long", ()) - .expect("Failed to call schedule_long"); - advance_seconds(&pic, 5); - call_candid::<_, ()>(&pic, canister_id, "cancel_long", ()).expect("Failed to call cancel_long"); - advance_seconds(&pic, 5); - - call_candid::<_, ()>(&pic, canister_id, "start_repeating", ()) - .expect("Failed to call start_repeating"); - advance_seconds(&pic, 3); - call_candid::<_, ()>(&pic, canister_id, "stop_repeating", ()) - .expect("Failed to call stop_repeating"); - advance_seconds(&pic, 2); - - let (events,): (Vec,) = - query_candid(&pic, canister_id, "get_events", ()).expect("Failed to call get_events"); - assert_eq!( - events[..], - ["1", "2", "3", "4", "repeat", "repeat", "repeat"] - ); -} - -#[test] -fn test_timers_can_cancel_themselves() { - let pic = PocketIc::new(); - let wasm = cargo_build_canister("timers"); - let canister_id = pic.create_canister(); - pic.add_cycles(canister_id, INIT_CYCLES); - pic.install_canister(canister_id, wasm, vec![], None); - - call_candid::<_, ()>(&pic, canister_id, "set_self_cancelling_timer", ()) - .expect("Failed to call set_self_cancelling_timer"); - call_candid::<_, ()>(&pic, canister_id, "set_self_cancelling_periodic_timer", ()) - .expect("Failed to call set_self_cancelling_periodic_timer"); - - advance_seconds(&pic, 1); - - let (events,): (Vec,) = - query_candid(&pic, canister_id, "get_events", ()).expect("Failed to call get_events"); - assert_eq!( - events, - ["timer cancelled self", "periodic timer cancelled self"] - ); -} - -#[test] -fn test_scheduling_many_timers() { - // Must be more than the queue limit (500) - let timers_to_schedule = 1_000; - let pic = PocketIc::new(); - let wasm = cargo_build_canister("timers"); - let canister_id = pic.create_canister(); - pic.add_cycles(canister_id, 100_000_000_000_000u128); - pic.install_canister(canister_id, wasm, vec![], None); - - let () = call_candid( - &pic, - canister_id, - "schedule_n_timers", - (timers_to_schedule,), - ) - .expect("Error calling schedule_n_timers"); - - // Up to 20 timers will be executed per round - // Be conservative that advance 2 times the minimum number of rounds - const TIMERS_PER_ROUND: u32 = 20; - advance_seconds(&pic, 2 * timers_to_schedule / TIMERS_PER_ROUND); - - let (executed_timers,): (u32,) = query_candid(&pic, canister_id, "executed_timers", ()) - .expect("Error querying executed_timers"); - - assert_eq!(timers_to_schedule, executed_timers); -} - -fn advance_seconds(pic: &PocketIc, seconds: u32) { - for _ in 0..seconds { - pic.advance_time(Duration::from_secs(1)); - pic.tick(); - } -} - -#[test] -fn test_set_global_timers() { - let pic = PocketIc::new(); - - let wasm = cargo_build_canister("timers"); - let canister_id = pic.create_canister(); - pic.add_cycles(canister_id, INIT_CYCLES); - pic.install_canister(canister_id, wasm, vec![], None); - - // Set a 9s timer at t0, it expires at t1 = t0 + 9s - let t0 = pic - .get_time() - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap() - .as_nanos() as u64; - let t1 = t0 + 9_000_000_000; - call_candid::<_, ()>(&pic, canister_id, "schedule_long", ()) - .expect("Failed to call schedule_long"); - - // 5 seconds later, the 9s timer is still active - advance_seconds(&pic, 5); - - // Set the expiration time of the timer to t2 = t1 + 5s - let t2 = t1 + 5_000_000_000; - let (previous,) = call_candid::<(u64,), (u64,)>(&pic, canister_id, "set_global_timer", (t2,)) - .expect("Failed to call set_global_timer"); - assert!(previous.abs_diff(t1) < 2); // time error no more than 1 nanosecond - - // Deactivate the timer - let (previous,) = call_candid::<(u64,), (u64,)>(&pic, canister_id, "set_global_timer", (0,)) - .expect("Failed to call set_global_timer"); - assert!(previous.abs_diff(t2) < 2); // time error no more than 1 nanosecond -} - -#[test] -fn test_canister_info() { - let pic = PocketIc::new(); - let wasm = cargo_build_canister("canister_info"); - // As of PocketIC server v5.0.0 and client v4.0.0, the first canister creation happens at (time0+4). - // Each operation advances the Pic by 2 nanos, except for the last operation which advances only by 1 nano. - let time0: u64 = pic - .get_time() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_nanos() - .try_into() - .unwrap(); - let canister_id = pic.create_canister(); - pic.add_cycles(canister_id, INIT_CYCLES); - pic.install_canister(canister_id, wasm, vec![], None); - - let new_canister: (Principal,) = call_candid(&pic, canister_id, "canister_lifecycle", ()) - .expect("Error calling canister_lifecycle"); - - let () = call_candid_as( - &pic, - Principal::management_canister(), - RawEffectivePrincipal::None, - Principal::anonymous(), - "uninstall_code", - (CanisterIdRecord { - canister_id: new_canister.0, - },), - ) - .expect("Error calling uninstall_code"); - let () = call_candid_as( - &pic, - Principal::management_canister(), - RawEffectivePrincipal::None, - Principal::anonymous(), - "install_code", - (InstallCodeArgument { - mode: CanisterInstallMode::Install, - arg: vec![], - wasm_module: vec![0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00], - canister_id: new_canister.0, - },), - ) - .expect("Error calling install_code"); - - let info: (CanisterInfoResponse,) = call_candid(&pic, canister_id, "info", (new_canister.0,)) - .expect("Error calling canister_info"); - - assert_eq!( - info.0, - CanisterInfoResponse { - total_num_changes: 9, - recent_changes: vec![ - CanisterChange { - timestamp_nanos: time0 + 4, - canister_version: 0, - origin: CanisterChangeOrigin::FromCanister(FromCanisterRecord { - canister_id, - canister_version: Some(1) - }), - details: CanisterChangeDetails::Creation(CreationRecord { - controllers: vec![canister_id] - }), - }, - CanisterChange { - timestamp_nanos: time0 + 6, - canister_version: 1, - origin: CanisterChangeOrigin::FromCanister(FromCanisterRecord { - canister_id, - canister_version: Some(2) - }), - details: CanisterChangeDetails::CodeDeployment(CodeDeploymentRecord { - mode: Install, - module_hash: hex::decode( - "93a44bbb96c751218e4c00d479e4c14358122a389acca16205b1e4d0dc5f9476" - ) - .unwrap(), - }), - }, - CanisterChange { - timestamp_nanos: time0 + 8, - canister_version: 2, - origin: CanisterChangeOrigin::FromCanister(FromCanisterRecord { - canister_id, - canister_version: Some(3) - }), - details: CanisterChangeDetails::CodeUninstall, - }, - CanisterChange { - timestamp_nanos: time0 + 10, - canister_version: 3, - origin: CanisterChangeOrigin::FromCanister(FromCanisterRecord { - canister_id, - canister_version: Some(4) - }), - details: CanisterChangeDetails::CodeDeployment(CodeDeploymentRecord { - mode: Install, - module_hash: hex::decode( - "93a44bbb96c751218e4c00d479e4c14358122a389acca16205b1e4d0dc5f9476" - ) - .unwrap(), - }), - }, - CanisterChange { - timestamp_nanos: time0 + 12, - canister_version: 4, - origin: CanisterChangeOrigin::FromCanister(FromCanisterRecord { - canister_id, - canister_version: Some(5) - }), - details: CanisterChangeDetails::CodeDeployment(CodeDeploymentRecord { - mode: Reinstall, - module_hash: hex::decode( - "93a44bbb96c751218e4c00d479e4c14358122a389acca16205b1e4d0dc5f9476" - ) - .unwrap(), - }), - }, - CanisterChange { - timestamp_nanos: time0 + 14, - canister_version: 5, - origin: CanisterChangeOrigin::FromCanister(FromCanisterRecord { - canister_id, - canister_version: Some(6) - }), - details: CanisterChangeDetails::CodeDeployment(CodeDeploymentRecord { - mode: Upgrade, - module_hash: hex::decode( - "93a44bbb96c751218e4c00d479e4c14358122a389acca16205b1e4d0dc5f9476" - ) - .unwrap(), - }), - }, - CanisterChange { - timestamp_nanos: time0 + 16, - canister_version: 6, - origin: CanisterChangeOrigin::FromCanister(FromCanisterRecord { - canister_id, - canister_version: Some(7) - }), - details: CanisterChangeDetails::ControllersChange(ControllersChangeRecord { - controllers: vec![Principal::anonymous(), canister_id, new_canister.0] - }), - }, - CanisterChange { - timestamp_nanos: time0 + 18, - canister_version: 7, - origin: CanisterChangeOrigin::FromUser(FromUserRecord { - user_id: Principal::anonymous(), - }), - details: CanisterChangeDetails::CodeUninstall, - }, - CanisterChange { - timestamp_nanos: time0 + 19, - canister_version: 8, - origin: CanisterChangeOrigin::FromUser(FromUserRecord { - user_id: Principal::anonymous(), - }), - details: CanisterChangeDetails::CodeDeployment(CodeDeploymentRecord { - mode: Install, - module_hash: hex::decode( - "93a44bbb96c751218e4c00d479e4c14358122a389acca16205b1e4d0dc5f9476" - ) - .unwrap(), - }), - }, - ], - module_hash: Some( - hex::decode("93a44bbb96c751218e4c00d479e4c14358122a389acca16205b1e4d0dc5f9476") - .unwrap() - ), - controllers: vec![Principal::anonymous(), canister_id, new_canister.0], - } - ); -} - -#[test] -fn test_cycles_burn() { - let pic = PocketIc::new(); - let wasm = cargo_build_canister("api-call"); - let canister_id = pic.create_canister(); - pic.add_cycles(canister_id, INIT_CYCLES); - - pic.install_canister(canister_id, wasm, vec![], None); - eprintln!("Canister installed."); - let balance1 = pic.cycle_balance(canister_id); - eprintln!("Balance 1: {balance1}"); - - // Scenario 1: burn less than balance - let attempted1 = 1000u128; - let (burned,): (u128,) = call_candid(&pic, canister_id, "cycles_burn", (attempted1,)) - .expect("Error calling cycles_burn"); - eprintln!("Attempted to burn {attempted1}, actually burned {burned}"); - assert_eq!(burned, attempted1); - let balance2 = pic.cycle_balance(canister_id); - eprintln!("Balance 2: {balance2}"); - - // Scenario 2: burn more than balance - let attempted2 = balance2 + 1; - let (burned,): (u128,) = call_candid(&pic, canister_id, "cycles_burn", (attempted2,)) - .expect("Error calling cycles_burn"); - eprintln!("Attempted to burn {attempted2}, actually burned {burned}"); - assert!(burned < balance2); // restrained by reserved_balance and freezing_limit - let balance3 = pic.cycle_balance(canister_id); - eprintln!("Balance 3: {balance3}"); -} - -#[test] -fn test_call_management() { - let pic = PocketIc::new(); - let wasm = cargo_build_canister("management_caller"); - let canister_id = pic.create_canister(); - pic.add_cycles(canister_id, 300_000_000_000_000_000_000_000_000u128); - pic.install_canister(canister_id, wasm, vec![], None); - let subnet_id = pic.topology().get_app_subnets()[0]; - let () = call_candid(&pic, canister_id, "execute_subnet_info", (subnet_id,)) - .expect("Error calling execute_subnet_info"); - let () = call_candid(&pic, canister_id, "execute_main_methods", ()) - .expect("Error calling execute_main_methods"); - let () = call_candid(&pic, canister_id, "execute_provisional_methods", ()) - .expect("Error calling execute_provisional_methods"); -} - -#[test] -fn test_snapshot() { - let pic = PocketIcBuilder::new() - .with_application_subnet() - .with_nonmainnet_features(true) - .build(); - let wasm = cargo_build_canister("management_caller"); - let canister_id = pic.create_canister(); - pic.add_cycles(canister_id, 300_000_000_000_000_000_000_000_000u128); - pic.install_canister(canister_id, wasm, vec![], None); - let () = call_candid(&pic, canister_id, "execute_snapshot_methods", ()) - .expect("Error calling execute_snapshot_methods"); -} - -#[test] -fn test_chunk() { - let pic = PocketIc::new(); - let wasm = cargo_build_canister("chunk"); - let canister_id = pic.create_canister(); - pic.add_cycles(canister_id, 100_000_000_000_000); - pic.install_canister(canister_id, wasm, vec![], None); - let (target_canister_id,): (Principal,) = - call_candid(&pic, canister_id, "call_create_canister", ()) - .expect("Error calling call_create_canister"); - - let wasm_module = b"\x00asm\x01\x00\x00\x00".to_vec(); - let wasm_module_hash = sha2::Sha256::digest(&wasm_module).to_vec(); - let chunk1 = wasm_module[..4].to_vec(); - let chunk2 = wasm_module[4..].to_vec(); - let hash1_expected = sha2::Sha256::digest(&chunk1).to_vec(); - let hash2_expected = sha2::Sha256::digest(&chunk2).to_vec(); - - let (hash1_return,): (Vec,) = call_candid( - &pic, - canister_id, - "call_upload_chunk", - (target_canister_id, chunk1.clone()), - ) - .expect("Error calling call_upload_chunk"); - assert_eq!(&hash1_return, &hash1_expected); - - let () = call_candid( - &pic, - canister_id, - "call_clear_chunk_store", - (target_canister_id,), - ) - .expect("Error calling call_clear_chunk_store"); - - let (_hash1_return,): (Vec,) = call_candid( - &pic, - canister_id, - "call_upload_chunk", - (target_canister_id, chunk1), - ) - .expect("Error calling call_upload_chunk"); - let (_hash2_return,): (Vec,) = call_candid( - &pic, - canister_id, - "call_upload_chunk", - (target_canister_id, chunk2), - ) - .expect("Error calling call_upload_chunk"); - - let (hashes,): (Vec>,) = call_candid( - &pic, - canister_id, - "call_stored_chunks", - (target_canister_id,), - ) - .expect("Error calling call_stored_chunks"); - // the hashes returned are not guaranteed to be in order - assert_eq!(hashes.len(), 2); - assert!(hashes.contains(&hash1_expected)); - assert!(hashes.contains(&hash2_expected)); - - let () = call_candid( - &pic, - canister_id, - "call_install_chunked_code", - ( - target_canister_id, - // the order of the hashes matters - vec![hash1_expected, hash2_expected], - wasm_module_hash, - ), - ) - .expect("Error calling call_install_chunked_code"); -} diff --git a/e2e-tests/tests/http_request.rs b/e2e-tests/tests/http_request.rs new file mode 100644 index 000000000..cf250e871 --- /dev/null +++ b/e2e-tests/tests/http_request.rs @@ -0,0 +1,65 @@ +use candid::{Encode, Principal}; +use pocket_ic::common::rest::{ + CanisterHttpHeader, CanisterHttpReply, CanisterHttpRequest, CanisterHttpResponse, + MockCanisterHttpResponse, +}; +use pocket_ic::{PocketIc, WasmResult}; + +mod test_utilities; +use test_utilities::{cargo_build_canister, pocket_ic}; + +#[test] +fn test_http_request() { + let pic = pocket_ic(); + + let wasm = cargo_build_canister("http_request"); + let canister_id = pic.create_canister(); + pic.add_cycles(canister_id, 3_000_000_000_000u128); + pic.install_canister(canister_id, wasm, vec![], None); + + test_one_http_request(&pic, canister_id, "get_without_transform"); + test_one_http_request(&pic, canister_id, "post"); + test_one_http_request(&pic, canister_id, "head"); + test_one_http_request(&pic, canister_id, "get_with_transform"); + test_one_http_request(&pic, canister_id, "get_with_transform_closure"); +} + +fn test_one_http_request(pic: &PocketIc, canister_id: Principal, method: &str) { + let call_id = pic + .submit_call( + canister_id, + Principal::anonymous(), + method, + Encode!(&()).unwrap(), + ) + .unwrap(); + let canister_http_requests = tick_until_next_request(pic); + assert_eq!(canister_http_requests.len(), 1); + let request = &canister_http_requests[0]; + pic.mock_canister_http_response(MockCanisterHttpResponse { + subnet_id: request.subnet_id, + request_id: request.request_id, + response: CanisterHttpResponse::CanisterHttpReply(CanisterHttpReply { + status: 200, + headers: vec![CanisterHttpHeader { + name: "response_header_name".to_string(), + value: "response_header_value".to_string(), + }], + body: vec![42], + }), + additional_responses: vec![], + }); + let result = pic.await_call(call_id).unwrap(); + assert!(matches!(result, WasmResult::Reply(_))); +} + +fn tick_until_next_request(pic: &PocketIc) -> Vec { + for _ in 0..10 { + let requests = pic.get_canister_http(); + if !requests.is_empty() { + return requests; + } + pic.tick(); + } + vec![] +} diff --git a/e2e-tests/tests/management_canister.rs b/e2e-tests/tests/management_canister.rs new file mode 100644 index 000000000..d35fb2b02 --- /dev/null +++ b/e2e-tests/tests/management_canister.rs @@ -0,0 +1,58 @@ +use pocket_ic::call_candid; +use pocket_ic::common::rest::RawEffectivePrincipal; + +mod test_utilities; +use test_utilities::{cargo_build_canister, pocket_ic}; + +#[test] +fn test_management_canister() { + let pic = pocket_ic(); + + let wasm = cargo_build_canister("management_canister"); + let canister_id = pic.create_canister(); + let subnet_id = pic.get_subnet(canister_id).unwrap(); + pic.add_cycles(canister_id, 10_000_000_000_000u128); // 10 T + pic.install_canister(canister_id, wasm, vec![], None); + let () = call_candid(&pic, canister_id, RawEffectivePrincipal::None, "basic", ()).unwrap(); + let () = call_candid(&pic, canister_id, RawEffectivePrincipal::None, "ecdsa", ()).unwrap(); + let () = call_candid( + &pic, + canister_id, + RawEffectivePrincipal::None, + "schnorr", + (), + ) + .unwrap(); + let () = call_candid( + &pic, + canister_id, + RawEffectivePrincipal::None, + "metrics", + (subnet_id,), + ) + .unwrap(); + let () = call_candid( + &pic, + canister_id, + RawEffectivePrincipal::None, + "subnet", + (subnet_id,), + ) + .unwrap(); + let () = call_candid( + &pic, + canister_id, + RawEffectivePrincipal::None, + "provisional", + (), + ) + .unwrap(); + let () = call_candid( + &pic, + canister_id, + RawEffectivePrincipal::None, + "snapshots", + (), + ) + .unwrap(); +} diff --git a/e2e-tests/tests/simple_kv_store.rs b/e2e-tests/tests/simple_kv_store.rs new file mode 100644 index 000000000..e10758b34 --- /dev/null +++ b/e2e-tests/tests/simple_kv_store.rs @@ -0,0 +1,34 @@ +use pocket_ic::common::rest::RawEffectivePrincipal; +use pocket_ic::{call_candid, query_candid}; +use serde_bytes::ByteBuf; + +mod test_utilities; +use test_utilities::{cargo_build_canister, pocket_ic}; + +/// Checks that a canister that uses [`ic_cdk::storage::stable_save`] +/// and [`ic_cdk::storage::stable_restore`] functions can keep its data +/// across upgrades. +#[test] +fn test_storage_roundtrip() { + let pic = pocket_ic(); + let wasm = cargo_build_canister("simple_kv_store"); + let canister_id = pic.create_canister(); + pic.add_cycles(canister_id, 2_000_000_000_000); + pic.install_canister(canister_id, wasm.clone(), vec![], None); + + let () = call_candid( + &pic, + canister_id, + RawEffectivePrincipal::None, + "insert", + (&"candid", &b"did"), + ) + .expect("failed to insert 'candid'"); + + pic.upgrade_canister(canister_id, wasm, vec![], None) + .expect("failed to upgrade the simple_kv_store canister"); + + let (result,): (Option,) = + query_candid(&pic, canister_id, "lookup", (&"candid",)).expect("failed to lookup 'candid'"); + assert_eq!(result, Some(ByteBuf::from(b"did".to_vec()))); +} diff --git a/e2e-tests/tests/test_utilities.rs b/e2e-tests/tests/test_utilities.rs new file mode 100644 index 000000000..61289f214 --- /dev/null +++ b/e2e-tests/tests/test_utilities.rs @@ -0,0 +1,76 @@ +use cargo_metadata::MetadataCommand; +use pocket_ic::{PocketIc, PocketIcBuilder}; +use std::path::PathBuf; +use std::process::Command; + +/// Builds a canister with the specified name from the current +/// package and returns the WebAssembly module. +pub fn cargo_build_canister(bin_name: &str) -> Vec { + let dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()); + + let cargo_toml_path = dir.join("Cargo.toml"); + + let target_dir = MetadataCommand::new() + .manifest_path(&cargo_toml_path) + .no_deps() + .exec() + .expect("failed to run cargo metadata") + .target_directory; + + // We use a different target path to stop the native cargo build + // cache being invalidated every time we run this function + let wasm_target_dir = target_dir.join("canister-build"); + + let mut cmd = Command::new("cargo"); + let target = match std::env::var("WASM64") { + Ok(_) => { + cmd.args([ + "+nightly", + "build", + "-Z", + "build-std=std,panic_abort", + "--target", + "wasm64-unknown-unknown", + ]); + "wasm64-unknown-unknown" + } + Err(_) => { + cmd.args(["build", "--target", "wasm32-unknown-unknown"]); + "wasm32-unknown-unknown" + } + }; + + let cmd = cmd.args([ + "--bin", + bin_name, + "--profile", + "canister-release", + "--manifest-path", + &cargo_toml_path.to_string_lossy(), + "--target-dir", + wasm_target_dir.as_ref(), + ]); + + cmd.output().expect("failed to compile the wasm binary"); + + let wasm_path = wasm_target_dir + .join(target) + .join("canister-release") + .join(bin_name) + .with_extension("wasm"); + + std::fs::read(&wasm_path).unwrap_or_else(|e| { + panic!( + "failed to read compiled Wasm file from {:?}: {}", + &wasm_path, e + ) + }) +} + +pub fn pocket_ic() -> PocketIc { + PocketIcBuilder::new() + .with_application_subnet() + .with_nonmainnet_features(true) + .with_ii_subnet() + .build() +} diff --git a/e2e-tests/tests/timers.rs b/e2e-tests/tests/timers.rs new file mode 100644 index 000000000..c35457b0e --- /dev/null +++ b/e2e-tests/tests/timers.rs @@ -0,0 +1,195 @@ +use pocket_ic::common::rest::RawEffectivePrincipal; +use pocket_ic::{call_candid, query_candid, PocketIc}; +use std::time::Duration; +use std::time::SystemTime; + +mod test_utilities; +use test_utilities::{cargo_build_canister, pocket_ic}; + +#[test] +fn test_timers() { + let pic = pocket_ic(); + let wasm = cargo_build_canister("timers"); + let canister_id = pic.create_canister(); + pic.add_cycles(canister_id, 2_000_000_000_000); + pic.install_canister(canister_id, wasm, vec![], None); + + call_candid::<(), ()>( + &pic, + canister_id, + RawEffectivePrincipal::None, + "schedule", + (), + ) + .expect("Failed to call schedule"); + advance_seconds(&pic, 5); + + call_candid::<_, ()>( + &pic, + canister_id, + RawEffectivePrincipal::None, + "schedule_long", + (), + ) + .expect("Failed to call schedule_long"); + advance_seconds(&pic, 5); + call_candid::<_, ()>( + &pic, + canister_id, + RawEffectivePrincipal::None, + "cancel_long", + (), + ) + .expect("Failed to call cancel_long"); + advance_seconds(&pic, 5); + + call_candid::<_, ()>( + &pic, + canister_id, + RawEffectivePrincipal::None, + "start_repeating", + (), + ) + .expect("Failed to call start_repeating"); + advance_seconds(&pic, 3); + call_candid::<_, ()>( + &pic, + canister_id, + RawEffectivePrincipal::None, + "stop_repeating", + (), + ) + .expect("Failed to call stop_repeating"); + advance_seconds(&pic, 2); + + let (events,): (Vec,) = + query_candid(&pic, canister_id, "get_events", ()).expect("Failed to call get_events"); + assert_eq!( + events[..], + ["1", "2", "3", "4", "repeat", "repeat", "repeat"] + ); +} + +#[test] +fn test_timers_can_cancel_themselves() { + let pic = pocket_ic(); + let wasm = cargo_build_canister("timers"); + let canister_id = pic.create_canister(); + pic.add_cycles(canister_id, 2_000_000_000_000); + pic.install_canister(canister_id, wasm, vec![], None); + + call_candid::<_, ()>( + &pic, + canister_id, + RawEffectivePrincipal::None, + "set_self_cancelling_timer", + (), + ) + .expect("Failed to call set_self_cancelling_timer"); + call_candid::<_, ()>( + &pic, + canister_id, + RawEffectivePrincipal::None, + "set_self_cancelling_periodic_timer", + (), + ) + .expect("Failed to call set_self_cancelling_periodic_timer"); + + advance_seconds(&pic, 1); + + let (events,): (Vec,) = + query_candid(&pic, canister_id, "get_events", ()).expect("Failed to call get_events"); + assert_eq!( + events, + ["timer cancelled self", "periodic timer cancelled self"] + ); +} + +#[test] +fn test_scheduling_many_timers() { + // Must be more than the queue limit (500) + let timers_to_schedule = 1_000; + let pic = pocket_ic(); + let wasm = cargo_build_canister("timers"); + let canister_id = pic.create_canister(); + pic.add_cycles(canister_id, 100_000_000_000_000u128); + pic.install_canister(canister_id, wasm, vec![], None); + + let () = call_candid( + &pic, + canister_id, + RawEffectivePrincipal::None, + "schedule_n_timers", + (timers_to_schedule,), + ) + .expect("Error calling schedule_n_timers"); + + // Up to 20 timers will be executed per round + // Be conservative that advance 2 times the minimum number of rounds + const TIMERS_PER_ROUND: u32 = 20; + advance_seconds(&pic, 2 * timers_to_schedule / TIMERS_PER_ROUND); + + let (executed_timers,): (u32,) = query_candid(&pic, canister_id, "executed_timers", ()) + .expect("Error querying executed_timers"); + + assert_eq!(timers_to_schedule, executed_timers); +} + +fn advance_seconds(pic: &PocketIc, seconds: u32) { + for _ in 0..seconds { + pic.advance_time(Duration::from_secs(1)); + pic.tick(); + } +} + +#[test] +fn test_set_global_timers() { + let pic = pocket_ic(); + + let wasm = cargo_build_canister("timers"); + let canister_id = pic.create_canister(); + pic.add_cycles(canister_id, 2_000_000_000_000); + pic.install_canister(canister_id, wasm, vec![], None); + + // Set a 9s timer at t0, it expires at t1 = t0 + 9s + let t0 = pic + .get_time() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_nanos() as u64; + let t1 = t0 + 9_000_000_000; + call_candid::<_, ()>( + &pic, + canister_id, + RawEffectivePrincipal::None, + "schedule_long", + (), + ) + .expect("Failed to call schedule_long"); + + // 5 seconds later, the 9s timer is still active + advance_seconds(&pic, 5); + + // Set the expiration time of the timer to t2 = t1 + 5s + let t2 = t1 + 5_000_000_000; + let (previous,) = call_candid::<(u64,), (u64,)>( + &pic, + canister_id, + RawEffectivePrincipal::None, + "global_timer_set", + (t2,), + ) + .unwrap(); + assert!(previous.abs_diff(t1) < 2); // time error no more than 1 nanosecond + + // Deactivate the timer + let (previous,) = call_candid::<(u64,), (u64,)>( + &pic, + canister_id, + RawEffectivePrincipal::None, + "global_timer_set", + (0,), + ) + .unwrap(); + assert!(previous.abs_diff(t2) < 2); // time error no more than 1 nanosecond +} diff --git a/examples/asset_storage/.gitignore b/examples/asset_storage/.gitignore deleted file mode 100644 index 6cb3f2522..000000000 --- a/examples/asset_storage/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -.dfx/ -canisters/ -target/ -build/ diff --git a/examples/asset_storage/Cargo.toml b/examples/asset_storage/Cargo.toml deleted file mode 100644 index f9b923407..000000000 --- a/examples/asset_storage/Cargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[workspace] -members = ["src/asset_storage_rs"] -resolver = "2" - -[profile.release] -lto = true -opt-level = 'z' diff --git a/examples/asset_storage/dfx.json b/examples/asset_storage/dfx.json deleted file mode 100644 index b1a79205b..000000000 --- a/examples/asset_storage/dfx.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "version": 1, - "canisters": { - "asset_storage": { - "type": "custom", - "candid": "src/asset_storage_rs/asset_storage.did", - "wasm": "target/wasm32-unknown-unknown/release/asset_storage_rs-opt.wasm", - "build": "sh ../build.sh asset_storage asset_storage_rs" - } - } -} diff --git a/examples/asset_storage/src/asset_storage_rs/Cargo.toml b/examples/asset_storage/src/asset_storage_rs/Cargo.toml deleted file mode 100644 index ef7509a19..000000000 --- a/examples/asset_storage/src/asset_storage_rs/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "asset_storage_rs" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[lib] -path = "lib.rs" -crate-type = ["cdylib"] - -[dependencies] -candid = "0.10" -ic-cdk = { path = "../../../../src/ic-cdk" } diff --git a/examples/asset_storage/src/asset_storage_rs/asset_storage.did b/examples/asset_storage/src/asset_storage_rs/asset_storage.did deleted file mode 100644 index 825222e0a..000000000 --- a/examples/asset_storage/src/asset_storage_rs/asset_storage.did +++ /dev/null @@ -1,6 +0,0 @@ -service : { - "retrieve": (text) -> (vec nat8) query; - "store": (text, vec nat8) -> (); - - "add_user": (principal) -> (); -} diff --git a/examples/asset_storage/src/asset_storage_rs/lib.rs b/examples/asset_storage/src/asset_storage_rs/lib.rs deleted file mode 100644 index 1a71ada4b..000000000 --- a/examples/asset_storage/src/asset_storage_rs/lib.rs +++ /dev/null @@ -1,56 +0,0 @@ -use candid::Principal; -use ic_cdk::{api::call::ManualReply, init, post_upgrade, pre_upgrade, query, storage, update}; -use std::cell::RefCell; -use std::collections::{BTreeMap, BTreeSet}; - -type Users = BTreeSet; -type Store = BTreeMap>; - -thread_local! { - static USERS: RefCell = RefCell::default(); - static STORE: RefCell = RefCell::default(); -} - -#[init] -fn init() { - USERS.with(|users| users.borrow_mut().insert(ic_cdk::api::caller())); -} - -fn is_user() -> Result<(), String> { - if USERS.with(|users| users.borrow().contains(&ic_cdk::api::caller())) { - Ok(()) - } else { - Err("Store can only be set by the owner of the asset canister.".to_string()) - } -} - -#[update(guard = "is_user")] -fn store(path: String, contents: Vec) { - STORE.with(|store| store.borrow_mut().insert(path, contents)); -} - -#[query(manual_reply = true)] -fn retrieve(path: String) -> ManualReply> { - STORE.with(|store| match store.borrow().get(&path) { - Some(content) => ManualReply::one(content), - None => panic!("Path {} not found.", path), - }) -} - -#[update(guard = "is_user")] -fn add_user(principal: Principal) { - USERS.with(|users| users.borrow_mut().insert(principal)); -} - -#[pre_upgrade] -fn pre_upgrade() { - USERS.with(|users| storage::stable_save((users,)).unwrap()); -} - -#[post_upgrade] -fn post_upgrade() { - let (old_users,): (BTreeSet,) = storage::stable_restore().unwrap(); - USERS.with(|users| *users.borrow_mut() = old_users); -} - -ic_cdk::export_candid!(); diff --git a/examples/asset_storage/tests/basic.bats b/examples/asset_storage/tests/basic.bats deleted file mode 100644 index c26ad4129..000000000 --- a/examples/asset_storage/tests/basic.bats +++ /dev/null @@ -1,66 +0,0 @@ -load ../../bats/bats-assert/load.bash - -# Executed before each test. -setup() { - cd examples/asset_storage - # Make sure the directory is clean. - dfx start --clean --background - - x=$(mktemp -d -t cdk-XXXXXXXX) - export DFX_CONFIG_ROOT="$x" - dfx identity new alice --storage-mode=plaintext - dfx identity new bob --storage-mode=plaintext - dfx identity new charlie --storage-mode=plaintext -} - -# executed after each test -teardown() { - dfx stop - rm -rf "$DFX_CONFIG_ROOT" -} - -@test "Can store and restore assets" { - dfx --identity alice deploy - run dfx --identity alice canister call asset_storage store '("asset_name", vec { 1; 2; 3; })' - assert_success - run dfx --identity alice canister call asset_storage retrieve '("asset_name")' - assert_success - - # Test that an unknown asset fails. - run dfx --identity alice canister call asset_storage retrieve '("unknown")' - assert_failure -} - -@test "Unauthorized identity cannot store" { - dfx --identity alice deploy - dfx --identity alice canister call asset_storage store '("asset_name", vec { 1; 2; 3; })' - dfx --identity alice canister call asset_storage retrieve '("asset_name")' - - # add charlie - run dfx --identity alice canister call asset_storage add_user "(principal \"$(dfx --identity charlie identity get-principal)\")" - assert_success - - # bob cannot upload assets - run dfx --identity bob canister call asset_storage store '("asset_name", vec { 1; })' - assert_failure - - # charlie can upload assets - run dfx --identity charlie canister call asset_storage store '("asset_name_2", vec { 1; 2; 3; })' - assert_success -} - -@test "Can upgrade and keep the access control list" { - dfx --identity alice deploy - dfx --identity alice canister call asset_storage store '("asset_name", vec { 1; 2; 3; })' - dfx --identity alice canister call asset_storage add_user "(principal \"$(dfx --identity charlie identity get-principal)\")" - - dfx canister install --all --mode=upgrade - - # bob still cannot upload assets - run dfx --identity bob canister call asset_storage store '("asset_name", vec { 1; })' - assert_failure - - # charlie still can upload assets - run dfx --identity charlie canister call asset_storage store '("asset_name_2", vec { 1; 2; 3; })' - assert_success -} diff --git a/examples/bats/bats-assert b/examples/bats/bats-assert deleted file mode 160000 index e2d855bc7..000000000 --- a/examples/bats/bats-assert +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e2d855bc78619ee15b0c702b5c30fb074101159f diff --git a/examples/bats/bats-core b/examples/bats/bats-core deleted file mode 160000 index f1f5115e3..000000000 --- a/examples/bats/bats-core +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f1f5115e3621e47fc41a32e36450afa95fdb2830 diff --git a/examples/bats/bats-support b/examples/bats/bats-support deleted file mode 160000 index 9bf10e876..000000000 --- a/examples/bats/bats-support +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 9bf10e876dd6b624fe44423f0b35e064225f7556 diff --git a/examples/build.sh b/examples/build.sh deleted file mode 100755 index ead0d61d5..000000000 --- a/examples/build.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env bash -set -e - -name="$1" -package="$2" -root="$(dirname "$0")/.." -example_root="$(dirname "$0")/$name" -did_file="/tmp/a.did" - -cargo update --manifest-path="$example_root/Cargo.toml" - -cargo build --manifest-path="$example_root/Cargo.toml" \ - --target wasm32-unknown-unknown \ - --release \ - --package "$package" - -candid-extractor "$example_root/target/wasm32-unknown-unknown/release/$package.wasm" 2>/dev/null > $did_file || true - -ic-wasm "$example_root/target/wasm32-unknown-unknown/release/$package.wasm" \ - -o "$example_root/target/wasm32-unknown-unknown/release/$package.wasm" \ - metadata candid:service -v public -f $did_file - -ic-wasm "$example_root/target/wasm32-unknown-unknown/release/$package.wasm" \ - -o "$example_root/target/wasm32-unknown-unknown/release/$package-opt.wasm" \ - shrink diff --git a/examples/chess/.gitignore b/examples/chess/.gitignore deleted file mode 100644 index aa2f61438..000000000 --- a/examples/chess/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -.dfx/ -canisters/ -target/ -node_modules/ -dist/ diff --git a/examples/chess/Cargo.toml b/examples/chess/Cargo.toml deleted file mode 100644 index c1a34a5b4..000000000 --- a/examples/chess/Cargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[workspace] -members = ["src/chess_rs"] -resolver = "2" - -[profile.release] -lto = true -opt-level = 'z' diff --git a/examples/chess/dfx.json b/examples/chess/dfx.json deleted file mode 100644 index 0c26e6535..000000000 --- a/examples/chess/dfx.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "version": 1, - "canisters": { - "chess_rs": { - "type": "custom", - "candid": "src/chess_rs/chess.did", - "wasm": "target/wasm32-unknown-unknown/release/chess_rs-opt.wasm", - "build": "sh ../build.sh chess chess_rs" - } - } -} diff --git a/examples/chess/src/chess_rs/Cargo.toml b/examples/chess/src/chess_rs/Cargo.toml deleted file mode 100644 index 7bcf08b43..000000000 --- a/examples/chess/src/chess_rs/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "chess_rs" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[lib] -path = "lib.rs" -crate-type = ["cdylib"] - -[dependencies] -candid = "0.10" -ic-cdk = { path = "../../../../src/ic-cdk" } -serde = "1.0.111" -tanton = "1.0.0" -getrandom = { version = "0.2", features = [ - "custom", -] } # tanton requires this to compile on wasm target diff --git a/examples/chess/src/chess_rs/chess.did b/examples/chess/src/chess_rs/chess.did deleted file mode 100644 index 90d4e8b58..000000000 --- a/examples/chess/src/chess_rs/chess.did +++ /dev/null @@ -1,7 +0,0 @@ -service : { - "new": (text, bool) -> (); - "move": (text, text) -> (bool); - "aiMove": (text) -> (); - - "getFen": (text) -> (opt text) query; -} diff --git a/examples/chess/src/chess_rs/getrandom_fail.rs b/examples/chess/src/chess_rs/getrandom_fail.rs deleted file mode 100644 index 5d6225e41..000000000 --- a/examples/chess/src/chess_rs/getrandom_fail.rs +++ /dev/null @@ -1,11 +0,0 @@ -use core::num::NonZeroU32; -use getrandom::{register_custom_getrandom, Error}; - -// Some application-specific error code -const MY_CUSTOM_ERROR_CODE: u32 = Error::CUSTOM_START + 42; -pub fn always_fail(_buf: &mut [u8]) -> Result<(), Error> { - let code = NonZeroU32::new(MY_CUSTOM_ERROR_CODE).unwrap(); - Err(Error::from(code)) -} - -register_custom_getrandom!(always_fail); diff --git a/examples/chess/src/chess_rs/lib.rs b/examples/chess/src/chess_rs/lib.rs deleted file mode 100644 index bd2fb37e3..000000000 --- a/examples/chess/src/chess_rs/lib.rs +++ /dev/null @@ -1,79 +0,0 @@ -use candid::CandidType; -use ic_cdk::{query, update}; -use serde::Serialize; -use std::cell::RefCell; -use std::collections::BTreeMap; -use tanton::tools::Searcher; - -mod getrandom_fail; - -type GameStore = BTreeMap; - -#[derive(Clone, Debug, Default, CandidType, Serialize)] -pub struct Game { - pub fen: String, -} - -pub struct GameInternal { - pub board: tanton::Board, -} - -thread_local! { - static STORE: RefCell = RefCell::default(); -} - -#[update] -fn new(name: String, white: bool) { - STORE.with(|game_store| { - game_store.borrow_mut().insert( - name.clone(), - GameInternal { - board: tanton::Board::start_pos(), - }, - ); - }); - - // If the user is playing black; - if !white { - ai_move(name); - } -} - -#[update(name = "move")] -fn uci_move(name: String, m: String) -> bool { - let should_move = STORE.with(|game_store| { - let mut game_store = game_store.borrow_mut(); - let game = game_store - .get_mut(&name) - .unwrap_or_else(|| panic!("Game {} does not exist.", name)); - - // If the move is valid, also apply the next move using AI. - game.board.apply_uci_move(&m) - }); - if should_move { - ai_move(name); - } - should_move -} - -#[update(name = "aiMove")] -fn ai_move(name: String) { - STORE.with(|game_store| { - let mut game_store = game_store.borrow_mut(); - let game = game_store - .get_mut(&name) - .unwrap_or_else(|| panic!("Game {} does not exist.", name)); - - let b = game.board.shallow_clone(); - let m = tanton::bots::MiniMaxSearcher::best_move(b, 3); - - game.board.apply_move(m); - }); -} - -#[query(name = "getFen")] -fn get_fen(name: String) -> Option { - STORE.with(|game_store| game_store.borrow().get(&name).map(|game| game.board.fen())) -} - -ic_cdk::export_candid!(); diff --git a/examples/chess/src/declarations/chess_rs/chess_rs.did b/examples/chess/src/declarations/chess_rs/chess_rs.did deleted file mode 100644 index 90d4e8b58..000000000 --- a/examples/chess/src/declarations/chess_rs/chess_rs.did +++ /dev/null @@ -1,7 +0,0 @@ -service : { - "new": (text, bool) -> (); - "move": (text, text) -> (bool); - "aiMove": (text) -> (); - - "getFen": (text) -> (opt text) query; -} diff --git a/examples/chess/src/declarations/chess_rs/chess_rs.did.d.ts b/examples/chess/src/declarations/chess_rs/chess_rs.did.d.ts deleted file mode 100644 index 223b33183..000000000 --- a/examples/chess/src/declarations/chess_rs/chess_rs.did.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { Principal } from '@dfinity/principal'; -export interface _SERVICE { - 'aiMove' : (arg_0: string) => Promise, - 'getFen' : (arg_0: string) => Promise<[] | [string]>, - 'move' : (arg_0: string, arg_1: string) => Promise, - 'new' : (arg_0: string, arg_1: boolean) => Promise, -} \ No newline at end of file diff --git a/examples/chess/src/declarations/chess_rs/chess_rs.did.js b/examples/chess/src/declarations/chess_rs/chess_rs.did.js deleted file mode 100644 index 40f751836..000000000 --- a/examples/chess/src/declarations/chess_rs/chess_rs.did.js +++ /dev/null @@ -1,9 +0,0 @@ -export const idlFactory = ({ IDL }) => { - return IDL.Service({ - 'aiMove' : IDL.Func([IDL.Text], [], []), - 'getFen' : IDL.Func([IDL.Text], [IDL.Opt(IDL.Text)], ['query']), - 'move' : IDL.Func([IDL.Text, IDL.Text], [IDL.Bool], []), - 'new' : IDL.Func([IDL.Text, IDL.Bool], [], []), - }); -}; -export const init = ({ IDL }) => { return []; }; \ No newline at end of file diff --git a/examples/chess/src/declarations/chess_rs/index.js b/examples/chess/src/declarations/chess_rs/index.js deleted file mode 100644 index 6624ae644..000000000 --- a/examples/chess/src/declarations/chess_rs/index.js +++ /dev/null @@ -1,32 +0,0 @@ -import { Actor, HttpAgent } from "@dfinity/agent"; - -// Imports candid interface -import { idlFactory } from './chess_rs.did.js'; -// CANISTER_ID is replaced by webpack based on node enviroment -export const canisterId = process.env.CHESS_RS_CANISTER_ID; - -/** - * - * @param {string | Principal} canisterId Canister ID of Agent - * @param {{agentOptions?: import("@dfinity/agent").HttpAgentOptions; actorOptions?: import("@dfinity/agent").ActorConfig}} [options] - * @return {import("@dfinity/agent").ActorSubclass} - */ - export const createActor = (canisterId, options) => { - const agent = new HttpAgent({ ...options?.agentOptions }); - - // Fetch root key for certificate validation during development - if(process.env.NODE_ENV !== "production") agent.fetchRootKey(); - - // Creates an actor with using the candid interface and the HttpAgent - return Actor.createActor(idlFactory, { - agent, - canisterId, - ...options?.actorOptions, - }); -}; - -/** - * A ready-to-use agent for the chess_rs canister - * @type {import("@dfinity/agent").ActorSubclass} - */ - export const chess_rs = createActor(canisterId); \ No newline at end of file diff --git a/examples/chess/src/declarations/chess_rs_assets/assetstorage.did b/examples/chess/src/declarations/chess_rs_assets/assetstorage.did deleted file mode 100644 index d11ecd992..000000000 --- a/examples/chess/src/declarations/chess_rs_assets/assetstorage.did +++ /dev/null @@ -1,140 +0,0 @@ -type BatchId = nat; -type ChunkId = nat; -type Key = text; -type Time = int; - -type CreateAssetArguments = record { - key: Key; - content_type: text; -}; - -// Add or change content for an asset, by content encoding -type SetAssetContentArguments = record { - key: Key; - content_encoding: text; - chunk_ids: vec ChunkId; - sha256: opt blob; -}; - -// Remove content for an asset, by content encoding -type UnsetAssetContentArguments = record { - key: Key; - content_encoding: text; -}; - -// Delete an asset -type DeleteAssetArguments = record { - key: Key; -}; - -// Reset everything -type ClearArguments = record {}; - -type BatchOperationKind = variant { - CreateAsset: CreateAssetArguments; - SetAssetContent: SetAssetContentArguments; - - UnsetAssetContent: UnsetAssetContentArguments; - DeleteAsset: DeleteAssetArguments; - - Clear: ClearArguments; -}; - -type HeaderField = record { text; text; }; - -type HttpRequest = record { - method: text; - url: text; - headers: vec HeaderField; - body: blob; -}; - -type HttpResponse = record { - status_code: nat16; - headers: vec HeaderField; - body: blob; - streaming_strategy: opt StreamingStrategy; -}; - -type StreamingCallbackHttpResponse = record { - body: blob; - token: opt StreamingCallbackToken; -}; - -type StreamingCallbackToken = record { - key: Key; - content_encoding: text; - index: nat; - sha256: opt blob; -}; - -type StreamingStrategy = variant { - Callback: record { - callback: func (StreamingCallbackToken) -> (opt StreamingCallbackHttpResponse) query; - token: StreamingCallbackToken; - }; -}; - -service: { - - get: (record { - key: Key; - accept_encodings: vec text; - }) -> (record { - content: blob; // may be the entirety of the content, or just chunk index 0 - content_type: text; - content_encoding: text; - sha256: opt blob; // sha256 of entire asset encoding, calculated by dfx and passed in SetAssetContentArguments - total_length: nat; // all chunks except last have size == content.size() - }) query; - - // if get() returned chunks > 1, call this to retrieve them. - // chunks may or may not be split up at the same boundaries as presented to create_chunk(). - get_chunk: (record { - key: Key; - content_encoding: text; - index: nat; - sha256: opt blob; // sha256 of entire asset encoding, calculated by dfx and passed in SetAssetContentArguments - }) -> (record { content: blob }) query; - - list : (record {}) -> (vec record { - key: Key; - content_type: text; - encodings: vec record { - content_encoding: text; - sha256: opt blob; // sha256 of entire asset encoding, calculated by dfx and passed in SetAssetContentArguments - length: nat; // Size of this encoding's blob. Calculated when uploading assets. - modified: Time; - }; - }) query; - - create_batch : (record {}) -> (record { batch_id: BatchId }); - - create_chunk: (record { batch_id: BatchId; content: blob }) -> (record { chunk_id: ChunkId }); - - // Perform all operations successfully, or reject - commit_batch: (record { batch_id: BatchId; operations: vec BatchOperationKind }) -> (); - - create_asset: (CreateAssetArguments) -> (); - set_asset_content: (SetAssetContentArguments) -> (); - unset_asset_content: (UnsetAssetContentArguments) -> (); - - delete_asset: (DeleteAssetArguments) -> (); - - clear: (ClearArguments) -> (); - - // Single call to create an asset with content for a single content encoding that - // fits within the message ingress limit. - store: (record { - key: Key; - content_type: text; - content_encoding: text; - content: blob; - sha256: opt blob - }) -> (); - - http_request: (request: HttpRequest) -> (HttpResponse) query; - http_request_streaming_callback: (token: StreamingCallbackToken) -> (opt StreamingCallbackHttpResponse) query; - - authorize: (principal) -> (); -} diff --git a/examples/chess/src/declarations/chess_rs_assets/chess_rs_assets.did b/examples/chess/src/declarations/chess_rs_assets/chess_rs_assets.did deleted file mode 100644 index d11ecd992..000000000 --- a/examples/chess/src/declarations/chess_rs_assets/chess_rs_assets.did +++ /dev/null @@ -1,140 +0,0 @@ -type BatchId = nat; -type ChunkId = nat; -type Key = text; -type Time = int; - -type CreateAssetArguments = record { - key: Key; - content_type: text; -}; - -// Add or change content for an asset, by content encoding -type SetAssetContentArguments = record { - key: Key; - content_encoding: text; - chunk_ids: vec ChunkId; - sha256: opt blob; -}; - -// Remove content for an asset, by content encoding -type UnsetAssetContentArguments = record { - key: Key; - content_encoding: text; -}; - -// Delete an asset -type DeleteAssetArguments = record { - key: Key; -}; - -// Reset everything -type ClearArguments = record {}; - -type BatchOperationKind = variant { - CreateAsset: CreateAssetArguments; - SetAssetContent: SetAssetContentArguments; - - UnsetAssetContent: UnsetAssetContentArguments; - DeleteAsset: DeleteAssetArguments; - - Clear: ClearArguments; -}; - -type HeaderField = record { text; text; }; - -type HttpRequest = record { - method: text; - url: text; - headers: vec HeaderField; - body: blob; -}; - -type HttpResponse = record { - status_code: nat16; - headers: vec HeaderField; - body: blob; - streaming_strategy: opt StreamingStrategy; -}; - -type StreamingCallbackHttpResponse = record { - body: blob; - token: opt StreamingCallbackToken; -}; - -type StreamingCallbackToken = record { - key: Key; - content_encoding: text; - index: nat; - sha256: opt blob; -}; - -type StreamingStrategy = variant { - Callback: record { - callback: func (StreamingCallbackToken) -> (opt StreamingCallbackHttpResponse) query; - token: StreamingCallbackToken; - }; -}; - -service: { - - get: (record { - key: Key; - accept_encodings: vec text; - }) -> (record { - content: blob; // may be the entirety of the content, or just chunk index 0 - content_type: text; - content_encoding: text; - sha256: opt blob; // sha256 of entire asset encoding, calculated by dfx and passed in SetAssetContentArguments - total_length: nat; // all chunks except last have size == content.size() - }) query; - - // if get() returned chunks > 1, call this to retrieve them. - // chunks may or may not be split up at the same boundaries as presented to create_chunk(). - get_chunk: (record { - key: Key; - content_encoding: text; - index: nat; - sha256: opt blob; // sha256 of entire asset encoding, calculated by dfx and passed in SetAssetContentArguments - }) -> (record { content: blob }) query; - - list : (record {}) -> (vec record { - key: Key; - content_type: text; - encodings: vec record { - content_encoding: text; - sha256: opt blob; // sha256 of entire asset encoding, calculated by dfx and passed in SetAssetContentArguments - length: nat; // Size of this encoding's blob. Calculated when uploading assets. - modified: Time; - }; - }) query; - - create_batch : (record {}) -> (record { batch_id: BatchId }); - - create_chunk: (record { batch_id: BatchId; content: blob }) -> (record { chunk_id: ChunkId }); - - // Perform all operations successfully, or reject - commit_batch: (record { batch_id: BatchId; operations: vec BatchOperationKind }) -> (); - - create_asset: (CreateAssetArguments) -> (); - set_asset_content: (SetAssetContentArguments) -> (); - unset_asset_content: (UnsetAssetContentArguments) -> (); - - delete_asset: (DeleteAssetArguments) -> (); - - clear: (ClearArguments) -> (); - - // Single call to create an asset with content for a single content encoding that - // fits within the message ingress limit. - store: (record { - key: Key; - content_type: text; - content_encoding: text; - content: blob; - sha256: opt blob - }) -> (); - - http_request: (request: HttpRequest) -> (HttpResponse) query; - http_request_streaming_callback: (token: StreamingCallbackToken) -> (opt StreamingCallbackHttpResponse) query; - - authorize: (principal) -> (); -} diff --git a/examples/chess/src/declarations/chess_rs_assets/chess_rs_assets.did.d.ts b/examples/chess/src/declarations/chess_rs_assets/chess_rs_assets.did.d.ts deleted file mode 100644 index b3166873d..000000000 --- a/examples/chess/src/declarations/chess_rs_assets/chess_rs_assets.did.d.ts +++ /dev/null @@ -1,117 +0,0 @@ -import type { Principal } from '@dfinity/principal'; -export type BatchId = bigint; -export type BatchOperationKind = { 'CreateAsset' : CreateAssetArguments } | - { 'UnsetAssetContent' : UnsetAssetContentArguments } | - { 'DeleteAsset' : DeleteAssetArguments } | - { 'SetAssetContent' : SetAssetContentArguments } | - { 'Clear' : ClearArguments }; -export type ChunkId = bigint; -export type ClearArguments = {}; -export interface CreateAssetArguments { 'key' : Key, 'content_type' : string } -export interface DeleteAssetArguments { 'key' : Key } -export type HeaderField = [string, string]; -export interface HttpRequest { - 'url' : string, - 'method' : string, - 'body' : Array, - 'headers' : Array, -} -export interface HttpResponse { - 'body' : Array, - 'headers' : Array, - 'streaming_strategy' : [] | [StreamingStrategy], - 'status_code' : number, -} -export type Key = string; -export interface SetAssetContentArguments { - 'key' : Key, - 'sha256' : [] | [Array], - 'chunk_ids' : Array, - 'content_encoding' : string, -} -export interface StreamingCallbackHttpResponse { - 'token' : [] | [StreamingCallbackToken], - 'body' : Array, -} -export interface StreamingCallbackToken { - 'key' : Key, - 'sha256' : [] | [Array], - 'index' : bigint, - 'content_encoding' : string, -} -export type StreamingStrategy = { - 'Callback' : { - 'token' : StreamingCallbackToken, - 'callback' : [Principal, string], - } - }; -export type Time = bigint; -export interface UnsetAssetContentArguments { - 'key' : Key, - 'content_encoding' : string, -} -export interface _SERVICE { - 'authorize' : (arg_0: Principal) => Promise, - 'clear' : (arg_0: ClearArguments) => Promise, - 'commit_batch' : ( - arg_0: { 'batch_id' : BatchId, 'operations' : Array }, - ) => Promise, - 'create_asset' : (arg_0: CreateAssetArguments) => Promise, - 'create_batch' : (arg_0: {}) => Promise<{ 'batch_id' : BatchId }>, - 'create_chunk' : ( - arg_0: { 'content' : Array, 'batch_id' : BatchId }, - ) => Promise<{ 'chunk_id' : ChunkId }>, - 'delete_asset' : (arg_0: DeleteAssetArguments) => Promise, - 'get' : ( - arg_0: { 'key' : Key, 'accept_encodings' : Array }, - ) => Promise< - { - 'content' : Array, - 'sha256' : [] | [Array], - 'content_type' : string, - 'content_encoding' : string, - 'total_length' : bigint, - } - >, - 'get_chunk' : ( - arg_0: { - 'key' : Key, - 'sha256' : [] | [Array], - 'index' : bigint, - 'content_encoding' : string, - }, - ) => Promise<{ 'content' : Array }>, - 'http_request' : (arg_0: HttpRequest) => Promise, - 'http_request_streaming_callback' : ( - arg_0: StreamingCallbackToken, - ) => Promise<[] | [StreamingCallbackHttpResponse]>, - 'list' : (arg_0: {}) => Promise< - Array< - { - 'key' : Key, - 'encodings' : Array< - { - 'modified' : Time, - 'sha256' : [] | [Array], - 'length' : bigint, - 'content_encoding' : string, - } - >, - 'content_type' : string, - } - > - >, - 'set_asset_content' : (arg_0: SetAssetContentArguments) => Promise, - 'store' : ( - arg_0: { - 'key' : Key, - 'content' : Array, - 'sha256' : [] | [Array], - 'content_type' : string, - 'content_encoding' : string, - }, - ) => Promise, - 'unset_asset_content' : (arg_0: UnsetAssetContentArguments) => Promise< - undefined - >, -} \ No newline at end of file diff --git a/examples/chess/src/declarations/chess_rs_assets/chess_rs_assets.did.js b/examples/chess/src/declarations/chess_rs_assets/chess_rs_assets.did.js deleted file mode 100644 index 79123b18b..000000000 --- a/examples/chess/src/declarations/chess_rs_assets/chess_rs_assets.did.js +++ /dev/null @@ -1,155 +0,0 @@ -export const idlFactory = ({ IDL }) => { - const ClearArguments = IDL.Record({}); - const BatchId = IDL.Nat; - const Key = IDL.Text; - const CreateAssetArguments = IDL.Record({ - 'key' : Key, - 'content_type' : IDL.Text, - }); - const UnsetAssetContentArguments = IDL.Record({ - 'key' : Key, - 'content_encoding' : IDL.Text, - }); - const DeleteAssetArguments = IDL.Record({ 'key' : Key }); - const ChunkId = IDL.Nat; - const SetAssetContentArguments = IDL.Record({ - 'key' : Key, - 'sha256' : IDL.Opt(IDL.Vec(IDL.Nat8)), - 'chunk_ids' : IDL.Vec(ChunkId), - 'content_encoding' : IDL.Text, - }); - const BatchOperationKind = IDL.Variant({ - 'CreateAsset' : CreateAssetArguments, - 'UnsetAssetContent' : UnsetAssetContentArguments, - 'DeleteAsset' : DeleteAssetArguments, - 'SetAssetContent' : SetAssetContentArguments, - 'Clear' : ClearArguments, - }); - const HeaderField = IDL.Tuple(IDL.Text, IDL.Text); - const HttpRequest = IDL.Record({ - 'url' : IDL.Text, - 'method' : IDL.Text, - 'body' : IDL.Vec(IDL.Nat8), - 'headers' : IDL.Vec(HeaderField), - }); - const StreamingCallbackToken = IDL.Record({ - 'key' : Key, - 'sha256' : IDL.Opt(IDL.Vec(IDL.Nat8)), - 'index' : IDL.Nat, - 'content_encoding' : IDL.Text, - }); - const StreamingCallbackHttpResponse = IDL.Record({ - 'token' : IDL.Opt(StreamingCallbackToken), - 'body' : IDL.Vec(IDL.Nat8), - }); - const StreamingStrategy = IDL.Variant({ - 'Callback' : IDL.Record({ - 'token' : StreamingCallbackToken, - 'callback' : IDL.Func( - [StreamingCallbackToken], - [IDL.Opt(StreamingCallbackHttpResponse)], - ['query'], - ), - }), - }); - const HttpResponse = IDL.Record({ - 'body' : IDL.Vec(IDL.Nat8), - 'headers' : IDL.Vec(HeaderField), - 'streaming_strategy' : IDL.Opt(StreamingStrategy), - 'status_code' : IDL.Nat16, - }); - const Time = IDL.Int; - return IDL.Service({ - 'authorize' : IDL.Func([IDL.Principal], [], []), - 'clear' : IDL.Func([ClearArguments], [], []), - 'commit_batch' : IDL.Func( - [ - IDL.Record({ - 'batch_id' : BatchId, - 'operations' : IDL.Vec(BatchOperationKind), - }), - ], - [], - [], - ), - 'create_asset' : IDL.Func([CreateAssetArguments], [], []), - 'create_batch' : IDL.Func( - [IDL.Record({})], - [IDL.Record({ 'batch_id' : BatchId })], - [], - ), - 'create_chunk' : IDL.Func( - [IDL.Record({ 'content' : IDL.Vec(IDL.Nat8), 'batch_id' : BatchId })], - [IDL.Record({ 'chunk_id' : ChunkId })], - [], - ), - 'delete_asset' : IDL.Func([DeleteAssetArguments], [], []), - 'get' : IDL.Func( - [IDL.Record({ 'key' : Key, 'accept_encodings' : IDL.Vec(IDL.Text) })], - [ - IDL.Record({ - 'content' : IDL.Vec(IDL.Nat8), - 'sha256' : IDL.Opt(IDL.Vec(IDL.Nat8)), - 'content_type' : IDL.Text, - 'content_encoding' : IDL.Text, - 'total_length' : IDL.Nat, - }), - ], - ['query'], - ), - 'get_chunk' : IDL.Func( - [ - IDL.Record({ - 'key' : Key, - 'sha256' : IDL.Opt(IDL.Vec(IDL.Nat8)), - 'index' : IDL.Nat, - 'content_encoding' : IDL.Text, - }), - ], - [IDL.Record({ 'content' : IDL.Vec(IDL.Nat8) })], - ['query'], - ), - 'http_request' : IDL.Func([HttpRequest], [HttpResponse], ['query']), - 'http_request_streaming_callback' : IDL.Func( - [StreamingCallbackToken], - [IDL.Opt(StreamingCallbackHttpResponse)], - ['query'], - ), - 'list' : IDL.Func( - [IDL.Record({})], - [ - IDL.Vec( - IDL.Record({ - 'key' : Key, - 'encodings' : IDL.Vec( - IDL.Record({ - 'modified' : Time, - 'sha256' : IDL.Opt(IDL.Vec(IDL.Nat8)), - 'length' : IDL.Nat, - 'content_encoding' : IDL.Text, - }) - ), - 'content_type' : IDL.Text, - }) - ), - ], - ['query'], - ), - 'set_asset_content' : IDL.Func([SetAssetContentArguments], [], []), - 'store' : IDL.Func( - [ - IDL.Record({ - 'key' : Key, - 'content' : IDL.Vec(IDL.Nat8), - 'sha256' : IDL.Opt(IDL.Vec(IDL.Nat8)), - 'content_type' : IDL.Text, - 'content_encoding' : IDL.Text, - }), - ], - [], - [], - ), - 'unset_asset_content' : IDL.Func([UnsetAssetContentArguments], [], []), - }); -}; -export const init = ({ IDL }) => { return []; }; \ No newline at end of file diff --git a/examples/chess/src/declarations/chess_rs_assets/index.js b/examples/chess/src/declarations/chess_rs_assets/index.js deleted file mode 100644 index 34b0e9eb7..000000000 --- a/examples/chess/src/declarations/chess_rs_assets/index.js +++ /dev/null @@ -1,32 +0,0 @@ -import { Actor, HttpAgent } from "@dfinity/agent"; - -// Imports candid interface -import { idlFactory } from './chess_rs_assets.did.js'; -// CANISTER_ID is replaced by webpack based on node enviroment -export const canisterId = process.env.CHESS_RS_ASSETS_CANISTER_ID; - -/** - * - * @param {string | Principal} canisterId Canister ID of Agent - * @param {{agentOptions?: import("@dfinity/agent").HttpAgentOptions; actorOptions?: import("@dfinity/agent").ActorConfig}} [options] - * @return {import("@dfinity/agent").ActorSubclass} - */ - export const createActor = (canisterId, options) => { - const agent = new HttpAgent({ ...options?.agentOptions }); - - // Fetch root key for certificate validation during development - if(process.env.NODE_ENV !== "production") agent.fetchRootKey(); - - // Creates an actor with using the candid interface and the HttpAgent - return Actor.createActor(idlFactory, { - agent, - canisterId, - ...options?.actorOptions, - }); -}; - -/** - * A ready-to-use agent for the chess_rs_assets canister - * @type {import("@dfinity/agent").ActorSubclass} - */ - export const chess_rs_assets = createActor(canisterId); \ No newline at end of file diff --git a/examples/chess/tests/basic.bats b/examples/chess/tests/basic.bats deleted file mode 100644 index a9ee3acfd..000000000 --- a/examples/chess/tests/basic.bats +++ /dev/null @@ -1,25 +0,0 @@ -load ../../bats/bats-assert/load.bash - -# Executed before each test. -setup() { - cd examples/chess - - dfx start --clean --background -} - -# executed after each test -teardown() { - dfx stop -} - -@test "Can play chess against AI" { - dfx deploy - run dfx canister call chess_rs new '("test", true)' - assert_output '()' - run dfx canister call chess_rs move '("test", "e2e4")' - assert_output '(true)' - run dfx canister call chess_rs move '("test", "d2d3")' - assert_output '(true)' - run dfx canister call chess_rs getFen '("test")' - assert_output '(opt "rnb1kbnr/pp1ppppp/1qp5/8/4P3/3P4/PPP2PPP/RNBQKBNR w KQkq - 1 3")' -} diff --git a/examples/counter/.gitignore b/examples/counter/.gitignore deleted file mode 100644 index 958d33e64..000000000 --- a/examples/counter/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -.dfx/ -canisters/ -target/ - -src/counter_rs/counter.did diff --git a/examples/counter/Cargo.toml b/examples/counter/Cargo.toml deleted file mode 100644 index 46f1a57ca..000000000 --- a/examples/counter/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[workspace] -members = ["src/counter_rs", "src/inter_rs", "src/inter2_rs"] -resolver = "2" - -[workspace.dependencies] -candid = "0.10" -ic-cdk = { path = "../../src/ic-cdk" } -ic-cdk-bindgen = { path = "../../src/ic-cdk-bindgen" } diff --git a/examples/counter/dfx.json b/examples/counter/dfx.json deleted file mode 100644 index 02c890da7..000000000 --- a/examples/counter/dfx.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "version": 1, - "canisters": { - "counter_rs": { - "type": "custom", - "candid": "src/counter_rs/counter.did", - "wasm": "target/wasm32-unknown-unknown/release/counter_rs-opt.wasm", - "build": [ - "sh ../build.sh counter counter_rs", - "cp /tmp/a.did src/counter_rs/counter.did" - ] - }, - "counter_mo": { - "type": "motoko", - "main": "src/counter_mo/main.mo" - }, - "inter_rs": { - "type": "custom", - "candid": "src/inter_rs/lib.did", - "wasm": "target/wasm32-unknown-unknown/release/inter_rs-opt.wasm", - "build": "sh ../build.sh counter inter_rs", - "dependencies": [ - "counter_mo" - ] - }, - "inter_mo": { - "type": "motoko", - "main": "src/inter_mo/main.mo" - }, - "inter2_rs": { - "type": "custom", - "candid": "src/inter2_rs/lib.did", - "wasm": "target/wasm32-unknown-unknown/release/inter2_rs-opt.wasm", - "build": "sh ../build.sh counter inter2_rs", - "dependencies": [ - "inter_mo" - ] - }, - "inter2_mo": { - "type": "motoko", - "main": "src/inter2_mo/main.mo" - } - }, - "defaults": { - "build": { - "output": "canisters/" - }, - "start": { - "address": "127.0.0.1", - "port": 8000, - "serve_root": "canisters/eeoo/assets" - } - } -} diff --git a/examples/counter/src/counter_mo/main.mo b/examples/counter/src/counter_mo/main.mo deleted file mode 100644 index 406161b27..000000000 --- a/examples/counter/src/counter_mo/main.mo +++ /dev/null @@ -1,15 +0,0 @@ -actor Counter { - var cell : Nat = 0; - - public func inc() : async () { - cell += 1; - }; - - public query func read() : async Nat { - cell - }; - - public func write(n: Nat) : async () { - cell := n; - }; -} diff --git a/examples/counter/src/counter_rs/Cargo.toml b/examples/counter/src/counter_rs/Cargo.toml deleted file mode 100644 index 9e79d3c98..000000000 --- a/examples/counter/src/counter_rs/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "counter_rs" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[lib] -path = "lib.rs" -crate-type = ["cdylib"] - -[dependencies] -candid = { workspace = true } -ic-cdk = { workspace = true } -lazy_static = "1.4.0" diff --git a/examples/counter/src/counter_rs/lib.rs b/examples/counter/src/counter_rs/lib.rs deleted file mode 100644 index 0368e7dad..000000000 --- a/examples/counter/src/counter_rs/lib.rs +++ /dev/null @@ -1,37 +0,0 @@ -use candid::Principal; -use ic_cdk::{api::call::ManualReply, init, query, update}; -use std::cell::{Cell, RefCell}; - -thread_local! { - static COUNTER: RefCell = RefCell::new(candid::Nat::from(0u8)); - static OWNER: Cell = Cell::new(Principal::from_slice(&[])); -} - -#[init] -fn init() { - OWNER.with(|owner| owner.set(ic_cdk::api::caller())); -} - -#[update(debug = true, decoding_quota = 50, skipping_quota = 0)] -fn inc() { - ic_cdk::println!("{:?}", OWNER.with(|owner| owner.get())); - COUNTER.with(|counter| *counter.borrow_mut() += 1u64); -} - -#[query(manual_reply = true)] -fn read() -> ManualReply { - COUNTER.with(|counter| ManualReply::one(counter)) -} - -#[update] -fn write(input: candid::Nat) { - COUNTER.with(|counter| *counter.borrow_mut() = input); -} - -#[update(hidden = true)] -fn update_hidden() {} - -#[query(hidden = true)] -fn query_hidden() {} - -ic_cdk::export_candid!(); diff --git a/examples/counter/src/inter2_mo/main.mo b/examples/counter/src/inter2_mo/main.mo deleted file mode 100644 index 5831058c1..000000000 --- a/examples/counter/src/inter2_mo/main.mo +++ /dev/null @@ -1,15 +0,0 @@ -import CounterRs "canister:inter_rs"; - -actor Counter { - public func inc() : async () { - await CounterRs.inc() - }; - - public func read() : async Nat { - await CounterRs.read() - }; - - public func write(n: Nat) : async () { - await CounterRs.write(n) - }; -} diff --git a/examples/counter/src/inter2_rs/Cargo.toml b/examples/counter/src/inter2_rs/Cargo.toml deleted file mode 100644 index db86938ff..000000000 --- a/examples/counter/src/inter2_rs/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "inter2_rs" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[lib] -path = "lib.rs" -crate-type = ["cdylib"] - -[dependencies] -candid = { workspace = true } -ic-cdk = { workspace = true } - -[build-dependencies] -ic-cdk-bindgen = { workspace = true } diff --git a/examples/counter/src/inter2_rs/build.rs b/examples/counter/src/inter2_rs/build.rs deleted file mode 100644 index 250d93a24..000000000 --- a/examples/counter/src/inter2_rs/build.rs +++ /dev/null @@ -1,11 +0,0 @@ -use ic_cdk_bindgen::{Builder, Config}; -use std::path::PathBuf; - -fn main() { - let manifest_dir = - PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").expect("Cannot find manifest dir")); - let inter_mo = Config::new("inter_mo"); - let mut builder = Builder::new(); - builder.add(inter_mo); - builder.build(Some(manifest_dir.join("declarations"))); -} diff --git a/examples/counter/src/inter2_rs/lib.did b/examples/counter/src/inter2_rs/lib.did deleted file mode 100644 index 8881912b1..000000000 --- a/examples/counter/src/inter2_rs/lib.did +++ /dev/null @@ -1,5 +0,0 @@ -service : { - "inc": () -> (); - "read": () -> (nat); - "write": (nat) -> (); -} diff --git a/examples/counter/src/inter2_rs/lib.rs b/examples/counter/src/inter2_rs/lib.rs deleted file mode 100644 index 195d36f2d..000000000 --- a/examples/counter/src/inter2_rs/lib.rs +++ /dev/null @@ -1,21 +0,0 @@ -use ic_cdk::update; - -mod declarations; -use declarations::inter_mo::inter_mo; - -#[update] -async fn read() -> candid::Nat { - inter_mo.read().await.unwrap().0 -} - -#[update] -async fn inc() { - inter_mo.inc().await.unwrap() -} - -#[update] -async fn write(input: candid::Nat) { - inter_mo.write(input).await.unwrap() -} - -ic_cdk::export_candid!(); diff --git a/examples/counter/src/inter_mo/main.mo b/examples/counter/src/inter_mo/main.mo deleted file mode 100644 index 5b705232c..000000000 --- a/examples/counter/src/inter_mo/main.mo +++ /dev/null @@ -1,15 +0,0 @@ -import CounterRs "canister:counter_rs"; - -actor Counter { - public func inc() : async () { - await CounterRs.inc() - }; - - public func read() : async Nat { - await CounterRs.read() - }; - - public func write(n: Nat) : async () { - await CounterRs.write(n) - }; -} diff --git a/examples/counter/src/inter_rs/Cargo.toml b/examples/counter/src/inter_rs/Cargo.toml deleted file mode 100644 index 6be96c0f6..000000000 --- a/examples/counter/src/inter_rs/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "inter_rs" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[lib] -path = "lib.rs" -crate-type = ["cdylib"] - -[dependencies] -candid = { workspace = true } -ic-cdk = { workspace = true } - -[build-dependencies] -ic-cdk-bindgen = { workspace = true } diff --git a/examples/counter/src/inter_rs/build.rs b/examples/counter/src/inter_rs/build.rs deleted file mode 100644 index 9b9493ff0..000000000 --- a/examples/counter/src/inter_rs/build.rs +++ /dev/null @@ -1,11 +0,0 @@ -use ic_cdk_bindgen::{Builder, Config}; -use std::path::PathBuf; - -fn main() { - let manifest_dir = - PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").expect("Cannot find manifest dir")); - let counter = Config::new("counter_mo"); - let mut builder = Builder::new(); - builder.add(counter); - builder.build(Some(manifest_dir.join("declarations"))); -} diff --git a/examples/counter/src/inter_rs/lib.did b/examples/counter/src/inter_rs/lib.did deleted file mode 100644 index 8881912b1..000000000 --- a/examples/counter/src/inter_rs/lib.did +++ /dev/null @@ -1,5 +0,0 @@ -service : { - "inc": () -> (); - "read": () -> (nat); - "write": (nat) -> (); -} diff --git a/examples/counter/src/inter_rs/lib.rs b/examples/counter/src/inter_rs/lib.rs deleted file mode 100644 index 4657db6c7..000000000 --- a/examples/counter/src/inter_rs/lib.rs +++ /dev/null @@ -1,21 +0,0 @@ -use ic_cdk::update; - -mod declarations; -use declarations::counter_mo::counter_mo; - -#[update] -async fn read() -> candid::Nat { - counter_mo.read().await.unwrap().0 -} - -#[update] -async fn inc() { - counter_mo.inc().await.unwrap() -} - -#[update] -async fn write(input: candid::Nat) { - counter_mo.write(input).await.unwrap() -} - -ic_cdk::export_candid!(); diff --git a/examples/counter/tests/basic.bats b/examples/counter/tests/basic.bats deleted file mode 100644 index eff4af782..000000000 --- a/examples/counter/tests/basic.bats +++ /dev/null @@ -1,69 +0,0 @@ -load ../../bats/bats-assert/load.bash - -# Executed before each test. -setup() { - cd examples/counter - # Make sure the directory is clean. - dfx start --clean --background -} - -# executed after each test -teardown() { - dfx stop -} - -@test "Can counter (counter_rs)" { - dfx deploy - run dfx canister call counter_rs read - assert_output '(0 : nat)' - dfx canister call counter_rs inc - run dfx canister call counter_rs read - assert_output '(1 : nat)' - run dfx canister call counter_rs write '(5)' - dfx canister call counter_rs inc - run dfx canister call counter_rs read - assert_output '(6 : nat)' -} - -@test "Can counter (inter_rs)" { - dfx deploy - run dfx canister call inter_rs read - assert_output '(0 : nat)' - dfx canister call inter_rs inc - run dfx canister call inter_rs read - assert_output '(1 : nat)' - run dfx canister call inter_rs write '(5)' - dfx canister call inter_rs inc - run dfx canister call inter_rs read - assert_output '(6 : nat)' -} - -@test "Can counter (inter2_rs)" { - dfx deploy - - run dfx canister call counter_rs read - assert_output '(0 : nat)' - - run dfx canister call inter2_rs read - assert_output '(0 : nat)' - dfx canister call inter2_rs inc - run dfx canister call inter2_rs read - assert_output '(1 : nat)' - run dfx canister call inter2_rs write '(5)' - dfx canister call inter2_rs inc - run dfx canister call inter2_rs read - assert_output '(6 : nat)' - - # Check that counter_rs has 6 too. - run dfx canister call counter_rs read - assert_output '(6 : nat)' -} - -@test "counter_rs generated Candid excludes hidden methods" { - dfx build --check counter_rs - run grep -q update_hidden src/counter_rs/counter.did - assert_failure - - run grep -q query_hidden src/counter_rs/counter.did - assert_failure -} diff --git a/examples/management_canister/.gitignore b/examples/management_canister/.gitignore deleted file mode 100644 index 789b75c9c..000000000 --- a/examples/management_canister/.gitignore +++ /dev/null @@ -1 +0,0 @@ -ic-btc-canister.wasm.gz diff --git a/examples/management_canister/Cargo.toml b/examples/management_canister/Cargo.toml deleted file mode 100644 index 012852de8..000000000 --- a/examples/management_canister/Cargo.toml +++ /dev/null @@ -1,3 +0,0 @@ -[workspace] -members = ["src/caller"] -resolver = "2" diff --git a/examples/management_canister/dfx.json b/examples/management_canister/dfx.json deleted file mode 100644 index 1420a1204..000000000 --- a/examples/management_canister/dfx.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "version": 1, - "canisters": { - "caller": { - "type": "custom", - "candid": "src/caller/caller.did", - "wasm": "target/wasm32-unknown-unknown/release/caller-opt.wasm", - "build": "sh ../build.sh management_canister caller" - } - } -} \ No newline at end of file diff --git a/examples/management_canister/src/caller/Cargo.toml b/examples/management_canister/src/caller/Cargo.toml deleted file mode 100644 index 2da01207d..000000000 --- a/examples/management_canister/src/caller/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "caller" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[lib] -path = "lib.rs" -crate-type = ["cdylib"] - -[dependencies] -candid = "0.10" -ic-cdk = { path = "../../../../src/ic-cdk", features = ["transform-closure"] } -sha2 = "0.10" diff --git a/examples/management_canister/src/caller/caller.did b/examples/management_canister/src/caller/caller.did deleted file mode 100644 index 5a0431007..000000000 --- a/examples/management_canister/src/caller/caller.did +++ /dev/null @@ -1,7 +0,0 @@ -service : { - "execute_main_methods" : () -> (); - "execute_provisional_methods" : () -> (); - "http_request_example" : () -> (); - "execute_ecdsa_methods" : () -> (); - "execute_bitcoin_methods" : () -> (); -} \ No newline at end of file diff --git a/examples/management_canister/src/caller/lib.rs b/examples/management_canister/src/caller/lib.rs deleted file mode 100644 index 2ac5d9b94..000000000 --- a/examples/management_canister/src/caller/lib.rs +++ /dev/null @@ -1,174 +0,0 @@ -use ic_cdk::*; - -mod http_request { - use super::*; - use ic_cdk::api::management_canister::http_request::*; - - fn http_request_required_cycles(arg: &CanisterHttpRequestArgument) -> u128 { - let max_response_bytes = match arg.max_response_bytes { - Some(ref n) => *n as u128, - None => 2 * 1024 * 1024u128, // default 2MiB - }; - let arg_raw = candid::utils::encode_args((arg,)).expect("Failed to encode arguments."); - // The fee is for a 13-node subnet to demonstrate a typical usage. - (3_000_000u128 - + 60_000u128 * 13 - + (arg_raw.len() as u128 + "http_request".len() as u128) * 400 - + max_response_bytes * 800) - * 13 - } - - #[update] - async fn http_request_example() { - let url = "https://example.com".to_string(); - let arg = CanisterHttpRequestArgument { - url, - max_response_bytes: Some(3000), - method: HttpMethod::GET, - headers: vec![], - body: None, - transform: None, - }; - let header = HttpHeader { - name: "custom-header".to_string(), - value: "test".to_string(), - }; - let cycles = http_request_required_cycles(&arg); - let response = http_request_with_closure(arg.clone(), cycles, { - let header = header.clone(); - move |mut response| { - response.headers = vec![header]; - response - } - }) - .await - .unwrap() - .0; - assert_eq!(response.status, 200u8); - assert_eq!(response.headers.get(0), Some(&header)); - } -} - -mod ecdsa { - use super::*; - use ic_cdk::api::management_canister::ecdsa::*; - use sha2::Digest; - - #[update] - async fn execute_ecdsa_methods() { - let key_id = EcdsaKeyId { - curve: EcdsaCurve::Secp256k1, - name: "dfx_test_key".to_string(), - }; - let derivation_path = vec![]; - let arg = EcdsaPublicKeyArgument { - canister_id: None, - derivation_path: derivation_path.clone(), - key_id: key_id.clone(), - }; - let EcdsaPublicKeyResponse { - public_key, - chain_code, - } = ecdsa_public_key(arg).await.unwrap().0; - assert_eq!(public_key.len(), 33); - assert_eq!(chain_code.len(), 32); - - let message = "hello world"; - let message_hash = sha2::Sha256::digest(message).to_vec(); - let arg = SignWithEcdsaArgument { - message_hash, - derivation_path, - key_id, - }; - let SignWithEcdsaResponse { signature } = sign_with_ecdsa(arg).await.unwrap().0; - assert_eq!(signature.len(), 64); - } -} - -mod schnorr { - use super::*; - use ic_cdk::api::management_canister::schnorr::*; - - #[update] - async fn execute_schnorr_methods() { - let key_id = SchnorrKeyId { - algorithm: SchnorrAlgorithm::Bip340secp256k1, - name: "dfx_test_key".to_string(), - }; - let derivation_path = vec![]; - let arg = SchnorrPublicKeyArgument { - canister_id: None, - derivation_path: derivation_path.clone(), - key_id: key_id.clone(), - }; - let SchnorrPublicKeyResponse { - public_key, - chain_code, - } = schnorr_public_key(arg).await.unwrap().0; - assert_eq!(public_key.len(), 33); - assert_eq!(chain_code.len(), 32); - - let message = "hello world".into(); - let arg = SignWithSchnorrArgument { - message, - derivation_path, - key_id, - }; - let SignWithSchnorrResponse { signature } = sign_with_schnorr(arg).await.unwrap().0; - assert_eq!(signature.len(), 64); - } -} - -mod bitcoin { - use super::*; - use ic_cdk::api::{call::RejectionCode, management_canister::bitcoin::*}; - - #[update] - async fn execute_bitcoin_methods() { - let address = "bcrt1qu58aj62urda83c00eylc6w34yl2s6e5rkzqet7".to_string(); - - let network = BitcoinNetwork::Regtest; - let arg = GetBalanceRequest { - address: address.clone(), - network, - min_confirmations: Some(1), - }; - let _balance = bitcoin_get_balance(arg).await.unwrap().0; - - let arg = GetUtxosRequest { - address: address.clone(), - network, - filter: Some(UtxoFilter::MinConfirmations(1)), - }; - let mut response = bitcoin_get_utxos(arg).await.unwrap().0; - - while let Some(page) = response.next_page { - ic_cdk::println!("bitcoin_get_utxos next page"); - let arg = GetUtxosRequest { - address: address.clone(), - network, - filter: Some(UtxoFilter::Page(page)), - }; - response = bitcoin_get_utxos(arg).await.unwrap().0; - } - - let arg = GetCurrentFeePercentilesRequest { network }; - let _percentiles = bitcoin_get_current_fee_percentiles(arg).await.unwrap().0; - - let arg = SendTransactionRequest { - transaction: vec![], - network, - }; - let response = bitcoin_send_transaction(arg).await; - assert!(response.is_err()); - if let Err((rejection_code, rejection_reason)) = response { - assert_eq!(rejection_code, RejectionCode::CanisterReject); - assert_eq!( - &rejection_reason, - "send_transaction failed: MalformedTransaction" - ); - }; - } -} - -ic_cdk::export_candid!(); diff --git a/examples/management_canister/tests/basic.bats b/examples/management_canister/tests/basic.bats deleted file mode 100644 index 6abf4d195..000000000 --- a/examples/management_canister/tests/basic.bats +++ /dev/null @@ -1,44 +0,0 @@ -load ../../bats/bats-assert/load.bash - -# Executed before each test. -setup() { - cd examples/management_canister -} - -# executed after each test -teardown() { - dfx stop -} - -@test "http_request example succeed" { - dfx start --clean --background # canister-http default on - dfx deploy - run dfx canister call caller http_request_example - assert_success -} - -@test "ecdsa methods succeed" { - dfx start --clean --background - dfx deploy - run dfx canister call caller execute_ecdsa_methods - assert_success -} - -@test "schnorr methods succeed" { - dfx start --clean --background - dfx deploy - run dfx canister call caller execute_schnorr_methods - assert_success -} - -@test "bitcoin methods succeed" { - bitcoind -regtest -daemonwait - - dfx start --clean --background --enable-bitcoin - - dfx deploy - run dfx canister call caller execute_bitcoin_methods - assert_success - - bitcoin-cli -regtest stop -} diff --git a/examples/print/.gitignore b/examples/print/.gitignore deleted file mode 100644 index 6cb3f2522..000000000 --- a/examples/print/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -.dfx/ -canisters/ -target/ -build/ diff --git a/examples/print/Cargo.toml b/examples/print/Cargo.toml deleted file mode 100644 index 08e5fe590..000000000 --- a/examples/print/Cargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[workspace] -members = ["src/print_rs"] -resolver = "2" - -[workspace.dependencies] -ic-cdk = { path = "../../src/ic-cdk" } -candid = "0.10" diff --git a/examples/print/dfx.json b/examples/print/dfx.json deleted file mode 100644 index 64ac19cd3..000000000 --- a/examples/print/dfx.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "version": 1, - "canisters": { - "print": { - "type": "custom", - "candid": "src/print_rs/print.did", - "wasm": "target/wasm32-unknown-unknown/release/print_rs-opt.wasm", - "build": "sh ../build.sh print print_rs" - } - } -} diff --git a/examples/print/src/print_rs/Cargo.toml b/examples/print/src/print_rs/Cargo.toml deleted file mode 100644 index 365149732..000000000 --- a/examples/print/src/print_rs/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "print_rs" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[lib] -path = "lib.rs" -crate-type = ["cdylib"] - -[dependencies] -ic-cdk.workspace = true -candid.workspace = true diff --git a/examples/print/src/print_rs/lib.rs b/examples/print/src/print_rs/lib.rs deleted file mode 100644 index aa188ba2f..000000000 --- a/examples/print/src/print_rs/lib.rs +++ /dev/null @@ -1,6 +0,0 @@ -#[ic_cdk::query] -fn print() { - ic_cdk::print("Hello World"); -} - -ic_cdk::export_candid!(); diff --git a/examples/print/src/print_rs/print.did b/examples/print/src/print_rs/print.did deleted file mode 100644 index 4bd85a4c0..000000000 --- a/examples/print/src/print_rs/print.did +++ /dev/null @@ -1,3 +0,0 @@ -service : { - "print": () -> () query; -} diff --git a/examples/print/tests/basic.bats b/examples/print/tests/basic.bats deleted file mode 100644 index aca0d2877..000000000 --- a/examples/print/tests/basic.bats +++ /dev/null @@ -1,31 +0,0 @@ -load ../../bats/bats-assert/load.bash - -# Executed before each test. -setup() { - cd examples/print - # Make sure the directory is clean. - dfx start --clean --background -} - -# executed after each test -teardown() { - dfx stop -} - -@test "Can print" { - dfx deploy - - run dfx canister call print print - assert_success -} - -@test "candid-extractor supports version and help" { - run candid-extractor --version - assert_success - run candid-extractor -V - assert_success - run candid-extractor --help - assert_success - run candid-extractor -h - assert_success -} diff --git a/examples/profile/.gitignore b/examples/profile/.gitignore deleted file mode 100644 index 47995179f..000000000 --- a/examples/profile/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -.dfx/ -canisters/ -target/ diff --git a/examples/profile/Cargo.toml b/examples/profile/Cargo.toml deleted file mode 100644 index f860ac065..000000000 --- a/examples/profile/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[workspace] -members = ["src/profile_rs", "src/profile_inter_rs"] -resolver = "2" - -[workspace.dependencies] -candid = "0.10" -ic-cdk = { path = "../../src/ic-cdk" } -ic-cdk-bindgen = { path = "../../src/ic-cdk-bindgen" } -serde = "1" diff --git a/examples/profile/dfx.json b/examples/profile/dfx.json deleted file mode 100644 index a64297603..000000000 --- a/examples/profile/dfx.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "version": 1, - "canisters": { - "profile-inter-rs": { - "type": "custom", - "candid": "src/profile_inter_rs/profile.did", - "wasm": "target/wasm32-unknown-unknown/release/profile_inter_rs-opt.wasm", - "build": "sh ../build.sh profile profile_inter_rs", - "dependencies": [ - "profile-rs" - ] - }, - "profile-rs": { - "type": "custom", - "candid": "src/profile_rs/profile.did", - "wasm": "target/wasm32-unknown-unknown/release/profile_rs-opt.wasm", - "build": "sh ../build.sh profile profile_rs" - } - } -} diff --git a/examples/profile/src/profile_inter_rs/Cargo.toml b/examples/profile/src/profile_inter_rs/Cargo.toml deleted file mode 100644 index bb44f33a3..000000000 --- a/examples/profile/src/profile_inter_rs/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "profile_inter_rs" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[lib] -path = "lib.rs" -crate-type = ["cdylib"] - -[dependencies] -candid = { workspace = true } -ic-cdk = { workspace = true } -serde = { workspace = true } - -[build-dependencies] -ic-cdk-bindgen = { workspace = true } diff --git a/examples/profile/src/profile_inter_rs/build.rs b/examples/profile/src/profile_inter_rs/build.rs deleted file mode 100644 index a2a375747..000000000 --- a/examples/profile/src/profile_inter_rs/build.rs +++ /dev/null @@ -1,13 +0,0 @@ -use ic_cdk_bindgen::{Builder, Config}; -use std::path::PathBuf; - -fn main() { - // A workaround to force always rerun build.rs - println!("cargo:rerun-if-changed=NULL"); - let manifest_dir = - PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").expect("Cannot find manifest dir")); - let profile_rs = Config::new("profile_rs"); - let mut builder = Builder::new(); - builder.add(profile_rs); - builder.build(Some(manifest_dir.join("declarations"))); -} diff --git a/examples/profile/src/profile_inter_rs/lib.rs b/examples/profile/src/profile_inter_rs/lib.rs deleted file mode 100644 index 90db9834f..000000000 --- a/examples/profile/src/profile_inter_rs/lib.rs +++ /dev/null @@ -1,26 +0,0 @@ -use ic_cdk::update; - -mod declarations; -use declarations::profile_rs::{profile_rs, Profile}; - -#[update(name = "getSelf")] -async fn get_self() -> Profile { - profile_rs.get_self().await.unwrap().0 -} - -#[update] -async fn get(name: String) -> Profile { - profile_rs.get(name).await.unwrap().0 -} - -#[update] -async fn update(profile: Profile) { - profile_rs.update(profile).await.unwrap() -} - -#[update] -async fn search(text: String) -> Option { - profile_rs.search(text).await.unwrap().0 -} - -ic_cdk::export_candid!(); diff --git a/examples/profile/src/profile_inter_rs/profile.did b/examples/profile/src/profile_inter_rs/profile.did deleted file mode 100644 index eeb02c00a..000000000 --- a/examples/profile/src/profile_inter_rs/profile.did +++ /dev/null @@ -1,12 +0,0 @@ -type Profile = record { - "name": text; - "description": text; - "keywords": vec text; -}; - -service : { - "getSelf": () -> (Profile); - "get": (text) -> (Profile); - "update": (Profile) -> (); - "search": (text) -> (opt Profile); -} diff --git a/examples/profile/src/profile_rs/Cargo.toml b/examples/profile/src/profile_rs/Cargo.toml deleted file mode 100644 index bbd1f0447..000000000 --- a/examples/profile/src/profile_rs/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "profile_rs" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[lib] -path = "lib.rs" -crate-type = ["cdylib"] - -[dependencies] -candid = { workspace = true } -ic-cdk = { workspace = true } -serde = { workspace = true } diff --git a/examples/profile/src/profile_rs/lib.rs b/examples/profile/src/profile_rs/lib.rs deleted file mode 100644 index b1cf44117..000000000 --- a/examples/profile/src/profile_rs/lib.rs +++ /dev/null @@ -1,84 +0,0 @@ -use candid::{CandidType, Deserialize, Principal}; -use ic_cdk::{api::call::ManualReply, query, update}; -use std::cell::RefCell; -use std::collections::BTreeMap; - -type IdStore = BTreeMap; -type ProfileStore = BTreeMap; - -#[derive(Clone, Debug, Default, CandidType, Deserialize)] -struct Profile { - pub name: String, - pub description: String, - pub keywords: Vec, -} - -thread_local! { - static PROFILE_STORE: RefCell = RefCell::default(); - static ID_STORE: RefCell = RefCell::default(); -} - -#[query(name = "getSelf", manual_reply = true)] -fn get_self() -> ManualReply { - let id = ic_cdk::api::caller(); - PROFILE_STORE.with(|profile_store| { - if let Some(profile) = profile_store.borrow().get(&id) { - ManualReply::one(profile) - } else { - ManualReply::one(Profile::default()) - } - }) -} - -#[query(manual_reply = true)] -fn get(name: String) -> ManualReply { - ID_STORE.with(|id_store| { - PROFILE_STORE.with(|profile_store| { - let profile_store = profile_store.borrow(); - if let Some(profile) = id_store - .borrow() - .get(&name) - .and_then(|id| profile_store.get(id)) - { - ManualReply::one(profile) - } else { - ManualReply::one(Profile::default()) - } - }) - }) -} - -#[update] -fn update(profile: Profile) { - let principal_id = ic_cdk::api::caller(); - ID_STORE.with(|id_store| { - id_store - .borrow_mut() - .insert(profile.name.clone(), principal_id); - }); - PROFILE_STORE.with(|profile_store| { - profile_store.borrow_mut().insert(principal_id, profile); - }); -} - -#[query(manual_reply = true)] -fn search(text: String) -> ManualReply> { - let text = text.to_lowercase(); - PROFILE_STORE.with(|profile_store| { - for (_, p) in profile_store.borrow().iter() { - if p.name.to_lowercase().contains(&text) || p.description.to_lowercase().contains(&text) - { - return ManualReply::one(Some(p)); - } - - for x in p.keywords.iter() { - if x.to_lowercase() == text { - return ManualReply::one(Some(p)); - } - } - } - ManualReply::one(None::) - }) -} - -ic_cdk::export_candid!(); diff --git a/examples/profile/src/profile_rs/profile.did b/examples/profile/src/profile_rs/profile.did deleted file mode 100644 index f3655f6f4..000000000 --- a/examples/profile/src/profile_rs/profile.did +++ /dev/null @@ -1,12 +0,0 @@ -type Profile = record { - "name": text; - "description": text; - "keywords": vec text; -}; - -service : { - "getSelf": () -> (Profile) query; - "get": (text) -> (Profile) query; - "update": (Profile) -> (); - "search": (text) -> (opt Profile) query; -} diff --git a/examples/profile/tests/basic.bats b/examples/profile/tests/basic.bats deleted file mode 100644 index e2edbfe22..000000000 --- a/examples/profile/tests/basic.bats +++ /dev/null @@ -1,48 +0,0 @@ -load ../../bats/bats-assert/load.bash - -# Executed before each test. -setup() { - cd examples/profile -} - -# executed after each test -teardown() { - dfx stop -} - -@test "Can get, update, search (profile_rs)" { - dfx start --clean --background - dfx deploy - - run dfx canister call profile-rs getSelf - assert_output '(record { name = ""; description = ""; keywords = vec {} })' - - run dfx canister call profile-rs update 'record {"name"= "abc"; "description"="123"; "keywords"= vec {} }' - assert_success - - run dfx canister call profile-rs get abc - assert_output '(record { name = "abc"; description = "123"; keywords = vec {} })' - - run dfx canister call profile-rs search ab - assert_output '(opt record { name = "abc"; description = "123"; keywords = vec {} })' - - run dfx canister call profile-inter-rs getSelf - assert_output '(record { name = ""; description = ""; keywords = vec {} })' - - run dfx canister call profile-inter-rs update 'record {"name"= "def"; "description"="456"; "keywords"= vec {} }' - assert_success - - run dfx canister call profile-inter-rs get def - assert_output '(record { name = "def"; description = "456"; keywords = vec {} })' - - run dfx canister call profile-inter-rs search de - assert_output '(opt record { name = "def"; description = "456"; keywords = vec {} })' -} - -@test "ic-cdk-bindgen warns about deprecated env vars when running with dfx v0.13.1" { - dfxvm install 0.13.1 - run dfx +0.13.1 build --check - assert_success - assert_regex "$output" "The environment variable CANISTER_CANDID_PATH_profile_rs is deprecated. Please set CANISTER_CANDID_PATH_PROFILE_RS instead. Upgrading dfx may fix this issue." - assert_regex "$output" "The environment variable CANISTER_ID_profile_rs is deprecated. Please set CANISTER_ID_PROFILE_RS instead. Upgrading dfx may fix this issue." -} \ No newline at end of file diff --git a/examples/rust-toolchain.toml b/examples/rust-toolchain.toml deleted file mode 100644 index 8fbec9af3..000000000 --- a/examples/rust-toolchain.toml +++ /dev/null @@ -1,4 +0,0 @@ -[toolchain] -channel = "stable" # intentionally not pinned to a specific version -targets = ["wasm32-unknown-unknown"] -components = ["rustfmt", "clippy"] diff --git a/src/ic-cdk-bindgen/CHANGELOG.md b/ic-cdk-bindgen/CHANGELOG.md similarity index 100% rename from src/ic-cdk-bindgen/CHANGELOG.md rename to ic-cdk-bindgen/CHANGELOG.md diff --git a/src/ic-cdk-bindgen/Cargo.toml b/ic-cdk-bindgen/Cargo.toml similarity index 100% rename from src/ic-cdk-bindgen/Cargo.toml rename to ic-cdk-bindgen/Cargo.toml diff --git a/ic-cdk-bindgen/LICENSE b/ic-cdk-bindgen/LICENSE new file mode 120000 index 000000000..ea5b60640 --- /dev/null +++ b/ic-cdk-bindgen/LICENSE @@ -0,0 +1 @@ +../LICENSE \ No newline at end of file diff --git a/src/ic-cdk-bindgen/README.md b/ic-cdk-bindgen/README.md similarity index 100% rename from src/ic-cdk-bindgen/README.md rename to ic-cdk-bindgen/README.md diff --git a/src/ic-cdk-bindgen/src/code_generator.rs b/ic-cdk-bindgen/src/code_generator.rs similarity index 100% rename from src/ic-cdk-bindgen/src/code_generator.rs rename to ic-cdk-bindgen/src/code_generator.rs diff --git a/src/ic-cdk-bindgen/src/lib.rs b/ic-cdk-bindgen/src/lib.rs similarity index 100% rename from src/ic-cdk-bindgen/src/lib.rs rename to ic-cdk-bindgen/src/lib.rs diff --git a/src/ic-cdk-macros/CHANGELOG.md b/ic-cdk-macros/CHANGELOG.md similarity index 100% rename from src/ic-cdk-macros/CHANGELOG.md rename to ic-cdk-macros/CHANGELOG.md diff --git a/src/ic-cdk-macros/Cargo.toml b/ic-cdk-macros/Cargo.toml similarity index 94% rename from src/ic-cdk-macros/Cargo.toml rename to ic-cdk-macros/Cargo.toml index b164000ac..1677ae8c5 100644 --- a/src/ic-cdk-macros/Cargo.toml +++ b/ic-cdk-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ic-cdk-macros" -version = "0.17.1" # sync with ic-cdk +version = "0.18.0-alpha.1" # sync with ic-cdk authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ic-cdk-macros/LICENSE b/ic-cdk-macros/LICENSE new file mode 120000 index 000000000..ea5b60640 --- /dev/null +++ b/ic-cdk-macros/LICENSE @@ -0,0 +1 @@ +../LICENSE \ No newline at end of file diff --git a/src/ic-cdk-macros/README.md b/ic-cdk-macros/README.md similarity index 100% rename from src/ic-cdk-macros/README.md rename to ic-cdk-macros/README.md diff --git a/src/ic-cdk-macros/src/export.rs b/ic-cdk-macros/src/export.rs similarity index 76% rename from src/ic-cdk-macros/src/export.rs rename to ic-cdk-macros/src/export.rs index 320c94d77..2b80cf847 100644 --- a/src/ic-cdk-macros/src/export.rs +++ b/ic-cdk-macros/src/export.rs @@ -16,12 +16,6 @@ struct ExportAttributes { pub composite: bool, #[serde(default)] pub hidden: bool, - #[serde(default)] - pub decoding_quota: Option, - #[serde(default = "default_skipping_quota")] - pub skipping_quota: Option, - #[serde(default)] - pub debug: bool, } #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -65,10 +59,6 @@ impl std::fmt::Display for MethodType { } } -fn default_skipping_quota() -> Option { - Some(10_000) -} - fn get_args(method: MethodType, signature: &Signature) -> Result)>, Error> { // We only need the tuple of arguments, not their types. Magic of type inference. let mut args = vec![]; @@ -84,14 +74,17 @@ fn get_args(method: MethodType, signature: &Signature) -> Result { - if let Pat::Ident(PatIdent { ident, .. }) = pat.as_ref() { - (ident.clone(), ty.clone()) + let ident = if let Pat::Ident(PatIdent { ident, .. }) = pat.as_ref() { + // If the argument is named the same as the function, we need to rename it. + if ident == &signature.ident { + format_ident!("__arg_{}", ident, span = pat.span()) + } else { + ident.clone() + } } else { - ( - format_ident!("__unnamed_arg_{i}", span = pat.span()), - ty.clone(), - ) - } + format_ident!("__unnamed_arg_{i}", span = pat.span()) + }; + (ident, ty.clone()) } }; @@ -178,10 +171,13 @@ fn dfn_macro( let return_encode = if method.is_lifecycle() || attrs.manual_reply { quote! {} } else { - match return_length { - 0 => quote! { ic_cdk::api::call::reply(()) }, - 1 => quote! { ic_cdk::api::call::reply((result,)) }, - _ => quote! { ic_cdk::api::call::reply(result) }, + let return_bytes = match return_length { + 0 => quote! { ::candid::utils::encode_one(()).unwrap() }, + 1 => quote! { ::candid::utils::encode_one(result).unwrap() }, + _ => quote! { ::candid::utils::encode_args(result).unwrap() }, + }; + quote! { + ::ic_cdk::api::msg_reply(#return_bytes); } }; @@ -191,29 +187,9 @@ fn dfn_macro( let arg_decode = if method.is_lifecycle() && arg_count == 0 { quote! {} } else { - let decoding_quota = if let Some(n) = attrs.decoding_quota { - quote! { Some(#n) } - } else { - quote! { None } - }; - let skipping_quota = if let Some(n) = attrs.skipping_quota { - quote! { Some(#n) } - } else { - quote! { None } - }; - let debug = if attrs.debug { - quote! { true } - } else { - quote! { false } - }; - let config = quote! { - ic_cdk::api::call::ArgDecoderConfig { - decoding_quota: #decoding_quota, - skipping_quota: #skipping_quota, - debug: #debug, - } - }; - quote! { let ( #( #arg_tuple, )* ) = ic_cdk::api::call::arg_data(#config); } + quote! { + let arg_bytes = ::ic_cdk::api::msg_arg_data(); + let ( #( #arg_tuple, )* ) = ::candid::utils::decode_args(&arg_bytes).unwrap(); } }; let guard = if let Some(guard_name) = attrs.guard { @@ -229,7 +205,7 @@ fn dfn_macro( quote! { let r: Result<(), String> = #guard_ident (); if let Err(e) = r { - ic_cdk::api::call::reject(&e); + ::ic_cdk::api::msg_reject(&e); return; } } @@ -263,11 +239,11 @@ fn dfn_macro( #[cfg_attr(target_family = "wasm", export_name = #export_name)] #[cfg_attr(not(target_family = "wasm"), export_name = #host_compatible_name)] fn #outer_function_ident() { - ic_cdk::setup(); + ::ic_cdk::setup(); #guard - ic_cdk::spawn(async { + ::ic_cdk::spawn(async { #arg_decode let result = #function_call; #return_encode @@ -342,17 +318,12 @@ mod test { #[cfg_attr(target_family = "wasm", export_name = "canister_query query")] #[cfg_attr(not(target_family = "wasm"), export_name = "canister_query.query")] fn #fn_name() { - ic_cdk::setup(); - ic_cdk::spawn(async { - let () = ic_cdk::api::call::arg_data( - ic_cdk::api::call::ArgDecoderConfig { - decoding_quota: None, - skipping_quota: Some(10000usize), - debug: false, - } - ); + ::ic_cdk::setup(); + ::ic_cdk::spawn(async { + let arg_bytes = ::ic_cdk::api::msg_arg_data(); + let () = ::candid::utils::decode_args(&arg_bytes).unwrap(); let result = query(); - ic_cdk::api::call::reply(()) + ::ic_cdk::api::msg_reply(::candid::utils::encode_one(()).unwrap()); }); } }; @@ -366,6 +337,7 @@ mod test { _ => panic!("not a function"), }; } + #[test] fn ic_query_return_one_value() { let generated = ic_query( @@ -385,17 +357,12 @@ mod test { #[cfg_attr(target_family = "wasm", export_name = "canister_query query")] #[cfg_attr(not(target_family = "wasm"), export_name = "canister_query.query")] fn #fn_name() { - ic_cdk::setup(); - ic_cdk::spawn(async { - let () = ic_cdk::api::call::arg_data( - ic_cdk::api::call::ArgDecoderConfig { - decoding_quota: None, - skipping_quota: Some(10000usize), - debug: false, - } - ); + ::ic_cdk::setup(); + ::ic_cdk::spawn(async { + let arg_bytes = ::ic_cdk::api::msg_arg_data(); + let () = ::candid::utils::decode_args(&arg_bytes).unwrap(); let result = query(); - ic_cdk::api::call::reply((result,)) + ::ic_cdk::api::msg_reply(::candid::utils::encode_one(result).unwrap()); }); } }; @@ -429,17 +396,12 @@ mod test { #[cfg_attr(target_family = "wasm", export_name = "canister_query query")] #[cfg_attr(not(target_family = "wasm"), export_name = "canister_query.query")] fn #fn_name() { - ic_cdk::setup(); - ic_cdk::spawn(async { - let () = ic_cdk::api::call::arg_data( - ic_cdk::api::call::ArgDecoderConfig { - decoding_quota: None, - skipping_quota: Some(10000usize), - debug: false, - } - ); + ::ic_cdk::setup(); + ::ic_cdk::spawn(async { + let arg_bytes = ::ic_cdk::api::msg_arg_data(); + let () = ::candid::utils::decode_args(&arg_bytes).unwrap(); let result = query(); - ic_cdk::api::call::reply(result) + ::ic_cdk::api::msg_reply(::candid::utils::encode_args(result).unwrap()); }); } }; @@ -473,17 +435,12 @@ mod test { #[cfg_attr(target_family = "wasm", export_name = "canister_query query")] #[cfg_attr(not(target_family = "wasm"), export_name = "canister_query.query")] fn #fn_name() { - ic_cdk::setup(); - ic_cdk::spawn(async { - let (a, ) = ic_cdk::api::call::arg_data( - ic_cdk::api::call::ArgDecoderConfig { - decoding_quota: None, - skipping_quota: Some(10000usize), - debug: false, - } - ); + ::ic_cdk::setup(); + ::ic_cdk::spawn(async { + let arg_bytes = ::ic_cdk::api::msg_arg_data(); + let (a,) = ::candid::utils::decode_args(&arg_bytes).unwrap(); let result = query(a); - ic_cdk::api::call::reply(()) + ::ic_cdk::api::msg_reply(::candid::utils::encode_one(()).unwrap()); }); } }; @@ -517,17 +474,12 @@ mod test { #[cfg_attr(target_family = "wasm", export_name = "canister_query query")] #[cfg_attr(not(target_family = "wasm"), export_name = "canister_query.query")] fn #fn_name() { - ic_cdk::setup(); - ic_cdk::spawn(async { - let (a, b, ) = ic_cdk::api::call::arg_data( - ic_cdk::api::call::ArgDecoderConfig { - decoding_quota: None, - skipping_quota: Some(10000usize), - debug: false, - } - ); + ::ic_cdk::setup(); + ::ic_cdk::spawn(async { + let arg_bytes = ::ic_cdk::api::msg_arg_data(); + let (a, b,) = ::candid::utils::decode_args(&arg_bytes).unwrap(); let result = query(a, b); - ic_cdk::api::call::reply(()) + ::ic_cdk::api::msg_reply(::candid::utils::encode_one(()).unwrap()); }); } }; @@ -556,22 +508,16 @@ mod test { syn::Item::Fn(ref f) => &f.sig.ident, _ => panic!("Incorrect parsed AST."), }; - let expected = quote! { #[cfg_attr(target_family = "wasm", export_name = "canister_query query")] #[cfg_attr(not(target_family = "wasm"), export_name = "canister_query.query")] fn #fn_name() { - ic_cdk::setup(); - ic_cdk::spawn(async { - let (a, b, ) = ic_cdk::api::call::arg_data( - ic_cdk::api::call::ArgDecoderConfig { - decoding_quota: None, - skipping_quota: Some(10000usize), - debug: false, - } - ); + ::ic_cdk::setup(); + ::ic_cdk::spawn(async { + let arg_bytes = ::ic_cdk::api::msg_arg_data(); + let (a, b,) = ::candid::utils::decode_args(&arg_bytes).unwrap(); let result = query(a, b); - ic_cdk::api::call::reply((result,)) + ::ic_cdk::api::msg_reply(::candid::utils::encode_one(result).unwrap()); }); } }; @@ -605,17 +551,12 @@ mod test { #[cfg_attr(target_family = "wasm", export_name = "canister_query custom_query")] #[cfg_attr(not(target_family = "wasm"), export_name = "canister_query.custom_query")] fn #fn_name() { - ic_cdk::setup(); - ic_cdk::spawn(async { - let () = ic_cdk::api::call::arg_data( - ic_cdk::api::call::ArgDecoderConfig { - decoding_quota: None, - skipping_quota: Some(10000usize), - debug: false, - } - ); + ::ic_cdk::setup(); + ::ic_cdk::spawn(async { + let arg_bytes = ::ic_cdk::api::msg_arg_data(); + let () = ::candid::utils::decode_args(&arg_bytes).unwrap(); let result = query(); - ic_cdk::api::call::reply(()) + ::ic_cdk::api::msg_reply(::candid::utils::encode_one(()).unwrap()); }); } }; diff --git a/src/ic-cdk-macros/src/lib.rs b/ic-cdk-macros/src/lib.rs similarity index 100% rename from src/ic-cdk-macros/src/lib.rs rename to ic-cdk-macros/src/lib.rs diff --git a/src/ic-cdk-timers/CHANGELOG.md b/ic-cdk-timers/CHANGELOG.md similarity index 100% rename from src/ic-cdk-timers/CHANGELOG.md rename to ic-cdk-timers/CHANGELOG.md diff --git a/src/ic-cdk-timers/Cargo.toml b/ic-cdk-timers/Cargo.toml similarity index 94% rename from src/ic-cdk-timers/Cargo.toml rename to ic-cdk-timers/Cargo.toml index 3ebb7788f..38a27b7cc 100644 --- a/src/ic-cdk-timers/Cargo.toml +++ b/ic-cdk-timers/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ic-cdk-timers" -version = "0.11.0" +version = "0.12.0-alpha.1" authors.workspace = true edition.workspace = true license.workspace = true @@ -20,6 +20,7 @@ keywords = ["internet-computer", "dfinity", "canister", "cdk"] include = ["src", "Cargo.toml", "LICENSE", "README.md"] [dependencies] +candid.workspace = true ic0.workspace = true ic-cdk.workspace = true serde.workspace = true diff --git a/ic-cdk-timers/LICENSE b/ic-cdk-timers/LICENSE new file mode 120000 index 000000000..ea5b60640 --- /dev/null +++ b/ic-cdk-timers/LICENSE @@ -0,0 +1 @@ +../LICENSE \ No newline at end of file diff --git a/src/ic-cdk-timers/README.md b/ic-cdk-timers/README.md similarity index 100% rename from src/ic-cdk-timers/README.md rename to ic-cdk-timers/README.md diff --git a/src/ic-cdk-timers/src/lib.rs b/ic-cdk-timers/src/lib.rs similarity index 84% rename from src/ic-cdk-timers/src/lib.rs rename to ic-cdk-timers/src/lib.rs index b025f9f6a..064f828ca 100644 --- a/src/ic-cdk-timers/src/lib.rs +++ b/ic-cdk-timers/src/lib.rs @@ -29,7 +29,7 @@ use std::{ use futures::{stream::FuturesUnordered, StreamExt}; use slotmap::{new_key_type, KeyData, SlotMap}; -use ic_cdk::api::call::RejectionCode; +use ic_cdk::call::{Call, CallError, CallPerformErrorCode, RejectCode, SendableCall}; // To ensure that tasks are removable seamlessly, there are two separate concepts here: tasks, for the actual function being called, // and timers, the scheduled execution of tasks. As this is an implementation detail, this does not affect the exported name TimerId, @@ -113,11 +113,12 @@ extern "C" fn global_timer() { call_futures.push(async move { ( timer, - ic_cdk::call( - ic_cdk::api::id(), + Call::new( + ic_cdk::api::canister_self(), " timer_executor", - (task_id.0.as_ffi(),), ) + .with_arg(task_id.0.as_ffi()) + .call::<()>() .await, ) }); @@ -131,25 +132,30 @@ extern "C" fn global_timer() { // run all the collected tasks, and clean up after them if necessary while let Some((timer, res)) = call_futures.next().await { let task_id = timer.task; - match res { - Ok(()) => {} - Err((code, msg)) => { - ic_cdk::println!("in canister_global_timer: {code:?}: {msg}"); - match code { - RejectionCode::SysTransient => { - // Try to execute the timer again later. - TIMERS.with(|timers| { - timers.borrow_mut().push(timer); - }); - continue; + if let Err(e) = res { + ic_cdk::println!("[ic-cdk-timers] canister_global_timer: {e:?}"); + let mut retry_later = false; + match e { + CallError::CallRejected(reject_code, _) => { + if reject_code == RejectCode::SysTransient { + retry_later = true; + } + } + CallError::CallPerformFailed(call_perform_error_code) => { + if call_perform_error_code == CallPerformErrorCode::SysTransient { + retry_later = true; } - RejectionCode::NoError - | RejectionCode::SysFatal - | RejectionCode::DestinationInvalid - | RejectionCode::CanisterReject - | RejectionCode::CanisterError - | RejectionCode::Unknown => {} } + CallError::CandidEncodeFailed(_) | CallError::CandidDecodeFailed(_) => { + // These errors are not transient, and will not be retried. + } + } + if retry_later { + // Try to execute the timer again later. + TIMERS.with(|timers| { + timers.borrow_mut().push(timer); + }); + continue; } } TASKS.with(|tasks| { @@ -254,7 +260,7 @@ fn update_ic0_timer() { }; if should_change { // SAFETY: ic0::global_timer_set is always a safe call - unsafe { ic0::global_timer_set(soonest_timer.unwrap() as i64) }; + unsafe { ic0::global_timer_set(soonest_timer.unwrap()) }; MOST_RECENT.with(|recent| recent.set(soonest_timer)); } }); @@ -269,15 +275,16 @@ fn update_ic0_timer() { export_name = "canister_update_ic_cdk_internal.timer_executor" )] extern "C" fn timer_executor() { - if ic_cdk::api::caller() != ic_cdk::api::id() { + use candid::utils::{decode_one, encode_one}; + if ic_cdk::api::msg_caller() != ic_cdk::api::canister_self() { ic_cdk::trap("This function is internal to ic-cdk and should not be called externally."); } - let config = ic_cdk::api::call::ArgDecoderConfig { - decoding_quota: Some(10_000), - skipping_quota: Some(100), - debug: false, - }; - let (task_id,) = ic_cdk::api::call::arg_data(config); + let arg_bytes = ic_cdk::api::msg_arg_data(); + // timer_executor is only called by the canister itself (from global_timer), + // so we can safely assume that the argument is a valid TimerId (u64). + // And we don't need decode_one_with_config/DecoderConfig to defense against malicious payload. + let task_id: u64 = decode_one(&arg_bytes).unwrap(); + let task_id = TimerId(KeyData::from_ffi(task_id)); // We can't be holding `TASKS` when we call the function, because it may want to schedule more tasks. // Instead, we swap the task out in order to call it, and then either swap it back in, or remove it. @@ -297,5 +304,5 @@ extern "C" fn timer_executor() { } } } - ic_cdk::api::call::reply(()); + ic_cdk::api::msg_reply(encode_one(()).unwrap()); } diff --git a/src/ic-cdk/CHANGELOG.md b/ic-cdk/CHANGELOG.md similarity index 99% rename from src/ic-cdk/CHANGELOG.md rename to ic-cdk/CHANGELOG.md index 3e0525b3a..3723d2f49 100644 --- a/src/ic-cdk/CHANGELOG.md +++ b/ic-cdk/CHANGELOG.md @@ -14,6 +14,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Support management canister method: `subnet_info`. (#532) - Add types: `SubnetInfoArgs` and `SubnetInfoResult`. +### Fixed + +- Fix update/query macro could not handle function arguments with the same name as the function itself. (#525) + ## [0.17.0] - 2024-11-04 ### Changed diff --git a/src/ic-cdk/Cargo.toml b/ic-cdk/Cargo.toml similarity index 89% rename from src/ic-cdk/Cargo.toml rename to ic-cdk/Cargo.toml index d933df6b9..2ba2cef3c 100644 --- a/src/ic-cdk/Cargo.toml +++ b/ic-cdk/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ic-cdk" -version = "0.17.1" # sync with ic-cdk-macros +version = "0.18.0-alpha.1" # sync with ic-cdk-macros authors.workspace = true edition.workspace = true license.workspace = true @@ -27,10 +27,11 @@ ic0.workspace = true # Dependents won't accidentaly upgrading ic-cdk-macros only but not ic-cdk. # ic-cdk-macros is a hidden dependency, re-exported by ic-cdk. # It should not be included by users direcly. -ic-cdk-macros = { path = "../ic-cdk-macros", version = "=0.17.1" } +ic-cdk-macros = { path = "../ic-cdk-macros", version = "=0.18.0-alpha.1" } serde.workspace = true serde_bytes.workspace = true slotmap = { workspace = true, optional = true } +thiserror = "2.0" [dev-dependencies] anyhow = "1" diff --git a/ic-cdk/LICENSE b/ic-cdk/LICENSE new file mode 120000 index 000000000..ea5b60640 --- /dev/null +++ b/ic-cdk/LICENSE @@ -0,0 +1 @@ +../LICENSE \ No newline at end of file diff --git a/src/ic-cdk/README.md b/ic-cdk/README.md similarity index 100% rename from src/ic-cdk/README.md rename to ic-cdk/README.md diff --git a/ic-cdk/src/api.rs b/ic-cdk/src/api.rs new file mode 100644 index 000000000..961452c41 --- /dev/null +++ b/ic-cdk/src/api.rs @@ -0,0 +1,583 @@ +//! System API bindings. +//! +//! This module provides Rust ergonomic bindings to the system APIs. +//! +//! Some APIs require more advanced handling and are organized into separate modules: +//! * For the inter-canister calls API, see the [`call`](mod@crate::call) module. +//! * For the stable memory management API, see the . +//! * The basic bindings are provided in this module including [`stable_size`], [`stable_grow`], [`stable_read`] and [`stable_write`]. +//! * The [`stable`](crate::stable) module provides more advanced functionalities, e.g. support for `std::io` traits. +//! +//! APIs that are only available for `wasm32` are not included. +//! As a result, system APIs with a numeric postfix (indicating the data bit width) are bound to names without the postfix. +//! For example, `ic0::msg_cycles_available128` is bound to [`msg_cycles_available`], while `ic0::msg_cycles_available` has no binding. +//! +//! Functions that provide bindings for a single system API method share the same name as the system API. +//! For example, `ic0::msg_reject_code` is bound to [`msg_reject_code`]. +//! +//! Functions that wrap multiple system API methods are named using the common prefix of the wrapped methods. +//! For example, [`msg_arg_data`] wraps both `ic0::msg_arg_data_size` and `ic0::msg_arg_data_copy`. + +use candid::Principal; +use std::convert::TryFrom; + +pub mod call; +pub mod management_canister; +#[doc(hidden)] +#[deprecated( + since = "0.18.0", + note = "The `api::stable` module has been moved to `stable` (crate root)." +)] +pub mod stable; + +/// Gets the message argument data. +pub fn msg_arg_data() -> Vec { + // SAFETY: ic0.msg_arg_data_size is always safe to call. + let len = unsafe { ic0::msg_arg_data_size() }; + let mut bytes = Vec::with_capacity(len); + // SAFETY: + // `bytes`, being mutable and allocated to `len` bytes, is safe to pass to ic0.msg_arg_data_copy with no offset + // ic0.msg_arg_data_copy writes to all of `bytes[0..len]`, so `set_len` is safe to call with the new len. + unsafe { + ic0::msg_arg_data_copy(bytes.as_mut_ptr() as usize, 0, len); + bytes.set_len(len); + } + bytes +} + +/// Gets the identity of the caller, which may be a canister id or a user id. +/// +/// During canister installation or upgrade, this is the id of the user or canister requesting the installation or upgrade. +/// During a system task (heartbeat or global timer), this is the id of the management canister. +pub fn msg_caller() -> Principal { + // SAFETY: ic0.msg_caller_size is always safe to call. + let len = unsafe { ic0::msg_caller_size() }; + let mut bytes = vec![0u8; len]; + // SAFETY: Because `bytes` is mutable, and allocated to `len` bytes, it is safe to be passed to `ic0.msg_caller_copy` with a 0-offset. + unsafe { + ic0::msg_caller_copy(bytes.as_mut_ptr() as usize, 0, len); + } + // Trust that the system always returns a valid principal. + Principal::try_from(&bytes).unwrap() +} + +/// Returns the reject code, if the current function is invoked as a reject callback. +pub fn msg_reject_code() -> u32 { + // SAFETY: ic0.msg_reject_code is always safe to call. + unsafe { ic0::msg_reject_code() } +} + +/// Gets the reject message. +/// +/// This function can only be called in the reject callback. +/// +/// Traps if there is no reject message (i.e. if reject_code is 0). +pub fn msg_reject_msg() -> String { + // SAFETY: ic0.msg_reject_msg_size is always safe to call. + let len = unsafe { ic0::msg_reject_msg_size() }; + let mut bytes = vec![0u8; len]; + // SAFETY: `bytes`, being mutable and allocated to `len` bytes, is safe to pass to ic0.msg_reject_msg_copy with no offset + unsafe { + ic0::msg_reject_msg_copy(bytes.as_mut_ptr() as usize, 0, len); + } + String::from_utf8_lossy(&bytes).into_owned() +} + +/// Replies to the sender with the data. +pub fn msg_reply>(data: T) { + let buf = data.as_ref(); + if !buf.is_empty() { + // SAFETY: `buf`, being &[u8], is a readable sequence of bytes, and therefore valid to pass to ic0.msg_reply. + unsafe { ic0::msg_reply_data_append(buf.as_ptr() as usize, buf.len()) } + }; + // SAFETY: ic0.msg_reply is always safe to call. + unsafe { ic0::msg_reply() }; +} + +/// Rejects the call with a diagnostic message. +pub fn msg_reject>(message: T) { + let buf = message.as_ref(); + // SAFETY: `buf`, being &str, is a readable sequence of UTF8 bytes and therefore can be passed to ic0.msg_reject. + unsafe { + ic0::msg_reject(buf.as_ptr() as usize, buf.len()); + } +} + +/// Gets the number of cycles transferred by the caller of the current call, still available in this message. +pub fn msg_cycles_available() -> u128 { + let mut recv = 0u128; + // SAFETY: recv is writable and sixteen bytes wide, and therefore is safe to pass to ic0.msg_cycles_available128 + unsafe { + ic0::msg_cycles_available128(&mut recv as *mut u128 as usize); + } + recv +} + +/// Gets the amount of cycles that came back with the response as a refund +/// +/// This function can only be used in a callback handler (reply or reject). +/// The refund has already been added to the canister balance automatically. +pub fn msg_cycles_refunded() -> u128 { + let mut recv = 0u128; + // SAFETY: recv is writable and sixteen bytes wide, and therefore is safe to pass to ic0.msg_cycles_refunded128 + unsafe { + ic0::msg_cycles_refunded128(&mut recv as *mut u128 as usize); + } + recv +} + +/// Moves cycles from the call to the canister balance. +/// +/// The actual amount moved will be returned. +pub fn msg_cycles_accept(max_amount: u128) -> u128 { + let high = (max_amount >> 64) as u64; + let low = (max_amount & u64::MAX as u128) as u64; + let mut recv = 0u128; + // SAFETY: `recv` is writable and sixteen bytes wide, and therefore safe to pass to ic0.msg_cycles_accept128 + unsafe { + ic0::msg_cycles_accept128(high, low, &mut recv as *mut u128 as usize); + } + recv +} + +/// Burns cycles from the canister. +/// +/// Returns the amount of cycles that were actually burned. +pub fn cycles_burn(amount: u128) -> u128 { + let amount_high = (amount >> 64) as u64; + let amount_low = (amount & u64::MAX as u128) as u64; + let mut dst = 0u128; + // SAFETY: `dst` is writable and sixteen bytes wide, and therefore safe to pass to ic0.cycles_burn128 + unsafe { ic0::cycles_burn128(amount_high, amount_low, &mut dst as *mut u128 as usize) } + dst +} + +/// Gets canister's own identity. +pub fn canister_self() -> Principal { + // SAFETY: ic0.canister_self_size is always safe to call. + let len = unsafe { ic0::canister_self_size() }; + let mut bytes = vec![0u8; len]; + // SAFETY: Because `bytes` is mutable, and allocated to `len` bytes, it is safe to be passed to `ic0.canister_self_copy` with a 0-offset. + unsafe { + ic0::canister_self_copy(bytes.as_mut_ptr() as usize, 0, len); + } + // Trust that the system always returns a valid principal. + Principal::try_from(&bytes).unwrap() +} + +/// Gets the current cycle balance of the canister +pub fn canister_cycle_balance() -> u128 { + let mut recv = 0u128; + // SAFETY: recv is writable and the size expected by ic0.canister_cycle_balance128. + unsafe { ic0::canister_cycle_balance128(&mut recv as *mut u128 as usize) } + recv +} + +/// Gets the status of the canister. +/// +/// The status is one of the following: +/// * 1: Running +/// * 2: Stopping +/// * 3: Stopped +pub fn canister_status() -> CanisterStatusCode { + // SAFETY: ic0.canister_status is always safe to call. + unsafe { ic0::canister_status() }.into() +} + +/// The status of a canister. +/// +/// See [Canister status](https://internetcomputer.org/docs/current/references/ic-interface-spec/#system-api-canister-status). +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[repr(u32)] +pub enum CanisterStatusCode { + /// Running. + Running = 1, + /// Stopping. + Stopping = 2, + /// Stopped. + Stopped = 3, + /// A status code that is not recognized by this library. + Unrecognized(u32), +} + +impl From for CanisterStatusCode { + fn from(value: u32) -> Self { + match value { + 1 => Self::Running, + 2 => Self::Stopping, + 3 => Self::Stopped, + _ => Self::Unrecognized(value), + } + } +} + +impl From for u32 { + fn from(value: CanisterStatusCode) -> Self { + match value { + CanisterStatusCode::Running => 1, + CanisterStatusCode::Stopping => 2, + CanisterStatusCode::Stopped => 3, + CanisterStatusCode::Unrecognized(value) => value, + } + } +} + +impl PartialEq for CanisterStatusCode { + fn eq(&self, other: &u32) -> bool { + let self_as_u32: u32 = (*self).into(); + self_as_u32 == *other + } +} + +/// Gets the canister version. +/// +/// See [Canister version](https://internetcomputer.org/docs/current/references/ic-interface-spec/#system-api-canister-version). +pub fn canister_version() -> u64 { + // SAFETY: ic0.canister_version is always safe to call. + unsafe { ic0::canister_version() } +} + +/// Gets the name of the method to be inspected. +/// +/// This function is only available in the `canister_inspect_message` context. +pub fn msg_method_name() -> String { + // SAFETY: ic0.msg_method_name_size is always safe to call. + let len: u32 = unsafe { ic0::msg_method_name_size() as u32 }; + let mut bytes = vec![0u8; len as usize]; + // SAFETY: `bytes` is writable and allocated to `len` bytes, and therefore can be safely passed to ic0.msg_method_name_copy + unsafe { + ic0::msg_method_name_copy(bytes.as_mut_ptr() as usize, 0, len as usize); + } + String::from_utf8_lossy(&bytes).into_owned() +} + +/// Accepts the message in `canister_inspect_message`. +/// +/// This function is only available in the `canister_inspect_message` context. +/// This function traps if invoked twice. +pub fn accept_message() { + // SAFETY: ic0.accept_message is always safe to call. + unsafe { ic0::accept_message() } +} + +/// Gets the current size of the stable memory (in WebAssembly pages). +/// +/// One WebAssembly page is 64KiB. +pub fn stable_size() -> u64 { + // SAFETY: ic0.stable64_size is always safe to call. + unsafe { ic0::stable64_size() } +} + +/// Attempts to grow the stable memory by `new_pages` many pages containing zeroes. +/// +/// One WebAssembly page is 64KiB. +/// +/// If successful, returns the previous size of the memory (in pages). +/// Otherwise, returns `u64::MAX`. +pub fn stable_grow(new_pages: u64) -> u64 { + // SAFETY: ic0.stable64_grow is always safe to call. + unsafe { ic0::stable64_grow(new_pages) } +} + +/// Writes data to the stable memory location specified by an offset. +/// +/// # Warning +/// This will panic if `offset + buf.len()` exceeds the current size of stable memory. +/// Call [`stable_grow`] to request more stable memory if needed. +pub fn stable_write(offset: u64, buf: &[u8]) { + // SAFETY: `buf`, being &[u8], is a readable sequence of bytes, and therefore valid to pass to ic0.stable64_write. + unsafe { + ic0::stable64_write(offset, buf.as_ptr() as u64, buf.len() as u64); + } +} + +/// Reads data from the stable memory location specified by an offset. +/// +/// # Warning +/// This will panic if `offset + buf.len()` exceeds the current size of stable memory. +pub fn stable_read(offset: u64, buf: &mut [u8]) { + // SAFETY: `buf`, being &mut [u8], is a writable sequence of bytes, and therefore valid to pass to ic0.stable64_read. + unsafe { + ic0::stable64_read(buf.as_ptr() as u64, offset, buf.len() as u64); + } +} + +/// Sets the certified data of this canister. +/// +/// Canisters can store up to 32 bytes of data that is certified by +/// the system on a regular basis. One can call [data_certificate] +/// function from a query call to get a certificate authenticating the +/// value set by calling this function. +/// +/// This function can only be called from the following contexts: +/// * "canister_init", "canister_pre_upgrade" and "canister_post_upgrade" +/// hooks. +/// * "canister_update" calls. +/// * reply or reject callbacks. +/// +/// # Panics +/// +/// * This function traps if data.len() > 32. +/// * This function traps if it's called from an illegal context +/// (e.g., from a query call). +pub fn certified_data_set>(data: T) { + let buf = data.as_ref(); + // SAFETY: uf is a slice ref, its pointer and length are valid to pass to ic0.certified_data_set. + unsafe { ic0::certified_data_set(buf.as_ptr() as usize, buf.len()) } +} + +/// When called from a query call, returns the data certificate authenticating +/// certified_data set by this canister. +/// +/// Returns `None` if called not from a query call. +pub fn data_certificate() -> Option> { + // SAFETY: ic0.data_certificate_present is always safe to call. + if unsafe { ic0::data_certificate_present() } == 0 { + return None; + } + // SAFETY: ic0.data_certificate_size is always safe to call. + let n = unsafe { ic0::data_certificate_size() }; + let mut buf = vec![0u8; n]; + // SAFETY: Because `buf` is mutable and allocated to `n` bytes, it is valid to receive from ic0.data_certificate_bytes with no offset + unsafe { + ic0::data_certificate_copy(buf.as_mut_ptr() as usize, 0, n); + } + Some(buf) +} + +/// Gets current timestamp, in nanoseconds since the epoch (1970-01-01) +pub fn time() -> u64 { + // SAFETY: ic0.time is always safe to call. + unsafe { ic0::time() } +} + +/// Sets global timer. +/// +/// The canister can set a global timer to make the system +/// schedule a call to the exported `canister_global_timer` +/// Wasm method after the specified time. +/// The time must be provided as nanoseconds since 1970-01-01. +/// +/// The function returns the previous value of the timer. +/// If no timer is set before invoking the function, then the function returns zero. +/// +/// Passing zero as an argument to the function deactivates the timer and thus +/// prevents the system from scheduling calls to the canister's `canister_global_timer` Wasm method. +pub fn global_timer_set(timestamp: u64) -> u64 { + // SAFETY: ic0.global_timer_set is always safe to call. + unsafe { ic0::global_timer_set(timestamp) } +} + +/// Gets the value of specified performance counter. +/// +/// See [`PerformanceCounterType`] for available counter types. +#[inline] +pub fn performance_counter(counter_type: impl Into) -> u64 { + let counter_type: u32 = counter_type.into().into(); + // SAFETY: ic0.performance_counter is always safe to call. + unsafe { ic0::performance_counter(counter_type) } +} + +/// The type of performance counter. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[repr(u32)] +pub enum PerformanceCounterType { + /// Current execution instruction counter. + /// + /// The number of WebAssembly instructions the canister has executed + /// since the beginning of the current Message execution. + InstructionCounter, + /// Call context instruction counter + /// + /// The number of WebAssembly instructions the canister has executed + /// within the call context of the current Message execution + /// since Call context creation. + /// The counter monotonically increases across all message executions + /// in the call context until the corresponding call context is removed. + CallContextInstructionCounter, + /// A performance counter type that is not recognized by this library. + Unrecognized(u32), +} + +impl From for PerformanceCounterType { + fn from(value: u32) -> Self { + match value { + 0 => Self::InstructionCounter, + 1 => Self::CallContextInstructionCounter, + _ => Self::Unrecognized(value), + } + } +} + +impl From for u32 { + fn from(value: PerformanceCounterType) -> Self { + match value { + PerformanceCounterType::InstructionCounter => 0, + PerformanceCounterType::CallContextInstructionCounter => 1, + PerformanceCounterType::Unrecognized(value) => value, + } + } +} + +impl PartialEq for PerformanceCounterType { + fn eq(&self, other: &u32) -> bool { + let self_as_u32: u32 = (*self).into(); + self_as_u32 == *other + } +} + +/// Returns the number of instructions that the canister executed since the last [entry +/// point](https://internetcomputer.org/docs/current/references/ic-interface-spec/#entry-points). +#[inline] +pub fn instruction_counter() -> u64 { + performance_counter(0) +} + +/// Returns the number of WebAssembly instructions the canister has executed +/// within the call context of the current Message execution since +/// Call context creation. +/// +/// The counter monotonically increases across all message executions +/// in the call context until the corresponding call context is removed. +#[inline] +pub fn call_context_instruction_counter() -> u64 { + performance_counter(1) +} + +/// Determines if a Principal is a controller of the canister. +pub fn is_controller(principal: &Principal) -> bool { + let slice = principal.as_slice(); + // SAFETY: `principal.as_bytes()`, being `&[u8]`, is a readable sequence of bytes and therefore safe to pass to `ic0.is_controller`. + unsafe { ic0::is_controller(slice.as_ptr() as usize, slice.len()) != 0 } +} + +/// Checks if in replicated execution. +/// +/// The canister can check whether it is currently running in replicated or non replicated execution. +pub fn in_replicated_execution() -> bool { + // SAFETY: ic0.in_replicated_execution is always safe to call. + match unsafe { ic0::in_replicated_execution() } { + 0 => false, + 1 => true, + _ => unreachable!(), + } +} + +/// Emits textual trace messages. +/// +/// On the "real" network, these do not do anything. +/// +/// When executing in an environment that supports debugging, this copies out the data +/// and logs, prints or stores it in an environment-appropriate way. +pub fn debug_print>(data: T) { + let buf = data.as_ref(); + // SAFETY: `buf` is a readable sequence of bytes and therefore can be passed to ic0.debug_print. + unsafe { + ic0::debug_print(buf.as_ptr() as usize, buf.len()); + } +} + +/// Traps with the given message. +/// +/// The environment may copy out the data and log, print or store it in an environment-appropriate way, +/// or include it in system-generated reject messages where appropriate. +pub fn trap>(data: T) -> ! { + let buf = data.as_ref(); + // SAFETY: `buf` is a readable sequence of bytes and therefore can be passed to ic0.trap. + unsafe { + ic0::trap(buf.as_ptr() as usize, buf.len()); + } + unreachable!() +} + +// # Deprecated API bindings +// +// The following functions are deprecated and will be removed in the future. +// They are kept here for compatibility with existing code. + +/// Prints the given message. +#[deprecated(since = "0.18.0", note = "Use `debug_print` instead")] +pub fn print>(s: S) { + let s = s.as_ref(); + // SAFETY: `s`, being &str, is a readable sequence of bytes and therefore can be passed to ic0.debug_print. + unsafe { + ic0::debug_print(s.as_ptr() as usize, s.len()); + } +} + +/// Returns the caller of the current call. +#[deprecated(since = "0.18.0", note = "Use `msg_caller` instead")] +pub fn caller() -> Principal { + // SAFETY: ic0.msg_caller_size is always safe to call. + let len = unsafe { ic0::msg_caller_size() }; + let mut bytes = vec![0u8; len]; + // SAFETY: Because `bytes` is mutable, and allocated to `len` bytes, it is safe to be passed to `ic0.msg_caller_copy` with a 0-offset. + unsafe { + ic0::msg_caller_copy(bytes.as_mut_ptr() as usize, 0, len); + } + Principal::try_from(&bytes).unwrap() +} + +/// Returns the canister id as a blob. +#[deprecated(since = "0.18.0", note = "Use `canister_self` instead")] +pub fn id() -> Principal { + // SAFETY: ic0.canister_self_size is always safe to call. + let len = unsafe { ic0::canister_self_size() }; + let mut bytes = vec![0u8; len]; + // SAFETY: Because `bytes` is mutable, and allocated to `len` bytes, it is safe to be passed to `ic0.canister_self_copy` with a 0-offset. + unsafe { + ic0::canister_self_copy(bytes.as_mut_ptr() as usize, 0, len); + } + Principal::try_from(&bytes).unwrap() +} + +/// Gets the amount of funds available in the canister. +#[deprecated(since = "0.18.0", note = "Use `canister_cycle_balance` instead")] +pub fn canister_balance128() -> u128 { + let mut recv = 0u128; + // SAFETY: recv is writable and the size expected by ic0.canister_cycle_balance128. + unsafe { ic0::canister_cycle_balance128(&mut recv as *mut u128 as usize) } + recv +} + +/// Sets the certified data of this canister. +/// +/// Canisters can store up to 32 bytes of data that is certified by +/// the system on a regular basis. One can call [data_certificate] +/// function from a query call to get a certificate authenticating the +/// value set by calling this function. +/// +/// This function can only be called from the following contexts: +/// * "canister_init", "canister_pre_upgrade" and "canister_post_upgrade" +/// hooks. +/// * "canister_update" calls. +/// * reply or reject callbacks. +/// +/// # Panics +/// +/// * This function traps if data.len() > 32. +/// * This function traps if it's called from an illegal context +/// (e.g., from a query call). +#[deprecated(since = "0.18.0", note = "Use `certified_data_set` instead")] +pub fn set_certified_data(data: &[u8]) { + // SAFETY: because data is a slice ref, its pointer and length are valid to pass to ic0.certified_data_set. + unsafe { ic0::certified_data_set(data.as_ptr() as usize, data.len()) } +} + +/// Sets global timer. +/// +/// The canister can set a global timer to make the system +/// schedule a call to the exported canister_global_timer +/// Wasm method after the specified time. +/// The time must be provided as nanoseconds since 1970-01-01. +/// +/// The function returns the previous value of the timer. +/// If no timer is set before invoking the function, then the function returns zero. +/// +/// Passing zero as an argument to the function deactivates the timer and thus +/// prevents the system from scheduling calls to the canister's canister_global_timer Wasm method. +#[deprecated(since = "0.18.0", note = "Use `global_timer_set` instead")] +pub fn set_global_timer(timestamp: u64) -> u64 { + // SAFETY: ic0.global_timer_set is always safe to call. + unsafe { ic0::global_timer_set(timestamp) } +} diff --git a/src/ic-cdk/src/api/call.rs b/ic-cdk/src/api/call.rs similarity index 84% rename from src/ic-cdk/src/api/call.rs rename to ic-cdk/src/api/call.rs index 2fd130210..0f77c3670 100644 --- a/src/ic-cdk/src/api/call.rs +++ b/ic-cdk/src/api/call.rs @@ -1,4 +1,6 @@ //! APIs to make and manage calls in the canister. + +#![allow(deprecated)] use crate::api::trap; use candid::utils::{decode_args_with_config_debug, ArgumentDecoder, ArgumentEncoder}; use candid::{ @@ -16,7 +18,11 @@ use std::task::{Context, Poll, Waker}; /// /// These can be obtained either using `reject_code()` or `reject_result()`. #[allow(missing_docs)] -#[repr(i32)] +#[deprecated( + since = "0.18.0", + note = "Please use `ic_cdk::call::RejectCode` instead." +)] +#[repr(usize)] #[derive(CandidType, Deserialize, Clone, Copy, Hash, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum RejectionCode { NoError = 0, @@ -30,8 +36,8 @@ pub enum RejectionCode { Unknown, } -impl From for RejectionCode { - fn from(code: i32) -> Self { +impl From for RejectionCode { + fn from(code: usize) -> Self { match code { 0 => RejectionCode::NoError, 1 => RejectionCode::SysFatal, @@ -46,13 +52,17 @@ impl From for RejectionCode { impl From for RejectionCode { fn from(code: u32) -> Self { - RejectionCode::from(code as i32) + RejectionCode::from(code as usize) } } /// The result of a Call. /// /// Errors on the IC have two components; a Code and a message associated with it. +#[deprecated( + since = "0.18.0", + note = "Please use `ic_cdk::call::CallResult` instead." +)] pub type CallResult = Result; // Internal state for the Future when sending a call. @@ -88,10 +98,10 @@ impl> Future for CallFuture { // SAFETY: // `callee`, being &[u8], is a readable sequence of bytes and therefore can be passed to ic0.call_new. // `method`, being &str, is a readable sequence of bytes and therefore can be passed to ic0.call_new. - // `callback` is a function with signature (env : i32) -> () and therefore can be called as both reply and reject fn for ic0.call_new. + // `callback` is a function with signature (env : usize) -> () and therefore can be called as both reply and reject fn for ic0.call_new. // `state_ptr` is a pointer created via Weak::into_raw, and can therefore be passed as the userdata for `callback`. // `args`, being a &[u8], is a readable sequence of bytes and therefore can be passed to ic0.call_data_append. - // `cleanup` is a function with signature (env : i32) -> () and therefore can be called as a cleanup fn for ic0.call_on_cleanup. + // `cleanup` is a function with signature (env : usize) -> () and therefore can be called as a cleanup fn for ic0.call_on_cleanup. // `state_ptr` is a pointer created via Weak::into_raw, and can therefore be passed as the userdata for `cleanup`. // ic0.call_perform is always safe to call. // callback and cleanup are safe to parameterize with T because: @@ -99,19 +109,19 @@ impl> Future for CallFuture { // - if the future is *not* dropped before the callback is called, the compiler will mandate that any data borrowed by T is still alive let err_code = unsafe { ic0::call_new( - callee.as_ptr() as i32, - callee.len() as i32, - method.as_ptr() as i32, - method.len() as i32, - callback:: as usize as i32, - state_ptr as i32, - callback:: as usize as i32, - state_ptr as i32, + callee.as_ptr() as usize, + callee.len(), + method.as_ptr() as usize, + method.len(), + callback:: as usize as usize, + state_ptr as usize, + callback:: as usize as usize, + state_ptr as usize, ); - ic0::call_data_append(args.as_ptr() as i32, args.len() as i32); + ic0::call_data_append(args.as_ptr() as usize, args.len()); add_payment(payment); - ic0::call_on_cleanup(cleanup:: as usize as i32, state_ptr as i32); + ic0::call_on_cleanup(cleanup:: as usize as usize, state_ptr as usize); ic0::call_perform() }; @@ -197,7 +207,7 @@ fn add_payment(payment: u128) { let low = (payment & u64::MAX as u128) as u64; // SAFETY: ic0.call_cycles_add128 is always safe to call. unsafe { - ic0::call_cycles_add128(high as i64, low as i64); + ic0::call_cycles_add128(high, low); } } @@ -221,6 +231,10 @@ fn add_payment(payment: u128) { /// * If the payment is non-zero and the system fails to deliver the notification, the behaviour /// is unspecified: the funds can be either reimbursed or consumed irrevocably by the IC depending /// on the underlying implementation of one-way calls. +#[deprecated( + since = "0.18.0", + note = "Please use `ic_cdk::call::Call::new().with_arg(,,).with_cycles(..).call_oneway()` instead." +)] pub fn notify_with_payment128( id: Principal, method: &str, @@ -232,6 +246,10 @@ pub fn notify_with_payment128( } /// Like [notify_with_payment128], but sets the payment to zero. +#[deprecated( + since = "0.18.0", + note = "Please use `ic_cdk::call::Call::new().with_arg(,,).with_cycles(..).call_oneway()` instead." +)] pub fn notify( id: Principal, method: &str, @@ -241,6 +259,10 @@ pub fn notify( } /// Like [notify], but sends the argument as raw bytes, skipping Candid serialization. +#[deprecated( + since = "0.18.0", + note = "Please use `ic_cdk::call::Call::new().with_raw_args(..).with_cycles(..).call_oneway()` instead." +)] pub fn notify_raw( id: Principal, method: &str, @@ -263,17 +285,17 @@ pub fn notify_raw( // ic0.call_perform is always safe to call. let err_code = unsafe { ic0::call_new( - callee.as_ptr() as i32, - callee.len() as i32, - method.as_ptr() as i32, - method.len() as i32, - /* reply_fun = */ -1, - /* reply_env = */ -1, - /* reject_fun = */ -1, - /* reject_env = */ -1, + callee.as_ptr() as usize, + callee.len(), + method.as_ptr() as usize, + method.len(), + /* reply_fun = */ usize::MAX, + /* reply_env = */ usize::MAX, + /* reject_fun = */ usize::MAX, + /* reject_env = */ usize::MAX, ); add_payment(payment); - ic0::call_data_append(args_raw.as_ptr() as i32, args_raw.len() as i32); + ic0::call_data_append(args_raw.as_ptr() as usize, args_raw.len()); ic0::call_perform() }; match err_code { @@ -297,6 +319,10 @@ pub fn notify_raw( /// call_raw(callee_canister(), "add_user", b"abcd", 1_000_000u64).await.unwrap() /// } /// ``` +#[deprecated( + since = "0.18.0", + note = "Please use `ic_cdk::call::Call::new().with_raw_args(..).with_cycles(..).call()` instead." +)] pub fn call_raw<'a, T: AsRef<[u8]> + Send + Sync + 'a>( id: Principal, method: &str, @@ -320,6 +346,10 @@ pub fn call_raw<'a, T: AsRef<[u8]> + Send + Sync + 'a>( /// call_raw128(callee_canister(), "add_user", b"abcd", 1_000_000u128).await.unwrap() /// } /// ``` +#[deprecated( + since = "0.18.0", + note = "Please use `ic_cdk::call::Call::new().with_raw_args(..).with_cycles(..).call()` instead." +)] pub fn call_raw128<'a, T: AsRef<[u8]> + Send + Sync + 'a>( id: Principal, method: &str, @@ -386,6 +416,10 @@ fn decoder_error_to_reject(err: candid::error::Error) -> (RejectionCode, Stri /// * The type annotation on return type is required. Or the return type can be inferred from the context. /// * The asynchronous call must be awaited in order for the inter-canister call to be made. /// * If the reply payload is not a valid encoding of the expected type `T`, the call results in [RejectionCode::CanisterError] error. +#[deprecated( + since = "0.18.0", + note = "Please use `ic_cdk::call::Call::new().with_arg(..).call()` instead." +)] pub fn call ArgumentDecoder<'a>>( id: Principal, method: &str, @@ -428,6 +462,10 @@ pub fn call ArgumentDecoder<'a>>( /// * The type annotation on return type is required. Or the return type can be inferred from the context. /// * The asynchronous call must be awaited in order for the inter-canister call to be made. /// * If the reply payload is not a valid encoding of the expected type `T`, the call results in [RejectionCode::CanisterError] error. +#[deprecated( + since = "0.18.0", + note = "Please use `ic_cdk::call::Call::new().with_arg(..).with_cycles(..).call()` instead." +)] pub fn call_with_payment ArgumentDecoder<'a>>( id: Principal, method: &str, @@ -471,6 +509,10 @@ pub fn call_with_payment ArgumentDecoder<'a>>( /// * The type annotation on return type is required. Or the return type can be inferred from the context. /// * The asynchronous call must be awaited in order for the inter-canister call to be made. /// * If the reply payload is not a valid encoding of the expected type `T`, the call results in [RejectionCode::CanisterError] error. +#[deprecated( + since = "0.18.0", + note = "Please use `ic_cdk::call::Call::new().with_arg(..).with_cycles(..).call()` instead." +)] pub fn call_with_payment128 ArgumentDecoder<'a>>( id: Principal, method: &str, @@ -517,6 +559,10 @@ pub fn call_with_payment128 ArgumentDecoder<'a>>( /// user_id /// } /// ``` +#[deprecated( + since = "0.18.0", + note = "Please use `ic_cdk::call::Call::new().with_arg(..).with_cycles(..).with_decoder_config(..).call()` instead." +)] pub fn call_with_config<'b, T: ArgumentEncoder, R: for<'a> ArgumentDecoder<'a>>( id: Principal, method: &'b str, @@ -563,6 +609,10 @@ fn print_decoding_debug_info(title: &str, cost: &DecoderConfig, pre_cycles: Opti /// /// It will be Ok(T) if the call succeeded (with T being the arg_data), /// and [reject_message()] if it failed. +#[deprecated( + since = "0.18.0", + note = "Please use `ic_cdk::api::{msg_reject_code, msg_reject_msg}` instead." +)] pub fn result ArgumentDecoder<'a>>() -> Result { match reject_code() { RejectionCode::NoError => { @@ -573,6 +623,10 @@ pub fn result ArgumentDecoder<'a>>() -> Result { } /// Returns the rejection code for the call. +#[deprecated( + since = "0.18.0", + note = "Please use `ic_cdk::api::msg_reject_code` instead." +)] pub fn reject_code() -> RejectionCode { // SAFETY: ic0.msg_reject_code is always safe to call. let code = unsafe { ic0::msg_reject_code() }; @@ -580,35 +634,47 @@ pub fn reject_code() -> RejectionCode { } /// Returns the rejection message. +#[deprecated( + since = "0.18.0", + note = "Please use `ic_cdk::api::msg_reject_msg` instead." +)] pub fn reject_message() -> String { // SAFETY: ic0.msg_reject_msg_size is always safe to call. let len: u32 = unsafe { ic0::msg_reject_msg_size() as u32 }; let mut bytes = vec![0u8; len as usize]; // SAFETY: `bytes`, being mutable and allocated to `len` bytes, is safe to pass to ic0.msg_reject_msg_copy with no offset unsafe { - ic0::msg_reject_msg_copy(bytes.as_mut_ptr() as i32, 0, len as i32); + ic0::msg_reject_msg_copy(bytes.as_mut_ptr() as usize, 0, len as usize); } String::from_utf8_lossy(&bytes).into_owned() } /// Rejects the current call with the message. +#[deprecated( + since = "0.18.0", + note = "Please use `ic_cdk::api::msg_reject` instead." +)] pub fn reject(message: &str) { let err_message = message.as_bytes(); // SAFETY: `err_message`, being &[u8], is a readable sequence of bytes, and therefore valid to pass to ic0.msg_reject. unsafe { - ic0::msg_reject(err_message.as_ptr() as i32, err_message.len() as i32); + ic0::msg_reject(err_message.as_ptr() as usize, err_message.len()); } } /// An io::Write for message replies. #[derive(Debug, Copy, Clone)] +#[deprecated( + since = "0.18.0", + note = "Please use `ic_cdk::api::msg_reply` instead." +)] pub struct CallReplyWriter; impl std::io::Write for CallReplyWriter { fn write(&mut self, buf: &[u8]) -> std::io::Result { // SAFETY: buf, being &[u8], is a readable sequence of bytes, and therefore valid to pass to ic0.msg_reply_data_append. unsafe { - ic0::msg_reply_data_append(buf.as_ptr() as i32, buf.len() as i32); + ic0::msg_reply_data_append(buf.as_ptr() as usize, buf.len()); } Ok(buf.len()) } @@ -619,6 +685,10 @@ impl std::io::Write for CallReplyWriter { } /// Replies to the current call with a candid argument. +#[deprecated( + since = "0.18.0", + note = "Please use `ic_cdk::api::msg_reply` instead." +)] pub fn reply(reply: T) { write_args(&mut CallReplyWriter, reply).expect("Could not encode reply."); // SAFETY: ic0.msg_reply is always safe to call. @@ -629,18 +699,25 @@ pub fn reply(reply: T) { /// Returns the amount of cycles that were transferred by the caller /// of the current call, and is still available in this message. +#[deprecated( + since = "0.18.0", + note = "Please use `ic_cdk::api::msg_cycles_available` instead." +)] pub fn msg_cycles_available() -> u64 { - // SAFETY: ic0.msg_cycles_available is always safe to call. - unsafe { ic0::msg_cycles_available() as u64 } + msg_cycles_available128() as u64 } /// Returns the amount of cycles that were transferred by the caller /// of the current call, and is still available in this message. +#[deprecated( + since = "0.18.0", + note = "Please use `ic_cdk::api::msg_cycles_available` instead." +)] pub fn msg_cycles_available128() -> u128 { let mut recv = 0u128; // SAFETY: recv is writable and sixteen bytes wide, and therefore is safe to pass to ic0.msg_cycles_available128 unsafe { - ic0::msg_cycles_available128(&mut recv as *mut u128 as i32); + ic0::msg_cycles_available128(&mut recv as *mut u128 as usize); } recv } @@ -648,19 +725,26 @@ pub fn msg_cycles_available128() -> u128 { /// Returns the amount of cycles that came back with the response as a refund. /// /// The refund has already been added to the canister balance automatically. +#[deprecated( + since = "0.18.0", + note = "Please use `ic_cdk::api::msg_cycles_refunded` instead." +)] pub fn msg_cycles_refunded() -> u64 { - // SAFETY: ic0.msg_cycles_refunded is always safe to call - unsafe { ic0::msg_cycles_refunded() as u64 } + msg_cycles_refunded128() as u64 } /// Returns the amount of cycles that came back with the response as a refund. /// /// The refund has already been added to the canister balance automatically. +#[deprecated( + since = "0.18.0", + note = "Please use `ic_cdk::api::msg_cycles_refunded` instead." +)] pub fn msg_cycles_refunded128() -> u128 { let mut recv = 0u128; // SAFETY: recv is writable and sixteen bytes wide, and therefore is safe to pass to ic0.msg_cycles_refunded128 unsafe { - ic0::msg_cycles_refunded128(&mut recv as *mut u128 as i32); + ic0::msg_cycles_refunded128(&mut recv as *mut u128 as usize); } recv } @@ -668,56 +752,79 @@ pub fn msg_cycles_refunded128() -> u128 { /// Moves cycles from the call to the canister balance. /// /// The actual amount moved will be returned. +#[deprecated( + since = "0.18.0", + note = "Please use `ic_cdk::api::msg_cycles_accept` instead." +)] pub fn msg_cycles_accept(max_amount: u64) -> u64 { - // SAFETY: ic0.msg_cycles_accept is always safe to call. - unsafe { ic0::msg_cycles_accept(max_amount as i64) as u64 } + msg_cycles_accept128(max_amount as u128) as u64 } /// Moves cycles from the call to the canister balance. /// /// The actual amount moved will be returned. +#[deprecated( + since = "0.18.0", + note = "Please use `ic_cdk::api::msg_cycles_accept` instead." +)] pub fn msg_cycles_accept128(max_amount: u128) -> u128 { let high = (max_amount >> 64) as u64; let low = (max_amount & u64::MAX as u128) as u64; let mut recv = 0u128; // SAFETY: `recv` is writable and sixteen bytes wide, and therefore safe to pass to ic0.msg_cycles_accept128 unsafe { - ic0::msg_cycles_accept128(high as i64, low as i64, &mut recv as *mut u128 as i32); + ic0::msg_cycles_accept128(high, low, &mut recv as *mut u128 as usize); } recv } /// Returns the argument data as bytes. +#[deprecated( + since = "0.18.0", + note = "Please use `ic_cdk::api::msg_arg_data` instead." +)] pub fn arg_data_raw() -> Vec { // SAFETY: ic0.msg_arg_data_size is always safe to call. - let len: usize = unsafe { ic0::msg_arg_data_size() as usize }; + let len: usize = unsafe { ic0::msg_arg_data_size() }; let mut bytes = Vec::with_capacity(len); // SAFETY: // `bytes`, being mutable and allocated to `len` bytes, is safe to pass to ic0.msg_arg_data_copy with no offset // ic0.msg_arg_data_copy writes to all of `bytes[0..len]`, so `set_len` is safe to call with the new len. unsafe { - ic0::msg_arg_data_copy(bytes.as_mut_ptr() as i32, 0, len as i32); + ic0::msg_arg_data_copy(bytes.as_mut_ptr() as usize, 0, len); bytes.set_len(len); } bytes } /// Gets the len of the raw-argument-data-bytes. +#[deprecated( + since = "0.18.0", + note = "Please use `ic_cdk::api::msg_arg_data` instead." +)] pub fn arg_data_raw_size() -> usize { // SAFETY: ic0.msg_arg_data_size is always safe to call. - unsafe { ic0::msg_arg_data_size() as usize } + unsafe { ic0::msg_arg_data_size() } } /// Replies with the bytes passed +#[deprecated( + since = "0.18.0", + note = "Please use `ic_cdk::api::msg_reply` instead." +)] pub fn reply_raw(buf: &[u8]) { if !buf.is_empty() { // SAFETY: `buf`, being &[u8], is a readable sequence of bytes, and therefore valid to pass to ic0.msg_reject. - unsafe { ic0::msg_reply_data_append(buf.as_ptr() as i32, buf.len() as i32) } + unsafe { ic0::msg_reply_data_append(buf.as_ptr() as usize, buf.len()) } }; // SAFETY: ic0.msg_reply is always safe to call. unsafe { ic0::msg_reply() }; } +#[deprecated( + since = "0.18.0", + note = "Please use `ic_cdk::call::DecoderConfig` instead." +)] #[derive(Debug)] /// Config to control the behavior of decoding canister endpoint arguments. pub struct ArgDecoderConfig { @@ -755,13 +862,17 @@ impl Default for ArgDecoderConfig { /// Returns the argument data in the current call. Traps if the data cannot be /// decoded. +#[deprecated( + since = "0.18.0", + note = "Please use `ic_cdk::call::msg_arg_data` instead." +)] pub fn arg_data ArgumentDecoder<'a>>(arg_config: ArgDecoderConfig) -> R { let bytes = arg_data_raw(); let config = arg_config.to_candid_config(); let res = decode_args_with_config_debug(&bytes, &config); match res { - Err(e) => trap(&format!("failed to decode call arguments: {:?}", e)), + Err(e) => trap(format!("failed to decode call arguments: {:?}", e)), Ok((r, cost)) => { if arg_config.debug { print_decoding_debug_info("Argument", &cost, None); @@ -772,6 +883,10 @@ pub fn arg_data ArgumentDecoder<'a>>(arg_config: ArgDecoderConfig) -> } /// Accepts the ingress message. +#[deprecated( + since = "0.18.0", + note = "Please use `ic_cdk::api::accept_message` instead." +)] pub fn accept_message() { // SAFETY: ic0.accept_message is always safe to call. unsafe { @@ -780,13 +895,17 @@ pub fn accept_message() { } /// Returns the name of current canister method. +#[deprecated( + since = "0.18.0", + note = "Please use `ic_cdk::api::msg_method_name` instead." +)] pub fn method_name() -> String { // SAFETY: ic0.msg_method_name_size is always safe to call. let len: u32 = unsafe { ic0::msg_method_name_size() as u32 }; let mut bytes = vec![0u8; len as usize]; // SAFETY: `bytes` is writable and allocated to `len` bytes, and therefore can be safely passed to ic0.msg_method_name_copy unsafe { - ic0::msg_method_name_copy(bytes.as_mut_ptr() as i32, 0, len as i32); + ic0::msg_method_name_copy(bytes.as_mut_ptr() as usize, 0, len as usize); } String::from_utf8_lossy(&bytes).into_owned() } @@ -800,7 +919,7 @@ pub fn method_name() -> String { )] pub fn performance_counter(counter_type: u32) -> u64 { // SAFETY: ic0.performance_counter is always safe to call. - unsafe { ic0::performance_counter(counter_type as i32) as u64 } + unsafe { ic0::performance_counter(counter_type) } } /// Pretends to have the Candid type `T`, but unconditionally errors diff --git a/src/ic-cdk/src/api/management_canister/bitcoin/mod.rs b/ic-cdk/src/api/management_canister/bitcoin/mod.rs similarity index 100% rename from src/ic-cdk/src/api/management_canister/bitcoin/mod.rs rename to ic-cdk/src/api/management_canister/bitcoin/mod.rs diff --git a/src/ic-cdk/src/api/management_canister/bitcoin/types.rs b/ic-cdk/src/api/management_canister/bitcoin/types.rs similarity index 100% rename from src/ic-cdk/src/api/management_canister/bitcoin/types.rs rename to ic-cdk/src/api/management_canister/bitcoin/types.rs diff --git a/src/ic-cdk/src/api/management_canister/ecdsa/mod.rs b/ic-cdk/src/api/management_canister/ecdsa/mod.rs similarity index 100% rename from src/ic-cdk/src/api/management_canister/ecdsa/mod.rs rename to ic-cdk/src/api/management_canister/ecdsa/mod.rs diff --git a/src/ic-cdk/src/api/management_canister/ecdsa/types.rs b/ic-cdk/src/api/management_canister/ecdsa/types.rs similarity index 100% rename from src/ic-cdk/src/api/management_canister/ecdsa/types.rs rename to ic-cdk/src/api/management_canister/ecdsa/types.rs diff --git a/src/ic-cdk/src/api/management_canister/http_request/mod.rs b/ic-cdk/src/api/management_canister/http_request/mod.rs similarity index 85% rename from src/ic-cdk/src/api/management_canister/http_request/mod.rs rename to ic-cdk/src/api/management_canister/http_request/mod.rs index 48e214d2e..21c34f1d8 100644 --- a/src/ic-cdk/src/api/management_canister/http_request/mod.rs +++ b/ic-cdk/src/api/management_canister/http_request/mod.rs @@ -32,11 +32,11 @@ pub async fn http_request( #[cfg(feature = "transform-closure")] thread_local! { #[allow(clippy::type_complexity)] - static TRANSFORMS: RefCell HttpResponse>>> = RefCell::default(); + static TRANSFORMS_LEGACY: RefCell HttpResponse>>> = RefCell::default(); } #[cfg(feature = "transform-closure")] -#[export_name = "canister_query http_transform"] +#[export_name = "canister_query http_transform_legacy"] extern "C" fn http_transform() { use crate::api::{ call::{arg_data, reply, ArgDecoderConfig}, @@ -49,9 +49,9 @@ extern "C" fn http_transform() { let (args,): (TransformArgs,) = arg_data(ArgDecoderConfig::default()); let int = u64::from_be_bytes(args.context[..].try_into().unwrap()); let key = DefaultKey::from(KeyData::from_ffi(int)); - let func = TRANSFORMS.with(|transforms| transforms.borrow_mut().remove(key)); + let func = TRANSFORMS_LEGACY.with(|transforms| transforms.borrow_mut().remove(key)); let Some(func) = func else { - crate::trap(&format!("Missing transform function for request {int}")); + crate::trap(format!("Missing transform function for request {int}")); }; let transformed = func(args.response); reply((transformed,)) @@ -77,11 +77,11 @@ pub async fn http_request_with_closure( "`CanisterHttpRequestArgument`'s `transform` field must be `None` when using a closure" ); let transform_func = Box::new(transform_func) as _; - let key = TRANSFORMS.with(|transforms| transforms.borrow_mut().insert(transform_func)); + let key = TRANSFORMS_LEGACY.with(|transforms| transforms.borrow_mut().insert(transform_func)); struct DropGuard(DefaultKey); impl Drop for DropGuard { fn drop(&mut self) { - TRANSFORMS.with(|transforms| transforms.borrow_mut().remove(self.0)); + TRANSFORMS_LEGACY.with(|transforms| transforms.borrow_mut().remove(self.0)); } } let key = DropGuard(key); @@ -89,7 +89,7 @@ pub async fn http_request_with_closure( let arg = CanisterHttpRequestArgument { transform: Some(TransformContext { function: TransformFunc(candid::Func { - method: " http_transform".into(), + method: " http_transform_legacy".into(), principal: crate::id(), }), context, diff --git a/src/ic-cdk/src/api/management_canister/http_request/types.rs b/ic-cdk/src/api/management_canister/http_request/types.rs similarity index 100% rename from src/ic-cdk/src/api/management_canister/http_request/types.rs rename to ic-cdk/src/api/management_canister/http_request/types.rs diff --git a/src/ic-cdk/src/api/management_canister/main/mod.rs b/ic-cdk/src/api/management_canister/main/mod.rs similarity index 100% rename from src/ic-cdk/src/api/management_canister/main/mod.rs rename to ic-cdk/src/api/management_canister/main/mod.rs diff --git a/src/ic-cdk/src/api/management_canister/main/types.rs b/ic-cdk/src/api/management_canister/main/types.rs similarity index 100% rename from src/ic-cdk/src/api/management_canister/main/types.rs rename to ic-cdk/src/api/management_canister/main/types.rs diff --git a/src/ic-cdk/src/api/management_canister/mod.rs b/ic-cdk/src/api/management_canister/mod.rs similarity index 97% rename from src/ic-cdk/src/api/management_canister/mod.rs rename to ic-cdk/src/api/management_canister/mod.rs index dd9c83a77..532f09c75 100644 --- a/src/ic-cdk/src/api/management_canister/mod.rs +++ b/ic-cdk/src/api/management_canister/mod.rs @@ -9,7 +9,7 @@ //! //! [1]: https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-management-canister //! [2]: https://internetcomputer.org/assets/files/ic-a45d11feb0ba0494055083f9d2d21ddf.did - +#![allow(deprecated)] pub mod bitcoin; pub mod ecdsa; pub mod http_request; diff --git a/src/ic-cdk/src/api/management_canister/provisional.rs b/ic-cdk/src/api/management_canister/provisional.rs similarity index 100% rename from src/ic-cdk/src/api/management_canister/provisional.rs rename to ic-cdk/src/api/management_canister/provisional.rs diff --git a/src/ic-cdk/src/api/management_canister/schnorr/mod.rs b/ic-cdk/src/api/management_canister/schnorr/mod.rs similarity index 100% rename from src/ic-cdk/src/api/management_canister/schnorr/mod.rs rename to ic-cdk/src/api/management_canister/schnorr/mod.rs diff --git a/src/ic-cdk/src/api/management_canister/schnorr/types.rs b/ic-cdk/src/api/management_canister/schnorr/types.rs similarity index 100% rename from src/ic-cdk/src/api/management_canister/schnorr/types.rs rename to ic-cdk/src/api/management_canister/schnorr/types.rs diff --git a/src/ic-cdk/src/api/stable/canister.rs b/ic-cdk/src/api/stable/canister.rs similarity index 72% rename from src/ic-cdk/src/api/stable/canister.rs rename to ic-cdk/src/api/stable/canister.rs index 248b1980c..1fb29208c 100644 --- a/src/ic-cdk/src/api/stable/canister.rs +++ b/ic-cdk/src/api/stable/canister.rs @@ -9,15 +9,15 @@ pub struct CanisterStableMemory {} impl StableMemory for CanisterStableMemory { fn stable_size(&self) -> u64 { // SAFETY: ic0.stable64_size is always safe to call. - unsafe { ic0::stable64_size() as u64 } + unsafe { ic0::stable64_size() } } fn stable_grow(&self, new_pages: u64) -> Result { // SAFETY: ic0.stable64_grow is always safe to call. unsafe { - match ic0::stable64_grow(new_pages as i64) { - -1 => Err(StableMemoryError::OutOfMemory), - x => Ok(x as u64), + match ic0::stable64_grow(new_pages) { + u64::MAX => Err(StableMemoryError::OutOfMemory), + x => Ok(x), } } } @@ -25,14 +25,14 @@ impl StableMemory for CanisterStableMemory { fn stable_write(&self, offset: u64, buf: &[u8]) { // SAFETY: `buf`, being &[u8], is a readable sequence of bytes, and therefore valid to pass to ic0.stable64_write. unsafe { - ic0::stable64_write(offset as i64, buf.as_ptr() as i64, buf.len() as i64); + ic0::stable64_write(offset, buf.as_ptr() as u64, buf.len() as u64); } } fn stable_read(&self, offset: u64, buf: &mut [u8]) { // SAFETY: `buf`, being &mut [u8], is a writable sequence of bytes, and therefore valid to pass to ic0.stable64_read. unsafe { - ic0::stable64_read(buf.as_ptr() as i64, offset as i64, buf.len() as i64); + ic0::stable64_read(buf.as_ptr() as u64, offset, buf.len() as u64); } } } diff --git a/src/ic-cdk/src/api/stable/mod.rs b/ic-cdk/src/api/stable/mod.rs similarity index 98% rename from src/ic-cdk/src/api/stable/mod.rs rename to ic-cdk/src/api/stable/mod.rs index 47ead7ccf..fe5101da3 100644 --- a/src/ic-cdk/src/api/stable/mod.rs +++ b/ic-cdk/src/api/stable/mod.rs @@ -3,8 +3,6 @@ //! You can check the [Internet Computer Specification](https://internetcomputer.org/docs/current/references/ic-interface-spec/#system-api-stable-memory) //! for a in-depth explanation of stable memory. mod canister; -#[cfg(test)] -mod tests; pub use canister::CanisterStableMemory; use std::{error, fmt, io}; @@ -37,11 +35,6 @@ pub trait StableMemory { fn stable_read(&self, offset: u64, buf: &mut [u8]); } -/// Gets current size of the stable memory (in WASM pages). -pub fn stable_size() -> u64 { - CANISTER_STABLE_MEMORY.stable_size() -} - /// A possible error value when dealing with stable memory. #[derive(Debug)] pub enum StableMemoryError { @@ -62,6 +55,11 @@ impl fmt::Display for StableMemoryError { impl error::Error for StableMemoryError {} +/// Gets current size of the stable memory (in WASM pages). +pub fn stable_size() -> u64 { + CANISTER_STABLE_MEMORY.stable_size() +} + /// Attempts to grow the stable memory by `new_pages` (added pages). /// /// Returns an error if it wasn't possible. Otherwise, returns the previous @@ -101,7 +99,7 @@ pub fn stable_bytes() -> Vec { // `vec`, being mutable and allocated to `size` bytes, is safe to pass to ic0.stable_read with no offset. // ic0.stable_read writes to all of `vec[0..size]`, so `set_len` is safe to call with the new size. unsafe { - ic0::stable64_read(vec.as_ptr() as i64, 0, size as i64); + ic0::stable64_read(vec.as_ptr() as u64, 0, size as u64); vec.set_len(size); } vec @@ -133,11 +131,6 @@ impl Default for StableIO { } } -// Helper macro to implement StableIO for both 32-bit and 64-bit. -// -// We use a macro here since capturing all the traits required to add and manipulate memory -// addresses with generics becomes cumbersome. - impl StableIO { /// Creates a new `StableIO` which writes to the selected memory pub fn with_memory(memory: M, offset: u64) -> Self { diff --git a/ic-cdk/src/call.rs b/ic-cdk/src/call.rs new file mode 100644 index 000000000..5161a1d43 --- /dev/null +++ b/ic-cdk/src/call.rs @@ -0,0 +1,854 @@ +//! APIs to make and manage calls in the canister. +use crate::api::{msg_arg_data, msg_reject_code, msg_reject_msg}; +use candid::utils::{decode_args_with_config_debug, ArgumentDecoder, ArgumentEncoder}; +use candid::{decode_args, encode_args, encode_one, CandidType, Deserialize, Principal}; +use std::future::Future; +use std::pin::Pin; +use std::sync::atomic::Ordering; +use std::sync::{Arc, RwLock, Weak}; +use std::task::{Context, Poll, Waker}; + +/// Reject code explains why the inter-canister call is rejected. +/// +/// See [Reject codes](https://internetcomputer.org/docs/current/references/ic-interface-spec/#reject-codes) for more details. +#[repr(u32)] +#[derive(CandidType, Deserialize, Clone, Copy, Hash, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum RejectCode { + /// No error. + NoError = 0, + + /// Fatal system error, retry unlikely to be useful. + SysFatal = 1, + /// Transient system error, retry might be possible. + SysTransient = 2, + /// Invalid destination (e.g. canister/account does not exist). + DestinationInvalid = 3, + /// Explicit reject by the canister. + CanisterReject = 4, + /// Canister error (e.g., trap, no response). + CanisterError = 5, + /// Response unknown; system stopped waiting for it (e.g., timed out, or system under high load). + SysUnknown = 6, + + /// Unrecognized reject code. + /// + /// Note that this variant is not part of the IC interface spec, and is used to represent + /// reject codes that are not recognized by the library. + Unrecognized(u32), +} + +impl From for RejectCode { + fn from(code: u32) -> Self { + match code { + // 0 is a special code meaning "no error" + 0 => RejectCode::NoError, + 1 => RejectCode::SysFatal, + 2 => RejectCode::SysTransient, + 3 => RejectCode::DestinationInvalid, + 4 => RejectCode::CanisterReject, + 5 => RejectCode::CanisterError, + 6 => RejectCode::SysUnknown, + n => RejectCode::Unrecognized(n), + } + } +} + +impl From for u32 { + fn from(code: RejectCode) -> u32 { + match code { + RejectCode::NoError => 0, + RejectCode::SysFatal => 1, + RejectCode::SysTransient => 2, + RejectCode::DestinationInvalid => 3, + RejectCode::CanisterReject => 4, + RejectCode::CanisterError => 5, + RejectCode::SysUnknown => 6, + RejectCode::Unrecognized(n) => n, + } + } +} + +impl PartialEq for RejectCode { + fn eq(&self, other: &u32) -> bool { + let self_as_u32: u32 = (*self).into(); + self_as_u32 == *other + } +} + +/// Error codes from the `ic0.call_perform` system API. +/// +/// See [`ic0.call_perform`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#system-api-call) for more details. +/// +/// So far, the specified codes (1, 2, 3) share the same meaning as the corresponding [`RejectCode`]s. +#[repr(u32)] +#[derive(CandidType, Deserialize, Clone, Copy, Hash, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum CallPerformErrorCode { + /// No error. + NoError = 0, + + /// Fatal system error, retry unlikely to be useful. + SysFatal = 1, + /// Transient system error, retry might be possible. + SysTransient = 2, + /// Invalid destination (e.g. canister/account does not exist). + DestinationInvalid = 3, + + /// Unrecognized error code. + /// + /// Note that this variant is not part of the IC interface spec, and is used to represent + /// rejection codes that are not recognized by the library. + Unrecognized(u32), +} + +impl From for CallPerformErrorCode { + fn from(code: u32) -> Self { + match code { + 0 => CallPerformErrorCode::NoError, + 1 => CallPerformErrorCode::SysFatal, + 2 => CallPerformErrorCode::SysTransient, + 3 => CallPerformErrorCode::DestinationInvalid, + n => CallPerformErrorCode::Unrecognized(n), + } + } +} + +impl From for u32 { + fn from(code: CallPerformErrorCode) -> u32 { + match code { + CallPerformErrorCode::NoError => 0, + CallPerformErrorCode::SysFatal => 1, + CallPerformErrorCode::SysTransient => 2, + CallPerformErrorCode::DestinationInvalid => 3, + CallPerformErrorCode::Unrecognized(n) => n, + } + } +} + +impl PartialEq for CallPerformErrorCode { + fn eq(&self, other: &u32) -> bool { + let self_as_u32: u32 = (*self).into(); + self_as_u32 == *other + } +} + +/// The error type for inter-canister calls. +#[derive(thiserror::Error, Debug, Clone)] +pub enum CallError { + /// The arguments could not be encoded. + /// + /// This can only happen when the arguments are provided using [`Call::with_arg`] and [`Call::with_args`]. + /// Though the type system guarantees that the arguments are valid Candid types, + /// it is possible that the encoding fails for reasons such as memory allocation failure. + #[error("Failed to encode the arguments: {0}")] + CandidEncodeFailed(String), + + /// The call immediately failed when invoking the call_perform system API. + #[error("The IC was not able to enqueue the call with code {0:?}")] + CallPerformFailed(CallPerformErrorCode), + + /// The call was rejected. + /// + /// Please handle the error by matching on the rejection code. + #[error("The call was rejected with code {0:?} and message: {1}")] + CallRejected(RejectCode, String), + + /// The response could not be decoded. + /// + /// This can only happen when making the call using [`call`][SendableCall::call] + /// or [`call_tuple`][SendableCall::call_tuple]. + /// Because they decode the response to a Candid type. + #[error("Failed to decode the response as {0}")] + CandidDecodeFailed(String), +} + +/// Result of a inter-canister call. +pub type CallResult = Result; + +/// Inter-Canister Call. +/// +/// # Note +/// +/// The [`Call`] defaults to a 10-second timeout for Best-Effort Responses. +/// To change the timeout, use the [`change_timeout`][ConfigurableCall::change_timeout] method. +/// To get a guaranteed response, use the [`with_guaranteed_response`][ConfigurableCall::with_guaranteed_response] method. +#[derive(Debug)] +pub struct Call<'a> { + canister_id: Principal, + method: &'a str, + cycles: Option, + timeout_seconds: Option, + decoder_config: Option, +} + +/// Inter-Canister Call with typed argument. +/// +/// The argument must impl [`CandidType`]. +#[derive(Debug)] +pub struct CallWithArg<'a, T> { + call: Call<'a>, + arg: T, +} + +/// Inter-Canister Call with typed arguments. +/// +/// The arguments are a tuple of types, each implementing [`CandidType`]. +#[derive(Debug)] +pub struct CallWithArgs<'a, T> { + call: Call<'a>, + args: T, +} + +/// Inter-Canister Call with raw arguments. +#[derive(Debug)] +pub struct CallWithRawArgs<'a, A> { + call: Call<'a>, + raw_args: A, +} + +impl<'a> Call<'a> { + /// Constructs a new call with the Canister id and method name. + /// + /// # Note + /// + /// The [`Call`] defaults to a 10-second timeout for Best-Effort Responses. + /// To change the timeout, use the [`change_timeout`][ConfigurableCall::change_timeout] method. + /// To get a guaranteed response, use the [`with_guaranteed_response`][ConfigurableCall::with_guaranteed_response] method. + pub fn new(canister_id: Principal, method: &'a str) -> Self { + Self { + canister_id, + method, + cycles: None, + // Default to 10 seconds. + timeout_seconds: Some(10), + decoder_config: None, + } + } + + /// Sets the argument for the call. + /// + /// The argument must implement [`CandidType`]. + pub fn with_arg(self, arg: T) -> CallWithArg<'a, T> { + CallWithArg { call: self, arg } + } + + /// Sets the arguments for the call. + /// + /// The arguments are a tuple of types, each implementing [`CandidType`]. + pub fn with_args(self, args: T) -> CallWithArgs<'a, T> { + CallWithArgs { call: self, args } + } + + /// Sets the arguments for the call as raw bytes. + pub fn with_raw_args(self, raw_args: A) -> CallWithRawArgs<'a, A> { + CallWithRawArgs { + call: self, + raw_args, + } + } +} + +/// Methods to configure a call. +pub trait ConfigurableCall { + /// Sets the cycles payment for the call. + /// + /// If invoked multiple times, the last value takes effect. + fn with_cycles(self, cycles: u128) -> Self; + + /// Sets the call to have a guaranteed response. + /// + /// If [`change_timeout`](ConfigurableCall::change_timeout) is invoked after this method, + /// the call will instead be set with Best-Effort Responses. + fn with_guaranteed_response(self) -> Self; + + /// Sets the timeout for the Best-Effort Responses. + /// + /// If not set, the call defaults to a 10-second timeout. + /// If invoked multiple times, the last value takes effect. + /// If [`with_guaranteed_response`](ConfigurableCall::with_guaranteed_response) is invoked after this method, + /// the timeout will be ignored. + fn change_timeout(self, timeout_seconds: u32) -> Self; + + /// Sets the [DecoderConfig] for decoding the call response. + fn with_decoder_config(self, decoder_config: DecoderConfig) -> Self; +} + +impl<'a> ConfigurableCall for Call<'a> { + fn with_cycles(mut self, cycles: u128) -> Self { + self.cycles = Some(cycles); + self + } + + fn with_guaranteed_response(mut self) -> Self { + self.timeout_seconds = None; + self + } + + fn change_timeout(mut self, timeout_seconds: u32) -> Self { + self.timeout_seconds = Some(timeout_seconds); + self + } + + fn with_decoder_config(mut self, decoder_config: DecoderConfig) -> Self { + self.decoder_config = Some(decoder_config); + self + } +} + +impl<'a, T> ConfigurableCall for CallWithArg<'a, T> { + fn with_cycles(mut self, cycles: u128) -> Self { + self.call.cycles = Some(cycles); + self + } + + fn with_guaranteed_response(mut self) -> Self { + self.call.timeout_seconds = None; + self + } + + fn change_timeout(mut self, timeout_seconds: u32) -> Self { + self.call.timeout_seconds = Some(timeout_seconds); + self + } + + fn with_decoder_config(mut self, decoder_config: DecoderConfig) -> Self { + self.call.decoder_config = Some(decoder_config); + self + } +} + +impl<'a, T> ConfigurableCall for CallWithArgs<'a, T> { + fn with_cycles(mut self, cycles: u128) -> Self { + self.call.cycles = Some(cycles); + self + } + + fn with_guaranteed_response(mut self) -> Self { + self.call.timeout_seconds = None; + self + } + + fn change_timeout(mut self, timeout_seconds: u32) -> Self { + self.call.timeout_seconds = Some(timeout_seconds); + self + } + + fn with_decoder_config(mut self, decoder_config: DecoderConfig) -> Self { + self.call.decoder_config = Some(decoder_config); + self + } +} + +impl<'a, A> ConfigurableCall for CallWithRawArgs<'a, A> { + fn with_cycles(mut self, cycles: u128) -> Self { + self.call.cycles = Some(cycles); + self + } + + fn with_guaranteed_response(mut self) -> Self { + self.call.timeout_seconds = None; + self + } + + fn change_timeout(mut self, timeout_seconds: u32) -> Self { + self.call.timeout_seconds = Some(timeout_seconds); + self + } + + fn with_decoder_config(mut self, decoder_config: DecoderConfig) -> Self { + self.call.decoder_config = Some(decoder_config); + self + } +} + +/// Methods to send a call. +pub trait SendableCall { + /// Sends the call and gets the reply as raw bytes. + fn call_raw(self) -> impl Future>> + Send + Sync; + + #[doc(hidden)] + /// For [`SendableCall::call`] internal use only. + fn get_decoder_config(&self) -> Option; + + /// Sends the call and decodes the reply to a Candid type. + fn call(self) -> impl Future> + Send + Sync + where + Self: Sized, + R: CandidType + for<'b> Deserialize<'b>, + { + let decoder_config = self.get_decoder_config(); + let fut = self.call_raw(); + async { + let bytes = fut.await?; + match decoder_config { + Some(decoder_config) => { + let pre_cycles = if decoder_config.debug { + Some(crate::api::performance_counter(0)) + } else { + None + }; + let config = decoder_config.to_candid_config(); + match decode_args_with_config_debug::<(R,)>(&bytes, &config) { + Err(e) => Err(decoder_error_to_call_error::(e)), + Ok((r, cost)) => { + if decoder_config.debug { + print_decoding_debug_info( + std::any::type_name::(), + &cost, + pre_cycles, + ); + } + Ok(r.0) + } + } + } + None => decode_args::<(R,)>(&bytes) + .map_err(decoder_error_to_call_error::) + .map(|r| r.0), + } + } + } + + /// Sends the call and decodes the reply to a Candid type. + fn call_tuple(self) -> impl Future> + Send + Sync + where + Self: Sized, + R: for<'b> ArgumentDecoder<'b>, + { + let decoder_config = self.get_decoder_config(); + let fut = self.call_raw(); + async { + let bytes = fut.await?; + match decoder_config { + Some(decoder_config) => { + let pre_cycles = if decoder_config.debug { + Some(crate::api::performance_counter(0)) + } else { + None + }; + let config = decoder_config.to_candid_config(); + match decode_args_with_config_debug(&bytes, &config) { + Err(e) => Err(decoder_error_to_call_error::(e)), + Ok((r, cost)) => { + if decoder_config.debug { + print_decoding_debug_info( + std::any::type_name::(), + &cost, + pre_cycles, + ); + } + Ok(r) + } + } + } + None => decode_args(&bytes).map_err(decoder_error_to_call_error::), + } + } + } + + /// Sends the call and ignores the reply. + fn call_oneway(self) -> CallResult<()>; +} + +impl SendableCall for Call<'_> { + fn call_raw(self) -> impl Future>> + Send + Sync { + let args_raw = vec![0x44, 0x49, 0x44, 0x4c, 0x00, 0x00]; + call_raw_internal::>( + self.canister_id, + self.method, + args_raw, + self.cycles, + self.timeout_seconds, + ) + } + + fn get_decoder_config(&self) -> Option { + self.decoder_config.clone() + } + + fn call_oneway(self) -> CallResult<()> { + let args_raw = vec![0x44, 0x49, 0x44, 0x4c, 0x00, 0x00]; + call_oneway_internal::>( + self.canister_id, + self.method, + args_raw, + self.cycles, + self.timeout_seconds, + ) + } +} + +impl<'a, T: ArgumentEncoder + Send + Sync> SendableCall for CallWithArgs<'a, T> { + async fn call_raw(self) -> CallResult> { + let args_raw = encode_args(self.args).map_err(encoder_error_to_call_error::)?; + call_raw_internal( + self.call.canister_id, + self.call.method, + args_raw, + self.call.cycles, + self.call.timeout_seconds, + ) + .await + } + + fn get_decoder_config(&self) -> Option { + self.call.decoder_config.clone() + } + + fn call_oneway(self) -> CallResult<()> { + let args_raw = encode_args(self.args).map_err(encoder_error_to_call_error::)?; + call_oneway_internal( + self.call.canister_id, + self.call.method, + args_raw, + self.call.cycles, + self.call.timeout_seconds, + ) + } +} + +impl<'a, T: CandidType + Send + Sync> SendableCall for CallWithArg<'a, T> { + async fn call_raw(self) -> CallResult> { + let args_raw = encode_one(self.arg).map_err(encoder_error_to_call_error::)?; + call_raw_internal( + self.call.canister_id, + self.call.method, + args_raw, + self.call.cycles, + self.call.timeout_seconds, + ) + .await + } + + fn get_decoder_config(&self) -> Option { + self.call.decoder_config.clone() + } + + fn call_oneway(self) -> CallResult<()> { + let args_raw = encode_one(self.arg).map_err(encoder_error_to_call_error::)?; + call_oneway_internal( + self.call.canister_id, + self.call.method, + args_raw, + self.call.cycles, + self.call.timeout_seconds, + ) + } +} + +impl<'a, A: AsRef<[u8]> + Send + Sync + 'a> SendableCall for CallWithRawArgs<'a, A> { + fn call_raw(self) -> impl Future>> + Send + Sync { + call_raw_internal( + self.call.canister_id, + self.call.method, + self.raw_args, + self.call.cycles, + self.call.timeout_seconds, + ) + } + + fn get_decoder_config(&self) -> Option { + self.call.decoder_config.clone() + } + + fn call_oneway(self) -> CallResult<()> { + call_oneway_internal( + self.call.canister_id, + self.call.method, + self.raw_args, + self.call.cycles, + self.call.timeout_seconds, + ) + } +} + +// # Internal ================================================================= + +// Internal state for the Future when sending a call. +struct CallFutureState> { + result: Option>>, + waker: Option, + id: Principal, + method: String, + arg: T, + cycles: Option, + timeout_seconds: Option, +} + +struct CallFuture> { + state: Arc>>, +} + +impl> Future for CallFuture { + type Output = CallResult>; + + fn poll(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll { + let self_ref = Pin::into_inner(self); + let mut state = self_ref.state.write().unwrap(); + + if let Some(result) = state.result.take() { + Poll::Ready(result) + } else { + if state.waker.is_none() { + let callee = state.id.as_slice(); + let method = &state.method; + let state_ptr = Weak::into_raw(Arc::downgrade(&self_ref.state)); + // SAFETY: + // `callee`, being &[u8], is a readable sequence of bytes and therefore can be passed to ic0.call_new. + // `method`, being &str, is a readable sequence of bytes and therefore can be passed to ic0.call_new. + // `callback` is a function with signature (env : usize) -> () and therefore can be called as both reply and reject fn for ic0.call_new. + // `state_ptr` is a pointer created via Weak::into_raw, and can therefore be passed as the userdata for `callback`. + // `args`, being a &[u8], is a readable sequence of bytes and therefore can be passed to ic0.call_data_append. + // `cleanup` is a function with signature (env : usize) -> () and therefore can be called as a cleanup fn for ic0.call_on_cleanup. + // `state_ptr` is a pointer created via Weak::into_raw, and can therefore be passed as the userdata for `cleanup`. + // ic0.call_perform is always safe to call. + // callback and cleanup are safe to parameterize with T because: + // - if the future is dropped before the callback is called, there will be no more strong references and the weak reference will fail to upgrade + // - if the future is *not* dropped before the callback is called, the compiler will mandate that any data borrowed by T is still alive + let err_code = unsafe { + ic0::call_new( + callee.as_ptr() as usize, + callee.len(), + method.as_ptr() as usize, + method.len(), + callback:: as usize, + state_ptr as usize, + callback:: as usize, + state_ptr as usize, + ); + let arg = state.arg.as_ref(); + if !arg.is_empty() { + ic0::call_data_append(arg.as_ptr() as usize, arg.len()); + } + if let Some(cycles) = state.cycles { + call_cycles_add(cycles); + } + if let Some(timeout_seconds) = state.timeout_seconds { + ic0::call_with_best_effort_response(timeout_seconds); + } + ic0::call_on_cleanup(cleanup:: as usize, state_ptr as usize); + ic0::call_perform() + }; + + // The conversion fails only when the err_code is 0, which means the call was successfully enqueued. + match CallPerformErrorCode::from(err_code) { + CallPerformErrorCode::NoError => {} + c => { + let result = Err(CallError::CallPerformFailed(c)); + state.result = Some(result.clone()); + return Poll::Ready(result); + } + } + } + state.waker = Some(context.waker().clone()); + Poll::Pending + } + } +} + +/// The callback from IC dereferences the future from a raw pointer, assigns the +/// result and calls the waker. We cannot use a closure here because we pass raw +/// pointers to the System and back. +/// +/// # Safety +/// +/// This function must only be passed to the IC with a pointer from Weak::into_raw as userdata. +unsafe extern "C" fn callback>(state_ptr: *const RwLock>) { + // SAFETY: This function is only ever called by the IC, and we only ever pass a Weak as userdata. + let state = unsafe { Weak::from_raw(state_ptr) }; + if let Some(state) = state.upgrade() { + // Make sure to un-borrow_mut the state. + { + state.write().unwrap().result = Some(match RejectCode::from(msg_reject_code()) { + RejectCode::NoError => Ok(msg_arg_data()), + c => Err(CallError::CallRejected(c, msg_reject_msg())), + }); + } + let w = state.write().unwrap().waker.take(); + if let Some(waker) = w { + // This is all to protect this little guy here which will call the poll() which + // borrow_mut() the state as well. So we need to be careful to not double-borrow_mut. + waker.wake() + } + } +} + +/// This function is called when [`callback`] was just called with the same parameter, and trapped. +/// We can't guarantee internal consistency at this point, but we can at least e.g. drop mutex guards. +/// Waker is a very opaque API, so the best we can do is set a global flag and proceed normally. +/// +/// # Safety +/// +/// This function must only be passed to the IC with a pointer from Weak::into_raw as userdata. +unsafe extern "C" fn cleanup>(state_ptr: *const RwLock>) { + // SAFETY: This function is only ever called by the IC, and we only ever pass a Weak as userdata. + let state = unsafe { Weak::from_raw(state_ptr) }; + if let Some(state) = state.upgrade() { + // We set the call result, even though it won't be read on the + // default executor, because we can't guarantee it was called on + // our executor. However, we are not allowed to inspect + // reject_code() inside of a cleanup callback, so always set the + // result to a reject. + // + // Borrowing does not trap - the rollback from the + // previous trap ensures that the RwLock can be borrowed again. + let w = state.write().unwrap().waker.take(); + if let Some(waker) = w { + // Flag that we do not want to actually wake the task - we + // want to drop it *without* executing it. + crate::futures::CLEANUP.store(true, Ordering::Relaxed); + waker.wake(); + crate::futures::CLEANUP.store(false, Ordering::Relaxed); + } + } +} + +fn call_raw_internal<'a, T: AsRef<[u8]> + Send + Sync + 'a>( + id: Principal, + method: &str, + args_raw: T, + cycles: Option, + timeout_seconds: Option, +) -> impl Future>> + Send + Sync + 'a { + let state = Arc::new(RwLock::new(CallFutureState { + result: None, + waker: None, + id, + method: method.to_string(), + arg: args_raw, + cycles, + timeout_seconds, + })); + CallFuture { state } +} + +fn call_oneway_internal>( + id: Principal, + method: &str, + args_raw: T, + cycles: Option, + timeout_seconds: Option, +) -> CallResult<()> { + let callee = id.as_slice(); + // We set all callbacks to usize::MAX, which is guaranteed to be invalid callback index. + // The system will still deliver the reply, but it will trap immediately because the callback + // is not a valid function. See + // https://www.joachim-breitner.de/blog/789-Zero-downtime_upgrades_of_Internet_Computer_canisters#one-way-calls + // for more context. + + // SAFETY: + // ic0.call_new: + // `callee_src` and `callee_size`: `callee` being &[u8], is a readable sequence of bytes. + // `name_src` and `name_size`: `method`, being &str, is a readable sequence of bytes. + // `reply_fun` and `reject_fun`: In "notify" style call, we want these callback functions to not be called. So pass `usize::MAX` which is a function pointer the wasm module cannot possibly contain. + // `reply_env` and `reject_env`: Since the callback functions will never be called, any value can be passed as its context parameter. + // ic0.call_data_append: + // `args`, being a &[u8], is a readable sequence of bytes. + // ic0.call_with_best_effort_response is always safe to call. + // ic0.call_perform is always safe to call. + let err_code = unsafe { + ic0::call_new( + callee.as_ptr() as usize, + callee.len(), + method.as_ptr() as usize, + method.len(), + usize::MAX, + usize::MAX, + usize::MAX, + usize::MAX, + ); + let arg = args_raw.as_ref(); + if !arg.is_empty() { + ic0::call_data_append(arg.as_ptr() as usize, arg.len()); + } + if let Some(cycles) = cycles { + call_cycles_add(cycles); + } + if let Some(timeout_seconds) = timeout_seconds { + ic0::call_with_best_effort_response(timeout_seconds); + } + ic0::call_perform() + }; + // The conversion fails only when the err_code is 0, which means the call was successfully enqueued. + match CallPerformErrorCode::from(err_code) { + CallPerformErrorCode::NoError => Ok(()), + c => Err(CallError::CallPerformFailed(c)), + } +} + +// # Internal END ============================================================= + +fn call_cycles_add(cycles: u128) { + if cycles == 0 { + return; + } + let high = (cycles >> 64) as u64; + let low = (cycles & u64::MAX as u128) as u64; + // SAFETY: ic0.call_cycles_add128 is always safe to call. + unsafe { + ic0::call_cycles_add128(high, low); + } +} + +fn print_decoding_debug_info( + title: &str, + cost: &candid::de::DecoderConfig, + pre_cycles: Option, +) { + use crate::api::{debug_print, performance_counter}; + let pre_cycles = pre_cycles.unwrap_or(0); + let instrs = performance_counter(0) - pre_cycles; + debug_print(format!("[Debug] {title} decoding instructions: {instrs}")); + if let Some(n) = cost.decoding_quota { + debug_print(format!("[Debug] {title} decoding cost: {n}")); + } + if let Some(n) = cost.skipping_quota { + debug_print(format!("[Debug] {title} skipping cost: {n}")); + } +} + +#[derive(Debug, Clone)] +/// Config to control the behavior of decoding canister endpoint arguments. +pub struct DecoderConfig { + /// Limit the total amount of work the deserializer can perform. See [docs on the Candid library](https://docs.rs/candid/latest/candid/de/struct.DecoderConfig.html#method.set_decoding_quota) to understand the cost model. + pub decoding_quota: Option, + /// Limit the total amount of work for skipping unneeded data on the wire. See [docs on the Candid library](https://docs.rs/candid/latest/candid/de/struct.DecoderConfig.html#method.set_skipping_quota) to understand the skipping cost. + pub skipping_quota: Option, + /// When set to true, print instruction count and the decoding/skipping cost to the replica log. + pub debug: bool, +} + +impl DecoderConfig { + fn to_candid_config(&self) -> candid::de::DecoderConfig { + let mut config = candid::de::DecoderConfig::new(); + if let Some(n) = self.decoding_quota { + config.set_decoding_quota(n); + } + if let Some(n) = self.skipping_quota { + config.set_skipping_quota(n); + } + if self.debug { + config.set_full_error_message(true); + } + config + } +} + +impl Default for DecoderConfig { + fn default() -> Self { + Self { + decoding_quota: None, + skipping_quota: Some(10_000), + debug: false, + } + } +} + +/// Converts a decoder error to a CallError. +fn decoder_error_to_call_error(err: candid::error::Error) -> CallError { + CallError::CandidDecodeFailed(format!("{}: {}", std::any::type_name::(), err)) +} + +/// Converts a encoder error to a CallError. +fn encoder_error_to_call_error(err: candid::error::Error) -> CallError { + CallError::CandidEncodeFailed(format!("{}: {}", std::any::type_name::(), err)) +} diff --git a/src/ic-cdk/src/futures.rs b/ic-cdk/src/futures.rs similarity index 98% rename from src/ic-cdk/src/futures.rs rename to ic-cdk/src/futures.rs index 35a70504a..7a58a58ae 100644 --- a/src/ic-cdk/src/futures.rs +++ b/ic-cdk/src/futures.rs @@ -9,9 +9,9 @@ use self::waker::WakerState; /// Must be called on every top-level future corresponding to a method call of a /// canister by the IC, other than async functions marked `#[update]` or similar. -#[cfg_attr(not(target_arch = "wasm32"), allow(unused_variables, unreachable_code))] +#[cfg_attr(not(target_family = "wasm"), allow(unused_variables, unreachable_code))] pub fn spawn>(future: F) { - #[cfg(not(target_arch = "wasm32"))] + #[cfg(not(target_family = "wasm"))] panic!("Cannot be run outside of wasm!"); // really, just cannot be run in a multi-threaded environment let pinned_future = Box::pin(future); let waker_state = Rc::new(WakerState { diff --git a/src/ic-cdk/src/lib.rs b/ic-cdk/src/lib.rs similarity index 74% rename from src/ic-cdk/src/lib.rs rename to ic-cdk/src/lib.rs index 4fbd1ac22..e9d6bb12e 100644 --- a/src/ic-cdk/src/lib.rs +++ b/ic-cdk/src/lib.rs @@ -12,20 +12,28 @@ #[cfg(target_feature = "atomics")] compile_error!("This version of the CDK does not support multithreading."); +pub mod prelude; + pub mod api; +pub mod call; mod futures; mod macros; +pub mod management_canister; mod printer; +pub mod stable; pub mod storage; use std::sync::atomic::{AtomicBool, Ordering}; #[doc(inline)] -pub use api::call::call; -#[doc(inline)] -pub use api::call::notify; +pub use api::trap; + #[doc(inline)] -pub use api::{caller, id, print, trap}; +#[allow(deprecated)] +pub use api::{ + call::{call, notify}, + caller, id, print, +}; #[doc(inline)] pub use macros::*; @@ -55,15 +63,15 @@ pub fn spawn>(future: F) { } /// Format and then print the formatted message -#[cfg(target_arch = "wasm32")] +#[cfg(target_family = "wasm")] #[macro_export] macro_rules! println { - ($fmt:expr) => ($crate::print(format!($fmt))); - ($fmt:expr, $($arg:tt)*) => ($crate::print(format!($fmt, $($arg)*))); + ($fmt:expr) => ($crate::api::debug_print(format!($fmt))); + ($fmt:expr, $($arg:tt)*) => ($crate::api::debug_print(format!($fmt, $($arg)*))); } /// Format and then print the formatted message -#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(target_family = "wasm"))] #[macro_export] macro_rules! println { ($fmt:expr) => (std::println!($fmt)); @@ -71,15 +79,15 @@ macro_rules! println { } /// Format and then print the formatted message -#[cfg(target_arch = "wasm32")] +#[cfg(target_family = "wasm")] #[macro_export] macro_rules! eprintln { - ($fmt:expr) => ($crate::print(format!($fmt))); - ($fmt:expr, $($arg:tt)*) => ($crate::print(format!($fmt, $($arg)*))); + ($fmt:expr) => ($crate::api::debug_print(format!($fmt))); + ($fmt:expr, $($arg:tt)*) => ($crate::api::debug_print(format!($fmt, $($arg)*))); } /// Format and then print the formatted message -#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(target_family = "wasm"))] #[macro_export] macro_rules! eprintln { ($fmt:expr) => (std::eprintln!($fmt)); diff --git a/src/ic-cdk/src/macros.rs b/ic-cdk/src/macros.rs similarity index 100% rename from src/ic-cdk/src/macros.rs rename to ic-cdk/src/macros.rs diff --git a/ic-cdk/src/management_canister.rs b/ic-cdk/src/management_canister.rs new file mode 100644 index 000000000..cdd71fd54 --- /dev/null +++ b/ic-cdk/src/management_canister.rs @@ -0,0 +1,1741 @@ +//! Functions and types for calling [the IC management canister][1]. +//! +//! This module is a direct translation from its Candid interface description. +//! +//! The functions and types defined in this module serves these purposes: +//! * Make it easy to construct correct request data. +//! * Handle the response ergonomically. +//! * For those calls require cycles payments, the cycles amount is an explicit argument. +//! +//! [1]: https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-management-canister + +use crate::api::canister_version; +use crate::call::{Call, CallResult, ConfigurableCall, SendableCall}; +use candid::{CandidType, Nat, Principal}; +use serde::{Deserialize, Serialize}; + +/// Canister ID. +pub type CanisterId = Principal; + +/// Chunk hash. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct ChunkHash { + /// The hash of an uploaded chunk + #[serde(with = "serde_bytes")] + pub hash: Vec, +} + +/// Log Visibility. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, +)] +pub enum LogVisibility { + /// Controllers. + #[default] + #[serde(rename = "controllers")] + Controllers, + /// Public. + #[serde(rename = "public")] + Public, + /// Allowed viewers. + #[serde(rename = "allowed_viewers")] + AllowedViewers(Vec), +} + +/// Canister settings. +/// +/// The settings are optional. If they are not explicitly set, the default values will be applied automatically. +/// +/// See [`settings`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-create_canister). +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, +)] +pub struct CanisterSettings { + /// A list of at most 10 principals. + /// + /// The principals in this list become the *controllers* of the canister. + /// + /// Default value: A list containing only the caller of the create_canister call. + pub controllers: Option>, + /// Must be a number between 0 and 100, inclusively. + /// + /// It indicates how much compute power should be guaranteed to this canister, + /// expressed as a percentage of the maximum compute power that a single canister can allocate. + /// + /// If the IC cannot provide the requested allocation, + /// for example because it is oversubscribed, the call will be **rejected**. + /// + /// Default value: 0 + pub compute_allocation: Option, + /// Must be a number between 0 and 248 (i.e 256TB), inclusively. + /// + /// It indicates how much memory the canister is allowed to use in total. + /// + /// If the IC cannot provide the requested allocation, + /// for example because it is oversubscribed, the call will be **rejected**. + /// + /// If set to 0, then memory growth of the canister will be best-effort and subject to the available memory on the IC. + /// + /// Default value: 0 + pub memory_allocation: Option, + /// Must be a number between 0 and 264-1, inclusively. + /// + /// It indicates a length of time in seconds. + /// + /// Default value: 2592000 (approximately 30 days). + pub freezing_threshold: Option, + /// Must be a number between 0 and 2128-1, inclusively. + /// + /// It indicates the upper limit on `reserved_cycles` of the canister. + /// + /// Default value: 5_000_000_000_000 (5 trillion cycles). + pub reserved_cycles_limit: Option, + /// Defines who is allowed to read the canister's logs. + /// + /// Default value: Controllers + pub log_visibility: Option, + /// Must be a number between 0 and 248-1 (i.e 256TB), inclusively. + /// + /// It indicates the upper limit on the WASM heap memory consumption of the canister. + /// + /// Default value: 3_221_225_472 (3 GiB). + pub wasm_memory_limit: Option, +} + +/// Like [`CanisterSettings`]. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, +)] +pub struct DefiniteCanisterSettings { + /// Controllers of the canister. + pub controllers: Vec, + /// Compute allocation. + pub compute_allocation: Nat, + /// Memory allocation. + pub memory_allocation: Nat, + /// Freezing threshold. + pub freezing_threshold: Nat, + /// Reserved cycles limit. + pub reserved_cycles_limit: Nat, + /// Visibility of canister logs. + pub log_visibility: LogVisibility, + /// The Wasm memory limit. + pub wasm_memory_limit: Nat, +} + +// create_canister ------------------------------------------------------------ + +/// Register a new canister and get its canister id. +/// +/// See [IC method `create_canister`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-create_canister). +/// +/// This call requires cycles payment. The required cycles varies according to the subnet size (number of nodes). +/// Check [Gas and cycles cost](https://internetcomputer.org/docs/current/developer-docs/gas-cost) for more details. +pub async fn create_canister( + arg: CreateCanisterArgs, + cycles: u128, +) -> CallResult { + let complete_arg = CreateCanisterArgsComplete { + settings: arg.settings, + sender_canister_version: Some(canister_version()), + }; + Call::new(Principal::management_canister(), "create_canister") + .with_arg(complete_arg) + .with_guaranteed_response() + .with_cycles(cycles) + .call() + .await +} + +/// Argument type of [`create_canister`]. +/// +/// Please note that this type is a reduced version of [`CreateCanisterArgsComplete`]. +/// The `sender_canister_version` field is removed as it is set automatically in [`create_canister`]. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, +)] +pub struct CreateCanisterArgs { + /// See [`CanisterSettings`]. + pub settings: Option, +} + +/// Complete argument type of `create_canister`. +/// +/// Please note that this type is not used directly as the argument of [`create_canister`]. +/// The function [`create_canister`] takes [`CreateCanisterArgs`] instead. +/// +/// If you want to manually call `create_canister` (construct and invoke a [`Call`]), you should use this complete type. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, +)] +pub struct CreateCanisterArgsComplete { + /// See [`CanisterSettings`]. + pub settings: Option, + /// sender_canister_version must be set to [`canister_version`]. + pub sender_canister_version: Option, +} + +/// Result type of [`create_canister`]. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, +)] +pub struct CreateCanisterResult { + /// Canister ID. + pub canister_id: CanisterId, +} + +// create_canister END -------------------------------------------------------- + +// update_settings ------------------------------------------------------------ + +/// Update the settings of a canister. +/// +/// See [IC method `update_settings`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-update_settings). +pub async fn update_settings(arg: UpdateSettingsArgs) -> CallResult<()> { + let complete_arg = UpdateSettingsArgsComplete { + canister_id: arg.canister_id, + settings: arg.settings, + sender_canister_version: Some(canister_version()), + }; + Call::new(Principal::management_canister(), "update_settings") + .with_arg(complete_arg) + .call() + .await +} + +/// Argument type of [`update_settings`] +/// +/// Please note that this type is a reduced version of [`UpdateSettingsArgsComplete`]. +/// +/// The `sender_canister_version` field is removed as it is set automatically in [`update_settings`]. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct UpdateSettingsArgs { + /// Canister ID. + pub canister_id: CanisterId, + /// See [CanisterSettings]. + pub settings: CanisterSettings, +} + +/// Complete argument type of `update_settings`. +/// +/// Please note that this type is not used directly as the argument of [`update_settings`]. +/// The function [`update_settings`] takes [`UpdateSettingsArgs`] instead. +/// +/// If you want to manually call `update_settings` (construct and invoke a [Call]), you should use this complete type. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct UpdateSettingsArgsComplete { + /// Canister ID. + pub canister_id: CanisterId, + /// See [CanisterSettings]. + pub settings: CanisterSettings, + /// sender_canister_version must be set to [`canister_version`]. + pub sender_canister_version: Option, +} + +// update_settings END -------------------------------------------------------- + +// upload_chunk --------------------------------------------------------------- + +/// Upload a chunk to the chunk store of a canister. +/// +/// See [IC method `upload_chunk`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-upload_chunk). +pub async fn upload_chunk(arg: UploadChunkArgs) -> CallResult { + Call::new(Principal::management_canister(), "upload_chunk") + .with_arg(arg) + .call() + .await +} + +/// Argument type of [`upload_chunk`]. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct UploadChunkArgs { + /// The canister whose chunk store the chunk will be uploaded to. + pub canister_id: CanisterId, + /// The chunk bytes (max size 1MB). + #[serde(with = "serde_bytes")] + pub chunk: Vec, +} + +/// Result type of [`upload_chunk`]. +pub type UploadChunkResult = ChunkHash; + +// upload_chunk END ----------------------------------------------------------- + +// clear_chunk_store ---------------------------------------------------------- + +/// Clear the chunk store of a canister. +/// +/// See [IC method `clear_chunk_store`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-clear_chunk_store). +pub async fn clear_chunk_store(arg: ClearChunkStoreArgs) -> CallResult<()> { + Call::new(Principal::management_canister(), "clear_chunk_store") + .with_arg(arg) + .call() + .await +} + +/// Argument type of [`clear_chunk_store`]. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct ClearChunkStoreArgs { + /// The canister whose chunk store will be cleared. + pub canister_id: CanisterId, +} + +// clear_chunk_store END ------------------------------------------------------ + +// stored_chunks -------------------------------------------------------------- + +/// Get the hashes of all chunks stored in the chunk store of a canister. +/// +/// See [IC method `stored_chunks`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-stored_chunks). +pub async fn stored_chunks(arg: StoredChunksArgs) -> CallResult { + Call::new(Principal::management_canister(), "stored_chunks") + .with_arg(arg) + .call() + .await +} + +/// Argument type of [`stored_chunks`]. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct StoredChunksArgs { + /// The canister whose chunk store will be queried. + pub canister_id: CanisterId, +} + +/// Result type of [`stored_chunks`]. +pub type StoredChunksResult = Vec; + +// stored_chunks END ---------------------------------------------------------- + +// install_code --------------------------------------------------------------- + +/// Install code into a canister. +/// +/// See [IC method `install_code`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-install_code). +pub async fn install_code(arg: InstallCodeArgs) -> CallResult<()> { + let complete_arg = InstallCodeArgsComplete { + mode: arg.mode, + canister_id: arg.canister_id, + wasm_module: arg.wasm_module, + arg: arg.arg, + sender_canister_version: Some(canister_version()), + }; + Call::new(Principal::management_canister(), "install_code") + .with_arg(complete_arg) + .call() + .await +} + +/// The mode with which a canister is installed. +/// +/// This second version of the mode allows someone to specify the +/// optional `SkipPreUpgrade` parameter in case of an upgrade +#[derive( + CandidType, + Serialize, + Deserialize, + Debug, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + Clone, + Copy, + Default, +)] +pub enum CanisterInstallMode { + /// A fresh install of a new canister. + #[default] + #[serde(rename = "install")] + Install, + /// Reinstalling a canister that was already installed. + #[serde(rename = "reinstall")] + Reinstall, + /// Upgrade an existing canister. + #[serde(rename = "upgrade")] + Upgrade(Option), +} + +/// Flags for canister installation with [`CanisterInstallMode::Upgrade`]. +#[derive( + CandidType, + Serialize, + Deserialize, + Debug, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + Clone, + Copy, + Default, +)] +pub struct UpgradeFlags { + /// If set to `true`, the `pre_upgrade` step will be skipped during the canister upgrade. + pub skip_pre_upgrade: Option, + /// If set to `Keep`, the WASM heap memory will be preserved instead of cleared. + pub wasm_memory_persistence: Option, +} + +/// Wasm memory persistence setting for [`UpgradeFlags`]. +#[derive( + CandidType, + Serialize, + Deserialize, + Debug, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + Clone, + Copy, + Default, +)] +pub enum WasmMemoryPersistence { + /// Preserve heap memory. + #[serde(rename = "keep")] + Keep, + /// Clear heap memory. + #[default] + #[serde(rename = "replace")] + Replace, +} + +/// WASM module. +pub type WasmModule = Vec; + +/// Argument type of [`install_code`]. +/// +/// Please note that this type is a reduced version of [`InstallCodeArgsComplete`]. +/// The `sender_canister_version` field is removed as it is set automatically in [`install_code`]. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct InstallCodeArgs { + /// See [CanisterInstallMode]. + pub mode: CanisterInstallMode, + /// Canister ID. + pub canister_id: CanisterId, + /// Code to be installed. + pub wasm_module: WasmModule, + /// The argument to be passed to `canister_init` or `canister_post_upgrade`. + #[serde(with = "serde_bytes")] + pub arg: Vec, +} + +/// Complete argument type of `install_code`. +/// +/// Please note that this type is not used directly as the argument of [`install_code`]. +/// The function [`install_code`] takes [`InstallCodeArgs`] instead. +/// +/// If you want to manually call `install_code` (construct and invoke a [`Call`]), you should use this complete type. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct InstallCodeArgsComplete { + /// See [`CanisterInstallMode`]. + pub mode: CanisterInstallMode, + /// Canister ID. + pub canister_id: CanisterId, + /// Code to be installed. + pub wasm_module: WasmModule, + /// The argument to be passed to `canister_init` or `canister_post_upgrade`. + #[serde(with = "serde_bytes")] + pub arg: Vec, + /// sender_canister_version must be set to [`canister_version`]. + pub sender_canister_version: Option, +} + +// install_code END ----------------------------------------------------------- + +// install_chunked_code ------------------------------------------------------- + +/// Install code into a canister where the code has previously been uploaded in chunks. +/// +/// See [IC method `install_chunked_code`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-install_chunked_code). +pub async fn install_chunked_code(arg: InstallChunkedCodeArgs) -> CallResult<()> { + let complete_arg = InstallChunkedCodeArgsComplete { + mode: arg.mode, + target_canister: arg.target_canister, + store_canister: arg.store_canister, + chunk_hashes_list: arg.chunk_hashes_list, + wasm_module_hash: arg.wasm_module_hash, + arg: arg.arg, + sender_canister_version: Some(canister_version()), + }; + Call::new(Principal::management_canister(), "install_chunked_code") + .with_arg(complete_arg) + .call() + .await +} + +/// Argument type of [`install_chunked_code`]. +/// +/// Please note that this type is a reduced version of [`InstallChunkedCodeArgsComplete`]. +/// The `sender_canister_version` field is removed as it is set automatically in [`install_chunked_code`]. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct InstallChunkedCodeArgs { + /// See [`CanisterInstallMode`]. + pub mode: CanisterInstallMode, + /// Principal of the canister being installed. + pub target_canister: CanisterId, + /// The canister in whose chunk storage the chunks are stored (defaults to target_canister if not specified). + pub store_canister: Option, + /// The list of chunks that make up the canister wasm. + pub chunk_hashes_list: Vec, + /// The sha256 hash of the wasm. + #[serde(with = "serde_bytes")] + pub wasm_module_hash: Vec, + /// The argument to be passed to `canister_init` or `canister_post_upgrade`. + #[serde(with = "serde_bytes")] + pub arg: Vec, +} + +/// Complete argument type of `install_chunked_code`. +/// +/// Please note that this type is not used directly as the argument of [`install_chunked_code`]. +/// The function [`install_chunked_code`] takes [`InstallChunkedCodeArgs`] instead. +/// +/// If you want to manually call `install_chunked_code` (construct and invoke a [`Call`]), you should use this complete type. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct InstallChunkedCodeArgsComplete { + /// See [`CanisterInstallMode`]. + pub mode: CanisterInstallMode, + /// Principal of the canister being installed. + pub target_canister: CanisterId, + /// The canister in whose chunk storage the chunks are stored (defaults to target_canister if not specified). + pub store_canister: Option, + /// The list of chunks that make up the canister wasm. + pub chunk_hashes_list: Vec, + /// The sha256 hash of the wasm. + #[serde(with = "serde_bytes")] + pub wasm_module_hash: Vec, + /// The argument to be passed to `canister_init` or `canister_post_upgrade`. + #[serde(with = "serde_bytes")] + pub arg: Vec, + /// sender_canister_version must be set to [`canister_version`]. + pub sender_canister_version: Option, +} + +// install_chunked_code END --------------------------------------------------- + +// uninstall_code ------------------------------------------------------------- + +/// Remove a canister's code and state, making the canister empty again. +/// +/// See [IC method `uninstall_code`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-uninstall_code). +pub async fn uninstall_code(arg: UninstallCodeArgs) -> CallResult<()> { + let complete_arg = UninstallCodeArgsComplete { + canister_id: arg.canister_id, + sender_canister_version: Some(canister_version()), + }; + Call::new(Principal::management_canister(), "uninstall_code") + .with_arg(complete_arg) + .call() + .await +} + +/// Argument type of [`uninstall_code`]. +/// +/// Please note that this type is a reduced version of [`UninstallCodeArgsComplete`]. +/// The `sender_canister_version` field is removed as it is set automatically in [`uninstall_code`]. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct UninstallCodeArgs { + /// Canister ID. + pub canister_id: CanisterId, +} + +/// Complete argument type of `uninstall_code`. +/// +/// Please note that this type is not used directly as the argument of [`uninstall_code`]. +/// The function [`uninstall_code`] takes [`UninstallCodeArgs`] instead. +/// +/// If you want to manually call `uninstall_code` (construct and invoke a [Call]), you should use this complete type. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct UninstallCodeArgsComplete { + /// Canister ID. + pub canister_id: CanisterId, + /// sender_canister_version must be set to [`canister_version`]. + pub sender_canister_version: Option, +} + +// uninstall_code END --------------------------------------------------------- + +// start_canister ------------------------------------------------------------- + +/// Start a canister if the canister status was `stopped` or `stopping`. +/// +/// See [IC method `start_canister`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-start_canister). +pub async fn start_canister(arg: StartCanisterArgs) -> CallResult<()> { + Call::new(Principal::management_canister(), "start_canister") + .with_arg(arg) + .call() + .await +} + +/// Argument type of [`start_canister`]. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct StartCanisterArgs { + /// Canister ID. + pub canister_id: CanisterId, +} + +// start_canister END --------------------------------------------------------- + +// stop_canister -------------------------------------------------------------- + +/// Stop a canister. +/// +/// See [IC method `stop_canister`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-stop_canister). +pub async fn stop_canister(arg: StopCanisterArgs) -> CallResult<()> { + Call::new(Principal::management_canister(), "stop_canister") + .with_arg(arg) + .call() + .await +} + +/// Argument type of [`stop_canister`]. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct StopCanisterArgs { + /// Canister ID. + pub canister_id: CanisterId, +} + +// stop_canister END ---------------------------------------------------------- + +// canister_status ------------------------------------------------------------ + +/// Get status information about the canister. +/// +/// See [IC method `canister_status`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-canister_status). +pub async fn canister_status(arg: CanisterStatusArgs) -> CallResult { + Call::new(Principal::management_canister(), "canister_status") + .with_arg(arg) + .call() + .await +} + +/// Argument type of [`canister_status`]. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct CanisterStatusArgs { + /// Canister ID. + pub canister_id: CanisterId, +} + +/// Return type of [`canister_status`]. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct CanisterStatusResult { + /// See [CanisterStatusType]. + pub status: CanisterStatusType, + /// See [DefiniteCanisterSettings]. + pub settings: DefiniteCanisterSettings, + /// A SHA256 hash of the module installed on the canister. This is null if the canister is empty. + pub module_hash: Option>, + /// The memory size taken by the canister. + pub memory_size: Nat, + /// The cycle balance of the canister. + pub cycles: Nat, + /// The reserved cycles balance of the canister. + /// These are cycles that are reserved by the resource reservation mechanism + /// on storage allocation. See also the `reserved_cycles_limit` parameter in + /// canister settings. + pub reserved_cycles: Nat, + /// Amount of cycles burned per day. + pub idle_cycles_burned_per_day: Nat, + /// Query statistics. + pub query_stats: QueryStats, +} + +/// Status of a canister. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, +)] +pub enum CanisterStatusType { + /// The canister is running. + #[serde(rename = "running")] + Running, + /// The canister is stopping. + #[serde(rename = "stopping")] + Stopping, + /// The canister is stopped. + #[serde(rename = "stopped")] + Stopped, +} + +/// Query statistics, returned by [`canister_status`]. +#[derive( + CandidType, Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, +)] +pub struct QueryStats { + /// Total number of query calls. + pub num_calls_total: Nat, + /// Total number of instructions executed by query calls. + pub num_instructions_total: Nat, + /// Total number of payload bytes use for query call requests. + pub request_payload_bytes_total: Nat, + /// Total number of payload bytes use for query call responses. + pub response_payload_bytes_total: Nat, +} + +// canister_status END -------------------------------------------------------- + +// canister_info -------------------------------------------------------------- + +/// Get public information about the canister. +/// +/// See [IC method `canister_info`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-canister_info). +pub async fn canister_info(arg: CanisterInfoArgs) -> CallResult { + Call::new(Principal::management_canister(), "canister_info") + .with_arg(arg) + .call() + .await +} + +/// Argument type of [`canister_info`]. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct CanisterInfoArgs { + /// Canister ID. + pub canister_id: Principal, + /// Number of most recent changes requested to be retrieved from canister history. + /// No changes are retrieved if this field is null. + pub num_requested_changes: Option, +} + +/// Return type of [`canister_info`]. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct CanisterInfoResult { + /// Total number of changes ever recorded in canister history. + /// This might be higher than the number of canister changes in `recent_changes` + /// because the IC might drop old canister changes from its history + /// (with `20` most recent canister changes to always remain in the list). + pub total_num_changes: u64, + /// The canister changes stored in the order from the oldest to the most recent. + pub recent_changes: Vec, + /// A SHA256 hash of the module installed on the canister. This is null if the canister is empty. + pub module_hash: Option>, + /// Controllers of the canister. + pub controllers: Vec, +} + +/// Details about a canister change initiated by a user. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct FromUserRecord { + /// Principal of the user. + pub user_id: Principal, +} + +/// Details about a canister change initiated by a canister (called _originator_). +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct FromCanisterRecord { + /// Canister ID of the originator. + pub canister_id: Principal, + /// Canister version of the originator when the originator initiated the change. + /// This is null if the original does not include its canister version + /// in the field `sender_canister_version` of the management canister payload. + pub canister_version: Option, +} + +/// Provides details on who initiated a canister change. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub enum CanisterChangeOrigin { + /// See [`FromUserRecord`]. + #[serde(rename = "from_user")] + FromUser(FromUserRecord), + /// See [`FromCanisterRecord`]. + #[serde(rename = "from_canister")] + FromCanister(FromCanisterRecord), +} + +/// Details about a canister creation. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct CreationRecord { + /// Initial set of canister controllers. + pub controllers: Vec, +} + +/// The mode with which a canister is installed. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, +)] +pub enum CodeDeploymentMode { + /// A fresh install of a new canister. + #[serde(rename = "install")] + Install, + /// Reinstalling a canister that was already installed. + #[serde(rename = "reinstall")] + Reinstall, + /// Upgrade an existing canister. + #[serde(rename = "upgrade")] + Upgrade, +} + +/// Details about a canister code deployment. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct CodeDeploymentRecord { + /// See [`CodeDeploymentMode`]. + pub mode: CodeDeploymentMode, + /// A SHA256 hash of the new module installed on the canister. + #[serde(with = "serde_bytes")] + pub module_hash: Vec, +} + +/// Details about loading canister snapshot. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct LoadSnapshotRecord { + /// The version of the canister at the time that the snapshot was taken + pub canister_version: u64, + /// The ID of the snapshot that was loaded. + pub snapshot_id: SnapshotId, + /// The timestamp at which the snapshot was taken. + pub taken_at_timestamp: u64, +} + +/// Details about updating canister controllers. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct ControllersChangeRecord { + /// The full new set of canister controllers. + pub controllers: Vec, +} + +/// Provides details on the respective canister change. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub enum CanisterChangeDetails { + /// See [`CreationRecord`]. + #[serde(rename = "creation")] + Creation(CreationRecord), + /// Uninstalling canister's module. + #[serde(rename = "code_uninstall")] + CodeUninstall, + /// See [`CodeDeploymentRecord`]. + #[serde(rename = "code_deployment")] + CodeDeployment(CodeDeploymentRecord), + /// See [`LoadSnapshotRecord`]. + #[serde(rename = "load_snapshot")] + LoadSnapshot(LoadSnapshotRecord), + /// See [`ControllersChangeRecord`]. + #[serde(rename = "controllers_change")] + ControllersChange(ControllersChangeRecord), +} + +/// Represents a canister change as stored in the canister history. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct CanisterChange { + /// The system timestamp (in nanoseconds since Unix Epoch) at which the change was performed. + pub timestamp_nanos: u64, + /// The canister version after performing the change. + pub canister_version: u64, + /// The change's origin (a user or a canister). + pub origin: CanisterChangeOrigin, + /// The change's details. + pub details: CanisterChangeDetails, +} + +// canister_info END ---------------------------------------------------------- + +// delete_canister ------------------------------------------------------------ + +/// Delete a canister. +/// +/// See [IC method `delete_canister`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-delete_canister). +pub async fn delete_canister(arg: DeleteCanisterArgs) -> CallResult<()> { + Call::new(Principal::management_canister(), "delete_canister") + .with_arg(arg) + .call() + .await +} + +/// Argument type of [`delete_canister`]. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct DeleteCanisterArgs { + /// Canister ID. + pub canister_id: CanisterId, +} + +// delete_canister END -------------------------------------------------------- + +// deposit_cycles ------------------------------------------------------------- + +/// Deposit cycles to a canister. +/// +/// See [IC method `deposit_cycles`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-deposit_cycles). +pub async fn deposit_cycles(arg: DepositCyclesArgs, cycles: u128) -> CallResult<()> { + Call::new(Principal::management_canister(), "deposit_cycles") + .with_arg(arg) + .with_guaranteed_response() + .with_cycles(cycles) + .call() + .await +} + +/// Argument type of [`deposit_cycles`]. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct DepositCyclesArgs { + /// Canister ID. + pub canister_id: CanisterId, +} + +// deposit_cycles END --------------------------------------------------------- + +// raw_rand ------------------------------------------------------------------- + +// Get 32 pseudo-random bytes. +/// +/// See [IC method `raw_rand`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-raw_rand). +pub async fn raw_rand() -> CallResult> { + Call::new(Principal::management_canister(), "raw_rand") + .call() + .await +} + +// raw_rand END --------------------------------------------------------------- + +// http_request --------------------------------------------------------------- + +/// Make an HTTP request to a given URL and return the HTTP response, possibly after a transformation. +/// +/// See [IC method `http_request`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-http_request). +/// +/// This call requires cycles payment. The required cycles is a function of the request size and max_response_bytes. +/// Check [HTTPS outcalls cycles cost](https://internetcomputer.org/docs/current/developer-docs/gas-cost#https-outcalls) for more details. +pub async fn http_request(arg: HttpRequestArgs, cycles: u128) -> CallResult { + Call::new(Principal::management_canister(), "http_request") + .with_arg(arg) + .with_guaranteed_response() + .with_cycles(cycles) + .call() + .await +} + +/// Argument type of [`http_request`]. +#[derive(CandidType, Deserialize, Debug, PartialEq, Eq, Clone, Default)] +pub struct HttpRequestArgs { + /// The requested URL. + pub url: String, + /// The maximal size of the response in bytes. If None, 2MiB will be the limit. + /// This value affects the cost of the http request and it is highly recommended + /// to set it as low as possible to avoid unnecessary extra costs. + /// See also the [pricing section of HTTP outcalls documentation](https://internetcomputer.org/docs/current/developer-docs/integrations/http_requests/http_requests-how-it-works#pricing). + pub max_response_bytes: Option, + /// The method of HTTP request. + pub method: HttpMethod, + /// List of HTTP request headers and their corresponding values. + pub headers: Vec, + /// Optionally provide request body. + pub body: Option>, + /// Name of the transform function which is `func (transform_args) -> (http_response) query`. + /// Set to `None` if you are using `http_request_with` or `http_request_with_cycles_with`. + pub transform: Option, +} + +/// The returned HTTP response. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, +)] +pub struct HttpRequestResult { + /// The response status (e.g., 200, 404). + pub status: candid::Nat, + /// List of HTTP response headers and their corresponding values. + pub headers: Vec, + /// The response’s body. + #[serde(with = "serde_bytes")] + pub body: Vec, +} + +/// HTTP method. +#[derive( + CandidType, + Serialize, + Deserialize, + Debug, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + Clone, + Copy, + Default, +)] +pub enum HttpMethod { + /// GET + #[default] + #[serde(rename = "get")] + GET, + /// POST + #[serde(rename = "post")] + POST, + /// HEAD + #[serde(rename = "head")] + HEAD, +} +/// HTTP header. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, +)] +pub struct HttpHeader { + /// Name + pub name: String, + /// Value + pub value: String, +} + +/// ```text +/// record { +/// function : func(record { response : http_request_result; context : blob }) -> (http_request_result) query; +/// context : blob; +/// }; +/// ``` +#[derive(CandidType, Clone, Debug, Deserialize, PartialEq, Eq)] +pub struct TransformContext { + /// `func(record { response : http_request_result; context : blob }) -> (http_request_result) query;`. + pub function: TransformFunc, + + /// Context to be passed to `transform` function to transform HTTP response for consensus + #[serde(with = "serde_bytes")] + pub context: Vec, +} + +impl TransformContext { + /// Constructs a [`TransformContext`] from a query method name and context. + /// The principal is assumed to be the ID of current canister. + pub fn from_name(candid_function_name: String, context: Vec) -> Self { + Self { + context, + function: TransformFunc(candid::Func { + method: candid_function_name, + principal: crate::api::canister_self(), + }), + } + } +} + +mod transform_func { + #![allow(missing_docs)] + use super::{HttpRequestResult, TransformArgs}; + candid::define_function!(pub TransformFunc : (TransformArgs) -> (HttpRequestResult) query); +} + +/// "transform" function of type: `func(record { response : http_request_result; context : blob }) -> (http_request_result) query` +pub use transform_func::TransformFunc; + +/// Type used for encoding/decoding: +/// `record { +/// response : http_response; +/// context : blob; +/// }` +#[derive(CandidType, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct TransformArgs { + /// Raw response from remote service, to be transformed + pub response: HttpRequestResult, + + /// Context for response transformation + #[serde(with = "serde_bytes")] + pub context: Vec, +} + +#[cfg(feature = "transform-closure")] +mod transform_closure { + use super::{ + http_request, CallResult, HttpRequestArgs, HttpRequestResult, Principal, TransformArgs, + TransformContext, + }; + use candid::{decode_one, encode_one}; + use slotmap::{DefaultKey, Key, KeyData, SlotMap}; + use std::cell::RefCell; + + thread_local! { + #[allow(clippy::type_complexity)] + static TRANSFORMS: RefCell HttpRequestResult>>> = RefCell::default(); + } + + #[export_name = "canister_query http_transform"] + extern "C" fn http_transform() { + use crate::api::{msg_arg_data, msg_caller, msg_reply}; + if msg_caller() != Principal::management_canister() { + crate::trap("This function is internal to ic-cdk and should not be called externally."); + } + crate::setup(); + let arg_bytes = msg_arg_data(); + let transform_args: TransformArgs = decode_one(&arg_bytes).unwrap(); + let int = u64::from_be_bytes(transform_args.context[..].try_into().unwrap()); + let key = DefaultKey::from(KeyData::from_ffi(int)); + let func = TRANSFORMS.with(|transforms| transforms.borrow_mut().remove(key)); + let Some(func) = func else { + crate::trap(format!("Missing transform function for request {int}")); + }; + let transformed = func(transform_args.response); + let encoded = encode_one(transformed).unwrap(); + msg_reply(encoded); + } + + /// Make an HTTP request to a given URL and return the HTTP response, after a transformation. + /// + /// Do not set the `transform` field of `arg`. To use a Candid function, call [`http_request`] instead. + /// + /// See [IC method `http_request`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-http_request). + /// + /// This call requires cycles payment. The required cycles is a function of the request size and max_response_bytes. + /// Check [Gas and cycles cost](https://internetcomputer.org/docs/current/developer-docs/gas-cost) for more details. + #[cfg_attr(docsrs, doc(cfg(feature = "transform-closure")))] + pub async fn http_request_with_closure( + arg: HttpRequestArgs, + cycles: u128, + transform_func: impl FnOnce(HttpRequestResult) -> HttpRequestResult + 'static, + ) -> CallResult { + assert!( + arg.transform.is_none(), + "The `transform` field in `HttpRequestArgs` must be `None` when using a closure" + ); + let transform_func = Box::new(transform_func) as _; + let key = TRANSFORMS.with(|transforms| transforms.borrow_mut().insert(transform_func)); + struct DropGuard(DefaultKey); + impl Drop for DropGuard { + fn drop(&mut self) { + TRANSFORMS.with(|transforms| transforms.borrow_mut().remove(self.0)); + } + } + let key = DropGuard(key); + let context = key.0.data().as_ffi().to_be_bytes().to_vec(); + let arg = HttpRequestArgs { + transform: Some(TransformContext::from_name( + " http_transform".to_string(), + context, + )), + ..arg + }; + http_request(arg, cycles).await + } +} + +#[cfg(feature = "transform-closure")] +pub use transform_closure::http_request_with_closure; + +// http_request END ----------------------------------------------------------- + +// # Threshold ECDSA signature ================================================ + +/// ECDSA KeyId. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, +)] +pub struct EcdsaKeyId { + /// See [`EcdsaCurve`]. + pub curve: EcdsaCurve, + /// Name. + pub name: String, +} + +/// ECDSA Curve. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, +)] +pub enum EcdsaCurve { + /// secp256k1 + #[serde(rename = "secp256k1")] + Secp256k1, +} + +impl Default for EcdsaCurve { + fn default() -> Self { + Self::Secp256k1 + } +} + +// ecdsa_public_key ----------------------------------------------------------- + +/// Return a SEC1 encoded ECDSA public key for the given canister using the given derivation path. +/// +/// See [IC method `ecdsa_public_key`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-ecdsa_public_key). +pub async fn ecdsa_public_key(arg: EcdsaPublicKeyArgs) -> CallResult { + Call::new(Principal::management_canister(), "ecdsa_public_key") + .with_arg(arg) + .call() + .await +} + +/// Argument type of [`ecdsa_public_key`]. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, +)] +pub struct EcdsaPublicKeyArgs { + /// Canister id, default to the canister id of the caller if None. + pub canister_id: Option, + /// A vector of variable length byte strings. + pub derivation_path: Vec>, + /// See [EcdsaKeyId]. + pub key_id: EcdsaKeyId, +} + +/// Response Type of [`ecdsa_public_key`]. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, +)] +pub struct EcdsaPublicKeyResult { + /// An ECDSA public key encoded in SEC1 compressed form. + #[serde(with = "serde_bytes")] + pub public_key: Vec, + /// Can be used to deterministically derive child keys of the public_key. + #[serde(with = "serde_bytes")] + pub chain_code: Vec, +} + +// ecda_public_key END -------------------------------------------------------- + +// sign_with_ecdsa ------------------------------------------------------------ + +/// Return a new ECDSA signature of the given message_hash that can be separately verified against a derived ECDSA public key. +/// +/// See [IC method `sign_with_ecdsa`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-sign_with_ecdsa). +/// +/// This call requires cycles payment. +/// This method handles the cycles cost under the hood. +/// Check [Threshold signatures](https://internetcomputer.org/docs/current/references/t-sigs-how-it-works) for more details. +pub async fn sign_with_ecdsa(arg: SignWithEcdsaArgs) -> CallResult { + Call::new(Principal::management_canister(), "sign_with_ecdsa") + .with_arg(arg) + .with_guaranteed_response() + .with_cycles(SIGN_WITH_ECDSA_FEE) + .call() + .await +} + +/// https://internetcomputer.org/docs/current/references/t-sigs-how-it-works#fees-for-the-t-ecdsa-production-key +const SIGN_WITH_ECDSA_FEE: u128 = 26_153_846_153; + +/// Argument type of [`sign_with_ecdsa`]. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, +)] +pub struct SignWithEcdsaArgs { + /// Hash of the message with length of 32 bytes. + #[serde(with = "serde_bytes")] + pub message_hash: Vec, + /// A vector of variable length byte strings. + pub derivation_path: Vec>, + /// See [EcdsaKeyId]. + pub key_id: EcdsaKeyId, +} + +/// Response type of [`sign_with_ecdsa`]. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, +)] +pub struct SignWithEcdsaResult { + /// Encoded as the concatenation of the SEC1 encodings of the two values r and s. + #[serde(with = "serde_bytes")] + pub signature: Vec, +} + +// sign_with_ecdsa END -------------------------------------------------------- + +// # Threshold ECDSA signature END ============================================ + +// # Threshold Schnorr signature ============================================== + +/// Schnorr KeyId. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, +)] +pub struct SchnorrKeyId { + /// See [`SchnorrAlgorithm`]. + pub algorithm: SchnorrAlgorithm, + /// Name. + pub name: String, +} + +/// Schnorr Algorithm. +#[derive( + CandidType, + Serialize, + Deserialize, + Debug, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + Clone, + Copy, + Default, +)] +pub enum SchnorrAlgorithm { + /// BIP-340 secp256k1. + #[serde(rename = "bip340secp256k1")] + #[default] + Bip340secp256k1, + /// ed25519. + #[serde(rename = "ed25519")] + Ed25519, +} + +// schnorr_public_key ---------------------------------------------------------- + +/// Return a SEC1 encoded Schnorr public key for the given canister using the given derivation path. +/// +/// See [IC method `schnorr_public_key`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-schnorr_public_key). +pub async fn schnorr_public_key(arg: SchnorrPublicKeyArgs) -> CallResult { + Call::new(Principal::management_canister(), "schnorr_public_key") + .with_arg(arg) + .call() + .await +} + +/// Argument Type of [`schnorr_public_key`]. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, +)] +pub struct SchnorrPublicKeyArgs { + /// Canister id, default to the canister id of the caller if None. + pub canister_id: Option, + /// A vector of variable length byte strings. + pub derivation_path: Vec>, + /// See [SchnorrKeyId]. + pub key_id: SchnorrKeyId, +} + +/// Response Type of [`schnorr_public_key`]. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, +)] +pub struct SchnorrPublicKeyResult { + /// An Schnorr public key encoded in SEC1 compressed form. + #[serde(with = "serde_bytes")] + pub public_key: Vec, + /// Can be used to deterministically derive child keys of the public_key. + #[serde(with = "serde_bytes")] + pub chain_code: Vec, +} + +// schnorr_public_key END ----------------------------------------------------- + +// sign_with_schnorr ---------------------------------------------------------- + +/// Return a new Schnorr signature of the given message that can be separately verified against a derived Schnorr public key. +/// +/// See [IC method `sign_with_schnorr`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-sign_with_schnorr). +/// +/// This call requires cycles payment. +/// This method handles the cycles cost under the hood. +/// Check [Threshold signatures](https://internetcomputer.org/docs/current/references/t-sigs-how-it-works) for more details. +pub async fn sign_with_schnorr(arg: SignWithSchnorrArgs) -> CallResult { + Call::new(Principal::management_canister(), "sign_with_schnorr") + .with_arg(arg) + .with_guaranteed_response() + .with_cycles(SIGN_WITH_SCHNORR_FEE) + .call() + .await +} + +/// https://internetcomputer.org/docs/current/references/t-sigs-how-it-works/#fees-for-the-t-schnorr-production-key +const SIGN_WITH_SCHNORR_FEE: u128 = 26_153_846_153; + +/// Argument Type of [`sign_with_schnorr`]. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, +)] +pub struct SignWithSchnorrArgs { + /// Message to be signed. + #[serde(with = "serde_bytes")] + pub message: Vec, + /// A vector of variable length byte strings. + pub derivation_path: Vec>, + /// See [SchnorrKeyId]. + pub key_id: SchnorrKeyId, +} + +/// Response Type of [`sign_with_schnorr`]. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, +)] +pub struct SignWithSchnorrResult { + /// The encoding of the signature depends on the key ID's algorithm. + #[serde(with = "serde_bytes")] + pub signature: Vec, +} + +// sign_with_schnorr END ------------------------------------------------------ + +// # Threshold Schnorr signature END ========================================== + +// node_metrics_history ------------------------------------------------------- + +/// Get a time series of node metrics of a subnet. +/// +/// See [IC method `node_metrics_history`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-node-metrics-history). +pub async fn node_metrics_history( + arg: NodeMetricsHistoryArgs, +) -> CallResult { + Call::new(Principal::management_canister(), "node_metrics_history") + .with_arg(arg) + .call() + .await +} + +/// Argument type of [`node_metrics_history`]. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct NodeMetricsHistoryArgs { + /// Subnet ID. + pub subnet_id: Principal, + /// The returned time series will start at this timestamp. + pub start_at_timestamp_nanos: u64, +} + +/// Return type of [`node_metrics_history`]. +pub type NodeMetricsHistoryResult = Vec; + +/// A record in [`NodeMetricsHistoryResult`]. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct NodeMetricsHistoryRecord { + /// The timestamp of the record. + pub timestamp_nanos: u64, + /// The metrics of the nodes. + pub node_metrics: Vec, +} + +/// Node metrics. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct NodeMetrics { + /// The principal characterizing a node. + pub node_id: Principal, + /// The number of blocks proposed by this node. + pub num_blocks_proposed_total: u64, + /// The number of failed block proposals by this node. + pub num_blocks_failures_total: u64, +} + +// node_metrics_history END --------------------------------------------------- + +// subnet_info ---------------------------------------------------------------- + +/// Get subnet metadata. +/// +/// See [IC method `subnet_info`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-subnet-info). +pub async fn subnet_info(arg: SubnetInfoArgs) -> CallResult { + Call::new(Principal::management_canister(), "subnet_info") + .with_arg(arg) + .call() + .await +} + +/// Argument type of [`subnet_info`]. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct SubnetInfoArgs { + /// Subnet ID. + pub subnet_id: Principal, +} + +/// Result type of [`subnet_info`]. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct SubnetInfoResult { + /// Replica version of the subnet. + pub replica_version: String, +} + +// subnet_info END ------------------------------------------------------------ + +// # provisional interfaces for the pre-ledger world ========================== + +// provisional_create_canister_with_cycles ------------------------------------ + +/// Create a new canister with specified amount of cycles balance. +/// +/// This method is only available in local development instances. +/// +/// See [IC method `provisional_create_canister_with_cycles`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-provisional_create_canister_with_cycles). +pub async fn provisional_create_canister_with_cycles( + arg: ProvisionalCreateCanisterWithCyclesArgs, +) -> CallResult { + Call::new( + Principal::management_canister(), + "provisional_create_canister_with_cycles", + ) + .with_arg(arg) + .with_guaranteed_response() + .call() + .await +} + +/// Argument type of [`provisional_create_canister_with_cycles`]. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, +)] +pub struct ProvisionalCreateCanisterWithCyclesArgs { + /// The created canister will have this amount of cycles. + pub amount: Option, + /// See [CanisterSettings]. + pub settings: Option, +} + +/// Result type of [`provisional_create_canister_with_cycles`]. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct ProvisionalCreateCanisterWithCyclesResult { + /// Canister ID of the created canister. + pub canister_id: CanisterId, +} + +// provisional_delete_canister_with_cycles END -------------------------------- + +// provisional_top_up_canister ------------------------------------------------ + +/// Add cycles to a canister. +/// +/// This method is only available in local development instances. +/// +/// See [IC method `provisional_top_up_canister`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-provisional_top_up_canister). +pub async fn provisional_top_up_canister(arg: ProvisionalTopUpCanisterArgs) -> CallResult<()> { + Call::new( + Principal::management_canister(), + "provisional_top_up_canister", + ) + .with_arg(arg) + .with_guaranteed_response() + .call() + .await +} + +/// Argument type of [`provisional_top_up_canister`]. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct ProvisionalTopUpCanisterArgs { + /// Canister ID. + pub canister_id: CanisterId, + /// Amount of cycles to be added. + pub amount: Nat, +} + +// provisional_top_up_canister END -------------------------------------------- + +// # provisional interfaces for the pre-ledger world END ====================== + +// # Canister snapshots ======================================================= + +/// Snapshot ID. +pub type SnapshotId = Vec; + +/// A snapshot of the state of the canister at a given point in time. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, +)] +pub struct Snapshot { + /// ID of the snapshot. + pub id: SnapshotId, + /// The timestamp at which the snapshot was taken. + pub taken_at_timestamp: u64, + /// The size of the snapshot in bytes. + pub total_size: u64, +} + +// take_canister_snapshot ----------------------------------------------------- + +/// Take a snapshot of the specified canister. +/// +/// A snapshot consists of the wasm memory, stable memory, certified variables, wasm chunk store and wasm binary. +/// +/// See [IC method `take_canister_snapshot`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-take_canister_snapshot). +pub async fn take_canister_snapshot( + arg: TakeCanisterSnapshotArgs, +) -> CallResult { + Call::new(Principal::management_canister(), "take_canister_snapshot") + .with_arg(arg) + .with_guaranteed_response() + .call() + .await +} + +/// Argument type of [`take_canister_snapshot`]. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct TakeCanisterSnapshotArgs { + /// Canister ID. + pub canister_id: CanisterId, + /// An optional snapshot ID to be replaced by the new snapshot. + /// + /// The snapshot identified by the specified ID will be deleted once a new snapshot has been successfully created. + pub replace_snapshot: Option, +} + +/// Return type of [`take_canister_snapshot`]. +pub type TakeCanisterSnapshotReturn = Snapshot; + +// take_canister_snapshot END ------------------------------------------------- + +// load_canister_snapshot ----------------------------------------------------- + +/// Load a snapshot onto the canister. +/// +/// It fails if no snapshot with the specified `snapshot_id` can be found. +/// +/// See [IC method `load_canister_snapshot`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-load_canister_snapshot). +pub async fn load_canister_snapshot(arg: LoadCanisterSnapshotArgs) -> CallResult<()> { + let complete_arg = LoadCanisterSnapshotArgsComplete { + canister_id: arg.canister_id, + snapshot_id: arg.snapshot_id, + sender_canister_version: Some(canister_version()), + }; + Call::new(Principal::management_canister(), "load_canister_snapshot") + .with_arg(complete_arg) + .with_guaranteed_response() + .call() + .await +} + +/// Argument type of [`load_canister_snapshot`]. +/// +/// Please note that this type is a reduced version of [`LoadCanisterSnapshotArgsComplete`]. +/// The `sender_canister_version` field is removed as it is set automatically in [`load_canister_snapshot`]. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct LoadCanisterSnapshotArgs { + /// Canister ID. + pub canister_id: CanisterId, + /// ID of the snapshot to be loaded. + pub snapshot_id: SnapshotId, +} + +/// Complete argument type of [`load_canister_snapshot`]. +/// +/// Please note that this type is not used directly as the argument of [`load_canister_snapshot`]. +/// The function [`load_canister_snapshot`] takes [`LoadCanisterSnapshotArgs`] instead. +/// +/// If you want to manually call `load_canister_snapshot` (construct and invoke a [`Call`]), you should use this complete type. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct LoadCanisterSnapshotArgsComplete { + /// Canister ID. + pub canister_id: CanisterId, + /// ID of the snapshot to be loaded. + pub snapshot_id: SnapshotId, + /// sender_canister_version must be set to [`canister_version`]. + pub sender_canister_version: Option, +} + +// load_canister_snapshot END ------------------------------------------------- + +// list_canister_snapshots ---------------------------------------------------- + +/// List the snapshots of the canister. +/// +/// See [IC method `list_canister_snapshots`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-list_canister_snapshots). +pub async fn list_canister_snapshots( + arg: ListCanisterSnapshotsArgs, +) -> CallResult { + Call::new(Principal::management_canister(), "list_canister_snapshots") + .with_arg(arg) + .call() + .await +} + +/// Argument type of [`list_canister_snapshots`]. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct ListCanisterSnapshotsArgs { + /// Canister ID. + pub canister_id: CanisterId, +} + +/// Return type of [`list_canister_snapshots`]. +pub type ListCanisterSnapshotsReturn = Vec; + +// list_canister_snapshots END ------------------------------------------------ + +// delete_canister_snapshot --------------------------------------------------- + +/// Delete a specified snapshot that belongs to an existing canister. +/// +/// An error will be returned if the snapshot is not found. +/// +/// See [IC method `delete_canister_snapshot`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-delete_canister_snapshot). +pub async fn delete_canister_snapshot(arg: DeleteCanisterSnapshotArgs) -> CallResult<()> { + Call::new(Principal::management_canister(), "delete_canister_snapshot") + .with_arg(arg) + .call() + .await +} + +/// Argument type of [`delete_canister_snapshot`]. +#[derive( + CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, +)] +pub struct DeleteCanisterSnapshotArgs { + /// Canister ID. + pub canister_id: CanisterId, + /// ID of the snapshot to be deleted. + pub snapshot_id: SnapshotId, +} + +// delete_canister_snapshot END ----------------------------------------------- + +// # Canister snapshots END =================================================== diff --git a/ic-cdk/src/prelude.rs b/ic-cdk/src/prelude.rs new file mode 100644 index 000000000..d697f1780 --- /dev/null +++ b/ic-cdk/src/prelude.rs @@ -0,0 +1,7 @@ +//! The prelude module contains the most commonly used types and traits. +pub use crate::api::{canister_self, debug_print, msg_caller, trap}; +pub use crate::call::{Call, CallResult, ConfigurableCall, RejectCode, SendableCall}; +pub use crate::macros::{ + export_candid, heartbeat, init, inspect_message, post_upgrade, pre_upgrade, query, update, +}; +pub use crate::{eprintln, println, setup, spawn}; diff --git a/src/ic-cdk/src/printer.rs b/ic-cdk/src/printer.rs similarity index 95% rename from src/ic-cdk/src/printer.rs rename to ic-cdk/src/printer.rs index cfc864e6c..f22fd1707 100644 --- a/src/ic-cdk/src/printer.rs +++ b/ic-cdk/src/printer.rs @@ -17,7 +17,7 @@ pub fn set_panic_hook() { }; let err_info = format!("Panicked at '{}', {}:{}:{}", msg, file, line, col); - api::print(&err_info); + api::debug_print(&err_info); api::trap(&err_info); })); } diff --git a/ic-cdk/src/stable.rs b/ic-cdk/src/stable.rs new file mode 100644 index 000000000..2c1e84e8c --- /dev/null +++ b/ic-cdk/src/stable.rs @@ -0,0 +1,775 @@ +//! APIs to manage stable memory. +//! +//! You can check the [Internet Computer Specification](https://internetcomputer.org/docs/current/references/ic-interface-spec/#system-api-stable-memory) +//! for a in-depth explanation of stable memory. +// mod canister; +// #[cfg(test)] +// mod tests; + +use std::{error, fmt, io}; + +/// WASM page size in bytes. +pub const WASM_PAGE_SIZE_IN_BYTES: u64 = 64 * 1024; // 64KB + +static CANISTER_STABLE_MEMORY: CanisterStableMemory = CanisterStableMemory {}; + +/// A trait defining the stable memory API which each canister running on the IC can make use of +pub trait StableMemory { + /// Gets current size of the stable memory (in WASM pages). + fn stable_size(&self) -> u64; + + /// Attempts to grow the stable memory by `new_pages` (added pages). + /// + /// Returns an error if it wasn't possible. Otherwise, returns the previous + /// size that was reserved. + /// + /// *Note*: Pages are 64KiB in WASM. + fn stable_grow(&self, new_pages: u64) -> Result; + + /// Writes data to the stable memory location specified by an offset. + /// + /// Warning - this will panic if `offset + buf.len()` exceeds the current size of stable memory. + /// Use `stable_grow` to request more stable memory if needed. + fn stable_write(&self, offset: u64, buf: &[u8]); + + /// Reads data from the stable memory location specified by an offset. + fn stable_read(&self, offset: u64, buf: &mut [u8]); +} + +/// A possible error value when dealing with stable memory. +#[derive(Debug)] +pub enum StableMemoryError { + /// No more stable memory could be allocated. + OutOfMemory, + /// Attempted to read more stable memory than had been allocated. + OutOfBounds, +} + +impl fmt::Display for StableMemoryError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::OutOfMemory => f.write_str("Out of memory"), + Self::OutOfBounds => f.write_str("Read exceeds allocated memory"), + } + } +} + +impl error::Error for StableMemoryError {} + +/// A standard implementation of [`StableMemory`]. +/// +/// Useful for creating [`StableWriter`] and [`StableReader`]. +#[derive(Default, Debug, Copy, Clone)] +pub struct CanisterStableMemory {} + +impl StableMemory for CanisterStableMemory { + fn stable_size(&self) -> u64 { + // SAFETY: ic0.stable64_size is always safe to call. + unsafe { ic0::stable64_size() } + } + + fn stable_grow(&self, new_pages: u64) -> Result { + // SAFETY: ic0.stable64_grow is always safe to call. + unsafe { + match ic0::stable64_grow(new_pages) { + u64::MAX => Err(StableMemoryError::OutOfMemory), + x => Ok(x), + } + } + } + + fn stable_write(&self, offset: u64, buf: &[u8]) { + // SAFETY: `buf`, being &[u8], is a readable sequence of bytes, and therefore valid to pass to ic0.stable64_write. + unsafe { + ic0::stable64_write(offset, buf.as_ptr() as u64, buf.len() as u64); + } + } + + fn stable_read(&self, offset: u64, buf: &mut [u8]) { + // SAFETY: `buf`, being &mut [u8], is a writable sequence of bytes, and therefore valid to pass to ic0.stable64_read. + unsafe { + ic0::stable64_read(buf.as_ptr() as u64, offset, buf.len() as u64); + } + } +} + +/// Gets current size of the stable memory (in WASM pages). +pub fn stable_size() -> u64 { + CANISTER_STABLE_MEMORY.stable_size() +} + +/// Attempts to grow the stable memory by `new_pages` (added pages). +/// +/// Returns an error if it wasn't possible. Otherwise, returns the previous +/// size that was reserved. +/// +/// *Note*: Pages are 64KiB in WASM. +pub fn stable_grow(new_pages: u64) -> Result { + CANISTER_STABLE_MEMORY.stable_grow(new_pages) +} + +/// Writes data to the stable memory location specified by an offset. +/// +/// Warning - this will panic if `offset + buf.len()` exceeds the current size of stable memory. +/// Use `stable_grow` to request more stable memory if needed. +pub fn stable_write(offset: u64, buf: &[u8]) { + CANISTER_STABLE_MEMORY.stable_write(offset, buf) +} + +/// Reads data from the stable memory location specified by an offset. +pub fn stable_read(offset: u64, buf: &mut [u8]) { + CANISTER_STABLE_MEMORY.stable_read(offset, buf) +} + +/// Returns a copy of the stable memory. +/// +/// This will map the whole memory (even if not all of it has been written to). +/// +/// # Panics +/// +/// When the bytes of the stable memory cannot fit into a `Vec` which constrained by the usize. +pub fn stable_bytes() -> Vec { + let size = (stable_size() << 16) + .try_into() + .expect("overflow: stable memory too large to read in one go"); + let mut vec = Vec::with_capacity(size); + // SAFETY: + // `vec`, being mutable and allocated to `size` bytes, is safe to pass to ic0.stable_read with no offset. + // ic0.stable_read writes to all of `vec[0..size]`, so `set_len` is safe to call with the new size. + unsafe { + ic0::stable64_read(vec.as_ptr() as u64, 0, size as u64); + vec.set_len(size); + } + vec +} + +/// Performs generic IO (read, write, and seek) on stable memory. +/// +/// Warning: When using write functionality, this will overwrite any existing +/// data in stable memory as it writes, so ensure you set the `offset` value +/// accordingly if you wish to preserve existing data. +/// +/// Will attempt to grow the memory as it writes, +/// and keep offsets and total capacity. +#[derive(Debug)] +pub struct StableIO { + /// The offset of the next write. + offset: u64, + + /// The capacity, in pages. + capacity: u64, + + /// The stable memory to write data to. + memory: M, +} + +impl Default for StableIO { + fn default() -> Self { + Self::with_memory(CanisterStableMemory::default(), 0) + } +} + +impl StableIO { + /// Creates a new `StableIO` which writes to the selected memory + pub fn with_memory(memory: M, offset: u64) -> Self { + let capacity = memory.stable_size(); + Self { + offset, + capacity, + memory, + } + } + + /// Returns the offset of the writer + pub fn offset(&self) -> u64 { + self.offset + } + + /// Attempts to grow the memory by adding new pages. + pub fn grow(&mut self, new_pages: u64) -> Result<(), StableMemoryError> { + let old_page_count = self.memory.stable_grow(new_pages)?; + self.capacity = old_page_count + new_pages; + Ok(()) + } + + /// Writes a byte slice to the buffer. + /// + /// # Errors + /// + /// When it cannot grow the memory to accommodate the new data. + pub fn write(&mut self, buf: &[u8]) -> Result { + let required_capacity_bytes = self.offset + buf.len() as u64; + let required_capacity_pages = + (required_capacity_bytes + WASM_PAGE_SIZE_IN_BYTES - 1) / WASM_PAGE_SIZE_IN_BYTES; + let current_pages = self.capacity; + let additional_pages_required = required_capacity_pages.saturating_sub(current_pages); + + if additional_pages_required > 0 { + self.grow(additional_pages_required)?; + } + + self.memory.stable_write(self.offset, buf); + self.offset += buf.len() as u64; + Ok(buf.len()) + } + + /// Reads data from the stable memory location specified by an offset. + /// + /// # Errors + /// + /// The stable memory size is cached on creation of the StableReader. + /// Therefore, in following scenario, it will get an `OutOfBounds` error: + /// 1. Create a StableReader + /// 2. Write some data to the stable memory which causes it grow + /// 3. call `read()` to read the newly written bytes + pub fn read(&mut self, buf: &mut [u8]) -> Result { + let capacity_bytes = self.capacity * WASM_PAGE_SIZE_IN_BYTES; + let read_buf = if buf.len() as u64 + self.offset > capacity_bytes { + if self.offset < capacity_bytes { + // When usize=u32: + // (capacity_bytes - self.offset) < buf.len() <= u32::MAX == usize::MAX. + // So the cast below won't panic. + &mut buf[..(capacity_bytes - self.offset).try_into().unwrap()] + } else { + return Err(StableMemoryError::OutOfBounds); + } + } else { + buf + }; + self.memory.stable_read(self.offset, read_buf); + self.offset += read_buf.len() as u64; + Ok(read_buf.len()) + } + + // Helper used to implement io::Seek + fn seek(&mut self, offset: io::SeekFrom) -> io::Result { + self.offset = match offset { + io::SeekFrom::Start(offset) => offset, + io::SeekFrom::End(offset) => { + ((self.capacity * WASM_PAGE_SIZE_IN_BYTES) as i64 + offset) as u64 + } + io::SeekFrom::Current(offset) => (self.offset as i64 + offset) as u64, + }; + + Ok(self.offset) + } +} + +impl io::Write for StableIO { + fn write(&mut self, buf: &[u8]) -> Result { + self.write(buf) + .map_err(|e| io::Error::new(io::ErrorKind::OutOfMemory, e)) + } + + fn flush(&mut self) -> Result<(), io::Error> { + // Noop. + Ok(()) + } +} + +impl io::Read for StableIO { + fn read(&mut self, buf: &mut [u8]) -> Result { + Self::read(self, buf).or(Ok(0)) // Read defines EOF to be success + } +} + +impl io::Seek for StableIO { + fn seek(&mut self, offset: io::SeekFrom) -> io::Result { + self.seek(offset) + } +} + +// impl_stable_io!(u32); +// impl_stable_io!(u64); + +/// A writer to the stable memory. +/// +/// Warning: This will overwrite any existing data in stable memory as it writes, so ensure you set +/// the `offset` value accordingly if you wish to preserve existing data. +/// +/// Will attempt to grow the memory as it writes, +/// and keep offsets and total capacity. +#[derive(Debug)] +pub struct StableWriter(StableIO); + +#[allow(clippy::derivable_impls)] +impl Default for StableWriter { + #[inline] + fn default() -> Self { + Self(StableIO::default()) + } +} + +impl StableWriter { + /// Creates a new `StableWriter` which writes to the selected memory + #[inline] + pub fn with_memory(memory: M, offset: u64) -> Self { + Self(StableIO::::with_memory(memory, offset)) + } + + /// Returns the offset of the writer + #[inline] + pub fn offset(&self) -> u64 { + self.0.offset() + } + + /// Attempts to grow the memory by adding new pages. + #[inline] + pub fn grow(&mut self, new_pages: u64) -> Result<(), StableMemoryError> { + self.0.grow(new_pages) + } + + /// Writes a byte slice to the buffer. + /// + /// The only condition where this will + /// error out is if it cannot grow the memory. + #[inline] + pub fn write(&mut self, buf: &[u8]) -> Result { + self.0.write(buf) + } +} + +impl io::Write for StableWriter { + #[inline] + fn write(&mut self, buf: &[u8]) -> Result { + io::Write::write(&mut self.0, buf) + } + + #[inline] + fn flush(&mut self) -> Result<(), io::Error> { + io::Write::flush(&mut self.0) + } +} + +impl io::Seek for StableWriter { + #[inline] + fn seek(&mut self, pos: io::SeekFrom) -> io::Result { + io::Seek::seek(&mut self.0, pos) + } +} + +impl From> for StableWriter { + fn from(io: StableIO) -> Self { + Self(io) + } +} + +/// A writer to the stable memory which first writes the bytes to an in memory buffer and flushes +/// the buffer to stable memory each time it becomes full. +/// +/// Warning: This will overwrite any existing data in stable memory as it writes, so ensure you set +/// the `offset` value accordingly if you wish to preserve existing data. +/// +/// Note: Each call to grow or write to stable memory is a relatively expensive operation, so pick a +/// buffer size large enough to avoid excessive calls to stable memory. +#[derive(Debug)] +pub struct BufferedStableWriter { + inner: io::BufWriter>, +} + +impl BufferedStableWriter { + /// Creates a new `BufferedStableWriter` + pub fn new(buffer_size: usize) -> BufferedStableWriter { + BufferedStableWriter::with_writer(buffer_size, StableWriter::default()) + } +} + +impl BufferedStableWriter { + /// Creates a new `BufferedStableWriter` which writes to the selected memory + pub fn with_writer(buffer_size: usize, writer: StableWriter) -> BufferedStableWriter { + BufferedStableWriter { + inner: io::BufWriter::with_capacity(buffer_size, writer), + } + } + + /// Returns the offset of the writer + pub fn offset(&self) -> u64 { + self.inner.get_ref().offset() + } +} + +impl io::Write for BufferedStableWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.inner.write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + self.inner.flush() + } +} + +impl io::Seek for BufferedStableWriter { + #[inline] + fn seek(&mut self, pos: io::SeekFrom) -> io::Result { + io::Seek::seek(&mut self.inner, pos) + } +} + +// A reader to the stable memory. +/// +/// Keeps an offset and reads off stable memory consecutively. +#[derive(Debug)] +pub struct StableReader(StableIO); + +#[allow(clippy::derivable_impls)] +impl Default for StableReader { + fn default() -> Self { + Self(StableIO::default()) + } +} + +impl StableReader { + /// Creates a new `StableReader` which reads from the selected memory + #[inline] + pub fn with_memory(memory: M, offset: u64) -> Self { + Self(StableIO::::with_memory(memory, offset)) + } + + /// Returns the offset of the reader + #[inline] + pub fn offset(&self) -> u64 { + self.0.offset() + } + + /// Reads data from the stable memory location specified by an offset. + /// + /// Note: + /// The stable memory size is cached on creation of the StableReader. + /// Therefore, in following scenario, it will get an `OutOfBounds` error: + /// 1. Create a StableReader + /// 2. Write some data to the stable memory which causes it grow + /// 3. call `read()` to read the newly written bytes + #[inline] + pub fn read(&mut self, buf: &mut [u8]) -> Result { + self.0.read(buf) + } +} + +impl io::Read for StableReader { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> Result { + io::Read::read(&mut self.0, buf) + } +} + +impl io::Seek for StableReader { + #[inline] + fn seek(&mut self, pos: io::SeekFrom) -> io::Result { + io::Seek::seek(&mut self.0, pos) + } +} + +impl From> for StableReader { + fn from(io: StableIO) -> Self { + Self(io) + } +} + +/// A reader to the stable memory which reads bytes a chunk at a time as each chunk is required. +#[derive(Debug)] +pub struct BufferedStableReader { + inner: io::BufReader>, +} + +impl BufferedStableReader { + /// Creates a new `BufferedStableReader` + pub fn new(buffer_size: usize) -> BufferedStableReader { + BufferedStableReader::with_reader(buffer_size, StableReader::default()) + } +} + +impl BufferedStableReader { + /// Creates a new `BufferedStableReader` which reads from the selected memory + pub fn with_reader(buffer_size: usize, reader: StableReader) -> BufferedStableReader { + BufferedStableReader { + inner: io::BufReader::with_capacity(buffer_size, reader), + } + } + + /// Returns the offset of the reader + pub fn offset(&self) -> u64 { + self.inner.get_ref().offset() + } +} + +impl io::Read for BufferedStableReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.inner.read(buf) + } +} + +impl io::Seek for BufferedStableReader { + #[inline] + fn seek(&mut self, pos: io::SeekFrom) -> io::Result { + io::Seek::seek(&mut self.inner, pos) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::rc::Rc; + use std::sync::Mutex; + + #[derive(Default)] + pub struct TestStableMemory { + memory: Rc>>, + } + + impl TestStableMemory { + pub fn new(memory: Rc>>) -> TestStableMemory { + let bytes_len = memory.lock().unwrap().len(); + if bytes_len > 0 { + let pages_required = pages_required(bytes_len); + let bytes_required = pages_required * WASM_PAGE_SIZE_IN_BYTES; + memory + .lock() + .unwrap() + .resize(bytes_required.try_into().unwrap(), 0); + } + + TestStableMemory { memory } + } + } + + impl StableMemory for TestStableMemory { + fn stable_size(&self) -> u64 { + let bytes_len = self.memory.lock().unwrap().len(); + pages_required(bytes_len) + } + + fn stable_grow(&self, new_pages: u64) -> Result { + let new_bytes = new_pages * WASM_PAGE_SIZE_IN_BYTES; + + let mut vec = self.memory.lock().unwrap(); + let previous_len = vec.len() as u64; + let new_len = vec.len() as u64 + new_bytes; + vec.resize(new_len.try_into().unwrap(), 0); + Ok(previous_len / WASM_PAGE_SIZE_IN_BYTES) + } + + fn stable_write(&self, offset: u64, buf: &[u8]) { + let offset = offset as usize; + + let mut vec = self.memory.lock().unwrap(); + if offset + buf.len() > vec.len() { + panic!("stable memory out of bounds"); + } + vec[offset..(offset + buf.len())].clone_from_slice(buf); + } + + fn stable_read(&self, offset: u64, buf: &mut [u8]) { + let offset = offset as usize; + + let vec = self.memory.lock().unwrap(); + let count_to_copy = buf.len(); + + buf[..count_to_copy].copy_from_slice(&vec[offset..offset + count_to_copy]); + } + } + + fn pages_required(bytes_len: usize) -> u64 { + let page_size = WASM_PAGE_SIZE_IN_BYTES; + (bytes_len as u64 + page_size - 1) / page_size + } + + mod stable_writer_tests { + use super::*; + use rstest::rstest; + use std::io::{Seek, Write}; + + #[rstest] + #[case(None)] + #[case(Some(1))] + #[case(Some(10))] + #[case(Some(100))] + #[case(Some(1000))] + fn write_single_slice(#[case] buffer_size: Option) { + let memory = Rc::new(Mutex::new(Vec::new())); + let mut writer = build_writer(TestStableMemory::new(memory.clone()), buffer_size); + + let bytes = vec![1; 100]; + + writer.write_all(&bytes).unwrap(); + writer.flush().unwrap(); + + let result = &*memory.lock().unwrap(); + + assert_eq!(bytes, result[..bytes.len()]); + } + + #[rstest] + #[case(None)] + #[case(Some(1))] + #[case(Some(10))] + #[case(Some(100))] + #[case(Some(1000))] + fn write_many_slices(#[case] buffer_size: Option) { + let memory = Rc::new(Mutex::new(Vec::new())); + let mut writer = build_writer(TestStableMemory::new(memory.clone()), buffer_size); + + for i in 1..100 { + let bytes = vec![i as u8; i]; + writer.write_all(&bytes).unwrap(); + } + writer.flush().unwrap(); + + let result = &*memory.lock().unwrap(); + + let mut offset = 0; + for i in 1..100 { + let bytes = &result[offset..offset + i]; + assert_eq!(bytes, vec![i as u8; i]); + offset += i; + } + } + + #[rstest] + #[case(None)] + #[case(Some(1))] + #[case(Some(10))] + #[case(Some(100))] + #[case(Some(1000))] + fn ensure_only_requests_min_number_of_pages_required(#[case] buffer_size: Option) { + let memory = Rc::new(Mutex::new(Vec::new())); + let mut writer = build_writer(TestStableMemory::new(memory.clone()), buffer_size); + + let mut total_bytes = 0; + for i in 1..10000 { + let bytes = vec![i as u8; i]; + writer.write_all(&bytes).unwrap(); + total_bytes += i; + } + writer.flush().unwrap(); + + let capacity_pages = TestStableMemory::new(memory).stable_size(); + let min_pages_required = + (total_bytes as u64 + WASM_PAGE_SIZE_IN_BYTES - 1) / WASM_PAGE_SIZE_IN_BYTES; + + assert_eq!(capacity_pages, min_pages_required); + } + + #[test] + fn check_offset() { + const WRITE_SIZE: usize = 1025; + + let memory = Rc::new(Mutex::new(Vec::new())); + let mut writer = StableWriter::with_memory(TestStableMemory::new(memory.clone()), 0); + assert_eq!(writer.offset(), 0); + assert_eq!(writer.write(&vec![0; WRITE_SIZE]).unwrap(), WRITE_SIZE); + assert_eq!(writer.offset(), WRITE_SIZE as u64); + + let mut writer = BufferedStableWriter::with_writer( + WRITE_SIZE - 1, + StableWriter::with_memory(TestStableMemory::new(memory), 0), + ); + assert_eq!(writer.offset(), 0); + assert_eq!(writer.write(&vec![0; WRITE_SIZE]).unwrap(), WRITE_SIZE); + assert_eq!(writer.offset(), WRITE_SIZE as u64); + } + + #[test] + fn test_seek() { + let memory = Rc::new(Mutex::new(Vec::new())); + let mut writer = StableWriter::with_memory(TestStableMemory::new(memory.clone()), 0); + writer + .seek(std::io::SeekFrom::Start(WASM_PAGE_SIZE_IN_BYTES)) + .unwrap(); + assert_eq!(writer.stream_position().unwrap(), WASM_PAGE_SIZE_IN_BYTES); + assert_eq!(writer.write(&[1_u8]).unwrap(), 1); + assert_eq!( + writer.seek(std::io::SeekFrom::End(0)).unwrap(), + WASM_PAGE_SIZE_IN_BYTES * 2 + ); + let capacity_pages = TestStableMemory::new(memory).stable_size(); + assert_eq!(capacity_pages, 2); + } + + fn build_writer(memory: TestStableMemory, buffer_size: Option) -> Box { + let writer = StableWriter::with_memory(memory, 0); + if let Some(buffer_size) = buffer_size { + Box::new(BufferedStableWriter::with_writer(buffer_size, writer)) + } else { + Box::new(writer) + } + } + } + + mod stable_reader_tests { + use super::*; + use rstest::rstest; + use std::io::{Read, Seek}; + + #[rstest] + #[case(None)] + #[case(Some(1))] + #[case(Some(10))] + #[case(Some(100))] + #[case(Some(1000))] + fn reads_all_bytes(#[case] buffer_size: Option) { + let input = vec![1; 10_000]; + let memory = Rc::new(Mutex::new(input.clone())); + let mut reader = build_reader(TestStableMemory::new(memory), buffer_size); + + let mut output = Vec::new(); + reader.read_to_end(&mut output).unwrap(); + + assert_eq!(input, output[..input.len()]); + } + + #[test] + fn check_offset() { + const READ_SIZE: usize = 1025; + + let memory = Rc::new(Mutex::new(vec![1; READ_SIZE])); + let mut reader = StableReader::with_memory(TestStableMemory::new(memory.clone()), 0); + assert_eq!(reader.offset(), 0); + let mut bytes = vec![0; READ_SIZE]; + assert_eq!(reader.read(&mut bytes).unwrap(), READ_SIZE); + assert_eq!(reader.offset(), READ_SIZE as u64); + + let mut reader = BufferedStableReader::with_reader( + READ_SIZE - 1, + StableReader::with_memory(TestStableMemory::new(memory), 0), + ); + assert_eq!(reader.offset(), 0); + let mut bytes = vec![0; READ_SIZE]; + assert_eq!(reader.read(&mut bytes).unwrap(), READ_SIZE); + assert_eq!(reader.offset(), READ_SIZE as u64); + } + + #[test] + fn test_seek() { + const SIZE: usize = 1025; + let memory = Rc::new(Mutex::new((0..SIZE).map(|v| v as u8).collect::>())); + let mut reader = StableReader::with_memory(TestStableMemory::new(memory), 0); + let mut bytes = vec![0_u8; 1]; + + const OFFSET: usize = 200; + reader + .seek(std::io::SeekFrom::Start(OFFSET as u64)) + .unwrap(); + assert_eq!(reader.stream_position().unwrap() as usize, OFFSET); + assert_eq!(reader.read(&mut bytes).unwrap(), 1); + assert_eq!(&bytes, &[OFFSET as u8]); + assert_eq!( + reader.seek(std::io::SeekFrom::End(0)).unwrap(), + WASM_PAGE_SIZE_IN_BYTES + ); + reader + .seek(std::io::SeekFrom::Start(WASM_PAGE_SIZE_IN_BYTES * 2)) + .unwrap(); + // out of bounds so should fail + assert!(reader.read(&mut bytes).is_err()); + } + + fn build_reader(memory: TestStableMemory, buffer_size: Option) -> Box { + let reader = StableReader::with_memory(memory, 0); + if let Some(buffer_size) = buffer_size { + Box::new(BufferedStableReader::with_reader(buffer_size, reader)) + } else { + Box::new(reader) + } + } + } +} diff --git a/src/ic-cdk/src/storage.rs b/ic-cdk/src/storage.rs similarity index 97% rename from src/ic-cdk/src/storage.rs rename to ic-cdk/src/storage.rs index fe8d81451..c582a3ee4 100644 --- a/src/ic-cdk/src/storage.rs +++ b/ic-cdk/src/storage.rs @@ -1,5 +1,5 @@ //! Tools for managing stable storage of data in a canister. -use crate::api::stable; +use crate::stable; /// Saves the storage into the stable memory. /// diff --git a/src/ic-cdk/tests/compile_fail/lifecycle_functions_should_have_no_return.rs b/ic-cdk/tests/compile_fail/lifecycle_functions_should_have_no_return.rs similarity index 100% rename from src/ic-cdk/tests/compile_fail/lifecycle_functions_should_have_no_return.rs rename to ic-cdk/tests/compile_fail/lifecycle_functions_should_have_no_return.rs diff --git a/src/ic-cdk/tests/compile_fail/lifecycle_functions_should_have_no_return.stderr b/ic-cdk/tests/compile_fail/lifecycle_functions_should_have_no_return.stderr similarity index 100% rename from src/ic-cdk/tests/compile_fail/lifecycle_functions_should_have_no_return.stderr rename to ic-cdk/tests/compile_fail/lifecycle_functions_should_have_no_return.stderr diff --git a/src/ic-cdk/tests/compile_fail/no_generic.rs b/ic-cdk/tests/compile_fail/no_generic.rs similarity index 100% rename from src/ic-cdk/tests/compile_fail/no_generic.rs rename to ic-cdk/tests/compile_fail/no_generic.rs diff --git a/src/ic-cdk/tests/compile_fail/no_generic.stderr b/ic-cdk/tests/compile_fail/no_generic.stderr similarity index 100% rename from src/ic-cdk/tests/compile_fail/no_generic.stderr rename to ic-cdk/tests/compile_fail/no_generic.stderr diff --git a/src/ic-cdk/tests/compile_fail/no_guard_function_for_lifecycle.rs b/ic-cdk/tests/compile_fail/no_guard_function_for_lifecycle.rs similarity index 100% rename from src/ic-cdk/tests/compile_fail/no_guard_function_for_lifecycle.rs rename to ic-cdk/tests/compile_fail/no_guard_function_for_lifecycle.rs diff --git a/src/ic-cdk/tests/compile_fail/no_guard_function_for_lifecycle.stderr b/ic-cdk/tests/compile_fail/no_guard_function_for_lifecycle.stderr similarity index 100% rename from src/ic-cdk/tests/compile_fail/no_guard_function_for_lifecycle.stderr rename to ic-cdk/tests/compile_fail/no_guard_function_for_lifecycle.stderr diff --git a/src/ic-cdk/tests/compile_fail/no_self.rs b/ic-cdk/tests/compile_fail/no_self.rs similarity index 100% rename from src/ic-cdk/tests/compile_fail/no_self.rs rename to ic-cdk/tests/compile_fail/no_self.rs diff --git a/src/ic-cdk/tests/compile_fail/no_self.stderr b/ic-cdk/tests/compile_fail/no_self.stderr similarity index 100% rename from src/ic-cdk/tests/compile_fail/no_self.stderr rename to ic-cdk/tests/compile_fail/no_self.stderr diff --git a/src/ic-cdk/tests/compile_fail/only_function.rs b/ic-cdk/tests/compile_fail/only_function.rs similarity index 100% rename from src/ic-cdk/tests/compile_fail/only_function.rs rename to ic-cdk/tests/compile_fail/only_function.rs diff --git a/src/ic-cdk/tests/compile_fail/only_function.stderr b/ic-cdk/tests/compile_fail/only_function.stderr similarity index 100% rename from src/ic-cdk/tests/compile_fail/only_function.stderr rename to ic-cdk/tests/compile_fail/only_function.stderr diff --git a/src/ic-cdk/tests/compile_test.rs b/ic-cdk/tests/compile_test.rs similarity index 100% rename from src/ic-cdk/tests/compile_test.rs rename to ic-cdk/tests/compile_test.rs diff --git a/src/ic-cdk/tests/pass/blank_methods.rs b/ic-cdk/tests/pass/blank_methods.rs similarity index 100% rename from src/ic-cdk/tests/pass/blank_methods.rs rename to ic-cdk/tests/pass/blank_methods.rs diff --git a/ic-cdk/tests/pass/method_arg_same_name.rs b/ic-cdk/tests/pass/method_arg_same_name.rs new file mode 100644 index 000000000..749db8d54 --- /dev/null +++ b/ic-cdk/tests/pass/method_arg_same_name.rs @@ -0,0 +1,13 @@ +use ic_cdk::{query, update}; + +#[update] +fn foo(foo: i32) -> i32 { + foo +} + +#[query] +fn bar(bar: i32) -> i32 { + bar +} + +fn main() {} diff --git a/ic0.txt b/ic0.txt deleted file mode 100644 index c842e930d..000000000 --- a/ic0.txt +++ /dev/null @@ -1,71 +0,0 @@ -ic0.msg_arg_data_size : () -> i32; // I U Q CQ Ry CRy F -ic0.msg_arg_data_copy : (dst : i32, offset : i32, size : i32) -> (); // I U Q CQ Ry CRy F -ic0.msg_caller_size : () -> i32; // * -ic0.msg_caller_copy : (dst : i32, offset: i32, size : i32) -> (); // * -ic0.msg_reject_code : () -> i32; // Ry Rt CRy CRt -ic0.msg_reject_msg_size : () -> i32; // Rt CRt -ic0.msg_reject_msg_copy : (dst : i32, offset : i32, size : i32) -> (); // Rt CRt - -ic0.msg_reply_data_append : (src : i32, size : i32) -> (); // U Q CQ Ry Rt CRy CRt -ic0.msg_reply : () -> (); // U Q CQ Ry Rt CRy CRt -ic0.msg_reject : (src : i32, size : i32) -> (); // U Q CQ Ry Rt CRy CRt - -ic0.msg_cycles_available : () -> i64; // U Rt Ry -ic0.msg_cycles_available128 : (dst : i32) -> (); // U Rt Ry -ic0.msg_cycles_refunded : () -> i64; // Rt Ry -ic0.msg_cycles_refunded128 : (dst : i32) -> (); // Rt Ry -ic0.msg_cycles_accept : (max_amount : i64) -> (amount : i64); // U Rt Ry -ic0.msg_cycles_accept128 : (max_amount_high : i64, max_amount_low: i64, dst : i32) - -> (); // U Rt Ry - -ic0.cycles_burn128 : (amount_high : i64, amount_low : i64, dst : i32) -> (); // I G U Ry Rt C T - -ic0.canister_self_size : () -> i32; // * -ic0.canister_self_copy : (dst : i32, offset : i32, size : i32) -> (); // * -ic0.canister_cycle_balance : () -> i64; // * -ic0.canister_cycle_balance128 : (dst : i32) -> (); // * -ic0.canister_status : () -> i32; // * -ic0.canister_version : () -> i64; // * - -ic0.msg_method_name_size : () -> i32; // F -ic0.msg_method_name_copy : (dst : i32, offset : i32, size : i32) -> (); // F -ic0.accept_message : () -> (); // F - -ic0.call_new : // U CQ Ry Rt CRy CRt T - ( callee_src : i32, - callee_size : i32, - name_src : i32, - name_size : i32, - reply_fun : i32, - reply_env : i32, - reject_fun : i32, - reject_env : i32 - ) -> (); -ic0.call_on_cleanup : (fun : i32, env : i32) -> (); // U CQ Ry Rt CRy CRt T -ic0.call_data_append : (src : i32, size : i32) -> (); // U CQ Ry Rt CRy CRt T -ic0.call_cycles_add : (amount : i64) -> (); // U Ry Rt T -ic0.call_cycles_add128 : (amount_high : i64, amount_low: i64) -> (); // U Ry Rt T -ic0.call_perform : () -> ( err_code : i32 ); // U CQ Ry Rt CRy CRt T - -ic0.stable_size : () -> (page_count : i32); // * s -ic0.stable_grow : (new_pages : i32) -> (old_page_count : i32); // * s -ic0.stable_write : (offset : i32, src : i32, size : i32) -> (); // * s -ic0.stable_read : (dst : i32, offset : i32, size : i32) -> (); // * s -ic0.stable64_size : () -> (page_count : i64); // * s -ic0.stable64_grow : (new_pages : i64) -> (old_page_count : i64); // * s -ic0.stable64_write : (offset : i64, src : i64, size : i64) -> (); // * s -ic0.stable64_read : (dst : i64, offset : i64, size : i64) -> (); // * s - -ic0.certified_data_set : (src: i32, size: i32) -> (); // I G U Ry Rt T -ic0.data_certificate_present : () -> i32; // * -ic0.data_certificate_size : () -> i32; // Q CQ -ic0.data_certificate_copy : (dst: i32, offset: i32, size: i32) -> (); // Q CQ - -ic0.time : () -> (timestamp : i64); // * -ic0.global_timer_set : (timestamp : i64) -> i64; // I G U Ry Rt C T -ic0.performance_counter : (counter_type : i32) -> (counter : i64); // * s -ic0.is_controller: (src: i32, size: i32) -> ( result: i32); // * s -ic0.in_replicated_execution: () -> (result: i32); // * s - -ic0.debug_print : (src : i32, size : i32) -> (); // * s -ic0.trap : (src : i32, size : i32) -> (); // * s diff --git a/src/ic0/Cargo.toml b/ic0/Cargo.toml similarity index 96% rename from src/ic0/Cargo.toml rename to ic0/Cargo.toml index 266013dcf..657ef0d7a 100644 --- a/src/ic0/Cargo.toml +++ b/ic0/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ic0" -version = "0.23.0" +version = "0.24.0-alpha.1" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ic0/LICENSE b/ic0/LICENSE new file mode 120000 index 000000000..ea5b60640 --- /dev/null +++ b/ic0/LICENSE @@ -0,0 +1 @@ +../LICENSE \ No newline at end of file diff --git a/src/ic0/README.md b/ic0/README.md similarity index 82% rename from src/ic0/README.md rename to ic0/README.md index bbb6ea1a4..1c1b610a5 100644 --- a/src/ic0/README.md +++ b/ic0/README.md @@ -6,7 +6,7 @@ Internet Computer System API binding. `ic0` is simply an unsafe Rust translation of the System API as described in the [IC interface specification][1]. -## Update and Version Strategy +## Update `ic0` keeps in step with the IC interface specification. Particularly, `ic0` is directly generated from [system API][1] in that repo. @@ -17,6 +17,4 @@ When interface-spec releases a new version that modify [system API][1]: `src/ic0.rs` should be updated. -The version of `ic0` crate will also bump to the same version as the IC interface specification. - [1]: https://internetcomputer.org/docs/current/references/ic-interface-spec/#system-api-imports diff --git a/ic0/ic0.txt b/ic0/ic0.txt new file mode 100644 index 000000000..a6cb3d920 --- /dev/null +++ b/ic0/ic0.txt @@ -0,0 +1,55 @@ + ic0.msg_arg_data_size : () -> I; // I U RQ NRQ CQ Ry CRy F + ic0.msg_arg_data_copy : (dst : I, offset : I, size : I) -> (); // I U RQ NRQ CQ Ry CRy F + ic0.msg_caller_size : () -> I; // * + ic0.msg_caller_copy : (dst : I, offset : I, size : I) -> (); // * + ic0.msg_reject_code : () -> i32; // Ry Rt CRy CRt + ic0.msg_reject_msg_size : () -> I ; // Rt CRt + ic0.msg_reject_msg_copy : (dst : I, offset : I, size : I) -> (); // Rt CRt + ic0.msg_deadline : () -> i64; // U Q CQ Ry Rt CRy CRt + ic0.msg_reply_data_append : (src : I, size : I) -> (); // U RQ NRQ CQ Ry Rt CRy CRt + ic0.msg_reply : () -> (); // U RQ NRQ CQ Ry Rt CRy CRt + ic0.msg_reject : (src : I, size : I) -> (); // U RQ NRQ CQ Ry Rt CRy CRt + ic0.msg_cycles_available128 : (dst : I) -> (); // U Rt Ry + ic0.msg_cycles_refunded128 : (dst : I) -> (); // Rt Ry + ic0.msg_cycles_accept128 : (max_amount_high : i64, max_amount_low: i64, dst : I) + -> (); // U Rt Ry + ic0.cycles_burn128 : (amount_high : i64, amount_low : i64, dst : I) + -> (); // I G U Ry Rt C T + ic0.canister_self_size : () -> I; // * + ic0.canister_self_copy : (dst : I, offset : I, size : I) -> (); // * + ic0.canister_cycle_balance128 : (dst : I) -> (); // * + ic0.canister_status : () -> i32; // * + ic0.canister_version : () -> i64; // * + ic0.msg_method_name_size : () -> I; // F + ic0.msg_method_name_copy : (dst : I, offset : I, size : I) -> (); // F + ic0.accept_message : () -> (); // F + ic0.call_new : + ( callee_src : I, + callee_size : I, + name_src : I, + name_size : I, + reply_fun : I, + reply_env : I, + reject_fun : I, + reject_env : I + ) -> (); // U CQ Ry Rt CRy CRt T + ic0.call_on_cleanup : (fun : I, env : I) -> (); // U CQ Ry Rt CRy CRt T + ic0.call_data_append : (src : I, size : I) -> (); // U CQ Ry Rt CRy CRt T + ic0.call_with_best_effort_response : (timeout_seconds : i32) -> (); // U CQ Ry Rt CRy CRt T + ic0.call_cycles_add128 : (amount_high : i64, amount_low: i64) -> (); // U Ry Rt T + ic0.call_perform : () -> ( err_code : i32 ); // U CQ Ry Rt CRy CRt T + ic0.stable64_size : () -> (page_count : i64); // * s + ic0.stable64_grow : (new_pages : i64) -> (old_page_count : i64); // * s + ic0.stable64_write : (offset : i64, src : i64, size : i64) -> (); // * s + ic0.stable64_read : (dst : i64, offset : i64, size : i64) -> (); // * s + ic0.certified_data_set : (src : I, size : I) -> (); // I G U Ry Rt T + ic0.data_certificate_present : () -> i32; // * + ic0.data_certificate_size : () -> I; // NRQ CQ + ic0.data_certificate_copy : (dst : I, offset : I, size : I) -> (); // NRQ CQ + ic0.time : () -> (timestamp : i64); // * + ic0.global_timer_set : (timestamp : i64) -> i64; // I G U Ry Rt C T + ic0.performance_counter : (counter_type : i32) -> (counter : i64); // * s + ic0.is_controller: (src : I, size : I) -> ( result: i32); // * s + ic0.in_replicated_execution: () -> (result: i32); // * s + ic0.debug_print : (src : I, size : I) -> (); // * s + ic0.trap : (src : I, size : I) -> (); // * s diff --git a/ic0/src/ic0.rs b/ic0/src/ic0.rs new file mode 100644 index 000000000..cbc01abc3 --- /dev/null +++ b/ic0/src/ic0.rs @@ -0,0 +1,210 @@ +// This file is generated from ic0.txt. +// Don't manually modify it. +#[cfg(target_family = "wasm")] +#[link(wasm_import_module = "ic0")] +extern "C" { + pub fn msg_arg_data_size() -> usize; + pub fn msg_arg_data_copy(dst: usize, offset: usize, size: usize); + pub fn msg_caller_size() -> usize; + pub fn msg_caller_copy(dst: usize, offset: usize, size: usize); + pub fn msg_reject_code() -> u32; + pub fn msg_reject_msg_size() -> usize; + pub fn msg_reject_msg_copy(dst: usize, offset: usize, size: usize); + pub fn msg_deadline() -> u64; + pub fn msg_reply_data_append(src: usize, size: usize); + pub fn msg_reply(); + pub fn msg_reject(src: usize, size: usize); + pub fn msg_cycles_available128(dst: usize); + pub fn msg_cycles_refunded128(dst: usize); + pub fn msg_cycles_accept128(max_amount_high: u64, max_amount_low: u64, dst: usize); + pub fn cycles_burn128(amount_high: u64, amount_low: u64, dst: usize); + pub fn canister_self_size() -> usize; + pub fn canister_self_copy(dst: usize, offset: usize, size: usize); + pub fn canister_cycle_balance128(dst: usize); + pub fn canister_status() -> u32; + pub fn canister_version() -> u64; + pub fn msg_method_name_size() -> usize; + pub fn msg_method_name_copy(dst: usize, offset: usize, size: usize); + pub fn accept_message(); + pub fn call_new( + callee_src: usize, + callee_size: usize, + name_src: usize, + name_size: usize, + reply_fun: usize, + reply_env: usize, + reject_fun: usize, + reject_env: usize, + ); + pub fn call_on_cleanup(fun: usize, env: usize); + pub fn call_data_append(src: usize, size: usize); + pub fn call_with_best_effort_response(timeout_seconds: u32); + pub fn call_cycles_add128(amount_high: u64, amount_low: u64); + pub fn call_perform() -> u32; + pub fn stable64_size() -> u64; + pub fn stable64_grow(new_pages: u64) -> u64; + pub fn stable64_write(offset: u64, src: u64, size: u64); + pub fn stable64_read(dst: u64, offset: u64, size: u64); + pub fn certified_data_set(src: usize, size: usize); + pub fn data_certificate_present() -> u32; + pub fn data_certificate_size() -> usize; + pub fn data_certificate_copy(dst: usize, offset: usize, size: usize); + pub fn time() -> u64; + pub fn global_timer_set(timestamp: u64) -> u64; + pub fn performance_counter(counter_type: u32) -> u64; + pub fn is_controller(src: usize, size: usize) -> u32; + pub fn in_replicated_execution() -> u32; + pub fn debug_print(src: usize, size: usize); + pub fn trap(src: usize, size: usize); +} + +#[cfg(not(target_family = "wasm"))] +#[allow(unused_variables)] +#[allow(clippy::missing_safety_doc)] +#[allow(clippy::too_many_arguments)] +mod non_wasm { + pub unsafe fn msg_arg_data_size() -> usize { + panic!("msg_arg_data_size should only be called inside canisters."); + } + pub unsafe fn msg_arg_data_copy(dst: usize, offset: usize, size: usize) { + panic!("msg_arg_data_copy should only be called inside canisters."); + } + pub unsafe fn msg_caller_size() -> usize { + panic!("msg_caller_size should only be called inside canisters."); + } + pub unsafe fn msg_caller_copy(dst: usize, offset: usize, size: usize) { + panic!("msg_caller_copy should only be called inside canisters."); + } + pub unsafe fn msg_reject_code() -> u32 { + panic!("msg_reject_code should only be called inside canisters."); + } + pub unsafe fn msg_reject_msg_size() -> usize { + panic!("msg_reject_msg_size should only be called inside canisters."); + } + pub unsafe fn msg_reject_msg_copy(dst: usize, offset: usize, size: usize) { + panic!("msg_reject_msg_copy should only be called inside canisters."); + } + pub unsafe fn msg_deadline() -> u64 { + panic!("msg_deadline should only be called inside canisters."); + } + pub unsafe fn msg_reply_data_append(src: usize, size: usize) { + panic!("msg_reply_data_append should only be called inside canisters."); + } + pub unsafe fn msg_reply() { + panic!("msg_reply should only be called inside canisters."); + } + pub unsafe fn msg_reject(src: usize, size: usize) { + panic!("msg_reject should only be called inside canisters."); + } + pub unsafe fn msg_cycles_available128(dst: usize) { + panic!("msg_cycles_available128 should only be called inside canisters."); + } + pub unsafe fn msg_cycles_refunded128(dst: usize) { + panic!("msg_cycles_refunded128 should only be called inside canisters."); + } + pub unsafe fn msg_cycles_accept128(max_amount_high: u64, max_amount_low: u64, dst: usize) { + panic!("msg_cycles_accept128 should only be called inside canisters."); + } + pub unsafe fn cycles_burn128(amount_high: u64, amount_low: u64, dst: usize) { + panic!("cycles_burn128 should only be called inside canisters."); + } + pub unsafe fn canister_self_size() -> usize { + panic!("canister_self_size should only be called inside canisters."); + } + pub unsafe fn canister_self_copy(dst: usize, offset: usize, size: usize) { + panic!("canister_self_copy should only be called inside canisters."); + } + pub unsafe fn canister_cycle_balance128(dst: usize) { + panic!("canister_cycle_balance128 should only be called inside canisters."); + } + pub unsafe fn canister_status() -> u32 { + panic!("canister_status should only be called inside canisters."); + } + pub unsafe fn canister_version() -> u64 { + panic!("canister_version should only be called inside canisters."); + } + pub unsafe fn msg_method_name_size() -> usize { + panic!("msg_method_name_size should only be called inside canisters."); + } + pub unsafe fn msg_method_name_copy(dst: usize, offset: usize, size: usize) { + panic!("msg_method_name_copy should only be called inside canisters."); + } + pub unsafe fn accept_message() { + panic!("accept_message should only be called inside canisters."); + } + pub unsafe fn call_new( + callee_src: usize, + callee_size: usize, + name_src: usize, + name_size: usize, + reply_fun: usize, + reply_env: usize, + reject_fun: usize, + reject_env: usize, + ) { + panic!("call_new should only be called inside canisters."); + } + pub unsafe fn call_on_cleanup(fun: usize, env: usize) { + panic!("call_on_cleanup should only be called inside canisters."); + } + pub unsafe fn call_data_append(src: usize, size: usize) { + panic!("call_data_append should only be called inside canisters."); + } + pub unsafe fn call_with_best_effort_response(timeout_seconds: u32) { + panic!("call_with_best_effort_response should only be called inside canisters."); + } + pub unsafe fn call_cycles_add128(amount_high: u64, amount_low: u64) { + panic!("call_cycles_add128 should only be called inside canisters."); + } + pub unsafe fn call_perform() -> u32 { + panic!("call_perform should only be called inside canisters."); + } + pub unsafe fn stable64_size() -> u64 { + panic!("stable64_size should only be called inside canisters."); + } + pub unsafe fn stable64_grow(new_pages: u64) -> u64 { + panic!("stable64_grow should only be called inside canisters."); + } + pub unsafe fn stable64_write(offset: u64, src: u64, size: u64) { + panic!("stable64_write should only be called inside canisters."); + } + pub unsafe fn stable64_read(dst: u64, offset: u64, size: u64) { + panic!("stable64_read should only be called inside canisters."); + } + pub unsafe fn certified_data_set(src: usize, size: usize) { + panic!("certified_data_set should only be called inside canisters."); + } + pub unsafe fn data_certificate_present() -> u32 { + panic!("data_certificate_present should only be called inside canisters."); + } + pub unsafe fn data_certificate_size() -> usize { + panic!("data_certificate_size should only be called inside canisters."); + } + pub unsafe fn data_certificate_copy(dst: usize, offset: usize, size: usize) { + panic!("data_certificate_copy should only be called inside canisters."); + } + pub unsafe fn time() -> u64 { + panic!("time should only be called inside canisters."); + } + pub unsafe fn global_timer_set(timestamp: u64) -> u64 { + panic!("global_timer_set should only be called inside canisters."); + } + pub unsafe fn performance_counter(counter_type: u32) -> u64 { + panic!("performance_counter should only be called inside canisters."); + } + pub unsafe fn is_controller(src: usize, size: usize) -> u32 { + panic!("is_controller should only be called inside canisters."); + } + pub unsafe fn in_replicated_execution() -> u32 { + panic!("in_replicated_execution should only be called inside canisters."); + } + pub unsafe fn debug_print(src: usize, size: usize) { + panic!("debug_print should only be called inside canisters."); + } + pub unsafe fn trap(src: usize, size: usize) { + panic!("trap should only be called inside canisters."); + } +} + +#[cfg(not(target_family = "wasm"))] +pub use non_wasm::*; diff --git a/src/ic0/src/lib.rs b/ic0/src/lib.rs similarity index 100% rename from src/ic0/src/lib.rs rename to ic0/src/lib.rs diff --git a/src/ic0/util/ic0build.rs b/ic0/util/ic0build.rs similarity index 83% rename from src/ic0/util/ic0build.rs rename to ic0/util/ic0build.rs index 2d509730a..ee310a3d6 100644 --- a/src/ic0/util/ic0build.rs +++ b/ic0/util/ic0build.rs @@ -35,12 +35,17 @@ impl Parse for SystemAPI { let args: Vec = args.iter().cloned().collect(); for arg in &args { match arg { - FnArg::Receiver(r) => return Err(Error::new(r.span(), "receiver not expected")), + FnArg::Receiver(r) => return Err(Error::new(r.span(), "arguments can't be self")), FnArg::Typed(pat_type) => match &*pat_type.ty { syn::Type::Path(ty) => { type_supported(ty)?; } - _ => return Err(Error::new(pat_type.span(), "expected type as i32")), + _ => { + return Err(Error::new( + pat_type.span(), + "argument types can only be i32, i64 or isize", + )) + } }, } } @@ -76,13 +81,17 @@ impl Parse for SystemAPI { } fn type_supported(ty: &TypePath) -> Result<()> { - let supported = match ty.path.get_ident() { - Some(i) => i == "i32" || i == "i64", - None => false, - }; - match supported { - true => Ok(()), - false => Err(Error::new(ty.span(), "expected i32 or i64")), + let ty = ty + .path + .get_ident() + .ok_or(Error::new(ty.span(), "cannot get ident from: {ty:?}"))?; + if ty == "u32" || ty == "u64" || ty == "usize" { + Ok(()) + } else { + Err(Error::new( + ty.span(), + "ic0.txt should only contain i32, i64 or I", + )) } } @@ -107,7 +116,10 @@ impl Parse for IC0 { fn main() { let s = include_str!("../ic0.txt"); - let ic0: IC0 = syn::parse_str(s).unwrap(); + let s = s.replace('I', "usize"); + let s = s.replace("i32", "u32"); + let s = s.replace("i64", "u64"); + let ic0: IC0 = syn::parse_str(&s).unwrap(); let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); d.push("src/ic0.rs"); @@ -118,7 +130,7 @@ fn main() { f, r#"// This file is generated from ic0.txt. // Don't manually modify it. -#[cfg(target_arch = "wasm32")] +#[cfg(target_family = "wasm")] #[link(wasm_import_module = "ic0")] extern "C" {{"#, ) @@ -147,7 +159,7 @@ extern "C" {{"#, writeln!( f, r#" -#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(target_family = "wasm"))] #[allow(unused_variables)] #[allow(clippy::missing_safety_doc)] #[allow(clippy::too_many_arguments)] @@ -182,7 +194,7 @@ mod non_wasm{{"#, f, r#"}} -#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(target_family = "wasm"))] pub use non_wasm::*; "# ) diff --git a/src/ic0/util/work.rs b/ic0/util/work.rs similarity index 100% rename from src/ic0/util/work.rs rename to ic0/util/work.rs diff --git a/library/ic-ledger-types/Cargo.toml b/library/ic-ledger-types/Cargo.toml index 18c9fc6eb..0b8789b62 100644 --- a/library/ic-ledger-types/Cargo.toml +++ b/library/ic-ledger-types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ic-ledger-types" -version = "0.14.0" +version = "0.15.0-alpha.1" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/library/ic-ledger-types/src/lib.rs b/library/ic-ledger-types/src/lib.rs index 53ae8a601..fc6193663 100644 --- a/library/ic-ledger-types/src/lib.rs +++ b/library/ic-ledger-types/src/lib.rs @@ -18,7 +18,7 @@ use serde::{Deserialize, Serialize}; use serde_bytes::ByteBuf; use sha2::Digest; -use ic_cdk::api::call::CallResult; +use ic_cdk::call::{Call, CallResult, SendableCall}; /// The subaccount that is used by default. pub const DEFAULT_SUBACCOUNT: Subaccount = Subaccount([0; 32]); @@ -675,14 +675,14 @@ impl CandidType for QueryArchiveFn { /// /// # Example /// ```no_run -/// use ic_cdk::api::{caller, call::call}; +/// use ic_cdk::api::msg_caller; /// use ic_ledger_types::{AccountIdentifier, AccountBalanceArgs, Tokens, DEFAULT_SUBACCOUNT, MAINNET_LEDGER_CANISTER_ID, account_balance}; /// /// async fn check_callers_balance() -> Tokens { /// account_balance( /// MAINNET_LEDGER_CANISTER_ID, /// AccountBalanceArgs { -/// account: AccountIdentifier::new(&caller(), &DEFAULT_SUBACCOUNT) +/// account: AccountIdentifier::new(&msg_caller(), &DEFAULT_SUBACCOUNT) /// } /// ).await.expect("call to ledger failed") /// } @@ -691,14 +691,16 @@ pub async fn account_balance( ledger_canister_id: Principal, args: AccountBalanceArgs, ) -> CallResult { - let (icp,) = ic_cdk::call(ledger_canister_id, "account_balance", (args,)).await?; - Ok(icp) + Call::new(ledger_canister_id, "account_balance") + .with_arg(args) + .call() + .await } /// Calls the "transfer" method on the specified canister. /// # Example /// ```no_run -/// use ic_cdk::api::{caller, call::call}; +/// use ic_cdk::api::msg_caller; /// use ic_ledger_types::{AccountIdentifier, BlockIndex, Memo, TransferArgs, Tokens, DEFAULT_SUBACCOUNT, DEFAULT_FEE, MAINNET_LEDGER_CANISTER_ID, transfer}; /// /// async fn transfer_to_caller() -> BlockIndex { @@ -709,7 +711,7 @@ pub async fn account_balance( /// amount: Tokens::from_e8s(1_000_000), /// fee: DEFAULT_FEE, /// from_subaccount: None, -/// to: AccountIdentifier::new(&caller(), &DEFAULT_SUBACCOUNT), +/// to: AccountIdentifier::new(&msg_caller(), &DEFAULT_SUBACCOUNT), /// created_at_time: None, /// } /// ).await.expect("call to ledger failed").expect("transfer failed") @@ -719,8 +721,10 @@ pub async fn transfer( ledger_canister_id: Principal, args: TransferArgs, ) -> CallResult { - let (result,) = ic_cdk::call(ledger_canister_id, "transfer", (args,)).await?; - Ok(result) + Call::new(ledger_canister_id, "transfer") + .with_arg(args) + .call() + .await } /// Return type of the `token_symbol` function. @@ -734,7 +738,6 @@ pub struct Symbol { /// # Example /// ```no_run /// use candid::Principal; -/// use ic_cdk::api::{caller, call::call}; /// use ic_ledger_types::{Symbol, token_symbol}; /// /// async fn symbol(ledger_canister_id: Principal) -> String { @@ -742,15 +745,14 @@ pub struct Symbol { /// } /// ``` pub async fn token_symbol(ledger_canister_id: Principal) -> CallResult { - let (result,) = ic_cdk::call(ledger_canister_id, "token_symbol", ()).await?; - Ok(result) + Call::new(ledger_canister_id, "token_symbol").call().await } /// Calls the "query_block" method on the specified canister. /// # Example /// ```no_run /// use candid::Principal; -/// use ic_cdk::api::call::CallResult; +/// use ic_cdk::call::CallResult; /// use ic_ledger_types::{BlockIndex, Block, GetBlocksArgs, query_blocks, query_archived_blocks}; /// /// async fn query_one_block(ledger: Principal, block_index: BlockIndex) -> CallResult> { @@ -778,8 +780,10 @@ pub async fn query_blocks( ledger_canister_id: Principal, args: GetBlocksArgs, ) -> CallResult { - let (result,) = ic_cdk::call(ledger_canister_id, "query_blocks", (args,)).await?; - Ok(result) + Call::new(ledger_canister_id, "query_blocks") + .with_arg(args) + .call() + .await } /// Continues a query started in [`query_blocks`] by calling its returned archive function. @@ -788,7 +792,7 @@ pub async fn query_blocks( /// /// ```no_run /// use candid::Principal; -/// use ic_cdk::api::call::CallResult; +/// use ic_cdk::call::CallResult; /// use ic_ledger_types::{BlockIndex, Block, GetBlocksArgs, query_blocks, query_archived_blocks}; /// /// async fn query_one_block(ledger: Principal, block_index: BlockIndex) -> CallResult> { @@ -816,8 +820,10 @@ pub async fn query_archived_blocks( func: &QueryArchiveFn, args: GetBlocksArgs, ) -> CallResult { - let (result,) = ic_cdk::api::call::call(func.0.principal, &func.0.method, (args,)).await?; - Ok(result) + Call::new(func.0.principal, &func.0.method) + .with_arg(args) + .call() + .await } #[cfg(test)] diff --git a/src/candid-extractor/CHANGELOG.md b/src/candid-extractor/CHANGELOG.md deleted file mode 100644 index ece920116..000000000 --- a/src/candid-extractor/CHANGELOG.md +++ /dev/null @@ -1,37 +0,0 @@ -# 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.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [unreleased] - -## [0.1.4] - 2024-05-10 - -### Added - -- Upgrade `ic0` to 0.23.0 which includes the new system API `in_replicated_execution`. - -## [0.1.3] - 2024-04-22 - -### Added - -- Use `clap` to support `-V`/`--version` and `-h`/`--help`. (#485) - -## [0.1.1] - 2023-10-11 - -### Added - -- Includes new system API `cycles_burn128`. (#434) - -## [0.1.1] - 2023-09-19 - -### Added - -- Release from the [CI workflow](../../.github/workflows/release-candid-extractor.yml). (#427) - -## [0.1.0] - 2023-09-18 - -### Added - -- The first release. (#424) diff --git a/src/candid-extractor/Cargo.toml b/src/candid-extractor/Cargo.toml deleted file mode 100644 index 42dbc1f86..000000000 --- a/src/candid-extractor/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -name = "candid-extractor" -version = "0.1.4" -authors.workspace = true -edition.workspace = true -license.workspace = true -rust-version.workspace = true -repository.workspace = true -description = "CLI tool to extract candid definition from canister WASM." -readme = "README.md" -categories = ["development-tools"] -keywords = ["internet-computer", "wasm", "dfinity", "canister", "cdk"] -include = ["src", "Cargo.toml", "LICENSE", "README.md", "ic_mock.wat"] - -[dependencies] -anyhow = "1.0.72" -wasmtime = "19" -clap = { version = "4", features = ["derive"] } - -[dev-dependencies] -quote.workspace = true -syn = { workspace = true, features = ["parsing", "full", "extra-traits"] } - -[[example]] -name = "generate_mock_wat" -path = "util/generate_mock_wat.rs" - -[package.metadata.binstall] -pkg-url = "{ repo }/releases/download/candid-extractor-v{ version }/{ name }-{ target }{ archive-suffix }" diff --git a/src/candid-extractor/LICENSE b/src/candid-extractor/LICENSE deleted file mode 120000 index 30cff7403..000000000 --- a/src/candid-extractor/LICENSE +++ /dev/null @@ -1 +0,0 @@ -../../LICENSE \ No newline at end of file diff --git a/src/candid-extractor/README.md b/src/candid-extractor/README.md deleted file mode 100644 index 534c69126..000000000 --- a/src/candid-extractor/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# candid-extractor - -A CLI tool to extract candid definition from canister WASM. - -## Installation - -``` -cargo install candid-extractor -``` - -## Usage - -``` -candid-extractor path/to/canister.wasm -``` - -## Update ic_mock.wat - -`candid-extractor` requires a mock WASM (`ic_mock.wat`) which provides ic0 imports. - -Such `ic_mock.wat` is directly generated from the [system API][1]. - -When interface-spec releases a new version that modify ic0 system API: - -1. replace `ic0.txt` in the root of this project; -2. execute `cargo run --example=generate_mock_wat`; - -`ic_mock.wat` should be updated. - -[1]: https://internetcomputer.org/docs/current/references/ic-interface-spec/#system-api-imports diff --git a/src/candid-extractor/ic0.txt b/src/candid-extractor/ic0.txt deleted file mode 120000 index ee3cab32a..000000000 --- a/src/candid-extractor/ic0.txt +++ /dev/null @@ -1 +0,0 @@ -../../ic0.txt \ No newline at end of file diff --git a/src/candid-extractor/ic_mock.wat b/src/candid-extractor/ic_mock.wat deleted file mode 100644 index 2886218a5..000000000 --- a/src/candid-extractor/ic_mock.wat +++ /dev/null @@ -1,55 +0,0 @@ -(module - ;; This file is generated from ic0.txt. - ;; Don't manually modify it. - (func (export "msg_arg_data_size") (result i32) i32.const 0) - (func (export "msg_arg_data_copy") (param i32 i32 i32) ) - (func (export "msg_caller_size") (result i32) i32.const 0) - (func (export "msg_caller_copy") (param i32 i32 i32) ) - (func (export "msg_reject_code") (result i32) i32.const 0) - (func (export "msg_reject_msg_size") (result i32) i32.const 0) - (func (export "msg_reject_msg_copy") (param i32 i32 i32) ) - (func (export "msg_reply_data_append") (param i32 i32) ) - (func (export "msg_reply") ) - (func (export "msg_reject") (param i32 i32) ) - (func (export "msg_cycles_available") (result i64) i64.const 0) - (func (export "msg_cycles_available128") (param i32) ) - (func (export "msg_cycles_refunded") (result i64) i64.const 0) - (func (export "msg_cycles_refunded128") (param i32) ) - (func (export "msg_cycles_accept") (param i64) (result i64) i64.const 0) - (func (export "msg_cycles_accept128") (param i64 i64 i32) ) - (func (export "cycles_burn128") (param i64 i64 i32) ) - (func (export "canister_self_size") (result i32) i32.const 0) - (func (export "canister_self_copy") (param i32 i32 i32) ) - (func (export "canister_cycle_balance") (result i64) i64.const 0) - (func (export "canister_cycle_balance128") (param i32) ) - (func (export "canister_status") (result i32) i32.const 0) - (func (export "canister_version") (result i64) i64.const 0) - (func (export "msg_method_name_size") (result i32) i32.const 0) - (func (export "msg_method_name_copy") (param i32 i32 i32) ) - (func (export "accept_message") ) - (func (export "call_new") (param i32 i32 i32 i32 i32 i32 i32 i32) ) - (func (export "call_on_cleanup") (param i32 i32) ) - (func (export "call_data_append") (param i32 i32) ) - (func (export "call_cycles_add") (param i64) ) - (func (export "call_cycles_add128") (param i64 i64) ) - (func (export "call_perform") (result i32) i32.const 0) - (func (export "stable_size") (result i32) i32.const 0) - (func (export "stable_grow") (param i32) (result i32) i32.const 0) - (func (export "stable_write") (param i32 i32 i32) ) - (func (export "stable_read") (param i32 i32 i32) ) - (func (export "stable64_size") (result i64) i64.const 0) - (func (export "stable64_grow") (param i64) (result i64) i64.const 0) - (func (export "stable64_write") (param i64 i64 i64) ) - (func (export "stable64_read") (param i64 i64 i64) ) - (func (export "certified_data_set") (param i32 i32) ) - (func (export "data_certificate_present") (result i32) i32.const 0) - (func (export "data_certificate_size") (result i32) i32.const 0) - (func (export "data_certificate_copy") (param i32 i32 i32) ) - (func (export "time") (result i64) i64.const 0) - (func (export "global_timer_set") (param i64) (result i64) i64.const 0) - (func (export "performance_counter") (param i32) (result i64) i64.const 0) - (func (export "is_controller") (param i32 i32) (result i32) i32.const 0) - (func (export "in_replicated_execution") (result i32) i32.const 0) - (func (export "debug_print") (param i32 i32) ) - (func (export "trap") (param i32 i32) ) -) diff --git a/src/candid-extractor/src/extract.rs b/src/candid-extractor/src/extract.rs deleted file mode 100644 index eaebd4033..000000000 --- a/src/candid-extractor/src/extract.rs +++ /dev/null @@ -1,38 +0,0 @@ -use anyhow::Result; -use std::path::Path; -use wasmtime::*; - -static IC0: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/ic_mock.wat")); - -pub(crate) fn extract_candid

(wasm_path: P) -> Result -where - P: AsRef, -{ - let mut store: Store<()> = Store::<()>::default(); - - let mut linker = Linker::new(store.engine()); - let ic0_module = Module::new(store.engine(), IC0)?; - let ic0 = linker.instantiate(&mut store, &ic0_module)?; - linker.instance(&mut store, "ic0", ic0)?; - - let module = Module::from_file(store.engine(), wasm_path)?; - let canister = linker.instantiate(&mut store, &module)?; - - let get_candid_pointer = - canister.get_typed_func::<(), i32>(&mut store, "get_candid_pointer")?; - let candid_pointer = get_candid_pointer.call(&mut store, ())?; - - let memory = canister - .get_memory(&mut store, "memory") - .ok_or_else(|| anyhow::format_err!("failed to find `memory` export"))?; - let memory_buffer = memory.data(&store); - - let mut i = candid_pointer as usize; - let mut str_vec = vec![]; - while memory_buffer[i] != 0 { - str_vec.push(memory_buffer[i]); - i += 1; - } - let s = String::from_utf8(str_vec)?; - Ok(s) -} diff --git a/src/candid-extractor/src/main.rs b/src/candid-extractor/src/main.rs deleted file mode 100644 index 0f3b2a699..000000000 --- a/src/candid-extractor/src/main.rs +++ /dev/null @@ -1,20 +0,0 @@ -use anyhow::Result; -use clap::Parser; -use std::path::PathBuf; - -mod extract; - -/// Extract the Candid interface from a Canister WASM file. -#[derive(Parser)] -#[command(version, about)] -struct Cli { - /// Path to the Canister WASM file. - path: PathBuf, -} - -fn main() -> Result<()> { - let cli = Cli::parse(); - let candid = extract::extract_candid(cli.path)?; - println!("{candid}"); - Ok(()) -} diff --git a/src/candid-extractor/util/generate_mock_wat.rs b/src/candid-extractor/util/generate_mock_wat.rs deleted file mode 100644 index 3f2cd54b1..000000000 --- a/src/candid-extractor/util/generate_mock_wat.rs +++ /dev/null @@ -1,157 +0,0 @@ -use quote::{quote, ToTokens}; -use syn::parse::{Parse, ParseStream, Result}; -use syn::punctuated::Punctuated; -use syn::spanned::Spanned; -use syn::token::Comma; -use syn::{parenthesized, Error}; -use syn::{FnArg, Ident, Token, TypePath}; - -use std::fs; -use std::io::Write; -use std::path::PathBuf; - -#[derive(Clone, Debug)] -pub struct SystemAPI { - pub name: Ident, - pub arg_types: Vec, - pub output: Option, -} - -impl Parse for SystemAPI { - fn parse(input: ParseStream) -> Result { - let ic0_token: Ident = input.parse()?; - if ic0_token != "ic0" { - return Err(Error::new(ic0_token.span(), "expected `ic0`")); - } - input.parse::()?; - let name: Ident = input.parse()?; - input.parse::()?; - - // args - let content; - parenthesized!(content in input); - let args = Punctuated::::parse_terminated(&content)?; - let args: Vec = args.iter().cloned().collect(); - let mut arg_types = vec![]; - for arg in &args { - match arg { - FnArg::Receiver(r) => return Err(Error::new(r.span(), "receiver not expected")), - FnArg::Typed(pat_type) => match &*pat_type.ty { - syn::Type::Path(ty) => { - type_supported(ty)?; - arg_types.push(ty.clone()); - } - _ => return Err(Error::new(pat_type.span(), "expected type as i32")), - }, - } - } - - input.parse::]>()?; - - // output - let output = if input.peek(syn::token::Paren) { - let content; - parenthesized!(content in input); - if content.is_empty() { - None - } else { - let _output_name: Ident = content.parse()?; - content.parse::()?; - let ty: TypePath = content.parse()?; - if !content.is_empty() { - return Err(Error::new(ty.span(), "expected only one return type")); - } - type_supported(&ty)?; - Some(ty) - } - } else { - let ty: TypePath = input.parse()?; - type_supported(&ty)?; - Some(ty) - }; - - input.parse::()?; - - Ok(Self { - name, - arg_types, - output, - }) - } -} - -fn type_supported(ty: &TypePath) -> Result<()> { - let supported = match ty.path.get_ident() { - Some(i) => i == "i32" || i == "i64", - None => false, - }; - match supported { - true => Ok(()), - false => Err(Error::new(ty.span(), "expected i32 or i64")), - } -} - -#[derive(Clone, Debug)] -pub struct IC0 { - pub apis: Vec, -} - -impl Parse for IC0 { - fn parse(input: ParseStream) -> Result { - Ok(Self { - apis: { - let mut apis = vec![]; - while !input.is_empty() { - apis.push(input.parse()?); - } - apis - }, - }) - } -} - -fn main() { - let s = include_str!("../ic0.txt"); - let ic0: IC0 = syn::parse_str(s).unwrap(); - - let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - d.push("ic_mock.wat"); - - let mut f = fs::File::create(d).unwrap(); - - writeln!( - f, - r#"(module - ;; This file is generated from ic0.txt. - ;; Don't manually modify it."#, - ) - .unwrap(); - - for api in &ic0.apis { - let fn_name = &api.name; - let arg_types = &api.arg_types; - - let params = if arg_types.is_empty() { - quote! {} - } else { - quote! { - (param #(#arg_types)*) - } - }; - - let result = if let Some(output) = &api.output { - format!( - "(result {0}) {0}.const 0", - output.to_token_stream().to_string() - ) - } else { - "".to_string() - }; - - let r = format!(" (func (export \"{fn_name}\") {params} {result})"); - - writeln!(f, "{}", r).unwrap(); - } - - writeln!(f, ")").unwrap(); -} diff --git a/src/ic-cdk-bindgen/LICENSE b/src/ic-cdk-bindgen/LICENSE deleted file mode 120000 index 30cff7403..000000000 --- a/src/ic-cdk-bindgen/LICENSE +++ /dev/null @@ -1 +0,0 @@ -../../LICENSE \ No newline at end of file diff --git a/src/ic-cdk-macros/LICENSE b/src/ic-cdk-macros/LICENSE deleted file mode 120000 index 30cff7403..000000000 --- a/src/ic-cdk-macros/LICENSE +++ /dev/null @@ -1 +0,0 @@ -../../LICENSE \ No newline at end of file diff --git a/src/ic-cdk-timers/LICENSE b/src/ic-cdk-timers/LICENSE deleted file mode 120000 index 30cff7403..000000000 --- a/src/ic-cdk-timers/LICENSE +++ /dev/null @@ -1 +0,0 @@ -../../LICENSE \ No newline at end of file diff --git a/src/ic-cdk/LICENSE b/src/ic-cdk/LICENSE deleted file mode 120000 index 30cff7403..000000000 --- a/src/ic-cdk/LICENSE +++ /dev/null @@ -1 +0,0 @@ -../../LICENSE \ No newline at end of file diff --git a/src/ic-cdk/src/api/mod.rs b/src/ic-cdk/src/api/mod.rs deleted file mode 100644 index 5663b46fe..000000000 --- a/src/ic-cdk/src/api/mod.rs +++ /dev/null @@ -1,207 +0,0 @@ -//! System API and low level functions for it. -use candid::Principal; -use std::convert::TryFrom; - -pub mod call; -pub mod management_canister; -pub mod stable; - -/// Prints the given message. -pub fn print>(s: S) { - let s = s.as_ref(); - // SAFETY: `s`, being &str, is a readable sequence of bytes and therefore can be passed to ic0.debug_print. - unsafe { - ic0::debug_print(s.as_ptr() as i32, s.len() as i32); - } -} - -/// Traps with the given message. -pub fn trap(message: &str) -> ! { - // SAFETY: `message`, being &str, is a readable sequence of bytes and therefore can be passed to ic0.trap. - unsafe { - ic0::trap(message.as_ptr() as i32, message.len() as i32); - } - unreachable!() -} - -/// Gets current timestamp, in nanoseconds since the epoch (1970-01-01) -pub fn time() -> u64 { - // SAFETY: ic0.time is always safe to call. - unsafe { ic0::time() as u64 } -} - -/// Returns the caller of the current call. -pub fn caller() -> Principal { - // SAFETY: ic0.msg_caller_size is always safe to call. - let len: u32 = unsafe { ic0::msg_caller_size() as u32 }; - let mut bytes = vec![0u8; len as usize]; - // SAFETY: Because `bytes` is mutable, and allocated to `len` bytes, it is safe to be passed to `ic0.msg_caller_copy` with a 0-offset. - unsafe { - ic0::msg_caller_copy(bytes.as_mut_ptr() as i32, 0, len as i32); - } - Principal::try_from(&bytes).unwrap() -} - -/// Returns the canister id as a blob. -pub fn id() -> Principal { - // SAFETY: ic0.canister_self_size is always safe to call. - let len: u32 = unsafe { ic0::canister_self_size() as u32 }; - let mut bytes = vec![0u8; len as usize]; - // SAFETY: Because `bytes` is mutable, and allocated to `len` bytes, it is safe to be passed to `ic0.canister_self_copy` with a 0-offset. - unsafe { - ic0::canister_self_copy(bytes.as_mut_ptr() as i32, 0, len as i32); - } - Principal::try_from(&bytes).unwrap() -} - -/// Gets the amount of funds available in the canister. -pub fn canister_balance() -> u64 { - // SAFETY: ic0.canister_cycle_balance is always safe to call. - unsafe { ic0::canister_cycle_balance() as u64 } -} - -/// Gets the amount of funds available in the canister. -pub fn canister_balance128() -> u128 { - let mut recv = 0u128; - // SAFETY: recv is writable and the size expected by ic0.canister_cycle_balance128. - unsafe { ic0::canister_cycle_balance128(&mut recv as *mut u128 as i32) } - recv -} - -/// Sets the certified data of this canister. -/// -/// Canisters can store up to 32 bytes of data that is certified by -/// the system on a regular basis. One can call [data_certificate] -/// function from a query call to get a certificate authenticating the -/// value set by calling this function. -/// -/// This function can only be called from the following contexts: -/// * "canister_init", "canister_pre_upgrade" and "canister_post_upgrade" -/// hooks. -/// * "canister_update" calls. -/// * reply or reject callbacks. -/// -/// # Panics -/// -/// * This function traps if data.len() > 32. -/// * This function traps if it's called from an illegal context -/// (e.g., from a query call). -pub fn set_certified_data(data: &[u8]) { - // SAFETY: because data is a slice ref, its pointer and length are valid to pass to ic0.certified_data_set. - unsafe { ic0::certified_data_set(data.as_ptr() as i32, data.len() as i32) } -} - -/// When called from a query call, returns the data certificate authenticating -/// certified_data set by this canister. -/// -/// Returns None if called not from a query call. -pub fn data_certificate() -> Option> { - // SAFETY: ic0.data_certificate_present is always safe to call. - if unsafe { ic0::data_certificate_present() } == 0 { - return None; - } - - // SAFETY: ic0.data_certificate_size is always safe to call. - let n = unsafe { ic0::data_certificate_size() }; - let mut buf = vec![0u8; n as usize]; - // SAFETY: Because `buf` is mutable and allocated to `n` bytes, it is valid to receive from ic0.data_certificate_bytes with no offset - unsafe { - ic0::data_certificate_copy(buf.as_mut_ptr() as i32, 0i32, n); - } - Some(buf) -} - -/// Returns the number of instructions that the canister executed since the last [entry -/// point](https://internetcomputer.org/docs/current/references/ic-interface-spec/#entry-points). -#[inline] -pub fn instruction_counter() -> u64 { - performance_counter(0) -} - -/// Returns the number of WebAssembly instructions the canister has executed -/// within the call context of the current Message execution since -/// Call context creation. -/// -/// The counter monotonically increases across all message executions -/// in the call context until the corresponding call context is removed. -#[inline] -pub fn call_context_instruction_counter() -> u64 { - performance_counter(1) -} - -/// Gets the value of specified performance counter. -/// -/// Supported counter types: -/// * `0` : current execution instruction counter. The number of WebAssembly -/// instructions the canister has executed since the beginning of the -/// current Message execution. -/// * `1` : call context instruction counter. The number of WebAssembly -/// instructions the canister has executed within the call context -/// of the current Message execution since Call context creation. -/// The counter monotonically increases across all message executions -/// in the call context until the corresponding call context is removed. -#[inline] -pub fn performance_counter(counter_type: u32) -> u64 { - // SAFETY: ic0.performance_counter is always safe to call. - unsafe { ic0::performance_counter(counter_type as i32) as u64 } -} - -/// Gets the value of canister version. -pub fn canister_version() -> u64 { - // SAFETY: ic0.canister_version is always safe to call. - unsafe { ic0::canister_version() as u64 } -} - -/// Determines if a Principal is a controller of the canister. -pub fn is_controller(principal: &Principal) -> bool { - let slice = principal.as_slice(); - // SAFETY: `principal.as_bytes()`, being `&[u8]`, is a readable sequence of bytes and therefore safe to pass to `ic0.is_controller`. - unsafe { ic0::is_controller(slice.as_ptr() as i32, slice.len() as i32) != 0 } -} - -/// Burns cycles from the canister. -/// -/// Returns the amount of cycles that were actually burned. -pub fn cycles_burn(amount: u128) -> u128 { - let amount_high = (amount >> 64) as u64; - let amount_low = (amount & u64::MAX as u128) as u64; - let mut dst = 0u128; - // SAFETY: `dst` is writable and sixteen bytes wide, and therefore safe to pass to ic0.cycles_burn128 - unsafe { - ic0::cycles_burn128( - amount_high as i64, - amount_low as i64, - &mut dst as *mut u128 as i32, - ) - } - dst -} - -/// Sets global timer. -/// -/// The canister can set a global timer to make the system -/// schedule a call to the exported canister_global_timer -/// Wasm method after the specified time. -/// The time must be provided as nanoseconds since 1970-01-01. -/// -/// The function returns the previous value of the timer. -/// If no timer is set before invoking the function, then the function returns zero. -/// -/// Passing zero as an argument to the function deactivates the timer and thus -/// prevents the system from scheduling calls to the canister's canister_global_timer Wasm method. -pub fn set_global_timer(timestamp: u64) -> u64 { - // SAFETY: ic0.global_timer_set is always safe to call. - unsafe { ic0::global_timer_set(timestamp as i64) as u64 } -} - -/// Checks if in replicated execution. -/// -/// The canister can check whether it is currently running in replicated or non replicated execution. -pub fn in_replicated_execution() -> bool { - // SAFETY: ic0.in_replicated_execution is always safe to call. - match unsafe { ic0::in_replicated_execution() } { - 0 => false, - 1 => true, - _ => unreachable!(), - } -} diff --git a/src/ic-cdk/src/api/stable/tests.rs b/src/ic-cdk/src/api/stable/tests.rs deleted file mode 100644 index aaf592849..000000000 --- a/src/ic-cdk/src/api/stable/tests.rs +++ /dev/null @@ -1,265 +0,0 @@ -use super::*; -use std::rc::Rc; -use std::sync::Mutex; - -#[derive(Default)] -pub struct TestStableMemory { - memory: Rc>>, -} - -impl TestStableMemory { - pub fn new(memory: Rc>>) -> TestStableMemory { - let bytes_len = memory.lock().unwrap().len(); - if bytes_len > 0 { - let pages_required = pages_required(bytes_len); - let bytes_required = pages_required * WASM_PAGE_SIZE_IN_BYTES; - memory - .lock() - .unwrap() - .resize(bytes_required.try_into().unwrap(), 0); - } - - TestStableMemory { memory } - } -} - -impl StableMemory for TestStableMemory { - fn stable_size(&self) -> u64 { - let bytes_len = self.memory.lock().unwrap().len(); - pages_required(bytes_len) - } - - fn stable_grow(&self, new_pages: u64) -> Result { - let new_bytes = new_pages * WASM_PAGE_SIZE_IN_BYTES; - - let mut vec = self.memory.lock().unwrap(); - let previous_len = vec.len() as u64; - let new_len = vec.len() as u64 + new_bytes; - vec.resize(new_len.try_into().unwrap(), 0); - Ok(previous_len / WASM_PAGE_SIZE_IN_BYTES) - } - - fn stable_write(&self, offset: u64, buf: &[u8]) { - let offset = offset as usize; - - let mut vec = self.memory.lock().unwrap(); - if offset + buf.len() > vec.len() { - panic!("stable memory out of bounds"); - } - vec[offset..(offset + buf.len())].clone_from_slice(buf); - } - - fn stable_read(&self, offset: u64, buf: &mut [u8]) { - let offset = offset as usize; - - let vec = self.memory.lock().unwrap(); - let count_to_copy = buf.len(); - - buf[..count_to_copy].copy_from_slice(&vec[offset..offset + count_to_copy]); - } -} - -fn pages_required(bytes_len: usize) -> u64 { - let page_size = WASM_PAGE_SIZE_IN_BYTES; - (bytes_len as u64 + page_size - 1) / page_size -} - -mod stable_writer_tests { - use super::*; - use rstest::rstest; - use std::io::{Seek, Write}; - - #[rstest] - #[case(None)] - #[case(Some(1))] - #[case(Some(10))] - #[case(Some(100))] - #[case(Some(1000))] - fn write_single_slice(#[case] buffer_size: Option) { - let memory = Rc::new(Mutex::new(Vec::new())); - let mut writer = build_writer(TestStableMemory::new(memory.clone()), buffer_size); - - let bytes = vec![1; 100]; - - writer.write_all(&bytes).unwrap(); - writer.flush().unwrap(); - - let result = &*memory.lock().unwrap(); - - assert_eq!(bytes, result[..bytes.len()]); - } - - #[rstest] - #[case(None)] - #[case(Some(1))] - #[case(Some(10))] - #[case(Some(100))] - #[case(Some(1000))] - fn write_many_slices(#[case] buffer_size: Option) { - let memory = Rc::new(Mutex::new(Vec::new())); - let mut writer = build_writer(TestStableMemory::new(memory.clone()), buffer_size); - - for i in 1..100 { - let bytes = vec![i as u8; i]; - writer.write_all(&bytes).unwrap(); - } - writer.flush().unwrap(); - - let result = &*memory.lock().unwrap(); - - let mut offset = 0; - for i in 1..100 { - let bytes = &result[offset..offset + i]; - assert_eq!(bytes, vec![i as u8; i]); - offset += i; - } - } - - #[rstest] - #[case(None)] - #[case(Some(1))] - #[case(Some(10))] - #[case(Some(100))] - #[case(Some(1000))] - fn ensure_only_requests_min_number_of_pages_required(#[case] buffer_size: Option) { - let memory = Rc::new(Mutex::new(Vec::new())); - let mut writer = build_writer(TestStableMemory::new(memory.clone()), buffer_size); - - let mut total_bytes = 0; - for i in 1..10000 { - let bytes = vec![i as u8; i]; - writer.write_all(&bytes).unwrap(); - total_bytes += i; - } - writer.flush().unwrap(); - - let capacity_pages = TestStableMemory::new(memory).stable_size(); - let min_pages_required = - (total_bytes as u64 + WASM_PAGE_SIZE_IN_BYTES - 1) / WASM_PAGE_SIZE_IN_BYTES; - - assert_eq!(capacity_pages, min_pages_required); - } - - #[test] - fn check_offset() { - const WRITE_SIZE: usize = 1025; - - let memory = Rc::new(Mutex::new(Vec::new())); - let mut writer = StableWriter::with_memory(TestStableMemory::new(memory.clone()), 0); - assert_eq!(writer.offset(), 0); - assert_eq!(writer.write(&vec![0; WRITE_SIZE]).unwrap(), WRITE_SIZE); - assert_eq!(writer.offset(), WRITE_SIZE as u64); - - let mut writer = BufferedStableWriter::with_writer( - WRITE_SIZE - 1, - StableWriter::with_memory(TestStableMemory::new(memory), 0), - ); - assert_eq!(writer.offset(), 0); - assert_eq!(writer.write(&vec![0; WRITE_SIZE]).unwrap(), WRITE_SIZE); - assert_eq!(writer.offset(), WRITE_SIZE as u64); - } - - #[test] - fn test_seek() { - let memory = Rc::new(Mutex::new(Vec::new())); - let mut writer = StableWriter::with_memory(TestStableMemory::new(memory.clone()), 0); - writer - .seek(std::io::SeekFrom::Start(WASM_PAGE_SIZE_IN_BYTES)) - .unwrap(); - assert_eq!(writer.stream_position().unwrap(), WASM_PAGE_SIZE_IN_BYTES); - assert_eq!(writer.write(&[1_u8]).unwrap(), 1); - assert_eq!( - writer.seek(std::io::SeekFrom::End(0)).unwrap(), - WASM_PAGE_SIZE_IN_BYTES * 2 - ); - let capacity_pages = TestStableMemory::new(memory).stable_size(); - assert_eq!(capacity_pages, 2); - } - - fn build_writer(memory: TestStableMemory, buffer_size: Option) -> Box { - let writer = StableWriter::with_memory(memory, 0); - if let Some(buffer_size) = buffer_size { - Box::new(BufferedStableWriter::with_writer(buffer_size, writer)) - } else { - Box::new(writer) - } - } -} - -mod stable_reader_tests { - use super::*; - use rstest::rstest; - use std::io::{Read, Seek}; - - #[rstest] - #[case(None)] - #[case(Some(1))] - #[case(Some(10))] - #[case(Some(100))] - #[case(Some(1000))] - fn reads_all_bytes(#[case] buffer_size: Option) { - let input = vec![1; 10_000]; - let memory = Rc::new(Mutex::new(input.clone())); - let mut reader = build_reader(TestStableMemory::new(memory), buffer_size); - - let mut output = Vec::new(); - reader.read_to_end(&mut output).unwrap(); - - assert_eq!(input, output[..input.len()]); - } - - #[test] - fn check_offset() { - const READ_SIZE: usize = 1025; - - let memory = Rc::new(Mutex::new(vec![1; READ_SIZE])); - let mut reader = StableReader::with_memory(TestStableMemory::new(memory.clone()), 0); - assert_eq!(reader.offset(), 0); - let mut bytes = vec![0; READ_SIZE]; - assert_eq!(reader.read(&mut bytes).unwrap(), READ_SIZE); - assert_eq!(reader.offset(), READ_SIZE as u64); - - let mut reader = BufferedStableReader::with_reader( - READ_SIZE - 1, - StableReader::with_memory(TestStableMemory::new(memory), 0), - ); - assert_eq!(reader.offset(), 0); - let mut bytes = vec![0; READ_SIZE]; - assert_eq!(reader.read(&mut bytes).unwrap(), READ_SIZE); - assert_eq!(reader.offset(), READ_SIZE as u64); - } - - #[test] - fn test_seek() { - const SIZE: usize = 1025; - let memory = Rc::new(Mutex::new((0..SIZE).map(|v| v as u8).collect::>())); - let mut reader = StableReader::with_memory(TestStableMemory::new(memory), 0); - let mut bytes = vec![0_u8; 1]; - - const OFFSET: usize = 200; - reader - .seek(std::io::SeekFrom::Start(OFFSET as u64)) - .unwrap(); - assert_eq!(reader.stream_position().unwrap() as usize, OFFSET); - assert_eq!(reader.read(&mut bytes).unwrap(), 1); - assert_eq!(&bytes, &[OFFSET as u8]); - assert_eq!( - reader.seek(std::io::SeekFrom::End(0)).unwrap(), - WASM_PAGE_SIZE_IN_BYTES - ); - reader - .seek(std::io::SeekFrom::Start(WASM_PAGE_SIZE_IN_BYTES * 2)) - .unwrap(); - // out of bounds so should fail - assert!(reader.read(&mut bytes).is_err()); - } - - fn build_reader(memory: TestStableMemory, buffer_size: Option) -> Box { - let reader = StableReader::with_memory(memory, 0); - if let Some(buffer_size) = buffer_size { - Box::new(BufferedStableReader::with_reader(buffer_size, reader)) - } else { - Box::new(reader) - } - } -} diff --git a/src/ic0/LICENSE b/src/ic0/LICENSE deleted file mode 120000 index 30cff7403..000000000 --- a/src/ic0/LICENSE +++ /dev/null @@ -1 +0,0 @@ -../../LICENSE \ No newline at end of file diff --git a/src/ic0/ic0.txt b/src/ic0/ic0.txt deleted file mode 120000 index ee3cab32a..000000000 --- a/src/ic0/ic0.txt +++ /dev/null @@ -1 +0,0 @@ -../../ic0.txt \ No newline at end of file diff --git a/src/ic0/src/ic0.rs b/src/ic0/src/ic0.rs deleted file mode 100644 index 9a2ed93ed..000000000 --- a/src/ic0/src/ic0.rs +++ /dev/null @@ -1,238 +0,0 @@ -// This file is generated from ic0.txt. -// Don't manually modify it. -#[cfg(target_arch = "wasm32")] -#[link(wasm_import_module = "ic0")] -extern "C" { - pub fn msg_arg_data_size() -> i32; - pub fn msg_arg_data_copy(dst: i32, offset: i32, size: i32); - pub fn msg_caller_size() -> i32; - pub fn msg_caller_copy(dst: i32, offset: i32, size: i32); - pub fn msg_reject_code() -> i32; - pub fn msg_reject_msg_size() -> i32; - pub fn msg_reject_msg_copy(dst: i32, offset: i32, size: i32); - pub fn msg_reply_data_append(src: i32, size: i32); - pub fn msg_reply(); - pub fn msg_reject(src: i32, size: i32); - pub fn msg_cycles_available() -> i64; - pub fn msg_cycles_available128(dst: i32); - pub fn msg_cycles_refunded() -> i64; - pub fn msg_cycles_refunded128(dst: i32); - pub fn msg_cycles_accept(max_amount: i64) -> i64; - pub fn msg_cycles_accept128(max_amount_high: i64, max_amount_low: i64, dst: i32); - pub fn cycles_burn128(amount_high: i64, amount_low: i64, dst: i32); - pub fn canister_self_size() -> i32; - pub fn canister_self_copy(dst: i32, offset: i32, size: i32); - pub fn canister_cycle_balance() -> i64; - pub fn canister_cycle_balance128(dst: i32); - pub fn canister_status() -> i32; - pub fn canister_version() -> i64; - pub fn msg_method_name_size() -> i32; - pub fn msg_method_name_copy(dst: i32, offset: i32, size: i32); - pub fn accept_message(); - pub fn call_new( - callee_src: i32, - callee_size: i32, - name_src: i32, - name_size: i32, - reply_fun: i32, - reply_env: i32, - reject_fun: i32, - reject_env: i32, - ); - pub fn call_on_cleanup(fun: i32, env: i32); - pub fn call_data_append(src: i32, size: i32); - pub fn call_cycles_add(amount: i64); - pub fn call_cycles_add128(amount_high: i64, amount_low: i64); - pub fn call_perform() -> i32; - pub fn stable_size() -> i32; - pub fn stable_grow(new_pages: i32) -> i32; - pub fn stable_write(offset: i32, src: i32, size: i32); - pub fn stable_read(dst: i32, offset: i32, size: i32); - pub fn stable64_size() -> i64; - pub fn stable64_grow(new_pages: i64) -> i64; - pub fn stable64_write(offset: i64, src: i64, size: i64); - pub fn stable64_read(dst: i64, offset: i64, size: i64); - pub fn certified_data_set(src: i32, size: i32); - pub fn data_certificate_present() -> i32; - pub fn data_certificate_size() -> i32; - pub fn data_certificate_copy(dst: i32, offset: i32, size: i32); - pub fn time() -> i64; - pub fn global_timer_set(timestamp: i64) -> i64; - pub fn performance_counter(counter_type: i32) -> i64; - pub fn is_controller(src: i32, size: i32) -> i32; - pub fn in_replicated_execution() -> i32; - pub fn debug_print(src: i32, size: i32); - pub fn trap(src: i32, size: i32); -} - -#[cfg(not(target_arch = "wasm32"))] -#[allow(unused_variables)] -#[allow(clippy::missing_safety_doc)] -#[allow(clippy::too_many_arguments)] -mod non_wasm { - pub unsafe fn msg_arg_data_size() -> i32 { - panic!("msg_arg_data_size should only be called inside canisters."); - } - pub unsafe fn msg_arg_data_copy(dst: i32, offset: i32, size: i32) { - panic!("msg_arg_data_copy should only be called inside canisters."); - } - pub unsafe fn msg_caller_size() -> i32 { - panic!("msg_caller_size should only be called inside canisters."); - } - pub unsafe fn msg_caller_copy(dst: i32, offset: i32, size: i32) { - panic!("msg_caller_copy should only be called inside canisters."); - } - pub unsafe fn msg_reject_code() -> i32 { - panic!("msg_reject_code should only be called inside canisters."); - } - pub unsafe fn msg_reject_msg_size() -> i32 { - panic!("msg_reject_msg_size should only be called inside canisters."); - } - pub unsafe fn msg_reject_msg_copy(dst: i32, offset: i32, size: i32) { - panic!("msg_reject_msg_copy should only be called inside canisters."); - } - pub unsafe fn msg_reply_data_append(src: i32, size: i32) { - panic!("msg_reply_data_append should only be called inside canisters."); - } - pub unsafe fn msg_reply() { - panic!("msg_reply should only be called inside canisters."); - } - pub unsafe fn msg_reject(src: i32, size: i32) { - panic!("msg_reject should only be called inside canisters."); - } - pub unsafe fn msg_cycles_available() -> i64 { - panic!("msg_cycles_available should only be called inside canisters."); - } - pub unsafe fn msg_cycles_available128(dst: i32) { - panic!("msg_cycles_available128 should only be called inside canisters."); - } - pub unsafe fn msg_cycles_refunded() -> i64 { - panic!("msg_cycles_refunded should only be called inside canisters."); - } - pub unsafe fn msg_cycles_refunded128(dst: i32) { - panic!("msg_cycles_refunded128 should only be called inside canisters."); - } - pub unsafe fn msg_cycles_accept(max_amount: i64) -> i64 { - panic!("msg_cycles_accept should only be called inside canisters."); - } - pub unsafe fn msg_cycles_accept128(max_amount_high: i64, max_amount_low: i64, dst: i32) { - panic!("msg_cycles_accept128 should only be called inside canisters."); - } - pub unsafe fn cycles_burn128(amount_high: i64, amount_low: i64, dst: i32) { - panic!("cycles_burn128 should only be called inside canisters."); - } - pub unsafe fn canister_self_size() -> i32 { - panic!("canister_self_size should only be called inside canisters."); - } - pub unsafe fn canister_self_copy(dst: i32, offset: i32, size: i32) { - panic!("canister_self_copy should only be called inside canisters."); - } - pub unsafe fn canister_cycle_balance() -> i64 { - panic!("canister_cycle_balance should only be called inside canisters."); - } - pub unsafe fn canister_cycle_balance128(dst: i32) { - panic!("canister_cycle_balance128 should only be called inside canisters."); - } - pub unsafe fn canister_status() -> i32 { - panic!("canister_status should only be called inside canisters."); - } - pub unsafe fn canister_version() -> i64 { - panic!("canister_version should only be called inside canisters."); - } - pub unsafe fn msg_method_name_size() -> i32 { - panic!("msg_method_name_size should only be called inside canisters."); - } - pub unsafe fn msg_method_name_copy(dst: i32, offset: i32, size: i32) { - panic!("msg_method_name_copy should only be called inside canisters."); - } - pub unsafe fn accept_message() { - panic!("accept_message should only be called inside canisters."); - } - pub unsafe fn call_new( - callee_src: i32, - callee_size: i32, - name_src: i32, - name_size: i32, - reply_fun: i32, - reply_env: i32, - reject_fun: i32, - reject_env: i32, - ) { - panic!("call_new should only be called inside canisters."); - } - pub unsafe fn call_on_cleanup(fun: i32, env: i32) { - panic!("call_on_cleanup should only be called inside canisters."); - } - pub unsafe fn call_data_append(src: i32, size: i32) { - panic!("call_data_append should only be called inside canisters."); - } - pub unsafe fn call_cycles_add(amount: i64) { - panic!("call_cycles_add should only be called inside canisters."); - } - pub unsafe fn call_cycles_add128(amount_high: i64, amount_low: i64) { - panic!("call_cycles_add128 should only be called inside canisters."); - } - pub unsafe fn call_perform() -> i32 { - panic!("call_perform should only be called inside canisters."); - } - pub unsafe fn stable_size() -> i32 { - panic!("stable_size should only be called inside canisters."); - } - pub unsafe fn stable_grow(new_pages: i32) -> i32 { - panic!("stable_grow should only be called inside canisters."); - } - pub unsafe fn stable_write(offset: i32, src: i32, size: i32) { - panic!("stable_write should only be called inside canisters."); - } - pub unsafe fn stable_read(dst: i32, offset: i32, size: i32) { - panic!("stable_read should only be called inside canisters."); - } - pub unsafe fn stable64_size() -> i64 { - panic!("stable64_size should only be called inside canisters."); - } - pub unsafe fn stable64_grow(new_pages: i64) -> i64 { - panic!("stable64_grow should only be called inside canisters."); - } - pub unsafe fn stable64_write(offset: i64, src: i64, size: i64) { - panic!("stable64_write should only be called inside canisters."); - } - pub unsafe fn stable64_read(dst: i64, offset: i64, size: i64) { - panic!("stable64_read should only be called inside canisters."); - } - pub unsafe fn certified_data_set(src: i32, size: i32) { - panic!("certified_data_set should only be called inside canisters."); - } - pub unsafe fn data_certificate_present() -> i32 { - panic!("data_certificate_present should only be called inside canisters."); - } - pub unsafe fn data_certificate_size() -> i32 { - panic!("data_certificate_size should only be called inside canisters."); - } - pub unsafe fn data_certificate_copy(dst: i32, offset: i32, size: i32) { - panic!("data_certificate_copy should only be called inside canisters."); - } - pub unsafe fn time() -> i64 { - panic!("time should only be called inside canisters."); - } - pub unsafe fn global_timer_set(timestamp: i64) -> i64 { - panic!("global_timer_set should only be called inside canisters."); - } - pub unsafe fn performance_counter(counter_type: i32) -> i64 { - panic!("performance_counter should only be called inside canisters."); - } - pub unsafe fn is_controller(src: i32, size: i32) -> i32 { - panic!("is_controller should only be called inside canisters."); - } - pub unsafe fn in_replicated_execution() -> i32 { - panic!("in_replicated_execution should only be called inside canisters."); - } - pub unsafe fn debug_print(src: i32, size: i32) { - panic!("debug_print should only be called inside canisters."); - } - pub unsafe fn trap(src: i32, size: i32) { - panic!("trap should only be called inside canisters."); - } -} - -#[cfg(not(target_arch = "wasm32"))] -pub use non_wasm::*;