diff --git a/.bazelignore b/.bazelignore new file mode 100644 index 00000000000..eb5a316cbd1 --- /dev/null +++ b/.bazelignore @@ -0,0 +1 @@ +target diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index e5f7c85bcb8..8628c222dd7 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,5 +1,5 @@ { - "image": "ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45", + "image": "ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d", "remoteUser": "ubuntu", "privileged": true, "runArgs": [ diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index fc5852efe00..b0a0f96202c 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -27,9 +27,9 @@ /WORKSPACE.bazel @dfinity/idx # [Rust Lang] -rust-toolchain.toml @dfinity/networking -rustfmt.toml @dfinity/networking -deny.toml @dfinity/networking +rust-toolchain.toml @dfinity/consensus +rustfmt.toml @dfinity/consensus +deny.toml @dfinity/consensus clippy.toml @dfinity/ic-interface-owners # [Golang] @@ -50,6 +50,7 @@ go_deps.bzl @dfinity/idx /packages/ic-ledger-hash-of/ @dfinity/finint /packages/pocket-ic/ @dfinity/pocket-ic /packages/ic-ethereum-types/ @dfinity/cross-chain-team +/packages/ic-metrics-assert/ @dfinity/cross-chain-team /packages/ic-sha3/ @dfinity/crypto-team /packages/ic-signature-verification/ @dfinity/crypto-team /packages/ic-vetkd-utils/ @dfinity/crypto-team @@ -91,22 +92,22 @@ go_deps.bzl @dfinity/idx /rs/artifact_pool/ @dfinity/consensus /rs/backup/ @dfinity/consensus /rs/bitcoin/ @dfinity/ic-interface-owners -/rs/bitcoin/adapter/ @dfinity/networking +/rs/bitcoin/adapter/ @dfinity/consensus /rs/bitcoin/ckbtc/ @dfinity/cross-chain-team /rs/bitcoin/mock/ @dfinity/cross-chain-team -/rs/bitcoin/client/ @dfinity/networking +/rs/bitcoin/client/ @dfinity/consensus /rs/bitcoin/consensus/ @dfinity/execution @dfinity/consensus /rs/bitcoin/checker/ @dfinity/cross-chain-team -/rs/bitcoin/service/ @dfinity/networking +/rs/bitcoin/service/ @dfinity/consensus /rs/bitcoin/replica_types/ @dfinity/execution -/rs/bitcoin/validation @dfinity/networking @dfinity/execution +/rs/bitcoin/validation @dfinity/consensus @dfinity/execution /rs/boundary_node/ @dfinity/boundary-node -/rs/canister_client/ @dfinity/networking +/rs/canister_client/ @dfinity/consensus /rs/canister_sandbox/ @dfinity/execution /rs/canonical_state/ @dfinity/ic-message-routing-owners /rs/canonical_state/tree_hash/ @dfinity/ic-message-routing-owners @dfinity/crypto-team /rs/certification/ @dfinity/ic-message-routing-owners @dfinity/crypto-team -/rs/config/ @dfinity/networking +/rs/config/ @dfinity/consensus /rs/config/src/embedders.rs @dfinity/execution /rs/config/src/execution_environment.rs @dfinity/execution /rs/config/src/state_manager.rs @dfinity/ic-message-routing-owners @@ -125,18 +126,18 @@ go_deps.bzl @dfinity/idx /rs/ethereum/ @dfinity/cross-chain-team /rs/execution_environment/ @dfinity/execution /rs/fuzzers/ @dfinity/product-security -/rs/http_endpoints/ @dfinity/networking +/rs/http_endpoints/ @dfinity/consensus /rs/http_endpoints/fuzz/ @dfinity/product-security -/rs/http_endpoints/xnet/ @dfinity/networking @dfinity/ic-message-routing-owners +/rs/http_endpoints/xnet/ @dfinity/consensus @dfinity/ic-message-routing-owners /rs/http_utils/ @dfinity/consensus -/rs/https_outcalls/ @dfinity/networking +/rs/https_outcalls/ @dfinity/consensus /rs/https_outcalls/consensus/ @dfinity/consensus /rs/ic_os/ @dfinity/node /rs/ic_os/fstrim_tool/ @dfinity/node @dfinity/crypto-team /rs/ic_os/nss_icos/ @dfinity/dre /rs/ingress_manager/ @dfinity/consensus /rs/interfaces/ @dfinity/ic-interface-owners -/rs/interfaces/adapter_client/ @dfinity/networking +/rs/interfaces/adapter_client/ @dfinity/consensus /rs/interfaces/certified_stream_store/ @dfinity/ic-message-routing-owners /rs/interfaces/registry/ @dfinity/nns-team /rs/interfaces/src/canister_http.rs @dfinity/consensus @@ -147,22 +148,23 @@ go_deps.bzl @dfinity/idx /rs/interfaces/src/dkg.rs @dfinity/consensus /rs/interfaces/src/execution_environment.rs @dfinity/execution /rs/interfaces/src/messaging.rs @dfinity/ic-message-routing-owners -/rs/interfaces/src/p2p.rs @dfinity/networking -/rs/interfaces/src/p2p/ @dfinity/networking +/rs/interfaces/src/p2p.rs @dfinity/consensus +/rs/interfaces/src/p2p/ @dfinity/consensus +/rs/interfaces/src/vetkd.rs @dfinity/consensus /rs/interfaces/state_manager/ @dfinity/ic-message-routing-owners /rs/ledger_suite/ @dfinity/finint /rs/limits/ @dfinity/ic-interface-owners /rs/memory_tracker/ @dfinity/execution /rs/messaging/ @dfinity/ic-message-routing-owners -/rs/monitoring/ @dfinity/networking -/rs/monitoring/backtrace/ @dfinity/networking @dfinity/ic-message-routing-owners -/rs/monitoring/metrics @dfinity/networking @dfinity/ic-message-routing-owners -/rs/monitoring/pprof/ @dfinity/networking @dfinity/ic-message-routing-owners +/rs/monitoring/ @dfinity/consensus +/rs/monitoring/backtrace/ @dfinity/consensus @dfinity/ic-message-routing-owners +/rs/monitoring/metrics @dfinity/consensus @dfinity/ic-message-routing-owners +/rs/monitoring/pprof/ @dfinity/consensus @dfinity/ic-message-routing-owners /rs/nervous_system/ @dfinity/nns-team /rs/nns/ @dfinity/nns-team /rs/orchestrator/ @dfinity/consensus /rs/orchestrator/src/hostos_upgrade.rs @dfinity/consensus @dfinity/node -/rs/p2p/ @dfinity/networking +/rs/p2p/ @dfinity/consensus /rs/phantom_newtype/ @dfinity/ic-interface-owners /rs/pocket_ic_server/ @dfinity/pocket-ic /rs/prep/ @dfinity/utopia @@ -170,13 +172,13 @@ go_deps.bzl @dfinity/idx /rs/protobuf/def/bitcoin/ @dfinity/execution /rs/protobuf/def/crypto/ @dfinity/crypto-team /rs/protobuf/def/messaging/ @dfinity/ic-message-routing-owners -/rs/protobuf/def/p2p/ @dfinity/networking +/rs/protobuf/def/p2p/ @dfinity/consensus /rs/protobuf/def/registry/ @dfinity/nns-team /rs/protobuf/def/state/ @dfinity/execution @dfinity/ic-message-routing-owners /rs/protobuf/gen/bitcoin/ @dfinity/execution /rs/protobuf/gen/crypto/ @dfinity/crypto-team /rs/protobuf/gen/messaging/ @dfinity/ic-message-routing-owners -/rs/protobuf/gen/p2p/ @dfinity/networking +/rs/protobuf/gen/p2p/ @dfinity/consensus /rs/protobuf/gen/registry/ @dfinity/nns-team /rs/protobuf/gen/state/ @dfinity/execution @dfinity/ic-message-routing-owners /rs/query_stats/ @dfinity/execution @dfinity/consensus @@ -184,7 +186,7 @@ go_deps.bzl @dfinity/idx /rs/registry/ @dfinity/nns-team /rs/registry/helpers/src/crypto.rs @dfinity/crypto-team /rs/registry/helpers/src/crypto/ @dfinity/crypto-team -/rs/registry/helpers/src/firewall.rs @dfinity/networking +/rs/registry/helpers/src/firewall.rs @dfinity/consensus /rs/registry/helpers/src/node.rs @dfinity/node /rs/registry/helpers/src/provisional_whitelist.rs @dfinity/execution /rs/registry/helpers/src/routing_table.rs @dfinity/execution @dfinity/ic-message-routing-owners @@ -192,7 +194,7 @@ go_deps.bzl @dfinity/idx /rs/registry/helpers/src/unassigned_nodes.rs @dfinity/consensus /rs/registry/helpers/tests/root_of_trust.rs @dfinity/crypto-team /rs/replay/ @dfinity/consensus -/rs/replica/ @dfinity/networking +/rs/replica/ @dfinity/consensus /rs/replica_tests/ @dfinity/execution /rs/replicated_state/ @dfinity/execution @dfinity/ic-message-routing-owners /rs/replicated_state/src/canister_state/queues.rs @dfinity/ic-message-routing-owners @@ -204,7 +206,7 @@ go_deps.bzl @dfinity/idx /rs/rust_canisters/backtrace_canister @dfinity/execution /rs/rust_canisters/memory_test/ @dfinity/execution /rs/rust_canisters/call_tree_test/ @dfinity/execution -/rs/rust_canisters/proxy_canister/ @dfinity/networking +/rs/rust_canisters/proxy_canister/ @dfinity/consensus /rs/rust_canisters/response_payload_test/ @dfinity/execution /rs/rust_canisters/stable_structures/ @dfinity/execution /rs/rust_canisters/stable_memory_integrity @dfinity/execution @@ -215,7 +217,7 @@ go_deps.bzl @dfinity/idx /rs/rust_canisters/downstream_calls_test/ @dfinity/ic-message-routing-owners /rs/rust_canisters/random_traffic_test/ @dfinity/ic-message-routing-owners /rs/sns/ @dfinity/nns-team -/rs/starter/ @dfinity/networking +/rs/starter/ @dfinity/consensus /rs/state_layout/ @dfinity/ic-message-routing-owners /rs/state_machine_tests/ @dfinity/ic-message-routing-owners @dfinity/pocket-ic /rs/state_manager/ @dfinity/ic-message-routing-owners @@ -228,7 +230,7 @@ go_deps.bzl @dfinity/idx /rs/test_utilities/embedders/ @dfinity/execution /rs/test_utilities/execution_environment/ @dfinity/execution /rs/test_utilities/in_memory_logger/ @dfinity/crypto-team -/rs/test_utilities/metrics @dfinity/networking @dfinity/ic-message-routing-owners +/rs/test_utilities/metrics @dfinity/consensus @dfinity/ic-message-routing-owners /rs/test_utilities/src/crypto.rs @dfinity/crypto-team /rs/test_utilities/src/crypto/ @dfinity/crypto-team /rs/test_utilities/src/cycles_account_manager.rs @dfinity/execution @@ -238,7 +240,7 @@ go_deps.bzl @dfinity/idx /rs/tests/idx/ @dfinity/idx /rs/tests/testnets/ @dfinity/idx /rs/tests/research @dfinity/research -/rs/tests/driver/src/driver/simulate_network.rs @dfinity/networking @dfinity/idx +/rs/tests/driver/src/driver/simulate_network.rs @dfinity/consensus @dfinity/idx /rs/tests/boundary_nodes/ @dfinity/boundary-node /rs/tests/ckbtc/ @dfinity/cross-chain-team /rs/tests/consensus/ @dfinity/consensus @@ -248,7 +250,8 @@ go_deps.bzl @dfinity/idx /rs/tests/execution/ @dfinity/execution /rs/tests/financial_integrations/ @dfinity/finint /rs/tests/message_routing/ @dfinity/ic-message-routing-owners -/rs/tests/networking/ @dfinity/networking +/rs/tests/nested/ @dfinity/node +/rs/tests/networking/ @dfinity/consensus /rs/tests/nns/ @dfinity/nns-team /rs/tests/node/ @dfinity/node /rs/tests/query_stats/ @dfinity/execution @dfinity/consensus @@ -264,7 +267,7 @@ go_deps.bzl @dfinity/idx /rs/types/ @dfinity/ic-interface-owners /rs/types/exhaustive_derive/ @dfinity/consensus /rs/types/management_canister_types/ @dfinity/execution -/rs/types/types/src/artifact.rs @dfinity/consensus @dfinity/networking +/rs/types/types/src/artifact.rs @dfinity/consensus /rs/types/types/src/batch.rs @dfinity/consensus /rs/types/types/src/batch/ @dfinity/consensus /rs/types/types/src/canister_http.rs @dfinity/execution @dfinity/consensus @@ -278,6 +281,7 @@ go_deps.bzl @dfinity/idx /rs/types/wasm_types/ @dfinity/execution /rs/universal_canister/ @dfinity/execution /rs/utils/ @dfinity/ic-interface-owners +/rs/utils/thread/ @dfinity/ic-message-routing-owners /rs/utils/ensure/ @dfinity/finint /rs/validator/ @dfinity/crypto-team /rs/wasm_transform/ @dfinity/execution diff --git a/.github/actions/bazel-test-all/action.yaml b/.github/actions/bazel-test-all/action.yaml index 2317de7b86b..211d57beed9 100644 --- a/.github/actions/bazel-test-all/action.yaml +++ b/.github/actions/bazel-test-all/action.yaml @@ -17,8 +17,10 @@ inputs: required: false default: '--output_base=/var/tmp/bazel-output/' BUILDEVENT_APIKEY: - required: true - SSH_PRIVATE_KEY: + required: false + SSH_PRIVATE_KEY_BACKUP_POD: + required: false + GPG_PASSPHRASE: required: false runs: @@ -30,23 +32,25 @@ runs: run: | set +e # manual error handling to ensure we can run some post-build commands - # temporarily set permissions again until we can figure out issue - if [ -e /cache ]; then - sudo chown -RL 1001:1001 /cache - fi - - if [ -n "$SSH_PRIVATE_KEY" ]; then + if [ -n "$SSH_PRIVATE_KEY_BACKUP_POD" ]; then # The following adds the SSH private key to the ssh-agent such that CI can SSH into the backup pod. - test -z "${SSH_AUTH_SOCK:-}" && { eval "$(ssh-agent -s)"; ssh-add - <<< "${SSH_PRIVATE_KEY}"; } + test -z "${SSH_AUTH_SOCK:-}" && { eval "$(ssh-agent -s)"; ssh-add - <<< "${SSH_PRIVATE_KEY_BACKUP_POD}"; } rm -rf ~/.ssh mkdir -p ~/.ssh chmod 0700 ~/.ssh echo -e "Host *\nUser github-runner\n" > ~/.ssh/config fi + # unset honeycomb api key but use it latter for exporter + # TODO: remove exporter when users can use superset + KEY=${BUILDEVENT_APIKEY:-""} + unset BUILDEVENT_APIKEY + ${GITHUB_WORKSPACE}/ci/bazel-scripts/main.sh BAZEL_EXIT_CODE="$?" + export BUILDEVENT_APIKEY="$KEY" + if [ -n "$BUILDEVENT_APIKEY" ] && [ -f ./bazel-bep.pb ]; then # avoid output unless an error occurs during bes export. This ensures # only the (more relevant) output from the main bazel command is shown. @@ -56,7 +60,13 @@ runs: cat "$exportout" fi rm "$exportout" + echo "BEP events exported to honeycomb!" + fi + if [ -n "$GPG_PASSPHRASE" ] && [ -f ./bazel-bep.pb ]; then + gpg --symmetric --cipher-algo AES256 -o bazel-bep.pb.gpg \ + --passphrase "$GPG_PASSPHRASE" --batch --yes bazel-bep.pb fi + rm -f bazel-bep.pb # output node name to gihub step summary [ -n "${NODE_NAME:-}" ] && echo "Run on node: $NODE_NAME" >>$GITHUB_STEP_SUMMARY @@ -74,4 +84,5 @@ runs: CI_JOB_URL: "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" CI_PULL_REQUEST_TARGET_BRANCH_NAME: ${{ github.event.pull_request.base.ref }} MERGE_BASE_SHA: ${{ github.event.pull_request.base.sha }} - SSH_PRIVATE_KEY: ${{ inputs.SSH_PRIVATE_KEY }} + SSH_PRIVATE_KEY_BACKUP_POD: ${{ inputs.SSH_PRIVATE_KEY_BACKUP_POD }} + GPG_PASSPHRASE: ${{ inputs.GPG_PASSPHRASE }} diff --git a/.github/workflows-source/ci-main.yml b/.github/workflows-source/ci-main.yml index ae371889aee..4cd27322afa 100644 --- a/.github/workflows-source/ci-main.yml +++ b/.github/workflows-source/ci-main.yml @@ -27,12 +27,12 @@ env: CI_EVENT_NAME: ${{ github.event_name }} BRANCH_NAME: ${{ github.head_ref || github.ref_name }} CI_RUN_ID: ${{ github.run_id }} - RUSTFLAGS: "--remap-path-prefix=${CI_PROJECT_DIR}=/ic" + RUSTFLAGS: "--remap-path-prefix=${{ github.workspace }}=/ic" BUILDEVENT_DATASET: "github-ci-dfinity" anchors: image: &image - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d dind-large-setup: &dind-large-setup runs-on: labels: dind-large @@ -49,13 +49,12 @@ anchors: container: <<: *image timeout-minutes: 30 - docker-login: &docker-login - name: Login to Dockerhub + before-script: &before-script + name: Before script + id: before-script shell: bash - run: ./ci/scripts/docker-login.sh - env: - DOCKER_HUB_USER: ${{ vars.DOCKER_HUB_USER }} - DOCKER_HUB_PASSWORD_RO: ${{ secrets.DOCKER_HUB_PASSWORD_RO }} + run: | + [ -n "${NODE_NAME:-}" ] && echo "Node: $NODE_NAME" checkout: &checkout name: Checkout uses: actions/checkout@v4 @@ -87,7 +86,7 @@ anchors: if-no-files-found: ignore compression-level: 9 path: | - bazel-bep.pb + bazel-bep.pb.gpg profile.json jobs: @@ -95,17 +94,17 @@ jobs: name: Bazel Test All <<: *dind-large-setup runs-on: - group: zh1 + group: ln1 labels: dind-large env: - AWS_SHARED_CREDENTIALS_CONTENT: ${{ secrets.AWS_SHARED_CREDENTIALS_FILE }} + CLOUD_CREDENTIALS_CONTENT: ${{ secrets.CLOUD_CREDENTIALS_CONTENT }} # Only run ci/bazel-scripts/diff.sh on PRs that are not labeled with "CI_ALL_BAZEL_TARGETS". OVERRIDE_DIDC_CHECK: ${{ contains(github.event.pull_request.labels.*.name, 'CI_OVERRIDE_DIDC_CHECK') }} CI_OVERRIDE_BUF_BREAKING: ${{ contains(github.event.pull_request.labels.*.name, 'CI_OVERRIDE_BUF_BREAKING') }} RUN_ON_DIFF_ONLY: ${{ github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'CI_ALL_BAZEL_TARGETS') }} steps: - <<: *checkout - - <<: *docker-login + - <<: *before-script - name: Set BAZEL_EXTRA_ARGS shell: bash run: | @@ -153,7 +152,8 @@ jobs: BAZEL_CI_CONFIG: "--config=ci --repository_cache=/cache/bazel" # check if PR title contains release and set timeout filters accordingly BAZEL_EXTRA_ARGS: ${{ env.BAZEL_EXTRA_ARGS }} - BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_API_TOKEN }} + BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_TOKEN }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - <<: *bazel-bep - <<: *bazel-upload @@ -163,7 +163,7 @@ jobs: if: ${{ contains(github.event.pull_request.labels.*.name, 'CI_BUILD_CHECK') }} steps: - <<: *checkout - - <<: *docker-login + - <<: *before-script - name: Run bazel build --config=check //rs/... id: bazel-build-config-check uses: ./.github/actions/bazel-test-all/ @@ -171,6 +171,7 @@ jobs: BAZEL_COMMAND: "build" BAZEL_TARGETS: "//rs/..." BAZEL_CI_CONFIG: "--config=check --config=ci --keep_going" + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - <<: *bazel-bep bazel-test-macos-intel: @@ -190,25 +191,27 @@ jobs: run: | echo "/usr/local/bin" >> $GITHUB_PATH echo "$HOME/.cargo/bin:" >> $GITHUB_PATH - - <<: *docker-login + # use llvm-clang instead of apple's + echo "CC=/usr/local/opt/llvm/bin/clang" >> "$GITHUB_ENV" - name: Run Bazel Test Darwin x86-64 id: bazel-test-darwin-x86-64 uses: ./.github/actions/bazel-test-all/ env: - AWS_SHARED_CREDENTIALS_CONTENT: ${{ secrets.AWS_SHARED_CREDENTIALS_FILE }} + CLOUD_CREDENTIALS_CONTENT: ${{ secrets.CLOUD_CREDENTIALS_CONTENT }} with: BAZEL_CI_CONFIG: "--config=ci --config macos_ci" BAZEL_COMMAND: test BAZEL_EXTRA_ARGS: '--test_tag_filters=test_macos' - BAZEL_STARTUP_ARGS: "--output_base /var/tmp/bazel-output/${CI_RUN_ID}" + BAZEL_STARTUP_ARGS: "--output_base /var/tmp/bazel-output/" BAZEL_TARGETS: "//rs/... //publish/binaries/..." - BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_API_TOKEN }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - <<: *bazel-bep - name: Purge Bazel Output if: always() shell: bash run: | - sudo rm -rf /private/var/tmp/bazel-output + # Clean up the output base for the next run + sudo rm -rf /var/tmp/bazel-output bazel-build-fuzzers: name: Bazel Build Fuzzers @@ -222,7 +225,7 @@ jobs: BAZEL_COMMAND: "build" BAZEL_TARGETS: "//rs/..." BAZEL_EXTRA_ARGS: "--keep_going --config=fuzzing --build_tag_filters=libfuzzer" - BUILDEVENT_APIKEY: ${{ secrets. HONEYCOMB_API_TOKEN }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - <<: *bazel-bep bazel-build-fuzzers-afl: @@ -237,7 +240,7 @@ jobs: BAZEL_COMMAND: "build" BAZEL_TARGETS: "//rs/..." BAZEL_EXTRA_ARGS: "--keep_going --config=afl" - BUILDEVENT_APIKEY: ${{ secrets. HONEYCOMB_API_TOKEN }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - <<: *bazel-bep python-ci-tests: @@ -273,7 +276,7 @@ jobs: if: ${{ github.event_name != 'merge_group' }} steps: - <<: *checkout - - <<: *docker-login + - <<: *before-script - name: Run Build IC id: build-ic shell: bash @@ -284,13 +287,11 @@ jobs: rm -rf "/cache/job/${CI_JOB_NAME}/${CI_RUN_ID}" mkdir -p "/cache/job/${CI_JOB_NAME}/${CI_RUN_ID}/artifacts" ln -s "/cache/job/${CI_JOB_NAME}/${CI_RUN_ID}/artifacts" /__w/$REPO_NAME/$REPO_NAME/artifacts - buildevents cmd "$CI_RUN_ID" "$CI_JOB_NAME" build-command -- \ - "$CI_PROJECT_DIR"/ci/scripts/run-build-ic.sh + "$CI_PROJECT_DIR"/ci/scripts/run-build-ic.sh rm -rf "/cache/job/${CI_JOB_NAME}/${CI_RUN_ID}" env: BAZEL_COMMAND: "build" MERGE_BASE_SHA: ${{ github.event.pull_request.base.sha }} - BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_API_TOKEN }} BRANCH_HEAD_SHA: ${{ github.event.pull_request.head.sha }} RUN_ON_DIFF_ONLY: ${{ github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'CI_ALL_BAZEL_TARGETS') }} - name: Upload build-ic.tar @@ -379,12 +380,10 @@ jobs: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' shell: bash - env: - BUILDEVENT_APIKEY: ${{ secrets. HONEYCOMB_API_TOKEN }} run: | set -eExuo pipefail - buildevents cmd "$CI_RUN_ID" "$CI_JOB_NAME" build-command -- \ - "$CI_PROJECT_DIR"/ci/scripts/rust-lint.sh + export CARGO_TERM_COLOR=always # ensure output has colors + "$CI_PROJECT_DIR"/ci/scripts/rust-lint.sh cargo-build-release-linux: name: Cargo Build Release Linux @@ -412,9 +411,7 @@ jobs: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' shell: bash - env: - BUILDEVENT_APIKEY: ${{ secrets. HONEYCOMB_API_TOKEN }} run: | set -eExuo pipefail - buildevents cmd "$CI_RUN_ID" "$CI_JOB_NAME" build-command -- \ - cargo build --release --locked + export CARGO_TERM_COLOR=always # ensure output has colors + cargo build --release --locked diff --git a/.github/workflows-source/ci-pr-only.yml b/.github/workflows-source/ci-pr-only.yml index 6d13482cbb7..2072e4571ea 100644 --- a/.github/workflows-source/ci-pr-only.yml +++ b/.github/workflows-source/ci-pr-only.yml @@ -17,7 +17,7 @@ env: anchors: image: &image - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d dind-small-setup: &dind-small-setup timeout-minutes: 30 runs-on: @@ -64,17 +64,6 @@ jobs: set -euo pipefail cd "${GITHUB_WORKSPACE}"/bin ./build-all-fuzzers.sh --zip - - name: Upload bazel-bep - if: always() - uses: actions/upload-artifact@v4 - with: - name: ${{ github.job }}-bep - retention-days: 14 - if-no-files-found: ignore - compression-level: 9 - path: | - bazel-bep.pb - profile.json lock-generate: name: Lock Generate diff --git a/.github/workflows-source/release-testing.yml b/.github/workflows-source/release-testing.yml index 9b7c1c854d7..66e64b43fe4 100644 --- a/.github/workflows-source/release-testing.yml +++ b/.github/workflows-source/release-testing.yml @@ -23,7 +23,7 @@ env: anchors: image: &image - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d dind-large-setup: &dind-large-setup runs-on: group: zh1 @@ -40,13 +40,12 @@ anchors: uses: actions/checkout@v4 with: ref: ${{ github.event.workflow_run.head_branch }} - docker-login: &docker-login - name: Login to Dockerhub + before-script: &before-script + name: Before script + id: before-script shell: bash - run: ./ci/scripts/docker-login.sh - env: - DOCKER_HUB_USER: ${{ vars.DOCKER_HUB_USER }} - DOCKER_HUB_PASSWORD_RO: ${{ secrets.DOCKER_HUB_PASSWORD_RO }} + run: | + [ -n "${NODE_NAME:-}" ] && echo "Node: $NODE_NAME" bazel-bep: &bazel-bep name: Upload bazel-bep # runs only if previous step succeeded or failed; @@ -59,7 +58,7 @@ anchors: if-no-files-found: ignore compression-level: 9 path: | - bazel-bep.pb + bazel-bep.pb.gpg profile.json jobs: @@ -73,7 +72,7 @@ jobs: <<: *dind-large-setup steps: - <<: *checkout - - <<: *docker-login + - <<: *before-script - name: Run Bazel System Test Nightly id: bazel-test-all uses: ./.github/actions/bazel-test-all/ @@ -82,7 +81,8 @@ jobs: BAZEL_TARGETS: "//rs/tests/..." BAZEL_CI_CONFIG: "--config=ci --repository_cache=/cache/bazel" BAZEL_EXTRA_ARGS: "--keep_going --test_tag_filters=system_test_nightly" - BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_API_TOKEN }} + BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_TOKEN }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - <<: *bazel-bep bazel-system-test-staging: @@ -90,7 +90,7 @@ jobs: <<: *dind-large-setup steps: - <<: *checkout - - <<: *docker-login + - <<: *before-script - name: Run Bazel System Test Staging id: bazel-test-all uses: ./.github/actions/bazel-test-all/ @@ -99,7 +99,8 @@ jobs: BAZEL_TARGETS: "//rs/tests/..." BAZEL_CI_CONFIG: "--config=ci --repository_cache=/cache/bazel" BAZEL_EXTRA_ARGS: "--keep_going --test_tag_filters=system_test_staging" - BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_API_TOKEN }} + BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_TOKEN }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - <<: *bazel-bep bazel-system-test-hotfix: @@ -108,7 +109,7 @@ jobs: timeout-minutes: 90 steps: - <<: *checkout - - <<: *docker-login + - <<: *before-script - name: Run Bazel Test All id: bazel-test-all uses: ./.github/actions/bazel-test-all/ @@ -117,7 +118,8 @@ jobs: BAZEL_TARGETS: "//rs/tests/..." BAZEL_CI_CONFIG: "--config=ci --repository_cache=/cache/bazel" BAZEL_EXTRA_ARGS: "--keep_going --test_tag_filters=system_test_hotfix" - BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_API_TOKEN }} + BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_TOKEN }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - <<: *bazel-bep dependency-scan-release-cut: @@ -135,7 +137,7 @@ jobs: REPO_NAME: ${{ github.repository }} steps: - <<: *checkout - - <<: *docker-login + - <<: *before-script - name: Set up Python uses: actions/setup-python@v5 with: @@ -182,7 +184,7 @@ jobs: <<: *dind-large-setup steps: - <<: *checkout - - <<: *docker-login + - <<: *before-script - name: Run qualification for version ${{ matrix.version }} from the tip of the branch uses: ./.github/actions/bazel-test-all/ with: @@ -190,7 +192,8 @@ jobs: BAZEL_TARGETS: "//rs/tests/dre:guest_os_qualification" BAZEL_CI_CONFIG: "--config=systest --repository_cache=/cache/bazel" BAZEL_EXTRA_ARGS: "--keep_going --test_timeout=7200 --test_env=OLD_VERSION=${{ matrix.version }}" - BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_API_TOKEN }} + BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_TOKEN }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - <<: *bazel-bep name: Upload bazel bep for version ${{ matrix.version }} with: diff --git a/.github/workflows-source/schedule-daily.yml b/.github/workflows-source/schedule-daily.yml index d8546002961..a9384c7ae7d 100644 --- a/.github/workflows-source/schedule-daily.yml +++ b/.github/workflows-source/schedule-daily.yml @@ -16,7 +16,7 @@ env: anchors: image: &image - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d dind-large-setup: &dind-large-setup runs-on: group: zh1 @@ -31,13 +31,12 @@ anchors: checkout: &checkout name: Checkout uses: actions/checkout@v4 - docker-login: &docker-login - name: Login to Dockerhub + before-script: &before-script + name: Before script + id: before-script shell: bash - run: ./ci/scripts/docker-login.sh - env: - DOCKER_HUB_USER: ${{ vars.DOCKER_HUB_USER }} - DOCKER_HUB_PASSWORD_RO: ${{ secrets.DOCKER_HUB_PASSWORD_RO }} + run: | + [ -n "${NODE_NAME:-}" ] && echo "Node: $NODE_NAME" bazel-bep: &bazel-bep name: Upload bazel-bep # runs only if previous step succeeded or failed; @@ -50,7 +49,7 @@ anchors: if-no-files-found: ignore compression-level: 9 path: | - bazel-bep.pb + bazel-bep.pb.gpg profile.json jobs: @@ -64,7 +63,7 @@ jobs: labels: dind-large steps: - <<: *checkout - - <<: *docker-login + - <<: *before-script - name: Run Bazel Launch Bare Metal shell: bash run: | @@ -108,7 +107,7 @@ jobs: timeout-minutes: 720 # 12 hours steps: - <<: *checkout - - <<: *docker-login + - <<: *before-script - name: Run FI Tests Nightly id: bazel-test-all uses: ./.github/actions/bazel-test-all/ @@ -117,8 +116,9 @@ jobs: BAZEL_TARGETS: "//rs/ledger_suite/..." BAZEL_CI_CONFIG: "--config=ci --repository_cache=/cache/bazel" BAZEL_EXTRA_ARGS: "--keep_going --test_tag_filters=fi_tests_nightly --test_env=SSH_AUTH_SOCK --test_timeout=43200" - BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_API_TOKEN }} - SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} + BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_TOKEN }} + SSH_PRIVATE_KEY_BACKUP_POD: ${{ secrets.SSH_PRIVATE_KEY_BACKUP_POD }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - <<: *bazel-bep nns-tests-nightly: @@ -127,7 +127,7 @@ jobs: timeout-minutes: 20 steps: - <<: *checkout - - <<: *docker-login + - <<: *before-script - name: Run NNS Tests Nightly id: bazel-test-all uses: ./.github/actions/bazel-test-all/ @@ -136,8 +136,9 @@ jobs: BAZEL_TARGETS: "//rs/nns/..." BAZEL_CI_CONFIG: "--config=ci --repository_cache=/cache/bazel" BAZEL_EXTRA_ARGS: "--keep_going --test_tag_filters=nns_tests_nightly --test_env=SSH_AUTH_SOCK --test_env=NNS_CANISTER_UPGRADE_SEQUENCE=all" - BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_API_TOKEN }} - SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} + BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_TOKEN }} + SSH_PRIVATE_KEY_BACKUP_POD: ${{ secrets.SSH_PRIVATE_KEY_BACKUP_POD }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - <<: *bazel-bep system-tests-benchmarks-nightly: @@ -146,7 +147,7 @@ jobs: timeout-minutes: 480 steps: - <<: *checkout - - <<: *docker-login + - <<: *before-script - name: Set Benchmark Targets shell: bash run: | @@ -161,7 +162,8 @@ jobs: BAZEL_CI_CONFIG: "--config=ci --repository_cache=/cache/bazel" # note: there's just one performance cluster, so the job can't be parallelized BAZEL_EXTRA_ARGS: "--test_tag_filters=system_test_benchmark --//bazel:enable_upload_perf_systest_results=True --keep_going --jobs 1" - BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_API_TOKEN }} + BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_TOKEN }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - <<: *bazel-bep - name: Post Slack Notification uses: slackapi/slack-github-action@6c661ce58804a1a20f6dc5fbee7f0381b469e001 # v1.25.0 diff --git a/.github/workflows-source/schedule-hourly.yml b/.github/workflows-source/schedule-hourly.yml index 580333253f4..607e449e9ea 100644 --- a/.github/workflows-source/schedule-hourly.yml +++ b/.github/workflows-source/schedule-hourly.yml @@ -16,7 +16,7 @@ env: anchors: image: &image - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d dind-large-setup: &dind-large-setup runs-on: labels: dind-large @@ -30,13 +30,12 @@ anchors: checkout: &checkout name: Checkout uses: actions/checkout@v4 - docker-login: &docker-login - name: Login to Dockerhub + before-script: &before-script + name: Before script + id: before-script shell: bash - run: ./ci/scripts/docker-login.sh - env: - DOCKER_HUB_USER: ${{ vars.DOCKER_HUB_USER }} - DOCKER_HUB_PASSWORD_RO: ${{ secrets.DOCKER_HUB_PASSWORD_RO }} + run: | + [ -n "${NODE_NAME:-}" ] && echo "Node: $NODE_NAME" bazel-bep: &bazel-bep name: Upload bazel-bep # runs only if previous step succeeded or failed; @@ -49,7 +48,7 @@ anchors: if-no-files-found: ignore compression-level: 9 path: | - bazel-bep.pb + bazel-bep.pb.gpg profile.json jobs: @@ -58,15 +57,16 @@ jobs: <<: *dind-large-setup steps: - <<: *checkout - - <<: *docker-login + - <<: *before-script - name: Run Bazel Build All No Cache uses: ./.github/actions/bazel-test-all/ env: - AWS_SHARED_CREDENTIALS_CONTENT: ${{ secrets.AWS_SHARED_CREDENTIALS_FILE }} + CLOUD_CREDENTIALS_CONTENT: ${{ secrets.CLOUD_CREDENTIALS_CONTENT }} with: BAZEL_CI_CONFIG: "--config=ci" BAZEL_COMMAND: "build" BAZEL_EXTRA_ARGS: "--repository_cache= --disk_cache= --noremote_accept_cached --remote_instance_name=${CI_COMMIT_SHA} --@rules_rust//rust/settings:pipelined_compilation=True" + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - <<: *bazel-bep bazel-system-test-hourly: @@ -77,7 +77,7 @@ jobs: labels: dind-large steps: - <<: *checkout - - <<: *docker-login + - <<: *before-script - name: Run Bazel System Test Hourly id: bazel-test-all uses: ./.github/actions/bazel-test-all/ @@ -86,7 +86,8 @@ jobs: BAZEL_TARGETS: "//rs/..." BAZEL_CI_CONFIG: "--config=ci --repository_cache=/cache/bazel" BAZEL_EXTRA_ARGS: "--keep_going --test_tag_filters=system_test_hourly" - BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_API_TOKEN }} + BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_TOKEN }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - <<: *bazel-bep bazel-run-fuzzers-hourly: diff --git a/.github/workflows/anonymization-backend-release.yml b/.github/workflows/anonymization-backend-release.yml index 6cb04a8c820..3f44f4cd37f 100644 --- a/.github/workflows/anonymization-backend-release.yml +++ b/.github/workflows/anonymization-backend-release.yml @@ -33,7 +33,7 @@ jobs: labels: dind-large container: - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d options: >- -e NODE_NAME --privileged --cgroupns host -v /cache:/cache -v /var/sysimage:/var/sysimage -v /var/tmp:/var/tmp -v /ceph-s3-info:/ceph-s3-info diff --git a/.github/workflows/ci-generate-ci.yml b/.github/workflows/ci-generate-ci.yml index 17fc90db937..1ecf196331e 100644 --- a/.github/workflows/ci-generate-ci.yml +++ b/.github/workflows/ci-generate-ci.yml @@ -47,10 +47,11 @@ jobs: if [ -n "$(git status --porcelain)" ]; then git config --global user.name "IDX GitHub Automation" - git config --global user.email "IDX GitHub Automation" + git config --global user.email "idx@dfinity.org" git add . git commit -m "IDX GitHub Automation" git push + exit 1 else echo "git working tree clean - no changes to be committed" fi diff --git a/.github/workflows/ci-main.yml b/.github/workflows/ci-main.yml index e69dad8e241..2397ae12455 100644 --- a/.github/workflows/ci-main.yml +++ b/.github/workflows/ci-main.yml @@ -24,21 +24,21 @@ env: CI_EVENT_NAME: ${{ github.event_name }} BRANCH_NAME: ${{ github.head_ref || github.ref_name }} CI_RUN_ID: ${{ github.run_id }} - RUSTFLAGS: "--remap-path-prefix=${CI_PROJECT_DIR}=/ic" + RUSTFLAGS: "--remap-path-prefix=${{ github.workspace }}=/ic" BUILDEVENT_DATASET: "github-ci-dfinity" jobs: bazel-test-all: name: Bazel Test All container: - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d options: >- -e NODE_NAME --privileged --cgroupns host -v /cache:/cache -v /var/sysimage:/var/sysimage -v /var/tmp:/var/tmp -v /ceph-s3-info:/ceph-s3-info timeout-minutes: 90 runs-on: - group: zh1 + group: ln1 labels: dind-large env: - AWS_SHARED_CREDENTIALS_CONTENT: ${{ secrets.AWS_SHARED_CREDENTIALS_FILE }} + CLOUD_CREDENTIALS_CONTENT: ${{ secrets.CLOUD_CREDENTIALS_CONTENT }} # Only run ci/bazel-scripts/diff.sh on PRs that are not labeled with "CI_ALL_BAZEL_TARGETS". OVERRIDE_DIDC_CHECK: ${{ contains(github.event.pull_request.labels.*.name, 'CI_OVERRIDE_DIDC_CHECK') }} CI_OVERRIDE_BUF_BREAKING: ${{ contains(github.event.pull_request.labels.*.name, 'CI_OVERRIDE_BUF_BREAKING') }} @@ -48,12 +48,11 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: ${{ github.event_name == 'pull_request' && 256 || 0 }} - - name: Login to Dockerhub + - name: Before script + id: before-script shell: bash - run: ./ci/scripts/docker-login.sh - env: - DOCKER_HUB_USER: ${{ vars.DOCKER_HUB_USER }} - DOCKER_HUB_PASSWORD_RO: ${{ secrets.DOCKER_HUB_PASSWORD_RO }} + run: | + [ -n "${NODE_NAME:-}" ] && echo "Node: $NODE_NAME" - name: Set BAZEL_EXTRA_ARGS shell: bash run: | @@ -101,7 +100,8 @@ jobs: BAZEL_CI_CONFIG: "--config=ci --repository_cache=/cache/bazel" # check if PR title contains release and set timeout filters accordingly BAZEL_EXTRA_ARGS: ${{ env.BAZEL_EXTRA_ARGS }} - BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_API_TOKEN }} + BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_TOKEN }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - name: Upload bazel-bep # runs only if previous step succeeded or failed; # we avoid collecting artifacts of jobs that were cancelled @@ -113,7 +113,7 @@ jobs: if-no-files-found: ignore compression-level: 9 path: | - bazel-bep.pb + bazel-bep.pb.gpg profile.json - name: Upload bazel-targets uses: actions/upload-artifact@v4 @@ -127,7 +127,7 @@ jobs: runs-on: labels: dind-large container: - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d options: >- -e NODE_NAME --privileged --cgroupns host -v /cache:/cache -v /var/sysimage:/var/sysimage -v /var/tmp:/var/tmp -v /ceph-s3-info:/ceph-s3-info timeout-minutes: 90 @@ -138,12 +138,11 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: ${{ github.event_name == 'pull_request' && 256 || 0 }} - - name: Login to Dockerhub + - name: Before script + id: before-script shell: bash - run: ./ci/scripts/docker-login.sh - env: - DOCKER_HUB_USER: ${{ vars.DOCKER_HUB_USER }} - DOCKER_HUB_PASSWORD_RO: ${{ secrets.DOCKER_HUB_PASSWORD_RO }} + run: | + [ -n "${NODE_NAME:-}" ] && echo "Node: $NODE_NAME" - name: Run bazel build --config=check //rs/... id: bazel-build-config-check uses: ./.github/actions/bazel-test-all/ @@ -151,6 +150,7 @@ jobs: BAZEL_COMMAND: "build" BAZEL_TARGETS: "//rs/..." BAZEL_CI_CONFIG: "--config=check --config=ci --keep_going" + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - name: Upload bazel-bep # runs only if previous step succeeded or failed; # we avoid collecting artifacts of jobs that were cancelled @@ -162,7 +162,7 @@ jobs: if-no-files-found: ignore compression-level: 9 path: | - bazel-bep.pb + bazel-bep.pb.gpg profile.json bazel-test-macos-intel: name: Bazel Test macOS Intel @@ -183,24 +183,20 @@ jobs: run: | echo "/usr/local/bin" >> $GITHUB_PATH echo "$HOME/.cargo/bin:" >> $GITHUB_PATH - - name: Login to Dockerhub - shell: bash - run: ./ci/scripts/docker-login.sh - env: - DOCKER_HUB_USER: ${{ vars.DOCKER_HUB_USER }} - DOCKER_HUB_PASSWORD_RO: ${{ secrets.DOCKER_HUB_PASSWORD_RO }} + # use llvm-clang instead of apple's + echo "CC=/usr/local/opt/llvm/bin/clang" >> "$GITHUB_ENV" - name: Run Bazel Test Darwin x86-64 id: bazel-test-darwin-x86-64 uses: ./.github/actions/bazel-test-all/ env: - AWS_SHARED_CREDENTIALS_CONTENT: ${{ secrets.AWS_SHARED_CREDENTIALS_FILE }} + CLOUD_CREDENTIALS_CONTENT: ${{ secrets.CLOUD_CREDENTIALS_CONTENT }} with: BAZEL_CI_CONFIG: "--config=ci --config macos_ci" BAZEL_COMMAND: test BAZEL_EXTRA_ARGS: '--test_tag_filters=test_macos' - BAZEL_STARTUP_ARGS: "--output_base /var/tmp/bazel-output/${CI_RUN_ID}" + BAZEL_STARTUP_ARGS: "--output_base /var/tmp/bazel-output/" BAZEL_TARGETS: "//rs/... //publish/binaries/..." - BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_API_TOKEN }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - name: Upload bazel-bep # runs only if previous step succeeded or failed; # we avoid collecting artifacts of jobs that were cancelled @@ -212,19 +208,20 @@ jobs: if-no-files-found: ignore compression-level: 9 path: | - bazel-bep.pb + bazel-bep.pb.gpg profile.json - name: Purge Bazel Output if: always() shell: bash run: | - sudo rm -rf /private/var/tmp/bazel-output + # Clean up the output base for the next run + sudo rm -rf /var/tmp/bazel-output bazel-build-fuzzers: name: Bazel Build Fuzzers runs-on: labels: dind-large container: - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d options: >- -e NODE_NAME --privileged --cgroupns host -v /cache:/cache -v /var/sysimage:/var/sysimage -v /var/tmp:/var/tmp -v /ceph-s3-info:/ceph-s3-info timeout-minutes: 90 @@ -240,7 +237,7 @@ jobs: BAZEL_COMMAND: "build" BAZEL_TARGETS: "//rs/..." BAZEL_EXTRA_ARGS: "--keep_going --config=fuzzing --build_tag_filters=libfuzzer" - BUILDEVENT_APIKEY: ${{ secrets. HONEYCOMB_API_TOKEN }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - name: Upload bazel-bep # runs only if previous step succeeded or failed; # we avoid collecting artifacts of jobs that were cancelled @@ -252,14 +249,14 @@ jobs: if-no-files-found: ignore compression-level: 9 path: | - bazel-bep.pb + bazel-bep.pb.gpg profile.json bazel-build-fuzzers-afl: name: Bazel Build Fuzzers AFL runs-on: labels: dind-large container: - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d options: >- -e NODE_NAME --privileged --cgroupns host -v /cache:/cache -v /var/sysimage:/var/sysimage -v /var/tmp:/var/tmp -v /ceph-s3-info:/ceph-s3-info timeout-minutes: 90 @@ -275,7 +272,7 @@ jobs: BAZEL_COMMAND: "build" BAZEL_TARGETS: "//rs/..." BAZEL_EXTRA_ARGS: "--keep_going --config=afl" - BUILDEVENT_APIKEY: ${{ secrets. HONEYCOMB_API_TOKEN }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - name: Upload bazel-bep # runs only if previous step succeeded or failed; # we avoid collecting artifacts of jobs that were cancelled @@ -287,14 +284,14 @@ jobs: if-no-files-found: ignore compression-level: 9 path: | - bazel-bep.pb + bazel-bep.pb.gpg profile.json python-ci-tests: name: Python CI Tests runs-on: labels: dind-small container: - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d timeout-minutes: 30 steps: - name: Checkout @@ -324,7 +321,7 @@ jobs: build-ic: name: Build IC container: - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d options: >- -e NODE_NAME --privileged --cgroupns host -v /cache:/cache -v /var/sysimage:/var/sysimage -v /var/tmp:/var/tmp -v /ceph-s3-info:/ceph-s3-info timeout-minutes: 90 @@ -338,12 +335,11 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: ${{ github.event_name == 'pull_request' && 256 || 0 }} - - name: Login to Dockerhub + - name: Before script + id: before-script shell: bash - run: ./ci/scripts/docker-login.sh - env: - DOCKER_HUB_USER: ${{ vars.DOCKER_HUB_USER }} - DOCKER_HUB_PASSWORD_RO: ${{ secrets.DOCKER_HUB_PASSWORD_RO }} + run: | + [ -n "${NODE_NAME:-}" ] && echo "Node: $NODE_NAME" - name: Run Build IC id: build-ic shell: bash @@ -354,13 +350,11 @@ jobs: rm -rf "/cache/job/${CI_JOB_NAME}/${CI_RUN_ID}" mkdir -p "/cache/job/${CI_JOB_NAME}/${CI_RUN_ID}/artifacts" ln -s "/cache/job/${CI_JOB_NAME}/${CI_RUN_ID}/artifacts" /__w/$REPO_NAME/$REPO_NAME/artifacts - buildevents cmd "$CI_RUN_ID" "$CI_JOB_NAME" build-command -- \ - "$CI_PROJECT_DIR"/ci/scripts/run-build-ic.sh + "$CI_PROJECT_DIR"/ci/scripts/run-build-ic.sh rm -rf "/cache/job/${CI_JOB_NAME}/${CI_RUN_ID}" env: BAZEL_COMMAND: "build" MERGE_BASE_SHA: ${{ github.event.pull_request.base.sha }} - BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_API_TOKEN }} BRANCH_HEAD_SHA: ${{ github.event.pull_request.head.sha }} RUN_ON_DIFF_ONLY: ${{ github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'CI_ALL_BAZEL_TARGETS') }} - name: Upload build-ic.tar @@ -427,7 +421,7 @@ jobs: cargo-clippy-linux: name: Cargo Clippy Linux container: - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d timeout-minutes: 30 runs-on: group: ch1 @@ -455,16 +449,14 @@ jobs: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' shell: bash - env: - BUILDEVENT_APIKEY: ${{ secrets. HONEYCOMB_API_TOKEN }} run: | set -eExuo pipefail - buildevents cmd "$CI_RUN_ID" "$CI_JOB_NAME" build-command -- \ - "$CI_PROJECT_DIR"/ci/scripts/rust-lint.sh + export CARGO_TERM_COLOR=always # ensure output has colors + "$CI_PROJECT_DIR"/ci/scripts/rust-lint.sh cargo-build-release-linux: name: Cargo Build Release Linux container: - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d timeout-minutes: 30 runs-on: group: ch1 @@ -492,9 +484,7 @@ jobs: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' shell: bash - env: - BUILDEVENT_APIKEY: ${{ secrets. HONEYCOMB_API_TOKEN }} run: | set -eExuo pipefail - buildevents cmd "$CI_RUN_ID" "$CI_JOB_NAME" build-command -- \ - cargo build --release --locked + export CARGO_TERM_COLOR=always # ensure output has colors + cargo build --release --locked diff --git a/.github/workflows/ci-pr-only-nns-team-reminder.yml b/.github/workflows/ci-pr-only-nns-team-reminder.yml new file mode 100644 index 00000000000..9f8577375f5 --- /dev/null +++ b/.github/workflows/ci-pr-only-nns-team-reminder.yml @@ -0,0 +1,68 @@ +name: Governance Unreleased Changelog Reminder + +on: + pull_request: + types: + - review_requested + +jobs: + mainJob: + runs-on: ubuntu-latest + steps: + - uses: actions/github-script@v6 + id: mainStep + # If the PR requires nns-team to approve, GitHub will force nns-team to + # be in requested_teams. Therefore, the following condition is always + # met when nns-team must approve. (Further filtering takes place in the + # script itself.) + if: contains(github.event.pull_request.requested_teams.*.name, 'nns-team') + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + retries: 3 + script: | + const pullRequestNumber = context.payload.number; + + // Skip reminder if we already reminded (to avoid spam). + const reviews = await github.rest.pulls.listReviews({ + owner: "dfinity", + repo: "ic", + pull_number: pullRequestNumber, + }); + const alreadyRemindedAboutUnreleasedChangelog = reviews + .data + .some(review => review + .body + .startsWith("If this pull request affects the behavior of any canister owned by") + ); + console.log("alreadyRemindedAboutUnreleasedChangelog = " + alreadyRemindedAboutUnreleasedChangelog); + if (alreadyRemindedAboutUnreleasedChangelog) { + return; + } + + // Post a review to remind the author to update unreleased_changelog.md. + // TODO: Figure out how to post in such a way that there is a "Resolve" button nearby. + console.log("Adding reminder to update unreleased_changelog.md..."); + const reminderText = ` + If this pull request affects the behavior of any canister owned by + the Governance team, remember to update the corresponding + unreleased_changes.md file(s). + + To acknowldge this reminder (and unblock the PR), dismiss this + code review by going to the bottom of the pull request page, and + supply one of the following reasons: + + 1. Done. + + 2. No canister behavior changes. + ` + .replace(/^ +/gm, '') + .trim(); + await github.rest.pulls.createReview({ + owner: "dfinity", + repo: "ic", + pull_number: pullRequestNumber, + body: reminderText, + // This is what forces the author to explicitly acknowledge. + event: "REQUEST_CHANGES", + }); + console.log("Reminder was added successfully."); diff --git a/.github/workflows/ci-pr-only.yml b/.github/workflows/ci-pr-only.yml index 1d9a94645d2..91a8301c95b 100644 --- a/.github/workflows/ci-pr-only.yml +++ b/.github/workflows/ci-pr-only.yml @@ -20,7 +20,7 @@ jobs: runs-on: labels: dind-large container: - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d options: >- -e NODE_NAME if: ${{ github.event_name != 'merge_group' }} @@ -44,24 +44,13 @@ jobs: set -euo pipefail cd "${GITHUB_WORKSPACE}"/bin ./build-all-fuzzers.sh --zip - - name: Upload bazel-bep - if: always() - uses: actions/upload-artifact@v4 - with: - name: ${{ github.job }}-bep - retention-days: 14 - if-no-files-found: ignore - compression-level: 9 - path: | - bazel-bep.pb - profile.json lock-generate: name: Lock Generate timeout-minutes: 30 runs-on: labels: dind-small container: - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d options: >- -e NODE_NAME if: ${{ github.event_name != 'merge_group' }} @@ -102,7 +91,7 @@ jobs: runs-on: labels: dind-small container: - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d options: >- -e NODE_NAME if: ${{ github.event_name != 'merge_group' }} @@ -150,7 +139,7 @@ jobs: runs-on: labels: dind-large container: - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d options: >- -e NODE_NAME if: contains(github.event.pull_request.labels.*.name, 'CI_COVERAGE') diff --git a/.github/workflows/rate-limits-backend-release.yml b/.github/workflows/rate-limits-backend-release.yml index b17b9c10ffb..02948d0370f 100644 --- a/.github/workflows/rate-limits-backend-release.yml +++ b/.github/workflows/rate-limits-backend-release.yml @@ -33,7 +33,7 @@ jobs: labels: dind-large container: - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d options: >- -e NODE_NAME --privileged --cgroupns host -v /cache:/cache -v /var/sysimage:/var/sysimage -v /var/tmp:/var/tmp -v /ceph-s3-info:/ceph-s3-info diff --git a/.github/workflows/release-testing.yml b/.github/workflows/release-testing.yml index f6cdc4c7500..e5826bdae6c 100644 --- a/.github/workflows/release-testing.yml +++ b/.github/workflows/release-testing.yml @@ -28,7 +28,7 @@ jobs: group: zh1 labels: dind-large container: - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d options: >- -e NODE_NAME --privileged --cgroupns host -v /cache:/cache -v /var/sysimage:/var/sysimage -v /var/tmp:/var/tmp timeout-minutes: 180 # 3 hours @@ -37,12 +37,11 @@ jobs: uses: actions/checkout@v4 with: ref: ${{ github.event.workflow_run.head_branch }} - - name: Login to Dockerhub + - name: Before script + id: before-script shell: bash - run: ./ci/scripts/docker-login.sh - env: - DOCKER_HUB_USER: ${{ vars.DOCKER_HUB_USER }} - DOCKER_HUB_PASSWORD_RO: ${{ secrets.DOCKER_HUB_PASSWORD_RO }} + run: | + [ -n "${NODE_NAME:-}" ] && echo "Node: $NODE_NAME" - name: Run Bazel System Test Nightly id: bazel-test-all uses: ./.github/actions/bazel-test-all/ @@ -51,7 +50,8 @@ jobs: BAZEL_TARGETS: "//rs/tests/..." BAZEL_CI_CONFIG: "--config=ci --repository_cache=/cache/bazel" BAZEL_EXTRA_ARGS: "--keep_going --test_tag_filters=system_test_nightly" - BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_API_TOKEN }} + BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_TOKEN }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - name: Upload bazel-bep # runs only if previous step succeeded or failed; # we avoid collecting artifacts of jobs that were cancelled @@ -63,7 +63,7 @@ jobs: if-no-files-found: ignore compression-level: 9 path: | - bazel-bep.pb + bazel-bep.pb.gpg profile.json bazel-system-test-staging: name: Bazel System Test Staging @@ -71,7 +71,7 @@ jobs: group: zh1 labels: dind-large container: - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d options: >- -e NODE_NAME --privileged --cgroupns host -v /cache:/cache -v /var/sysimage:/var/sysimage -v /var/tmp:/var/tmp timeout-minutes: 180 # 3 hours @@ -80,12 +80,11 @@ jobs: uses: actions/checkout@v4 with: ref: ${{ github.event.workflow_run.head_branch }} - - name: Login to Dockerhub + - name: Before script + id: before-script shell: bash - run: ./ci/scripts/docker-login.sh - env: - DOCKER_HUB_USER: ${{ vars.DOCKER_HUB_USER }} - DOCKER_HUB_PASSWORD_RO: ${{ secrets.DOCKER_HUB_PASSWORD_RO }} + run: | + [ -n "${NODE_NAME:-}" ] && echo "Node: $NODE_NAME" - name: Run Bazel System Test Staging id: bazel-test-all uses: ./.github/actions/bazel-test-all/ @@ -94,7 +93,8 @@ jobs: BAZEL_TARGETS: "//rs/tests/..." BAZEL_CI_CONFIG: "--config=ci --repository_cache=/cache/bazel" BAZEL_EXTRA_ARGS: "--keep_going --test_tag_filters=system_test_staging" - BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_API_TOKEN }} + BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_TOKEN }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - name: Upload bazel-bep # runs only if previous step succeeded or failed; # we avoid collecting artifacts of jobs that were cancelled @@ -106,7 +106,7 @@ jobs: if-no-files-found: ignore compression-level: 9 path: | - bazel-bep.pb + bazel-bep.pb.gpg profile.json bazel-system-test-hotfix: name: Bazel System Test Hotfix @@ -114,7 +114,7 @@ jobs: group: zh1 labels: dind-large container: - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d options: >- -e NODE_NAME --privileged --cgroupns host -v /cache:/cache -v /var/sysimage:/var/sysimage -v /var/tmp:/var/tmp timeout-minutes: 90 @@ -123,12 +123,11 @@ jobs: uses: actions/checkout@v4 with: ref: ${{ github.event.workflow_run.head_branch }} - - name: Login to Dockerhub + - name: Before script + id: before-script shell: bash - run: ./ci/scripts/docker-login.sh - env: - DOCKER_HUB_USER: ${{ vars.DOCKER_HUB_USER }} - DOCKER_HUB_PASSWORD_RO: ${{ secrets.DOCKER_HUB_PASSWORD_RO }} + run: | + [ -n "${NODE_NAME:-}" ] && echo "Node: $NODE_NAME" - name: Run Bazel Test All id: bazel-test-all uses: ./.github/actions/bazel-test-all/ @@ -137,7 +136,8 @@ jobs: BAZEL_TARGETS: "//rs/tests/..." BAZEL_CI_CONFIG: "--config=ci --repository_cache=/cache/bazel" BAZEL_EXTRA_ARGS: "--keep_going --test_tag_filters=system_test_hotfix" - BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_API_TOKEN }} + BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_TOKEN }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - name: Upload bazel-bep # runs only if previous step succeeded or failed; # we avoid collecting artifacts of jobs that were cancelled @@ -149,7 +149,7 @@ jobs: if-no-files-found: ignore compression-level: 9 path: | - bazel-bep.pb + bazel-bep.pb.gpg profile.json dependency-scan-release-cut: name: Dependency Scan for Release @@ -157,7 +157,7 @@ jobs: group: zh1 labels: dind-large container: - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d options: >- -e NODE_NAME --privileged --cgroupns host -v /cache:/cache -v /var/sysimage:/var/sysimage -v /var/tmp:/var/tmp timeout-minutes: 60 @@ -175,12 +175,11 @@ jobs: uses: actions/checkout@v4 with: ref: ${{ github.event.workflow_run.head_branch }} - - name: Login to Dockerhub + - name: Before script + id: before-script shell: bash - run: ./ci/scripts/docker-login.sh - env: - DOCKER_HUB_USER: ${{ vars.DOCKER_HUB_USER }} - DOCKER_HUB_PASSWORD_RO: ${{ secrets.DOCKER_HUB_PASSWORD_RO }} + run: | + [ -n "${NODE_NAME:-}" ] && echo "Node: $NODE_NAME" - name: Set up Python uses: actions/setup-python@v5 with: @@ -205,7 +204,7 @@ jobs: group: zh1 labels: dind-large container: - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d options: >- -e NODE_NAME --privileged --cgroupns host -v /cache:/cache -v /var/sysimage:/var/sysimage -v /var/tmp:/var/tmp timeout-minutes: 180 # 3 hours @@ -233,7 +232,7 @@ jobs: group: zh1 labels: dind-large container: - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d options: >- -e NODE_NAME --privileged --cgroupns host -v /cache:/cache -v /var/sysimage:/var/sysimage -v /var/tmp:/var/tmp timeout-minutes: 180 # 3 hours @@ -242,12 +241,11 @@ jobs: uses: actions/checkout@v4 with: ref: ${{ github.event.workflow_run.head_branch }} - - name: Login to Dockerhub + - name: Before script + id: before-script shell: bash - run: ./ci/scripts/docker-login.sh - env: - DOCKER_HUB_USER: ${{ vars.DOCKER_HUB_USER }} - DOCKER_HUB_PASSWORD_RO: ${{ secrets.DOCKER_HUB_PASSWORD_RO }} + run: | + [ -n "${NODE_NAME:-}" ] && echo "Node: $NODE_NAME" - name: Run qualification for version ${{ matrix.version }} from the tip of the branch uses: ./.github/actions/bazel-test-all/ with: @@ -255,7 +253,8 @@ jobs: BAZEL_TARGETS: "//rs/tests/dre:guest_os_qualification" BAZEL_CI_CONFIG: "--config=systest --repository_cache=/cache/bazel" BAZEL_EXTRA_ARGS: "--keep_going --test_timeout=7200 --test_env=OLD_VERSION=${{ matrix.version }}" - BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_API_TOKEN }} + BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_TOKEN }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - # runs only if previous step succeeded or failed; # we avoid collecting artifacts of jobs that were cancelled if: success() || failure() @@ -266,6 +265,6 @@ jobs: if-no-files-found: ignore compression-level: 9 path: | - bazel-bep.pb + bazel-bep.pb.gpg profile.json name: ${{ github.job }}-${{ matrix.version }}-bep diff --git a/.github/workflows/rosetta-release.yml b/.github/workflows/rosetta-release.yml index 2da864a410d..cd99e734580 100644 --- a/.github/workflows/rosetta-release.yml +++ b/.github/workflows/rosetta-release.yml @@ -22,7 +22,7 @@ jobs: runs-on: labels: dind-large container: - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d steps: - name: Checkout uses: actions/checkout@v4 diff --git a/.github/workflows/schedule-daily.yml b/.github/workflows/schedule-daily.yml index 833d90f273b..ef9b5061b2f 100644 --- a/.github/workflows/schedule-daily.yml +++ b/.github/workflows/schedule-daily.yml @@ -15,7 +15,7 @@ jobs: bazel-test-bare-metal: name: Bazel Test Bare Metal container: - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d options: >- -e NODE_NAME --privileged --cgroupns host -v /cache:/cache -v /var/sysimage:/var/sysimage -v /var/tmp:/var/tmp timeout-minutes: 120 @@ -25,12 +25,11 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - - name: Login to Dockerhub + - name: Before script + id: before-script shell: bash - run: ./ci/scripts/docker-login.sh - env: - DOCKER_HUB_USER: ${{ vars.DOCKER_HUB_USER }} - DOCKER_HUB_PASSWORD_RO: ${{ secrets.DOCKER_HUB_PASSWORD_RO }} + run: | + [ -n "${NODE_NAME:-}" ] && echo "Node: $NODE_NAME" - name: Run Bazel Launch Bare Metal shell: bash run: | @@ -73,19 +72,18 @@ jobs: group: zh1 labels: dind-large container: - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d options: >- -e NODE_NAME --privileged --cgroupns host -v /cache:/cache -v /var/sysimage:/var/sysimage -v /var/tmp:/var/tmp timeout-minutes: 720 # 12 hours steps: - name: Checkout uses: actions/checkout@v4 - - name: Login to Dockerhub + - name: Before script + id: before-script shell: bash - run: ./ci/scripts/docker-login.sh - env: - DOCKER_HUB_USER: ${{ vars.DOCKER_HUB_USER }} - DOCKER_HUB_PASSWORD_RO: ${{ secrets.DOCKER_HUB_PASSWORD_RO }} + run: | + [ -n "${NODE_NAME:-}" ] && echo "Node: $NODE_NAME" - name: Run FI Tests Nightly id: bazel-test-all uses: ./.github/actions/bazel-test-all/ @@ -94,8 +92,9 @@ jobs: BAZEL_TARGETS: "//rs/ledger_suite/..." BAZEL_CI_CONFIG: "--config=ci --repository_cache=/cache/bazel" BAZEL_EXTRA_ARGS: "--keep_going --test_tag_filters=fi_tests_nightly --test_env=SSH_AUTH_SOCK --test_timeout=43200" - BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_API_TOKEN }} - SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} + BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_TOKEN }} + SSH_PRIVATE_KEY_BACKUP_POD: ${{ secrets.SSH_PRIVATE_KEY_BACKUP_POD }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - name: Upload bazel-bep # runs only if previous step succeeded or failed; # we avoid collecting artifacts of jobs that were cancelled @@ -107,7 +106,7 @@ jobs: if-no-files-found: ignore compression-level: 9 path: | - bazel-bep.pb + bazel-bep.pb.gpg profile.json nns-tests-nightly: name: Bazel Test NNS Nightly @@ -115,19 +114,18 @@ jobs: group: zh1 labels: dind-large container: - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d options: >- -e NODE_NAME --privileged --cgroupns host -v /cache:/cache -v /var/sysimage:/var/sysimage -v /var/tmp:/var/tmp timeout-minutes: 20 steps: - name: Checkout uses: actions/checkout@v4 - - name: Login to Dockerhub + - name: Before script + id: before-script shell: bash - run: ./ci/scripts/docker-login.sh - env: - DOCKER_HUB_USER: ${{ vars.DOCKER_HUB_USER }} - DOCKER_HUB_PASSWORD_RO: ${{ secrets.DOCKER_HUB_PASSWORD_RO }} + run: | + [ -n "${NODE_NAME:-}" ] && echo "Node: $NODE_NAME" - name: Run NNS Tests Nightly id: bazel-test-all uses: ./.github/actions/bazel-test-all/ @@ -136,8 +134,9 @@ jobs: BAZEL_TARGETS: "//rs/nns/..." BAZEL_CI_CONFIG: "--config=ci --repository_cache=/cache/bazel" BAZEL_EXTRA_ARGS: "--keep_going --test_tag_filters=nns_tests_nightly --test_env=SSH_AUTH_SOCK --test_env=NNS_CANISTER_UPGRADE_SEQUENCE=all" - BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_API_TOKEN }} - SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} + BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_TOKEN }} + SSH_PRIVATE_KEY_BACKUP_POD: ${{ secrets.SSH_PRIVATE_KEY_BACKUP_POD }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - name: Upload bazel-bep # runs only if previous step succeeded or failed; # we avoid collecting artifacts of jobs that were cancelled @@ -149,7 +148,7 @@ jobs: if-no-files-found: ignore compression-level: 9 path: | - bazel-bep.pb + bazel-bep.pb.gpg profile.json system-tests-benchmarks-nightly: name: Bazel System Test Benchmarks @@ -157,19 +156,18 @@ jobs: group: zh1 labels: dind-large container: - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d options: >- -e NODE_NAME --privileged --cgroupns host -v /cache:/cache -v /var/sysimage:/var/sysimage -v /var/tmp:/var/tmp timeout-minutes: 480 steps: - name: Checkout uses: actions/checkout@v4 - - name: Login to Dockerhub + - name: Before script + id: before-script shell: bash - run: ./ci/scripts/docker-login.sh - env: - DOCKER_HUB_USER: ${{ vars.DOCKER_HUB_USER }} - DOCKER_HUB_PASSWORD_RO: ${{ secrets.DOCKER_HUB_PASSWORD_RO }} + run: | + [ -n "${NODE_NAME:-}" ] && echo "Node: $NODE_NAME" - name: Set Benchmark Targets shell: bash run: | @@ -184,7 +182,8 @@ jobs: BAZEL_CI_CONFIG: "--config=ci --repository_cache=/cache/bazel" # note: there's just one performance cluster, so the job can't be parallelized BAZEL_EXTRA_ARGS: "--test_tag_filters=system_test_benchmark --//bazel:enable_upload_perf_systest_results=True --keep_going --jobs 1" - BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_API_TOKEN }} + BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_TOKEN }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - name: Upload bazel-bep # runs only if previous step succeeded or failed; # we avoid collecting artifacts of jobs that were cancelled @@ -196,7 +195,7 @@ jobs: if-no-files-found: ignore compression-level: 9 path: | - bazel-bep.pb + bazel-bep.pb.gpg profile.json - name: Post Slack Notification uses: slackapi/slack-github-action@6c661ce58804a1a20f6dc5fbee7f0381b469e001 # v1.25.0 @@ -212,7 +211,7 @@ jobs: group: zh1 labels: dind-large container: - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d options: >- -e NODE_NAME --privileged --cgroupns host -v /cache:/cache -v /var/sysimage:/var/sysimage -v /var/tmp:/var/tmp timeout-minutes: 60 @@ -264,7 +263,7 @@ jobs: group: zh1 labels: dind-large container: - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d options: >- -e NODE_NAME --privileged --cgroupns host -v /cache:/cache -v /var/sysimage:/var/sysimage -v /var/tmp:/var/tmp timeout-minutes: 120 diff --git a/.github/workflows/schedule-hourly.yml b/.github/workflows/schedule-hourly.yml index f374fb886c1..9625e8b4072 100644 --- a/.github/workflows/schedule-hourly.yml +++ b/.github/workflows/schedule-hourly.yml @@ -17,27 +17,27 @@ jobs: runs-on: labels: dind-large container: - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d options: >- -e NODE_NAME --privileged --cgroupns host -v /cache:/cache -v /var/sysimage:/var/sysimage -v /var/tmp:/var/tmp timeout-minutes: 120 steps: - name: Checkout uses: actions/checkout@v4 - - name: Login to Dockerhub + - name: Before script + id: before-script shell: bash - run: ./ci/scripts/docker-login.sh - env: - DOCKER_HUB_USER: ${{ vars.DOCKER_HUB_USER }} - DOCKER_HUB_PASSWORD_RO: ${{ secrets.DOCKER_HUB_PASSWORD_RO }} + run: | + [ -n "${NODE_NAME:-}" ] && echo "Node: $NODE_NAME" - name: Run Bazel Build All No Cache uses: ./.github/actions/bazel-test-all/ env: - AWS_SHARED_CREDENTIALS_CONTENT: ${{ secrets.AWS_SHARED_CREDENTIALS_FILE }} + CLOUD_CREDENTIALS_CONTENT: ${{ secrets.CLOUD_CREDENTIALS_CONTENT }} with: BAZEL_CI_CONFIG: "--config=ci" BAZEL_COMMAND: "build" BAZEL_EXTRA_ARGS: "--repository_cache= --disk_cache= --noremote_accept_cached --remote_instance_name=${CI_COMMIT_SHA} --@rules_rust//rust/settings:pipelined_compilation=True" + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - name: Upload bazel-bep # runs only if previous step succeeded or failed; # we avoid collecting artifacts of jobs that were cancelled @@ -49,12 +49,12 @@ jobs: if-no-files-found: ignore compression-level: 9 path: | - bazel-bep.pb + bazel-bep.pb.gpg profile.json bazel-system-test-hourly: name: Bazel System Tests Hourly container: - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d options: >- -e NODE_NAME --privileged --cgroupns host -v /cache:/cache -v /var/sysimage:/var/sysimage -v /var/tmp:/var/tmp timeout-minutes: 120 @@ -64,12 +64,11 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - - name: Login to Dockerhub + - name: Before script + id: before-script shell: bash - run: ./ci/scripts/docker-login.sh - env: - DOCKER_HUB_USER: ${{ vars.DOCKER_HUB_USER }} - DOCKER_HUB_PASSWORD_RO: ${{ secrets.DOCKER_HUB_PASSWORD_RO }} + run: | + [ -n "${NODE_NAME:-}" ] && echo "Node: $NODE_NAME" - name: Run Bazel System Test Hourly id: bazel-test-all uses: ./.github/actions/bazel-test-all/ @@ -78,7 +77,8 @@ jobs: BAZEL_TARGETS: "//rs/..." BAZEL_CI_CONFIG: "--config=ci --repository_cache=/cache/bazel" BAZEL_EXTRA_ARGS: "--keep_going --test_tag_filters=system_test_hourly" - BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_API_TOKEN }} + BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_TOKEN }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - name: Upload bazel-bep # runs only if previous step succeeded or failed; # we avoid collecting artifacts of jobs that were cancelled @@ -90,14 +90,14 @@ jobs: if-no-files-found: ignore compression-level: 9 path: | - bazel-bep.pb + bazel-bep.pb.gpg profile.json bazel-run-fuzzers-hourly: name: Bazel Run Fuzzers Hourly runs-on: labels: dind-large container: - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d options: >- -e NODE_NAME --privileged --cgroupns host -v /cache:/cache -v /var/sysimage:/var/sysimage -v /var/tmp:/var/tmp timeout-minutes: 120 diff --git a/.github/workflows/schedule-rust-bench.yml b/.github/workflows/schedule-rust-bench.yml index b95f77f7bc5..eb8e515e004 100644 --- a/.github/workflows/schedule-rust-bench.yml +++ b/.github/workflows/schedule-rust-bench.yml @@ -20,7 +20,7 @@ jobs: # see linux-x86-64 runner group labels: rust-benchmarks container: - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d # running on bare metal machine using ubuntu user options: --user ubuntu -v /cache:/cache timeout-minutes: 720 # 12 hours diff --git a/.github/workflows/schedule-weekly.yml b/.github/workflows/schedule-weekly.yml index 918fe9b4864..e0ac84c0026 100644 --- a/.github/workflows/schedule-weekly.yml +++ b/.github/workflows/schedule-weekly.yml @@ -14,7 +14,7 @@ jobs: runs-on: labels: dind-large container: - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d options: >- -e NODE_NAME -v /cache:/cache diff --git a/.github/workflows/system-tests-k8s.yml b/.github/workflows/system-tests-k8s.yml index 5544f6a0994..b95d62f5bdf 100644 --- a/.github/workflows/system-tests-k8s.yml +++ b/.github/workflows/system-tests-k8s.yml @@ -8,7 +8,10 @@ name: System Tests K8s on: schedule: - - cron: "0 3 * * *" + - cron: "0 1 * * *" # Run at 1 AM + - cron: "0 3 * * *" # Run at 3 AM + - cron: "0 5 * * *" # Run at 5 AM + - cron: "0 7 * * *" # Run at 7 AM pull_request: paths: - '.github/workflows/system-tests-k8s.yml' @@ -19,9 +22,13 @@ on: required: false default: '//rs/tests/nns:node_removal_from_registry_test' jobs: - description: 'Concurrent Bazel Jobs' + description: 'Concurrent Bazel Test Jobs' required: false - default: '32' + default: '10' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name != 'schedule' }} env: TARGETS: | @@ -29,17 +36,15 @@ env: github.event_name == 'workflow_dispatch' && github.event.inputs.targets || '//rs/tests/nns:node_removal_from_registry_test' }} JOBS: | - ${{ github.event_name == 'schedule' && '12' || + ${{ github.event_name == 'schedule' && '10' || github.event_name == 'workflow_dispatch' && github.event.inputs.jobs || - '32' }} + '10' }} BRANCH_NAME: ${{ github.head_ref || github.ref_name }} CI_COMMIT_SHA: ${{ github.sha }} CI_JOB_NAME: ${{ github.job }} CI_PIPELINE_SOURCE: ${{ github.event_name }} CI_PROJECT_DIR: ${{ github.workspace }} CI_RUN_ID: ${{ github.run_id }} - BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_API_TOKEN }} - BUILDEVENT_DATASET: "github-ci-dfinity" jobs: system-tests-k8s: @@ -48,7 +53,7 @@ jobs: group: ln1 labels: dind-large container: - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d options: >- -e NODE_NAME -e KUBECONFIG --privileged --cgroupns host @@ -58,12 +63,11 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Login to Dockerhub + - name: Before script + id: before-script shell: bash - run: ./ci/scripts/docker-login.sh - env: - DOCKER_HUB_USER: ${{ vars.DOCKER_HUB_USER }} - DOCKER_HUB_PASSWORD_RO: ${{ secrets.DOCKER_HUB_PASSWORD_RO }} + run: | + [ -n "${NODE_NAME:-}" ] && echo "Node: $NODE_NAME" - name: Set KUBECONFIG shell: bash @@ -77,13 +81,13 @@ jobs: id: bazel-test-all uses: ./.github/actions/bazel-test-all/ env: - AWS_SHARED_CREDENTIALS_CONTENT: ${{ secrets.AWS_SHARED_CREDENTIALS_FILE }} + CLOUD_CREDENTIALS_CONTENT: ${{ secrets.CLOUD_CREDENTIALS_CONTENT }} with: BAZEL_COMMAND: "test" BAZEL_TARGETS: "${{ env.TARGETS }}" BAZEL_CI_CONFIG: "--config=ci --repository_cache=/cache/bazel" - BAZEL_EXTRA_ARGS: "--jobs=${{ env.JOBS }} --test_tag_filters=k8s,-manual,-colocated,-system_test_hourly,-system_test_nightly --k8s" - BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_API_TOKEN }} + BAZEL_EXTRA_ARGS: "--local_test_jobs=${{ env.JOBS }} --test_tag_filters=k8s,-manual,-colocated,-long_test,-system_test_hourly,-system_test_nightly --k8s" + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - name: Upload bazel-bep uses: actions/upload-artifact@v4 @@ -94,7 +98,7 @@ jobs: if-no-files-found: ignore compression-level: 9 path: | - bazel-bep.pb + bazel-bep.pb.gpg profile.json system-tests-k8s-hourly: @@ -103,26 +107,19 @@ jobs: group: ln1 labels: dind-large container: - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d options: >- -e NODE_NAME -e KUBECONFIG --privileged --cgroupns host -v /cache:/cache -v /var/sysimage:/var/sysimage -v /var/tmp:/var/tmp timeout-minutes: 150 + # always run after 'system-tests-k8s' job only on manual dispatch and schedule + if: ${{ always() && (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') }} needs: [system-tests-k8s] - # always run after 'system-tests-k8s' job but on schedule event only - if: ${{ always() && github.event_name == 'schedule' }} steps: - name: Checkout uses: actions/checkout@v4 - - name: Login to Dockerhub - shell: bash - run: ./ci/scripts/docker-login.sh - env: - DOCKER_HUB_USER: ${{ vars.DOCKER_HUB_USER }} - DOCKER_HUB_PASSWORD_RO: ${{ secrets.DOCKER_HUB_PASSWORD_RO }} - - name: Set KUBECONFIG shell: bash run: | @@ -131,17 +128,36 @@ jobs: env: TNET_KUBECONFIG: ${{ secrets.TNET_KUBECONFIG }} + - name: Get Bazel Target List + shell: bash + run: | + # Query Bazel for targets: system tests with k8s flag AND (long_test flag OR system_test_hourly flag) + T=$(bazel query 'attr(tags, "k8s", tests(attr(tags, "long_test|system_test_hourly", //...))) except attr(tags, "colocated|manual|system_test_nightly", //...)') + + # Handle empty target list + if [[ -z "$T" ]]; then + echo "No Bazel targets found matching the criteria." + echo "TARGETS=" >> $GITHUB_ENV + exit 1 + fi + + # Convert to space-separated list and trim + T=$(echo "$T" | tr '\n' ' ' | sed -e 's/,$//' -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') + + # Export to GitHub environment + echo "TARGETS=$T" >> $GITHUB_ENV + - name: Run System Tests on K8s id: bazel-test-all uses: ./.github/actions/bazel-test-all/ env: - AWS_SHARED_CREDENTIALS_CONTENT: ${{ secrets.AWS_SHARED_CREDENTIALS_FILE }} + CLOUD_CREDENTIALS_CONTENT: ${{ secrets.CLOUD_CREDENTIALS_CONTENT }} with: BAZEL_COMMAND: "test" BAZEL_TARGETS: "${{ env.TARGETS }}" BAZEL_CI_CONFIG: "--config=ci --repository_cache=/cache/bazel" - BAZEL_EXTRA_ARGS: "--jobs=${{ env.JOBS }} --test_tag_filters=k8s,system_test_hourly,-manual,-colocated,-system_test_nightly --k8s" - BUILDEVENT_APIKEY: ${{ secrets.HONEYCOMB_API_TOKEN }} + BAZEL_EXTRA_ARGS: "--local_test_jobs=${{ env.JOBS }} --test_tag_filters=k8s --k8s --flaky_test_attempts=3" + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - name: Upload bazel-bep uses: actions/upload-artifact@v4 @@ -152,5 +168,5 @@ jobs: if-no-files-found: ignore compression-level: 9 path: | - bazel-bep.pb + bazel-bep.pb.gpg profile.json diff --git a/.github/workflows/test-namespace-darwin.yaml b/.github/workflows/test-namespace-darwin.yaml index 69f85eb4fa8..2f89d52daca 100644 --- a/.github/workflows/test-namespace-darwin.yaml +++ b/.github/workflows/test-namespace-darwin.yaml @@ -32,6 +32,9 @@ jobs: # Build and test, excluding 'upload' jobs that are not required on macOS (used in reproducibility tests) - name: Test run: | + # Until we have a hermetic CC toolchain, tell bazel to use the "real" clang + # (instead of Apple's, which sometimes breaks on wasm32) + export CC=/opt/homebrew/opt/llvm/bin/clang bazel \ --noworkspace_rc \ --bazelrc=./bazel/conf/.bazelrc.build --bazelrc=/tmp/bazel-cache.bazelrc \ diff --git a/.github/workflows/update-mainnet-revisions.yaml b/.github/workflows/update-mainnet-revisions.yaml index d847894404e..605c82259e1 100644 --- a/.github/workflows/update-mainnet-revisions.yaml +++ b/.github/workflows/update-mainnet-revisions.yaml @@ -38,7 +38,7 @@ jobs: runs-on: labels: dind-small container: - image: ghcr.io/dfinity/ic-build@sha256:4fd13b47285e783c3a6f35aadd9559d097c0de162a1cf221ead66ab1598d5d45 + image: ghcr.io/dfinity/ic-build@sha256:2e8185171700872d48fdfb4b08e175fca5be27b3fbbc4d7bed681ec8486f8b1d options: >- -e NODE_NAME --privileged --cgroupns host -v /cache:/cache -v /var/sysimage:/var/sysimage -v /var/tmp:/var/tmp -v /ceph-s3-info:/ceph-s3-info steps: diff --git a/Cargo.Bazel.Fuzzing.json.lock b/Cargo.Bazel.Fuzzing.json.lock index 0193c3d81d8..9b05121d90b 100644 --- a/Cargo.Bazel.Fuzzing.json.lock +++ b/Cargo.Bazel.Fuzzing.json.lock @@ -1,5 +1,5 @@ { - "checksum": "cb75bff2d9f6d8056d5a1b773fe08cd804f54831a43b9b23ed7da9d675a827c2", + "checksum": "c11f9d916275798c8e01da272c783f80087e93acaa382ced8cbe7c4b41534454", "crates": { "abnf 0.12.0": { "name": "abnf", @@ -537,7 +537,7 @@ "target": "regex_lite" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -969,7 +969,7 @@ "target": "regex_lite" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -1332,6 +1332,143 @@ ], "license_file": "LICENSE-APACHE" }, + "aes 0.8.4": { + "name": "aes", + "version": "0.8.4", + "package_url": "https://github.com/RustCrypto/block-ciphers", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/aes/0.8.4/download", + "sha256": "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" + } + }, + "targets": [ + { + "Library": { + "crate_name": "aes", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "aes", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "cfg-if 1.0.0", + "target": "cfg_if" + }, + { + "id": "cipher 0.4.4", + "target": "cipher" + } + ], + "selects": { + "cfg(any(target_arch = \"aarch64\", target_arch = \"x86_64\", target_arch = \"x86\"))": [ + { + "id": "cpufeatures 0.2.9", + "target": "cpufeatures" + } + ] + } + }, + "edition": "2021", + "version": "0.8.4" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, + "aes-gcm 0.10.3": { + "name": "aes-gcm", + "version": "0.10.3", + "package_url": "https://github.com/RustCrypto/AEADs", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/aes-gcm/0.10.3/download", + "sha256": "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" + } + }, + "targets": [ + { + "Library": { + "crate_name": "aes_gcm", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "aes_gcm", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "aes", + "alloc", + "default", + "getrandom", + "rand_core" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "aead 0.5.2", + "target": "aead" + }, + { + "id": "aes 0.8.4", + "target": "aes" + }, + { + "id": "cipher 0.4.4", + "target": "cipher" + }, + { + "id": "ctr 0.9.2", + "target": "ctr" + }, + { + "id": "ghash 0.5.1", + "target": "ghash" + }, + { + "id": "subtle 2.6.1", + "target": "subtle" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.10.3" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "ahash 0.7.8": { "name": "ahash", "version": "0.7.8", @@ -1646,7 +1783,7 @@ "target": "schemars" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -1821,14 +1958,14 @@ ], "license_file": null }, - "allocator-api2 0.2.16": { + "allocator-api2 0.2.21": { "name": "allocator-api2", - "version": "0.2.16", + "version": "0.2.21", "package_url": "https://github.com/zakarumych/allocator-api2", "repository": { "Http": { - "url": "https://static.crates.io/crates/allocator-api2/0.2.16/download", - "sha256": "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + "url": "https://static.crates.io/crates/allocator-api2/0.2.21/download", + "sha256": "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" } }, "targets": [ @@ -1857,14 +1994,14 @@ "selects": {} }, "edition": "2018", - "version": "0.2.16" + "version": "0.2.21" }, "license": "MIT OR Apache-2.0", "license_ids": [ "Apache-2.0", "MIT" ], - "license_file": "license" + "license_file": "LICENSE-APACHE" }, "android-tzdata 0.1.1": { "name": "android-tzdata", @@ -2468,6 +2605,71 @@ ], "license_file": "LICENSE-APACHE" }, + "argon2 0.4.1": { + "name": "argon2", + "version": "0.4.1", + "package_url": "https://github.com/RustCrypto/password-hashes/tree/master/argon2", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/argon2/0.4.1/download", + "sha256": "db4ce4441f99dbd377ca8a8f57b698c44d0d6e712d8329b5040da5a64aa1ce73" + } + }, + "targets": [ + { + "Library": { + "crate_name": "argon2", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "argon2", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "alloc", + "default", + "password-hash", + "rand" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "base64ct 1.6.0", + "target": "base64ct" + }, + { + "id": "blake2 0.10.6", + "target": "blake2" + }, + { + "id": "password-hash 0.4.2", + "target": "password_hash" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.4.1" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "arrayvec 0.5.2": { "name": "arrayvec", "version": "0.5.2", @@ -2674,7 +2876,7 @@ "target": "percent_encoding" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -2772,7 +2974,7 @@ "target": "quote" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -3123,7 +3325,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -3284,6 +3486,62 @@ ], "license_file": "LICENSE-APACHE" }, + "async-channel 1.9.0": { + "name": "async-channel", + "version": "1.9.0", + "package_url": "https://github.com/smol-rs/async-channel", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/async-channel/1.9.0/download", + "sha256": "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" + } + }, + "targets": [ + { + "Library": { + "crate_name": "async_channel", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "async_channel", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "concurrent-queue 2.5.0", + "target": "concurrent_queue" + }, + { + "id": "event-listener 2.5.3", + "target": "event_listener" + }, + { + "id": "futures-core 0.3.31", + "target": "futures_core" + } + ], + "selects": {} + }, + "edition": "2018", + "version": "1.9.0" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "async-channel 2.3.1": { "name": "async-channel", "version": "2.3.1", @@ -4103,6 +4361,54 @@ ], "license_file": "LICENSE-APACHE" }, + "async-watch 0.3.1": { + "name": "async-watch", + "version": "0.3.1", + "package_url": "https://github.com/cynecx/async-watch", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/async-watch/0.3.1/download", + "sha256": "a078faf4e27c0c6cc0efb20e5da59dcccc04968ebf2801d8e0b2195124cdcdb2" + } + }, + "targets": [ + { + "Library": { + "crate_name": "async_watch", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "async_watch", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "event-listener 2.5.3", + "target": "event_listener" + } + ], + "selects": {} + }, + "edition": "2018", + "version": "0.3.1" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "async-web-client 0.6.2": { "name": "async-web-client", "version": "0.6.2", @@ -4545,7 +4851,7 @@ "target": "pin_project_lite" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -4808,7 +5114,7 @@ "target": "pin_project_lite" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -5335,7 +5641,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" } ], @@ -5393,6 +5699,51 @@ ], "license_file": "LICENSE-CC0" }, + "base16ct 0.1.1": { + "name": "base16ct", + "version": "0.1.1", + "package_url": "https://github.com/RustCrypto/formats/tree/master/base16ct", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/base16ct/0.1.1/download", + "sha256": "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + } + }, + "targets": [ + { + "Library": { + "crate_name": "base16ct", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "base16ct", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "alloc" + ], + "selects": {} + }, + "edition": "2021", + "version": "0.1.1" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "base16ct 0.2.0": { "name": "base16ct", "version": "0.2.0", @@ -5506,6 +5857,12 @@ "compile_data_glob": [ "**" ], + "crate_features": { + "common": [ + "std" + ], + "selects": {} + }, "deps": { "common": [ { @@ -5802,7 +6159,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -5938,7 +6295,8 @@ ], "crate_features": { "common": [ - "alloc" + "alloc", + "std" ], "selects": {} }, @@ -6028,7 +6386,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -6466,6 +6824,104 @@ ], "license_file": null }, + "bip32 0.4.0": { + "name": "bip32", + "version": "0.4.0", + "package_url": "https://github.com/iqlusioninc/crates/tree/main/bip32", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/bip32/0.4.0/download", + "sha256": "b30ed1d6f8437a487a266c8293aeb95b61a23261273e3e02912cdb8b68bf798b" + } + }, + "targets": [ + { + "Library": { + "crate_name": "bip32", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "bip32", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "alloc", + "bip39", + "default", + "k256", + "mnemonic", + "once_cell", + "pbkdf2", + "secp256k1", + "std" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "bs58 0.4.0", + "target": "bs58" + }, + { + "id": "hmac 0.12.1", + "target": "hmac" + }, + { + "id": "k256 0.11.6", + "target": "k256" + }, + { + "id": "once_cell 1.19.0", + "target": "once_cell" + }, + { + "id": "pbkdf2 0.11.0", + "target": "pbkdf2" + }, + { + "id": "rand_core 0.6.4", + "target": "rand_core" + }, + { + "id": "ripemd 0.1.3", + "target": "ripemd" + }, + { + "id": "sha2 0.10.8", + "target": "sha2" + }, + { + "id": "subtle 2.6.1", + "target": "subtle" + }, + { + "id": "zeroize 1.8.1", + "target": "zeroize" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.4.0" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "bip32 0.5.1": { "name": "bip32", "version": "0.5.1", @@ -6720,7 +7176,7 @@ "target": "secp256k1" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -6836,7 +7292,7 @@ "target": "secp256k1" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde", "alias": "actual_serde" } @@ -6881,14 +7337,14 @@ ], "license_file": null }, - "bitcoin 0.32.2": { + "bitcoin 0.32.5": { "name": "bitcoin", - "version": "0.32.2", + "version": "0.32.5", "package_url": "https://github.com/rust-bitcoin/rust-bitcoin/", "repository": { "Http": { - "url": "https://static.crates.io/crates/bitcoin/0.32.2/download", - "sha256": "ea507acc1cd80fc084ace38544bbcf7ced7c2aa65b653b102de0ce718df668f6" + "url": "https://static.crates.io/crates/bitcoin/0.32.5/download", + "sha256": "ce6bc65742dea50536e35ad42492b234c27904a27f0abdcbce605015cb4ea026" } }, "targets": [ @@ -6922,6 +7378,18 @@ "compile_data_glob": [ "**" ], + "crate_features": { + "common": [ + "actual-serde", + "default", + "rand", + "rand-std", + "secp-recovery", + "serde", + "std" + ], + "selects": {} + }, "deps": { "common": [ { @@ -6934,7 +7402,7 @@ "target": "bech32" }, { - "id": "bitcoin 0.32.2", + "id": "bitcoin 0.32.5", "target": "build_script_build" }, { @@ -6969,6 +7437,11 @@ { "id": "secp256k1 0.29.0", "target": "secp256k1" + }, + { + "id": "serde 1.0.217", + "target": "serde", + "alias": "actual_serde" } ], "selects": {} @@ -6995,7 +7468,7 @@ ], "selects": {} }, - "version": "0.32.2" + "version": "0.32.5" }, "build_script_attrs": { "compile_data_glob": [ @@ -7055,7 +7528,9 @@ "crate_features": { "common": [ "alloc", - "default" + "default", + "serde", + "std" ], "selects": {} }, @@ -7064,6 +7539,10 @@ { "id": "bitcoin-internals 0.3.0", "target": "build_script_build" + }, + { + "id": "serde 1.0.217", + "target": "serde" } ], "selects": {} @@ -7116,7 +7595,8 @@ ], "crate_features": { "common": [ - "alloc" + "alloc", + "std" ], "selects": {} }, @@ -7235,7 +7715,9 @@ ], "crate_features": { "common": [ - "alloc" + "alloc", + "serde", + "std" ], "selects": {} }, @@ -7245,6 +7727,10 @@ "id": "bitcoin-internals 0.3.0", "target": "bitcoin_internals", "alias": "internals" + }, + { + "id": "serde 1.0.217", + "target": "serde" } ], "selects": {} @@ -7297,7 +7783,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -7357,7 +7843,7 @@ "alias": "internals" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -7405,7 +7891,9 @@ "common": [ "alloc", "bitcoin-io", - "io" + "io", + "serde", + "std" ], "selects": {} }, @@ -7419,6 +7907,10 @@ "id": "hex-conservative 0.2.1", "target": "hex_conservative", "alias": "hex" + }, + { + "id": "serde 1.0.217", + "target": "serde" } ], "selects": {} @@ -7432,14 +7924,14 @@ ], "license_file": null }, - "bitcoincore-rpc 0.15.0": { + "bitcoincore-rpc 0.19.0": { "name": "bitcoincore-rpc", - "version": "0.15.0", + "version": "0.19.0", "package_url": "https://github.com/rust-bitcoin/rust-bitcoincore-rpc/", "repository": { "Http": { - "url": "https://static.crates.io/crates/bitcoincore-rpc/0.15.0/download", - "sha256": "dd0e67dbf7a9971e7f4276f6089e9e814ce0f624a03216b7d92d00351ae7fb3e" + "url": "https://static.crates.io/crates/bitcoincore-rpc/0.19.0/download", + "sha256": "aedd23ae0fd321affb4bbbc36126c6f49a32818dc6b979395d24da8c9d4e80ee" } }, "targets": [ @@ -7461,14 +7953,21 @@ "compile_data_glob": [ "**" ], + "crate_features": { + "common": [ + "default", + "rand" + ], + "selects": {} + }, "deps": { "common": [ { - "id": "bitcoincore-rpc-json 0.15.0", + "id": "bitcoincore-rpc-json 0.19.0", "target": "bitcoincore_rpc_json" }, { - "id": "jsonrpc 0.12.1", + "id": "jsonrpc 0.18.0", "target": "jsonrpc" }, { @@ -7476,7 +7975,7 @@ "target": "log" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -7486,8 +7985,8 @@ ], "selects": {} }, - "edition": "2015", - "version": "0.15.0" + "edition": "2018", + "version": "0.19.0" }, "license": "CC0-1.0", "license_ids": [ @@ -7495,14 +7994,14 @@ ], "license_file": "LICENSE" }, - "bitcoincore-rpc-json 0.15.0": { + "bitcoincore-rpc-json 0.19.0": { "name": "bitcoincore-rpc-json", - "version": "0.15.0", + "version": "0.19.0", "package_url": "https://github.com/rust-bitcoin/rust-bitcoincore-rpc/", "repository": { "Http": { - "url": "https://static.crates.io/crates/bitcoincore-rpc-json/0.15.0/download", - "sha256": "2e2ae16202721ba8c3409045681fac790a5ddc791f05731a2df22c0c6bffc0f1" + "url": "https://static.crates.io/crates/bitcoincore-rpc-json/0.19.0/download", + "sha256": "d8909583c5fab98508e80ef73e5592a651c954993dc6b7739963257d19f0e71a" } }, "targets": [ @@ -7524,14 +8023,21 @@ "compile_data_glob": [ "**" ], + "crate_features": { + "common": [ + "default", + "rand" + ], + "selects": {} + }, "deps": { "common": [ { - "id": "bitcoin 0.28.2", + "id": "bitcoin 0.32.5", "target": "bitcoin" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -7541,8 +8047,8 @@ ], "selects": {} }, - "edition": "2015", - "version": "0.15.0" + "edition": "2021", + "version": "0.19.0" }, "license": "CC0-1.0", "license_ids": [ @@ -7796,6 +8302,54 @@ ], "license_file": "LICENSE.txt" }, + "blake2 0.10.6": { + "name": "blake2", + "version": "0.10.6", + "package_url": "https://github.com/RustCrypto/hashes", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/blake2/0.10.6/download", + "sha256": "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" + } + }, + "targets": [ + { + "Library": { + "crate_name": "blake2", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "blake2", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "digest 0.10.7", + "target": "digest" + } + ], + "selects": {} + }, + "edition": "2018", + "version": "0.10.6" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "block-buffer 0.9.0": { "name": "block-buffer", "version": "0.9.0", @@ -8405,6 +8959,61 @@ ], "license_file": "LICENSE" }, + "bs58 0.4.0": { + "name": "bs58", + "version": "0.4.0", + "package_url": "https://github.com/mycorrhiza/bs58-rs", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/bs58/0.4.0/download", + "sha256": "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" + } + }, + "targets": [ + { + "Library": { + "crate_name": "bs58", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "bs58", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "check", + "sha2" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "sha2 0.9.9", + "target": "sha2" + } + ], + "selects": {} + }, + "edition": "2018", + "version": "0.4.0" + }, + "license": "MIT/Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "bs58 0.5.0": { "name": "bs58", "version": "0.5.0", @@ -8739,7 +9348,7 @@ "target": "semver" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -8875,14 +9484,14 @@ ], "license_file": null }, - "bumpalo 3.14.0": { + "bumpalo 3.16.0": { "name": "bumpalo", - "version": "3.14.0", + "version": "3.16.0", "package_url": "https://github.com/fitzgen/bumpalo", "repository": { "Http": { - "url": "https://static.crates.io/crates/bumpalo/3.14.0/download", - "sha256": "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + "url": "https://static.crates.io/crates/bumpalo/3.16.0/download", + "sha256": "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" } }, "targets": [ @@ -8906,12 +9515,22 @@ ], "crate_features": { "common": [ + "allocator-api2", "default" ], "selects": {} }, + "deps": { + "common": [ + { + "id": "allocator-api2 0.2.21", + "target": "allocator_api2" + } + ], + "selects": {} + }, "edition": "2021", - "version": "3.14.0" + "version": "3.16.0" }, "license": "MIT OR Apache-2.0", "license_ids": [ @@ -9045,7 +9664,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -9326,7 +9945,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -9500,7 +10119,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" }, { @@ -9558,14 +10177,14 @@ ], "license_file": null }, - "cached 0.47.0": { + "cached 0.49.2": { "name": "cached", - "version": "0.47.0", + "version": "0.49.2", "package_url": "https://github.com/jaemk/cached", "repository": { "Http": { - "url": "https://static.crates.io/crates/cached/0.47.0/download", - "sha256": "69b0116662497bc24e4b177c90eaf8870e39e2714c3fcfa296327a93f593fc21" + "url": "https://static.crates.io/crates/cached/0.49.2/download", + "sha256": "f251fd1e72720ca07bf5d8e310f54a193fd053479a1f6342c6663ee4fa01cf96" } }, "targets": [ @@ -9587,26 +10206,8 @@ "compile_data_glob": [ "**" ], - "crate_features": { - "common": [ - "ahash", - "cached_proc_macro", - "cached_proc_macro_types", - "default", - "proc_macro" - ], - "selects": {} - }, "deps": { "common": [ - { - "id": "ahash 0.8.11", - "target": "ahash" - }, - { - "id": "cached_proc_macro_types 0.1.0", - "target": "cached_proc_macro_types" - }, { "id": "hashbrown 0.14.5", "target": "hashbrown" @@ -9627,16 +10228,7 @@ "selects": {} }, "edition": "2018", - "proc_macro_deps": { - "common": [ - { - "id": "cached_proc_macro 0.18.1", - "target": "cached_proc_macro" - } - ], - "selects": {} - }, - "version": "0.47.0" + "version": "0.49.2" }, "license": "MIT", "license_ids": [ @@ -9644,14 +10236,14 @@ ], "license_file": "LICENSE" }, - "cached 0.49.2": { + "cached 0.52.0": { "name": "cached", - "version": "0.49.2", + "version": "0.52.0", "package_url": "https://github.com/jaemk/cached", "repository": { "Http": { - "url": "https://static.crates.io/crates/cached/0.49.2/download", - "sha256": "f251fd1e72720ca07bf5d8e310f54a193fd053479a1f6342c6663ee4fa01cf96" + "url": "https://static.crates.io/crates/cached/0.52.0/download", + "sha256": "a8466736fe5dbcaf8b8ee24f9bbefe43c884dc3e9ff7178da70f55bffca1133c" } }, "targets": [ @@ -9673,8 +10265,18 @@ "compile_data_glob": [ "**" ], + "crate_features": { + "common": [ + "ahash" + ], + "selects": {} + }, "deps": { "common": [ + { + "id": "ahash 0.8.11", + "target": "ahash" + }, { "id": "hashbrown 0.14.5", "target": "hashbrown" @@ -9695,7 +10297,7 @@ "selects": {} }, "edition": "2018", - "version": "0.49.2" + "version": "0.52.0" }, "license": "MIT", "license_ids": [ @@ -9703,14 +10305,14 @@ ], "license_file": "LICENSE" }, - "cached 0.52.0": { + "cached 0.54.0": { "name": "cached", - "version": "0.52.0", + "version": "0.54.0", "package_url": "https://github.com/jaemk/cached", "repository": { "Http": { - "url": "https://static.crates.io/crates/cached/0.52.0/download", - "sha256": "a8466736fe5dbcaf8b8ee24f9bbefe43c884dc3e9ff7178da70f55bffca1133c" + "url": "https://static.crates.io/crates/cached/0.54.0/download", + "sha256": "9718806c4a2fe9e8a56fd736f97b340dd10ed1be8ed733ed50449f351dc33cae" } }, "targets": [ @@ -9734,7 +10336,11 @@ ], "crate_features": { "common": [ - "ahash" + "ahash", + "cached_proc_macro", + "cached_proc_macro_types", + "default", + "proc_macro" ], "selects": {} }, @@ -9745,12 +10351,12 @@ "target": "ahash" }, { - "id": "hashbrown 0.14.5", - "target": "hashbrown" + "id": "cached_proc_macro_types 0.1.1", + "target": "cached_proc_macro_types" }, { - "id": "instant 0.1.12", - "target": "instant" + "id": "hashbrown 0.14.5", + "target": "hashbrown" }, { "id": "once_cell 1.19.0", @@ -9759,12 +10365,25 @@ { "id": "thiserror 1.0.68", "target": "thiserror" + }, + { + "id": "web-time 1.1.0", + "target": "web_time" } ], "selects": {} }, "edition": "2018", - "version": "0.52.0" + "proc_macro_deps": { + "common": [ + { + "id": "cached_proc_macro 0.23.0", + "target": "cached_proc_macro" + } + ], + "selects": {} + }, + "version": "0.54.0" }, "license": "MIT", "license_ids": [ @@ -9772,14 +10391,14 @@ ], "license_file": "LICENSE" }, - "cached_proc_macro 0.18.1": { + "cached_proc_macro 0.23.0": { "name": "cached_proc_macro", - "version": "0.18.1", + "version": "0.23.0", "package_url": "https://github.com/jaemk/cached", "repository": { "Http": { - "url": "https://static.crates.io/crates/cached_proc_macro/0.18.1/download", - "sha256": "c878c71c2821aa2058722038a59a67583a4240524687c6028571c9b395ded61f" + "url": "https://static.crates.io/crates/cached_proc_macro/0.23.0/download", + "sha256": "2f42a145ed2d10dce2191e1dcf30cfccfea9026660e143662ba5eec4017d5daa" } }, "targets": [ @@ -9804,7 +10423,7 @@ "deps": { "common": [ { - "id": "darling 0.14.4", + "id": "darling 0.20.10", "target": "darling" }, { @@ -9816,29 +10435,29 @@ "target": "quote" }, { - "id": "syn 1.0.109", + "id": "syn 2.0.87", "target": "syn" } ], "selects": {} }, "edition": "2018", - "version": "0.18.1" + "version": "0.23.0" }, "license": "MIT", "license_ids": [ "MIT" ], - "license_file": null + "license_file": "LICENSE" }, - "cached_proc_macro_types 0.1.0": { + "cached_proc_macro_types 0.1.1": { "name": "cached_proc_macro_types", - "version": "0.1.0", + "version": "0.1.1", "package_url": "https://github.com/jaemk/cached", "repository": { "Http": { - "url": "https://static.crates.io/crates/cached_proc_macro_types/0.1.0/download", - "sha256": "3a4f925191b4367301851c6d99b09890311d74b0d43f274c0b34c86d308a3663" + "url": "https://static.crates.io/crates/cached_proc_macro_types/0.1.1/download", + "sha256": "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0" } }, "targets": [ @@ -9861,13 +10480,13 @@ "**" ], "edition": "2018", - "version": "0.1.0" + "version": "0.1.1" }, "license": "MIT", "license_ids": [ "MIT" ], - "license_file": null + "license_file": "LICENSE" }, "camino 1.1.6": { "name": "camino", @@ -9924,7 +10543,7 @@ "target": "build_script_build" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -9996,7 +10615,7 @@ "target": "canbench_rs" }, { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -10028,7 +10647,7 @@ "target": "semver" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -10091,7 +10710,7 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -10099,7 +10718,7 @@ "target": "ic_cdk" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -10178,14 +10797,14 @@ ], "license_file": null }, - "candid 0.10.10": { + "candid 0.10.13": { "name": "candid", - "version": "0.10.10", + "version": "0.10.13", "package_url": "https://github.com/dfinity/candid", "repository": { "Http": { - "url": "https://static.crates.io/crates/candid/0.10.10/download", - "sha256": "6c30ee7f886f296b6422c0ff017e89dd4f831521dfdcc76f3f71aae1ce817222" + "url": "https://static.crates.io/crates/candid/0.10.13/download", + "sha256": "a253bab4a9be502c82332b60cbeee6202ad0692834efeec95fae9f29db33d692" } }, "targets": [ @@ -10257,7 +10876,7 @@ "target": "pretty" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -10313,7 +10932,7 @@ ], "selects": {} }, - "version": "0.10.10" + "version": "0.10.13" }, "license": "Apache-2.0", "license_ids": [ @@ -10428,7 +11047,7 @@ "target": "anyhow" }, { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -10528,7 +11147,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -10594,7 +11213,7 @@ "target": "semver" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -10652,20 +11271,14 @@ ], "license_file": "LICENSE-APACHE" }, - "cc 1.0.83": { + "cc 1.1.37": { "name": "cc", - "version": "1.0.83", + "version": "1.1.37", "package_url": "https://github.com/rust-lang/cc-rs", "repository": { "Http": { - "url": "https://static.crates.io/crates/cc/1.0.83/download", - "sha256": "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0", - "patch_args": [ - "-p1" - ], - "patches": [ - "@@//bazel:cc_rs.patch" - ] + "url": "https://static.crates.io/crates/cc/1.1.37/download", + "sha256": "40545c26d092346d8a8dab71ee48e7685a7a9cba76e634790c215b41a4a7b4cf" } }, "targets": [ @@ -10691,97 +11304,98 @@ "common": [], "selects": { "aarch64-apple-darwin": [ - "jobserver", "parallel" ], "aarch64-pc-windows-msvc": [ - "jobserver", "parallel" ], "aarch64-unknown-linux-gnu": [ - "jobserver", "parallel" ], "aarch64-unknown-nixos-gnu": [ - "jobserver", "parallel" ], "arm-unknown-linux-gnueabi": [ - "jobserver", "parallel" ], "i686-pc-windows-msvc": [ - "jobserver", "parallel" ], "i686-unknown-linux-gnu": [ - "jobserver", "parallel" ], "powerpc-unknown-linux-gnu": [ - "jobserver", "parallel" ], "s390x-unknown-linux-gnu": [ - "jobserver", "parallel" ], "x86_64-apple-darwin": [ - "jobserver", "parallel" ], "x86_64-pc-windows-msvc": [ - "jobserver", "parallel" ], "x86_64-unknown-freebsd": [ - "jobserver", "parallel" ], "x86_64-unknown-linux-gnu": [ - "jobserver", "parallel" ], "x86_64-unknown-nixos-gnu": [ - "jobserver", "parallel" ] } }, "deps": { - "common": [], + "common": [ + { + "id": "shlex 1.3.0", + "target": "shlex" + } + ], "selects": { "aarch64-apple-darwin": [ { - "id": "jobserver 0.1.27", + "id": "jobserver 0.1.32", "target": "jobserver" + }, + { + "id": "libc 0.2.158", + "target": "libc" } ], "aarch64-pc-windows-msvc": [ { - "id": "jobserver 0.1.27", + "id": "jobserver 0.1.32", "target": "jobserver" } ], "aarch64-unknown-linux-gnu": [ { - "id": "jobserver 0.1.27", + "id": "jobserver 0.1.32", "target": "jobserver" + }, + { + "id": "libc 0.2.158", + "target": "libc" } ], "aarch64-unknown-nixos-gnu": [ { - "id": "jobserver 0.1.27", + "id": "jobserver 0.1.32", "target": "jobserver" + }, + { + "id": "libc 0.2.158", + "target": "libc" } ], "arm-unknown-linux-gnueabi": [ { - "id": "jobserver 0.1.27", + "id": "jobserver 0.1.32", "target": "jobserver" - } - ], - "cfg(unix)": [ + }, { "id": "libc 0.2.158", "target": "libc" @@ -10789,62 +11403,90 @@ ], "i686-pc-windows-msvc": [ { - "id": "jobserver 0.1.27", + "id": "jobserver 0.1.32", "target": "jobserver" } ], "i686-unknown-linux-gnu": [ { - "id": "jobserver 0.1.27", + "id": "jobserver 0.1.32", "target": "jobserver" + }, + { + "id": "libc 0.2.158", + "target": "libc" } ], "powerpc-unknown-linux-gnu": [ { - "id": "jobserver 0.1.27", + "id": "jobserver 0.1.32", "target": "jobserver" + }, + { + "id": "libc 0.2.158", + "target": "libc" } ], "s390x-unknown-linux-gnu": [ { - "id": "jobserver 0.1.27", + "id": "jobserver 0.1.32", "target": "jobserver" + }, + { + "id": "libc 0.2.158", + "target": "libc" } ], "x86_64-apple-darwin": [ { - "id": "jobserver 0.1.27", + "id": "jobserver 0.1.32", "target": "jobserver" + }, + { + "id": "libc 0.2.158", + "target": "libc" } ], "x86_64-pc-windows-msvc": [ { - "id": "jobserver 0.1.27", + "id": "jobserver 0.1.32", "target": "jobserver" } ], "x86_64-unknown-freebsd": [ { - "id": "jobserver 0.1.27", + "id": "jobserver 0.1.32", "target": "jobserver" + }, + { + "id": "libc 0.2.158", + "target": "libc" } ], "x86_64-unknown-linux-gnu": [ { - "id": "jobserver 0.1.27", + "id": "jobserver 0.1.32", "target": "jobserver" + }, + { + "id": "libc 0.2.158", + "target": "libc" } ], "x86_64-unknown-nixos-gnu": [ { - "id": "jobserver 0.1.27", + "id": "jobserver 0.1.32", "target": "jobserver" + }, + { + "id": "libc 0.2.158", + "target": "libc" } ] } }, "edition": "2018", - "version": "1.0.83" + "version": "1.1.37" }, "license": "MIT OR Apache-2.0", "license_ids": [ @@ -10973,7 +11615,7 @@ "target": "regex_syntax" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -11008,19 +11650,19 @@ "target": "crossterm" } ], - "aarch64-fuchsia": [ + "aarch64-linux-android": [ { "id": "crossterm 0.27.0", "target": "crossterm" } ], - "aarch64-linux-android": [ + "aarch64-pc-windows-msvc": [ { "id": "crossterm 0.27.0", "target": "crossterm" } ], - "aarch64-pc-windows-msvc": [ + "aarch64-unknown-fuchsia": [ { "id": "crossterm 0.27.0", "target": "crossterm" @@ -11144,7 +11786,7 @@ "target": "wasm_bindgen" } ], - "wasm32-wasi": [ + "wasm32-wasip1": [ { "id": "serde-wasm-bindgen 0.5.0", "target": "serde_wasm_bindgen" @@ -11166,25 +11808,25 @@ "target": "crossterm" } ], - "x86_64-fuchsia": [ + "x86_64-linux-android": [ { "id": "crossterm 0.27.0", "target": "crossterm" } ], - "x86_64-linux-android": [ + "x86_64-pc-windows-msvc": [ { "id": "crossterm 0.27.0", "target": "crossterm" } ], - "x86_64-pc-windows-msvc": [ + "x86_64-unknown-freebsd": [ { "id": "crossterm 0.27.0", "target": "crossterm" } ], - "x86_64-unknown-freebsd": [ + "x86_64-unknown-fuchsia": [ { "id": "crossterm 0.27.0", "target": "crossterm" @@ -11624,7 +12266,7 @@ "target": "num_traits" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -11647,12 +12289,6 @@ "target": "iana_time_zone" } ], - "aarch64-fuchsia": [ - { - "id": "iana-time-zone 0.1.59", - "target": "iana_time_zone" - } - ], "aarch64-linux-android": [ { "id": "android-tzdata 0.1.1", @@ -11669,6 +12305,12 @@ "target": "windows_targets" } ], + "aarch64-unknown-fuchsia": [ + { + "id": "iana-time-zone 0.1.59", + "target": "iana_time_zone" + } + ], "aarch64-unknown-linux-gnu": [ { "id": "iana-time-zone 0.1.59", @@ -11777,12 +12419,6 @@ "target": "iana_time_zone" } ], - "x86_64-fuchsia": [ - { - "id": "iana-time-zone 0.1.59", - "target": "iana_time_zone" - } - ], "x86_64-linux-android": [ { "id": "android-tzdata 0.1.1", @@ -11805,6 +12441,12 @@ "target": "iana_time_zone" } ], + "x86_64-unknown-fuchsia": [ + { + "id": "iana-time-zone 0.1.59", + "target": "iana_time_zone" + } + ], "x86_64-unknown-linux-gnu": [ { "id": "iana-time-zone 0.1.59", @@ -11876,7 +12518,7 @@ "target": "ciborium_ll" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -12822,7 +13464,7 @@ "target": "reqwest" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -13151,7 +13793,7 @@ "target": "pretty_assertions" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -13496,6 +14138,7 @@ "crate_features": { "common": [ "ansi-parsing", + "default", "unicode-width" ], "selects": {} @@ -13939,6 +14582,65 @@ ], "license_file": "LICENSE-APACHE" }, + "core-foundation 0.10.0": { + "name": "core-foundation", + "version": "0.10.0", + "package_url": "https://github.com/servo/core-foundation-rs", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/core-foundation/0.10.0/download", + "sha256": "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" + } + }, + "targets": [ + { + "Library": { + "crate_name": "core_foundation", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "core_foundation", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "default", + "link" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "core-foundation-sys 0.8.7", + "target": "core_foundation_sys" + }, + { + "id": "libc 0.2.158", + "target": "libc" + } + ], + "selects": {} + }, + "edition": "2018", + "version": "0.10.0" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "core-foundation-sys 0.8.7": { "name": "core-foundation-sys", "version": "0.8.7", @@ -14033,7 +14735,7 @@ "target": "log" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -14092,7 +14794,7 @@ "target": "bitcoin_private" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -14228,14 +14930,14 @@ ], "license_file": "LICENSE-APACHE" }, - "cranelift-bforest 0.114.0": { + "cranelift-bforest 0.115.0": { "name": "cranelift-bforest", - "version": "0.114.0", + "version": "0.115.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/cranelift-bforest/0.114.0/download", - "sha256": "2ba4f80548f22dc9c43911907b5e322c5555544ee85f785115701e6a28c9abe1" + "url": "https://static.crates.io/crates/cranelift-bforest/0.115.0/download", + "sha256": "ac89549be94911dd0e839b4a7db99e9ed29c17517e1c026f61066884c168aa3c" } }, "targets": [ @@ -14260,14 +14962,14 @@ "deps": { "common": [ { - "id": "cranelift-entity 0.114.0", + "id": "cranelift-entity 0.115.0", "target": "cranelift_entity" } ], "selects": {} }, "edition": "2021", - "version": "0.114.0" + "version": "0.115.0" }, "license": "Apache-2.0 WITH LLVM-exception", "license_ids": [ @@ -14275,14 +14977,14 @@ ], "license_file": "LICENSE" }, - "cranelift-bitset 0.114.0": { + "cranelift-bitset 0.115.0": { "name": "cranelift-bitset", - "version": "0.114.0", + "version": "0.115.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/cranelift-bitset/0.114.0/download", - "sha256": "005884e3649c3e5ff2dc79e8a94b138f11569cc08a91244a292714d2a86e9156" + "url": "https://static.crates.io/crates/cranelift-bitset/0.115.0/download", + "sha256": "b9bd49369f76c77e34e641af85d0956869237832c118964d08bf5f51f210875a" } }, "targets": [ @@ -14313,7 +15015,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -14323,13 +15025,13 @@ "proc_macro_deps": { "common": [ { - "id": "serde_derive 1.0.214", + "id": "serde_derive 1.0.217", "target": "serde_derive" } ], "selects": {} }, - "version": "0.114.0" + "version": "0.115.0" }, "license": "Apache-2.0 WITH LLVM-exception", "license_ids": [ @@ -14337,14 +15039,14 @@ ], "license_file": null }, - "cranelift-codegen 0.114.0": { + "cranelift-codegen 0.115.0": { "name": "cranelift-codegen", - "version": "0.114.0", + "version": "0.115.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/cranelift-codegen/0.114.0/download", - "sha256": "fe4036255ec33ce9a37495dfbcfc4e1118fd34e693eff9a1e106336b7cd16a9b" + "url": "https://static.crates.io/crates/cranelift-codegen/0.115.0/download", + "sha256": "fd96ce9cf8efebd7f5ab8ced5a0ce44250280bbae9f593d74a6d7effc3582a35" } }, "targets": [ @@ -14391,31 +15093,31 @@ "deps": { "common": [ { - "id": "bumpalo 3.14.0", + "id": "bumpalo 3.16.0", "target": "bumpalo" }, { - "id": "cranelift-bforest 0.114.0", + "id": "cranelift-bforest 0.115.0", "target": "cranelift_bforest" }, { - "id": "cranelift-bitset 0.114.0", + "id": "cranelift-bitset 0.115.0", "target": "cranelift_bitset" }, { - "id": "cranelift-codegen 0.114.0", + "id": "cranelift-codegen 0.115.0", "target": "build_script_build" }, { - "id": "cranelift-codegen-shared 0.114.0", + "id": "cranelift-codegen-shared 0.115.0", "target": "cranelift_codegen_shared" }, { - "id": "cranelift-control 0.114.0", + "id": "cranelift-control 0.115.0", "target": "cranelift_control" }, { - "id": "cranelift-entity 0.114.0", + "id": "cranelift-entity 0.115.0", "target": "cranelift_entity" }, { @@ -14431,7 +15133,7 @@ "target": "log" }, { - "id": "regalloc2 0.10.2", + "id": "regalloc2 0.11.1", "target": "regalloc2" }, { @@ -14450,7 +15152,7 @@ "selects": {} }, "edition": "2021", - "version": "0.114.0" + "version": "0.115.0" }, "build_script_attrs": { "compile_data_glob": [ @@ -14462,11 +15164,11 @@ "deps": { "common": [ { - "id": "cranelift-codegen-meta 0.114.0", + "id": "cranelift-codegen-meta 0.115.0", "target": "cranelift_codegen_meta" }, { - "id": "cranelift-isle 0.114.0", + "id": "cranelift-isle 0.115.0", "target": "cranelift_isle" } ], @@ -14479,14 +15181,14 @@ ], "license_file": "LICENSE" }, - "cranelift-codegen-meta 0.114.0": { + "cranelift-codegen-meta 0.115.0": { "name": "cranelift-codegen-meta", - "version": "0.114.0", + "version": "0.115.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/cranelift-codegen-meta/0.114.0/download", - "sha256": "f7ca74f4b68319da11d39e894437cb6e20ec7c2e11fbbda823c3bf207beedff7", + "url": "https://static.crates.io/crates/cranelift-codegen-meta/0.115.0/download", + "sha256": "5a68e358827afe4bfb6239fcbf6fbd5ac56206ece8a99c8f5f9bbd518773281a", "patch_args": [ "-p4" ], @@ -14517,14 +15219,14 @@ "deps": { "common": [ { - "id": "cranelift-codegen-shared 0.114.0", + "id": "cranelift-codegen-shared 0.115.0", "target": "cranelift_codegen_shared" } ], "selects": {} }, "edition": "2021", - "version": "0.114.0" + "version": "0.115.0" }, "license": "Apache-2.0 WITH LLVM-exception", "license_ids": [ @@ -14532,14 +15234,14 @@ ], "license_file": "LICENSE" }, - "cranelift-codegen-shared 0.114.0": { + "cranelift-codegen-shared 0.115.0": { "name": "cranelift-codegen-shared", - "version": "0.114.0", + "version": "0.115.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/cranelift-codegen-shared/0.114.0/download", - "sha256": "897e54f433a0269c4187871aa06d452214d5515d228d5bdc22219585e9eef895" + "url": "https://static.crates.io/crates/cranelift-codegen-shared/0.115.0/download", + "sha256": "e184c9767afbe73d50c55ec29abcf4c32f9baf0d9d22b86d58c4d55e06dee181" } }, "targets": [ @@ -14562,7 +15264,7 @@ "**" ], "edition": "2021", - "version": "0.114.0" + "version": "0.115.0" }, "license": "Apache-2.0 WITH LLVM-exception", "license_ids": [ @@ -14570,14 +15272,14 @@ ], "license_file": "LICENSE" }, - "cranelift-control 0.114.0": { + "cranelift-control 0.115.0": { "name": "cranelift-control", - "version": "0.114.0", + "version": "0.115.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/cranelift-control/0.114.0/download", - "sha256": "29cb4018f5bf59fb53f515fa9d80e6f8c5ce19f198dc538984ebd23ecf8965ec" + "url": "https://static.crates.io/crates/cranelift-control/0.115.0/download", + "sha256": "5cc7664f2a66f053e33f149e952bb5971d138e3af637f5097727ed6dc0ed95dd" } }, "targets": [ @@ -14616,7 +15318,7 @@ "selects": {} }, "edition": "2021", - "version": "0.114.0" + "version": "0.115.0" }, "license": "Apache-2.0 WITH LLVM-exception", "license_ids": [ @@ -14624,14 +15326,14 @@ ], "license_file": "LICENSE" }, - "cranelift-entity 0.114.0": { + "cranelift-entity 0.115.0": { "name": "cranelift-entity", - "version": "0.114.0", + "version": "0.115.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/cranelift-entity/0.114.0/download", - "sha256": "305399fd781a2953ac78c1396f02ff53144f39c33eb7fc7789cf4e8936d13a96" + "url": "https://static.crates.io/crates/cranelift-entity/0.115.0/download", + "sha256": "118597e3a9cf86c3556fa579a7a23b955fa18231651a52a77a2475d305a9cf84" } }, "targets": [ @@ -14664,11 +15366,11 @@ "deps": { "common": [ { - "id": "cranelift-bitset 0.114.0", + "id": "cranelift-bitset 0.115.0", "target": "cranelift_bitset" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -14678,13 +15380,13 @@ "proc_macro_deps": { "common": [ { - "id": "serde_derive 1.0.214", + "id": "serde_derive 1.0.217", "target": "serde_derive" } ], "selects": {} }, - "version": "0.114.0" + "version": "0.115.0" }, "license": "Apache-2.0 WITH LLVM-exception", "license_ids": [ @@ -14692,14 +15394,14 @@ ], "license_file": "LICENSE" }, - "cranelift-frontend 0.114.0": { + "cranelift-frontend 0.115.0": { "name": "cranelift-frontend", - "version": "0.114.0", + "version": "0.115.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/cranelift-frontend/0.114.0/download", - "sha256": "9230b460a128d53653456137751d27baf567947a3ab8c0c4d6e31fd08036d81e" + "url": "https://static.crates.io/crates/cranelift-frontend/0.115.0/download", + "sha256": "7638ea1efb069a0aa18d8ee67401b6b0d19f6bfe5de5e9ede348bfc80bb0d8c7" } }, "targets": [ @@ -14731,7 +15433,7 @@ "deps": { "common": [ { - "id": "cranelift-codegen 0.114.0", + "id": "cranelift-codegen 0.115.0", "target": "cranelift_codegen" }, { @@ -14750,7 +15452,7 @@ "selects": {} }, "edition": "2021", - "version": "0.114.0" + "version": "0.115.0" }, "license": "Apache-2.0 WITH LLVM-exception", "license_ids": [ @@ -14758,14 +15460,14 @@ ], "license_file": "LICENSE" }, - "cranelift-isle 0.114.0": { + "cranelift-isle 0.115.0": { "name": "cranelift-isle", - "version": "0.114.0", + "version": "0.115.0", "package_url": "https://github.com/bytecodealliance/wasmtime/tree/main/cranelift/isle", "repository": { "Http": { - "url": "https://static.crates.io/crates/cranelift-isle/0.114.0/download", - "sha256": "b961e24ae3ec9813a24a15ae64bbd2a42e4de4d79a7f3225a412e3b94e78d1c8", + "url": "https://static.crates.io/crates/cranelift-isle/0.115.0/download", + "sha256": "15c53e1152a0b01c4ed2b1e0535602b8e86458777dd9d18b28732b16325c7dc0", "patch_args": [ "-p4" ], @@ -14814,14 +15516,14 @@ "deps": { "common": [ { - "id": "cranelift-isle 0.114.0", + "id": "cranelift-isle 0.115.0", "target": "build_script_build" } ], "selects": {} }, "edition": "2021", - "version": "0.114.0" + "version": "0.115.0" }, "build_script_attrs": { "compile_data_glob": [ @@ -14837,14 +15539,14 @@ ], "license_file": null }, - "cranelift-native 0.114.0": { + "cranelift-native 0.115.0": { "name": "cranelift-native", - "version": "0.114.0", + "version": "0.115.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/cranelift-native/0.114.0/download", - "sha256": "4d5bd76df6c9151188dfa428c863b33da5b34561b67f43c0cf3f24a794f9fa1f" + "url": "https://static.crates.io/crates/cranelift-native/0.115.0/download", + "sha256": "7b7d8f895444fa52dd7bdd0bed11bf007a7fb43af65a6deac8fcc4094c6372f7" } }, "targets": [ @@ -14876,7 +15578,7 @@ "deps": { "common": [ { - "id": "cranelift-codegen 0.114.0", + "id": "cranelift-codegen 0.115.0", "target": "cranelift_codegen" }, { @@ -14894,7 +15596,7 @@ } }, "edition": "2021", - "version": "0.114.0" + "version": "0.115.0" }, "license": "Apache-2.0 WITH LLVM-exception", "license_ids": [ @@ -15083,7 +15785,7 @@ "target": "regex" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -15109,7 +15811,7 @@ "proc_macro_deps": { "common": [ { - "id": "serde_derive 1.0.214", + "id": "serde_derive 1.0.217", "target": "serde_derive" } ], @@ -15644,7 +16346,7 @@ "target": "signal_hook_mio" } ], - "aarch64-fuchsia": [ + "aarch64-linux-android": [ { "id": "mio 0.8.10", "target": "mio" @@ -15658,7 +16360,17 @@ "target": "signal_hook_mio" } ], - "aarch64-linux-android": [ + "aarch64-pc-windows-msvc": [ + { + "id": "crossterm_winapi 0.9.1", + "target": "crossterm_winapi" + }, + { + "id": "winapi 0.3.9", + "target": "winapi" + } + ], + "aarch64-unknown-fuchsia": [ { "id": "mio 0.8.10", "target": "mio" @@ -15672,16 +16384,6 @@ "target": "signal_hook_mio" } ], - "aarch64-pc-windows-msvc": [ - { - "id": "crossterm_winapi 0.9.1", - "target": "crossterm_winapi" - }, - { - "id": "winapi 0.3.9", - "target": "winapi" - } - ], "aarch64-unknown-linux-gnu": [ { "id": "mio 0.8.10", @@ -15894,7 +16596,7 @@ "target": "signal_hook_mio" } ], - "x86_64-fuchsia": [ + "x86_64-linux-android": [ { "id": "mio 0.8.10", "target": "mio" @@ -15908,7 +16610,17 @@ "target": "signal_hook_mio" } ], - "x86_64-linux-android": [ + "x86_64-pc-windows-msvc": [ + { + "id": "crossterm_winapi 0.9.1", + "target": "crossterm_winapi" + }, + { + "id": "winapi 0.3.9", + "target": "winapi" + } + ], + "x86_64-unknown-freebsd": [ { "id": "mio 0.8.10", "target": "mio" @@ -15922,17 +16634,7 @@ "target": "signal_hook_mio" } ], - "x86_64-pc-windows-msvc": [ - { - "id": "crossterm_winapi 0.9.1", - "target": "crossterm_winapi" - }, - { - "id": "winapi 0.3.9", - "target": "winapi" - } - ], - "x86_64-unknown-freebsd": [ + "x86_64-unknown-fuchsia": [ { "id": "mio 0.8.10", "target": "mio" @@ -16110,6 +16812,74 @@ ], "license_file": null }, + "crypto-bigint 0.4.9": { + "name": "crypto-bigint", + "version": "0.4.9", + "package_url": "https://github.com/RustCrypto/crypto-bigint", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/crypto-bigint/0.4.9/download", + "sha256": "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" + } + }, + "targets": [ + { + "Library": { + "crate_name": "crypto_bigint", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "crypto_bigint", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "generic-array", + "rand_core", + "zeroize" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "generic-array 0.14.7", + "target": "generic_array" + }, + { + "id": "rand_core 0.6.4", + "target": "rand_core" + }, + { + "id": "subtle 2.6.1", + "target": "subtle" + }, + { + "id": "zeroize 1.8.1", + "target": "zeroize" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.4.9" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "crypto-bigint 0.5.3": { "name": "crypto-bigint", "version": "0.5.3", @@ -16405,7 +17175,7 @@ "target": "ryu" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -16475,6 +17245,54 @@ ], "license_file": "LICENSE-MIT" }, + "ctr 0.9.2": { + "name": "ctr", + "version": "0.9.2", + "package_url": "https://github.com/RustCrypto/block-modes", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/ctr/0.9.2/download", + "sha256": "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" + } + }, + "targets": [ + { + "Library": { + "crate_name": "ctr", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "ctr", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "cipher 0.4.4", + "target": "cipher" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.9.2" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "ctrlc 3.4.5": { "name": "ctrlc", "version": "3.4.5", @@ -16917,14 +17735,14 @@ ], "license_file": "LICENSE" }, - "darling 0.14.4": { + "darling 0.20.10": { "name": "darling", - "version": "0.14.4", + "version": "0.20.10", "package_url": "https://github.com/TedDriggs/darling", "repository": { "Http": { - "url": "https://static.crates.io/crates/darling/0.14.4/download", - "sha256": "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" + "url": "https://static.crates.io/crates/darling/0.20.10/download", + "sha256": "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" } }, "targets": [ @@ -16956,86 +17774,23 @@ "deps": { "common": [ { - "id": "darling_core 0.14.4", + "id": "darling_core 0.20.10", "target": "darling_core" } ], "selects": {} }, - "edition": "2018", - "proc_macro_deps": { - "common": [ - { - "id": "darling_macro 0.14.4", - "target": "darling_macro" - } - ], - "selects": {} - }, - "version": "0.14.4" - }, - "license": "MIT", - "license_ids": [ - "MIT" - ], - "license_file": "LICENSE" - }, - "darling 0.20.3": { - "name": "darling", - "version": "0.20.3", - "package_url": "https://github.com/TedDriggs/darling", - "repository": { - "Http": { - "url": "https://static.crates.io/crates/darling/0.20.3/download", - "sha256": "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" - } - }, - "targets": [ - { - "Library": { - "crate_name": "darling", - "crate_root": "src/lib.rs", - "srcs": { - "allow_empty": true, - "include": [ - "**/*.rs" - ] - } - } - } - ], - "library_target_name": "darling", - "common_attrs": { - "compile_data_glob": [ - "**" - ], - "crate_features": { - "common": [ - "default", - "suggestions" - ], - "selects": {} - }, - "deps": { - "common": [ - { - "id": "darling_core 0.20.3", - "target": "darling_core" - } - ], - "selects": {} - }, - "edition": "2018", + "edition": "2021", "proc_macro_deps": { "common": [ { - "id": "darling_macro 0.20.3", + "id": "darling_macro 0.20.10", "target": "darling_macro" } ], "selects": {} }, - "version": "0.20.3" + "version": "0.20.10" }, "license": "MIT", "license_ids": [ @@ -17117,14 +17872,14 @@ ], "license_file": "LICENSE" }, - "darling_core 0.14.4": { + "darling_core 0.20.10": { "name": "darling_core", - "version": "0.14.4", + "version": "0.20.10", "package_url": "https://github.com/TedDriggs/darling", "repository": { "Http": { - "url": "https://static.crates.io/crates/darling_core/0.14.4/download", - "sha256": "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" + "url": "https://static.crates.io/crates/darling_core/0.20.10/download", + "sha256": "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" } }, "targets": [ @@ -17172,81 +17927,7 @@ "target": "quote" }, { - "id": "strsim 0.10.0", - "target": "strsim" - }, - { - "id": "syn 1.0.109", - "target": "syn" - } - ], - "selects": {} - }, - "edition": "2018", - "version": "0.14.4" - }, - "license": "MIT", - "license_ids": [ - "MIT" - ], - "license_file": "LICENSE" - }, - "darling_core 0.20.3": { - "name": "darling_core", - "version": "0.20.3", - "package_url": "https://github.com/TedDriggs/darling", - "repository": { - "Http": { - "url": "https://static.crates.io/crates/darling_core/0.20.3/download", - "sha256": "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" - } - }, - "targets": [ - { - "Library": { - "crate_name": "darling_core", - "crate_root": "src/lib.rs", - "srcs": { - "allow_empty": true, - "include": [ - "**/*.rs" - ] - } - } - } - ], - "library_target_name": "darling_core", - "common_attrs": { - "compile_data_glob": [ - "**" - ], - "crate_features": { - "common": [ - "strsim", - "suggestions" - ], - "selects": {} - }, - "deps": { - "common": [ - { - "id": "fnv 1.0.7", - "target": "fnv" - }, - { - "id": "ident_case 1.0.1", - "target": "ident_case" - }, - { - "id": "proc-macro2 1.0.89", - "target": "proc_macro2" - }, - { - "id": "quote 1.0.37", - "target": "quote" - }, - { - "id": "strsim 0.10.0", + "id": "strsim 0.11.1", "target": "strsim" }, { @@ -17256,8 +17937,8 @@ ], "selects": {} }, - "edition": "2018", - "version": "0.20.3" + "edition": "2021", + "version": "0.20.10" }, "license": "MIT", "license_ids": [ @@ -17320,14 +18001,14 @@ ], "license_file": "LICENSE" }, - "darling_macro 0.14.4": { + "darling_macro 0.20.10": { "name": "darling_macro", - "version": "0.14.4", + "version": "0.20.10", "package_url": "https://github.com/TedDriggs/darling", "repository": { "Http": { - "url": "https://static.crates.io/crates/darling_macro/0.14.4/download", - "sha256": "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" + "url": "https://static.crates.io/crates/darling_macro/0.20.10/download", + "sha256": "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" } }, "targets": [ @@ -17352,62 +18033,7 @@ "deps": { "common": [ { - "id": "darling_core 0.14.4", - "target": "darling_core" - }, - { - "id": "quote 1.0.37", - "target": "quote" - }, - { - "id": "syn 1.0.109", - "target": "syn" - } - ], - "selects": {} - }, - "edition": "2018", - "version": "0.14.4" - }, - "license": "MIT", - "license_ids": [ - "MIT" - ], - "license_file": "LICENSE" - }, - "darling_macro 0.20.3": { - "name": "darling_macro", - "version": "0.20.3", - "package_url": "https://github.com/TedDriggs/darling", - "repository": { - "Http": { - "url": "https://static.crates.io/crates/darling_macro/0.20.3/download", - "sha256": "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" - } - }, - "targets": [ - { - "ProcMacro": { - "crate_name": "darling_macro", - "crate_root": "src/lib.rs", - "srcs": { - "allow_empty": true, - "include": [ - "**/*.rs" - ] - } - } - } - ], - "library_target_name": "darling_macro", - "common_attrs": { - "compile_data_glob": [ - "**" - ], - "deps": { - "common": [ - { - "id": "darling_core 0.20.3", + "id": "darling_core 0.20.10", "target": "darling_core" }, { @@ -17421,8 +18047,8 @@ ], "selects": {} }, - "edition": "2018", - "version": "0.20.3" + "edition": "2021", + "version": "0.20.10" }, "license": "MIT", "license_ids": [ @@ -17645,6 +18271,141 @@ ], "license_file": "LICENSE" }, + "dbus 0.9.7": { + "name": "dbus", + "version": "0.9.7", + "package_url": "https://github.com/diwic/dbus-rs", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/dbus/0.9.7/download", + "sha256": "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" + } + }, + "targets": [ + { + "Library": { + "crate_name": "dbus", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "dbus", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "vendored" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "libc 0.2.158", + "target": "libc" + }, + { + "id": "libdbus-sys 0.2.5", + "target": "libdbus_sys" + } + ], + "selects": { + "cfg(windows)": [ + { + "id": "winapi 0.3.9", + "target": "winapi" + } + ] + } + }, + "edition": "2018", + "version": "0.9.7" + }, + "license": "Apache-2.0/MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, + "dbus-secret-service 4.0.3": { + "name": "dbus-secret-service", + "version": "4.0.3", + "package_url": "https://github.com/brotskydotcom/dbus-secret-service.git", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/dbus-secret-service/4.0.3/download", + "sha256": "b42a16374481d92aed73ae45b1f120207d8e71d24fb89f357fadbd8f946fd84b" + } + }, + "targets": [ + { + "Library": { + "crate_name": "dbus_secret_service", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "dbus_secret_service", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "vendored" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "dbus 0.9.7", + "target": "dbus" + }, + { + "id": "futures-util 0.3.31", + "target": "futures_util" + }, + { + "id": "num 0.4.3", + "target": "num" + }, + { + "id": "once_cell 1.19.0", + "target": "once_cell" + }, + { + "id": "rand 0.8.5", + "target": "rand" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "4.0.3" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "debugid 0.8.0": { "name": "debugid", "version": "0.8.0", @@ -17692,6 +18453,74 @@ ], "license_file": "LICENSE" }, + "der 0.6.1": { + "name": "der", + "version": "0.6.1", + "package_url": "https://github.com/RustCrypto/formats/tree/master/der", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/der/0.6.1/download", + "sha256": "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" + } + }, + "targets": [ + { + "Library": { + "crate_name": "der", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "der", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "alloc", + "const-oid", + "oid", + "pem", + "pem-rfc7468", + "std", + "zeroize" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "const-oid 0.9.5", + "target": "const_oid" + }, + { + "id": "pem-rfc7468 0.6.0", + "target": "pem_rfc7468" + }, + { + "id": "zeroize 1.8.1", + "target": "zeroize" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.6.1" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "der 0.7.8": { "name": "der", "version": "0.7.8", @@ -17944,6 +18773,7 @@ "common": [ "alloc", "powerfmt", + "serde", "std" ], "selects": {} @@ -17953,6 +18783,10 @@ { "id": "powerfmt 0.2.0", "target": "powerfmt" + }, + { + "id": "serde 1.0.217", + "target": "serde" } ], "selects": {} @@ -18175,6 +19009,270 @@ ], "license_file": "LICENSE" }, + "dfx-core 0.1.3": { + "name": "dfx-core", + "version": "0.1.3", + "package_url": "https://github.com/dfinity/sdk", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/dfx-core/0.1.3/download", + "sha256": "b4be4fc6929580b0ef8ac3678c03f31b59ffb1aa0d90ab754842d40cfc5b4552" + } + }, + "targets": [ + { + "Library": { + "crate_name": "dfx_core", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "dfx_core", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "aes-gcm 0.10.3", + "target": "aes_gcm" + }, + { + "id": "argon2 0.4.1", + "target": "argon2" + }, + { + "id": "backoff 0.4.0", + "target": "backoff" + }, + { + "id": "bip32 0.4.0", + "target": "bip32" + }, + { + "id": "byte-unit 4.0.19", + "target": "byte_unit" + }, + { + "id": "bytes 1.9.0", + "target": "bytes" + }, + { + "id": "candid 0.10.13", + "target": "candid" + }, + { + "id": "clap 4.5.20", + "target": "clap" + }, + { + "id": "dialoguer 0.11.0", + "target": "dialoguer" + }, + { + "id": "directories-next 2.0.0", + "target": "directories_next" + }, + { + "id": "dunce 1.0.5", + "target": "dunce" + }, + { + "id": "flate2 1.0.31", + "target": "flate2" + }, + { + "id": "handlebars 4.5.0", + "target": "handlebars" + }, + { + "id": "hex 0.4.3", + "target": "hex" + }, + { + "id": "humantime-serde 1.1.1", + "target": "humantime_serde" + }, + { + "id": "ic-agent 0.39.2", + "target": "ic_agent" + }, + { + "id": "ic-identity-hsm 0.39.2", + "target": "ic_identity_hsm" + }, + { + "id": "ic-utils 0.39.2", + "target": "ic_utils" + }, + { + "id": "itertools 0.10.5", + "target": "itertools" + }, + { + "id": "k256 0.11.6", + "target": "k256" + }, + { + "id": "keyring 3.4.0", + "target": "keyring" + }, + { + "id": "lazy_static 1.4.0", + "target": "lazy_static" + }, + { + "id": "reqwest 0.12.9", + "target": "reqwest" + }, + { + "id": "ring 0.16.20", + "target": "ring" + }, + { + "id": "schemars 0.8.16", + "target": "schemars" + }, + { + "id": "sec1 0.3.0", + "target": "sec1" + }, + { + "id": "semver 1.0.22", + "target": "semver" + }, + { + "id": "serde 1.0.217", + "target": "serde" + }, + { + "id": "serde_json 1.0.132", + "target": "serde_json" + }, + { + "id": "sha2 0.10.8", + "target": "sha2" + }, + { + "id": "slog 2.7.0", + "target": "slog" + }, + { + "id": "tar 0.4.39", + "target": "tar" + }, + { + "id": "tempfile 3.12.0", + "target": "tempfile" + }, + { + "id": "thiserror 1.0.68", + "target": "thiserror" + }, + { + "id": "time 0.3.36", + "target": "time" + }, + { + "id": "tiny-bip39 1.0.0", + "target": "bip39" + }, + { + "id": "url 2.5.3", + "target": "url" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.1.3" + }, + "license": "Apache-2.0", + "license_ids": [ + "Apache-2.0" + ], + "license_file": null + }, + "dialoguer 0.11.0": { + "name": "dialoguer", + "version": "0.11.0", + "package_url": "https://github.com/console-rs/dialoguer", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/dialoguer/0.11.0/download", + "sha256": "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de" + } + }, + "targets": [ + { + "Library": { + "crate_name": "dialoguer", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "dialoguer", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "default", + "editor", + "password", + "tempfile", + "zeroize" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "console 0.15.7", + "target": "console" + }, + { + "id": "shell-words 1.1.0", + "target": "shell_words" + }, + { + "id": "tempfile 3.12.0", + "target": "tempfile" + }, + { + "id": "thiserror 1.0.68", + "target": "thiserror" + }, + { + "id": "zeroize 1.8.1", + "target": "zeroize" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.11.0" + }, + "license": "MIT", + "license_ids": [ + "MIT" + ], + "license_file": "LICENSE" + }, "diff 0.1.13": { "name": "diff", "version": "0.1.13", @@ -18513,15 +19611,15 @@ }, { "id": "bitcoin 0.28.2", - "target": "bitcoin" + "target": "bitcoin", + "alias": "bitcoin_0_28" }, { - "id": "bitcoin 0.32.2", - "target": "bitcoin", - "alias": "bitcoin_0_32" + "id": "bitcoin 0.32.5", + "target": "bitcoin" }, { - "id": "bitcoincore-rpc 0.15.0", + "id": "bitcoincore-rpc 0.19.0", "target": "bitcoincore_rpc" }, { @@ -18573,7 +19671,7 @@ "target": "canbench_rs" }, { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -18585,7 +19683,7 @@ "target": "cargo_metadata" }, { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" }, { @@ -18672,6 +19770,10 @@ "id": "dashmap 5.5.3", "target": "dashmap" }, + { + "id": "dfx-core 0.1.3", + "target": "dfx_core" + }, { "id": "dyn-clone 1.0.14", "target": "dyn_clone" @@ -18809,7 +19911,7 @@ "target": "hyper_util" }, { - "id": "ic-agent 0.37.1", + "id": "ic-agent 0.39.2", "target": "ic_agent" }, { @@ -18825,27 +19927,32 @@ "target": "ic_canister_log" }, { - "id": "ic-canister-sig-creation 1.0.1", + "id": "ic-canister-sig-creation 1.1.0", "target": "ic_canister_sig_creation" }, { - "id": "ic-cbor 2.6.0", + "id": "ic-cbor 3.0.2", "target": "ic_cbor" }, { "id": "ic-cdk 0.16.0", "target": "ic_cdk" }, + { + "id": "ic-cdk 0.18.0-alpha.1", + "target": "ic_cdk", + "alias": "ic_cdk_next" + }, { "id": "ic-cdk-timers 0.11.0", "target": "ic_cdk_timers" }, { - "id": "ic-certificate-verification 2.6.0", + "id": "ic-certificate-verification 3.0.2", "target": "ic_certificate_verification" }, { - "id": "ic-certification 2.6.0", + "id": "ic-certification 3.0.2", "target": "ic_certification" }, { @@ -18853,11 +19960,11 @@ "target": "ic_certified_map" }, { - "id": "ic-http-certification 2.6.0", + "id": "ic-http-certification 3.0.2", "target": "ic_http_certification" }, { - "id": "ic-http-gateway 0.0.0", + "id": "ic-http-gateway 0.1.0", "target": "ic_http_gateway" }, { @@ -18865,7 +19972,7 @@ "target": "ic_metrics_encoder" }, { - "id": "ic-response-verification 2.6.0", + "id": "ic-response-verification 3.0.2", "target": "ic_response_verification" }, { @@ -18881,11 +19988,11 @@ "target": "ic_test_state_machine_client" }, { - "id": "ic-transport-types 0.37.1", + "id": "ic-transport-types 0.39.2", "target": "ic_transport_types" }, { - "id": "ic-utils 0.37.0", + "id": "ic-utils 0.39.2", "target": "ic_utils" }, { @@ -18949,7 +20056,7 @@ "target": "intmap" }, { - "id": "ipnet 2.8.0", + "id": "ipnet 2.10.1", "target": "ipnet" }, { @@ -19118,7 +20225,7 @@ "target": "opentelemetry_prometheus" }, { - "id": "opentelemetry_sdk 0.27.0", + "id": "opentelemetry_sdk 0.27.1", "target": "opentelemetry_sdk" }, { @@ -19359,7 +20466,7 @@ "target": "semver" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -19466,6 +20573,10 @@ "id": "syn 1.0.109", "target": "syn" }, + { + "id": "syscalls 0.6.18", + "target": "syscalls" + }, { "id": "tar 0.4.39", "target": "tar" @@ -19534,6 +20645,10 @@ "id": "tokio-socks 0.5.2", "target": "tokio_socks" }, + { + "id": "tokio-stream 0.1.17", + "target": "tokio_stream" + }, { "id": "tokio-test 0.4.4", "target": "tokio_test" @@ -19651,11 +20766,11 @@ "target": "wasmprinter" }, { - "id": "wasmtime 27.0.0", + "id": "wasmtime 28.0.0", "target": "wasmtime" }, { - "id": "wasmtime-environ 27.0.0", + "id": "wasmtime-environ 28.0.0", "target": "wasmtime_environ" }, { @@ -19724,6 +20839,11 @@ "id": "ic-cdk-macros 0.9.0", "target": "ic_cdk_macros" }, + { + "id": "ic-cdk-macros 0.18.0-alpha.1", + "target": "ic_cdk_macros", + "alias": "ic_cdk_macros_next" + }, { "id": "indoc 1.0.9", "target": "indoc" @@ -19765,6 +20885,58 @@ "license_ids": [], "license_file": null }, + "directories-next 2.0.0": { + "name": "directories-next", + "version": "2.0.0", + "package_url": "https://github.com/xdg-rs/dirs/tree/master/directories", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/directories-next/2.0.0/download", + "sha256": "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" + } + }, + "targets": [ + { + "Library": { + "crate_name": "directories_next", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "directories_next", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "cfg-if 1.0.0", + "target": "cfg_if" + }, + { + "id": "dirs-sys-next 0.1.2", + "target": "dirs_sys_next" + } + ], + "selects": {} + }, + "edition": "2018", + "version": "2.0.0" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "dirs 2.0.2": { "name": "dirs", "version": "2.0.2", @@ -20306,6 +21478,46 @@ ], "license_file": "LICENSE" }, + "dunce 1.0.5": { + "name": "dunce", + "version": "1.0.5", + "package_url": "https://gitlab.com/kornelski/dunce", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/dunce/1.0.5/download", + "sha256": "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + } + }, + "targets": [ + { + "Library": { + "crate_name": "dunce", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "dunce", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "edition": "2021", + "version": "1.0.5" + }, + "license": "CC0-1.0 OR MIT-0 OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "CC0-1.0", + "MIT-0" + ], + "license_file": "LICENSE" + }, "duration-string 0.3.0": { "name": "duration-string", "version": "0.3.0", @@ -20344,7 +21556,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -20396,6 +21608,82 @@ ], "license_file": "LICENSE-APACHE" }, + "ecdsa 0.14.8": { + "name": "ecdsa", + "version": "0.14.8", + "package_url": "https://github.com/RustCrypto/signatures/tree/master/ecdsa", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/ecdsa/0.14.8/download", + "sha256": "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" + } + }, + "targets": [ + { + "Library": { + "crate_name": "ecdsa", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "ecdsa", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "alloc", + "arithmetic", + "der", + "digest", + "hazmat", + "pem", + "pkcs8", + "rfc6979", + "sign", + "std", + "verify" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "der 0.6.1", + "target": "der" + }, + { + "id": "elliptic-curve 0.12.3", + "target": "elliptic_curve" + }, + { + "id": "rfc6979 0.3.1", + "target": "rfc6979" + }, + { + "id": "signature 1.6.4", + "target": "signature" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.14.8" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "ecdsa 0.16.9": { "name": "ecdsa", "version": "0.16.9", @@ -20429,6 +21717,7 @@ "common": [ "alloc", "arithmetic", + "default", "der", "digest", "hazmat", @@ -20596,7 +21885,7 @@ "target": "rand_core" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -20882,6 +22171,118 @@ ], "license_file": "LICENSE-APACHE" }, + "elliptic-curve 0.12.3": { + "name": "elliptic-curve", + "version": "0.12.3", + "package_url": "https://github.com/RustCrypto/traits/tree/master/elliptic-curve", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/elliptic-curve/0.12.3/download", + "sha256": "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" + } + }, + "targets": [ + { + "Library": { + "crate_name": "elliptic_curve", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "elliptic_curve", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "alloc", + "arithmetic", + "digest", + "ff", + "group", + "hazmat", + "pem", + "pem-rfc7468", + "pkcs8", + "sec1", + "std" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "base16ct 0.1.1", + "target": "base16ct" + }, + { + "id": "crypto-bigint 0.4.9", + "target": "crypto_bigint" + }, + { + "id": "der 0.6.1", + "target": "der" + }, + { + "id": "digest 0.10.7", + "target": "digest" + }, + { + "id": "ff 0.12.1", + "target": "ff" + }, + { + "id": "generic-array 0.14.7", + "target": "generic_array" + }, + { + "id": "group 0.12.1", + "target": "group" + }, + { + "id": "pem-rfc7468 0.6.0", + "target": "pem_rfc7468" + }, + { + "id": "pkcs8 0.9.0", + "target": "pkcs8" + }, + { + "id": "rand_core 0.6.4", + "target": "rand_core" + }, + { + "id": "sec1 0.3.0", + "target": "sec1" + }, + { + "id": "subtle 2.6.1", + "target": "subtle" + }, + { + "id": "zeroize 1.8.1", + "target": "zeroize" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.12.3" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "elliptic-curve 0.13.8": { "name": "elliptic-curve", "version": "0.13.8", @@ -20915,6 +22316,7 @@ "common": [ "alloc", "arithmetic", + "default", "digest", "ff", "group", @@ -21789,7 +23191,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -22027,7 +23429,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" } ], @@ -22102,7 +23504,7 @@ "target": "once_cell" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -22192,7 +23594,7 @@ "target": "regex" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -22481,7 +23883,7 @@ "target": "rlp" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -22562,7 +23964,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -22578,6 +23980,45 @@ ], "license_file": "LICENSE-APACHE" }, + "event-listener 2.5.3": { + "name": "event-listener", + "version": "2.5.3", + "package_url": "https://github.com/smol-rs/event-listener", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/event-listener/2.5.3/download", + "sha256": "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + } + }, + "targets": [ + { + "Library": { + "crate_name": "event_listener", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "event_listener", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "edition": "2018", + "version": "2.5.3" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "event-listener 4.0.3": { "name": "event-listener", "version": "4.0.3", @@ -22644,7 +24085,254 @@ "target": "parking" } ], - "aarch64-fuchsia": [ + "aarch64-linux-android": [ + { + "id": "parking 2.1.1", + "target": "parking" + } + ], + "aarch64-pc-windows-msvc": [ + { + "id": "parking 2.1.1", + "target": "parking" + } + ], + "aarch64-unknown-fuchsia": [ + { + "id": "parking 2.1.1", + "target": "parking" + } + ], + "aarch64-unknown-linux-gnu": [ + { + "id": "parking 2.1.1", + "target": "parking" + } + ], + "aarch64-unknown-nixos-gnu": [ + { + "id": "parking 2.1.1", + "target": "parking" + } + ], + "aarch64-unknown-nto-qnx710": [ + { + "id": "parking 2.1.1", + "target": "parking" + } + ], + "arm-unknown-linux-gnueabi": [ + { + "id": "parking 2.1.1", + "target": "parking" + } + ], + "armv7-linux-androideabi": [ + { + "id": "parking 2.1.1", + "target": "parking" + } + ], + "armv7-unknown-linux-gnueabi": [ + { + "id": "parking 2.1.1", + "target": "parking" + } + ], + "i686-apple-darwin": [ + { + "id": "parking 2.1.1", + "target": "parking" + } + ], + "i686-linux-android": [ + { + "id": "parking 2.1.1", + "target": "parking" + } + ], + "i686-pc-windows-msvc": [ + { + "id": "parking 2.1.1", + "target": "parking" + } + ], + "i686-unknown-freebsd": [ + { + "id": "parking 2.1.1", + "target": "parking" + } + ], + "i686-unknown-linux-gnu": [ + { + "id": "parking 2.1.1", + "target": "parking" + } + ], + "powerpc-unknown-linux-gnu": [ + { + "id": "parking 2.1.1", + "target": "parking" + } + ], + "riscv32imc-unknown-none-elf": [ + { + "id": "parking 2.1.1", + "target": "parking" + } + ], + "riscv64gc-unknown-none-elf": [ + { + "id": "parking 2.1.1", + "target": "parking" + } + ], + "s390x-unknown-linux-gnu": [ + { + "id": "parking 2.1.1", + "target": "parking" + } + ], + "thumbv7em-none-eabi": [ + { + "id": "parking 2.1.1", + "target": "parking" + } + ], + "thumbv8m.main-none-eabi": [ + { + "id": "parking 2.1.1", + "target": "parking" + } + ], + "x86_64-apple-darwin": [ + { + "id": "parking 2.1.1", + "target": "parking" + } + ], + "x86_64-apple-ios": [ + { + "id": "parking 2.1.1", + "target": "parking" + } + ], + "x86_64-linux-android": [ + { + "id": "parking 2.1.1", + "target": "parking" + } + ], + "x86_64-pc-windows-msvc": [ + { + "id": "parking 2.1.1", + "target": "parking" + } + ], + "x86_64-unknown-freebsd": [ + { + "id": "parking 2.1.1", + "target": "parking" + } + ], + "x86_64-unknown-fuchsia": [ + { + "id": "parking 2.1.1", + "target": "parking" + } + ], + "x86_64-unknown-linux-gnu": [ + { + "id": "parking 2.1.1", + "target": "parking" + } + ], + "x86_64-unknown-nixos-gnu": [ + { + "id": "parking 2.1.1", + "target": "parking" + } + ], + "x86_64-unknown-none": [ + { + "id": "parking 2.1.1", + "target": "parking" + } + ] + } + }, + "edition": "2021", + "version": "4.0.3" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, + "event-listener 5.3.1": { + "name": "event-listener", + "version": "5.3.1", + "package_url": "https://github.com/smol-rs/event-listener", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/event-listener/5.3.1/download", + "sha256": "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" + } + }, + "targets": [ + { + "Library": { + "crate_name": "event_listener", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "event_listener", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "default", + "parking", + "std" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "concurrent-queue 2.5.0", + "target": "concurrent_queue" + }, + { + "id": "pin-project-lite 0.2.13", + "target": "pin_project_lite" + } + ], + "selects": { + "aarch64-apple-darwin": [ + { + "id": "parking 2.1.1", + "target": "parking" + } + ], + "aarch64-apple-ios": [ + { + "id": "parking 2.1.1", + "target": "parking" + } + ], + "aarch64-apple-ios-sim": [ { "id": "parking 2.1.1", "target": "parking" @@ -22662,6 +24350,12 @@ "target": "parking" } ], + "aarch64-unknown-fuchsia": [ + { + "id": "parking 2.1.1", + "target": "parking" + } + ], "aarch64-unknown-linux-gnu": [ { "id": "parking 2.1.1", @@ -22776,12 +24470,6 @@ "target": "parking" } ], - "x86_64-fuchsia": [ - { - "id": "parking 2.1.1", - "target": "parking" - } - ], "x86_64-linux-android": [ { "id": "parking 2.1.1", @@ -22800,254 +24488,7 @@ "target": "parking" } ], - "x86_64-unknown-linux-gnu": [ - { - "id": "parking 2.1.1", - "target": "parking" - } - ], - "x86_64-unknown-nixos-gnu": [ - { - "id": "parking 2.1.1", - "target": "parking" - } - ], - "x86_64-unknown-none": [ - { - "id": "parking 2.1.1", - "target": "parking" - } - ] - } - }, - "edition": "2021", - "version": "4.0.3" - }, - "license": "Apache-2.0 OR MIT", - "license_ids": [ - "Apache-2.0", - "MIT" - ], - "license_file": "LICENSE-APACHE" - }, - "event-listener 5.3.1": { - "name": "event-listener", - "version": "5.3.1", - "package_url": "https://github.com/smol-rs/event-listener", - "repository": { - "Http": { - "url": "https://static.crates.io/crates/event-listener/5.3.1/download", - "sha256": "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" - } - }, - "targets": [ - { - "Library": { - "crate_name": "event_listener", - "crate_root": "src/lib.rs", - "srcs": { - "allow_empty": true, - "include": [ - "**/*.rs" - ] - } - } - } - ], - "library_target_name": "event_listener", - "common_attrs": { - "compile_data_glob": [ - "**" - ], - "crate_features": { - "common": [ - "default", - "parking", - "std" - ], - "selects": {} - }, - "deps": { - "common": [ - { - "id": "concurrent-queue 2.5.0", - "target": "concurrent_queue" - }, - { - "id": "pin-project-lite 0.2.13", - "target": "pin_project_lite" - } - ], - "selects": { - "aarch64-apple-darwin": [ - { - "id": "parking 2.1.1", - "target": "parking" - } - ], - "aarch64-apple-ios": [ - { - "id": "parking 2.1.1", - "target": "parking" - } - ], - "aarch64-apple-ios-sim": [ - { - "id": "parking 2.1.1", - "target": "parking" - } - ], - "aarch64-fuchsia": [ - { - "id": "parking 2.1.1", - "target": "parking" - } - ], - "aarch64-linux-android": [ - { - "id": "parking 2.1.1", - "target": "parking" - } - ], - "aarch64-pc-windows-msvc": [ - { - "id": "parking 2.1.1", - "target": "parking" - } - ], - "aarch64-unknown-linux-gnu": [ - { - "id": "parking 2.1.1", - "target": "parking" - } - ], - "aarch64-unknown-nixos-gnu": [ - { - "id": "parking 2.1.1", - "target": "parking" - } - ], - "aarch64-unknown-nto-qnx710": [ - { - "id": "parking 2.1.1", - "target": "parking" - } - ], - "arm-unknown-linux-gnueabi": [ - { - "id": "parking 2.1.1", - "target": "parking" - } - ], - "armv7-linux-androideabi": [ - { - "id": "parking 2.1.1", - "target": "parking" - } - ], - "armv7-unknown-linux-gnueabi": [ - { - "id": "parking 2.1.1", - "target": "parking" - } - ], - "i686-apple-darwin": [ - { - "id": "parking 2.1.1", - "target": "parking" - } - ], - "i686-linux-android": [ - { - "id": "parking 2.1.1", - "target": "parking" - } - ], - "i686-pc-windows-msvc": [ - { - "id": "parking 2.1.1", - "target": "parking" - } - ], - "i686-unknown-freebsd": [ - { - "id": "parking 2.1.1", - "target": "parking" - } - ], - "i686-unknown-linux-gnu": [ - { - "id": "parking 2.1.1", - "target": "parking" - } - ], - "powerpc-unknown-linux-gnu": [ - { - "id": "parking 2.1.1", - "target": "parking" - } - ], - "riscv32imc-unknown-none-elf": [ - { - "id": "parking 2.1.1", - "target": "parking" - } - ], - "riscv64gc-unknown-none-elf": [ - { - "id": "parking 2.1.1", - "target": "parking" - } - ], - "s390x-unknown-linux-gnu": [ - { - "id": "parking 2.1.1", - "target": "parking" - } - ], - "thumbv7em-none-eabi": [ - { - "id": "parking 2.1.1", - "target": "parking" - } - ], - "thumbv8m.main-none-eabi": [ - { - "id": "parking 2.1.1", - "target": "parking" - } - ], - "x86_64-apple-darwin": [ - { - "id": "parking 2.1.1", - "target": "parking" - } - ], - "x86_64-apple-ios": [ - { - "id": "parking 2.1.1", - "target": "parking" - } - ], - "x86_64-fuchsia": [ - { - "id": "parking 2.1.1", - "target": "parking" - } - ], - "x86_64-linux-android": [ - { - "id": "parking 2.1.1", - "target": "parking" - } - ], - "x86_64-pc-windows-msvc": [ - { - "id": "parking 2.1.1", - "target": "parking" - } - ], - "x86_64-unknown-freebsd": [ + "x86_64-unknown-fuchsia": [ { "id": "parking 2.1.1", "target": "parking" @@ -23231,7 +24672,7 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -23247,7 +24688,7 @@ "target": "num_bigint" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -23973,7 +25414,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" } ], @@ -24295,6 +25736,131 @@ ], "license_file": "LICENSE-APACHE" }, + "foldhash 0.1.4": { + "name": "foldhash", + "version": "0.1.4", + "package_url": "https://github.com/orlp/foldhash", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/foldhash/0.1.4/download", + "sha256": "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + } + }, + "targets": [ + { + "Library": { + "crate_name": "foldhash", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "foldhash", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "edition": "2021", + "version": "0.1.4" + }, + "license": "Zlib", + "license_ids": [ + "Zlib" + ], + "license_file": "LICENSE" + }, + "foreign-types 0.3.2": { + "name": "foreign-types", + "version": "0.3.2", + "package_url": "https://github.com/sfackler/foreign-types", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/foreign-types/0.3.2/download", + "sha256": "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" + } + }, + "targets": [ + { + "Library": { + "crate_name": "foreign_types", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "foreign_types", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "foreign-types-shared 0.1.1", + "target": "foreign_types_shared" + } + ], + "selects": {} + }, + "edition": "2015", + "version": "0.3.2" + }, + "license": "MIT/Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, + "foreign-types-shared 0.1.1": { + "name": "foreign-types-shared", + "version": "0.1.1", + "package_url": "https://github.com/sfackler/foreign-types", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/foreign-types-shared/0.1.1/download", + "sha256": "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + } + }, + "targets": [ + { + "Library": { + "crate_name": "foreign_types_shared", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "foreign_types_shared", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "edition": "2015", + "version": "0.1.1" + }, + "license": "MIT/Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "form_urlencoded 1.2.1": { "name": "form_urlencoded", "version": "1.2.1", @@ -25882,6 +27448,58 @@ ], "license_file": "LICENSE-APACHE" }, + "ghash 0.5.1": { + "name": "ghash", + "version": "0.5.1", + "package_url": "https://github.com/RustCrypto/universal-hashes", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/ghash/0.5.1/download", + "sha256": "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" + } + }, + "targets": [ + { + "Library": { + "crate_name": "ghash", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "ghash", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "opaque-debug 0.3.0", + "target": "opaque_debug" + }, + { + "id": "polyval 0.6.2", + "target": "polyval" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.5.1" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "gimli 0.26.2": { "name": "gimli", "version": "0.26.2", @@ -26190,6 +27808,62 @@ ], "license_file": null }, + "group 0.12.1": { + "name": "group", + "version": "0.12.1", + "package_url": "https://github.com/zkcrypto/group", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/group/0.12.1/download", + "sha256": "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" + } + }, + "targets": [ + { + "Library": { + "crate_name": "group", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "group", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "ff 0.12.1", + "target": "ff" + }, + { + "id": "rand_core 0.6.4", + "target": "rand_core" + }, + { + "id": "subtle 2.6.1", + "target": "subtle" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.12.1" + }, + "license": "MIT/Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "group 0.13.0": { "name": "group", "version": "0.13.0", @@ -26472,6 +28146,84 @@ ], "license_file": "LICENSE" }, + "handlebars 4.5.0": { + "name": "handlebars", + "version": "4.5.0", + "package_url": "https://github.com/sunng87/handlebars-rust", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/handlebars/4.5.0/download", + "sha256": "faa67bab9ff362228eb3d00bd024a4965d8231bbb7921167f0cfa66c6626b225" + } + }, + "targets": [ + { + "Library": { + "crate_name": "handlebars", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "handlebars", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "default" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "log 0.4.20", + "target": "log" + }, + { + "id": "pest 2.7.4", + "target": "pest" + }, + { + "id": "serde 1.0.217", + "target": "serde" + }, + { + "id": "serde_json 1.0.132", + "target": "serde_json" + }, + { + "id": "thiserror 1.0.68", + "target": "thiserror" + } + ], + "selects": {} + }, + "edition": "2021", + "proc_macro_deps": { + "common": [ + { + "id": "pest_derive 2.7.4", + "target": "pest_derive" + } + ], + "selects": {} + }, + "version": "4.5.0" + }, + "license": "MIT", + "license_ids": [ + "MIT" + ], + "license_file": "LICENSE" + }, "hashbrown 0.12.3": { "name": "hashbrown", "version": "0.12.3", @@ -26564,11 +28316,11 @@ "target": "ahash" }, { - "id": "allocator-api2 0.2.16", + "id": "allocator-api2 0.2.21", "target": "allocator_api2" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -26584,6 +28336,60 @@ ], "license_file": "LICENSE-APACHE" }, + "hashbrown 0.15.2": { + "name": "hashbrown", + "version": "0.15.2", + "package_url": "https://github.com/rust-lang/hashbrown", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/hashbrown/0.15.2/download", + "sha256": "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + } + }, + "targets": [ + { + "Library": { + "crate_name": "hashbrown", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "hashbrown", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "default-hasher" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "foldhash 0.1.4", + "target": "foldhash" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.15.2" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "hashlink 0.8.4": { "name": "hashlink", "version": "0.8.4", @@ -27219,7 +29025,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -27266,7 +29072,8 @@ ], "crate_features": { "common": [ - "alloc" + "alloc", + "std" ], "selects": {} }, @@ -27522,7 +29329,7 @@ "target": "idna" }, { - "id": "ipnet 2.8.0", + "id": "ipnet 2.10.1", "target": "ipnet" }, { @@ -28282,62 +30089,6 @@ ], "license_file": "LICENSE" }, - "http-body-to-bytes 0.2.0": { - "name": "http-body-to-bytes", - "version": "0.2.0", - "package_url": "https://github.com/bk-rs/http-body-ext", - "repository": { - "Http": { - "url": "https://static.crates.io/crates/http-body-to-bytes/0.2.0/download", - "sha256": "17a08236c6f51c2ee95d840f45acf8fa9e339390e00b4ef640857b2f2a534d70" - } - }, - "targets": [ - { - "Library": { - "crate_name": "http_body_to_bytes", - "crate_root": "src/lib.rs", - "srcs": { - "allow_empty": true, - "include": [ - "**/*.rs" - ] - } - } - } - ], - "library_target_name": "http_body_to_bytes", - "common_attrs": { - "compile_data_glob": [ - "**" - ], - "deps": { - "common": [ - { - "id": "bytes 1.9.0", - "target": "bytes" - }, - { - "id": "http-body 1.0.1", - "target": "http_body" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - } - ], - "selects": {} - }, - "edition": "2021", - "version": "0.2.0" - }, - "license": "Apache-2.0 OR MIT", - "license_ids": [ - "Apache-2.0", - "MIT" - ], - "license_file": "LICENSE-APACHE" - }, "http-body-util 0.1.2": { "name": "http-body-util", "version": "0.1.2", @@ -28638,7 +30389,7 @@ "target": "humantime" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -29106,15 +30857,15 @@ "webpki-roots", "webpki-tokio" ], - "aarch64-fuchsia": [ + "aarch64-linux-android": [ "webpki-roots", "webpki-tokio" ], - "aarch64-linux-android": [ + "aarch64-pc-windows-msvc": [ "webpki-roots", "webpki-tokio" ], - "aarch64-pc-windows-msvc": [ + "aarch64-unknown-fuchsia": [ "webpki-roots", "webpki-tokio" ], @@ -29194,10 +30945,6 @@ "webpki-roots", "webpki-tokio" ], - "x86_64-fuchsia": [ - "webpki-roots", - "webpki-tokio" - ], "x86_64-linux-android": [ "webpki-roots", "webpki-tokio" @@ -29210,6 +30957,10 @@ "webpki-roots", "webpki-tokio" ], + "x86_64-unknown-fuchsia": [ + "webpki-roots", + "webpki-tokio" + ], "x86_64-unknown-linux-gnu": [ "webpki-roots", "webpki-tokio" @@ -29291,19 +31042,19 @@ "target": "webpki_roots" } ], - "aarch64-fuchsia": [ + "aarch64-linux-android": [ { "id": "webpki-roots 0.26.1", "target": "webpki_roots" } ], - "aarch64-linux-android": [ + "aarch64-pc-windows-msvc": [ { "id": "webpki-roots 0.26.1", "target": "webpki_roots" } ], - "aarch64-pc-windows-msvc": [ + "aarch64-unknown-fuchsia": [ { "id": "webpki-roots 0.26.1", "target": "webpki_roots" @@ -29423,25 +31174,25 @@ "target": "webpki_roots" } ], - "x86_64-fuchsia": [ + "x86_64-linux-android": [ { "id": "webpki-roots 0.26.1", "target": "webpki_roots" } ], - "x86_64-linux-android": [ + "x86_64-pc-windows-msvc": [ { "id": "webpki-roots 0.26.1", "target": "webpki_roots" } ], - "x86_64-pc-windows-msvc": [ + "x86_64-unknown-freebsd": [ { "id": "webpki-roots 0.26.1", "target": "webpki_roots" } ], - "x86_64-unknown-freebsd": [ + "x86_64-unknown-fuchsia": [ { "id": "webpki-roots 0.26.1", "target": "webpki_roots" @@ -29864,7 +31615,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" } ], @@ -29878,14 +31629,14 @@ ], "license_file": "LICENSE-APACHE" }, - "ic-agent 0.37.1": { + "ic-agent 0.39.2": { "name": "ic-agent", - "version": "0.37.1", + "version": "0.39.2", "package_url": "https://github.com/dfinity/agent-rs", "repository": { "Http": { - "url": "https://static.crates.io/crates/ic-agent/0.37.1/download", - "sha256": "3fd3fdf5e5c4f4a9fe5ca612f0febd22dcb161d2f2b75b0142326732be5e4978" + "url": "https://static.crates.io/crates/ic-agent/0.39.2/download", + "sha256": "1ba408987ca48fc3eee6a613e760d076a9046cccbbb5ba29efbada339ab28ed9" } }, "targets": [ @@ -29910,19 +31661,29 @@ "crate_features": { "common": [ "default", - "experimental_sync_call", - "hyper", "pem", - "reqwest" + "ring" ], "selects": {} }, "deps": { "common": [ + { + "id": "arc-swap 1.7.1", + "target": "arc_swap" + }, + { + "id": "async-channel 1.9.0", + "target": "async_channel" + }, { "id": "async-lock 3.3.0", "target": "async_lock" }, + { + "id": "async-watch 0.3.1", + "target": "async_watch" + }, { "id": "backoff 0.4.0", "target": "backoff" @@ -29932,13 +31693,25 @@ "target": "cached" }, { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, + { + "id": "der 0.7.8", + "target": "der" + }, + { + "id": "ecdsa 0.16.9", + "target": "ecdsa" + }, { "id": "ed25519-consensus 2.1.0", "target": "ed25519_consensus" }, + { + "id": "elliptic-curve 0.13.8", + "target": "elliptic_curve" + }, { "id": "futures-util 0.3.31", "target": "futures_util" @@ -29956,15 +31729,11 @@ "target": "http_body" }, { - "id": "hyper 1.5.1", - "target": "hyper" - }, - { - "id": "ic-certification 2.6.0", + "id": "ic-certification 3.0.2", "target": "ic_certification" }, { - "id": "ic-transport-types 0.37.1", + "id": "ic-transport-types 0.39.2", "target": "ic_transport_types" }, { @@ -30012,7 +31781,7 @@ "target": "sec1" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -30032,732 +31801,32 @@ "target": "simple_asn1" }, { - "id": "thiserror 1.0.68", + "id": "stop-token 0.7.0", + "target": "stop_token" + }, + { + "id": "thiserror 2.0.3", "target": "thiserror" }, { "id": "time 0.3.36", "target": "time" }, + { + "id": "tower-service 0.3.3", + "target": "tower_service" + }, { "id": "url 2.5.3", "target": "url" } ], "selects": { - "aarch64-apple-darwin": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "aarch64-apple-ios": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "aarch64-apple-ios-sim": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "aarch64-fuchsia": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "aarch64-linux-android": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "aarch64-pc-windows-msvc": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "aarch64-unknown-linux-gnu": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "aarch64-unknown-nixos-gnu": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "aarch64-unknown-nto-qnx710": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "arm-unknown-linux-gnueabi": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "armv7-linux-androideabi": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "armv7-unknown-linux-gnueabi": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], "cfg(not(target_family = \"wasm\"))": [ - { - "id": "rustls-webpki 0.102.8", - "target": "webpki" - }, { "id": "tokio 1.42.0", "target": "tokio" } - ], - "i686-apple-darwin": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "i686-linux-android": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "i686-pc-windows-msvc": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "i686-unknown-freebsd": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "i686-unknown-linux-gnu": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "powerpc-unknown-linux-gnu": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "riscv32imc-unknown-none-elf": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "riscv64gc-unknown-none-elf": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "s390x-unknown-linux-gnu": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "thumbv7em-none-eabi": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "thumbv8m.main-none-eabi": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "x86_64-apple-darwin": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "x86_64-apple-ios": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "x86_64-fuchsia": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "x86_64-linux-android": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "x86_64-pc-windows-msvc": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "x86_64-unknown-freebsd": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "x86_64-unknown-linux-gnu": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "x86_64-unknown-nixos-gnu": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "x86_64-unknown-none": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } ] } }, @@ -30765,13 +31834,17 @@ "proc_macro_deps": { "common": [ { - "id": "serde_repr 0.1.16", + "id": "async-trait 0.1.83", + "target": "async_trait" + }, + { + "id": "serde_repr 0.1.19", "target": "serde_repr" } ], "selects": {} }, - "version": "0.37.1" + "version": "0.39.2" }, "license": "Apache-2.0", "license_ids": [ @@ -30953,7 +32026,7 @@ "target": "scopeguard" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -31091,11 +32164,11 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -31146,7 +32219,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -31161,14 +32234,16 @@ ], "license_file": "LICENSE" }, - "ic-canister-sig-creation 1.0.1": { + "ic-canister-sig-creation 1.1.0": { "name": "ic-canister-sig-creation", - "version": "1.0.1", + "version": "1.1.0", "package_url": "https://github.com/dfinity/ic-canister-sig-creation", "repository": { - "Http": { - "url": "https://static.crates.io/crates/ic-canister-sig-creation/1.0.1/download", - "sha256": "5d1fc58d747480967a25810d8a90d460e7e9ea4c669ab0286541a148736513f9" + "Git": { + "remote": "https://github.com/dfinity/ic-canister-sig-creation", + "commitish": { + "Rev": "7f9e931954637526295269155881207f6c832d6d" + } } }, "targets": [ @@ -31193,7 +32268,7 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -31201,15 +32276,15 @@ "target": "hex" }, { - "id": "ic-cdk 0.14.1", + "id": "ic-cdk 0.17.0", "target": "ic_cdk" }, { - "id": "ic-certification 2.6.0", + "id": "ic-certification 3.0.2", "target": "ic_certification" }, { - "id": "ic-representation-independent-hash 2.6.0", + "id": "ic-representation-independent-hash 3.0.2", "target": "ic_representation_independent_hash" }, { @@ -31217,7 +32292,7 @@ "target": "lazy_static" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -31233,14 +32308,14 @@ "target": "sha2" }, { - "id": "thiserror 1.0.68", + "id": "thiserror 2.0.3", "target": "thiserror" } ], "selects": {} }, "edition": "2021", - "version": "1.0.1" + "version": "1.1.0" }, "license": "Apache-2.0", "license_ids": [ @@ -31248,14 +32323,14 @@ ], "license_file": "LICENSE" }, - "ic-cbor 2.6.0": { + "ic-cbor 3.0.2": { "name": "ic-cbor", - "version": "2.6.0", + "version": "3.0.2", "package_url": "https://github.com/dfinity/response-verification", "repository": { "Http": { - "url": "https://static.crates.io/crates/ic-cbor/2.6.0/download", - "sha256": "02b0e48b4166c891e79d624f3a184b4a7c145d307576872d9a46dedb8c73ea8f" + "url": "https://static.crates.io/crates/ic-cbor/3.0.2/download", + "sha256": "5500d6e85bc2ca8ea8aaed16cb84811882589244831a2fd8eefe02e90b3006c6" } }, "targets": [ @@ -31280,11 +32355,11 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { - "id": "ic-certification 2.6.0", + "id": "ic-certification 3.0.2", "target": "ic_certification" }, { @@ -31303,7 +32378,7 @@ "selects": {} }, "edition": "2021", - "version": "2.6.0" + "version": "3.0.2" }, "license": "Apache-2.0", "license_ids": [ @@ -31343,7 +32418,7 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -31351,7 +32426,7 @@ "target": "ic0" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -31379,14 +32454,14 @@ ], "license_file": "LICENSE" }, - "ic-cdk 0.14.1": { + "ic-cdk 0.16.0": { "name": "ic-cdk", - "version": "0.14.1", + "version": "0.16.0", "package_url": "https://github.com/dfinity/cdk-rs", "repository": { "Http": { - "url": "https://static.crates.io/crates/ic-cdk/0.14.1/download", - "sha256": "9cff1a3c3db565e3384c9c9d6d676b0a3f89a0886f4f787294d9c946d844369f" + "url": "https://static.crates.io/crates/ic-cdk/0.16.0/download", + "sha256": "dd8ecacd682fa05a985253592963306cb9799622d7b1cce4b1edb89c6ec85be1" } }, "targets": [ @@ -31411,7 +32486,7 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -31419,7 +32494,7 @@ "target": "ic0" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -31433,13 +32508,13 @@ "proc_macro_deps": { "common": [ { - "id": "ic-cdk-macros 0.14.0", + "id": "ic-cdk-macros 0.16.0", "target": "ic_cdk_macros" } ], "selects": {} }, - "version": "0.14.1" + "version": "0.16.0" }, "license": "Apache-2.0", "license_ids": [ @@ -31447,14 +32522,14 @@ ], "license_file": "LICENSE" }, - "ic-cdk 0.16.0": { + "ic-cdk 0.17.0": { "name": "ic-cdk", - "version": "0.16.0", + "version": "0.17.0", "package_url": "https://github.com/dfinity/cdk-rs", "repository": { "Http": { - "url": "https://static.crates.io/crates/ic-cdk/0.16.0/download", - "sha256": "dd8ecacd682fa05a985253592963306cb9799622d7b1cce4b1edb89c6ec85be1" + "url": "https://static.crates.io/crates/ic-cdk/0.17.0/download", + "sha256": "b2abdf9341da9f9f6b451a40609cb69645a05a8e9eb7784c16209f16f2c0f76f" } }, "targets": [ @@ -31479,7 +32554,7 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -31487,7 +32562,7 @@ "target": "ic0" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -31501,13 +32576,13 @@ "proc_macro_deps": { "common": [ { - "id": "ic-cdk-macros 0.16.0", + "id": "ic-cdk-macros 0.17.0", "target": "ic_cdk_macros" } ], "selects": {} }, - "version": "0.16.0" + "version": "0.17.0" }, "license": "Apache-2.0", "license_ids": [ @@ -31515,14 +32590,17 @@ ], "license_file": "LICENSE" }, - "ic-cdk 0.17.0": { + "ic-cdk 0.18.0-alpha.1": { "name": "ic-cdk", - "version": "0.17.0", + "version": "0.18.0-alpha.1", "package_url": "https://github.com/dfinity/cdk-rs", "repository": { - "Http": { - "url": "https://static.crates.io/crates/ic-cdk/0.17.0/download", - "sha256": "b2abdf9341da9f9f6b451a40609cb69645a05a8e9eb7784c16209f16f2c0f76f" + "Git": { + "remote": "https://github.com/dfinity/cdk-rs.git", + "commitish": { + "Rev": "4e287ce51636b0e70768c193da38d2fc5324ea15" + }, + "strip_prefix": "ic-cdk" } }, "targets": [ @@ -31547,20 +32625,24 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { - "id": "ic0 0.23.0", + "id": "ic0 0.24.0-alpha.1", "target": "ic0" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { "id": "serde_bytes 0.11.15", "target": "serde_bytes" + }, + { + "id": "thiserror 2.0.3", + "target": "thiserror" } ], "selects": {} @@ -31569,13 +32651,13 @@ "proc_macro_deps": { "common": [ { - "id": "ic-cdk-macros 0.17.0", + "id": "ic-cdk-macros 0.18.0-alpha.1", "target": "ic_cdk_macros" } ], "selects": {} }, - "version": "0.17.0" + "version": "0.18.0-alpha.1" }, "license": "Apache-2.0", "license_ids": [ @@ -31615,7 +32697,7 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -31627,7 +32709,7 @@ "target": "quote" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -31682,7 +32764,7 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -31694,7 +32776,7 @@ "target": "quote" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -31717,14 +32799,14 @@ ], "license_file": "LICENSE" }, - "ic-cdk-macros 0.14.0": { + "ic-cdk-macros 0.16.0": { "name": "ic-cdk-macros", - "version": "0.14.0", + "version": "0.16.0", "package_url": "https://github.com/dfinity/cdk-rs", "repository": { "Http": { - "url": "https://static.crates.io/crates/ic-cdk-macros/0.14.0/download", - "sha256": "01dc6bc425ec048d6ac4137c7c0f2cfbd6f8b0be8efc568feae2b265f566117c" + "url": "https://static.crates.io/crates/ic-cdk-macros/0.16.0/download", + "sha256": "0d4d857135deef20cc7ea8f3869a30cd9cfeb1392b3a81043790b2cd82adc3e0" } }, "targets": [ @@ -31749,7 +32831,7 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -31761,7 +32843,7 @@ "target": "quote" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -31776,7 +32858,7 @@ "selects": {} }, "edition": "2021", - "version": "0.14.0" + "version": "0.16.0" }, "license": "Apache-2.0", "license_ids": [ @@ -31784,14 +32866,14 @@ ], "license_file": "LICENSE" }, - "ic-cdk-macros 0.16.0": { + "ic-cdk-macros 0.17.0": { "name": "ic-cdk-macros", - "version": "0.16.0", + "version": "0.17.0", "package_url": "https://github.com/dfinity/cdk-rs", "repository": { "Http": { - "url": "https://static.crates.io/crates/ic-cdk-macros/0.16.0/download", - "sha256": "0d4d857135deef20cc7ea8f3869a30cd9cfeb1392b3a81043790b2cd82adc3e0" + "url": "https://static.crates.io/crates/ic-cdk-macros/0.17.0/download", + "sha256": "b8df41980e95dead28735ab0f748c75477b0c5eab37a09a5641c78ec406a1db0" } }, "targets": [ @@ -31816,7 +32898,7 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -31828,7 +32910,7 @@ "target": "quote" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -31843,7 +32925,7 @@ "selects": {} }, "edition": "2021", - "version": "0.16.0" + "version": "0.17.0" }, "license": "Apache-2.0", "license_ids": [ @@ -31851,14 +32933,17 @@ ], "license_file": "LICENSE" }, - "ic-cdk-macros 0.17.0": { + "ic-cdk-macros 0.18.0-alpha.1": { "name": "ic-cdk-macros", - "version": "0.17.0", + "version": "0.18.0-alpha.1", "package_url": "https://github.com/dfinity/cdk-rs", "repository": { - "Http": { - "url": "https://static.crates.io/crates/ic-cdk-macros/0.17.0/download", - "sha256": "b8df41980e95dead28735ab0f748c75477b0c5eab37a09a5641c78ec406a1db0" + "Git": { + "remote": "https://github.com/dfinity/cdk-rs.git", + "commitish": { + "Rev": "4e287ce51636b0e70768c193da38d2fc5324ea15" + }, + "strip_prefix": "ic-cdk-macros" } }, "targets": [ @@ -31883,7 +32968,7 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -31895,7 +32980,7 @@ "target": "quote" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -31910,7 +32995,7 @@ "selects": {} }, "edition": "2021", - "version": "0.17.0" + "version": "0.18.0-alpha.1" }, "license": "Apache-2.0", "license_ids": [ @@ -31962,7 +33047,7 @@ "target": "ic0" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -31985,14 +33070,14 @@ ], "license_file": "LICENSE" }, - "ic-certificate-verification 2.6.0": { + "ic-certificate-verification 3.0.2": { "name": "ic-certificate-verification", - "version": "2.6.0", + "version": "3.0.2", "package_url": "https://github.com/dfinity/response-verification", "repository": { "Http": { - "url": "https://static.crates.io/crates/ic-certificate-verification/2.6.0/download", - "sha256": "586e09b06a93d930f6a33f5f909bb11d2e4a06be3635dd5da1eb0e6554b7dae4" + "url": "https://static.crates.io/crates/ic-certificate-verification/3.0.2/download", + "sha256": "2daec653eb7895b5549cdf58d871985711c03cf5e389f7800a970f4f42dc0897" } }, "targets": [ @@ -32017,19 +33102,19 @@ "deps": { "common": [ { - "id": "cached 0.47.0", + "id": "cached 0.54.0", "target": "cached" }, { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { - "id": "ic-cbor 2.6.0", + "id": "ic-cbor 3.0.2", "target": "ic_cbor" }, { - "id": "ic-certification 2.6.0", + "id": "ic-certification 3.0.2", "target": "ic_certification" }, { @@ -32064,7 +33149,7 @@ "selects": {} }, "edition": "2021", - "version": "2.6.0" + "version": "3.0.2" }, "license": "Apache-2.0", "license_ids": [ @@ -32104,8 +33189,74 @@ "crate_features": { "common": [ "default", - "serde", - "serde_bytes" + "serde", + "serde_bytes" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "hex 0.4.3", + "target": "hex" + }, + { + "id": "serde 1.0.217", + "target": "serde" + }, + { + "id": "serde_bytes 0.11.15", + "target": "serde_bytes" + }, + { + "id": "sha2 0.10.8", + "target": "sha2" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "2.6.0" + }, + "license": "Apache-2.0", + "license_ids": [ + "Apache-2.0" + ], + "license_file": "LICENSE" + }, + "ic-certification 3.0.2": { + "name": "ic-certification", + "version": "3.0.2", + "package_url": "https://github.com/dfinity/response-verification", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/ic-certification/3.0.2/download", + "sha256": "9eae40f26fcac9c141cad54d9aa5f423efffde78ac371057c53d275ebbcad443" + } + }, + "targets": [ + { + "Library": { + "crate_name": "ic_certification", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "ic_certification", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "default", + "serde" ], "selects": {} }, @@ -32116,7 +33267,7 @@ "target": "hex" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -32131,7 +33282,7 @@ "selects": {} }, "edition": "2021", - "version": "2.6.0" + "version": "3.0.2" }, "license": "Apache-2.0", "license_ids": [ @@ -32171,7 +33322,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -32194,14 +33345,14 @@ ], "license_file": "LICENSE" }, - "ic-http-certification 2.6.0": { + "ic-http-certification 3.0.2": { "name": "ic-http-certification", - "version": "2.6.0", + "version": "3.0.2", "package_url": "https://github.com/dfinity/response-verification", "repository": { "Http": { - "url": "https://static.crates.io/crates/ic-http-certification/2.6.0/download", - "sha256": "ff0b97e949845039149dc5e7ea6a7c12ee4333bb402e37bc507904643c7b3e41" + "url": "https://static.crates.io/crates/ic-http-certification/3.0.2/download", + "sha256": "479941fca8e68c2267cddf686d34ed6fb491168667ff259c08a3d65d28bd26d2" } }, "targets": [ @@ -32226,25 +33377,33 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "base64 0.22.1", + "target": "base64" + }, + { + "id": "candid 0.10.13", "target": "candid" }, { - "id": "http 0.2.12", + "id": "http 1.2.0", "target": "http" }, { - "id": "ic-certification 2.6.0", + "id": "ic-certification 3.0.2", "target": "ic_certification" }, { - "id": "ic-representation-independent-hash 2.6.0", + "id": "ic-representation-independent-hash 3.0.2", "target": "ic_representation_independent_hash" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, + { + "id": "serde_cbor 0.11.2", + "target": "serde_cbor" + }, { "id": "thiserror 1.0.68", "target": "thiserror" @@ -32257,7 +33416,7 @@ "selects": {} }, "edition": "2021", - "version": "2.6.0" + "version": "3.0.2" }, "license": "Apache-2.0", "license_ids": [ @@ -32265,17 +33424,14 @@ ], "license_file": "LICENSE" }, - "ic-http-gateway 0.0.0": { + "ic-http-gateway 0.1.0": { "name": "ic-http-gateway", - "version": "0.0.0", + "version": "0.1.0", "package_url": "https://github.com/dfinity/http-gateway", "repository": { - "Git": { - "remote": "https://github.com/dfinity/http-gateway", - "commitish": { - "Rev": "3be26b5a2c71bf56e05b910951c1935a1ac550c4" - }, - "strip_prefix": "packages/ic-http-gateway" + "Http": { + "url": "https://static.crates.io/crates/ic-http-gateway/0.1.0/download", + "sha256": "8e8b30a8ff19af1a7dc64b1dbe1a38f1b60c7eea566e2049f755ce3bace0e630" } }, "targets": [ @@ -32304,7 +33460,7 @@ "target": "bytes" }, { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -32324,19 +33480,19 @@ "target": "http_body_util" }, { - "id": "ic-agent 0.37.1", + "id": "ic-agent 0.39.2", "target": "ic_agent" }, { - "id": "ic-http-certification 2.6.0", + "id": "ic-http-certification 3.0.2", "target": "ic_http_certification" }, { - "id": "ic-response-verification 2.6.0", + "id": "ic-response-verification 3.0.2", "target": "ic_response_verification" }, { - "id": "ic-utils 0.37.0", + "id": "ic-utils 0.39.2", "target": "ic_utils" }, { @@ -32347,7 +33503,7 @@ "selects": {} }, "edition": "2021", - "version": "0.0.0" + "version": "0.1.0" }, "license": "Apache-2.0", "license_ids": [ @@ -32355,6 +33511,73 @@ ], "license_file": "LICENSE" }, + "ic-identity-hsm 0.39.2": { + "name": "ic-identity-hsm", + "version": "0.39.2", + "package_url": "https://github.com/dfinity/agent-rs", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/ic-identity-hsm/0.39.2/download", + "sha256": "0ebb94d7cb5be09bed47655008f0c2968a3a3253dcf680297f3e8475e4b317c4" + } + }, + "targets": [ + { + "Library": { + "crate_name": "ic_identity_hsm", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "ic_identity_hsm", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "hex 0.4.3", + "target": "hex" + }, + { + "id": "ic-agent 0.39.2", + "target": "ic_agent" + }, + { + "id": "pkcs11 0.5.0", + "target": "pkcs11" + }, + { + "id": "sha2 0.10.8", + "target": "sha2" + }, + { + "id": "simple_asn1 0.6.2", + "target": "simple_asn1" + }, + { + "id": "thiserror 2.0.3", + "target": "thiserror" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.39.2" + }, + "license": "Apache-2.0", + "license_ids": [ + "Apache-2.0" + ], + "license_file": null + }, "ic-metrics-encoder 1.1.1": { "name": "ic-metrics-encoder", "version": "1.1.1", @@ -32393,14 +33616,14 @@ ], "license_file": "LICENSE" }, - "ic-representation-independent-hash 2.6.0": { + "ic-representation-independent-hash 3.0.2": { "name": "ic-representation-independent-hash", - "version": "2.6.0", + "version": "3.0.2", "package_url": "https://github.com/dfinity/response-verification", "repository": { "Http": { - "url": "https://static.crates.io/crates/ic-representation-independent-hash/2.6.0/download", - "sha256": "08ae59483e377cd9aad94ec339ed1d2583b0d5929cab989328dac2d853b2f570" + "url": "https://static.crates.io/crates/ic-representation-independent-hash/3.0.2/download", + "sha256": "3643f12824280580d31e47d380f1be23abee29944a1430c3ed22b164ac8e68db" } }, "targets": [ @@ -32436,7 +33659,7 @@ "selects": {} }, "edition": "2021", - "version": "2.6.0" + "version": "3.0.2" }, "license": "Apache-2.0", "license_ids": [ @@ -32444,14 +33667,14 @@ ], "license_file": null }, - "ic-response-verification 2.6.0": { + "ic-response-verification 3.0.2": { "name": "ic-response-verification", - "version": "2.6.0", + "version": "3.0.2", "package_url": "https://github.com/dfinity/response-verification", "repository": { "Http": { - "url": "https://static.crates.io/crates/ic-response-verification/2.6.0/download", - "sha256": "2bef02ef84189d61a7d39889b7e9a3ae212d45c3df293513f7b2568027fd08a8" + "url": "https://static.crates.io/crates/ic-response-verification/3.0.2/download", + "sha256": "2b97514fada84797baf61a6a29f1c71695798c2628cb6013d97a5dd6ecc26df7" } }, "targets": [ @@ -32476,11 +33699,11 @@ "deps": { "common": [ { - "id": "base64 0.21.4", + "id": "base64 0.22.1", "target": "base64" }, { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -32492,27 +33715,27 @@ "target": "hex" }, { - "id": "http 0.2.12", + "id": "http 1.2.0", "target": "http" }, { - "id": "ic-cbor 2.6.0", + "id": "ic-cbor 3.0.2", "target": "ic_cbor" }, { - "id": "ic-certificate-verification 2.6.0", + "id": "ic-certificate-verification 3.0.2", "target": "ic_certificate_verification" }, { - "id": "ic-certification 2.6.0", + "id": "ic-certification 3.0.2", "target": "ic_certification" }, { - "id": "ic-http-certification 2.6.0", + "id": "ic-http-certification 3.0.2", "target": "ic_http_certification" }, { - "id": "ic-representation-independent-hash 2.6.0", + "id": "ic-representation-independent-hash 3.0.2", "target": "ic_representation_independent_hash" }, { @@ -32543,7 +33766,7 @@ "selects": {} }, "edition": "2021", - "version": "2.6.0" + "version": "3.0.2" }, "license": "Apache-2.0", "license_ids": [ @@ -32698,7 +33921,7 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -32706,7 +33929,7 @@ "target": "ciborium" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -32757,7 +33980,7 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -32773,7 +33996,7 @@ "target": "leb128" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -32795,7 +34018,7 @@ "proc_macro_deps": { "common": [ { - "id": "serde_repr 0.1.16", + "id": "serde_repr 0.1.19", "target": "serde_repr" } ], @@ -32809,14 +34032,102 @@ ], "license_file": null }, - "ic-utils 0.37.0": { + "ic-transport-types 0.39.2": { + "name": "ic-transport-types", + "version": "0.39.2", + "package_url": "https://github.com/dfinity/agent-rs", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/ic-transport-types/0.39.2/download", + "sha256": "21e2418868dd5857d2a5bac3f1cb6de1aecf2316d380997ef842aec3d8a79d4e" + } + }, + "targets": [ + { + "Library": { + "crate_name": "ic_transport_types", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "ic_transport_types", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "candid 0.10.13", + "target": "candid" + }, + { + "id": "hex 0.4.3", + "target": "hex" + }, + { + "id": "ic-certification 3.0.2", + "target": "ic_certification" + }, + { + "id": "leb128 0.2.5", + "target": "leb128" + }, + { + "id": "serde 1.0.217", + "target": "serde" + }, + { + "id": "serde_bytes 0.11.15", + "target": "serde_bytes" + }, + { + "id": "serde_cbor 0.11.2", + "target": "serde_cbor" + }, + { + "id": "sha2 0.10.8", + "target": "sha2" + }, + { + "id": "thiserror 2.0.3", + "target": "thiserror" + } + ], + "selects": {} + }, + "edition": "2021", + "proc_macro_deps": { + "common": [ + { + "id": "serde_repr 0.1.19", + "target": "serde_repr" + } + ], + "selects": {} + }, + "version": "0.39.2" + }, + "license": "Apache-2.0", + "license_ids": [ + "Apache-2.0" + ], + "license_file": null + }, + "ic-utils 0.39.2": { "name": "ic-utils", - "version": "0.37.0", + "version": "0.39.2", "package_url": "https://github.com/dfinity/agent-rs", "repository": { "Http": { - "url": "https://static.crates.io/crates/ic-utils/0.37.0/download", - "sha256": "2fa832296800758c9c921dd1704985ded6b3e6fbc3aee409727eb1f00d69a595" + "url": "https://static.crates.io/crates/ic-utils/0.39.2/download", + "sha256": "e1fb9c35ef4976a71d37f3ebf73ee43bb52b360be60d91d3a77f74fbc875dda4" } }, "targets": [ @@ -32847,7 +34158,7 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -32855,7 +34166,7 @@ "target": "futures_util" }, { - "id": "ic-agent 0.37.1", + "id": "ic-agent 0.39.2", "target": "ic_agent" }, { @@ -32867,7 +34178,7 @@ "target": "semver" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -32883,7 +34194,7 @@ "target": "strum" }, { - "id": "thiserror 1.0.68", + "id": "thiserror 2.0.3", "target": "thiserror" }, { @@ -32911,7 +34222,7 @@ ], "selects": {} }, - "version": "0.37.0" + "version": "0.39.2" }, "license": "Apache-2.0", "license_ids": [ @@ -33125,7 +34436,7 @@ "target": "anyhow" }, { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -33141,7 +34452,7 @@ "target": "rustc_demangle" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -33200,11 +34511,11 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -33333,6 +34644,47 @@ ], "license_file": "LICENSE" }, + "ic0 0.24.0-alpha.1": { + "name": "ic0", + "version": "0.24.0-alpha.1", + "package_url": "https://github.com/dfinity/cdk-rs", + "repository": { + "Git": { + "remote": "https://github.com/dfinity/cdk-rs.git", + "commitish": { + "Rev": "4e287ce51636b0e70768c193da38d2fc5324ea15" + }, + "strip_prefix": "ic0" + } + }, + "targets": [ + { + "Library": { + "crate_name": "ic0", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "ic0", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "edition": "2021", + "version": "0.24.0-alpha.1" + }, + "license": "Apache-2.0", + "license_ids": [ + "Apache-2.0" + ], + "license_file": "LICENSE" + }, "ic_bls12_381 0.10.0": { "name": "ic_bls12_381", "version": "0.10.0", @@ -33479,7 +34831,7 @@ "target": "data_encoding" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -33541,11 +34893,11 @@ "target": "anyhow" }, { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -33612,7 +34964,7 @@ "target": "anyhow" }, { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -33694,7 +35046,9 @@ "version": "1.5.0" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "icu_locid 1.5.0": { @@ -33766,7 +35120,9 @@ "version": "1.5.0" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "icu_locid_transform 1.5.0": { @@ -33842,7 +35198,9 @@ "version": "1.5.0" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "icu_locid_transform_data 1.5.0": { @@ -33878,7 +35236,9 @@ "version": "1.5.0" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "icu_normalizer 1.5.0": { @@ -33971,7 +35331,9 @@ "version": "1.5.0" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "icu_normalizer_data 1.5.0": { @@ -34007,7 +35369,9 @@ "version": "1.5.0" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "icu_properties 1.5.1": { @@ -34088,7 +35452,9 @@ "version": "1.5.1" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "icu_properties_data 1.5.0": { @@ -34124,7 +35490,9 @@ "version": "1.5.0" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "icu_provider 1.5.0": { @@ -34212,7 +35580,9 @@ "version": "1.5.0" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "icu_provider_macros 1.5.0": { @@ -34265,7 +35635,9 @@ "version": "1.5.0" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "id-arena 2.2.1": { @@ -34817,7 +36189,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -35069,7 +36441,7 @@ "target": "hashbrown" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -35647,7 +37019,7 @@ "target": "rustls_pki_types" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -35717,7 +37089,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -35827,14 +37199,14 @@ ], "license_file": "LICENSE-APACHE" }, - "ipnet 2.8.0": { + "ipnet 2.10.1": { "name": "ipnet", - "version": "2.8.0", + "version": "2.10.1", "package_url": "https://github.com/krisprice/ipnet", "repository": { "Http": { - "url": "https://static.crates.io/crates/ipnet/2.8.0/download", - "sha256": "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" + "url": "https://static.crates.io/crates/ipnet/2.10.1/download", + "sha256": "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" } }, "targets": [ @@ -35859,12 +37231,22 @@ "crate_features": { "common": [ "default", + "serde", "std" ], "selects": {} }, + "deps": { + "common": [ + { + "id": "serde 1.0.217", + "target": "serde" + } + ], + "selects": {} + }, "edition": "2018", - "version": "2.8.0" + "version": "2.10.1" }, "license": "MIT OR Apache-2.0", "license_ids": [ @@ -35912,7 +37294,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -36066,7 +37448,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -36428,14 +37810,14 @@ ], "license_file": "LICENSE-APACHE" }, - "jobserver 0.1.27": { + "jobserver 0.1.32": { "name": "jobserver", - "version": "0.1.27", - "package_url": "https://github.com/alexcrichton/jobserver-rs", + "version": "0.1.32", + "package_url": "https://github.com/rust-lang/jobserver-rs", "repository": { "Http": { - "url": "https://static.crates.io/crates/jobserver/0.1.27/download", - "sha256": "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" + "url": "https://static.crates.io/crates/jobserver/0.1.32/download", + "sha256": "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" } }, "targets": [ @@ -36468,10 +37850,10 @@ ] } }, - "edition": "2018", - "version": "0.1.27" + "edition": "2021", + "version": "0.1.32" }, - "license": "MIT/Apache-2.0", + "license": "MIT OR Apache-2.0", "license_ids": [ "Apache-2.0", "MIT" @@ -36566,7 +37948,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -36626,7 +38008,7 @@ "target": "pest" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -36724,16 +38106,14 @@ "license_ids": [], "license_file": "LICENSE" }, - "jsonrpc 0.12.1": { + "jsonrpc 0.13.0": { "name": "jsonrpc", - "version": "0.12.1", + "version": "0.13.0", "package_url": "https://github.com/apoelstra/rust-jsonrpc/", "repository": { - "Git": { - "remote": "https://github.com/apoelstra/rust-jsonrpc", - "commitish": { - "Rev": "e42044d8e0896317488dfbd65eb5563d76e937fe" - } + "Http": { + "url": "https://static.crates.io/crates/jsonrpc/0.13.0/download", + "sha256": "fd8d6b3f301ba426b30feca834a2a18d48d5b54e5065496b5c1b05537bee3639" } }, "targets": [ @@ -36771,7 +38151,7 @@ "target": "base64" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -36782,7 +38162,7 @@ "selects": {} }, "edition": "2018", - "version": "0.12.1" + "version": "0.13.0" }, "license": "CC0-1.0", "license_ids": [ @@ -36790,14 +38170,14 @@ ], "license_file": "LICENSE" }, - "jsonrpc 0.13.0": { + "jsonrpc 0.18.0": { "name": "jsonrpc", - "version": "0.13.0", + "version": "0.18.0", "package_url": "https://github.com/apoelstra/rust-jsonrpc/", "repository": { "Http": { - "url": "https://static.crates.io/crates/jsonrpc/0.13.0/download", - "sha256": "fd8d6b3f301ba426b30feca834a2a18d48d5b54e5065496b5c1b05537bee3639" + "url": "https://static.crates.io/crates/jsonrpc/0.18.0/download", + "sha256": "3662a38d341d77efecb73caf01420cfa5aa63c0253fd7bc05289ef9f6616e1bf" } }, "targets": [ @@ -36823,6 +38203,8 @@ "common": [ "base64", "default", + "minreq", + "minreq_http", "simple_http", "simple_tcp" ], @@ -36835,7 +38217,11 @@ "target": "base64" }, { - "id": "serde 1.0.214", + "id": "minreq 2.13.0", + "target": "minreq" + }, + { + "id": "serde 1.0.217", "target": "serde" }, { @@ -36846,7 +38232,7 @@ "selects": {} }, "edition": "2018", - "version": "0.13.0" + "version": "0.18.0" }, "license": "CC0-1.0", "license_ids": [ @@ -36854,6 +38240,96 @@ ], "license_file": "LICENSE" }, + "k256 0.11.6": { + "name": "k256", + "version": "0.11.6", + "package_url": "https://github.com/RustCrypto/elliptic-curves/tree/master/k256", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/k256/0.11.6/download", + "sha256": "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" + } + }, + "targets": [ + { + "Library": { + "crate_name": "k256", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "k256", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "arithmetic", + "default", + "digest", + "ecdsa", + "ecdsa-core", + "keccak256", + "pem", + "pkcs8", + "schnorr", + "sha2", + "sha256", + "sha3", + "std" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "cfg-if 1.0.0", + "target": "cfg_if" + }, + { + "id": "ecdsa 0.14.8", + "target": "ecdsa", + "alias": "ecdsa_core" + }, + { + "id": "elliptic-curve 0.12.3", + "target": "elliptic_curve" + }, + { + "id": "sha2 0.10.8", + "target": "sha2" + }, + { + "id": "sha3 0.10.8", + "target": "sha3" + } + ], + "selects": {} + }, + "edition": "2021", + "rustc_flags": { + "common": [ + "-C", + "opt-level=3" + ], + "selects": {} + }, + "version": "0.11.6" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "k256 0.13.4": { "name": "k256", "version": "0.13.4", @@ -37012,7 +38488,7 @@ "target": "build_script_build" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -37094,6 +38570,228 @@ ], "license_file": "LICENSE-APACHE" }, + "keyring 3.4.0": { + "name": "keyring", + "version": "3.4.0", + "package_url": "https://github.com/hwchen/keyring-rs.git", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/keyring/3.4.0/download", + "sha256": "bd3d701d3de5b9c4b0d9d077f8c2c66f0388d75e96932ebbb7cdff8713d7f7c6" + } + }, + "targets": [ + { + "Library": { + "crate_name": "keyring", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "keyring", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "apple-native", + "linux-native", + "sync-secret-service", + "vendored", + "windows-native" + ], + "selects": {} + }, + "deps": { + "common": [], + "selects": { + "aarch64-apple-darwin": [ + { + "id": "security-framework 3.0.1", + "target": "security_framework" + } + ], + "aarch64-apple-ios": [ + { + "id": "security-framework 3.0.1", + "target": "security_framework" + } + ], + "aarch64-apple-ios-sim": [ + { + "id": "security-framework 3.0.1", + "target": "security_framework" + } + ], + "aarch64-pc-windows-msvc": [ + { + "id": "byteorder 1.5.0", + "target": "byteorder" + }, + { + "id": "windows-sys 0.59.0", + "target": "windows_sys" + } + ], + "aarch64-unknown-linux-gnu": [ + { + "id": "dbus-secret-service 4.0.3", + "target": "dbus_secret_service" + }, + { + "id": "linux-keyutils 0.2.4", + "target": "linux_keyutils" + } + ], + "aarch64-unknown-nixos-gnu": [ + { + "id": "dbus-secret-service 4.0.3", + "target": "dbus_secret_service" + }, + { + "id": "linux-keyutils 0.2.4", + "target": "linux_keyutils" + } + ], + "arm-unknown-linux-gnueabi": [ + { + "id": "dbus-secret-service 4.0.3", + "target": "dbus_secret_service" + }, + { + "id": "linux-keyutils 0.2.4", + "target": "linux_keyutils" + } + ], + "armv7-unknown-linux-gnueabi": [ + { + "id": "dbus-secret-service 4.0.3", + "target": "dbus_secret_service" + }, + { + "id": "linux-keyutils 0.2.4", + "target": "linux_keyutils" + } + ], + "i686-apple-darwin": [ + { + "id": "security-framework 3.0.1", + "target": "security_framework" + } + ], + "i686-pc-windows-msvc": [ + { + "id": "byteorder 1.5.0", + "target": "byteorder" + }, + { + "id": "windows-sys 0.59.0", + "target": "windows_sys" + } + ], + "i686-unknown-freebsd": [ + { + "id": "dbus-secret-service 4.0.3", + "target": "dbus_secret_service" + } + ], + "i686-unknown-linux-gnu": [ + { + "id": "dbus-secret-service 4.0.3", + "target": "dbus_secret_service" + }, + { + "id": "linux-keyutils 0.2.4", + "target": "linux_keyutils" + } + ], + "powerpc-unknown-linux-gnu": [ + { + "id": "dbus-secret-service 4.0.3", + "target": "dbus_secret_service" + }, + { + "id": "linux-keyutils 0.2.4", + "target": "linux_keyutils" + } + ], + "s390x-unknown-linux-gnu": [ + { + "id": "dbus-secret-service 4.0.3", + "target": "dbus_secret_service" + }, + { + "id": "linux-keyutils 0.2.4", + "target": "linux_keyutils" + } + ], + "x86_64-apple-darwin": [ + { + "id": "security-framework 3.0.1", + "target": "security_framework" + } + ], + "x86_64-apple-ios": [ + { + "id": "security-framework 3.0.1", + "target": "security_framework" + } + ], + "x86_64-pc-windows-msvc": [ + { + "id": "byteorder 1.5.0", + "target": "byteorder" + }, + { + "id": "windows-sys 0.59.0", + "target": "windows_sys" + } + ], + "x86_64-unknown-freebsd": [ + { + "id": "dbus-secret-service 4.0.3", + "target": "dbus_secret_service" + } + ], + "x86_64-unknown-linux-gnu": [ + { + "id": "dbus-secret-service 4.0.3", + "target": "dbus_secret_service" + }, + { + "id": "linux-keyutils 0.2.4", + "target": "linux_keyutils" + } + ], + "x86_64-unknown-nixos-gnu": [ + { + "id": "dbus-secret-service 4.0.3", + "target": "dbus_secret_service" + }, + { + "id": "linux-keyutils 0.2.4", + "target": "linux_keyutils" + } + ] + } + }, + "edition": "2021", + "version": "3.4.0" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "kube 0.93.1": { "name": "kube", "version": "0.93.1", @@ -37302,7 +39000,7 @@ "target": "secrecy" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -37397,7 +39095,7 @@ "target": "k8s_openapi" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -38657,6 +40355,97 @@ ], "license_file": "LICENSE-APACHE" }, + "libdbus-sys 0.2.5": { + "name": "libdbus-sys", + "version": "0.2.5", + "package_url": "https://github.com/diwic/dbus-rs", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/libdbus-sys/0.2.5/download", + "sha256": "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72" + } + }, + "targets": [ + { + "Library": { + "crate_name": "libdbus_sys", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + }, + { + "BuildScript": { + "crate_name": "build_script_build", + "crate_root": "build.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "libdbus_sys", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "cc", + "default", + "pkg-config", + "vendored" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "libdbus-sys 0.2.5", + "target": "build_script_build" + } + ], + "selects": {} + }, + "edition": "2015", + "version": "0.2.5" + }, + "build_script_attrs": { + "compile_data_glob": [ + "**" + ], + "data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "cc 1.1.37", + "target": "cc" + }, + { + "id": "pkg-config 0.3.27", + "target": "pkg_config" + } + ], + "selects": {} + }, + "links": "dbus" + }, + "license": "Apache-2.0/MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "libflate 2.1.0": { "name": "libflate", "version": "2.1.0", @@ -38859,7 +40648,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" } ], @@ -38874,6 +40663,89 @@ ], "license_file": "LICENSE-APACHE" }, + "libloading 0.5.2": { + "name": "libloading", + "version": "0.5.2", + "package_url": "https://github.com/nagisa/rust_libloading/", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/libloading/0.5.2/download", + "sha256": "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" + } + }, + "targets": [ + { + "Library": { + "crate_name": "libloading", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + }, + { + "BuildScript": { + "crate_name": "build_script_build", + "crate_root": "build.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "libloading", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "libloading 0.5.2", + "target": "build_script_build" + } + ], + "selects": { + "cfg(windows)": [ + { + "id": "winapi 0.3.9", + "target": "winapi" + } + ] + } + }, + "edition": "2015", + "version": "0.5.2" + }, + "build_script_attrs": { + "compile_data_glob": [ + "**" + ], + "data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "cc 1.1.37", + "target": "cc" + } + ], + "selects": {} + } + }, + "license": "ISC", + "license_ids": [ + "ISC" + ], + "license_file": "LICENSE" + }, "libloading 0.7.4": { "name": "libloading", "version": "0.7.4", @@ -39141,7 +41013,7 @@ "target": "bindgen" }, { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" }, { @@ -39242,7 +41114,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" }, { @@ -39329,7 +41201,7 @@ "selects": { "cfg(unix)": [ { - "id": "openssl-sys 0.9.102", + "id": "openssl-sys 0.9.104", "target": "openssl_sys" } ] @@ -39348,7 +41220,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" }, { @@ -39375,7 +41247,7 @@ "selects": { "cfg(unix)": [ { - "id": "openssl-sys 0.9.102", + "id": "openssl-sys 0.9.104", "target": "openssl_sys" } ] @@ -39457,7 +41329,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" }, { @@ -39555,7 +41427,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" }, { @@ -39617,6 +41489,65 @@ ], "license_file": "LICENSE-APACHE" }, + "linux-keyutils 0.2.4": { + "name": "linux-keyutils", + "version": "0.2.4", + "package_url": "https://github.com/landhb/linux-keyutils", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/linux-keyutils/0.2.4/download", + "sha256": "761e49ec5fd8a5a463f9b84e877c373d888935b71c6be78f3767fe2ae6bed18e" + } + }, + "targets": [ + { + "Library": { + "crate_name": "linux_keyutils", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "linux_keyutils", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "default", + "std" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "bitflags 2.6.0", + "target": "bitflags" + }, + { + "id": "libc 0.2.158", + "target": "libc" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.2.4" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": null + }, "linux-raw-sys 0.4.13": { "name": "linux-raw-sys", "version": "0.4.13", @@ -39750,7 +41681,9 @@ "version": "0.7.3" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "little-loadshedder 0.2.0": { @@ -39961,7 +41894,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" }, { @@ -40266,8 +42199,6 @@ ], "crate_features": { "common": [ - "max_level_off", - "release_max_level_off", "std" ], "selects": {} @@ -40733,7 +42664,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" }, { @@ -41256,7 +43187,7 @@ "target": "memchr" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -41892,7 +43823,7 @@ "target": "rustls_pemfile" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -42308,6 +44239,93 @@ ], "license_file": "LICENSE" }, + "minreq 2.13.0": { + "name": "minreq", + "version": "2.13.0", + "package_url": "https://github.com/neonmoe/minreq", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/minreq/2.13.0/download", + "sha256": "36a8e50e917e18a37d500d27d40b7bc7d127e71c0c94fb2d83f43b4afd308390" + } + }, + "targets": [ + { + "Library": { + "crate_name": "minreq", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + }, + { + "BuildScript": { + "crate_name": "build_script_build", + "crate_root": "build.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "minreq", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "json-using-serde", + "serde", + "serde_json" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "log 0.4.20", + "target": "log" + }, + { + "id": "minreq 2.13.0", + "target": "build_script_build" + }, + { + "id": "serde 1.0.217", + "target": "serde" + }, + { + "id": "serde_json 1.0.132", + "target": "serde_json" + } + ], + "selects": {} + }, + "edition": "2018", + "version": "2.13.0" + }, + "build_script_attrs": { + "compile_data_glob": [ + "**" + ], + "data_glob": [ + "**" + ] + }, + "license": "ISC", + "license_ids": [ + "ISC" + ], + "license_file": null + }, "mio 0.8.10": { "name": "mio", "version": "0.8.10", @@ -42354,10 +44372,10 @@ "aarch64-apple-ios-sim": [ "os-ext" ], - "aarch64-fuchsia": [ + "aarch64-linux-android": [ "os-ext" ], - "aarch64-linux-android": [ + "aarch64-unknown-fuchsia": [ "os-ext" ], "aarch64-unknown-linux-gnu": [ @@ -42402,15 +44420,15 @@ "x86_64-apple-ios": [ "os-ext" ], - "x86_64-fuchsia": [ - "os-ext" - ], "x86_64-linux-android": [ "os-ext" ], "x86_64-unknown-freebsd": [ "os-ext" ], + "x86_64-unknown-fuchsia": [ + "os-ext" + ], "x86_64-unknown-linux-gnu": [ "os-ext" ], @@ -43421,7 +45439,7 @@ "target": "quote" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -43510,7 +45528,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -44154,6 +46172,82 @@ ], "license_file": null }, + "num 0.4.3": { + "name": "num", + "version": "0.4.3", + "package_url": "https://github.com/rust-num/num", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/num/0.4.3/download", + "sha256": "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" + } + }, + "targets": [ + { + "Library": { + "crate_name": "num", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "num", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "default", + "num-bigint", + "std" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "num-bigint 0.4.6", + "target": "num_bigint" + }, + { + "id": "num-complex 0.4.6", + "target": "num_complex" + }, + { + "id": "num-integer 0.1.46", + "target": "num_integer" + }, + { + "id": "num-iter 0.1.45", + "target": "num_iter" + }, + { + "id": "num-rational 0.4.2", + "target": "num_rational" + }, + { + "id": "num-traits 0.2.19", + "target": "num_traits" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.4.3" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "num-bigint 0.2.6": { "name": "num-bigint", "version": "0.2.6", @@ -44197,6 +46291,7 @@ ], "crate_features": { "common": [ + "default", "std" ], "selects": {} @@ -44293,7 +46388,7 @@ "target": "num_traits" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -44386,7 +46481,7 @@ "target": "num_integer" }, { - "id": "num-iter 0.1.43", + "id": "num-iter 0.1.45", "target": "num_iter" }, { @@ -44398,7 +46493,7 @@ "target": "rand" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -44430,6 +46525,60 @@ ], "license_file": "LICENSE-APACHE" }, + "num-complex 0.4.6": { + "name": "num-complex", + "version": "0.4.6", + "package_url": "https://github.com/rust-num/num-complex", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/num-complex/0.4.6/download", + "sha256": "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" + } + }, + "targets": [ + { + "Library": { + "crate_name": "num_complex", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "num_complex", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "std" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "num-traits 0.2.19", + "target": "num_traits" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.4.6" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "num-conv 0.1.0": { "name": "num-conv", "version": "0.1.0", @@ -44576,14 +46725,14 @@ ], "license_file": "LICENSE-APACHE" }, - "num-iter 0.1.43": { + "num-iter 0.1.45": { "name": "num-iter", - "version": "0.1.43", + "version": "0.1.45", "package_url": "https://github.com/rust-num/num-iter", "repository": { "Http": { - "url": "https://static.crates.io/crates/num-iter/0.1.43/download", - "sha256": "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" + "url": "https://static.crates.io/crates/num-iter/0.1.45/download", + "sha256": "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" } }, "targets": [ @@ -44598,18 +46747,6 @@ ] } } - }, - { - "BuildScript": { - "crate_name": "build_script_build", - "crate_root": "build.rs", - "srcs": { - "allow_empty": true, - "include": [ - "**/*.rs" - ] - } - } } ], "library_target_name": "num_iter", @@ -44617,16 +46754,61 @@ "compile_data_glob": [ "**" ], + "crate_features": { + "common": [], + "selects": { + "aarch64-unknown-linux-gnu": [ + "i128", + "std" + ], + "aarch64-unknown-nixos-gnu": [ + "i128", + "std" + ], + "arm-unknown-linux-gnueabi": [ + "i128", + "std" + ], + "armv7-unknown-linux-gnueabi": [ + "i128", + "std" + ], + "i686-unknown-freebsd": [ + "i128", + "std" + ], + "i686-unknown-linux-gnu": [ + "i128", + "std" + ], + "powerpc-unknown-linux-gnu": [ + "i128", + "std" + ], + "s390x-unknown-linux-gnu": [ + "i128", + "std" + ], + "x86_64-unknown-freebsd": [ + "i128", + "std" + ], + "x86_64-unknown-linux-gnu": [ + "i128", + "std" + ], + "x86_64-unknown-nixos-gnu": [ + "i128", + "std" + ] + } + }, "deps": { "common": [ { "id": "num-integer 0.1.46", "target": "num_integer" }, - { - "id": "num-iter 0.1.43", - "target": "build_script_build" - }, { "id": "num-traits 0.2.19", "target": "num_traits" @@ -44634,25 +46816,8 @@ ], "selects": {} }, - "edition": "2015", - "version": "0.1.43" - }, - "build_script_attrs": { - "compile_data_glob": [ - "**" - ], - "data_glob": [ - "**" - ], - "deps": { - "common": [ - { - "id": "autocfg 1.1.0", - "target": "autocfg" - } - ], - "selects": {} - } + "edition": "2018", + "version": "0.1.45" }, "license": "MIT OR Apache-2.0", "license_ids": [ @@ -44760,6 +46925,70 @@ ], "license_file": "LICENSE-APACHE" }, + "num-rational 0.4.2": { + "name": "num-rational", + "version": "0.4.2", + "package_url": "https://github.com/rust-num/num-rational", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/num-rational/0.4.2/download", + "sha256": "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" + } + }, + "targets": [ + { + "Library": { + "crate_name": "num_rational", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "num_rational", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "num-bigint", + "num-bigint-std", + "std" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "num-bigint 0.4.6", + "target": "num_bigint" + }, + { + "id": "num-integer 0.1.46", + "target": "num_integer" + }, + { + "id": "num-traits 0.2.19", + "target": "num_traits" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.4.2" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "num-traits 0.2.19": { "name": "num-traits", "version": "0.2.19", @@ -45185,14 +47414,14 @@ ], "license_file": "LICENSE-APACHE" }, - "object 0.36.1": { + "object 0.36.7": { "name": "object", - "version": "0.36.1", + "version": "0.36.7", "package_url": "https://github.com/gimli-rs/object", "repository": { "Http": { - "url": "https://static.crates.io/crates/object/0.36.1/download", - "sha256": "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" + "url": "https://static.crates.io/crates/object/0.36.7/download", + "sha256": "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" } }, "targets": [ @@ -45207,6 +47436,18 @@ ] } } + }, + { + "BuildScript": { + "crate_name": "build_script_build", + "crate_root": "build.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } } ], "library_target_name": "object", @@ -45236,7 +47477,7 @@ "target": "crc32fast" }, { - "id": "hashbrown 0.14.5", + "id": "hashbrown 0.15.2", "target": "hashbrown" }, { @@ -45246,12 +47487,24 @@ { "id": "memchr 2.6.4", "target": "memchr" + }, + { + "id": "object 0.36.7", + "target": "build_script_build" } ], "selects": {} }, "edition": "2018", - "version": "0.36.1" + "version": "0.36.7" + }, + "build_script_attrs": { + "compile_data_glob": [ + "**" + ], + "data_glob": [ + "**" + ] }, "license": "Apache-2.0 OR MIT", "license_ids": [ @@ -45675,6 +47928,173 @@ ], "license_file": "LICENSE-APACHE" }, + "openssl 0.10.69": { + "name": "openssl", + "version": "0.10.69", + "package_url": "https://github.com/sfackler/rust-openssl", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/openssl/0.10.69/download", + "sha256": "f5e534d133a060a3c19daec1eb3e98ec6f4685978834f2dbadfe2ec215bab64e" + } + }, + "targets": [ + { + "Library": { + "crate_name": "openssl", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + }, + { + "BuildScript": { + "crate_name": "build_script_build", + "crate_root": "build.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "openssl", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "bitflags 2.6.0", + "target": "bitflags" + }, + { + "id": "cfg-if 1.0.0", + "target": "cfg_if" + }, + { + "id": "foreign-types 0.3.2", + "target": "foreign_types" + }, + { + "id": "libc 0.2.158", + "target": "libc" + }, + { + "id": "once_cell 1.19.0", + "target": "once_cell" + }, + { + "id": "openssl 0.10.69", + "target": "build_script_build" + }, + { + "id": "openssl-sys 0.9.104", + "target": "openssl_sys", + "alias": "ffi" + } + ], + "selects": {} + }, + "edition": "2021", + "proc_macro_deps": { + "common": [ + { + "id": "openssl-macros 0.1.1", + "target": "openssl_macros" + } + ], + "selects": {} + }, + "version": "0.10.69" + }, + "build_script_attrs": { + "compile_data_glob": [ + "**" + ], + "data_glob": [ + "**" + ], + "link_deps": { + "common": [ + { + "id": "openssl-sys 0.9.104", + "target": "openssl_sys", + "alias": "ffi" + } + ], + "selects": {} + } + }, + "license": "Apache-2.0", + "license_ids": [ + "Apache-2.0" + ], + "license_file": "LICENSE" + }, + "openssl-macros 0.1.1": { + "name": "openssl-macros", + "version": "0.1.1", + "package_url": null, + "repository": { + "Http": { + "url": "https://static.crates.io/crates/openssl-macros/0.1.1/download", + "sha256": "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" + } + }, + "targets": [ + { + "ProcMacro": { + "crate_name": "openssl_macros", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "openssl_macros", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "proc-macro2 1.0.89", + "target": "proc_macro2" + }, + { + "id": "quote 1.0.37", + "target": "quote" + }, + { + "id": "syn 2.0.87", + "target": "syn" + } + ], + "selects": {} + }, + "edition": "2018", + "version": "0.1.1" + }, + "license": "MIT/Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "openssl-probe 0.1.5": { "name": "openssl-probe", "version": "0.1.5", @@ -45714,14 +48134,62 @@ ], "license_file": "LICENSE-APACHE" }, - "openssl-sys 0.9.102": { + "openssl-src 300.4.1+3.4.0": { + "name": "openssl-src", + "version": "300.4.1+3.4.0", + "package_url": "https://github.com/alexcrichton/openssl-src-rs", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/openssl-src/300.4.1+3.4.0/download", + "sha256": "faa4eac4138c62414b5622d1b31c5c304f34b406b013c079c2bbc652fdd6678c" + } + }, + "targets": [ + { + "Library": { + "crate_name": "openssl_src", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "openssl_src", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "cc 1.1.37", + "target": "cc" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "300.4.1+3.4.0" + }, + "license": "MIT/Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, + "openssl-sys 0.9.104": { "name": "openssl-sys", - "version": "0.9.102", + "version": "0.9.104", "package_url": "https://github.com/sfackler/rust-openssl", "repository": { "Http": { - "url": "https://static.crates.io/crates/openssl-sys/0.9.102/download", - "sha256": "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" + "url": "https://static.crates.io/crates/openssl-sys/0.9.104/download", + "sha256": "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" } }, "targets": [ @@ -45762,14 +48230,14 @@ "target": "libc" }, { - "id": "openssl-sys 0.9.102", + "id": "openssl-sys 0.9.104", "target": "build_script_main" } ], "selects": {} }, - "edition": "2018", - "version": "0.9.102" + "edition": "2021", + "version": "0.9.104" }, "build_script_attrs": { "compile_data_glob": [ @@ -45781,7 +48249,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" }, { @@ -46150,7 +48618,7 @@ "target": "opentelemetry_proto" }, { - "id": "opentelemetry_sdk 0.27.0", + "id": "opentelemetry_sdk 0.27.1", "target": "opentelemetry_sdk" }, { @@ -46374,7 +48842,7 @@ "target": "opentelemetry" }, { - "id": "opentelemetry_sdk 0.27.0", + "id": "opentelemetry_sdk 0.27.1", "target": "opentelemetry_sdk" }, { @@ -46917,7 +49385,7 @@ "target": "tokio" }, { - "id": "tokio-stream 0.1.16", + "id": "tokio-stream 0.1.17", "target": "tokio_stream" } ], @@ -46941,14 +49409,14 @@ ], "license_file": "LICENSE" }, - "opentelemetry_sdk 0.27.0": { + "opentelemetry_sdk 0.27.1": { "name": "opentelemetry_sdk", - "version": "0.27.0", + "version": "0.27.1", "package_url": "https://github.com/open-telemetry/opentelemetry-rust", "repository": { "Http": { - "url": "https://static.crates.io/crates/opentelemetry_sdk/0.27.0/download", - "sha256": "27b742c1cae4693792cc564e58d75a2a0ba29421a34a85b50da92efa89ecb2bc" + "url": "https://static.crates.io/crates/opentelemetry_sdk/0.27.1/download", + "sha256": "231e9d6ceef9b0b2546ddf52335785ce41252bc7474ee8ba05bfad277be13ab8" } }, "targets": [ @@ -47007,10 +49475,6 @@ "id": "glob 0.3.1", "target": "glob" }, - { - "id": "once_cell 1.19.0", - "target": "once_cell" - }, { "id": "opentelemetry 0.27.0", "target": "opentelemetry" @@ -47036,7 +49500,7 @@ "target": "tokio" }, { - "id": "tokio-stream 0.1.16", + "id": "tokio-stream 0.1.17", "target": "tokio_stream" }, { @@ -47056,7 +49520,7 @@ ], "selects": {} }, - "version": "0.27.0" + "version": "0.27.1" }, "license": "Apache-2.0", "license_ids": [ @@ -47492,7 +49956,7 @@ "target": "byte_slice_cast" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -47982,6 +50446,69 @@ ], "license_file": "LICENSE.txt" }, + "password-hash 0.4.2": { + "name": "password-hash", + "version": "0.4.2", + "package_url": "https://github.com/RustCrypto/traits/tree/master/password-hash", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/password-hash/0.4.2/download", + "sha256": "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" + } + }, + "targets": [ + { + "Library": { + "crate_name": "password_hash", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "password_hash", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "default", + "rand_core" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "base64ct 1.6.0", + "target": "base64ct" + }, + { + "id": "rand_core 0.6.4", + "target": "rand_core" + }, + { + "id": "subtle 2.6.1", + "target": "subtle" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.4.2" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "paste 1.0.15": { "name": "paste", "version": "1.0.15", @@ -48050,6 +50577,54 @@ ], "license_file": "LICENSE-APACHE" }, + "pbkdf2 0.11.0": { + "name": "pbkdf2", + "version": "0.11.0", + "package_url": "https://github.com/RustCrypto/password-hashes/tree/master/pbkdf2", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/pbkdf2/0.11.0/download", + "sha256": "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" + } + }, + "targets": [ + { + "Library": { + "crate_name": "pbkdf2", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "pbkdf2", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "digest 0.10.7", + "target": "digest" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.11.0" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "pbkdf2 0.12.2": { "name": "pbkdf2", "version": "0.12.2", @@ -48231,7 +50806,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" }, { @@ -48389,6 +50964,60 @@ ], "license_file": "LICENSE.md" }, + "pem-rfc7468 0.6.0": { + "name": "pem-rfc7468", + "version": "0.6.0", + "package_url": "https://github.com/RustCrypto/formats/tree/master/pem-rfc7468", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/pem-rfc7468/0.6.0/download", + "sha256": "24d159833a9105500e0398934e205e0773f0b27529557134ecfc51c27646adac" + } + }, + "targets": [ + { + "Library": { + "crate_name": "pem_rfc7468", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "pem_rfc7468", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "alloc" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "base64ct 1.6.0", + "target": "base64ct" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.6.0" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "pem-rfc7468 0.7.0": { "name": "pem-rfc7468", "version": "0.7.0", @@ -49692,6 +52321,116 @@ ], "license_file": "LICENSE-APACHE" }, + "pkcs11 0.5.0": { + "name": "pkcs11", + "version": "0.5.0", + "package_url": "https://github.com/mheese/rust-pkcs11", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/pkcs11/0.5.0/download", + "sha256": "3aca6d67e4c8613bfe455599d0233d00735f85df2001f6bfd9bb7ac0496b10af" + } + }, + "targets": [ + { + "Library": { + "crate_name": "pkcs11", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "pkcs11", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "libloading 0.5.2", + "target": "libloading" + }, + { + "id": "num-bigint 0.2.6", + "target": "num_bigint" + } + ], + "selects": {} + }, + "edition": "2015", + "version": "0.5.0" + }, + "license": "Apache-2.0", + "license_ids": [ + "Apache-2.0" + ], + "license_file": "LICENSE" + }, + "pkcs8 0.9.0": { + "name": "pkcs8", + "version": "0.9.0", + "package_url": "https://github.com/RustCrypto/formats/tree/master/pkcs8", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/pkcs8/0.9.0/download", + "sha256": "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" + } + }, + "targets": [ + { + "Library": { + "crate_name": "pkcs8", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "pkcs8", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "alloc", + "pem" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "der 0.6.1", + "target": "der" + }, + { + "id": "spki 0.6.0", + "target": "spki" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.9.0" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "pkcs8 0.10.2": { "name": "pkcs8", "version": "0.10.2", @@ -49987,7 +52726,7 @@ "target": "base64" }, { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -50011,7 +52750,7 @@ "target": "schemars" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -50224,6 +52963,69 @@ ], "license_file": "LICENSE-APACHE" }, + "polyval 0.6.2": { + "name": "polyval", + "version": "0.6.2", + "package_url": "https://github.com/RustCrypto/universal-hashes", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/polyval/0.6.2/download", + "sha256": "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" + } + }, + "targets": [ + { + "Library": { + "crate_name": "polyval", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "polyval", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "cfg-if 1.0.0", + "target": "cfg_if" + }, + { + "id": "opaque-debug 0.3.0", + "target": "opaque_debug" + }, + { + "id": "universal-hash 0.5.1", + "target": "universal_hash" + } + ], + "selects": { + "cfg(any(target_arch = \"aarch64\", target_arch = \"x86_64\", target_arch = \"x86\"))": [ + { + "id": "cpufeatures 0.2.9", + "target": "cpufeatures" + } + ] + } + }, + "edition": "2021", + "version": "0.6.2" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "portable-atomic 1.4.3": { "name": "portable-atomic", "version": "1.4.3", @@ -50347,7 +53149,7 @@ "target": "embedded_io" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -51327,7 +54129,7 @@ "target": "build_script_build" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -53126,7 +55928,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" } ], @@ -53302,14 +56104,14 @@ ], "license_file": "LICENSE" }, - "pulley-interpreter 27.0.0": { + "pulley-interpreter 28.0.0": { "name": "pulley-interpreter", - "version": "27.0.0", + "version": "28.0.0", "package_url": "https://github.com/bytecodealliance/wasmtime/tree/main/pulley", "repository": { "Http": { - "url": "https://static.crates.io/crates/pulley-interpreter/27.0.0/download", - "sha256": "a3b8d81cf799e20564931e9867ca32de545188c6ee4c2e0f6e41d32f0c7dc6fb" + "url": "https://static.crates.io/crates/pulley-interpreter/28.0.0/download", + "sha256": "403a1a95f4c18a45c86c7bff13df00347afd0abcbf2e54af273c837339ffcf77" } }, "targets": [ @@ -53334,7 +56136,7 @@ "deps": { "common": [ { - "id": "cranelift-bitset 0.114.0", + "id": "cranelift-bitset 0.115.0", "target": "cranelift_bitset" }, { @@ -53349,7 +56151,7 @@ "selects": {} }, "edition": "2021", - "version": "27.0.0" + "version": "28.0.0" }, "license": "Apache-2.0 WITH LLVM-exception", "license_ids": [ @@ -54340,13 +57142,13 @@ "target": "libc" } ], - "aarch64-fuchsia": [ + "aarch64-linux-android": [ { "id": "libc 0.2.158", "target": "libc" } ], - "aarch64-linux-android": [ + "aarch64-unknown-fuchsia": [ { "id": "libc 0.2.158", "target": "libc" @@ -54436,19 +57238,19 @@ "target": "libc" } ], - "x86_64-fuchsia": [ + "x86_64-linux-android": [ { "id": "libc 0.2.158", "target": "libc" } ], - "x86_64-linux-android": [ + "x86_64-unknown-freebsd": [ { "id": "libc 0.2.158", "target": "libc" } ], - "x86_64-unknown-freebsd": [ + "x86_64-unknown-fuchsia": [ { "id": "libc 0.2.158", "target": "libc" @@ -55888,14 +58690,14 @@ ], "license_file": "LICENSE" }, - "regalloc2 0.10.2": { + "regalloc2 0.11.1": { "name": "regalloc2", - "version": "0.10.2", + "version": "0.11.1", "package_url": "https://github.com/bytecodealliance/regalloc2", "repository": { "Http": { - "url": "https://static.crates.io/crates/regalloc2/0.10.2/download", - "sha256": "12908dbeb234370af84d0579b9f68258a0f67e201412dd9a2814e6f45b2fc0f0" + "url": "https://static.crates.io/crates/regalloc2/0.11.1/download", + "sha256": "145c1c267e14f20fb0f88aa76a1c5ffec42d592c1d28b3cd9148ae35916158d3" } }, "targets": [ @@ -55928,7 +58730,15 @@ "deps": { "common": [ { - "id": "hashbrown 0.14.5", + "id": "allocator-api2 0.2.21", + "target": "allocator_api2" + }, + { + "id": "bumpalo 3.16.0", + "target": "bumpalo" + }, + { + "id": "hashbrown 0.15.2", "target": "hashbrown" }, { @@ -55939,10 +58749,6 @@ "id": "rustc-hash 2.0.0", "target": "rustc_hash" }, - { - "id": "slice-group-by 0.3.1", - "target": "slice_group_by" - }, { "id": "smallvec 1.13.2", "target": "smallvec" @@ -55951,7 +58757,7 @@ "selects": {} }, "edition": "2018", - "version": "0.10.2" + "version": "0.11.1" }, "license": "Apache-2.0 WITH LLVM-exception", "license_ids": [ @@ -56574,7 +59380,7 @@ "target": "http" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -56689,7 +59495,7 @@ "target": "webpki_roots" } ], - "aarch64-fuchsia": [ + "aarch64-linux-android": [ { "id": "async-compression 0.4.4", "target": "async_compression" @@ -56719,7 +59525,7 @@ "target": "webpki_roots" } ], - "aarch64-linux-android": [ + "aarch64-pc-windows-msvc": [ { "id": "async-compression 0.4.4", "target": "async_compression" @@ -56749,7 +59555,7 @@ "target": "webpki_roots" } ], - "aarch64-pc-windows-msvc": [ + "aarch64-unknown-fuchsia": [ { "id": "async-compression 0.4.4", "target": "async_compression" @@ -56977,7 +59783,7 @@ "target": "hyper" }, { - "id": "ipnet 2.8.0", + "id": "ipnet 2.10.1", "target": "ipnet" }, { @@ -57425,7 +60231,7 @@ "target": "webpki_roots" } ], - "x86_64-fuchsia": [ + "x86_64-linux-android": [ { "id": "async-compression 0.4.4", "target": "async_compression" @@ -57455,7 +60261,7 @@ "target": "webpki_roots" } ], - "x86_64-linux-android": [ + "x86_64-pc-windows-msvc": [ { "id": "async-compression 0.4.4", "target": "async_compression" @@ -57485,7 +60291,7 @@ "target": "webpki_roots" } ], - "x86_64-pc-windows-msvc": [ + "x86_64-unknown-freebsd": [ { "id": "async-compression 0.4.4", "target": "async_compression" @@ -57515,7 +60321,7 @@ "target": "webpki_roots" } ], - "x86_64-unknown-freebsd": [ + "x86_64-unknown-fuchsia": [ { "id": "async-compression 0.4.4", "target": "async_compression" @@ -57724,7 +60530,7 @@ "target": "mime_guess" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -57899,7 +60705,7 @@ "target": "webpki_roots" } ], - "aarch64-fuchsia": [ + "aarch64-linux-android": [ { "id": "futures-channel 0.3.31", "target": "futures_channel" @@ -57949,7 +60755,7 @@ "target": "webpki_roots" } ], - "aarch64-linux-android": [ + "aarch64-pc-windows-msvc": [ { "id": "futures-channel 0.3.31", "target": "futures_channel" @@ -57999,7 +60805,7 @@ "target": "webpki_roots" } ], - "aarch64-pc-windows-msvc": [ + "aarch64-unknown-fuchsia": [ { "id": "futures-channel 0.3.31", "target": "futures_channel" @@ -58367,7 +61173,7 @@ "target": "hyper_util" }, { - "id": "ipnet 2.8.0", + "id": "ipnet 2.10.1", "target": "ipnet" }, { @@ -58975,7 +61781,7 @@ "target": "wasm_streams" } ], - "wasm32-wasi": [ + "wasm32-wasip1": [ { "id": "wasm-streams 0.4.0", "target": "wasm_streams" @@ -59081,7 +61887,7 @@ "target": "webpki_roots" } ], - "x86_64-fuchsia": [ + "x86_64-linux-android": [ { "id": "futures-channel 0.3.31", "target": "futures_channel" @@ -59131,7 +61937,7 @@ "target": "webpki_roots" } ], - "x86_64-linux-android": [ + "x86_64-pc-windows-msvc": [ { "id": "futures-channel 0.3.31", "target": "futures_channel" @@ -59181,7 +61987,7 @@ "target": "webpki_roots" } ], - "x86_64-pc-windows-msvc": [ + "x86_64-unknown-freebsd": [ { "id": "futures-channel 0.3.31", "target": "futures_channel" @@ -59231,7 +62037,7 @@ "target": "webpki_roots" } ], - "x86_64-unknown-freebsd": [ + "x86_64-unknown-fuchsia": [ { "id": "futures-channel 0.3.31", "target": "futures_channel" @@ -59502,6 +62308,62 @@ ], "license_file": "LICENSE-APACHE" }, + "rfc6979 0.3.1": { + "name": "rfc6979", + "version": "0.3.1", + "package_url": "https://github.com/RustCrypto/signatures/tree/master/rfc6979", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/rfc6979/0.3.1/download", + "sha256": "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" + } + }, + "targets": [ + { + "Library": { + "crate_name": "rfc6979", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "rfc6979", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "crypto-bigint 0.4.9", + "target": "crypto_bigint" + }, + { + "id": "hmac 0.12.1", + "target": "hmac" + }, + { + "id": "zeroize 1.8.1", + "target": "zeroize" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.3.1" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "rfc6979 0.4.0": { "name": "rfc6979", "version": "0.4.0", @@ -59807,7 +62669,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" } ], @@ -59924,7 +62786,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" } ], @@ -60912,7 +63774,7 @@ "target": "build_script_build" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -61349,7 +64211,7 @@ "time", "use-libc-auxv" ], - "aarch64-fuchsia": [ + "aarch64-linux-android": [ "default", "event", "mm", @@ -61359,7 +64221,7 @@ "time", "use-libc-auxv" ], - "aarch64-linux-android": [ + "aarch64-unknown-fuchsia": [ "default", "event", "mm", @@ -61530,7 +64392,7 @@ "termios", "use-libc-auxv" ], - "wasm32-wasi": [ + "wasm32-wasip1": [ "default", "termios", "use-libc-auxv" @@ -61555,7 +64417,7 @@ "time", "use-libc-auxv" ], - "x86_64-fuchsia": [ + "x86_64-linux-android": [ "default", "event", "mm", @@ -61565,7 +64427,7 @@ "time", "use-libc-auxv" ], - "x86_64-linux-android": [ + "x86_64-unknown-freebsd": [ "default", "event", "mm", @@ -61575,7 +64437,7 @@ "time", "use-libc-auxv" ], - "x86_64-unknown-freebsd": [ + "x86_64-unknown-fuchsia": [ "default", "event", "mm", @@ -61663,17 +64525,6 @@ "target": "libc" } ], - "aarch64-fuchsia": [ - { - "id": "errno 0.3.8", - "target": "errno", - "alias": "libc_errno" - }, - { - "id": "libc 0.2.158", - "target": "libc" - } - ], "aarch64-linux-android": [ { "id": "errno 0.3.8", @@ -61692,6 +64543,17 @@ "alias": "libc_errno" } ], + "aarch64-unknown-fuchsia": [ + { + "id": "errno 0.3.8", + "target": "errno", + "alias": "libc_errno" + }, + { + "id": "libc 0.2.158", + "target": "libc" + } + ], "aarch64-unknown-nto-qnx710": [ { "id": "errno 0.3.8", @@ -61865,7 +64727,7 @@ "target": "libc" } ], - "wasm32-wasi": [ + "wasm32-wasip1": [ { "id": "errno 0.3.8", "target": "errno", @@ -61898,7 +64760,7 @@ "target": "libc" } ], - "x86_64-fuchsia": [ + "x86_64-linux-android": [ { "id": "errno 0.3.8", "target": "errno", @@ -61909,25 +64771,25 @@ "target": "libc" } ], - "x86_64-linux-android": [ + "x86_64-pc-windows-msvc": [ { "id": "errno 0.3.8", "target": "errno", "alias": "libc_errno" - }, - { - "id": "libc 0.2.158", - "target": "libc" } ], - "x86_64-pc-windows-msvc": [ + "x86_64-unknown-freebsd": [ { "id": "errno 0.3.8", "target": "errno", "alias": "libc_errno" + }, + { + "id": "libc 0.2.158", + "target": "libc" } ], - "x86_64-unknown-freebsd": [ + "x86_64-unknown-fuchsia": [ { "id": "errno 0.3.8", "target": "errno", @@ -62398,7 +65260,7 @@ "target": "ring" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -63054,104 +65916,7 @@ "ring", "std" ], - "selects": { - "aarch64-apple-darwin": [ - "default" - ], - "aarch64-apple-ios": [ - "default" - ], - "aarch64-apple-ios-sim": [ - "default" - ], - "aarch64-fuchsia": [ - "default" - ], - "aarch64-linux-android": [ - "default" - ], - "aarch64-pc-windows-msvc": [ - "default" - ], - "aarch64-unknown-linux-gnu": [ - "default" - ], - "aarch64-unknown-nixos-gnu": [ - "default" - ], - "aarch64-unknown-nto-qnx710": [ - "default" - ], - "arm-unknown-linux-gnueabi": [ - "default" - ], - "armv7-linux-androideabi": [ - "default" - ], - "armv7-unknown-linux-gnueabi": [ - "default" - ], - "i686-apple-darwin": [ - "default" - ], - "i686-linux-android": [ - "default" - ], - "i686-pc-windows-msvc": [ - "default" - ], - "i686-unknown-freebsd": [ - "default" - ], - "i686-unknown-linux-gnu": [ - "default" - ], - "powerpc-unknown-linux-gnu": [ - "default" - ], - "riscv32imc-unknown-none-elf": [ - "default" - ], - "riscv64gc-unknown-none-elf": [ - "default" - ], - "s390x-unknown-linux-gnu": [ - "default" - ], - "thumbv7em-none-eabi": [ - "default" - ], - "thumbv8m.main-none-eabi": [ - "default" - ], - "x86_64-apple-darwin": [ - "default" - ], - "x86_64-apple-ios": [ - "default" - ], - "x86_64-fuchsia": [ - "default" - ], - "x86_64-linux-android": [ - "default" - ], - "x86_64-pc-windows-msvc": [ - "default" - ], - "x86_64-unknown-freebsd": [ - "default" - ], - "x86_64-unknown-linux-gnu": [ - "default" - ], - "x86_64-unknown-nixos-gnu": [ - "default" - ], - "x86_64-unknown-none": [ - "default" - ] - } + "selects": {} }, "deps": { "common": [ @@ -63650,7 +66415,7 @@ "target": "build_script_build" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -64047,6 +66812,90 @@ ], "license_file": null }, + "sec1 0.3.0": { + "name": "sec1", + "version": "0.3.0", + "package_url": "https://github.com/RustCrypto/formats/tree/master/sec1", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/sec1/0.3.0/download", + "sha256": "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" + } + }, + "targets": [ + { + "Library": { + "crate_name": "sec1", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "sec1", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "alloc", + "base16ct", + "default", + "der", + "generic-array", + "pem", + "pkcs8", + "point", + "std", + "subtle", + "zeroize" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "base16ct 0.1.1", + "target": "base16ct" + }, + { + "id": "der 0.6.1", + "target": "der" + }, + { + "id": "generic-array 0.14.7", + "target": "generic_array" + }, + { + "id": "pkcs8 0.9.0", + "target": "pkcs8" + }, + { + "id": "subtle 2.6.1", + "target": "subtle" + }, + { + "id": "zeroize 1.8.1", + "target": "zeroize" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.3.0" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "sec1 0.7.3": { "name": "sec1", "version": "0.7.3", @@ -64181,7 +67030,7 @@ "target": "secp256k1_sys" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -64252,7 +67101,7 @@ "target": "secp256k1_sys" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -64299,7 +67148,12 @@ "crate_features": { "common": [ "alloc", - "hashes" + "hashes", + "rand", + "rand-std", + "recovery", + "serde", + "std" ], "selects": {} }, @@ -64310,9 +67164,17 @@ "target": "bitcoin_hashes", "alias": "hashes" }, + { + "id": "rand 0.8.5", + "target": "rand" + }, { "id": "secp256k1-sys 0.10.0", "target": "secp256k1_sys" + }, + { + "id": "serde 1.0.217", + "target": "serde" } ], "selects": {} @@ -64396,7 +67258,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" } ], @@ -64481,7 +67343,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" } ], @@ -64538,7 +67400,9 @@ ], "crate_features": { "common": [ - "alloc" + "alloc", + "recovery", + "std" ], "selects": {} }, @@ -64570,7 +67434,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" } ], @@ -64624,7 +67488,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -64675,16 +67539,91 @@ ], "crate_features": { "common": [ - "OSX_10_10", - "OSX_10_11", + "OSX_10_10", + "OSX_10_11", + "OSX_10_12", + "OSX_10_13", + "OSX_10_14", + "OSX_10_9", + "alpn", + "default", + "serial-number-bigint", + "session-tickets" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "bitflags 2.6.0", + "target": "bitflags" + }, + { + "id": "core-foundation 0.9.4", + "target": "core_foundation" + }, + { + "id": "core-foundation-sys 0.8.7", + "target": "core_foundation_sys" + }, + { + "id": "libc 0.2.158", + "target": "libc" + }, + { + "id": "num-bigint 0.4.6", + "target": "num_bigint" + }, + { + "id": "security-framework-sys 2.12.0", + "target": "security_framework_sys" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "2.11.1" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, + "security-framework 3.0.1": { + "name": "security-framework", + "version": "3.0.1", + "package_url": "https://github.com/kornelski/rust-security-framework", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/security-framework/3.0.1/download", + "sha256": "e1415a607e92bec364ea2cf9264646dcce0f91e6d65281bd6f2819cca3bf39c8" + } + }, + "targets": [ + { + "Library": { + "crate_name": "security_framework", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "security_framework", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ "OSX_10_12", - "OSX_10_13", - "OSX_10_14", - "OSX_10_9", - "alpn", - "default", - "serial-number-bigint", - "session-tickets" + "default" ], "selects": {} }, @@ -64695,7 +67634,7 @@ "target": "bitflags" }, { - "id": "core-foundation 0.9.4", + "id": "core-foundation 0.10.0", "target": "core_foundation" }, { @@ -64706,10 +67645,6 @@ "id": "libc 0.2.158", "target": "libc" }, - { - "id": "num-bigint 0.4.6", - "target": "num_bigint" - }, { "id": "security-framework-sys 2.12.0", "target": "security_framework_sys" @@ -64718,7 +67653,7 @@ "selects": {} }, "edition": "2021", - "version": "2.11.1" + "version": "3.0.1" }, "license": "MIT OR Apache-2.0", "license_ids": [ @@ -64968,7 +67903,7 @@ "target": "build_script_build" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -64992,14 +67927,14 @@ ], "license_file": "LICENSE-APACHE" }, - "serde 1.0.214": { + "serde 1.0.217": { "name": "serde", - "version": "1.0.214", + "version": "1.0.217", "package_url": "https://github.com/serde-rs/serde", "repository": { "Http": { - "url": "https://static.crates.io/crates/serde/1.0.214/download", - "sha256": "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" + "url": "https://static.crates.io/crates/serde/1.0.217/download", + "sha256": "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" } }, "targets": [ @@ -65047,7 +67982,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "build_script_build" } ], @@ -65057,13 +67992,13 @@ "proc_macro_deps": { "common": [ { - "id": "serde_derive 1.0.214", + "id": "serde_derive 1.0.217", "target": "serde_derive" } ], "selects": {} }, - "version": "1.0.214" + "version": "1.0.217" }, "build_script_attrs": { "compile_data_glob": [ @@ -65120,7 +68055,7 @@ "target": "hex" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -65171,7 +68106,7 @@ "target": "ordered_float" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -65222,7 +68157,7 @@ "target": "js_sys" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -65280,7 +68215,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -65339,7 +68274,7 @@ "target": "half" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -65355,14 +68290,14 @@ ], "license_file": "LICENSE-APACHE" }, - "serde_derive 1.0.214": { + "serde_derive 1.0.217": { "name": "serde_derive", - "version": "1.0.214", + "version": "1.0.217", "package_url": "https://github.com/serde-rs/serde", "repository": { "Http": { - "url": "https://static.crates.io/crates/serde_derive/1.0.214/download", - "sha256": "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" + "url": "https://static.crates.io/crates/serde_derive/1.0.217/download", + "sha256": "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" } }, "targets": [ @@ -65408,7 +68343,7 @@ "selects": {} }, "edition": "2015", - "version": "1.0.214" + "version": "1.0.217" }, "license": "MIT OR Apache-2.0", "license_ids": [ @@ -65540,7 +68475,7 @@ "target": "ryu" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -65604,7 +68539,7 @@ "target": "itoa" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -65656,7 +68591,7 @@ "target": "percent_encoding" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -65712,7 +68647,7 @@ "target": "regex" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -65728,14 +68663,14 @@ ], "license_file": "LICENSE-APACHE" }, - "serde_repr 0.1.16": { + "serde_repr 0.1.19": { "name": "serde_repr", - "version": "0.1.16", + "version": "0.1.19", "package_url": "https://github.com/dtolnay/serde-repr", "repository": { "Http": { - "url": "https://static.crates.io/crates/serde_repr/0.1.16/download", - "sha256": "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00" + "url": "https://static.crates.io/crates/serde_repr/0.1.19/download", + "sha256": "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" } }, "targets": [ @@ -65775,7 +68710,7 @@ "selects": {} }, "edition": "2021", - "version": "0.1.16" + "version": "0.1.19" }, "license": "MIT OR Apache-2.0", "license_ids": [ @@ -65820,7 +68755,7 @@ "target": "proc_macro2" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -65879,7 +68814,7 @@ "target": "quote" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -65942,7 +68877,7 @@ "target": "ryu" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -65998,7 +68933,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -66069,7 +69004,7 @@ "target": "base64" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -66186,7 +69121,7 @@ "deps": { "common": [ { - "id": "darling 0.20.3", + "id": "darling 0.20.10", "target": "darling" }, { @@ -66254,7 +69189,7 @@ "target": "ryu" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -66318,7 +69253,7 @@ "target": "ryu" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -66790,6 +69725,52 @@ ], "license_file": "LICENSE" }, + "shell-words 1.1.0": { + "name": "shell-words", + "version": "1.1.0", + "package_url": "https://github.com/tmiasko/shell-words", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/shell-words/1.1.0/download", + "sha256": "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + } + }, + "targets": [ + { + "Library": { + "crate_name": "shell_words", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "shell_words", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "default", + "std" + ], + "selects": {} + }, + "edition": "2015", + "version": "1.1.0" + }, + "license": "MIT/Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "shlex 1.3.0": { "name": "shlex", "version": "1.3.0", @@ -67032,6 +70013,69 @@ ], "license_file": "LICENSE-APACHE" }, + "signature 1.6.4": { + "name": "signature", + "version": "1.6.4", + "package_url": "https://github.com/RustCrypto/traits/tree/master/signature", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/signature/1.6.4/download", + "sha256": "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" + } + }, + "targets": [ + { + "Library": { + "crate_name": "signature", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "signature", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "digest", + "digest-preview", + "hazmat-preview", + "rand-preview", + "rand_core", + "std" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "digest 0.10.7", + "target": "digest" + }, + { + "id": "rand_core 0.6.4", + "target": "rand_core" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "1.6.4" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "signature 2.2.0": { "name": "signature", "version": "2.2.0", @@ -67549,44 +70593,6 @@ ], "license_file": "LICENSE" }, - "slice-group-by 0.3.1": { - "name": "slice-group-by", - "version": "0.3.1", - "package_url": "https://github.com/Kerollmops/slice-group-by", - "repository": { - "Http": { - "url": "https://static.crates.io/crates/slice-group-by/0.3.1/download", - "sha256": "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" - } - }, - "targets": [ - { - "Library": { - "crate_name": "slice_group_by", - "crate_root": "src/lib.rs", - "srcs": { - "allow_empty": true, - "include": [ - "**/*.rs" - ] - } - } - } - ], - "library_target_name": "slice_group_by", - "common_attrs": { - "compile_data_glob": [ - "**" - ], - "edition": "2018", - "version": "0.3.1" - }, - "license": "MIT", - "license_ids": [ - "MIT" - ], - "license_file": "LICENSE" - }, "slog 2.7.0": { "name": "slog", "version": "2.7.0", @@ -67886,7 +70892,7 @@ "target": "erased_serde" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -68225,7 +71231,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -68570,15 +71576,15 @@ "aarch64-apple-ios-sim": [ "once" ], - "aarch64-fuchsia": [ - "once" - ], "aarch64-linux-android": [ "once" ], "aarch64-pc-windows-msvc": [ "once" ], + "aarch64-unknown-fuchsia": [ + "once" + ], "aarch64-unknown-linux-gnu": [ "once" ], @@ -68624,9 +71630,6 @@ "x86_64-apple-ios": [ "once" ], - "x86_64-fuchsia": [ - "once" - ], "x86_64-linux-android": [ "once" ], @@ -68636,6 +71639,9 @@ "x86_64-unknown-freebsd": [ "once" ], + "x86_64-unknown-fuchsia": [ + "once" + ], "x86_64-unknown-linux-gnu": [ "once" ], @@ -68656,6 +71662,66 @@ ], "license_file": "LICENSE" }, + "spki 0.6.0": { + "name": "spki", + "version": "0.6.0", + "package_url": "https://github.com/RustCrypto/formats/tree/master/spki", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/spki/0.6.0/download", + "sha256": "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" + } + }, + "targets": [ + { + "Library": { + "crate_name": "spki", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "spki", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "alloc", + "base64ct", + "pem" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "base64ct 1.6.0", + "target": "base64ct" + }, + { + "id": "der 0.6.1", + "target": "der" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.6.0" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "spki 0.7.3": { "name": "spki", "version": "0.7.3", @@ -68946,7 +72012,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" } ], @@ -68999,6 +72065,66 @@ ], "license_file": "LICENSE-APACHE" }, + "stop-token 0.7.0": { + "name": "stop-token", + "version": "0.7.0", + "package_url": "https://github.com/async-rs/stop-token", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/stop-token/0.7.0/download", + "sha256": "af91f480ee899ab2d9f8435bfdfc14d08a5754bd9d3fef1f1a1c23336aad6c8b" + } + }, + "targets": [ + { + "Library": { + "crate_name": "stop_token", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "stop_token", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "async-channel 1.9.0", + "target": "async_channel" + }, + { + "id": "cfg-if 1.0.0", + "target": "cfg_if" + }, + { + "id": "futures-core 0.3.31", + "target": "futures_core" + }, + { + "id": "pin-project-lite 0.2.13", + "target": "pin_project_lite" + } + ], + "selects": {} + }, + "edition": "2018", + "version": "0.7.0" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": null + }, "str_stack 0.1.0": { "name": "str_stack", "version": "0.1.0", @@ -69098,7 +72224,7 @@ "target": "precomputed_hash" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -70345,6 +73471,95 @@ ], "license_file": "LICENSE" }, + "syscalls 0.6.18": { + "name": "syscalls", + "version": "0.6.18", + "package_url": "https://github.com/jasonwhite/syscalls", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/syscalls/0.6.18/download", + "sha256": "43d0e35dc7d73976a53c7e6d7d177ef804a0c0ee774ec77bcc520c2216fd7cbe" + } + }, + "targets": [ + { + "Library": { + "crate_name": "syscalls", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + }, + { + "BuildScript": { + "crate_name": "build_script_build", + "crate_root": "build.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "syscalls", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "default", + "serde", + "serde_repr", + "std" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "serde 1.0.217", + "target": "serde" + }, + { + "id": "syscalls 0.6.18", + "target": "build_script_build" + } + ], + "selects": {} + }, + "edition": "2021", + "proc_macro_deps": { + "common": [ + { + "id": "serde_repr 0.1.19", + "target": "serde_repr" + } + ], + "selects": {} + }, + "version": "0.6.18" + }, + "build_script_attrs": { + "compile_data_glob": [ + "**" + ], + "data_glob": [ + "**" + ] + }, + "license": "BSD-2-Clause", + "license_ids": [ + "BSD-2-Clause" + ], + "license_file": "LICENSE" + }, "system-configuration 0.5.1": { "name": "system-configuration", "version": "0.5.1", @@ -70726,13 +73941,13 @@ "target": "xattr" } ], - "aarch64-fuchsia": [ + "aarch64-linux-android": [ { "id": "xattr 0.2.3", "target": "xattr" } ], - "aarch64-linux-android": [ + "aarch64-unknown-fuchsia": [ { "id": "xattr 0.2.3", "target": "xattr" @@ -70828,19 +74043,19 @@ "target": "xattr" } ], - "x86_64-fuchsia": [ + "x86_64-linux-android": [ { "id": "xattr 0.2.3", "target": "xattr" } ], - "x86_64-linux-android": [ + "x86_64-unknown-freebsd": [ { "id": "xattr 0.2.3", "target": "xattr" } ], - "x86_64-unknown-freebsd": [ + "x86_64-unknown-fuchsia": [ { "id": "xattr 0.2.3", "target": "xattr" @@ -71019,7 +74234,7 @@ "target": "rand" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -72347,7 +75562,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" } ], @@ -72466,6 +75681,8 @@ "local-offset", "macros", "parsing", + "serde", + "serde-human-readable", "std" ], "selects": {} @@ -72488,6 +75705,10 @@ "id": "powerfmt 0.2.0", "target": "powerfmt" }, + { + "id": "serde 1.0.217", + "target": "serde" + }, { "id": "time-core 0.1.2", "target": "time_core" @@ -72524,7 +75745,7 @@ "target": "num_threads" } ], - "aarch64-fuchsia": [ + "aarch64-linux-android": [ { "id": "libc 0.2.158", "target": "libc" @@ -72534,7 +75755,7 @@ "target": "num_threads" } ], - "aarch64-linux-android": [ + "aarch64-unknown-fuchsia": [ { "id": "libc 0.2.158", "target": "libc" @@ -72684,7 +75905,7 @@ "target": "num_threads" } ], - "x86_64-fuchsia": [ + "x86_64-linux-android": [ { "id": "libc 0.2.158", "target": "libc" @@ -72694,7 +75915,7 @@ "target": "num_threads" } ], - "x86_64-linux-android": [ + "x86_64-unknown-freebsd": [ { "id": "libc 0.2.158", "target": "libc" @@ -72704,7 +75925,7 @@ "target": "num_threads" } ], - "x86_64-unknown-freebsd": [ + "x86_64-unknown-fuchsia": [ { "id": "libc 0.2.158", "target": "libc" @@ -72826,7 +76047,8 @@ "crate_features": { "common": [ "formatting", - "parsing" + "parsing", + "serde" ], "selects": {} }, @@ -72853,6 +76075,110 @@ ], "license_file": "LICENSE-Apache" }, + "tiny-bip39 1.0.0": { + "name": "tiny-bip39", + "version": "1.0.0", + "package_url": "https://github.com/maciejhirsz/tiny-bip39/", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/tiny-bip39/1.0.0/download", + "sha256": "62cc94d358b5a1e84a5cb9109f559aa3c4d634d2b1b4de3d0fa4adc7c78e2861" + } + }, + "targets": [ + { + "Library": { + "crate_name": "bip39", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "bip39", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "chinese-simplified", + "chinese-traditional", + "default", + "french", + "italian", + "japanese", + "korean", + "spanish" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "anyhow 1.0.93", + "target": "anyhow" + }, + { + "id": "hmac 0.12.1", + "target": "hmac" + }, + { + "id": "once_cell 1.19.0", + "target": "once_cell" + }, + { + "id": "pbkdf2 0.11.0", + "target": "pbkdf2" + }, + { + "id": "rand 0.8.5", + "target": "rand" + }, + { + "id": "rustc-hash 1.1.0", + "target": "rustc_hash" + }, + { + "id": "sha2 0.10.8", + "target": "sha2" + }, + { + "id": "thiserror 1.0.68", + "target": "thiserror" + }, + { + "id": "unicode-normalization 0.1.22", + "target": "unicode_normalization" + }, + { + "id": "zeroize 1.8.1", + "target": "zeroize" + } + ], + "selects": { + "cfg(target_arch = \"wasm32\")": [ + { + "id": "wasm-bindgen 0.2.95", + "target": "wasm_bindgen" + } + ] + } + }, + "edition": "2018", + "version": "1.0.0" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "tiny-keccak 2.0.2": { "name": "tiny-keccak", "version": "2.0.2", @@ -73032,7 +76358,9 @@ "version": "0.7.6" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "tinytemplate 1.2.1": { @@ -73067,7 +76395,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -73415,7 +76743,7 @@ "target": "socket2" } ], - "aarch64-fuchsia": [ + "aarch64-linux-android": [ { "id": "libc 0.2.158", "target": "libc" @@ -73429,7 +76757,17 @@ "target": "socket2" } ], - "aarch64-linux-android": [ + "aarch64-pc-windows-msvc": [ + { + "id": "socket2 0.5.7", + "target": "socket2" + }, + { + "id": "windows-sys 0.52.0", + "target": "windows_sys" + } + ], + "aarch64-unknown-fuchsia": [ { "id": "libc 0.2.158", "target": "libc" @@ -73443,16 +76781,6 @@ "target": "socket2" } ], - "aarch64-pc-windows-msvc": [ - { - "id": "socket2 0.5.7", - "target": "socket2" - }, - { - "id": "windows-sys 0.52.0", - "target": "windows_sys" - } - ], "aarch64-unknown-linux-gnu": [ { "id": "libc 0.2.158", @@ -73689,7 +77017,7 @@ "target": "socket2" } ], - "x86_64-fuchsia": [ + "x86_64-linux-android": [ { "id": "libc 0.2.158", "target": "libc" @@ -73703,7 +77031,17 @@ "target": "socket2" } ], - "x86_64-linux-android": [ + "x86_64-pc-windows-msvc": [ + { + "id": "socket2 0.5.7", + "target": "socket2" + }, + { + "id": "windows-sys 0.52.0", + "target": "windows_sys" + } + ], + "x86_64-unknown-freebsd": [ { "id": "libc 0.2.158", "target": "libc" @@ -73717,17 +77055,7 @@ "target": "socket2" } ], - "x86_64-pc-windows-msvc": [ - { - "id": "socket2 0.5.7", - "target": "socket2" - }, - { - "id": "windows-sys 0.52.0", - "target": "windows_sys" - } - ], - "x86_64-unknown-freebsd": [ + "x86_64-unknown-fuchsia": [ { "id": "libc 0.2.158", "target": "libc" @@ -73954,7 +77282,7 @@ "target": "tokio" }, { - "id": "tokio-stream 0.1.16", + "id": "tokio-stream 0.1.17", "target": "tokio_stream" } ], @@ -74225,7 +77553,7 @@ "target": "pin_project" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -74320,14 +77648,14 @@ ], "license_file": "LICENSE" }, - "tokio-stream 0.1.16": { + "tokio-stream 0.1.17": { "name": "tokio-stream", - "version": "0.1.16", + "version": "0.1.17", "package_url": "https://github.com/tokio-rs/tokio", "repository": { "Http": { - "url": "https://static.crates.io/crates/tokio-stream/0.1.16/download", - "sha256": "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" + "url": "https://static.crates.io/crates/tokio-stream/0.1.17/download", + "sha256": "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" } }, "targets": [ @@ -74375,7 +77703,7 @@ "selects": {} }, "edition": "2021", - "version": "0.1.16" + "version": "0.1.17" }, "license": "MIT", "license_ids": [ @@ -74431,7 +77759,7 @@ "target": "tokio" }, { - "id": "tokio-stream 0.1.16", + "id": "tokio-stream 0.1.17", "target": "tokio_stream" } ], @@ -74645,7 +77973,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -74934,7 +78262,7 @@ "target": "tokio" }, { - "id": "tokio-stream 0.1.16", + "id": "tokio-stream 0.1.17", "target": "tokio_stream" }, { @@ -76290,7 +79618,7 @@ "target": "opentelemetry" }, { - "id": "opentelemetry_sdk 0.27.0", + "id": "opentelemetry_sdk 0.27.1", "target": "opentelemetry_sdk" }, { @@ -76368,7 +79696,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -76419,7 +79747,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -76574,7 +79902,7 @@ "target": "regex" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -76782,7 +80110,7 @@ "target": "idna" }, { - "id": "ipnet 2.8.0", + "id": "ipnet 2.10.1", "target": "ipnet" }, { @@ -77164,7 +80492,7 @@ "target": "tokio" }, { - "id": "tokio-stream 0.1.16", + "id": "tokio-stream 0.1.17", "target": "tokio_stream" }, { @@ -78136,7 +81464,7 @@ "target": "percent_encoding" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -78437,7 +81765,7 @@ "target": "getrandom" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -78673,7 +82001,7 @@ "target": "regex" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -79205,7 +82533,7 @@ "target": "scoped_tls" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -79429,7 +82757,7 @@ "deps": { "common": [ { - "id": "bumpalo 3.14.0", + "id": "bumpalo 3.16.0", "target": "bumpalo" }, { @@ -79844,14 +83172,14 @@ ], "license_file": null }, - "wasm-encoder 0.219.1": { + "wasm-encoder 0.221.2": { "name": "wasm-encoder", - "version": "0.219.1", + "version": "0.221.2", "package_url": "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wasm-encoder", "repository": { "Http": { - "url": "https://static.crates.io/crates/wasm-encoder/0.219.1/download", - "sha256": "29cbbd772edcb8e7d524a82ee8cef8dd046fc14033796a754c3ad246d019fa54" + "url": "https://static.crates.io/crates/wasm-encoder/0.221.2/download", + "sha256": "c17a3bd88f2155da63a1f2fcb8a56377a24f0b6dfed12733bb5f544e86f690c5" } }, "targets": [ @@ -79890,7 +83218,7 @@ "selects": {} }, "edition": "2021", - "version": "0.219.1" + "version": "0.221.2" }, "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", "license_ids": [ @@ -80156,7 +83484,7 @@ "target": "semver" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -80233,7 +83561,7 @@ "target": "semver" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -80249,14 +83577,14 @@ ], "license_file": null }, - "wasmparser 0.219.1": { + "wasmparser 0.221.2": { "name": "wasmparser", - "version": "0.219.1", + "version": "0.221.2", "package_url": "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wasmparser", "repository": { "Http": { - "url": "https://static.crates.io/crates/wasmparser/0.219.1/download", - "sha256": "5c771866898879073c53b565a6c7b49953795159836714ac56a5befb581227c5" + "url": "https://static.crates.io/crates/wasmparser/0.221.2/download", + "sha256": "9845c470a2e10b61dd42c385839cdd6496363ed63b5c9e420b5488b77bd22083" } }, "targets": [ @@ -80271,6 +83599,18 @@ ] } } + }, + { + "BuildScript": { + "crate_name": "build_script_build", + "crate_root": "build.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } } ], "library_target_name": "wasmparser", @@ -80283,6 +83623,7 @@ "component-model", "features", "serde", + "simd", "std", "validate" ], @@ -80290,35 +83631,35 @@ }, "deps": { "common": [ - { - "id": "ahash 0.8.11", - "target": "ahash" - }, { "id": "bitflags 2.6.0", "target": "bitflags" }, - { - "id": "hashbrown 0.14.5", - "target": "hashbrown" - }, - { - "id": "indexmap 2.2.6", - "target": "indexmap" - }, { "id": "semver 1.0.22", "target": "semver" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" + }, + { + "id": "wasmparser 0.221.2", + "target": "build_script_build" } ], "selects": {} }, "edition": "2021", - "version": "0.219.1" + "version": "0.221.2" + }, + "build_script_attrs": { + "compile_data_glob": [ + "**" + ], + "data_glob": [ + "**" + ] }, "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", "license_ids": [ @@ -80383,14 +83724,14 @@ ], "license_file": null }, - "wasmprinter 0.219.1": { + "wasmprinter 0.221.2": { "name": "wasmprinter", - "version": "0.219.1", + "version": "0.221.2", "package_url": "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wasmprinter", "repository": { "Http": { - "url": "https://static.crates.io/crates/wasmprinter/0.219.1/download", - "sha256": "228cdc1f30c27816da225d239ce4231f28941147d34713dee8f1fff7cb330e54" + "url": "https://static.crates.io/crates/wasmprinter/0.221.2/download", + "sha256": "a80742ff1b9e6d8c231ac7c7247782c6fc5bce503af760bca071811e5fc9ee56" } }, "targets": [ @@ -80430,14 +83771,14 @@ "target": "termcolor" }, { - "id": "wasmparser 0.219.1", + "id": "wasmparser 0.221.2", "target": "wasmparser" } ], "selects": {} }, "edition": "2021", - "version": "0.219.1" + "version": "0.221.2" }, "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", "license_ids": [ @@ -80446,14 +83787,14 @@ ], "license_file": null }, - "wasmtime 27.0.0": { + "wasmtime 28.0.0": { "name": "wasmtime", - "version": "27.0.0", + "version": "28.0.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/wasmtime/27.0.0/download", - "sha256": "5b79302e3e084713249cc5622e8608e7410afdeeea8c8026d04f491d1fab0b4b" + "url": "https://static.crates.io/crates/wasmtime/28.0.0/download", + "sha256": "f639ecae347b9a2227e453a7b7671e84370a0b61f47a15e0390fe9b7725e47b3" } }, "targets": [ @@ -80495,6 +83836,7 @@ "once_cell", "parallel-compilation", "runtime", + "signals-based-traps", "std" ], "selects": {} @@ -80510,7 +83852,7 @@ "target": "bitflags" }, { - "id": "bumpalo 3.14.0", + "id": "bumpalo 3.16.0", "target": "bumpalo" }, { @@ -80538,7 +83880,7 @@ "target": "log" }, { - "id": "object 0.36.1", + "id": "object 0.36.7", "target": "object" }, { @@ -80554,7 +83896,7 @@ "target": "rayon" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -80570,31 +83912,31 @@ "target": "target_lexicon" }, { - "id": "wasmparser 0.219.1", + "id": "wasmparser 0.221.2", "target": "wasmparser" }, { - "id": "wasmtime 27.0.0", + "id": "wasmtime 28.0.0", "target": "build_script_build" }, { - "id": "wasmtime-asm-macros 27.0.0", + "id": "wasmtime-asm-macros 28.0.0", "target": "wasmtime_asm_macros" }, { - "id": "wasmtime-cranelift 27.0.0", + "id": "wasmtime-cranelift 28.0.0", "target": "wasmtime_cranelift" }, { - "id": "wasmtime-environ 27.0.0", + "id": "wasmtime-environ 28.0.0", "target": "wasmtime_environ" }, { - "id": "wasmtime-jit-icache-coherence 27.0.0", + "id": "wasmtime-jit-icache-coherence 28.0.0", "target": "wasmtime_jit_icache_coherence" }, { - "id": "wasmtime-slab 27.0.0", + "id": "wasmtime-slab 28.0.0", "target": "wasmtime_slab" } ], @@ -80621,12 +83963,6 @@ "target": "rustix" } ], - "aarch64-fuchsia": [ - { - "id": "rustix 0.38.32", - "target": "rustix" - } - ], "aarch64-linux-android": [ { "id": "rustix 0.38.32", @@ -80639,6 +83975,12 @@ "target": "windows_sys" } ], + "aarch64-unknown-fuchsia": [ + { + "id": "rustix 0.38.32", + "target": "rustix" + } + ], "aarch64-unknown-linux-gnu": [ { "id": "memfd 0.6.4", @@ -80769,12 +84111,6 @@ "target": "rustix" } ], - "x86_64-fuchsia": [ - { - "id": "rustix 0.38.32", - "target": "rustix" - } - ], "x86_64-linux-android": [ { "id": "rustix 0.38.32", @@ -80793,6 +84129,12 @@ "target": "rustix" } ], + "x86_64-unknown-fuchsia": [ + { + "id": "rustix 0.38.32", + "target": "rustix" + } + ], "x86_64-unknown-linux-gnu": [ { "id": "memfd 0.6.4", @@ -80823,11 +84165,11 @@ "target": "paste" }, { - "id": "serde_derive 1.0.214", + "id": "serde_derive 1.0.217", "target": "serde_derive" }, { - "id": "wasmtime-versioned-export-macros 27.0.0", + "id": "wasmtime-versioned-export-macros 28.0.0", "target": "wasmtime_versioned_export_macros" } ], @@ -80854,7 +84196,7 @@ ], "selects": {} }, - "version": "27.0.0" + "version": "28.0.0" }, "build_script_attrs": { "compile_data_glob": [ @@ -80866,7 +84208,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" } ], @@ -80875,7 +84217,7 @@ "proc_macro_deps": { "common": [ { - "id": "wasmtime-versioned-export-macros 27.0.0", + "id": "wasmtime-versioned-export-macros 28.0.0", "target": "wasmtime_versioned_export_macros" } ], @@ -80888,14 +84230,14 @@ ], "license_file": "LICENSE" }, - "wasmtime-asm-macros 27.0.0": { + "wasmtime-asm-macros 28.0.0": { "name": "wasmtime-asm-macros", - "version": "27.0.0", + "version": "28.0.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/wasmtime-asm-macros/27.0.0/download", - "sha256": "fe53a24e7016a5222875d8ca3ad6024b464465985693c42098cd0bb710002c28" + "url": "https://static.crates.io/crates/wasmtime-asm-macros/28.0.0/download", + "sha256": "882a18800471cfc063c8b3ccf75723784acc3fd534009ac09421f2fac2fcdcec" } }, "targets": [ @@ -80927,7 +84269,7 @@ "selects": {} }, "edition": "2021", - "version": "27.0.0" + "version": "28.0.0" }, "license": "Apache-2.0 WITH LLVM-exception", "license_ids": [ @@ -80935,14 +84277,14 @@ ], "license_file": null }, - "wasmtime-component-macro 27.0.0": { + "wasmtime-component-macro 28.0.0": { "name": "wasmtime-component-macro", - "version": "27.0.0", + "version": "28.0.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/wasmtime-component-macro/27.0.0/download", - "sha256": "e118acbd2bc09b32ad8606bc7cef793bf5019c1b107772e64dc6c76b5055d40b" + "url": "https://static.crates.io/crates/wasmtime-component-macro/28.0.0/download", + "sha256": "eb5c0a77c9e1927c3d471f53cc13767c3d3438e5d5ffd394e3eb31c86445fd60" } }, "targets": [ @@ -80995,26 +84337,26 @@ "target": "syn" }, { - "id": "wasmtime-component-macro 27.0.0", + "id": "wasmtime-component-macro 28.0.0", "target": "build_script_build" }, { - "id": "wasmtime-component-util 27.0.0", + "id": "wasmtime-component-util 28.0.0", "target": "wasmtime_component_util" }, { - "id": "wasmtime-wit-bindgen 27.0.0", + "id": "wasmtime-wit-bindgen 28.0.0", "target": "wasmtime_wit_bindgen" }, { - "id": "wit-parser 0.219.1", + "id": "wit-parser 0.221.2", "target": "wit_parser" } ], "selects": {} }, "edition": "2021", - "version": "27.0.0" + "version": "28.0.0" }, "build_script_attrs": { "compile_data_glob": [ @@ -81030,14 +84372,14 @@ ], "license_file": null }, - "wasmtime-component-util 27.0.0": { + "wasmtime-component-util 28.0.0": { "name": "wasmtime-component-util", - "version": "27.0.0", + "version": "28.0.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/wasmtime-component-util/27.0.0/download", - "sha256": "4a6db4f3ee18c699629eabb9c64e77efe5a93a5137f098db7cab295037ba41c2" + "url": "https://static.crates.io/crates/wasmtime-component-util/28.0.0/download", + "sha256": "43702ca98bf5162eca0573db691ed9ecd36d716f8c6688410fe26ec16b6f9bcb" } }, "targets": [ @@ -81060,7 +84402,7 @@ "**" ], "edition": "2021", - "version": "27.0.0" + "version": "28.0.0" }, "license": "Apache-2.0 WITH LLVM-exception", "license_ids": [ @@ -81068,14 +84410,14 @@ ], "license_file": null }, - "wasmtime-cranelift 27.0.0": { + "wasmtime-cranelift 28.0.0": { "name": "wasmtime-cranelift", - "version": "27.0.0", + "version": "28.0.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/wasmtime-cranelift/27.0.0/download", - "sha256": "8b87e6c78f562b50aff1afd87ff32a57e241424c846c1c8f3c5fd352d2d62906" + "url": "https://static.crates.io/crates/wasmtime-cranelift/28.0.0/download", + "sha256": "20070aa5b75080a8932ec328419faf841df2bc6ceb16b55b0df2b952098392a2" } }, "targets": [ @@ -81115,23 +84457,23 @@ "target": "cfg_if" }, { - "id": "cranelift-codegen 0.114.0", + "id": "cranelift-codegen 0.115.0", "target": "cranelift_codegen" }, { - "id": "cranelift-control 0.114.0", + "id": "cranelift-control 0.115.0", "target": "cranelift_control" }, { - "id": "cranelift-entity 0.114.0", + "id": "cranelift-entity 0.115.0", "target": "cranelift_entity" }, { - "id": "cranelift-frontend 0.114.0", + "id": "cranelift-frontend 0.115.0", "target": "cranelift_frontend" }, { - "id": "cranelift-native 0.114.0", + "id": "cranelift-native 0.115.0", "target": "cranelift_native" }, { @@ -81147,7 +84489,7 @@ "target": "log" }, { - "id": "object 0.36.1", + "id": "object 0.36.7", "target": "object" }, { @@ -81163,11 +84505,11 @@ "target": "thiserror" }, { - "id": "wasmparser 0.219.1", + "id": "wasmparser 0.221.2", "target": "wasmparser" }, { - "id": "wasmtime-environ 27.0.0", + "id": "wasmtime-environ 28.0.0", "target": "wasmtime_environ" } ], @@ -81177,13 +84519,13 @@ "proc_macro_deps": { "common": [ { - "id": "wasmtime-versioned-export-macros 27.0.0", + "id": "wasmtime-versioned-export-macros 28.0.0", "target": "wasmtime_versioned_export_macros" } ], "selects": {} }, - "version": "27.0.0" + "version": "28.0.0" }, "license": "Apache-2.0 WITH LLVM-exception", "license_ids": [ @@ -81191,14 +84533,14 @@ ], "license_file": "LICENSE" }, - "wasmtime-environ 27.0.0": { + "wasmtime-environ 28.0.0": { "name": "wasmtime-environ", - "version": "27.0.0", + "version": "28.0.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/wasmtime-environ/27.0.0/download", - "sha256": "c25bfeaa16432d59a0706e2463d315ef4c9ebcfaf5605670b99d46373bdf9f27" + "url": "https://static.crates.io/crates/wasmtime-environ/28.0.0/download", + "sha256": "2604ddb24879d4dc1dedcb7081d7a8e017259bce916fdae097a97db52cbaab80" } }, "targets": [ @@ -81236,11 +84578,11 @@ "target": "anyhow" }, { - "id": "cranelift-bitset 0.114.0", + "id": "cranelift-bitset 0.115.0", "target": "cranelift_bitset" }, { - "id": "cranelift-entity 0.114.0", + "id": "cranelift-entity 0.115.0", "target": "cranelift_entity" }, { @@ -81256,7 +84598,7 @@ "target": "log" }, { - "id": "object 0.36.1", + "id": "object 0.36.7", "target": "object" }, { @@ -81264,7 +84606,7 @@ "target": "postcard" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -81276,15 +84618,15 @@ "target": "target_lexicon" }, { - "id": "wasm-encoder 0.219.1", + "id": "wasm-encoder 0.221.2", "target": "wasm_encoder" }, { - "id": "wasmparser 0.219.1", + "id": "wasmparser 0.221.2", "target": "wasmparser" }, { - "id": "wasmprinter 0.219.1", + "id": "wasmprinter 0.221.2", "target": "wasmprinter" } ], @@ -81294,13 +84636,13 @@ "proc_macro_deps": { "common": [ { - "id": "serde_derive 1.0.214", + "id": "serde_derive 1.0.217", "target": "serde_derive" } ], "selects": {} }, - "version": "27.0.0" + "version": "28.0.0" }, "license": "Apache-2.0 WITH LLVM-exception", "license_ids": [ @@ -81308,14 +84650,133 @@ ], "license_file": "LICENSE" }, - "wasmtime-jit-icache-coherence 27.0.0": { + "wasmtime-fiber 28.0.0": { + "name": "wasmtime-fiber", + "version": "28.0.0", + "package_url": "https://github.com/bytecodealliance/wasmtime", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/wasmtime-fiber/28.0.0/download", + "sha256": "98593412d2b167ebe2b59d4a17a184978a72f976b53b3a0ec05629451079ac1d" + } + }, + "targets": [ + { + "Library": { + "crate_name": "wasmtime_fiber", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + }, + { + "BuildScript": { + "crate_name": "build_script_build", + "crate_root": "build.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "wasmtime_fiber", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "anyhow 1.0.93", + "target": "anyhow" + }, + { + "id": "cfg-if 1.0.0", + "target": "cfg_if" + }, + { + "id": "wasmtime-asm-macros 28.0.0", + "target": "wasmtime_asm_macros" + }, + { + "id": "wasmtime-fiber 28.0.0", + "target": "build_script_build" + } + ], + "selects": { + "cfg(unix)": [ + { + "id": "rustix 0.38.32", + "target": "rustix" + } + ], + "cfg(windows)": [ + { + "id": "windows-sys 0.59.0", + "target": "windows_sys" + } + ] + } + }, + "edition": "2021", + "proc_macro_deps": { + "common": [ + { + "id": "wasmtime-versioned-export-macros 28.0.0", + "target": "wasmtime_versioned_export_macros" + } + ], + "selects": {} + }, + "version": "28.0.0" + }, + "build_script_attrs": { + "compile_data_glob": [ + "**" + ], + "data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "cc 1.1.37", + "target": "cc" + } + ], + "selects": {} + }, + "proc_macro_deps": { + "common": [ + { + "id": "wasmtime-versioned-export-macros 28.0.0", + "target": "wasmtime_versioned_export_macros" + } + ], + "selects": {} + } + }, + "license": "Apache-2.0 WITH LLVM-exception", + "license_ids": [ + "Apache-2.0" + ], + "license_file": "LICENSE" + }, + "wasmtime-jit-icache-coherence 28.0.0": { "name": "wasmtime-jit-icache-coherence", - "version": "27.0.0", + "version": "28.0.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/wasmtime-jit-icache-coherence/27.0.0/download", - "sha256": "91b218a92866f74f35162f5d03a4e0f62cd0e1cc624285b1014275e5d4575fad" + "url": "https://static.crates.io/crates/wasmtime-jit-icache-coherence/28.0.0/download", + "sha256": "d40d7722b9e1fbeae135715710a8a2570b1e6cf72b74dd653962d89831c6c70d" } }, "targets": [ @@ -81364,7 +84825,7 @@ } }, "edition": "2021", - "version": "27.0.0" + "version": "28.0.0" }, "license": "Apache-2.0 WITH LLVM-exception", "license_ids": [ @@ -81372,14 +84833,14 @@ ], "license_file": null }, - "wasmtime-slab 27.0.0": { + "wasmtime-slab 28.0.0": { "name": "wasmtime-slab", - "version": "27.0.0", + "version": "28.0.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/wasmtime-slab/27.0.0/download", - "sha256": "4d5f8acf677ee6b3b8ba400dd9753ea4769e56a95c4b30b045ac6d2d54b2f8ea" + "url": "https://static.crates.io/crates/wasmtime-slab/28.0.0/download", + "sha256": "8579c335220b4ece9aa490a0e8b46de78cd342b195ab21ff981d095e14b52383" } }, "targets": [ @@ -81402,7 +84863,7 @@ "**" ], "edition": "2021", - "version": "27.0.0" + "version": "28.0.0" }, "license": "Apache-2.0 WITH LLVM-exception", "license_ids": [ @@ -81410,14 +84871,14 @@ ], "license_file": null }, - "wasmtime-versioned-export-macros 27.0.0": { + "wasmtime-versioned-export-macros 28.0.0": { "name": "wasmtime-versioned-export-macros", - "version": "27.0.0", + "version": "28.0.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/wasmtime-versioned-export-macros/27.0.0/download", - "sha256": "df09be00c38f49172ca9936998938476e3f2df782673a39ae2ef9fb0838341b6" + "url": "https://static.crates.io/crates/wasmtime-versioned-export-macros/28.0.0/download", + "sha256": "d7de0a56fb0a69b185968f2d7a9ba54750920a806470dff7ad8de91ac06d277e" } }, "targets": [ @@ -81457,7 +84918,7 @@ "selects": {} }, "edition": "2021", - "version": "27.0.0" + "version": "28.0.0" }, "license": "Apache-2.0 WITH LLVM-exception", "license_ids": [ @@ -81465,14 +84926,93 @@ ], "license_file": null }, - "wasmtime-wit-bindgen 27.0.0": { + "wasmtime-winch 28.0.0": { + "name": "wasmtime-winch", + "version": "28.0.0", + "package_url": "https://github.com/bytecodealliance/wasmtime", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/wasmtime-winch/28.0.0/download", + "sha256": "abd309943c443f5590d12f9aba9ba63c481091c955a0a14de0c2a9e0e3aaeca9" + } + }, + "targets": [ + { + "Library": { + "crate_name": "wasmtime_winch", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "wasmtime_winch", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "anyhow 1.0.93", + "target": "anyhow" + }, + { + "id": "cranelift-codegen 0.115.0", + "target": "cranelift_codegen" + }, + { + "id": "gimli 0.31.1", + "target": "gimli" + }, + { + "id": "object 0.36.7", + "target": "object" + }, + { + "id": "target-lexicon 0.12.16", + "target": "target_lexicon" + }, + { + "id": "wasmparser 0.221.2", + "target": "wasmparser" + }, + { + "id": "wasmtime-cranelift 28.0.0", + "target": "wasmtime_cranelift" + }, + { + "id": "wasmtime-environ 28.0.0", + "target": "wasmtime_environ" + }, + { + "id": "winch-codegen 28.0.0", + "target": "winch_codegen" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "28.0.0" + }, + "license": "Apache-2.0 WITH LLVM-exception", + "license_ids": [ + "Apache-2.0" + ], + "license_file": "LICENSE" + }, + "wasmtime-wit-bindgen 28.0.0": { "name": "wasmtime-wit-bindgen", - "version": "27.0.0", + "version": "28.0.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/wasmtime-wit-bindgen/27.0.0/download", - "sha256": "bf3963c9c29df91564d8bd181eb00d0dbaeafa1b2a01e15952bb7391166b704e" + "url": "https://static.crates.io/crates/wasmtime-wit-bindgen/28.0.0/download", + "sha256": "969f83022dac3435d6469edb582ceed04cfe32aa44dc3ef16e5cb55574633df8" } }, "targets": [ @@ -81509,14 +85049,14 @@ "target": "indexmap" }, { - "id": "wit-parser 0.219.1", + "id": "wit-parser 0.221.2", "target": "wit_parser" } ], "selects": {} }, "edition": "2021", - "version": "27.0.0" + "version": "28.0.0" }, "license": "Apache-2.0 WITH LLVM-exception", "license_ids": [ @@ -81563,7 +85103,7 @@ "deps": { "common": [ { - "id": "bumpalo 3.14.0", + "id": "bumpalo 3.16.0", "target": "bumpalo" }, { @@ -82485,6 +86025,109 @@ ], "license_file": null }, + "winch-codegen 28.0.0": { + "name": "winch-codegen", + "version": "28.0.0", + "package_url": "https://github.com/bytecodealliance/wasmtime", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/winch-codegen/28.0.0/download", + "sha256": "9110decc2983ed94de904804dcd979ba59cbabc78a94fec6b1d8468ec513d0f6" + } + }, + "targets": [ + { + "Library": { + "crate_name": "winch_codegen", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + }, + { + "BuildScript": { + "crate_name": "build_script_build", + "crate_root": "build.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "winch_codegen", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "anyhow 1.0.93", + "target": "anyhow" + }, + { + "id": "cranelift-codegen 0.115.0", + "target": "cranelift_codegen" + }, + { + "id": "gimli 0.31.1", + "target": "gimli" + }, + { + "id": "regalloc2 0.11.1", + "target": "regalloc2" + }, + { + "id": "smallvec 1.13.2", + "target": "smallvec" + }, + { + "id": "target-lexicon 0.12.16", + "target": "target_lexicon" + }, + { + "id": "wasmparser 0.221.2", + "target": "wasmparser" + }, + { + "id": "wasmtime-cranelift 28.0.0", + "target": "wasmtime_cranelift" + }, + { + "id": "wasmtime-environ 28.0.0", + "target": "wasmtime_environ" + }, + { + "id": "winch-codegen 28.0.0", + "target": "build_script_build" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "28.0.0" + }, + "build_script_attrs": { + "compile_data_glob": [ + "**" + ], + "data_glob": [ + "**" + ] + }, + "license": "Apache-2.0 WITH LLVM-exception", + "license_ids": [ + "Apache-2.0" + ], + "license_file": "LICENSE" + }, "windows-core 0.52.0": { "name": "windows-core", "version": "0.52.0", @@ -82970,6 +86613,7 @@ "Win32_Networking", "Win32_Networking_WinSock", "Win32_Security", + "Win32_Security_Credentials", "Win32_Storage", "Win32_Storage_FileSystem", "Win32_System", @@ -84939,14 +88583,14 @@ ], "license_file": "LICENSE" }, - "wit-parser 0.219.1": { + "wit-parser 0.221.2": { "name": "wit-parser", - "version": "0.219.1", + "version": "0.221.2", "package_url": "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wit-parser", "repository": { "Http": { - "url": "https://static.crates.io/crates/wit-parser/0.219.1/download", - "sha256": "4a86f669283257e8e424b9a4fc3518e3ade0b95deb9fbc0f93a1876be3eda598" + "url": "https://static.crates.io/crates/wit-parser/0.221.2/download", + "sha256": "fbe1538eea6ea5ddbe5defd0dc82539ad7ba751e1631e9185d24a931f0a5adc8" } }, "targets": [ @@ -85000,7 +88644,7 @@ "target": "semver" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -85012,7 +88656,7 @@ "target": "unicode_xid" }, { - "id": "wasmparser 0.219.1", + "id": "wasmparser 0.221.2", "target": "wasmparser" } ], @@ -85022,13 +88666,13 @@ "proc_macro_deps": { "common": [ { - "id": "serde_derive 1.0.214", + "id": "serde_derive 1.0.217", "target": "serde_derive" } ], "selects": {} }, - "version": "0.219.1" + "version": "0.221.2" }, "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", "license_ids": [ @@ -85115,7 +88759,9 @@ "version": "0.5.5" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "wsl 0.1.0": { @@ -85240,7 +88886,7 @@ "target": "data_encoding" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -85778,7 +89424,9 @@ "version": "0.7.4" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "yoke-derive 0.7.4": { @@ -85835,7 +89483,9 @@ "version": "0.7.4" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "zerocopy 0.7.32": { @@ -86001,7 +89651,9 @@ "version": "0.1.4" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "zerofrom-derive 0.1.4": { @@ -86058,7 +89710,9 @@ "version": "0.1.4" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "zeroize 1.8.1": { @@ -86236,7 +89890,9 @@ "version": "0.10.4" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "zerovec-derive 0.10.3": { @@ -86289,7 +89945,9 @@ "version": "0.10.3" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "zstd 0.13.2": { @@ -86509,7 +90167,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" }, { @@ -86547,9 +90205,6 @@ "aarch64-apple-ios-sim": [ "aarch64-apple-ios-sim" ], - "aarch64-fuchsia": [ - "aarch64-fuchsia" - ], "aarch64-linux-android": [ "aarch64-linux-android" ], @@ -86557,6 +90212,9 @@ "aarch64-pc-windows-msvc": [ "aarch64-pc-windows-msvc" ], + "aarch64-unknown-fuchsia": [ + "aarch64-unknown-fuchsia" + ], "aarch64-unknown-linux-gnu": [ "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu" @@ -86599,10 +90257,10 @@ "cfg(all(not(curve25519_dalek_backend = \"fiat\"), not(curve25519_dalek_backend = \"serial\"), target_arch = \"x86_64\"))": [ "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-pc-windows-msvc", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu", "x86_64-unknown-none" @@ -86620,8 +90278,8 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", + "aarch64-unknown-fuchsia", "aarch64-unknown-nto-qnx710", "armv7-linux-androideabi", "i686-apple-darwin", @@ -86634,12 +90292,12 @@ "thumbv7em-none-eabi", "thumbv8m.main-none-eabi", "wasm32-unknown-unknown", - "wasm32-wasi", + "wasm32-wasip1", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-none" ], "cfg(all(target_arch = \"aarch64\", target_env = \"msvc\", not(windows_raw_dylib)))": [ @@ -86664,7 +90322,7 @@ "wasm32-unknown-unknown" ], "cfg(all(target_arch = \"wasm32\", target_os = \"wasi\"))": [ - "wasm32-wasi" + "wasm32-wasip1" ], "cfg(all(target_arch = \"wasm32\", target_vendor = \"unknown\", target_os = \"unknown\", target_env = \"\"))": [ "wasm32-unknown-unknown" @@ -86692,8 +90350,8 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -86708,14 +90366,14 @@ "s390x-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu" ], "cfg(all(unix, not(target_os = \"android\"), not(target_vendor = \"apple\"), not(target_arch = \"wasm32\")))": [ - "aarch64-fuchsia", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -86725,16 +90383,16 @@ "i686-unknown-linux-gnu", "powerpc-unknown-linux-gnu", "s390x-unknown-linux-gnu", - "x86_64-fuchsia", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu" ], "cfg(all(unix, not(target_os = \"macos\")))": [ "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -86747,9 +90405,9 @@ "powerpc-unknown-linux-gnu", "s390x-unknown-linux-gnu", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu" ], @@ -86758,9 +90416,9 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", "aarch64-pc-windows-msvc", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -86776,10 +90434,10 @@ "thumbv8m.main-none-eabi", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-pc-windows-msvc", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu", "x86_64-unknown-none" @@ -86788,9 +90446,9 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", "aarch64-pc-windows-msvc", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -86801,10 +90459,10 @@ "i686-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-pc-windows-msvc", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu", "x86_64-unknown-none" @@ -86813,9 +90471,9 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", "aarch64-pc-windows-msvc", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -86826,10 +90484,10 @@ "i686-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-pc-windows-msvc", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu", "x86_64-unknown-none" @@ -86846,17 +90504,17 @@ "i686-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-pc-windows-msvc", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu", "x86_64-unknown-none" ], "cfg(any(target_arch = \"x86\", target_arch = \"x86_64\", all(any(target_arch = \"aarch64\", target_arch = \"arm\"), any(target_os = \"android\", target_os = \"fuchsia\", target_os = \"linux\"))))": [ - "aarch64-fuchsia", "aarch64-linux-android", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "arm-unknown-linux-gnueabi", @@ -86869,10 +90527,10 @@ "i686-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-pc-windows-msvc", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu", "x86_64-unknown-none" @@ -86885,10 +90543,10 @@ "i686-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-pc-windows-msvc", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu", "x86_64-unknown-none" @@ -86931,9 +90589,9 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", "aarch64-pc-windows-msvc", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "arm-unknown-linux-gnueabi", @@ -86946,13 +90604,13 @@ "i686-unknown-linux-gnu", "powerpc-unknown-linux-gnu", "s390x-unknown-linux-gnu", - "wasm32-wasi", + "wasm32-wasip1", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-pc-windows-msvc", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu" ], @@ -87006,8 +90664,8 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -87022,9 +90680,9 @@ "s390x-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu" ], @@ -87032,8 +90690,8 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -87048,9 +90706,9 @@ "s390x-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu" ], @@ -87058,8 +90716,8 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -87072,12 +90730,12 @@ "i686-unknown-linux-gnu", "powerpc-unknown-linux-gnu", "s390x-unknown-linux-gnu", - "wasm32-wasi", + "wasm32-wasip1", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu" ], @@ -87085,9 +90743,9 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", "aarch64-pc-windows-msvc", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -87103,10 +90761,10 @@ "s390x-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-pc-windows-msvc", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu" ], @@ -87115,9 +90773,9 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", "aarch64-pc-windows-msvc", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -87134,13 +90792,13 @@ "riscv64gc-unknown-none-elf", "s390x-unknown-linux-gnu", "wasm32-unknown-unknown", - "wasm32-wasi", + "wasm32-wasip1", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-pc-windows-msvc", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu", "x86_64-unknown-none" @@ -87149,8 +90807,8 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -87168,19 +90826,19 @@ "thumbv7em-none-eabi", "thumbv8m.main-none-eabi", "wasm32-unknown-unknown", - "wasm32-wasi", + "wasm32-wasip1", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu", "x86_64-unknown-none" ], "cfg(not(any(target_os = \"macos\", target_os = \"ios\", target_os = \"windows\", target_arch = \"wasm32\")))": [ - "aarch64-fuchsia", "aarch64-linux-android", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -87196,9 +90854,9 @@ "s390x-unknown-linux-gnu", "thumbv7em-none-eabi", "thumbv8m.main-none-eabi", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu", "x86_64-unknown-none" @@ -87207,8 +90865,8 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -87227,9 +90885,9 @@ "thumbv8m.main-none-eabi", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu", "x86_64-unknown-none" @@ -87238,8 +90896,8 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -87256,12 +90914,12 @@ "s390x-unknown-linux-gnu", "thumbv7em-none-eabi", "thumbv8m.main-none-eabi", - "wasm32-wasi", + "wasm32-wasip1", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu", "x86_64-unknown-none" @@ -87270,9 +90928,9 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", "aarch64-pc-windows-msvc", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -87292,10 +90950,10 @@ "thumbv8m.main-none-eabi", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-pc-windows-msvc", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu", "x86_64-unknown-none" @@ -87304,9 +90962,9 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", "aarch64-pc-windows-msvc", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -87326,10 +90984,10 @@ "thumbv8m.main-none-eabi", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-pc-windows-msvc", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu", "x86_64-unknown-none" @@ -87338,8 +90996,8 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -87357,12 +91015,12 @@ "thumbv7em-none-eabi", "thumbv8m.main-none-eabi", "wasm32-unknown-unknown", - "wasm32-wasi", + "wasm32-wasip1", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu", "x86_64-unknown-none" @@ -87371,9 +91029,9 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", "aarch64-pc-windows-msvc", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -87392,13 +91050,13 @@ "thumbv7em-none-eabi", "thumbv8m.main-none-eabi", "wasm32-unknown-unknown", - "wasm32-wasi", + "wasm32-wasip1", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-pc-windows-msvc", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu", "x86_64-unknown-none" @@ -87408,16 +91066,16 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", "aarch64-pc-windows-msvc", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710" ], "cfg(target_arch = \"wasm32\")": [ "wasm32-unknown-unknown", - "wasm32-wasi" + "wasm32-wasip1" ], "cfg(target_arch = \"x86\")": [ "i686-apple-darwin", @@ -87429,10 +91087,10 @@ "cfg(target_arch = \"x86_64\")": [ "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-pc-windows-msvc", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu", "x86_64-unknown-none" @@ -87453,8 +91111,8 @@ "cfg(target_os = \"cloudabi\")": [], "cfg(target_os = \"dragonfly\")": [], "cfg(target_os = \"fuchsia\")": [ - "aarch64-fuchsia", - "x86_64-fuchsia" + "aarch64-unknown-fuchsia", + "x86_64-unknown-fuchsia" ], "cfg(target_os = \"haiku\")": [], "cfg(target_os = \"hermit\")": [], @@ -87481,7 +91139,7 @@ ], "cfg(target_os = \"redox\")": [], "cfg(target_os = \"wasi\")": [ - "wasm32-wasi" + "wasm32-wasip1" ], "cfg(target_os = \"windows\")": [ "aarch64-pc-windows-msvc", @@ -87493,8 +91151,8 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -87509,9 +91167,9 @@ "s390x-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu" ], @@ -87560,8 +91218,8 @@ "wasm32-unknown-unknown": [ "wasm32-unknown-unknown" ], - "wasm32-wasi": [ - "wasm32-wasi" + "wasm32-wasip1": [ + "wasm32-wasip1" ], "x86_64-apple-darwin": [ "x86_64-apple-darwin" @@ -87569,9 +91227,6 @@ "x86_64-apple-ios": [ "x86_64-apple-ios" ], - "x86_64-fuchsia": [ - "x86_64-fuchsia" - ], "x86_64-linux-android": [ "x86_64-linux-android" ], @@ -87583,6 +91238,9 @@ "x86_64-unknown-freebsd": [ "x86_64-unknown-freebsd" ], + "x86_64-unknown-fuchsia": [ + "x86_64-unknown-fuchsia" + ], "x86_64-unknown-linux-gnu": [ "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu" @@ -87626,8 +91284,8 @@ "bip32 0.5.1", "bit-vec 0.6.3", "bitcoin 0.28.2", - "bitcoin 0.32.2", - "bitcoincore-rpc 0.15.0", + "bitcoin 0.32.5", + "bitcoincore-rpc 0.19.0", "bitcoind 0.32.0", "bitflags 1.3.2", "bs58 0.5.0", @@ -87640,10 +91298,10 @@ "cached 0.49.2", "canbench 0.1.8", "canbench-rs 0.1.8", - "candid 0.10.10", + "candid 0.10.13", "candid_parser 0.1.2", "cargo_metadata 0.14.2", - "cc 1.0.83", + "cc 1.1.37", "cddl 0.9.4", "cfg-if 1.0.0", "chacha20poly1305 0.10.1", @@ -87665,6 +91323,7 @@ "curve25519-dalek 4.1.3", "cvt 0.1.2", "dashmap 5.5.3", + "dfx-core 0.1.3", "dyn-clone 1.0.14", "ed25519-dalek 2.1.1", "educe 0.4.23", @@ -87700,27 +91359,29 @@ "hyper-rustls 0.27.3", "hyper-socks2 0.9.1", "hyper-util 0.1.10", - "ic-agent 0.37.1", + "ic-agent 0.39.2", "ic-bn-lib 0.1.0", "ic-btc-interface 0.2.2", "ic-canister-log 0.2.0", - "ic-canister-sig-creation 1.0.1", - "ic-cbor 2.6.0", + "ic-canister-sig-creation 1.1.0", + "ic-cbor 3.0.2", "ic-cdk 0.16.0", + "ic-cdk 0.18.0-alpha.1", "ic-cdk-macros 0.9.0", + "ic-cdk-macros 0.18.0-alpha.1", "ic-cdk-timers 0.11.0", - "ic-certificate-verification 2.6.0", - "ic-certification 2.6.0", + "ic-certificate-verification 3.0.2", + "ic-certification 3.0.2", "ic-certified-map 0.3.4", - "ic-http-certification 2.6.0", - "ic-http-gateway 0.0.0", + "ic-http-certification 3.0.2", + "ic-http-gateway 0.1.0", "ic-metrics-encoder 1.1.1", - "ic-response-verification 2.6.0", + "ic-response-verification 3.0.2", "ic-sha3 1.0.0", "ic-stable-structures 0.6.5", "ic-test-state-machine-client 3.0.1", - "ic-transport-types 0.37.1", - "ic-utils 0.37.0", + "ic-transport-types 0.39.2", + "ic-utils 0.39.2", "ic-verify-bls-signature 0.6.0", "ic-wasm 0.8.4", "ic-xrc-types 1.2.0", @@ -87737,7 +91398,7 @@ "insta 1.34.0", "instant-acme 0.7.2", "intmap 1.1.0", - "ipnet 2.8.0", + "ipnet 2.10.1", "isocountry 0.3.2", "itertools 0.12.0", "json-patch 0.2.7", @@ -87780,7 +91441,7 @@ "opentelemetry 0.27.0", "opentelemetry-otlp 0.27.0", "opentelemetry-prometheus 0.13.0", - "opentelemetry_sdk 0.27.0", + "opentelemetry_sdk 0.27.1", "p256 0.13.2", "pairing 0.23.0", "parking_lot 0.12.1", @@ -87844,7 +91505,7 @@ "scraper 0.17.1", "secp256k1 0.22.2", "semver 1.0.22", - "serde 1.0.214", + "serde 1.0.217", "serde-bytes-repr 0.1.5", "serde_bytes 0.11.15", "serde_cbor 0.11.2", @@ -87872,6 +91533,7 @@ "stubborn-io 0.3.2", "subtle 2.6.1", "syn 1.0.109", + "syscalls 0.6.18", "tar 0.4.39", "tarpc 0.34.0", "tempfile 3.12.0", @@ -87890,6 +91552,7 @@ "tokio-rustls 0.26.0", "tokio-serde 0.8.0", "tokio-socks 0.5.2", + "tokio-stream 0.1.17", "tokio-test 0.4.4", "tokio-util 0.7.13", "toml 0.5.11", @@ -87919,8 +91582,8 @@ "wasm-smith 0.212.0", "wasmparser 0.217.0", "wasmprinter 0.217.0", - "wasmtime 27.0.0", - "wasmtime-environ 27.0.0", + "wasmtime 28.0.0", + "wasmtime-environ 28.0.0", "wast 212.0.0", "wat 1.212.0", "wee_alloc 0.4.5", @@ -87933,5 +91596,12 @@ "zeroize 1.8.1", "zstd 0.13.2" ], - "direct_dev_deps": [] + "direct_dev_deps": [], + "unused_patches": [ + { + "name": "jsonrpc", + "version": "0.12.1", + "source": "git+https://github.com/apoelstra/rust-jsonrpc?rev=e42044d#e42044d8e0896317488dfbd65eb5563d76e937fe" + } + ] } diff --git a/Cargo.Bazel.Fuzzing.toml.lock b/Cargo.Bazel.Fuzzing.toml.lock index 7d84abcd087..d635973c382 100644 --- a/Cargo.Bazel.Fuzzing.toml.lock +++ b/Cargo.Bazel.Fuzzing.toml.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "abnf" @@ -257,6 +257,31 @@ dependencies = [ "generic-array", ] +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if 1.0.0", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + [[package]] name = "ahash" version = "0.7.8" @@ -337,9 +362,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.16" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "android-tzdata" @@ -432,6 +457,17 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" +[[package]] +name = "argon2" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db4ce4441f99dbd377ca8a8f57b698c44d0d6e712d8329b5040da5a64aa1ce73" +dependencies = [ + "base64ct", + "blake2", + "password-hash", +] + [[package]] name = "arrayvec" version = "0.5.2" @@ -570,6 +606,17 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", +] + [[package]] name = "async-channel" version = "2.3.1" @@ -723,6 +770,15 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "async-watch" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a078faf4e27c0c6cc0efb20e5da59dcccc04968ebf2801d8e0b2195124cdcdb2" +dependencies = [ + "event-listener 2.5.3", +] + [[package]] name = "async-web-client" version = "0.6.2" @@ -964,6 +1020,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d27c3610c36aee21ce8ac510e6224498de4228ad772a171ed65643a24693a5a8" +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + [[package]] name = "base16ct" version = "0.2.0" @@ -1125,17 +1187,35 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "bip32" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b30ed1d6f8437a487a266c8293aeb95b61a23261273e3e02912cdb8b68bf798b" +dependencies = [ + "bs58 0.4.0", + "hmac", + "k256 0.11.6", + "once_cell", + "pbkdf2 0.11.0", + "rand_core 0.6.4", + "ripemd", + "sha2 0.10.8", + "subtle", + "zeroize", +] + [[package]] name = "bip32" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e141fb0f8be1c7b45887af94c88b182472b57c96b56773250ae00cd6a14a164" dependencies = [ - "bs58", + "bs58 0.5.0", "hmac", - "k256", + "k256 0.13.4", "once_cell", - "pbkdf2", + "pbkdf2 0.12.2", "rand_core 0.6.4", "ripemd", "sha2 0.10.8", @@ -1186,9 +1266,9 @@ dependencies = [ [[package]] name = "bitcoin" -version = "0.32.2" +version = "0.32.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea507acc1cd80fc084ace38544bbcf7ced7c2aa65b653b102de0ce718df668f6" +checksum = "ce6bc65742dea50536e35ad42492b234c27904a27f0abdcbce605015cb4ea026" dependencies = [ "base58ck", "bech32 0.11.0", @@ -1199,6 +1279,7 @@ dependencies = [ "hex-conservative", "hex_lit", "secp256k1 0.29.0", + "serde", ] [[package]] @@ -1206,6 +1287,9 @@ name = "bitcoin-internals" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30bdbe14aa07b06e6cfeffc529a1f099e5fbe249524f8125358604df99a4bed2" +dependencies = [ + "serde", +] [[package]] name = "bitcoin-io" @@ -1226,6 +1310,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5285c8bcaa25876d07f37e3d30c303f2609179716e11d688f51e8f1fe70063e2" dependencies = [ "bitcoin-internals", + "serde", ] [[package]] @@ -1255,16 +1340,17 @@ checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" dependencies = [ "bitcoin-io", "hex-conservative", + "serde", ] [[package]] name = "bitcoincore-rpc" -version = "0.15.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0e67dbf7a9971e7f4276f6089e9e814ce0f624a03216b7d92d00351ae7fb3e" +checksum = "aedd23ae0fd321affb4bbbc36126c6f49a32818dc6b979395d24da8c9d4e80ee" dependencies = [ "bitcoincore-rpc-json", - "jsonrpc 0.12.1", + "jsonrpc 0.18.0", "log", "serde", "serde_json", @@ -1272,11 +1358,11 @@ dependencies = [ [[package]] name = "bitcoincore-rpc-json" -version = "0.15.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e2ae16202721ba8c3409045681fac790a5ddc791f05731a2df22c0c6bffc0f1" +checksum = "d8909583c5fab98508e80ef73e5592a651c954993dc6b7739963257d19f0e71a" dependencies = [ - "bitcoin 0.28.2", + "bitcoin 0.32.5", "serde", "serde_json", ] @@ -1318,6 +1404,15 @@ dependencies = [ "wyz", ] +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "block-buffer" version = "0.9.0" @@ -1342,7 +1437,7 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" dependencies = [ - "async-channel", + "async-channel 2.3.1", "async-task", "futures-io", "futures-lite", @@ -1425,6 +1520,15 @@ dependencies = [ "alloc-stdlib", ] +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" +dependencies = [ + "sha2 0.9.9", +] + [[package]] name = "bs58" version = "0.5.0" @@ -1507,9 +1611,12 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +dependencies = [ + "allocator-api2", +] [[package]] name = "by_address" @@ -1610,13 +1717,10 @@ checksum = "4964518bd3b4a8190e832886cdc0da9794f12e8e6c1613a9e90ff331c4c8724b" [[package]] name = "cached" -version = "0.47.0" +version = "0.49.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69b0116662497bc24e4b177c90eaf8870e39e2714c3fcfa296327a93f593fc21" +checksum = "f251fd1e72720ca07bf5d8e310f54a193fd053479a1f6342c6663ee4fa01cf96" dependencies = [ - "ahash 0.8.11", - "cached_proc_macro", - "cached_proc_macro_types", "hashbrown 0.14.5", "instant", "once_cell", @@ -1625,10 +1729,11 @@ dependencies = [ [[package]] name = "cached" -version = "0.49.2" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f251fd1e72720ca07bf5d8e310f54a193fd053479a1f6342c6663ee4fa01cf96" +checksum = "a8466736fe5dbcaf8b8ee24f9bbefe43c884dc3e9ff7178da70f55bffca1133c" dependencies = [ + "ahash 0.8.11", "hashbrown 0.14.5", "instant", "once_cell", @@ -1637,34 +1742,36 @@ dependencies = [ [[package]] name = "cached" -version = "0.52.0" +version = "0.54.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8466736fe5dbcaf8b8ee24f9bbefe43c884dc3e9ff7178da70f55bffca1133c" +checksum = "9718806c4a2fe9e8a56fd736f97b340dd10ed1be8ed733ed50449f351dc33cae" dependencies = [ "ahash 0.8.11", + "cached_proc_macro", + "cached_proc_macro_types", "hashbrown 0.14.5", - "instant", "once_cell", "thiserror 1.0.68", + "web-time", ] [[package]] name = "cached_proc_macro" -version = "0.18.1" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c878c71c2821aa2058722038a59a67583a4240524687c6028571c9b395ded61f" +checksum = "2f42a145ed2d10dce2191e1dcf30cfccfea9026660e143662ba5eec4017d5daa" dependencies = [ - "darling 0.14.4", + "darling 0.20.10", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.87", ] [[package]] name = "cached_proc_macro_types" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a4f925191b4367301851c6d99b09890311d74b0d43f274c0b34c86d308a3663" +checksum = "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0" [[package]] name = "camino" @@ -1722,9 +1829,9 @@ dependencies = [ [[package]] name = "candid" -version = "0.10.10" +version = "0.10.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c30ee7f886f296b6422c0ff017e89dd4f831521dfdcc76f3f71aae1ce817222" +checksum = "a253bab4a9be502c82332b60cbeee6202ad0692834efeec95fae9f29db33d692" dependencies = [ "anyhow", "binread", @@ -1804,12 +1911,13 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.83" +version = "1.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "40545c26d092346d8a8dab71ee48e7685a7a9cba76e634790c215b41a4a7b4cf" dependencies = [ "jobserver", "libc", + "shlex", ] [[package]] @@ -1969,7 +2077,7 @@ checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" dependencies = [ "glob", "libc", - "libloading", + "libloading 0.7.4", ] [[package]] @@ -2289,6 +2397,16 @@ dependencies = [ "libc", ] +[[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" @@ -2341,18 +2459,18 @@ dependencies = [ [[package]] name = "cranelift-bforest" -version = "0.114.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ba4f80548f22dc9c43911907b5e322c5555544ee85f785115701e6a28c9abe1" +checksum = "ac89549be94911dd0e839b4a7db99e9ed29c17517e1c026f61066884c168aa3c" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-bitset" -version = "0.114.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "005884e3649c3e5ff2dc79e8a94b138f11569cc08a91244a292714d2a86e9156" +checksum = "b9bd49369f76c77e34e641af85d0956869237832c118964d08bf5f51f210875a" dependencies = [ "serde", "serde_derive", @@ -2360,9 +2478,9 @@ dependencies = [ [[package]] name = "cranelift-codegen" -version = "0.114.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4036255ec33ce9a37495dfbcfc4e1118fd34e693eff9a1e106336b7cd16a9b" +checksum = "fd96ce9cf8efebd7f5ab8ced5a0ce44250280bbae9f593d74a6d7effc3582a35" dependencies = [ "bumpalo", "cranelift-bforest", @@ -2384,33 +2502,33 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.114.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7ca74f4b68319da11d39e894437cb6e20ec7c2e11fbbda823c3bf207beedff7" +checksum = "5a68e358827afe4bfb6239fcbf6fbd5ac56206ece8a99c8f5f9bbd518773281a" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.114.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897e54f433a0269c4187871aa06d452214d5515d228d5bdc22219585e9eef895" +checksum = "e184c9767afbe73d50c55ec29abcf4c32f9baf0d9d22b86d58c4d55e06dee181" [[package]] name = "cranelift-control" -version = "0.114.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29cb4018f5bf59fb53f515fa9d80e6f8c5ce19f198dc538984ebd23ecf8965ec" +checksum = "5cc7664f2a66f053e33f149e952bb5971d138e3af637f5097727ed6dc0ed95dd" dependencies = [ "arbitrary", ] [[package]] name = "cranelift-entity" -version = "0.114.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "305399fd781a2953ac78c1396f02ff53144f39c33eb7fc7789cf4e8936d13a96" +checksum = "118597e3a9cf86c3556fa579a7a23b955fa18231651a52a77a2475d305a9cf84" dependencies = [ "cranelift-bitset", "serde", @@ -2419,9 +2537,9 @@ dependencies = [ [[package]] name = "cranelift-frontend" -version = "0.114.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9230b460a128d53653456137751d27baf567947a3ab8c0c4d6e31fd08036d81e" +checksum = "7638ea1efb069a0aa18d8ee67401b6b0d19f6bfe5de5e9ede348bfc80bb0d8c7" dependencies = [ "cranelift-codegen", "log", @@ -2431,15 +2549,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.114.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b961e24ae3ec9813a24a15ae64bbd2a42e4de4d79a7f3225a412e3b94e78d1c8" +checksum = "15c53e1152a0b01c4ed2b1e0535602b8e86458777dd9d18b28732b16325c7dc0" [[package]] name = "cranelift-native" -version = "0.114.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d5bd76df6c9151188dfa428c863b33da5b34561b67f43c0cf3f24a794f9fa1f" +checksum = "7b7d8f895444fa52dd7bdd0bed11bf007a7fb43af65a6deac8fcc4094c6372f7" dependencies = [ "cranelift-codegen", "libc", @@ -2580,6 +2698,18 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + [[package]] name = "crypto-bigint" version = "0.5.3" @@ -2647,6 +2777,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + [[package]] name = "ctrlc" version = "3.4.5" @@ -2668,7 +2807,7 @@ dependencies = [ "curve25519-dalek-derive", "digest 0.10.7", "fiat-crypto", - "group", + "group 0.13.0", "rand_core 0.6.4", "rustc_version", "subtle", @@ -2720,22 +2859,12 @@ dependencies = [ [[package]] name = "darling" -version = "0.14.4" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ - "darling_core 0.14.4", - "darling_macro 0.14.4", -] - -[[package]] -name = "darling" -version = "0.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" -dependencies = [ - "darling_core 0.20.3", - "darling_macro 0.20.3", + "darling_core 0.20.10", + "darling_macro 0.20.10", ] [[package]] @@ -2754,29 +2883,15 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim 0.10.0", - "syn 1.0.109", -] - -[[package]] -name = "darling_core" -version = "0.20.3" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "strsim 0.10.0", + "strsim 0.11.1", "syn 2.0.87", ] @@ -2793,22 +2908,11 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.14.4" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ - "darling_core 0.14.4", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "darling_macro" -version = "0.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" -dependencies = [ - "darling_core 0.20.3", + "darling_core 0.20.10", "quote", "syn 2.0.87", ] @@ -2852,6 +2956,31 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" +[[package]] +name = "dbus" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" +dependencies = [ + "libc", + "libdbus-sys", + "winapi 0.3.9", +] + +[[package]] +name = "dbus-secret-service" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42a16374481d92aed73ae45b1f120207d8e71d24fb89f357fadbd8f946fd84b" +dependencies = [ + "dbus", + "futures-util", + "num", + "once_cell", + "openssl", + "rand 0.8.5", +] + [[package]] name = "debugid" version = "0.8.0" @@ -2861,6 +2990,17 @@ dependencies = [ "uuid", ] +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", + "pem-rfc7468 0.6.0", + "zeroize", +] + [[package]] name = "der" version = "0.7.8" @@ -2870,7 +3010,7 @@ dependencies = [ "const-oid", "der_derive", "flagset", - "pem-rfc7468", + "pem-rfc7468 0.7.0", "zeroize", ] @@ -2944,6 +3084,64 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "dfx-core" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4be4fc6929580b0ef8ac3678c03f31b59ffb1aa0d90ab754842d40cfc5b4552" +dependencies = [ + "aes-gcm", + "argon2", + "backoff", + "bip32 0.4.0", + "byte-unit", + "bytes", + "candid", + "clap 4.5.20", + "dialoguer", + "directories-next", + "dunce", + "flate2", + "handlebars", + "hex", + "humantime-serde", + "ic-agent", + "ic-identity-hsm", + "ic-utils", + "itertools 0.10.5", + "k256 0.11.6", + "keyring", + "lazy_static", + "reqwest 0.12.9", + "ring 0.16.20", + "schemars", + "sec1 0.3.0", + "semver", + "serde", + "serde_json", + "sha2 0.10.8", + "slog", + "tar", + "tempfile", + "thiserror 1.0.68", + "time", + "tiny-bip39", + "url", +] + +[[package]] +name = "dialoguer" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de" +dependencies = [ + "console 0.15.7", + "shell-words", + "tempfile", + "thiserror 1.0.68", + "zeroize", +] + [[package]] name = "diff" version = "0.1.13" @@ -3007,14 +3205,14 @@ dependencies = [ "bech32 0.9.1", "bincode", "bindgen 0.65.1", - "bip32", + "bip32 0.5.1", "bit-vec", "bitcoin 0.28.2", - "bitcoin 0.32.2", + "bitcoin 0.32.5", "bitcoincore-rpc", "bitcoind", "bitflags 1.3.2", - "bs58", + "bs58 0.5.0", "build-info", "build-info-build", "by_address", @@ -3049,6 +3247,7 @@ dependencies = [ "curve25519-dalek", "cvt", "dashmap 5.5.3", + "dfx-core", "dyn-clone", "ed25519-dalek", "educe", @@ -3069,7 +3268,7 @@ dependencies = [ "futures-util", "get_if_addrs", "getrandom", - "group", + "group 0.13.0", "hashlink", "hex", "hex-literal", @@ -3091,10 +3290,12 @@ dependencies = [ "ic-canister-sig-creation", "ic-cbor", "ic-cdk 0.16.0", + "ic-cdk 0.18.0-alpha.1", + "ic-cdk-macros 0.18.0-alpha.1", "ic-cdk-macros 0.9.0", "ic-cdk-timers", "ic-certificate-verification", - "ic-certification", + "ic-certification 3.0.2", "ic-certified-map", "ic-http-certification", "ic-http-gateway", @@ -3103,7 +3304,7 @@ dependencies = [ "ic-sha3", "ic-stable-structures", "ic-test-state-machine-client", - "ic-transport-types", + "ic-transport-types 0.39.2", "ic-utils", "ic-verify-bls-signature 0.6.0", "ic-wasm", @@ -3126,7 +3327,7 @@ dependencies = [ "itertools 0.12.0", "json-patch", "json5", - "k256", + "k256 0.13.4", "k8s-openapi", "kube", "lazy_static", @@ -3155,7 +3356,7 @@ dependencies = [ "nix 0.24.3", "num-bigint 0.4.6", "num-bigint-dig", - "num-rational", + "num-rational 0.2.4", "num-traits", "num_cpus", "once_cell", @@ -3164,7 +3365,7 @@ dependencies = [ "opentelemetry 0.27.0", "opentelemetry-otlp", "opentelemetry-prometheus 0.13.0", - "opentelemetry_sdk 0.27.0", + "opentelemetry_sdk 0.27.1", "p256", "pairing", "parking_lot 0.12.1", @@ -3173,7 +3374,7 @@ dependencies = [ "pem 1.1.1", "pin-project-lite", "ping", - "pkcs8", + "pkcs8 0.10.2", "pkg-config", "pprof", "predicates", @@ -3239,7 +3440,7 @@ dependencies = [ "sha2 0.10.8", "sha3", "signal-hook", - "signature", + "signature 2.2.0", "simple_asn1", "simple_moving_average", "slog", @@ -3256,6 +3457,7 @@ dependencies = [ "stubborn-io", "subtle", "syn 1.0.109", + "syscalls", "tar", "tarpc", "tempfile", @@ -3274,6 +3476,7 @@ dependencies = [ "tokio-rustls 0.26.0", "tokio-serde", "tokio-socks", + "tokio-stream", "tokio-test", "tokio-util", "toml", @@ -3318,6 +3521,16 @@ dependencies = [ "zstd", ] +[[package]] +name = "directories-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" +dependencies = [ + "cfg-if 1.0.0", + "dirs-sys-next", +] + [[package]] name = "dirs" version = "2.0.2" @@ -3408,6 +3621,12 @@ dependencies = [ "dtoa", ] +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + [[package]] name = "duration-string" version = "0.3.0" @@ -3423,18 +3642,30 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23d2f3407d9a573d666de4b5bdf10569d73ca9478087346697dcbae6244bfbcd" +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der 0.6.1", + "elliptic-curve 0.12.3", + "rfc6979 0.3.1", + "signature 1.6.4", +] + [[package]] name = "ecdsa" version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ - "der", + "der 0.7.8", "digest 0.10.7", - "elliptic-curve", - "rfc6979", - "signature", - "spki", + "elliptic-curve 0.13.8", + "rfc6979 0.4.0", + "signature 2.2.0", + "spki 0.7.3", ] [[package]] @@ -3443,8 +3674,8 @@ version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ - "pkcs8", - "signature", + "pkcs8 0.10.2", + "signature 2.2.0", ] [[package]] @@ -3474,7 +3705,7 @@ dependencies = [ "rand_core 0.6.4", "serde", "sha2 0.10.8", - "signature", + "signature 2.2.0", "subtle", "zeroize", ] @@ -3503,22 +3734,43 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct 0.1.1", + "crypto-bigint 0.4.9", + "der 0.6.1", + "digest 0.10.7", + "ff 0.12.1", + "generic-array", + "group 0.12.1", + "pem-rfc7468 0.6.0", + "pkcs8 0.9.0", + "rand_core 0.6.4", + "sec1 0.3.0", + "subtle", + "zeroize", +] + [[package]] name = "elliptic-curve" version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ - "base16ct", - "crypto-bigint", + "base16ct 0.2.0", + "crypto-bigint 0.5.3", "digest 0.10.7", "ff 0.13.0", "generic-array", - "group", - "pem-rfc7468", - "pkcs8", + "group 0.13.0", + "pem-rfc7468 0.7.0", + "pkcs8 0.10.2", "rand_core 0.6.4", - "sec1", + "sec1 0.7.3", "subtle", "zeroize", ] @@ -3766,10 +4018,10 @@ dependencies = [ "bytes", "chrono", "const-hex", - "elliptic-curve", + "elliptic-curve 0.13.8", "ethabi", "generic-array", - "k256", + "k256 0.13.4", "num_enum", "open-fastrlp", "rand 0.8.5", @@ -3792,6 +4044,12 @@ dependencies = [ "serde", ] +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + [[package]] name = "event-listener" version = "4.0.3" @@ -4008,6 +4266,27 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -4262,6 +4541,16 @@ dependencies = [ "wasi", ] +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + [[package]] name = "gimli" version = "0.26.2" @@ -4314,6 +4603,17 @@ dependencies = [ "smallvec", ] +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff 0.12.1", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "group" version = "0.13.0" @@ -4369,6 +4669,20 @@ version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" +[[package]] +name = "handlebars" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faa67bab9ff362228eb3d00bd024a4965d8231bbb7921167f0cfa66c6626b225" +dependencies = [ + "log", + "pest", + "pest_derive", + "serde", + "serde_json", + "thiserror 1.0.68", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -4389,6 +4703,16 @@ dependencies = [ "serde", ] +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "foldhash", + "serde", +] + [[package]] name = "hashlink" version = "0.8.4" @@ -4685,17 +5009,6 @@ dependencies = [ "http 1.2.0", ] -[[package]] -name = "http-body-to-bytes" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17a08236c6f51c2ee95d840f45acf8fa9e339390e00b4ef640857b2f2a534d70" -dependencies = [ - "bytes", - "http-body 1.0.1", - "http-body-util", -] - [[package]] name = "http-body-util" version = "0.1.2" @@ -4919,48 +5232,50 @@ dependencies = [ [[package]] name = "ic-agent" -version = "0.37.1" +version = "0.39.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fd3fdf5e5c4f4a9fe5ca612f0febd22dcb161d2f2b75b0142326732be5e4978" +checksum = "1ba408987ca48fc3eee6a613e760d076a9046cccbbb5ba29efbada339ab28ed9" dependencies = [ + "arc-swap", + "async-channel 1.9.0", "async-lock", + "async-trait", + "async-watch", "backoff", "cached 0.52.0", "candid", + "der 0.7.8", + "ecdsa 0.16.9", "ed25519-consensus", + "elliptic-curve 0.13.8", "futures-util", "hex", "http 1.2.0", "http-body 1.0.1", - "http-body-to-bytes", - "http-body-util", - "hyper 1.5.1", - "hyper-rustls 0.27.3", - "hyper-util", - "ic-certification", - "ic-transport-types", + "ic-certification 3.0.2", + "ic-transport-types 0.39.2", "ic-verify-bls-signature 0.5.0", - "k256", + "k256 0.13.4", "leb128", "p256", "pem 3.0.4", - "pkcs8", + "pkcs8 0.10.2", "rand 0.8.5", "rangemap", "reqwest 0.12.9", "ring 0.17.7", - "rustls-webpki 0.102.8", - "sec1", + "sec1 0.7.3", "serde", "serde_bytes", "serde_cbor", "serde_repr", "sha2 0.10.8", "simple_asn1", - "thiserror 1.0.68", + "stop-token", + "thiserror 2.0.3", "time", "tokio", - "tower 0.4.13", + "tower-service", "url", ] @@ -5051,31 +5366,30 @@ dependencies = [ [[package]] name = "ic-canister-sig-creation" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d1fc58d747480967a25810d8a90d460e7e9ea4c669ab0286541a148736513f9" +version = "1.1.0" +source = "git+https://github.com/dfinity/ic-canister-sig-creation?rev=7f9e931954637526295269155881207f6c832d6d#7f9e931954637526295269155881207f6c832d6d" dependencies = [ "candid", "hex", - "ic-cdk 0.14.1", - "ic-certification", + "ic-cdk 0.17.0", + "ic-certification 3.0.2", "ic-representation-independent-hash", "lazy_static", "serde", "serde_bytes", "serde_cbor", "sha2 0.10.8", - "thiserror 1.0.68", + "thiserror 2.0.3", ] [[package]] name = "ic-cbor" -version = "2.6.0" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b0e48b4166c891e79d624f3a184b4a7c145d307576872d9a46dedb8c73ea8f" +checksum = "5500d6e85bc2ca8ea8aaed16cb84811882589244831a2fd8eefe02e90b3006c6" dependencies = [ "candid", - "ic-certification", + "ic-certification 3.0.2", "leb128", "nom", "thiserror 1.0.68", @@ -5096,12 +5410,12 @@ dependencies = [ [[package]] name = "ic-cdk" -version = "0.14.1" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cff1a3c3db565e3384c9c9d6d676b0a3f89a0886f4f787294d9c946d844369f" +checksum = "dd8ecacd682fa05a985253592963306cb9799622d7b1cce4b1edb89c6ec85be1" dependencies = [ "candid", - "ic-cdk-macros 0.14.0", + "ic-cdk-macros 0.16.0", "ic0 0.23.0", "serde", "serde_bytes", @@ -5109,12 +5423,12 @@ dependencies = [ [[package]] name = "ic-cdk" -version = "0.16.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8ecacd682fa05a985253592963306cb9799622d7b1cce4b1edb89c6ec85be1" +checksum = "b2abdf9341da9f9f6b451a40609cb69645a05a8e9eb7784c16209f16f2c0f76f" dependencies = [ "candid", - "ic-cdk-macros 0.16.0", + "ic-cdk-macros 0.17.0", "ic0 0.23.0", "serde", "serde_bytes", @@ -5122,15 +5436,15 @@ dependencies = [ [[package]] name = "ic-cdk" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2abdf9341da9f9f6b451a40609cb69645a05a8e9eb7784c16209f16f2c0f76f" +version = "0.18.0-alpha.1" +source = "git+https://github.com/dfinity/cdk-rs.git?rev=4e287ce51636b0e70768c193da38d2fc5324ea15#4e287ce51636b0e70768c193da38d2fc5324ea15" dependencies = [ "candid", - "ic-cdk-macros 0.17.0", - "ic0 0.23.0", + "ic-cdk-macros 0.18.0-alpha.1", + "ic0 0.24.0-alpha.1", "serde", "serde_bytes", + "thiserror 2.0.3", ] [[package]] @@ -5163,9 +5477,9 @@ dependencies = [ [[package]] name = "ic-cdk-macros" -version = "0.14.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01dc6bc425ec048d6ac4137c7c0f2cfbd6f8b0be8efc568feae2b265f566117c" +checksum = "0d4d857135deef20cc7ea8f3869a30cd9cfeb1392b3a81043790b2cd82adc3e0" dependencies = [ "candid", "proc-macro2", @@ -5177,9 +5491,9 @@ dependencies = [ [[package]] name = "ic-cdk-macros" -version = "0.16.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d4d857135deef20cc7ea8f3869a30cd9cfeb1392b3a81043790b2cd82adc3e0" +checksum = "b8df41980e95dead28735ab0f748c75477b0c5eab37a09a5641c78ec406a1db0" dependencies = [ "candid", "proc-macro2", @@ -5191,9 +5505,8 @@ dependencies = [ [[package]] name = "ic-cdk-macros" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8df41980e95dead28735ab0f748c75477b0c5eab37a09a5641c78ec406a1db0" +version = "0.18.0-alpha.1" +source = "git+https://github.com/dfinity/cdk-rs.git?rev=4e287ce51636b0e70768c193da38d2fc5324ea15#4e287ce51636b0e70768c193da38d2fc5324ea15" dependencies = [ "candid", "proc-macro2", @@ -5219,14 +5532,14 @@ dependencies = [ [[package]] name = "ic-certificate-verification" -version = "2.6.0" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "586e09b06a93d930f6a33f5f909bb11d2e4a06be3635dd5da1eb0e6554b7dae4" +checksum = "2daec653eb7895b5549cdf58d871985711c03cf5e389f7800a970f4f42dc0897" dependencies = [ - "cached 0.47.0", + "cached 0.54.0", "candid", "ic-cbor", - "ic-certification", + "ic-certification 3.0.2", "lazy_static", "leb128", "miracl_core_bls12381", @@ -5248,6 +5561,18 @@ dependencies = [ "sha2 0.10.8", ] +[[package]] +name = "ic-certification" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eae40f26fcac9c141cad54d9aa5f423efffde78ac371057c53d275ebbcad443" +dependencies = [ + "hex", + "serde", + "serde_bytes", + "sha2 0.10.8", +] + [[package]] name = "ic-certified-map" version = "0.3.4" @@ -5261,23 +5586,26 @@ dependencies = [ [[package]] name = "ic-http-certification" -version = "2.6.0" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff0b97e949845039149dc5e7ea6a7c12ee4333bb402e37bc507904643c7b3e41" +checksum = "479941fca8e68c2267cddf686d34ed6fb491168667ff259c08a3d65d28bd26d2" dependencies = [ + "base64 0.22.1", "candid", - "http 0.2.12", - "ic-certification", + "http 1.2.0", + "ic-certification 3.0.2", "ic-representation-independent-hash", "serde", + "serde_cbor", "thiserror 1.0.68", "urlencoding", ] [[package]] name = "ic-http-gateway" -version = "0.0.0" -source = "git+https://github.com/dfinity/http-gateway?tag=0.1.0-b0#3be26b5a2c71bf56e05b910951c1935a1ac550c4" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8b30a8ff19af1a7dc64b1dbe1a38f1b60c7eea566e2049f755ce3bace0e630" dependencies = [ "bytes", "candid", @@ -5292,6 +5620,20 @@ dependencies = [ "thiserror 1.0.68", ] +[[package]] +name = "ic-identity-hsm" +version = "0.39.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ebb94d7cb5be09bed47655008f0c2968a3a3253dcf680297f3e8475e4b317c4" +dependencies = [ + "hex", + "ic-agent", + "pkcs11", + "sha2 0.10.8", + "simple_asn1", + "thiserror 2.0.3", +] + [[package]] name = "ic-metrics-encoder" version = "1.1.1" @@ -5300,9 +5642,9 @@ checksum = "8b5c7628eac357aecda461130f8074468be5aa4d258a002032d82d817f79f1f8" [[package]] name = "ic-representation-independent-hash" -version = "2.6.0" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08ae59483e377cd9aad94ec339ed1d2583b0d5929cab989328dac2d853b2f570" +checksum = "3643f12824280580d31e47d380f1be23abee29944a1430c3ed22b164ac8e68db" dependencies = [ "leb128", "sha2 0.10.8", @@ -5310,18 +5652,18 @@ dependencies = [ [[package]] name = "ic-response-verification" -version = "2.6.0" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bef02ef84189d61a7d39889b7e9a3ae212d45c3df293513f7b2568027fd08a8" +checksum = "2b97514fada84797baf61a6a29f1c71695798c2628cb6013d97a5dd6ecc26df7" dependencies = [ - "base64 0.21.4", + "base64 0.22.1", "candid", "flate2", "hex", - "http 0.2.12", + "http 1.2.0", "ic-cbor", "ic-certificate-verification", - "ic-certification", + "ic-certification 3.0.2", "ic-http-certification", "ic-representation-independent-hash", "leb128", @@ -5370,7 +5712,7 @@ checksum = "875dc4704780383112e8e8b5063a1b98de114321d0c7d3e7f635dcf360a57fba" dependencies = [ "candid", "hex", - "ic-certification", + "ic-certification 2.6.0", "leb128", "serde", "serde_bytes", @@ -5379,11 +5721,29 @@ dependencies = [ "thiserror 1.0.68", ] +[[package]] +name = "ic-transport-types" +version = "0.39.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21e2418868dd5857d2a5bac3f1cb6de1aecf2316d380997ef842aec3d8a79d4e" +dependencies = [ + "candid", + "hex", + "ic-certification 3.0.2", + "leb128", + "serde", + "serde_bytes", + "serde_cbor", + "serde_repr", + "sha2 0.10.8", + "thiserror 2.0.3", +] + [[package]] name = "ic-utils" -version = "0.37.0" +version = "0.39.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fa832296800758c9c921dd1704985ded6b3e6fbc3aee409727eb1f00d69a595" +checksum = "e1fb9c35ef4976a71d37f3ebf73ee43bb52b360be60d91d3a77f74fbc875dda4" dependencies = [ "async-trait", "candid", @@ -5396,7 +5756,7 @@ dependencies = [ "sha2 0.10.8", "strum 0.26.3", "strum_macros 0.26.4", - "thiserror 1.0.68", + "thiserror 2.0.3", "time", "tokio", ] @@ -5473,6 +5833,11 @@ version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de254dd67bbd58073e23dc1c8553ba12fa1dc610a19de94ad2bbcd0460c067f" +[[package]] +name = "ic0" +version = "0.24.0-alpha.1" +source = "git+https://github.com/dfinity/cdk-rs.git?rev=4e287ce51636b0e70768c193da38d2fc5324ea15#4e287ce51636b0e70768c193da38d2fc5324ea15" + [[package]] name = "ic_bls12_381" version = "0.10.0" @@ -5481,7 +5846,7 @@ checksum = "22c65787944f32af084dffd0c68c1e544237b76e215654ddea8cd9f527dd8b69" dependencies = [ "digest 0.10.7", "ff 0.13.0", - "group", + "group 0.13.0", "pairing", "rand_core 0.6.4", "subtle", @@ -5914,9 +6279,12 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.8.0" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" +dependencies = [ + "serde", +] [[package]] name = "ipnetwork" @@ -6009,9 +6377,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.27" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ "libc", ] @@ -6064,8 +6432,9 @@ dependencies = [ [[package]] name = "jsonrpc" -version = "0.12.1" -source = "git+https://github.com/apoelstra/rust-jsonrpc?rev=e42044d#e42044d8e0896317488dfbd65eb5563d76e937fe" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd8d6b3f301ba426b30feca834a2a18d48d5b54e5065496b5c1b05537bee3639" dependencies = [ "base64 0.13.1", "serde", @@ -6074,15 +6443,29 @@ dependencies = [ [[package]] name = "jsonrpc" -version = "0.13.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd8d6b3f301ba426b30feca834a2a18d48d5b54e5065496b5c1b05537bee3639" +checksum = "3662a38d341d77efecb73caf01420cfa5aa63c0253fd7bc05289ef9f6616e1bf" dependencies = [ "base64 0.13.1", + "minreq", "serde", "serde_json", ] +[[package]] +name = "k256" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" +dependencies = [ + "cfg-if 1.0.0", + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", + "sha2 0.10.8", + "sha3", +] + [[package]] name = "k256" version = "0.13.4" @@ -6090,11 +6473,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" dependencies = [ "cfg-if 1.0.0", - "ecdsa", - "elliptic-curve", + "ecdsa 0.16.9", + "elliptic-curve 0.13.8", "once_cell", "sha2 0.10.8", - "signature", + "signature 2.2.0", ] [[package]] @@ -6119,6 +6502,20 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "keyring" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd3d701d3de5b9c4b0d9d077f8c2c66f0388d75e96932ebbb7cdff8713d7f7c6" +dependencies = [ + "byteorder", + "dbus-secret-service", + "linux-keyutils", + "openssl", + "security-framework 3.0.1", + "windows-sys 0.59.0", +] + [[package]] name = "kube" version = "0.93.1" @@ -6343,6 +6740,16 @@ version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +[[package]] +name = "libdbus-sys" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72" +dependencies = [ + "cc", + "pkg-config", +] + [[package]] name = "libflate" version = "2.1.0" @@ -6378,6 +6785,16 @@ dependencies = [ "once_cell", ] +[[package]] +name = "libloading" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" +dependencies = [ + "cc", + "winapi 0.3.9", +] + [[package]] name = "libloading" version = "0.7.4" @@ -6472,7 +6889,17 @@ dependencies = [ name = "linked-hash-map" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linux-keyutils" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "761e49ec5fd8a5a463f9b84e877c373d888935b71c6be78f3767fe2ae6bed18e" +dependencies = [ + "bitflags 2.6.0", + "libc", +] [[package]] name = "linux-raw-sys" @@ -6885,6 +7312,17 @@ dependencies = [ "adler", ] +[[package]] +name = "minreq" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36a8e50e917e18a37d500d27d40b7bc7d127e71c0c94fb2d83f43b4afd308390" +dependencies = [ + "log", + "serde", + "serde_json", +] + [[package]] name = "mio" version = "0.8.10" @@ -7167,6 +7605,20 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint 0.4.6", + "num-complex", + "num-integer", + "num-iter", + "num-rational 0.4.2", + "num-traits", +] + [[package]] name = "num-bigint" version = "0.2.6" @@ -7207,6 +7659,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -7234,9 +7695,9 @@ dependencies = [ [[package]] name = "num-iter" -version = "0.1.43" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ "autocfg 1.1.0", "num-integer", @@ -7255,6 +7716,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint 0.4.6", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -7322,12 +7794,12 @@ dependencies = [ [[package]] name = "object" -version = "0.36.1" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "crc32fast", - "hashbrown 0.14.5", + "hashbrown 0.15.2", "indexmap 2.2.6", "memchr", ] @@ -7397,20 +7869,56 @@ dependencies = [ "thiserror 1.0.68", ] +[[package]] +name = "openssl" +version = "0.10.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5e534d133a060a3c19daec1eb3e98ec6f4685978834f2dbadfe2ec215bab64e" +dependencies = [ + "bitflags 2.6.0", + "cfg-if 1.0.0", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "openssl-probe" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +[[package]] +name = "openssl-src" +version = "300.4.1+3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faa4eac4138c62414b5622d1b31c5c304f34b406b013c079c2bbc652fdd6678c" +dependencies = [ + "cc", +] + [[package]] name = "openssl-sys" -version = "0.9.102" +version = "0.9.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" dependencies = [ "cc", "libc", + "openssl-src", "pkg-config", "vcpkg", ] @@ -7476,7 +7984,7 @@ dependencies = [ "http 1.2.0", "opentelemetry 0.27.0", "opentelemetry-proto", - "opentelemetry_sdk 0.27.0", + "opentelemetry_sdk 0.27.1", "prost 0.13.3", "thiserror 1.0.68", "tokio", @@ -7517,7 +8025,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6e05acbfada5ec79023c85368af14abd0b307c015e9064d249b2a950ef459a6" dependencies = [ "opentelemetry 0.27.0", - "opentelemetry_sdk 0.27.0", + "opentelemetry_sdk 0.27.1", "prost 0.13.3", "tonic", ] @@ -7624,16 +8132,15 @@ dependencies = [ [[package]] name = "opentelemetry_sdk" -version = "0.27.0" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b742c1cae4693792cc564e58d75a2a0ba29421a34a85b50da92efa89ecb2bc" +checksum = "231e9d6ceef9b0b2546ddf52335785ce41252bc7474ee8ba05bfad277be13ab8" dependencies = [ "async-trait", "futures-channel", "futures-executor", "futures-util", "glob", - "once_cell", "opentelemetry 0.27.0", "percent-encoding", "rand 0.8.5", @@ -7689,8 +8196,8 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" dependencies = [ - "ecdsa", - "elliptic-curve", + "ecdsa 0.16.9", + "elliptic-curve 0.13.8", "primeorder", "sha2 0.10.8", ] @@ -7701,7 +8208,7 @@ version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81fec4625e73cf41ef4bb6846cafa6d44736525f442ba45e407c4a000a13996f" dependencies = [ - "group", + "group 0.13.0", ] [[package]] @@ -7790,12 +8297,32 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "487f2ccd1e17ce8c1bfab3a65c89525af41cfad4c8659021a1e9a2aacd73b89b" +[[package]] +name = "password-hash" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" +dependencies = [ + "base64ct", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "paste" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "pbkdf2" version = "0.12.2" @@ -7853,6 +8380,15 @@ dependencies = [ "serde", ] +[[package]] +name = "pem-rfc7468" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d159833a9105500e0398934e205e0773f0b27529557134ecfc51c27646adac" +dependencies = [ + "base64ct", +] + [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -8079,9 +8615,29 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" dependencies = [ - "der", - "pkcs8", - "spki", + "der 0.7.8", + "pkcs8 0.10.2", + "spki 0.7.3", +] + +[[package]] +name = "pkcs11" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3aca6d67e4c8613bfe455599d0233d00735f85df2001f6bfd9bb7ac0496b10af" +dependencies = [ + "libloading 0.5.2", + "num-bigint 0.2.6", +] + +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der 0.6.1", + "spki 0.6.0", ] [[package]] @@ -8090,8 +8646,8 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der", - "spki", + "der 0.7.8", + "spki 0.7.3", ] [[package]] @@ -8137,8 +8693,8 @@ dependencies = [ "base64 0.13.1", "candid", "hex", - "ic-certification", - "ic-transport-types", + "ic-certification 2.6.0", + "ic-transport-types 0.37.1", "reqwest 0.12.9", "schemars", "serde", @@ -8183,6 +8739,18 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + [[package]] name = "portable-atomic" version = "1.4.3" @@ -8334,7 +8902,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c2fcef82c0ec6eefcc179b978446c399b3cdf73c392c35604e399eee6df1ee3" dependencies = [ - "elliptic-curve", + "elliptic-curve 0.13.8", ] [[package]] @@ -8677,9 +9245,9 @@ dependencies = [ [[package]] name = "pulley-interpreter" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3b8d81cf799e20564931e9867ca32de545188c6ee4c2e0f6e41d32f0c7dc6fb" +checksum = "403a1a95f4c18a45c86c7bff13df00347afd0abcbf2e54af273c837339ffcf77" dependencies = [ "cranelift-bitset", "log", @@ -9088,14 +9656,15 @@ dependencies = [ [[package]] name = "regalloc2" -version = "0.10.2" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12908dbeb234370af84d0579b9f68258a0f67e201412dd9a2814e6f45b2fc0f0" +checksum = "145c1c267e14f20fb0f88aa76a1c5ffec42d592c1d28b3cd9148ae35916158d3" dependencies = [ - "hashbrown 0.14.5", + "allocator-api2", + "bumpalo", + "hashbrown 0.15.2", "log", "rustc-hash 2.0.0", - "slice-group-by", "smallvec", ] @@ -9273,6 +9842,17 @@ dependencies = [ "quick-error", ] +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint 0.4.9", + "hmac", + "zeroize", +] + [[package]] name = "rfc6979" version = "0.4.0" @@ -9417,11 +9997,11 @@ dependencies = [ "num-integer", "num-traits", "pkcs1", - "pkcs8", + "pkcs8 0.10.2", "rand_core 0.6.4", "sha2 0.10.8", - "signature", - "spki", + "signature 2.2.0", + "spki 0.7.3", "subtle", "zeroize", ] @@ -9638,7 +10218,7 @@ dependencies = [ "openssl-probe", "rustls-pemfile 1.0.3", "schannel", - "security-framework", + "security-framework 2.11.1", ] [[package]] @@ -9651,7 +10231,7 @@ dependencies = [ "rustls-pemfile 2.2.0", "rustls-pki-types", "schannel", - "security-framework", + "security-framework 2.11.1", ] [[package]] @@ -9664,7 +10244,7 @@ dependencies = [ "rustls-pemfile 2.2.0", "rustls-pki-types", "schannel", - "security-framework", + "security-framework 2.11.1", ] [[package]] @@ -9697,7 +10277,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4c7dc240fec5517e6c4eab3310438636cfe6391dfc345ba013109909a90d136" dependencies = [ - "core-foundation", + "core-foundation 0.9.4", "core-foundation-sys", "jni", "log", @@ -9706,7 +10286,7 @@ dependencies = [ "rustls-native-certs 0.7.0", "rustls-platform-verifier-android", "rustls-webpki 0.102.8", - "security-framework", + "security-framework 2.11.1", "security-framework-sys", "webpki-root-certs", "windows-sys 0.52.0", @@ -9881,16 +10461,30 @@ version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct 0.1.1", + "der 0.6.1", + "generic-array", + "pkcs8 0.9.0", + "subtle", + "zeroize", +] + [[package]] name = "sec1" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ - "base16ct", - "der", + "base16ct 0.2.0", + "der 0.7.8", "generic-array", - "pkcs8", + "pkcs8 0.10.2", "subtle", "zeroize", ] @@ -9925,7 +10519,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e0cc0f1cf93f4969faf3ea1c7d8a9faed25918d96affa959720823dfe86d4f3" dependencies = [ "bitcoin_hashes 0.14.0", + "rand 0.8.5", "secp256k1-sys 0.10.0", + "serde", ] [[package]] @@ -9972,13 +10568,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ "bitflags 2.6.0", - "core-foundation", + "core-foundation 0.9.4", "core-foundation-sys", "libc", "num-bigint 0.4.6", "security-framework-sys", ] +[[package]] +name = "security-framework" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1415a607e92bec364ea2cf9264646dcce0f91e6d65281bd6f2819cca3bf39c8" +dependencies = [ + "bitflags 2.6.0", + "core-foundation 0.10.0", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + [[package]] name = "security-framework-sys" version = "2.12.0" @@ -10019,9 +10628,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.214" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] @@ -10079,9 +10688,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.214" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", @@ -10146,9 +10755,9 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.16" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", @@ -10234,7 +10843,7 @@ version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" dependencies = [ - "darling 0.20.3", + "darling 0.20.10", "proc-macro2", "quote", "syn 2.0.87", @@ -10341,6 +10950,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + [[package]] name = "shlex" version = "1.3.0" @@ -10377,6 +10992,16 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + [[package]] name = "signature" version = "2.2.0" @@ -10458,12 +11083,6 @@ dependencies = [ "autocfg 1.1.0", ] -[[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" @@ -10619,6 +11238,16 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "spki" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der 0.6.1", +] + [[package]] name = "spki" version = "0.7.3" @@ -10626,7 +11255,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", - "der", + "der 0.7.8", ] [[package]] @@ -10672,6 +11301,18 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "stop-token" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af91f480ee899ab2d9f8435bfdfc14d08a5754bd9d3fef1f1a1c23336aad6c8b" +dependencies = [ + "async-channel 1.9.0", + "cfg-if 1.0.0", + "futures-core", + "pin-project-lite", +] + [[package]] name = "str_stack" version = "0.1.0" @@ -10876,6 +11517,16 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "syscalls" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d0e35dc7d73976a53c7e6d7d177ef804a0c0ee774ec77bcc520c2216fd7cbe" +dependencies = [ + "serde", + "serde_repr", +] + [[package]] name = "system-configuration" version = "0.5.1" @@ -10883,7 +11534,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.4", "system-configuration-sys", ] @@ -11228,6 +11879,25 @@ dependencies = [ "time-core", ] +[[package]] +name = "tiny-bip39" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62cc94d358b5a1e84a5cb9109f559aa3c4d634d2b1b4de3d0fa4adc7c78e2861" +dependencies = [ + "anyhow", + "hmac", + "once_cell", + "pbkdf2 0.11.0", + "rand 0.8.5", + "rustc-hash 1.1.0", + "sha2 0.10.8", + "thiserror 1.0.68", + "unicode-normalization", + "wasm-bindgen", + "zeroize", +] + [[package]] name = "tiny-keccak" version = "2.0.2" @@ -11406,9 +12076,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" dependencies = [ "futures-core", "pin-project-lite", @@ -11764,7 +12434,7 @@ dependencies = [ "js-sys", "once_cell", "opentelemetry 0.27.0", - "opentelemetry_sdk 0.27.0", + "opentelemetry_sdk 0.27.1", "smallvec", "tracing", "tracing-core", @@ -12355,12 +13025,12 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.219.1" +version = "0.221.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29cbbd772edcb8e7d524a82ee8cef8dd046fc14033796a754c3ad246d019fa54" +checksum = "c17a3bd88f2155da63a1f2fcb8a56377a24f0b6dfed12733bb5f544e86f690c5" dependencies = [ "leb128", - "wasmparser 0.219.1", + "wasmparser 0.221.2", ] [[package]] @@ -12432,13 +13102,12 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.219.1" +version = "0.221.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c771866898879073c53b565a6c7b49953795159836714ac56a5befb581227c5" +checksum = "9845c470a2e10b61dd42c385839cdd6496363ed63b5c9e420b5488b77bd22083" dependencies = [ - "ahash 0.8.11", "bitflags 2.6.0", - "hashbrown 0.14.5", + "hashbrown 0.15.2", "indexmap 2.2.6", "semver", "serde", @@ -12457,20 +13126,20 @@ dependencies = [ [[package]] name = "wasmprinter" -version = "0.219.1" +version = "0.221.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "228cdc1f30c27816da225d239ce4231f28941147d34713dee8f1fff7cb330e54" +checksum = "a80742ff1b9e6d8c231ac7c7247782c6fc5bce503af760bca071811e5fc9ee56" dependencies = [ "anyhow", "termcolor", - "wasmparser 0.219.1", + "wasmparser 0.221.2", ] [[package]] name = "wasmtime" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b79302e3e084713249cc5622e8608e7410afdeeea8c8026d04f491d1fab0b4b" +checksum = "f639ecae347b9a2227e453a7b7671e84370a0b61f47a15e0390fe9b7725e47b3" dependencies = [ "anyhow", "bitflags 2.6.0", @@ -12484,7 +13153,7 @@ dependencies = [ "log", "mach2", "memfd", - "object 0.36.1", + "object 0.36.7", "once_cell", "paste", "postcard", @@ -12497,31 +13166,33 @@ dependencies = [ "smallvec", "sptr", "target-lexicon", - "wasmparser 0.219.1", + "wasmparser 0.221.2", "wasmtime-asm-macros", "wasmtime-component-macro", "wasmtime-cranelift", "wasmtime-environ", + "wasmtime-fiber", "wasmtime-jit-icache-coherence", "wasmtime-slab", "wasmtime-versioned-export-macros", + "wasmtime-winch", "windows-sys 0.59.0", ] [[package]] name = "wasmtime-asm-macros" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe53a24e7016a5222875d8ca3ad6024b464465985693c42098cd0bb710002c28" +checksum = "882a18800471cfc063c8b3ccf75723784acc3fd534009ac09421f2fac2fcdcec" dependencies = [ "cfg-if 1.0.0", ] [[package]] name = "wasmtime-component-macro" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e118acbd2bc09b32ad8606bc7cef793bf5019c1b107772e64dc6c76b5055d40b" +checksum = "eb5c0a77c9e1927c3d471f53cc13767c3d3438e5d5ffd394e3eb31c86445fd60" dependencies = [ "anyhow", "proc-macro2", @@ -12534,15 +13205,15 @@ dependencies = [ [[package]] name = "wasmtime-component-util" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a6db4f3ee18c699629eabb9c64e77efe5a93a5137f098db7cab295037ba41c2" +checksum = "43702ca98bf5162eca0573db691ed9ecd36d716f8c6688410fe26ec16b6f9bcb" [[package]] name = "wasmtime-cranelift" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b87e6c78f562b50aff1afd87ff32a57e241424c846c1c8f3c5fd352d2d62906" +checksum = "20070aa5b75080a8932ec328419faf841df2bc6ceb16b55b0df2b952098392a2" dependencies = [ "anyhow", "cfg-if 1.0.0", @@ -12554,20 +13225,20 @@ dependencies = [ "gimli 0.31.1", "itertools 0.12.0", "log", - "object 0.36.1", + "object 0.36.7", "smallvec", "target-lexicon", "thiserror 1.0.68", - "wasmparser 0.219.1", + "wasmparser 0.221.2", "wasmtime-environ", "wasmtime-versioned-export-macros", ] [[package]] name = "wasmtime-environ" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c25bfeaa16432d59a0706e2463d315ef4c9ebcfaf5605670b99d46373bdf9f27" +checksum = "2604ddb24879d4dc1dedcb7081d7a8e017259bce916fdae097a97db52cbaab80" dependencies = [ "anyhow", "cranelift-bitset", @@ -12575,22 +13246,37 @@ dependencies = [ "gimli 0.31.1", "indexmap 2.2.6", "log", - "object 0.36.1", + "object 0.36.7", "postcard", "serde", "serde_derive", "smallvec", "target-lexicon", - "wasm-encoder 0.219.1", - "wasmparser 0.219.1", - "wasmprinter 0.219.1", + "wasm-encoder 0.221.2", + "wasmparser 0.221.2", + "wasmprinter 0.221.2", +] + +[[package]] +name = "wasmtime-fiber" +version = "28.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98593412d2b167ebe2b59d4a17a184978a72f976b53b3a0ec05629451079ac1d" +dependencies = [ + "anyhow", + "cc", + "cfg-if 1.0.0", + "rustix", + "wasmtime-asm-macros", + "wasmtime-versioned-export-macros", + "windows-sys 0.59.0", ] [[package]] name = "wasmtime-jit-icache-coherence" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91b218a92866f74f35162f5d03a4e0f62cd0e1cc624285b1014275e5d4575fad" +checksum = "d40d7722b9e1fbeae135715710a8a2570b1e6cf72b74dd653962d89831c6c70d" dependencies = [ "anyhow", "cfg-if 1.0.0", @@ -12600,26 +13286,43 @@ dependencies = [ [[package]] name = "wasmtime-slab" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d5f8acf677ee6b3b8ba400dd9753ea4769e56a95c4b30b045ac6d2d54b2f8ea" +checksum = "8579c335220b4ece9aa490a0e8b46de78cd342b195ab21ff981d095e14b52383" [[package]] name = "wasmtime-versioned-export-macros" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df09be00c38f49172ca9936998938476e3f2df782673a39ae2ef9fb0838341b6" +checksum = "d7de0a56fb0a69b185968f2d7a9ba54750920a806470dff7ad8de91ac06d277e" dependencies = [ "proc-macro2", "quote", "syn 2.0.87", ] +[[package]] +name = "wasmtime-winch" +version = "28.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abd309943c443f5590d12f9aba9ba63c481091c955a0a14de0c2a9e0e3aaeca9" +dependencies = [ + "anyhow", + "cranelift-codegen", + "gimli 0.31.1", + "object 0.36.7", + "target-lexicon", + "wasmparser 0.221.2", + "wasmtime-cranelift", + "wasmtime-environ", + "winch-codegen", +] + [[package]] name = "wasmtime-wit-bindgen" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf3963c9c29df91564d8bd181eb00d0dbaeafa1b2a01e15952bb7391166b704e" +checksum = "969f83022dac3435d6469edb582ceed04cfe32aa44dc3ef16e5cb55574633df8" dependencies = [ "anyhow", "heck 0.5.0", @@ -12760,6 +13463,23 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "winch-codegen" +version = "28.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9110decc2983ed94de904804dcd979ba59cbabc78a94fec6b1d8468ec513d0f6" +dependencies = [ + "anyhow", + "cranelift-codegen", + "gimli 0.31.1", + "regalloc2", + "smallvec", + "target-lexicon", + "wasmparser 0.221.2", + "wasmtime-cranelift", + "wasmtime-environ", +] + [[package]] name = "windows-core" version = "0.52.0" @@ -13043,9 +13763,9 @@ dependencies = [ [[package]] name = "wit-parser" -version = "0.219.1" +version = "0.221.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a86f669283257e8e424b9a4fc3518e3ade0b95deb9fbc0f93a1876be3eda598" +checksum = "fbe1538eea6ea5ddbe5defd0dc82539ad7ba751e1631e9185d24a931f0a5adc8" dependencies = [ "anyhow", "id-arena", @@ -13056,7 +13776,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.219.1", + "wasmparser 0.221.2", ] [[package]] @@ -13110,10 +13830,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94" dependencies = [ "const-oid", - "der", + "der 0.7.8", "sha1", - "signature", - "spki", + "signature 2.2.0", + "spki 0.7.3", "tls_codec", ] @@ -13310,3 +14030,9 @@ dependencies = [ "cc", "pkg-config", ] + +[[patch.unused]] +name = "jsonrpc" +version = "0.12.1" +source = "git+https://github.com/apoelstra/rust-jsonrpc?rev=e42044d#e42044d8e0896317488dfbd65eb5563d76e937fe" + diff --git a/Cargo.Bazel.json.lock b/Cargo.Bazel.json.lock index 681e97b733b..97325ef3498 100644 --- a/Cargo.Bazel.json.lock +++ b/Cargo.Bazel.json.lock @@ -1,5 +1,5 @@ { - "checksum": "6fe3acd7d34fb9438b36f7ec483d8e3f6f4e0a9ba130dc790a394422c002a555", + "checksum": "328a35840f7c103c76777c84791d47b1d1606b7503677e77bb1754e48206dbb4", "crates": { "abnf 0.12.0": { "name": "abnf", @@ -537,7 +537,7 @@ "target": "regex_lite" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -973,7 +973,7 @@ "target": "regex_lite" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -1336,6 +1336,143 @@ ], "license_file": "LICENSE-APACHE" }, + "aes 0.8.4": { + "name": "aes", + "version": "0.8.4", + "package_url": "https://github.com/RustCrypto/block-ciphers", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/aes/0.8.4/download", + "sha256": "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" + } + }, + "targets": [ + { + "Library": { + "crate_name": "aes", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "aes", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "cfg-if 1.0.0", + "target": "cfg_if" + }, + { + "id": "cipher 0.4.4", + "target": "cipher" + } + ], + "selects": { + "cfg(any(target_arch = \"aarch64\", target_arch = \"x86_64\", target_arch = \"x86\"))": [ + { + "id": "cpufeatures 0.2.9", + "target": "cpufeatures" + } + ] + } + }, + "edition": "2021", + "version": "0.8.4" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, + "aes-gcm 0.10.3": { + "name": "aes-gcm", + "version": "0.10.3", + "package_url": "https://github.com/RustCrypto/AEADs", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/aes-gcm/0.10.3/download", + "sha256": "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" + } + }, + "targets": [ + { + "Library": { + "crate_name": "aes_gcm", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "aes_gcm", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "aes", + "alloc", + "default", + "getrandom", + "rand_core" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "aead 0.5.2", + "target": "aead" + }, + { + "id": "aes 0.8.4", + "target": "aes" + }, + { + "id": "cipher 0.4.4", + "target": "cipher" + }, + { + "id": "ctr 0.9.2", + "target": "ctr" + }, + { + "id": "ghash 0.5.1", + "target": "ghash" + }, + { + "id": "subtle 2.6.1", + "target": "subtle" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.10.3" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "ahash 0.7.8": { "name": "ahash", "version": "0.7.8", @@ -1650,7 +1787,7 @@ "target": "schemars" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -1825,14 +1962,14 @@ ], "license_file": null }, - "allocator-api2 0.2.16": { + "allocator-api2 0.2.21": { "name": "allocator-api2", - "version": "0.2.16", + "version": "0.2.21", "package_url": "https://github.com/zakarumych/allocator-api2", "repository": { "Http": { - "url": "https://static.crates.io/crates/allocator-api2/0.2.16/download", - "sha256": "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + "url": "https://static.crates.io/crates/allocator-api2/0.2.21/download", + "sha256": "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" } }, "targets": [ @@ -1861,14 +1998,14 @@ "selects": {} }, "edition": "2018", - "version": "0.2.16" + "version": "0.2.21" }, "license": "MIT OR Apache-2.0", "license_ids": [ "Apache-2.0", "MIT" ], - "license_file": "license" + "license_file": "LICENSE-APACHE" }, "android-tzdata 0.1.1": { "name": "android-tzdata", @@ -2472,6 +2609,71 @@ ], "license_file": "LICENSE-APACHE" }, + "argon2 0.4.1": { + "name": "argon2", + "version": "0.4.1", + "package_url": "https://github.com/RustCrypto/password-hashes/tree/master/argon2", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/argon2/0.4.1/download", + "sha256": "db4ce4441f99dbd377ca8a8f57b698c44d0d6e712d8329b5040da5a64aa1ce73" + } + }, + "targets": [ + { + "Library": { + "crate_name": "argon2", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "argon2", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "alloc", + "default", + "password-hash", + "rand" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "base64ct 1.6.0", + "target": "base64ct" + }, + { + "id": "blake2 0.10.6", + "target": "blake2" + }, + { + "id": "password-hash 0.4.2", + "target": "password_hash" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.4.1" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "arrayvec 0.5.2": { "name": "arrayvec", "version": "0.5.2", @@ -2678,7 +2880,7 @@ "target": "percent_encoding" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -2776,7 +2978,7 @@ "target": "quote" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -3127,7 +3329,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -3288,6 +3490,62 @@ ], "license_file": "LICENSE-APACHE" }, + "async-channel 1.9.0": { + "name": "async-channel", + "version": "1.9.0", + "package_url": "https://github.com/smol-rs/async-channel", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/async-channel/1.9.0/download", + "sha256": "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" + } + }, + "targets": [ + { + "Library": { + "crate_name": "async_channel", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "async_channel", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "concurrent-queue 2.5.0", + "target": "concurrent_queue" + }, + { + "id": "event-listener 2.5.3", + "target": "event_listener" + }, + { + "id": "futures-core 0.3.31", + "target": "futures_core" + } + ], + "selects": {} + }, + "edition": "2018", + "version": "1.9.0" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "async-channel 2.3.1": { "name": "async-channel", "version": "2.3.1", @@ -4107,6 +4365,54 @@ ], "license_file": "LICENSE-APACHE" }, + "async-watch 0.3.1": { + "name": "async-watch", + "version": "0.3.1", + "package_url": "https://github.com/cynecx/async-watch", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/async-watch/0.3.1/download", + "sha256": "a078faf4e27c0c6cc0efb20e5da59dcccc04968ebf2801d8e0b2195124cdcdb2" + } + }, + "targets": [ + { + "Library": { + "crate_name": "async_watch", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "async_watch", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "event-listener 2.5.3", + "target": "event_listener" + } + ], + "selects": {} + }, + "edition": "2018", + "version": "0.3.1" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "async-web-client 0.6.2": { "name": "async-web-client", "version": "0.6.2", @@ -4549,7 +4855,7 @@ "target": "pin_project_lite" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -4812,7 +5118,7 @@ "target": "pin_project_lite" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -5336,7 +5642,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" } ], @@ -5394,6 +5700,51 @@ ], "license_file": "LICENSE-CC0" }, + "base16ct 0.1.1": { + "name": "base16ct", + "version": "0.1.1", + "package_url": "https://github.com/RustCrypto/formats/tree/master/base16ct", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/base16ct/0.1.1/download", + "sha256": "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + } + }, + "targets": [ + { + "Library": { + "crate_name": "base16ct", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "base16ct", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "alloc" + ], + "selects": {} + }, + "edition": "2021", + "version": "0.1.1" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "base16ct 0.2.0": { "name": "base16ct", "version": "0.2.0", @@ -5507,6 +5858,12 @@ "compile_data_glob": [ "**" ], + "crate_features": { + "common": [ + "std" + ], + "selects": {} + }, "deps": { "common": [ { @@ -5803,7 +6160,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -5939,7 +6296,8 @@ ], "crate_features": { "common": [ - "alloc" + "alloc", + "std" ], "selects": {} }, @@ -6029,7 +6387,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -6446,6 +6804,104 @@ ], "license_file": null }, + "bip32 0.4.0": { + "name": "bip32", + "version": "0.4.0", + "package_url": "https://github.com/iqlusioninc/crates/tree/main/bip32", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/bip32/0.4.0/download", + "sha256": "b30ed1d6f8437a487a266c8293aeb95b61a23261273e3e02912cdb8b68bf798b" + } + }, + "targets": [ + { + "Library": { + "crate_name": "bip32", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "bip32", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "alloc", + "bip39", + "default", + "k256", + "mnemonic", + "once_cell", + "pbkdf2", + "secp256k1", + "std" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "bs58 0.4.0", + "target": "bs58" + }, + { + "id": "hmac 0.12.1", + "target": "hmac" + }, + { + "id": "k256 0.11.6", + "target": "k256" + }, + { + "id": "once_cell 1.19.0", + "target": "once_cell" + }, + { + "id": "pbkdf2 0.11.0", + "target": "pbkdf2" + }, + { + "id": "rand_core 0.6.4", + "target": "rand_core" + }, + { + "id": "ripemd 0.1.3", + "target": "ripemd" + }, + { + "id": "sha2 0.10.8", + "target": "sha2" + }, + { + "id": "subtle 2.6.1", + "target": "subtle" + }, + { + "id": "zeroize 1.8.1", + "target": "zeroize" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.4.0" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "bip32 0.5.1": { "name": "bip32", "version": "0.5.1", @@ -6700,7 +7156,7 @@ "target": "secp256k1" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -6795,7 +7251,7 @@ "target": "secp256k1" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde", "alias": "actual_serde" } @@ -6819,14 +7275,14 @@ ], "license_file": null }, - "bitcoin 0.32.2": { + "bitcoin 0.32.5": { "name": "bitcoin", - "version": "0.32.2", + "version": "0.32.5", "package_url": "https://github.com/rust-bitcoin/rust-bitcoin/", "repository": { "Http": { - "url": "https://static.crates.io/crates/bitcoin/0.32.2/download", - "sha256": "ea507acc1cd80fc084ace38544bbcf7ced7c2aa65b653b102de0ce718df668f6" + "url": "https://static.crates.io/crates/bitcoin/0.32.5/download", + "sha256": "ce6bc65742dea50536e35ad42492b234c27904a27f0abdcbce605015cb4ea026" } }, "targets": [ @@ -6860,6 +7316,18 @@ "compile_data_glob": [ "**" ], + "crate_features": { + "common": [ + "actual-serde", + "default", + "rand", + "rand-std", + "secp-recovery", + "serde", + "std" + ], + "selects": {} + }, "deps": { "common": [ { @@ -6872,7 +7340,7 @@ "target": "bech32" }, { - "id": "bitcoin 0.32.2", + "id": "bitcoin 0.32.5", "target": "build_script_build" }, { @@ -6905,14 +7373,19 @@ "target": "hex_lit" }, { - "id": "secp256k1 0.29.0", + "id": "secp256k1 0.29.1", "target": "secp256k1" + }, + { + "id": "serde 1.0.217", + "target": "serde", + "alias": "actual_serde" } ], "selects": {} }, "edition": "2021", - "version": "0.32.2" + "version": "0.32.5" }, "build_script_attrs": { "compile_data_glob": [ @@ -6972,7 +7445,9 @@ "crate_features": { "common": [ "alloc", - "default" + "default", + "serde", + "std" ], "selects": {} }, @@ -6981,6 +7456,10 @@ { "id": "bitcoin-internals 0.3.0", "target": "build_script_build" + }, + { + "id": "serde 1.0.217", + "target": "serde" } ], "selects": {} @@ -7033,7 +7512,8 @@ ], "crate_features": { "common": [ - "alloc" + "alloc", + "std" ], "selects": {} }, @@ -7152,7 +7632,9 @@ ], "crate_features": { "common": [ - "alloc" + "alloc", + "serde", + "std" ], "selects": {} }, @@ -7162,6 +7644,10 @@ "id": "bitcoin-internals 0.3.0", "target": "bitcoin_internals", "alias": "internals" + }, + { + "id": "serde 1.0.217", + "target": "serde" } ], "selects": {} @@ -7214,7 +7700,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -7274,7 +7760,7 @@ "alias": "internals" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -7322,7 +7808,9 @@ "common": [ "alloc", "bitcoin-io", - "io" + "io", + "serde", + "std" ], "selects": {} }, @@ -7336,6 +7824,10 @@ "id": "hex-conservative 0.2.1", "target": "hex_conservative", "alias": "hex" + }, + { + "id": "serde 1.0.217", + "target": "serde" } ], "selects": {} @@ -7349,14 +7841,14 @@ ], "license_file": null }, - "bitcoincore-rpc 0.15.0": { + "bitcoincore-rpc 0.19.0": { "name": "bitcoincore-rpc", - "version": "0.15.0", + "version": "0.19.0", "package_url": "https://github.com/rust-bitcoin/rust-bitcoincore-rpc/", "repository": { "Http": { - "url": "https://static.crates.io/crates/bitcoincore-rpc/0.15.0/download", - "sha256": "dd0e67dbf7a9971e7f4276f6089e9e814ce0f624a03216b7d92d00351ae7fb3e" + "url": "https://static.crates.io/crates/bitcoincore-rpc/0.19.0/download", + "sha256": "aedd23ae0fd321affb4bbbc36126c6f49a32818dc6b979395d24da8c9d4e80ee" } }, "targets": [ @@ -7378,14 +7870,21 @@ "compile_data_glob": [ "**" ], + "crate_features": { + "common": [ + "default", + "rand" + ], + "selects": {} + }, "deps": { "common": [ { - "id": "bitcoincore-rpc-json 0.15.0", + "id": "bitcoincore-rpc-json 0.19.0", "target": "bitcoincore_rpc_json" }, { - "id": "jsonrpc 0.12.1", + "id": "jsonrpc 0.18.0", "target": "jsonrpc" }, { @@ -7393,7 +7892,7 @@ "target": "log" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -7403,8 +7902,8 @@ ], "selects": {} }, - "edition": "2015", - "version": "0.15.0" + "edition": "2018", + "version": "0.19.0" }, "license": "CC0-1.0", "license_ids": [ @@ -7412,14 +7911,14 @@ ], "license_file": "LICENSE" }, - "bitcoincore-rpc-json 0.15.0": { + "bitcoincore-rpc-json 0.19.0": { "name": "bitcoincore-rpc-json", - "version": "0.15.0", + "version": "0.19.0", "package_url": "https://github.com/rust-bitcoin/rust-bitcoincore-rpc/", "repository": { "Http": { - "url": "https://static.crates.io/crates/bitcoincore-rpc-json/0.15.0/download", - "sha256": "2e2ae16202721ba8c3409045681fac790a5ddc791f05731a2df22c0c6bffc0f1" + "url": "https://static.crates.io/crates/bitcoincore-rpc-json/0.19.0/download", + "sha256": "d8909583c5fab98508e80ef73e5592a651c954993dc6b7739963257d19f0e71a" } }, "targets": [ @@ -7441,14 +7940,21 @@ "compile_data_glob": [ "**" ], + "crate_features": { + "common": [ + "default", + "rand" + ], + "selects": {} + }, "deps": { "common": [ { - "id": "bitcoin 0.28.2", + "id": "bitcoin 0.32.5", "target": "bitcoin" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -7458,8 +7964,8 @@ ], "selects": {} }, - "edition": "2015", - "version": "0.15.0" + "edition": "2021", + "version": "0.19.0" }, "license": "CC0-1.0", "license_ids": [ @@ -7713,6 +8219,54 @@ ], "license_file": "LICENSE.txt" }, + "blake2 0.10.6": { + "name": "blake2", + "version": "0.10.6", + "package_url": "https://github.com/RustCrypto/hashes", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/blake2/0.10.6/download", + "sha256": "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" + } + }, + "targets": [ + { + "Library": { + "crate_name": "blake2", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "blake2", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "digest 0.10.7", + "target": "digest" + } + ], + "selects": {} + }, + "edition": "2018", + "version": "0.10.6" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "block-buffer 0.9.0": { "name": "block-buffer", "version": "0.9.0", @@ -8322,6 +8876,61 @@ ], "license_file": "LICENSE" }, + "bs58 0.4.0": { + "name": "bs58", + "version": "0.4.0", + "package_url": "https://github.com/mycorrhiza/bs58-rs", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/bs58/0.4.0/download", + "sha256": "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" + } + }, + "targets": [ + { + "Library": { + "crate_name": "bs58", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "bs58", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "check", + "sha2" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "sha2 0.9.9", + "target": "sha2" + } + ], + "selects": {} + }, + "edition": "2018", + "version": "0.4.0" + }, + "license": "MIT/Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "bs58 0.5.0": { "name": "bs58", "version": "0.5.0", @@ -8656,7 +9265,7 @@ "target": "semver" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -8823,10 +9432,20 @@ ], "crate_features": { "common": [ + "allocator-api2", "default" ], "selects": {} }, + "deps": { + "common": [ + { + "id": "allocator-api2 0.2.21", + "target": "allocator_api2" + } + ], + "selects": {} + }, "edition": "2021", "version": "3.16.0" }, @@ -8962,7 +9581,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -9243,7 +9862,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -9417,7 +10036,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" }, { @@ -9475,14 +10094,14 @@ ], "license_file": null }, - "cached 0.47.0": { + "cached 0.49.2": { "name": "cached", - "version": "0.47.0", + "version": "0.49.2", "package_url": "https://github.com/jaemk/cached", "repository": { "Http": { - "url": "https://static.crates.io/crates/cached/0.47.0/download", - "sha256": "69b0116662497bc24e4b177c90eaf8870e39e2714c3fcfa296327a93f593fc21" + "url": "https://static.crates.io/crates/cached/0.49.2/download", + "sha256": "f251fd1e72720ca07bf5d8e310f54a193fd053479a1f6342c6663ee4fa01cf96" } }, "targets": [ @@ -9504,26 +10123,8 @@ "compile_data_glob": [ "**" ], - "crate_features": { - "common": [ - "ahash", - "cached_proc_macro", - "cached_proc_macro_types", - "default", - "proc_macro" - ], - "selects": {} - }, "deps": { "common": [ - { - "id": "ahash 0.8.11", - "target": "ahash" - }, - { - "id": "cached_proc_macro_types 0.1.0", - "target": "cached_proc_macro_types" - }, { "id": "hashbrown 0.14.5", "target": "hashbrown" @@ -9544,16 +10145,7 @@ "selects": {} }, "edition": "2018", - "proc_macro_deps": { - "common": [ - { - "id": "cached_proc_macro 0.18.1", - "target": "cached_proc_macro" - } - ], - "selects": {} - }, - "version": "0.47.0" + "version": "0.49.2" }, "license": "MIT", "license_ids": [ @@ -9561,14 +10153,14 @@ ], "license_file": "LICENSE" }, - "cached 0.49.2": { + "cached 0.52.0": { "name": "cached", - "version": "0.49.2", + "version": "0.52.0", "package_url": "https://github.com/jaemk/cached", "repository": { "Http": { - "url": "https://static.crates.io/crates/cached/0.49.2/download", - "sha256": "f251fd1e72720ca07bf5d8e310f54a193fd053479a1f6342c6663ee4fa01cf96" + "url": "https://static.crates.io/crates/cached/0.52.0/download", + "sha256": "a8466736fe5dbcaf8b8ee24f9bbefe43c884dc3e9ff7178da70f55bffca1133c" } }, "targets": [ @@ -9590,8 +10182,18 @@ "compile_data_glob": [ "**" ], + "crate_features": { + "common": [ + "ahash" + ], + "selects": {} + }, "deps": { "common": [ + { + "id": "ahash 0.8.11", + "target": "ahash" + }, { "id": "hashbrown 0.14.5", "target": "hashbrown" @@ -9612,7 +10214,7 @@ "selects": {} }, "edition": "2018", - "version": "0.49.2" + "version": "0.52.0" }, "license": "MIT", "license_ids": [ @@ -9620,14 +10222,14 @@ ], "license_file": "LICENSE" }, - "cached 0.52.0": { + "cached 0.54.0": { "name": "cached", - "version": "0.52.0", + "version": "0.54.0", "package_url": "https://github.com/jaemk/cached", "repository": { "Http": { - "url": "https://static.crates.io/crates/cached/0.52.0/download", - "sha256": "a8466736fe5dbcaf8b8ee24f9bbefe43c884dc3e9ff7178da70f55bffca1133c" + "url": "https://static.crates.io/crates/cached/0.54.0/download", + "sha256": "9718806c4a2fe9e8a56fd736f97b340dd10ed1be8ed733ed50449f351dc33cae" } }, "targets": [ @@ -9651,7 +10253,11 @@ ], "crate_features": { "common": [ - "ahash" + "ahash", + "cached_proc_macro", + "cached_proc_macro_types", + "default", + "proc_macro" ], "selects": {} }, @@ -9662,12 +10268,12 @@ "target": "ahash" }, { - "id": "hashbrown 0.14.5", - "target": "hashbrown" + "id": "cached_proc_macro_types 0.1.1", + "target": "cached_proc_macro_types" }, { - "id": "instant 0.1.12", - "target": "instant" + "id": "hashbrown 0.14.5", + "target": "hashbrown" }, { "id": "once_cell 1.19.0", @@ -9676,12 +10282,25 @@ { "id": "thiserror 1.0.68", "target": "thiserror" + }, + { + "id": "web-time 1.1.0", + "target": "web_time" } ], "selects": {} }, "edition": "2018", - "version": "0.52.0" + "proc_macro_deps": { + "common": [ + { + "id": "cached_proc_macro 0.23.0", + "target": "cached_proc_macro" + } + ], + "selects": {} + }, + "version": "0.54.0" }, "license": "MIT", "license_ids": [ @@ -9689,14 +10308,14 @@ ], "license_file": "LICENSE" }, - "cached_proc_macro 0.18.1": { + "cached_proc_macro 0.23.0": { "name": "cached_proc_macro", - "version": "0.18.1", + "version": "0.23.0", "package_url": "https://github.com/jaemk/cached", "repository": { "Http": { - "url": "https://static.crates.io/crates/cached_proc_macro/0.18.1/download", - "sha256": "c878c71c2821aa2058722038a59a67583a4240524687c6028571c9b395ded61f" + "url": "https://static.crates.io/crates/cached_proc_macro/0.23.0/download", + "sha256": "2f42a145ed2d10dce2191e1dcf30cfccfea9026660e143662ba5eec4017d5daa" } }, "targets": [ @@ -9721,7 +10340,7 @@ "deps": { "common": [ { - "id": "darling 0.14.4", + "id": "darling 0.20.10", "target": "darling" }, { @@ -9733,29 +10352,29 @@ "target": "quote" }, { - "id": "syn 1.0.109", + "id": "syn 2.0.87", "target": "syn" } ], "selects": {} }, "edition": "2018", - "version": "0.18.1" + "version": "0.23.0" }, "license": "MIT", "license_ids": [ "MIT" ], - "license_file": null + "license_file": "LICENSE" }, - "cached_proc_macro_types 0.1.0": { + "cached_proc_macro_types 0.1.1": { "name": "cached_proc_macro_types", - "version": "0.1.0", + "version": "0.1.1", "package_url": "https://github.com/jaemk/cached", "repository": { "Http": { - "url": "https://static.crates.io/crates/cached_proc_macro_types/0.1.0/download", - "sha256": "3a4f925191b4367301851c6d99b09890311d74b0d43f274c0b34c86d308a3663" + "url": "https://static.crates.io/crates/cached_proc_macro_types/0.1.1/download", + "sha256": "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0" } }, "targets": [ @@ -9778,13 +10397,13 @@ "**" ], "edition": "2018", - "version": "0.1.0" + "version": "0.1.1" }, "license": "MIT", "license_ids": [ "MIT" ], - "license_file": null + "license_file": "LICENSE" }, "camino 1.1.6": { "name": "camino", @@ -9841,7 +10460,7 @@ "target": "build_script_build" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -9913,7 +10532,7 @@ "target": "canbench_rs" }, { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -9945,7 +10564,7 @@ "target": "semver" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -10008,7 +10627,7 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -10016,7 +10635,7 @@ "target": "ic_cdk" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -10095,14 +10714,14 @@ ], "license_file": null }, - "candid 0.10.10": { + "candid 0.10.13": { "name": "candid", - "version": "0.10.10", + "version": "0.10.13", "package_url": "https://github.com/dfinity/candid", "repository": { "Http": { - "url": "https://static.crates.io/crates/candid/0.10.10/download", - "sha256": "6c30ee7f886f296b6422c0ff017e89dd4f831521dfdcc76f3f71aae1ce817222" + "url": "https://static.crates.io/crates/candid/0.10.13/download", + "sha256": "a253bab4a9be502c82332b60cbeee6202ad0692834efeec95fae9f29db33d692" } }, "targets": [ @@ -10174,7 +10793,7 @@ "target": "pretty" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -10209,7 +10828,7 @@ ], "selects": {} }, - "version": "0.10.10" + "version": "0.10.13" }, "license": "Apache-2.0", "license_ids": [ @@ -10324,7 +10943,7 @@ "target": "anyhow" }, { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -10424,7 +11043,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -10490,7 +11109,7 @@ "target": "semver" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -10548,20 +11167,14 @@ ], "license_file": "LICENSE-APACHE" }, - "cc 1.0.83": { + "cc 1.1.37": { "name": "cc", - "version": "1.0.83", + "version": "1.1.37", "package_url": "https://github.com/rust-lang/cc-rs", "repository": { "Http": { - "url": "https://static.crates.io/crates/cc/1.0.83/download", - "sha256": "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0", - "patch_args": [ - "-p1" - ], - "patches": [ - "@@//bazel:cc_rs.patch" - ] + "url": "https://static.crates.io/crates/cc/1.1.37/download", + "sha256": "40545c26d092346d8a8dab71ee48e7685a7a9cba76e634790c215b41a4a7b4cf" } }, "targets": [ @@ -10587,97 +11200,98 @@ "common": [], "selects": { "aarch64-apple-darwin": [ - "jobserver", "parallel" ], "aarch64-pc-windows-msvc": [ - "jobserver", "parallel" ], "aarch64-unknown-linux-gnu": [ - "jobserver", "parallel" ], "aarch64-unknown-nixos-gnu": [ - "jobserver", "parallel" ], "arm-unknown-linux-gnueabi": [ - "jobserver", "parallel" ], "i686-pc-windows-msvc": [ - "jobserver", "parallel" ], "i686-unknown-linux-gnu": [ - "jobserver", "parallel" ], "powerpc-unknown-linux-gnu": [ - "jobserver", "parallel" ], "s390x-unknown-linux-gnu": [ - "jobserver", "parallel" ], "x86_64-apple-darwin": [ - "jobserver", "parallel" ], "x86_64-pc-windows-msvc": [ - "jobserver", "parallel" ], "x86_64-unknown-freebsd": [ - "jobserver", "parallel" ], "x86_64-unknown-linux-gnu": [ - "jobserver", "parallel" ], "x86_64-unknown-nixos-gnu": [ - "jobserver", "parallel" ] } }, "deps": { - "common": [], + "common": [ + { + "id": "shlex 1.3.0", + "target": "shlex" + } + ], "selects": { "aarch64-apple-darwin": [ { - "id": "jobserver 0.1.26", + "id": "jobserver 0.1.32", "target": "jobserver" + }, + { + "id": "libc 0.2.158", + "target": "libc" } ], "aarch64-pc-windows-msvc": [ { - "id": "jobserver 0.1.26", + "id": "jobserver 0.1.32", "target": "jobserver" } ], "aarch64-unknown-linux-gnu": [ { - "id": "jobserver 0.1.26", + "id": "jobserver 0.1.32", "target": "jobserver" + }, + { + "id": "libc 0.2.158", + "target": "libc" } ], "aarch64-unknown-nixos-gnu": [ { - "id": "jobserver 0.1.26", + "id": "jobserver 0.1.32", "target": "jobserver" + }, + { + "id": "libc 0.2.158", + "target": "libc" } ], "arm-unknown-linux-gnueabi": [ { - "id": "jobserver 0.1.26", + "id": "jobserver 0.1.32", "target": "jobserver" - } - ], - "cfg(unix)": [ + }, { "id": "libc 0.2.158", "target": "libc" @@ -10685,62 +11299,90 @@ ], "i686-pc-windows-msvc": [ { - "id": "jobserver 0.1.26", + "id": "jobserver 0.1.32", "target": "jobserver" } ], "i686-unknown-linux-gnu": [ { - "id": "jobserver 0.1.26", + "id": "jobserver 0.1.32", "target": "jobserver" + }, + { + "id": "libc 0.2.158", + "target": "libc" } ], "powerpc-unknown-linux-gnu": [ { - "id": "jobserver 0.1.26", + "id": "jobserver 0.1.32", "target": "jobserver" + }, + { + "id": "libc 0.2.158", + "target": "libc" } ], "s390x-unknown-linux-gnu": [ { - "id": "jobserver 0.1.26", + "id": "jobserver 0.1.32", "target": "jobserver" + }, + { + "id": "libc 0.2.158", + "target": "libc" } ], "x86_64-apple-darwin": [ { - "id": "jobserver 0.1.26", + "id": "jobserver 0.1.32", "target": "jobserver" + }, + { + "id": "libc 0.2.158", + "target": "libc" } ], "x86_64-pc-windows-msvc": [ { - "id": "jobserver 0.1.26", + "id": "jobserver 0.1.32", "target": "jobserver" } ], "x86_64-unknown-freebsd": [ { - "id": "jobserver 0.1.26", + "id": "jobserver 0.1.32", "target": "jobserver" + }, + { + "id": "libc 0.2.158", + "target": "libc" } ], "x86_64-unknown-linux-gnu": [ { - "id": "jobserver 0.1.26", + "id": "jobserver 0.1.32", "target": "jobserver" + }, + { + "id": "libc 0.2.158", + "target": "libc" } ], "x86_64-unknown-nixos-gnu": [ { - "id": "jobserver 0.1.26", + "id": "jobserver 0.1.32", "target": "jobserver" + }, + { + "id": "libc 0.2.158", + "target": "libc" } ] } }, "edition": "2018", - "version": "1.0.83" + "version": "1.1.37" }, "license": "MIT OR Apache-2.0", "license_ids": [ @@ -10869,7 +11511,7 @@ "target": "regex_syntax" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -10904,19 +11546,19 @@ "target": "crossterm" } ], - "aarch64-fuchsia": [ + "aarch64-linux-android": [ { "id": "crossterm 0.27.0", "target": "crossterm" } ], - "aarch64-linux-android": [ + "aarch64-pc-windows-msvc": [ { "id": "crossterm 0.27.0", "target": "crossterm" } ], - "aarch64-pc-windows-msvc": [ + "aarch64-unknown-fuchsia": [ { "id": "crossterm 0.27.0", "target": "crossterm" @@ -11040,7 +11682,7 @@ "target": "wasm_bindgen" } ], - "wasm32-wasi": [ + "wasm32-wasip1": [ { "id": "serde-wasm-bindgen 0.5.0", "target": "serde_wasm_bindgen" @@ -11062,25 +11704,25 @@ "target": "crossterm" } ], - "x86_64-fuchsia": [ + "x86_64-linux-android": [ { "id": "crossterm 0.27.0", "target": "crossterm" } ], - "x86_64-linux-android": [ + "x86_64-pc-windows-msvc": [ { "id": "crossterm 0.27.0", "target": "crossterm" } ], - "x86_64-pc-windows-msvc": [ + "x86_64-unknown-freebsd": [ { "id": "crossterm 0.27.0", "target": "crossterm" } ], - "x86_64-unknown-freebsd": [ + "x86_64-unknown-fuchsia": [ { "id": "crossterm 0.27.0", "target": "crossterm" @@ -11520,7 +12162,7 @@ "target": "num_traits" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -11543,12 +12185,6 @@ "target": "iana_time_zone" } ], - "aarch64-fuchsia": [ - { - "id": "iana-time-zone 0.1.59", - "target": "iana_time_zone" - } - ], "aarch64-linux-android": [ { "id": "android-tzdata 0.1.1", @@ -11565,6 +12201,12 @@ "target": "windows_targets" } ], + "aarch64-unknown-fuchsia": [ + { + "id": "iana-time-zone 0.1.59", + "target": "iana_time_zone" + } + ], "aarch64-unknown-linux-gnu": [ { "id": "iana-time-zone 0.1.59", @@ -11673,12 +12315,6 @@ "target": "iana_time_zone" } ], - "x86_64-fuchsia": [ - { - "id": "iana-time-zone 0.1.59", - "target": "iana_time_zone" - } - ], "x86_64-linux-android": [ { "id": "android-tzdata 0.1.1", @@ -11701,6 +12337,12 @@ "target": "iana_time_zone" } ], + "x86_64-unknown-fuchsia": [ + { + "id": "iana-time-zone 0.1.59", + "target": "iana_time_zone" + } + ], "x86_64-unknown-linux-gnu": [ { "id": "iana-time-zone 0.1.59", @@ -11772,7 +12414,7 @@ "target": "ciborium_ll" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -12718,7 +13360,7 @@ "target": "reqwest" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -13047,7 +13689,7 @@ "target": "pretty_assertions" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -13392,6 +14034,7 @@ "crate_features": { "common": [ "ansi-parsing", + "default", "unicode-width" ], "selects": {} @@ -13767,6 +14410,65 @@ ], "license_file": "LICENSE-APACHE" }, + "core-foundation 0.10.0": { + "name": "core-foundation", + "version": "0.10.0", + "package_url": "https://github.com/servo/core-foundation-rs", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/core-foundation/0.10.0/download", + "sha256": "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" + } + }, + "targets": [ + { + "Library": { + "crate_name": "core_foundation", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "core_foundation", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "default", + "link" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "core-foundation-sys 0.8.7", + "target": "core_foundation_sys" + }, + { + "id": "libc 0.2.158", + "target": "libc" + } + ], + "selects": {} + }, + "edition": "2018", + "version": "0.10.0" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "core-foundation-sys 0.8.7": { "name": "core-foundation-sys", "version": "0.8.7", @@ -13861,7 +14563,7 @@ "target": "log" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -13920,7 +14622,7 @@ "target": "bitcoin_private" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -14056,14 +14758,14 @@ ], "license_file": "LICENSE-APACHE" }, - "cranelift-bforest 0.114.0": { + "cranelift-bforest 0.115.0": { "name": "cranelift-bforest", - "version": "0.114.0", + "version": "0.115.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/cranelift-bforest/0.114.0/download", - "sha256": "2ba4f80548f22dc9c43911907b5e322c5555544ee85f785115701e6a28c9abe1" + "url": "https://static.crates.io/crates/cranelift-bforest/0.115.0/download", + "sha256": "ac89549be94911dd0e839b4a7db99e9ed29c17517e1c026f61066884c168aa3c" } }, "targets": [ @@ -14088,14 +14790,14 @@ "deps": { "common": [ { - "id": "cranelift-entity 0.114.0", + "id": "cranelift-entity 0.115.0", "target": "cranelift_entity" } ], "selects": {} }, "edition": "2021", - "version": "0.114.0" + "version": "0.115.0" }, "license": "Apache-2.0 WITH LLVM-exception", "license_ids": [ @@ -14103,14 +14805,14 @@ ], "license_file": "LICENSE" }, - "cranelift-bitset 0.114.0": { + "cranelift-bitset 0.115.0": { "name": "cranelift-bitset", - "version": "0.114.0", + "version": "0.115.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/cranelift-bitset/0.114.0/download", - "sha256": "005884e3649c3e5ff2dc79e8a94b138f11569cc08a91244a292714d2a86e9156" + "url": "https://static.crates.io/crates/cranelift-bitset/0.115.0/download", + "sha256": "b9bd49369f76c77e34e641af85d0956869237832c118964d08bf5f51f210875a" } }, "targets": [ @@ -14141,7 +14843,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -14151,13 +14853,13 @@ "proc_macro_deps": { "common": [ { - "id": "serde_derive 1.0.214", + "id": "serde_derive 1.0.217", "target": "serde_derive" } ], "selects": {} }, - "version": "0.114.0" + "version": "0.115.0" }, "license": "Apache-2.0 WITH LLVM-exception", "license_ids": [ @@ -14165,14 +14867,14 @@ ], "license_file": null }, - "cranelift-codegen 0.114.0": { + "cranelift-codegen 0.115.0": { "name": "cranelift-codegen", - "version": "0.114.0", + "version": "0.115.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/cranelift-codegen/0.114.0/download", - "sha256": "fe4036255ec33ce9a37495dfbcfc4e1118fd34e693eff9a1e106336b7cd16a9b" + "url": "https://static.crates.io/crates/cranelift-codegen/0.115.0/download", + "sha256": "fd96ce9cf8efebd7f5ab8ced5a0ce44250280bbae9f593d74a6d7effc3582a35" } }, "targets": [ @@ -14223,27 +14925,27 @@ "target": "bumpalo" }, { - "id": "cranelift-bforest 0.114.0", + "id": "cranelift-bforest 0.115.0", "target": "cranelift_bforest" }, { - "id": "cranelift-bitset 0.114.0", + "id": "cranelift-bitset 0.115.0", "target": "cranelift_bitset" }, { - "id": "cranelift-codegen 0.114.0", + "id": "cranelift-codegen 0.115.0", "target": "build_script_build" }, { - "id": "cranelift-codegen-shared 0.114.0", + "id": "cranelift-codegen-shared 0.115.0", "target": "cranelift_codegen_shared" }, { - "id": "cranelift-control 0.114.0", + "id": "cranelift-control 0.115.0", "target": "cranelift_control" }, { - "id": "cranelift-entity 0.114.0", + "id": "cranelift-entity 0.115.0", "target": "cranelift_entity" }, { @@ -14259,7 +14961,7 @@ "target": "log" }, { - "id": "regalloc2 0.10.2", + "id": "regalloc2 0.11.1", "target": "regalloc2" }, { @@ -14278,7 +14980,7 @@ "selects": {} }, "edition": "2021", - "version": "0.114.0" + "version": "0.115.0" }, "build_script_attrs": { "compile_data_glob": [ @@ -14290,11 +14992,11 @@ "deps": { "common": [ { - "id": "cranelift-codegen-meta 0.114.0", + "id": "cranelift-codegen-meta 0.115.0", "target": "cranelift_codegen_meta" }, { - "id": "cranelift-isle 0.114.0", + "id": "cranelift-isle 0.115.0", "target": "cranelift_isle" } ], @@ -14307,14 +15009,14 @@ ], "license_file": "LICENSE" }, - "cranelift-codegen-meta 0.114.0": { + "cranelift-codegen-meta 0.115.0": { "name": "cranelift-codegen-meta", - "version": "0.114.0", + "version": "0.115.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/cranelift-codegen-meta/0.114.0/download", - "sha256": "f7ca74f4b68319da11d39e894437cb6e20ec7c2e11fbbda823c3bf207beedff7", + "url": "https://static.crates.io/crates/cranelift-codegen-meta/0.115.0/download", + "sha256": "5a68e358827afe4bfb6239fcbf6fbd5ac56206ece8a99c8f5f9bbd518773281a", "patch_args": [ "-p4" ], @@ -14345,14 +15047,14 @@ "deps": { "common": [ { - "id": "cranelift-codegen-shared 0.114.0", + "id": "cranelift-codegen-shared 0.115.0", "target": "cranelift_codegen_shared" } ], "selects": {} }, "edition": "2021", - "version": "0.114.0" + "version": "0.115.0" }, "license": "Apache-2.0 WITH LLVM-exception", "license_ids": [ @@ -14360,14 +15062,14 @@ ], "license_file": "LICENSE" }, - "cranelift-codegen-shared 0.114.0": { + "cranelift-codegen-shared 0.115.0": { "name": "cranelift-codegen-shared", - "version": "0.114.0", + "version": "0.115.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/cranelift-codegen-shared/0.114.0/download", - "sha256": "897e54f433a0269c4187871aa06d452214d5515d228d5bdc22219585e9eef895" + "url": "https://static.crates.io/crates/cranelift-codegen-shared/0.115.0/download", + "sha256": "e184c9767afbe73d50c55ec29abcf4c32f9baf0d9d22b86d58c4d55e06dee181" } }, "targets": [ @@ -14390,7 +15092,7 @@ "**" ], "edition": "2021", - "version": "0.114.0" + "version": "0.115.0" }, "license": "Apache-2.0 WITH LLVM-exception", "license_ids": [ @@ -14398,14 +15100,14 @@ ], "license_file": "LICENSE" }, - "cranelift-control 0.114.0": { + "cranelift-control 0.115.0": { "name": "cranelift-control", - "version": "0.114.0", + "version": "0.115.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/cranelift-control/0.114.0/download", - "sha256": "29cb4018f5bf59fb53f515fa9d80e6f8c5ce19f198dc538984ebd23ecf8965ec" + "url": "https://static.crates.io/crates/cranelift-control/0.115.0/download", + "sha256": "5cc7664f2a66f053e33f149e952bb5971d138e3af637f5097727ed6dc0ed95dd" } }, "targets": [ @@ -14444,7 +15146,7 @@ "selects": {} }, "edition": "2021", - "version": "0.114.0" + "version": "0.115.0" }, "license": "Apache-2.0 WITH LLVM-exception", "license_ids": [ @@ -14452,14 +15154,14 @@ ], "license_file": "LICENSE" }, - "cranelift-entity 0.114.0": { + "cranelift-entity 0.115.0": { "name": "cranelift-entity", - "version": "0.114.0", + "version": "0.115.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/cranelift-entity/0.114.0/download", - "sha256": "305399fd781a2953ac78c1396f02ff53144f39c33eb7fc7789cf4e8936d13a96" + "url": "https://static.crates.io/crates/cranelift-entity/0.115.0/download", + "sha256": "118597e3a9cf86c3556fa579a7a23b955fa18231651a52a77a2475d305a9cf84" } }, "targets": [ @@ -14492,11 +15194,11 @@ "deps": { "common": [ { - "id": "cranelift-bitset 0.114.0", + "id": "cranelift-bitset 0.115.0", "target": "cranelift_bitset" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -14506,13 +15208,13 @@ "proc_macro_deps": { "common": [ { - "id": "serde_derive 1.0.214", + "id": "serde_derive 1.0.217", "target": "serde_derive" } ], "selects": {} }, - "version": "0.114.0" + "version": "0.115.0" }, "license": "Apache-2.0 WITH LLVM-exception", "license_ids": [ @@ -14520,14 +15222,14 @@ ], "license_file": "LICENSE" }, - "cranelift-frontend 0.114.0": { + "cranelift-frontend 0.115.0": { "name": "cranelift-frontend", - "version": "0.114.0", + "version": "0.115.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/cranelift-frontend/0.114.0/download", - "sha256": "9230b460a128d53653456137751d27baf567947a3ab8c0c4d6e31fd08036d81e" + "url": "https://static.crates.io/crates/cranelift-frontend/0.115.0/download", + "sha256": "7638ea1efb069a0aa18d8ee67401b6b0d19f6bfe5de5e9ede348bfc80bb0d8c7" } }, "targets": [ @@ -14559,7 +15261,7 @@ "deps": { "common": [ { - "id": "cranelift-codegen 0.114.0", + "id": "cranelift-codegen 0.115.0", "target": "cranelift_codegen" }, { @@ -14578,7 +15280,7 @@ "selects": {} }, "edition": "2021", - "version": "0.114.0" + "version": "0.115.0" }, "license": "Apache-2.0 WITH LLVM-exception", "license_ids": [ @@ -14586,14 +15288,14 @@ ], "license_file": "LICENSE" }, - "cranelift-isle 0.114.0": { + "cranelift-isle 0.115.0": { "name": "cranelift-isle", - "version": "0.114.0", + "version": "0.115.0", "package_url": "https://github.com/bytecodealliance/wasmtime/tree/main/cranelift/isle", "repository": { "Http": { - "url": "https://static.crates.io/crates/cranelift-isle/0.114.0/download", - "sha256": "b961e24ae3ec9813a24a15ae64bbd2a42e4de4d79a7f3225a412e3b94e78d1c8", + "url": "https://static.crates.io/crates/cranelift-isle/0.115.0/download", + "sha256": "15c53e1152a0b01c4ed2b1e0535602b8e86458777dd9d18b28732b16325c7dc0", "patch_args": [ "-p4" ], @@ -14642,14 +15344,14 @@ "deps": { "common": [ { - "id": "cranelift-isle 0.114.0", + "id": "cranelift-isle 0.115.0", "target": "build_script_build" } ], "selects": {} }, "edition": "2021", - "version": "0.114.0" + "version": "0.115.0" }, "build_script_attrs": { "compile_data_glob": [ @@ -14665,14 +15367,14 @@ ], "license_file": null }, - "cranelift-native 0.114.0": { + "cranelift-native 0.115.0": { "name": "cranelift-native", - "version": "0.114.0", + "version": "0.115.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/cranelift-native/0.114.0/download", - "sha256": "4d5bd76df6c9151188dfa428c863b33da5b34561b67f43c0cf3f24a794f9fa1f" + "url": "https://static.crates.io/crates/cranelift-native/0.115.0/download", + "sha256": "7b7d8f895444fa52dd7bdd0bed11bf007a7fb43af65a6deac8fcc4094c6372f7" } }, "targets": [ @@ -14704,7 +15406,7 @@ "deps": { "common": [ { - "id": "cranelift-codegen 0.114.0", + "id": "cranelift-codegen 0.115.0", "target": "cranelift_codegen" }, { @@ -14722,7 +15424,7 @@ } }, "edition": "2021", - "version": "0.114.0" + "version": "0.115.0" }, "license": "Apache-2.0 WITH LLVM-exception", "license_ids": [ @@ -14911,7 +15613,7 @@ "target": "regex" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -14937,7 +15639,7 @@ "proc_macro_deps": { "common": [ { - "id": "serde_derive 1.0.214", + "id": "serde_derive 1.0.217", "target": "serde_derive" } ], @@ -15472,7 +16174,7 @@ "target": "signal_hook_mio" } ], - "aarch64-fuchsia": [ + "aarch64-linux-android": [ { "id": "mio 0.8.10", "target": "mio" @@ -15486,7 +16188,17 @@ "target": "signal_hook_mio" } ], - "aarch64-linux-android": [ + "aarch64-pc-windows-msvc": [ + { + "id": "crossterm_winapi 0.9.1", + "target": "crossterm_winapi" + }, + { + "id": "winapi 0.3.9", + "target": "winapi" + } + ], + "aarch64-unknown-fuchsia": [ { "id": "mio 0.8.10", "target": "mio" @@ -15500,16 +16212,6 @@ "target": "signal_hook_mio" } ], - "aarch64-pc-windows-msvc": [ - { - "id": "crossterm_winapi 0.9.1", - "target": "crossterm_winapi" - }, - { - "id": "winapi 0.3.9", - "target": "winapi" - } - ], "aarch64-unknown-linux-gnu": [ { "id": "mio 0.8.10", @@ -15722,7 +16424,7 @@ "target": "signal_hook_mio" } ], - "x86_64-fuchsia": [ + "x86_64-linux-android": [ { "id": "mio 0.8.10", "target": "mio" @@ -15736,7 +16438,17 @@ "target": "signal_hook_mio" } ], - "x86_64-linux-android": [ + "x86_64-pc-windows-msvc": [ + { + "id": "crossterm_winapi 0.9.1", + "target": "crossterm_winapi" + }, + { + "id": "winapi 0.3.9", + "target": "winapi" + } + ], + "x86_64-unknown-freebsd": [ { "id": "mio 0.8.10", "target": "mio" @@ -15750,17 +16462,7 @@ "target": "signal_hook_mio" } ], - "x86_64-pc-windows-msvc": [ - { - "id": "crossterm_winapi 0.9.1", - "target": "crossterm_winapi" - }, - { - "id": "winapi 0.3.9", - "target": "winapi" - } - ], - "x86_64-unknown-freebsd": [ + "x86_64-unknown-fuchsia": [ { "id": "mio 0.8.10", "target": "mio" @@ -15938,6 +16640,74 @@ ], "license_file": null }, + "crypto-bigint 0.4.9": { + "name": "crypto-bigint", + "version": "0.4.9", + "package_url": "https://github.com/RustCrypto/crypto-bigint", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/crypto-bigint/0.4.9/download", + "sha256": "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" + } + }, + "targets": [ + { + "Library": { + "crate_name": "crypto_bigint", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "crypto_bigint", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "generic-array", + "rand_core", + "zeroize" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "generic-array 0.14.7", + "target": "generic_array" + }, + { + "id": "rand_core 0.6.4", + "target": "rand_core" + }, + { + "id": "subtle 2.6.1", + "target": "subtle" + }, + { + "id": "zeroize 1.8.1", + "target": "zeroize" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.4.9" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "crypto-bigint 0.5.2": { "name": "crypto-bigint", "version": "0.5.2", @@ -16233,7 +17003,7 @@ "target": "ryu" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -16303,6 +17073,54 @@ ], "license_file": "LICENSE-MIT" }, + "ctr 0.9.2": { + "name": "ctr", + "version": "0.9.2", + "package_url": "https://github.com/RustCrypto/block-modes", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/ctr/0.9.2/download", + "sha256": "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" + } + }, + "targets": [ + { + "Library": { + "crate_name": "ctr", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "ctr", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "cipher 0.4.4", + "target": "cipher" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.9.2" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "ctrlc 3.4.5": { "name": "ctrlc", "version": "3.4.5", @@ -16745,14 +17563,14 @@ ], "license_file": "LICENSE" }, - "darling 0.14.4": { + "darling 0.20.10": { "name": "darling", - "version": "0.14.4", + "version": "0.20.10", "package_url": "https://github.com/TedDriggs/darling", "repository": { "Http": { - "url": "https://static.crates.io/crates/darling/0.14.4/download", - "sha256": "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" + "url": "https://static.crates.io/crates/darling/0.20.10/download", + "sha256": "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" } }, "targets": [ @@ -16784,86 +17602,23 @@ "deps": { "common": [ { - "id": "darling_core 0.14.4", + "id": "darling_core 0.20.10", "target": "darling_core" } ], "selects": {} }, - "edition": "2018", - "proc_macro_deps": { - "common": [ - { - "id": "darling_macro 0.14.4", - "target": "darling_macro" - } - ], - "selects": {} - }, - "version": "0.14.4" - }, - "license": "MIT", - "license_ids": [ - "MIT" - ], - "license_file": "LICENSE" - }, - "darling 0.20.3": { - "name": "darling", - "version": "0.20.3", - "package_url": "https://github.com/TedDriggs/darling", - "repository": { - "Http": { - "url": "https://static.crates.io/crates/darling/0.20.3/download", - "sha256": "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" - } - }, - "targets": [ - { - "Library": { - "crate_name": "darling", - "crate_root": "src/lib.rs", - "srcs": { - "allow_empty": true, - "include": [ - "**/*.rs" - ] - } - } - } - ], - "library_target_name": "darling", - "common_attrs": { - "compile_data_glob": [ - "**" - ], - "crate_features": { - "common": [ - "default", - "suggestions" - ], - "selects": {} - }, - "deps": { - "common": [ - { - "id": "darling_core 0.20.3", - "target": "darling_core" - } - ], - "selects": {} - }, - "edition": "2018", + "edition": "2021", "proc_macro_deps": { "common": [ { - "id": "darling_macro 0.20.3", + "id": "darling_macro 0.20.10", "target": "darling_macro" } ], "selects": {} }, - "version": "0.20.3" + "version": "0.20.10" }, "license": "MIT", "license_ids": [ @@ -16945,14 +17700,14 @@ ], "license_file": "LICENSE" }, - "darling_core 0.14.4": { + "darling_core 0.20.10": { "name": "darling_core", - "version": "0.14.4", + "version": "0.20.10", "package_url": "https://github.com/TedDriggs/darling", "repository": { "Http": { - "url": "https://static.crates.io/crates/darling_core/0.14.4/download", - "sha256": "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" + "url": "https://static.crates.io/crates/darling_core/0.20.10/download", + "sha256": "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" } }, "targets": [ @@ -17000,81 +17755,7 @@ "target": "quote" }, { - "id": "strsim 0.10.0", - "target": "strsim" - }, - { - "id": "syn 1.0.109", - "target": "syn" - } - ], - "selects": {} - }, - "edition": "2018", - "version": "0.14.4" - }, - "license": "MIT", - "license_ids": [ - "MIT" - ], - "license_file": "LICENSE" - }, - "darling_core 0.20.3": { - "name": "darling_core", - "version": "0.20.3", - "package_url": "https://github.com/TedDriggs/darling", - "repository": { - "Http": { - "url": "https://static.crates.io/crates/darling_core/0.20.3/download", - "sha256": "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" - } - }, - "targets": [ - { - "Library": { - "crate_name": "darling_core", - "crate_root": "src/lib.rs", - "srcs": { - "allow_empty": true, - "include": [ - "**/*.rs" - ] - } - } - } - ], - "library_target_name": "darling_core", - "common_attrs": { - "compile_data_glob": [ - "**" - ], - "crate_features": { - "common": [ - "strsim", - "suggestions" - ], - "selects": {} - }, - "deps": { - "common": [ - { - "id": "fnv 1.0.7", - "target": "fnv" - }, - { - "id": "ident_case 1.0.1", - "target": "ident_case" - }, - { - "id": "proc-macro2 1.0.89", - "target": "proc_macro2" - }, - { - "id": "quote 1.0.37", - "target": "quote" - }, - { - "id": "strsim 0.10.0", + "id": "strsim 0.11.1", "target": "strsim" }, { @@ -17084,8 +17765,8 @@ ], "selects": {} }, - "edition": "2018", - "version": "0.20.3" + "edition": "2021", + "version": "0.20.10" }, "license": "MIT", "license_ids": [ @@ -17148,14 +17829,14 @@ ], "license_file": "LICENSE" }, - "darling_macro 0.14.4": { + "darling_macro 0.20.10": { "name": "darling_macro", - "version": "0.14.4", + "version": "0.20.10", "package_url": "https://github.com/TedDriggs/darling", "repository": { "Http": { - "url": "https://static.crates.io/crates/darling_macro/0.14.4/download", - "sha256": "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" + "url": "https://static.crates.io/crates/darling_macro/0.20.10/download", + "sha256": "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" } }, "targets": [ @@ -17180,62 +17861,7 @@ "deps": { "common": [ { - "id": "darling_core 0.14.4", - "target": "darling_core" - }, - { - "id": "quote 1.0.37", - "target": "quote" - }, - { - "id": "syn 1.0.109", - "target": "syn" - } - ], - "selects": {} - }, - "edition": "2018", - "version": "0.14.4" - }, - "license": "MIT", - "license_ids": [ - "MIT" - ], - "license_file": "LICENSE" - }, - "darling_macro 0.20.3": { - "name": "darling_macro", - "version": "0.20.3", - "package_url": "https://github.com/TedDriggs/darling", - "repository": { - "Http": { - "url": "https://static.crates.io/crates/darling_macro/0.20.3/download", - "sha256": "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" - } - }, - "targets": [ - { - "ProcMacro": { - "crate_name": "darling_macro", - "crate_root": "src/lib.rs", - "srcs": { - "allow_empty": true, - "include": [ - "**/*.rs" - ] - } - } - } - ], - "library_target_name": "darling_macro", - "common_attrs": { - "compile_data_glob": [ - "**" - ], - "deps": { - "common": [ - { - "id": "darling_core 0.20.3", + "id": "darling_core 0.20.10", "target": "darling_core" }, { @@ -17249,8 +17875,8 @@ ], "selects": {} }, - "edition": "2018", - "version": "0.20.3" + "edition": "2021", + "version": "0.20.10" }, "license": "MIT", "license_ids": [ @@ -17473,6 +18099,141 @@ ], "license_file": "LICENSE" }, + "dbus 0.9.7": { + "name": "dbus", + "version": "0.9.7", + "package_url": "https://github.com/diwic/dbus-rs", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/dbus/0.9.7/download", + "sha256": "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" + } + }, + "targets": [ + { + "Library": { + "crate_name": "dbus", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "dbus", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "vendored" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "libc 0.2.158", + "target": "libc" + }, + { + "id": "libdbus-sys 0.2.5", + "target": "libdbus_sys" + } + ], + "selects": { + "cfg(windows)": [ + { + "id": "winapi 0.3.9", + "target": "winapi" + } + ] + } + }, + "edition": "2018", + "version": "0.9.7" + }, + "license": "Apache-2.0/MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, + "dbus-secret-service 4.0.3": { + "name": "dbus-secret-service", + "version": "4.0.3", + "package_url": "https://github.com/brotskydotcom/dbus-secret-service.git", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/dbus-secret-service/4.0.3/download", + "sha256": "b42a16374481d92aed73ae45b1f120207d8e71d24fb89f357fadbd8f946fd84b" + } + }, + "targets": [ + { + "Library": { + "crate_name": "dbus_secret_service", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "dbus_secret_service", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "vendored" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "dbus 0.9.7", + "target": "dbus" + }, + { + "id": "futures-util 0.3.31", + "target": "futures_util" + }, + { + "id": "num 0.4.3", + "target": "num" + }, + { + "id": "once_cell 1.19.0", + "target": "once_cell" + }, + { + "id": "rand 0.8.5", + "target": "rand" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "4.0.3" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "debugid 0.8.0": { "name": "debugid", "version": "0.8.0", @@ -17520,6 +18281,74 @@ ], "license_file": "LICENSE" }, + "der 0.6.1": { + "name": "der", + "version": "0.6.1", + "package_url": "https://github.com/RustCrypto/formats/tree/master/der", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/der/0.6.1/download", + "sha256": "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" + } + }, + "targets": [ + { + "Library": { + "crate_name": "der", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "der", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "alloc", + "const-oid", + "oid", + "pem", + "pem-rfc7468", + "std", + "zeroize" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "const-oid 0.9.4", + "target": "const_oid" + }, + { + "id": "pem-rfc7468 0.6.0", + "target": "pem_rfc7468" + }, + { + "id": "zeroize 1.8.1", + "target": "zeroize" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.6.1" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "der 0.7.7": { "name": "der", "version": "0.7.7", @@ -17772,6 +18601,7 @@ "common": [ "alloc", "powerfmt", + "serde", "std" ], "selects": {} @@ -17781,6 +18611,10 @@ { "id": "powerfmt 0.2.0", "target": "powerfmt" + }, + { + "id": "serde 1.0.217", + "target": "serde" } ], "selects": {} @@ -18003,6 +18837,270 @@ ], "license_file": "LICENSE" }, + "dfx-core 0.1.3": { + "name": "dfx-core", + "version": "0.1.3", + "package_url": "https://github.com/dfinity/sdk", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/dfx-core/0.1.3/download", + "sha256": "b4be4fc6929580b0ef8ac3678c03f31b59ffb1aa0d90ab754842d40cfc5b4552" + } + }, + "targets": [ + { + "Library": { + "crate_name": "dfx_core", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "dfx_core", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "aes-gcm 0.10.3", + "target": "aes_gcm" + }, + { + "id": "argon2 0.4.1", + "target": "argon2" + }, + { + "id": "backoff 0.4.0", + "target": "backoff" + }, + { + "id": "bip32 0.4.0", + "target": "bip32" + }, + { + "id": "byte-unit 4.0.19", + "target": "byte_unit" + }, + { + "id": "bytes 1.9.0", + "target": "bytes" + }, + { + "id": "candid 0.10.13", + "target": "candid" + }, + { + "id": "clap 4.5.20", + "target": "clap" + }, + { + "id": "dialoguer 0.11.0", + "target": "dialoguer" + }, + { + "id": "directories-next 2.0.0", + "target": "directories_next" + }, + { + "id": "dunce 1.0.5", + "target": "dunce" + }, + { + "id": "flate2 1.0.31", + "target": "flate2" + }, + { + "id": "handlebars 4.5.0", + "target": "handlebars" + }, + { + "id": "hex 0.4.3", + "target": "hex" + }, + { + "id": "humantime-serde 1.1.1", + "target": "humantime_serde" + }, + { + "id": "ic-agent 0.39.2", + "target": "ic_agent" + }, + { + "id": "ic-identity-hsm 0.39.2", + "target": "ic_identity_hsm" + }, + { + "id": "ic-utils 0.39.0", + "target": "ic_utils" + }, + { + "id": "itertools 0.10.5", + "target": "itertools" + }, + { + "id": "k256 0.11.6", + "target": "k256" + }, + { + "id": "keyring 3.4.0", + "target": "keyring" + }, + { + "id": "lazy_static 1.4.0", + "target": "lazy_static" + }, + { + "id": "reqwest 0.12.9", + "target": "reqwest" + }, + { + "id": "ring 0.16.20", + "target": "ring" + }, + { + "id": "schemars 0.8.16", + "target": "schemars" + }, + { + "id": "sec1 0.3.0", + "target": "sec1" + }, + { + "id": "semver 1.0.22", + "target": "semver" + }, + { + "id": "serde 1.0.217", + "target": "serde" + }, + { + "id": "serde_json 1.0.132", + "target": "serde_json" + }, + { + "id": "sha2 0.10.8", + "target": "sha2" + }, + { + "id": "slog 2.7.0", + "target": "slog" + }, + { + "id": "tar 0.4.39", + "target": "tar" + }, + { + "id": "tempfile 3.12.0", + "target": "tempfile" + }, + { + "id": "thiserror 1.0.68", + "target": "thiserror" + }, + { + "id": "time 0.3.36", + "target": "time" + }, + { + "id": "tiny-bip39 1.0.0", + "target": "bip39" + }, + { + "id": "url 2.5.3", + "target": "url" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.1.3" + }, + "license": "Apache-2.0", + "license_ids": [ + "Apache-2.0" + ], + "license_file": null + }, + "dialoguer 0.11.0": { + "name": "dialoguer", + "version": "0.11.0", + "package_url": "https://github.com/console-rs/dialoguer", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/dialoguer/0.11.0/download", + "sha256": "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de" + } + }, + "targets": [ + { + "Library": { + "crate_name": "dialoguer", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "dialoguer", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "default", + "editor", + "password", + "tempfile", + "zeroize" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "console 0.15.7", + "target": "console" + }, + { + "id": "shell-words 1.1.0", + "target": "shell_words" + }, + { + "id": "tempfile 3.12.0", + "target": "tempfile" + }, + { + "id": "thiserror 1.0.68", + "target": "thiserror" + }, + { + "id": "zeroize 1.8.1", + "target": "zeroize" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.11.0" + }, + "license": "MIT", + "license_ids": [ + "MIT" + ], + "license_file": "LICENSE" + }, "diff 0.1.13": { "name": "diff", "version": "0.1.13", @@ -18341,15 +19439,15 @@ }, { "id": "bitcoin 0.28.2", - "target": "bitcoin" + "target": "bitcoin", + "alias": "bitcoin_0_28" }, { - "id": "bitcoin 0.32.2", - "target": "bitcoin", - "alias": "bitcoin_0_32" + "id": "bitcoin 0.32.5", + "target": "bitcoin" }, { - "id": "bitcoincore-rpc 0.15.0", + "id": "bitcoincore-rpc 0.19.0", "target": "bitcoincore_rpc" }, { @@ -18401,7 +19499,7 @@ "target": "canbench_rs" }, { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -18413,7 +19511,7 @@ "target": "cargo_metadata" }, { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" }, { @@ -18500,6 +19598,10 @@ "id": "dashmap 5.5.0", "target": "dashmap" }, + { + "id": "dfx-core 0.1.3", + "target": "dfx_core" + }, { "id": "dyn-clone 1.0.14", "target": "dyn_clone" @@ -18637,7 +19739,7 @@ "target": "hyper_util" }, { - "id": "ic-agent 0.37.1", + "id": "ic-agent 0.39.2", "target": "ic_agent" }, { @@ -18653,27 +19755,32 @@ "target": "ic_canister_log" }, { - "id": "ic-canister-sig-creation 1.0.1", + "id": "ic-canister-sig-creation 1.1.0", "target": "ic_canister_sig_creation" }, { - "id": "ic-cbor 2.6.0", + "id": "ic-cbor 3.0.2", "target": "ic_cbor" }, { "id": "ic-cdk 0.16.0", "target": "ic_cdk" }, + { + "id": "ic-cdk 0.18.0-alpha.1", + "target": "ic_cdk", + "alias": "ic_cdk_next" + }, { "id": "ic-cdk-timers 0.11.0", "target": "ic_cdk_timers" }, { - "id": "ic-certificate-verification 2.6.0", + "id": "ic-certificate-verification 3.0.2", "target": "ic_certificate_verification" }, { - "id": "ic-certification 2.6.0", + "id": "ic-certification 3.0.2", "target": "ic_certification" }, { @@ -18681,11 +19788,11 @@ "target": "ic_certified_map" }, { - "id": "ic-http-certification 2.6.0", + "id": "ic-http-certification 3.0.2", "target": "ic_http_certification" }, { - "id": "ic-http-gateway 0.0.0", + "id": "ic-http-gateway 0.1.0", "target": "ic_http_gateway" }, { @@ -18693,7 +19800,7 @@ "target": "ic_metrics_encoder" }, { - "id": "ic-response-verification 2.6.0", + "id": "ic-response-verification 3.0.2", "target": "ic_response_verification" }, { @@ -18709,11 +19816,11 @@ "target": "ic_test_state_machine_client" }, { - "id": "ic-transport-types 0.37.1", + "id": "ic-transport-types 0.39.2", "target": "ic_transport_types" }, { - "id": "ic-utils 0.37.0", + "id": "ic-utils 0.39.0", "target": "ic_utils" }, { @@ -18777,7 +19884,7 @@ "target": "intmap" }, { - "id": "ipnet 2.8.0", + "id": "ipnet 2.10.1", "target": "ipnet" }, { @@ -18946,7 +20053,7 @@ "target": "opentelemetry_prometheus" }, { - "id": "opentelemetry_sdk 0.27.0", + "id": "opentelemetry_sdk 0.27.1", "target": "opentelemetry_sdk" }, { @@ -19187,7 +20294,7 @@ "target": "semver" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -19294,6 +20401,10 @@ "id": "syn 1.0.109", "target": "syn" }, + { + "id": "syscalls 0.6.18", + "target": "syscalls" + }, { "id": "tar 0.4.39", "target": "tar" @@ -19362,6 +20473,10 @@ "id": "tokio-socks 0.5.2", "target": "tokio_socks" }, + { + "id": "tokio-stream 0.1.17", + "target": "tokio_stream" + }, { "id": "tokio-test 0.4.4", "target": "tokio_test" @@ -19479,11 +20594,11 @@ "target": "wasmprinter" }, { - "id": "wasmtime 27.0.0", + "id": "wasmtime 28.0.0", "target": "wasmtime" }, { - "id": "wasmtime-environ 27.0.0", + "id": "wasmtime-environ 28.0.0", "target": "wasmtime_environ" }, { @@ -19552,6 +20667,11 @@ "id": "ic-cdk-macros 0.9.0", "target": "ic_cdk_macros" }, + { + "id": "ic-cdk-macros 0.18.0-alpha.1", + "target": "ic_cdk_macros", + "alias": "ic_cdk_macros_next" + }, { "id": "indoc 1.0.9", "target": "indoc" @@ -19593,6 +20713,58 @@ "license_ids": [], "license_file": null }, + "directories-next 2.0.0": { + "name": "directories-next", + "version": "2.0.0", + "package_url": "https://github.com/xdg-rs/dirs/tree/master/directories", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/directories-next/2.0.0/download", + "sha256": "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" + } + }, + "targets": [ + { + "Library": { + "crate_name": "directories_next", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "directories_next", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "cfg-if 1.0.0", + "target": "cfg_if" + }, + { + "id": "dirs-sys-next 0.1.2", + "target": "dirs_sys_next" + } + ], + "selects": {} + }, + "edition": "2018", + "version": "2.0.0" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "dirs 2.0.2": { "name": "dirs", "version": "2.0.2", @@ -20134,6 +21306,46 @@ ], "license_file": "LICENSE" }, + "dunce 1.0.5": { + "name": "dunce", + "version": "1.0.5", + "package_url": "https://gitlab.com/kornelski/dunce", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/dunce/1.0.5/download", + "sha256": "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + } + }, + "targets": [ + { + "Library": { + "crate_name": "dunce", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "dunce", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "edition": "2021", + "version": "1.0.5" + }, + "license": "CC0-1.0 OR MIT-0 OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "CC0-1.0", + "MIT-0" + ], + "license_file": "LICENSE" + }, "duration-string 0.3.0": { "name": "duration-string", "version": "0.3.0", @@ -20172,7 +21384,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -20224,6 +21436,82 @@ ], "license_file": "LICENSE-APACHE" }, + "ecdsa 0.14.8": { + "name": "ecdsa", + "version": "0.14.8", + "package_url": "https://github.com/RustCrypto/signatures/tree/master/ecdsa", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/ecdsa/0.14.8/download", + "sha256": "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" + } + }, + "targets": [ + { + "Library": { + "crate_name": "ecdsa", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "ecdsa", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "alloc", + "arithmetic", + "der", + "digest", + "hazmat", + "pem", + "pkcs8", + "rfc6979", + "sign", + "std", + "verify" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "der 0.6.1", + "target": "der" + }, + { + "id": "elliptic-curve 0.12.3", + "target": "elliptic_curve" + }, + { + "id": "rfc6979 0.3.1", + "target": "rfc6979" + }, + { + "id": "signature 1.6.4", + "target": "signature" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.14.8" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "ecdsa 0.16.9": { "name": "ecdsa", "version": "0.16.9", @@ -20257,6 +21545,7 @@ "common": [ "alloc", "arithmetic", + "default", "der", "digest", "hazmat", @@ -20424,7 +21713,7 @@ "target": "rand_core" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -20710,6 +21999,118 @@ ], "license_file": "LICENSE-APACHE" }, + "elliptic-curve 0.12.3": { + "name": "elliptic-curve", + "version": "0.12.3", + "package_url": "https://github.com/RustCrypto/traits/tree/master/elliptic-curve", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/elliptic-curve/0.12.3/download", + "sha256": "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" + } + }, + "targets": [ + { + "Library": { + "crate_name": "elliptic_curve", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "elliptic_curve", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "alloc", + "arithmetic", + "digest", + "ff", + "group", + "hazmat", + "pem", + "pem-rfc7468", + "pkcs8", + "sec1", + "std" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "base16ct 0.1.1", + "target": "base16ct" + }, + { + "id": "crypto-bigint 0.4.9", + "target": "crypto_bigint" + }, + { + "id": "der 0.6.1", + "target": "der" + }, + { + "id": "digest 0.10.7", + "target": "digest" + }, + { + "id": "ff 0.12.1", + "target": "ff" + }, + { + "id": "generic-array 0.14.7", + "target": "generic_array" + }, + { + "id": "group 0.12.1", + "target": "group" + }, + { + "id": "pem-rfc7468 0.6.0", + "target": "pem_rfc7468" + }, + { + "id": "pkcs8 0.9.0", + "target": "pkcs8" + }, + { + "id": "rand_core 0.6.4", + "target": "rand_core" + }, + { + "id": "sec1 0.3.0", + "target": "sec1" + }, + { + "id": "subtle 2.6.1", + "target": "subtle" + }, + { + "id": "zeroize 1.8.1", + "target": "zeroize" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.12.3" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "elliptic-curve 0.13.8": { "name": "elliptic-curve", "version": "0.13.8", @@ -20743,6 +22144,7 @@ "common": [ "alloc", "arithmetic", + "default", "digest", "ff", "group", @@ -21633,7 +23035,7 @@ "target": "build_script_build" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -21879,7 +23281,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" } ], @@ -21954,7 +23356,7 @@ "target": "once_cell" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -22044,7 +23446,7 @@ "target": "regex" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -22332,7 +23734,7 @@ "target": "rlp" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -22413,7 +23815,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -22429,6 +23831,45 @@ ], "license_file": "LICENSE-APACHE" }, + "event-listener 2.5.3": { + "name": "event-listener", + "version": "2.5.3", + "package_url": "https://github.com/smol-rs/event-listener", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/event-listener/2.5.3/download", + "sha256": "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + } + }, + "targets": [ + { + "Library": { + "crate_name": "event_listener", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "event_listener", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "edition": "2018", + "version": "2.5.3" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "event-listener 4.0.3": { "name": "event-listener", "version": "4.0.3", @@ -22495,7 +23936,254 @@ "target": "parking" } ], - "aarch64-fuchsia": [ + "aarch64-linux-android": [ + { + "id": "parking 2.1.0", + "target": "parking" + } + ], + "aarch64-pc-windows-msvc": [ + { + "id": "parking 2.1.0", + "target": "parking" + } + ], + "aarch64-unknown-fuchsia": [ + { + "id": "parking 2.1.0", + "target": "parking" + } + ], + "aarch64-unknown-linux-gnu": [ + { + "id": "parking 2.1.0", + "target": "parking" + } + ], + "aarch64-unknown-nixos-gnu": [ + { + "id": "parking 2.1.0", + "target": "parking" + } + ], + "aarch64-unknown-nto-qnx710": [ + { + "id": "parking 2.1.0", + "target": "parking" + } + ], + "arm-unknown-linux-gnueabi": [ + { + "id": "parking 2.1.0", + "target": "parking" + } + ], + "armv7-linux-androideabi": [ + { + "id": "parking 2.1.0", + "target": "parking" + } + ], + "armv7-unknown-linux-gnueabi": [ + { + "id": "parking 2.1.0", + "target": "parking" + } + ], + "i686-apple-darwin": [ + { + "id": "parking 2.1.0", + "target": "parking" + } + ], + "i686-linux-android": [ + { + "id": "parking 2.1.0", + "target": "parking" + } + ], + "i686-pc-windows-msvc": [ + { + "id": "parking 2.1.0", + "target": "parking" + } + ], + "i686-unknown-freebsd": [ + { + "id": "parking 2.1.0", + "target": "parking" + } + ], + "i686-unknown-linux-gnu": [ + { + "id": "parking 2.1.0", + "target": "parking" + } + ], + "powerpc-unknown-linux-gnu": [ + { + "id": "parking 2.1.0", + "target": "parking" + } + ], + "riscv32imc-unknown-none-elf": [ + { + "id": "parking 2.1.0", + "target": "parking" + } + ], + "riscv64gc-unknown-none-elf": [ + { + "id": "parking 2.1.0", + "target": "parking" + } + ], + "s390x-unknown-linux-gnu": [ + { + "id": "parking 2.1.0", + "target": "parking" + } + ], + "thumbv7em-none-eabi": [ + { + "id": "parking 2.1.0", + "target": "parking" + } + ], + "thumbv8m.main-none-eabi": [ + { + "id": "parking 2.1.0", + "target": "parking" + } + ], + "x86_64-apple-darwin": [ + { + "id": "parking 2.1.0", + "target": "parking" + } + ], + "x86_64-apple-ios": [ + { + "id": "parking 2.1.0", + "target": "parking" + } + ], + "x86_64-linux-android": [ + { + "id": "parking 2.1.0", + "target": "parking" + } + ], + "x86_64-pc-windows-msvc": [ + { + "id": "parking 2.1.0", + "target": "parking" + } + ], + "x86_64-unknown-freebsd": [ + { + "id": "parking 2.1.0", + "target": "parking" + } + ], + "x86_64-unknown-fuchsia": [ + { + "id": "parking 2.1.0", + "target": "parking" + } + ], + "x86_64-unknown-linux-gnu": [ + { + "id": "parking 2.1.0", + "target": "parking" + } + ], + "x86_64-unknown-nixos-gnu": [ + { + "id": "parking 2.1.0", + "target": "parking" + } + ], + "x86_64-unknown-none": [ + { + "id": "parking 2.1.0", + "target": "parking" + } + ] + } + }, + "edition": "2021", + "version": "4.0.3" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, + "event-listener 5.3.1": { + "name": "event-listener", + "version": "5.3.1", + "package_url": "https://github.com/smol-rs/event-listener", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/event-listener/5.3.1/download", + "sha256": "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" + } + }, + "targets": [ + { + "Library": { + "crate_name": "event_listener", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "event_listener", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "default", + "parking", + "std" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "concurrent-queue 2.5.0", + "target": "concurrent_queue" + }, + { + "id": "pin-project-lite 0.2.13", + "target": "pin_project_lite" + } + ], + "selects": { + "aarch64-apple-darwin": [ + { + "id": "parking 2.1.0", + "target": "parking" + } + ], + "aarch64-apple-ios": [ + { + "id": "parking 2.1.0", + "target": "parking" + } + ], + "aarch64-apple-ios-sim": [ { "id": "parking 2.1.0", "target": "parking" @@ -22513,6 +24201,12 @@ "target": "parking" } ], + "aarch64-unknown-fuchsia": [ + { + "id": "parking 2.1.0", + "target": "parking" + } + ], "aarch64-unknown-linux-gnu": [ { "id": "parking 2.1.0", @@ -22627,12 +24321,6 @@ "target": "parking" } ], - "x86_64-fuchsia": [ - { - "id": "parking 2.1.0", - "target": "parking" - } - ], "x86_64-linux-android": [ { "id": "parking 2.1.0", @@ -22651,254 +24339,7 @@ "target": "parking" } ], - "x86_64-unknown-linux-gnu": [ - { - "id": "parking 2.1.0", - "target": "parking" - } - ], - "x86_64-unknown-nixos-gnu": [ - { - "id": "parking 2.1.0", - "target": "parking" - } - ], - "x86_64-unknown-none": [ - { - "id": "parking 2.1.0", - "target": "parking" - } - ] - } - }, - "edition": "2021", - "version": "4.0.3" - }, - "license": "Apache-2.0 OR MIT", - "license_ids": [ - "Apache-2.0", - "MIT" - ], - "license_file": "LICENSE-APACHE" - }, - "event-listener 5.3.1": { - "name": "event-listener", - "version": "5.3.1", - "package_url": "https://github.com/smol-rs/event-listener", - "repository": { - "Http": { - "url": "https://static.crates.io/crates/event-listener/5.3.1/download", - "sha256": "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" - } - }, - "targets": [ - { - "Library": { - "crate_name": "event_listener", - "crate_root": "src/lib.rs", - "srcs": { - "allow_empty": true, - "include": [ - "**/*.rs" - ] - } - } - } - ], - "library_target_name": "event_listener", - "common_attrs": { - "compile_data_glob": [ - "**" - ], - "crate_features": { - "common": [ - "default", - "parking", - "std" - ], - "selects": {} - }, - "deps": { - "common": [ - { - "id": "concurrent-queue 2.5.0", - "target": "concurrent_queue" - }, - { - "id": "pin-project-lite 0.2.13", - "target": "pin_project_lite" - } - ], - "selects": { - "aarch64-apple-darwin": [ - { - "id": "parking 2.1.0", - "target": "parking" - } - ], - "aarch64-apple-ios": [ - { - "id": "parking 2.1.0", - "target": "parking" - } - ], - "aarch64-apple-ios-sim": [ - { - "id": "parking 2.1.0", - "target": "parking" - } - ], - "aarch64-fuchsia": [ - { - "id": "parking 2.1.0", - "target": "parking" - } - ], - "aarch64-linux-android": [ - { - "id": "parking 2.1.0", - "target": "parking" - } - ], - "aarch64-pc-windows-msvc": [ - { - "id": "parking 2.1.0", - "target": "parking" - } - ], - "aarch64-unknown-linux-gnu": [ - { - "id": "parking 2.1.0", - "target": "parking" - } - ], - "aarch64-unknown-nixos-gnu": [ - { - "id": "parking 2.1.0", - "target": "parking" - } - ], - "aarch64-unknown-nto-qnx710": [ - { - "id": "parking 2.1.0", - "target": "parking" - } - ], - "arm-unknown-linux-gnueabi": [ - { - "id": "parking 2.1.0", - "target": "parking" - } - ], - "armv7-linux-androideabi": [ - { - "id": "parking 2.1.0", - "target": "parking" - } - ], - "armv7-unknown-linux-gnueabi": [ - { - "id": "parking 2.1.0", - "target": "parking" - } - ], - "i686-apple-darwin": [ - { - "id": "parking 2.1.0", - "target": "parking" - } - ], - "i686-linux-android": [ - { - "id": "parking 2.1.0", - "target": "parking" - } - ], - "i686-pc-windows-msvc": [ - { - "id": "parking 2.1.0", - "target": "parking" - } - ], - "i686-unknown-freebsd": [ - { - "id": "parking 2.1.0", - "target": "parking" - } - ], - "i686-unknown-linux-gnu": [ - { - "id": "parking 2.1.0", - "target": "parking" - } - ], - "powerpc-unknown-linux-gnu": [ - { - "id": "parking 2.1.0", - "target": "parking" - } - ], - "riscv32imc-unknown-none-elf": [ - { - "id": "parking 2.1.0", - "target": "parking" - } - ], - "riscv64gc-unknown-none-elf": [ - { - "id": "parking 2.1.0", - "target": "parking" - } - ], - "s390x-unknown-linux-gnu": [ - { - "id": "parking 2.1.0", - "target": "parking" - } - ], - "thumbv7em-none-eabi": [ - { - "id": "parking 2.1.0", - "target": "parking" - } - ], - "thumbv8m.main-none-eabi": [ - { - "id": "parking 2.1.0", - "target": "parking" - } - ], - "x86_64-apple-darwin": [ - { - "id": "parking 2.1.0", - "target": "parking" - } - ], - "x86_64-apple-ios": [ - { - "id": "parking 2.1.0", - "target": "parking" - } - ], - "x86_64-fuchsia": [ - { - "id": "parking 2.1.0", - "target": "parking" - } - ], - "x86_64-linux-android": [ - { - "id": "parking 2.1.0", - "target": "parking" - } - ], - "x86_64-pc-windows-msvc": [ - { - "id": "parking 2.1.0", - "target": "parking" - } - ], - "x86_64-unknown-freebsd": [ + "x86_64-unknown-fuchsia": [ { "id": "parking 2.1.0", "target": "parking" @@ -23082,7 +24523,7 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -23098,7 +24539,7 @@ "target": "num_bigint" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -23824,7 +25265,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" } ], @@ -24146,6 +25587,131 @@ ], "license_file": "LICENSE-APACHE" }, + "foldhash 0.1.4": { + "name": "foldhash", + "version": "0.1.4", + "package_url": "https://github.com/orlp/foldhash", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/foldhash/0.1.4/download", + "sha256": "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + } + }, + "targets": [ + { + "Library": { + "crate_name": "foldhash", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "foldhash", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "edition": "2021", + "version": "0.1.4" + }, + "license": "Zlib", + "license_ids": [ + "Zlib" + ], + "license_file": "LICENSE" + }, + "foreign-types 0.3.2": { + "name": "foreign-types", + "version": "0.3.2", + "package_url": "https://github.com/sfackler/foreign-types", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/foreign-types/0.3.2/download", + "sha256": "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" + } + }, + "targets": [ + { + "Library": { + "crate_name": "foreign_types", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "foreign_types", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "foreign-types-shared 0.1.1", + "target": "foreign_types_shared" + } + ], + "selects": {} + }, + "edition": "2015", + "version": "0.3.2" + }, + "license": "MIT/Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, + "foreign-types-shared 0.1.1": { + "name": "foreign-types-shared", + "version": "0.1.1", + "package_url": "https://github.com/sfackler/foreign-types", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/foreign-types-shared/0.1.1/download", + "sha256": "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + } + }, + "targets": [ + { + "Library": { + "crate_name": "foreign_types_shared", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "foreign_types_shared", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "edition": "2015", + "version": "0.1.1" + }, + "license": "MIT/Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "form_urlencoded 1.2.1": { "name": "form_urlencoded", "version": "1.2.1", @@ -25733,6 +27299,58 @@ ], "license_file": "LICENSE-APACHE" }, + "ghash 0.5.1": { + "name": "ghash", + "version": "0.5.1", + "package_url": "https://github.com/RustCrypto/universal-hashes", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/ghash/0.5.1/download", + "sha256": "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" + } + }, + "targets": [ + { + "Library": { + "crate_name": "ghash", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "ghash", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "opaque-debug 0.3.0", + "target": "opaque_debug" + }, + { + "id": "polyval 0.6.2", + "target": "polyval" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.5.1" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "gimli 0.26.2": { "name": "gimli", "version": "0.26.2", @@ -26041,6 +27659,62 @@ ], "license_file": null }, + "group 0.12.1": { + "name": "group", + "version": "0.12.1", + "package_url": "https://github.com/zkcrypto/group", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/group/0.12.1/download", + "sha256": "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" + } + }, + "targets": [ + { + "Library": { + "crate_name": "group", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "group", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "ff 0.12.1", + "target": "ff" + }, + { + "id": "rand_core 0.6.4", + "target": "rand_core" + }, + { + "id": "subtle 2.6.1", + "target": "subtle" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.12.1" + }, + "license": "MIT/Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "group 0.13.0": { "name": "group", "version": "0.13.0", @@ -26323,6 +27997,84 @@ ], "license_file": "LICENSE" }, + "handlebars 4.5.0": { + "name": "handlebars", + "version": "4.5.0", + "package_url": "https://github.com/sunng87/handlebars-rust", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/handlebars/4.5.0/download", + "sha256": "faa67bab9ff362228eb3d00bd024a4965d8231bbb7921167f0cfa66c6626b225" + } + }, + "targets": [ + { + "Library": { + "crate_name": "handlebars", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "handlebars", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "default" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "log 0.4.20", + "target": "log" + }, + { + "id": "pest 2.7.1", + "target": "pest" + }, + { + "id": "serde 1.0.217", + "target": "serde" + }, + { + "id": "serde_json 1.0.132", + "target": "serde_json" + }, + { + "id": "thiserror 1.0.68", + "target": "thiserror" + } + ], + "selects": {} + }, + "edition": "2021", + "proc_macro_deps": { + "common": [ + { + "id": "pest_derive 2.7.1", + "target": "pest_derive" + } + ], + "selects": {} + }, + "version": "4.5.0" + }, + "license": "MIT", + "license_ids": [ + "MIT" + ], + "license_file": "LICENSE" + }, "hashbrown 0.12.3": { "name": "hashbrown", "version": "0.12.3", @@ -26415,11 +28167,11 @@ "target": "ahash" }, { - "id": "allocator-api2 0.2.16", + "id": "allocator-api2 0.2.21", "target": "allocator_api2" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -26435,6 +28187,60 @@ ], "license_file": "LICENSE-APACHE" }, + "hashbrown 0.15.2": { + "name": "hashbrown", + "version": "0.15.2", + "package_url": "https://github.com/rust-lang/hashbrown", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/hashbrown/0.15.2/download", + "sha256": "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + } + }, + "targets": [ + { + "Library": { + "crate_name": "hashbrown", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "hashbrown", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "default-hasher" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "foldhash 0.1.4", + "target": "foldhash" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.15.2" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "hashlink 0.8.3": { "name": "hashlink", "version": "0.8.3", @@ -27074,7 +28880,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -27121,7 +28927,8 @@ ], "crate_features": { "common": [ - "alloc" + "alloc", + "std" ], "selects": {} }, @@ -27377,7 +29184,7 @@ "target": "idna" }, { - "id": "ipnet 2.8.0", + "id": "ipnet 2.10.1", "target": "ipnet" }, { @@ -28137,62 +29944,6 @@ ], "license_file": "LICENSE" }, - "http-body-to-bytes 0.2.0": { - "name": "http-body-to-bytes", - "version": "0.2.0", - "package_url": "https://github.com/bk-rs/http-body-ext", - "repository": { - "Http": { - "url": "https://static.crates.io/crates/http-body-to-bytes/0.2.0/download", - "sha256": "17a08236c6f51c2ee95d840f45acf8fa9e339390e00b4ef640857b2f2a534d70" - } - }, - "targets": [ - { - "Library": { - "crate_name": "http_body_to_bytes", - "crate_root": "src/lib.rs", - "srcs": { - "allow_empty": true, - "include": [ - "**/*.rs" - ] - } - } - } - ], - "library_target_name": "http_body_to_bytes", - "common_attrs": { - "compile_data_glob": [ - "**" - ], - "deps": { - "common": [ - { - "id": "bytes 1.9.0", - "target": "bytes" - }, - { - "id": "http-body 1.0.1", - "target": "http_body" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - } - ], - "selects": {} - }, - "edition": "2021", - "version": "0.2.0" - }, - "license": "Apache-2.0 OR MIT", - "license_ids": [ - "Apache-2.0", - "MIT" - ], - "license_file": "LICENSE-APACHE" - }, "http-body-util 0.1.2": { "name": "http-body-util", "version": "0.1.2", @@ -28493,7 +30244,7 @@ "target": "humantime" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -28961,15 +30712,15 @@ "webpki-roots", "webpki-tokio" ], - "aarch64-fuchsia": [ + "aarch64-linux-android": [ "webpki-roots", "webpki-tokio" ], - "aarch64-linux-android": [ + "aarch64-pc-windows-msvc": [ "webpki-roots", "webpki-tokio" ], - "aarch64-pc-windows-msvc": [ + "aarch64-unknown-fuchsia": [ "webpki-roots", "webpki-tokio" ], @@ -29049,10 +30800,6 @@ "webpki-roots", "webpki-tokio" ], - "x86_64-fuchsia": [ - "webpki-roots", - "webpki-tokio" - ], "x86_64-linux-android": [ "webpki-roots", "webpki-tokio" @@ -29065,6 +30812,10 @@ "webpki-roots", "webpki-tokio" ], + "x86_64-unknown-fuchsia": [ + "webpki-roots", + "webpki-tokio" + ], "x86_64-unknown-linux-gnu": [ "webpki-roots", "webpki-tokio" @@ -29146,19 +30897,19 @@ "target": "webpki_roots" } ], - "aarch64-fuchsia": [ + "aarch64-linux-android": [ { "id": "webpki-roots 0.26.1", "target": "webpki_roots" } ], - "aarch64-linux-android": [ + "aarch64-pc-windows-msvc": [ { "id": "webpki-roots 0.26.1", "target": "webpki_roots" } ], - "aarch64-pc-windows-msvc": [ + "aarch64-unknown-fuchsia": [ { "id": "webpki-roots 0.26.1", "target": "webpki_roots" @@ -29278,25 +31029,25 @@ "target": "webpki_roots" } ], - "x86_64-fuchsia": [ + "x86_64-linux-android": [ { "id": "webpki-roots 0.26.1", "target": "webpki_roots" } ], - "x86_64-linux-android": [ + "x86_64-pc-windows-msvc": [ { "id": "webpki-roots 0.26.1", "target": "webpki_roots" } ], - "x86_64-pc-windows-msvc": [ + "x86_64-unknown-freebsd": [ { "id": "webpki-roots 0.26.1", "target": "webpki_roots" } ], - "x86_64-unknown-freebsd": [ + "x86_64-unknown-fuchsia": [ { "id": "webpki-roots 0.26.1", "target": "webpki_roots" @@ -29719,7 +31470,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" } ], @@ -29733,14 +31484,14 @@ ], "license_file": "LICENSE-APACHE" }, - "ic-agent 0.37.1": { + "ic-agent 0.39.2": { "name": "ic-agent", - "version": "0.37.1", + "version": "0.39.2", "package_url": "https://github.com/dfinity/agent-rs", "repository": { "Http": { - "url": "https://static.crates.io/crates/ic-agent/0.37.1/download", - "sha256": "3fd3fdf5e5c4f4a9fe5ca612f0febd22dcb161d2f2b75b0142326732be5e4978" + "url": "https://static.crates.io/crates/ic-agent/0.39.2/download", + "sha256": "1ba408987ca48fc3eee6a613e760d076a9046cccbbb5ba29efbada339ab28ed9" } }, "targets": [ @@ -29765,19 +31516,29 @@ "crate_features": { "common": [ "default", - "experimental_sync_call", - "hyper", "pem", - "reqwest" + "ring" ], "selects": {} }, "deps": { "common": [ + { + "id": "arc-swap 1.7.1", + "target": "arc_swap" + }, + { + "id": "async-channel 1.9.0", + "target": "async_channel" + }, { "id": "async-lock 3.3.0", "target": "async_lock" }, + { + "id": "async-watch 0.3.1", + "target": "async_watch" + }, { "id": "backoff 0.4.0", "target": "backoff" @@ -29787,13 +31548,25 @@ "target": "cached" }, { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, + { + "id": "der 0.7.7", + "target": "der" + }, + { + "id": "ecdsa 0.16.9", + "target": "ecdsa" + }, { "id": "ed25519-consensus 2.1.0", "target": "ed25519_consensus" }, + { + "id": "elliptic-curve 0.13.8", + "target": "elliptic_curve" + }, { "id": "futures-util 0.3.31", "target": "futures_util" @@ -29811,15 +31584,11 @@ "target": "http_body" }, { - "id": "hyper 1.5.1", - "target": "hyper" - }, - { - "id": "ic-certification 2.6.0", + "id": "ic-certification 3.0.2", "target": "ic_certification" }, { - "id": "ic-transport-types 0.37.1", + "id": "ic-transport-types 0.39.2", "target": "ic_transport_types" }, { @@ -29867,7 +31636,7 @@ "target": "sec1" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -29887,732 +31656,32 @@ "target": "simple_asn1" }, { - "id": "thiserror 1.0.68", + "id": "stop-token 0.7.0", + "target": "stop_token" + }, + { + "id": "thiserror 2.0.3", "target": "thiserror" }, { "id": "time 0.3.36", "target": "time" }, + { + "id": "tower-service 0.3.3", + "target": "tower_service" + }, { "id": "url 2.5.3", "target": "url" } ], "selects": { - "aarch64-apple-darwin": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "aarch64-apple-ios": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "aarch64-apple-ios-sim": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "aarch64-fuchsia": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "aarch64-linux-android": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "aarch64-pc-windows-msvc": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "aarch64-unknown-linux-gnu": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "aarch64-unknown-nixos-gnu": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "aarch64-unknown-nto-qnx710": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "arm-unknown-linux-gnueabi": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "armv7-linux-androideabi": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "armv7-unknown-linux-gnueabi": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], "cfg(not(target_family = \"wasm\"))": [ - { - "id": "rustls-webpki 0.102.8", - "target": "webpki" - }, { "id": "tokio 1.42.0", "target": "tokio" } - ], - "i686-apple-darwin": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "i686-linux-android": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "i686-pc-windows-msvc": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "i686-unknown-freebsd": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "i686-unknown-linux-gnu": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "powerpc-unknown-linux-gnu": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "riscv32imc-unknown-none-elf": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "riscv64gc-unknown-none-elf": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "s390x-unknown-linux-gnu": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "thumbv7em-none-eabi": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "thumbv8m.main-none-eabi": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "x86_64-apple-darwin": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "x86_64-apple-ios": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "x86_64-fuchsia": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "x86_64-linux-android": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "x86_64-pc-windows-msvc": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "x86_64-unknown-freebsd": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "x86_64-unknown-linux-gnu": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "x86_64-unknown-nixos-gnu": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } - ], - "x86_64-unknown-none": [ - { - "id": "http-body-to-bytes 0.2.0", - "target": "http_body_to_bytes" - }, - { - "id": "http-body-util 0.1.2", - "target": "http_body_util" - }, - { - "id": "hyper-rustls 0.27.3", - "target": "hyper_rustls" - }, - { - "id": "hyper-util 0.1.10", - "target": "hyper_util" - }, - { - "id": "tower 0.4.13", - "target": "tower" - } ] } }, @@ -30620,13 +31689,17 @@ "proc_macro_deps": { "common": [ { - "id": "serde_repr 0.1.14", + "id": "async-trait 0.1.83", + "target": "async_trait" + }, + { + "id": "serde_repr 0.1.19", "target": "serde_repr" } ], "selects": {} }, - "version": "0.37.1" + "version": "0.39.2" }, "license": "Apache-2.0", "license_ids": [ @@ -30808,7 +31881,7 @@ "target": "scopeguard" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -30946,11 +32019,11 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -31001,7 +32074,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -31016,14 +32089,16 @@ ], "license_file": "LICENSE" }, - "ic-canister-sig-creation 1.0.1": { + "ic-canister-sig-creation 1.1.0": { "name": "ic-canister-sig-creation", - "version": "1.0.1", + "version": "1.1.0", "package_url": "https://github.com/dfinity/ic-canister-sig-creation", "repository": { - "Http": { - "url": "https://static.crates.io/crates/ic-canister-sig-creation/1.0.1/download", - "sha256": "5d1fc58d747480967a25810d8a90d460e7e9ea4c669ab0286541a148736513f9" + "Git": { + "remote": "https://github.com/dfinity/ic-canister-sig-creation", + "commitish": { + "Rev": "7f9e931954637526295269155881207f6c832d6d" + } } }, "targets": [ @@ -31048,7 +32123,7 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -31056,15 +32131,15 @@ "target": "hex" }, { - "id": "ic-cdk 0.14.1", + "id": "ic-cdk 0.17.0", "target": "ic_cdk" }, { - "id": "ic-certification 2.6.0", + "id": "ic-certification 3.0.2", "target": "ic_certification" }, { - "id": "ic-representation-independent-hash 2.6.0", + "id": "ic-representation-independent-hash 3.0.2", "target": "ic_representation_independent_hash" }, { @@ -31072,7 +32147,7 @@ "target": "lazy_static" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -31088,14 +32163,14 @@ "target": "sha2" }, { - "id": "thiserror 1.0.68", + "id": "thiserror 2.0.3", "target": "thiserror" } ], "selects": {} }, "edition": "2021", - "version": "1.0.1" + "version": "1.1.0" }, "license": "Apache-2.0", "license_ids": [ @@ -31103,14 +32178,14 @@ ], "license_file": "LICENSE" }, - "ic-cbor 2.6.0": { + "ic-cbor 3.0.2": { "name": "ic-cbor", - "version": "2.6.0", + "version": "3.0.2", "package_url": "https://github.com/dfinity/response-verification", "repository": { "Http": { - "url": "https://static.crates.io/crates/ic-cbor/2.6.0/download", - "sha256": "02b0e48b4166c891e79d624f3a184b4a7c145d307576872d9a46dedb8c73ea8f" + "url": "https://static.crates.io/crates/ic-cbor/3.0.2/download", + "sha256": "5500d6e85bc2ca8ea8aaed16cb84811882589244831a2fd8eefe02e90b3006c6" } }, "targets": [ @@ -31135,11 +32210,11 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { - "id": "ic-certification 2.6.0", + "id": "ic-certification 3.0.2", "target": "ic_certification" }, { @@ -31158,7 +32233,7 @@ "selects": {} }, "edition": "2021", - "version": "2.6.0" + "version": "3.0.2" }, "license": "Apache-2.0", "license_ids": [ @@ -31198,7 +32273,7 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -31206,7 +32281,7 @@ "target": "ic0" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -31234,14 +32309,14 @@ ], "license_file": "LICENSE" }, - "ic-cdk 0.14.1": { + "ic-cdk 0.16.0": { "name": "ic-cdk", - "version": "0.14.1", + "version": "0.16.0", "package_url": "https://github.com/dfinity/cdk-rs", "repository": { "Http": { - "url": "https://static.crates.io/crates/ic-cdk/0.14.1/download", - "sha256": "9cff1a3c3db565e3384c9c9d6d676b0a3f89a0886f4f787294d9c946d844369f" + "url": "https://static.crates.io/crates/ic-cdk/0.16.0/download", + "sha256": "dd8ecacd682fa05a985253592963306cb9799622d7b1cce4b1edb89c6ec85be1" } }, "targets": [ @@ -31266,7 +32341,7 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -31274,7 +32349,7 @@ "target": "ic0" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -31288,13 +32363,13 @@ "proc_macro_deps": { "common": [ { - "id": "ic-cdk-macros 0.14.0", + "id": "ic-cdk-macros 0.16.0", "target": "ic_cdk_macros" } ], "selects": {} }, - "version": "0.14.1" + "version": "0.16.0" }, "license": "Apache-2.0", "license_ids": [ @@ -31302,14 +32377,14 @@ ], "license_file": "LICENSE" }, - "ic-cdk 0.16.0": { + "ic-cdk 0.17.0": { "name": "ic-cdk", - "version": "0.16.0", + "version": "0.17.0", "package_url": "https://github.com/dfinity/cdk-rs", "repository": { "Http": { - "url": "https://static.crates.io/crates/ic-cdk/0.16.0/download", - "sha256": "dd8ecacd682fa05a985253592963306cb9799622d7b1cce4b1edb89c6ec85be1" + "url": "https://static.crates.io/crates/ic-cdk/0.17.0/download", + "sha256": "b2abdf9341da9f9f6b451a40609cb69645a05a8e9eb7784c16209f16f2c0f76f" } }, "targets": [ @@ -31334,7 +32409,7 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -31342,7 +32417,7 @@ "target": "ic0" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -31356,13 +32431,13 @@ "proc_macro_deps": { "common": [ { - "id": "ic-cdk-macros 0.16.0", + "id": "ic-cdk-macros 0.17.0", "target": "ic_cdk_macros" } ], "selects": {} }, - "version": "0.16.0" + "version": "0.17.0" }, "license": "Apache-2.0", "license_ids": [ @@ -31370,14 +32445,17 @@ ], "license_file": "LICENSE" }, - "ic-cdk 0.17.0": { + "ic-cdk 0.18.0-alpha.1": { "name": "ic-cdk", - "version": "0.17.0", + "version": "0.18.0-alpha.1", "package_url": "https://github.com/dfinity/cdk-rs", "repository": { - "Http": { - "url": "https://static.crates.io/crates/ic-cdk/0.17.0/download", - "sha256": "b2abdf9341da9f9f6b451a40609cb69645a05a8e9eb7784c16209f16f2c0f76f" + "Git": { + "remote": "https://github.com/dfinity/cdk-rs.git", + "commitish": { + "Rev": "4e287ce51636b0e70768c193da38d2fc5324ea15" + }, + "strip_prefix": "ic-cdk" } }, "targets": [ @@ -31402,20 +32480,24 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { - "id": "ic0 0.23.0", + "id": "ic0 0.24.0-alpha.1", "target": "ic0" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { "id": "serde_bytes 0.11.15", "target": "serde_bytes" + }, + { + "id": "thiserror 2.0.3", + "target": "thiserror" } ], "selects": {} @@ -31424,13 +32506,13 @@ "proc_macro_deps": { "common": [ { - "id": "ic-cdk-macros 0.17.0", + "id": "ic-cdk-macros 0.18.0-alpha.1", "target": "ic_cdk_macros" } ], "selects": {} }, - "version": "0.17.0" + "version": "0.18.0-alpha.1" }, "license": "Apache-2.0", "license_ids": [ @@ -31470,7 +32552,7 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -31482,7 +32564,7 @@ "target": "quote" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -31537,7 +32619,7 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -31549,7 +32631,7 @@ "target": "quote" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -31572,14 +32654,14 @@ ], "license_file": "LICENSE" }, - "ic-cdk-macros 0.14.0": { + "ic-cdk-macros 0.16.0": { "name": "ic-cdk-macros", - "version": "0.14.0", + "version": "0.16.0", "package_url": "https://github.com/dfinity/cdk-rs", "repository": { "Http": { - "url": "https://static.crates.io/crates/ic-cdk-macros/0.14.0/download", - "sha256": "01dc6bc425ec048d6ac4137c7c0f2cfbd6f8b0be8efc568feae2b265f566117c" + "url": "https://static.crates.io/crates/ic-cdk-macros/0.16.0/download", + "sha256": "0d4d857135deef20cc7ea8f3869a30cd9cfeb1392b3a81043790b2cd82adc3e0" } }, "targets": [ @@ -31604,7 +32686,7 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -31616,7 +32698,7 @@ "target": "quote" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -31631,7 +32713,7 @@ "selects": {} }, "edition": "2021", - "version": "0.14.0" + "version": "0.16.0" }, "license": "Apache-2.0", "license_ids": [ @@ -31639,14 +32721,14 @@ ], "license_file": "LICENSE" }, - "ic-cdk-macros 0.16.0": { + "ic-cdk-macros 0.17.0": { "name": "ic-cdk-macros", - "version": "0.16.0", + "version": "0.17.0", "package_url": "https://github.com/dfinity/cdk-rs", "repository": { "Http": { - "url": "https://static.crates.io/crates/ic-cdk-macros/0.16.0/download", - "sha256": "0d4d857135deef20cc7ea8f3869a30cd9cfeb1392b3a81043790b2cd82adc3e0" + "url": "https://static.crates.io/crates/ic-cdk-macros/0.17.0/download", + "sha256": "b8df41980e95dead28735ab0f748c75477b0c5eab37a09a5641c78ec406a1db0" } }, "targets": [ @@ -31671,7 +32753,7 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -31683,7 +32765,7 @@ "target": "quote" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -31698,7 +32780,7 @@ "selects": {} }, "edition": "2021", - "version": "0.16.0" + "version": "0.17.0" }, "license": "Apache-2.0", "license_ids": [ @@ -31706,14 +32788,17 @@ ], "license_file": "LICENSE" }, - "ic-cdk-macros 0.17.0": { + "ic-cdk-macros 0.18.0-alpha.1": { "name": "ic-cdk-macros", - "version": "0.17.0", + "version": "0.18.0-alpha.1", "package_url": "https://github.com/dfinity/cdk-rs", "repository": { - "Http": { - "url": "https://static.crates.io/crates/ic-cdk-macros/0.17.0/download", - "sha256": "b8df41980e95dead28735ab0f748c75477b0c5eab37a09a5641c78ec406a1db0" + "Git": { + "remote": "https://github.com/dfinity/cdk-rs.git", + "commitish": { + "Rev": "4e287ce51636b0e70768c193da38d2fc5324ea15" + }, + "strip_prefix": "ic-cdk-macros" } }, "targets": [ @@ -31738,7 +32823,7 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -31750,7 +32835,7 @@ "target": "quote" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -31765,7 +32850,7 @@ "selects": {} }, "edition": "2021", - "version": "0.17.0" + "version": "0.18.0-alpha.1" }, "license": "Apache-2.0", "license_ids": [ @@ -31817,7 +32902,7 @@ "target": "ic0" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -31840,14 +32925,14 @@ ], "license_file": "LICENSE" }, - "ic-certificate-verification 2.6.0": { + "ic-certificate-verification 3.0.2": { "name": "ic-certificate-verification", - "version": "2.6.0", + "version": "3.0.2", "package_url": "https://github.com/dfinity/response-verification", "repository": { "Http": { - "url": "https://static.crates.io/crates/ic-certificate-verification/2.6.0/download", - "sha256": "586e09b06a93d930f6a33f5f909bb11d2e4a06be3635dd5da1eb0e6554b7dae4" + "url": "https://static.crates.io/crates/ic-certificate-verification/3.0.2/download", + "sha256": "2daec653eb7895b5549cdf58d871985711c03cf5e389f7800a970f4f42dc0897" } }, "targets": [ @@ -31872,19 +32957,19 @@ "deps": { "common": [ { - "id": "cached 0.47.0", + "id": "cached 0.54.0", "target": "cached" }, { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { - "id": "ic-cbor 2.6.0", + "id": "ic-cbor 3.0.2", "target": "ic_cbor" }, { - "id": "ic-certification 2.6.0", + "id": "ic-certification 3.0.2", "target": "ic_certification" }, { @@ -31919,7 +33004,7 @@ "selects": {} }, "edition": "2021", - "version": "2.6.0" + "version": "3.0.2" }, "license": "Apache-2.0", "license_ids": [ @@ -31971,7 +33056,7 @@ "target": "hex" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -31994,6 +33079,72 @@ ], "license_file": "LICENSE" }, + "ic-certification 3.0.2": { + "name": "ic-certification", + "version": "3.0.2", + "package_url": "https://github.com/dfinity/response-verification", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/ic-certification/3.0.2/download", + "sha256": "9eae40f26fcac9c141cad54d9aa5f423efffde78ac371057c53d275ebbcad443" + } + }, + "targets": [ + { + "Library": { + "crate_name": "ic_certification", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "ic_certification", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "default", + "serde" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "hex 0.4.3", + "target": "hex" + }, + { + "id": "serde 1.0.217", + "target": "serde" + }, + { + "id": "serde_bytes 0.11.15", + "target": "serde_bytes" + }, + { + "id": "sha2 0.10.8", + "target": "sha2" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "3.0.2" + }, + "license": "Apache-2.0", + "license_ids": [ + "Apache-2.0" + ], + "license_file": "LICENSE" + }, "ic-certified-map 0.3.4": { "name": "ic-certified-map", "version": "0.3.4", @@ -32026,7 +33177,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -32049,14 +33200,14 @@ ], "license_file": "LICENSE" }, - "ic-http-certification 2.6.0": { + "ic-http-certification 3.0.2": { "name": "ic-http-certification", - "version": "2.6.0", + "version": "3.0.2", "package_url": "https://github.com/dfinity/response-verification", "repository": { "Http": { - "url": "https://static.crates.io/crates/ic-http-certification/2.6.0/download", - "sha256": "ff0b97e949845039149dc5e7ea6a7c12ee4333bb402e37bc507904643c7b3e41" + "url": "https://static.crates.io/crates/ic-http-certification/3.0.2/download", + "sha256": "479941fca8e68c2267cddf686d34ed6fb491168667ff259c08a3d65d28bd26d2" } }, "targets": [ @@ -32081,25 +33232,33 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "base64 0.22.1", + "target": "base64" + }, + { + "id": "candid 0.10.13", "target": "candid" }, { - "id": "http 0.2.12", + "id": "http 1.2.0", "target": "http" }, { - "id": "ic-certification 2.6.0", + "id": "ic-certification 3.0.2", "target": "ic_certification" }, { - "id": "ic-representation-independent-hash 2.6.0", + "id": "ic-representation-independent-hash 3.0.2", "target": "ic_representation_independent_hash" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, + { + "id": "serde_cbor 0.11.2", + "target": "serde_cbor" + }, { "id": "thiserror 1.0.68", "target": "thiserror" @@ -32112,7 +33271,7 @@ "selects": {} }, "edition": "2021", - "version": "2.6.0" + "version": "3.0.2" }, "license": "Apache-2.0", "license_ids": [ @@ -32120,17 +33279,14 @@ ], "license_file": "LICENSE" }, - "ic-http-gateway 0.0.0": { + "ic-http-gateway 0.1.0": { "name": "ic-http-gateway", - "version": "0.0.0", + "version": "0.1.0", "package_url": "https://github.com/dfinity/http-gateway", "repository": { - "Git": { - "remote": "https://github.com/dfinity/http-gateway", - "commitish": { - "Rev": "3be26b5a2c71bf56e05b910951c1935a1ac550c4" - }, - "strip_prefix": "packages/ic-http-gateway" + "Http": { + "url": "https://static.crates.io/crates/ic-http-gateway/0.1.0/download", + "sha256": "8e8b30a8ff19af1a7dc64b1dbe1a38f1b60c7eea566e2049f755ce3bace0e630" } }, "targets": [ @@ -32159,7 +33315,7 @@ "target": "bytes" }, { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -32179,19 +33335,19 @@ "target": "http_body_util" }, { - "id": "ic-agent 0.37.1", + "id": "ic-agent 0.39.2", "target": "ic_agent" }, { - "id": "ic-http-certification 2.6.0", + "id": "ic-http-certification 3.0.2", "target": "ic_http_certification" }, { - "id": "ic-response-verification 2.6.0", + "id": "ic-response-verification 3.0.2", "target": "ic_response_verification" }, { - "id": "ic-utils 0.37.0", + "id": "ic-utils 0.39.0", "target": "ic_utils" }, { @@ -32202,7 +33358,7 @@ "selects": {} }, "edition": "2021", - "version": "0.0.0" + "version": "0.1.0" }, "license": "Apache-2.0", "license_ids": [ @@ -32210,6 +33366,73 @@ ], "license_file": "LICENSE" }, + "ic-identity-hsm 0.39.2": { + "name": "ic-identity-hsm", + "version": "0.39.2", + "package_url": "https://github.com/dfinity/agent-rs", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/ic-identity-hsm/0.39.2/download", + "sha256": "0ebb94d7cb5be09bed47655008f0c2968a3a3253dcf680297f3e8475e4b317c4" + } + }, + "targets": [ + { + "Library": { + "crate_name": "ic_identity_hsm", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "ic_identity_hsm", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "hex 0.4.3", + "target": "hex" + }, + { + "id": "ic-agent 0.39.2", + "target": "ic_agent" + }, + { + "id": "pkcs11 0.5.0", + "target": "pkcs11" + }, + { + "id": "sha2 0.10.8", + "target": "sha2" + }, + { + "id": "simple_asn1 0.6.2", + "target": "simple_asn1" + }, + { + "id": "thiserror 2.0.3", + "target": "thiserror" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.39.2" + }, + "license": "Apache-2.0", + "license_ids": [ + "Apache-2.0" + ], + "license_file": null + }, "ic-metrics-encoder 1.1.1": { "name": "ic-metrics-encoder", "version": "1.1.1", @@ -32248,14 +33471,14 @@ ], "license_file": "LICENSE" }, - "ic-representation-independent-hash 2.6.0": { + "ic-representation-independent-hash 3.0.2": { "name": "ic-representation-independent-hash", - "version": "2.6.0", + "version": "3.0.2", "package_url": "https://github.com/dfinity/response-verification", "repository": { "Http": { - "url": "https://static.crates.io/crates/ic-representation-independent-hash/2.6.0/download", - "sha256": "08ae59483e377cd9aad94ec339ed1d2583b0d5929cab989328dac2d853b2f570" + "url": "https://static.crates.io/crates/ic-representation-independent-hash/3.0.2/download", + "sha256": "3643f12824280580d31e47d380f1be23abee29944a1430c3ed22b164ac8e68db" } }, "targets": [ @@ -32291,7 +33514,7 @@ "selects": {} }, "edition": "2021", - "version": "2.6.0" + "version": "3.0.2" }, "license": "Apache-2.0", "license_ids": [ @@ -32299,14 +33522,14 @@ ], "license_file": null }, - "ic-response-verification 2.6.0": { + "ic-response-verification 3.0.2": { "name": "ic-response-verification", - "version": "2.6.0", + "version": "3.0.2", "package_url": "https://github.com/dfinity/response-verification", "repository": { "Http": { - "url": "https://static.crates.io/crates/ic-response-verification/2.6.0/download", - "sha256": "2bef02ef84189d61a7d39889b7e9a3ae212d45c3df293513f7b2568027fd08a8" + "url": "https://static.crates.io/crates/ic-response-verification/3.0.2/download", + "sha256": "2b97514fada84797baf61a6a29f1c71695798c2628cb6013d97a5dd6ecc26df7" } }, "targets": [ @@ -32331,11 +33554,11 @@ "deps": { "common": [ { - "id": "base64 0.21.6", + "id": "base64 0.22.1", "target": "base64" }, { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -32347,27 +33570,27 @@ "target": "hex" }, { - "id": "http 0.2.12", + "id": "http 1.2.0", "target": "http" }, { - "id": "ic-cbor 2.6.0", + "id": "ic-cbor 3.0.2", "target": "ic_cbor" }, { - "id": "ic-certificate-verification 2.6.0", + "id": "ic-certificate-verification 3.0.2", "target": "ic_certificate_verification" }, { - "id": "ic-certification 2.6.0", + "id": "ic-certification 3.0.2", "target": "ic_certification" }, { - "id": "ic-http-certification 2.6.0", + "id": "ic-http-certification 3.0.2", "target": "ic_http_certification" }, { - "id": "ic-representation-independent-hash 2.6.0", + "id": "ic-representation-independent-hash 3.0.2", "target": "ic_representation_independent_hash" }, { @@ -32398,7 +33621,7 @@ "selects": {} }, "edition": "2021", - "version": "2.6.0" + "version": "3.0.2" }, "license": "Apache-2.0", "license_ids": [ @@ -32532,7 +33755,7 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -32540,7 +33763,7 @@ "target": "ciborium" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -32591,7 +33814,7 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -32607,7 +33830,7 @@ "target": "leb128" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -32629,7 +33852,7 @@ "proc_macro_deps": { "common": [ { - "id": "serde_repr 0.1.14", + "id": "serde_repr 0.1.19", "target": "serde_repr" } ], @@ -32643,14 +33866,102 @@ ], "license_file": null }, - "ic-utils 0.37.0": { + "ic-transport-types 0.39.2": { + "name": "ic-transport-types", + "version": "0.39.2", + "package_url": "https://github.com/dfinity/agent-rs", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/ic-transport-types/0.39.2/download", + "sha256": "21e2418868dd5857d2a5bac3f1cb6de1aecf2316d380997ef842aec3d8a79d4e" + } + }, + "targets": [ + { + "Library": { + "crate_name": "ic_transport_types", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "ic_transport_types", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "candid 0.10.13", + "target": "candid" + }, + { + "id": "hex 0.4.3", + "target": "hex" + }, + { + "id": "ic-certification 3.0.2", + "target": "ic_certification" + }, + { + "id": "leb128 0.2.5", + "target": "leb128" + }, + { + "id": "serde 1.0.217", + "target": "serde" + }, + { + "id": "serde_bytes 0.11.15", + "target": "serde_bytes" + }, + { + "id": "serde_cbor 0.11.2", + "target": "serde_cbor" + }, + { + "id": "sha2 0.10.8", + "target": "sha2" + }, + { + "id": "thiserror 2.0.3", + "target": "thiserror" + } + ], + "selects": {} + }, + "edition": "2021", + "proc_macro_deps": { + "common": [ + { + "id": "serde_repr 0.1.19", + "target": "serde_repr" + } + ], + "selects": {} + }, + "version": "0.39.2" + }, + "license": "Apache-2.0", + "license_ids": [ + "Apache-2.0" + ], + "license_file": null + }, + "ic-utils 0.39.0": { "name": "ic-utils", - "version": "0.37.0", + "version": "0.39.0", "package_url": "https://github.com/dfinity/agent-rs", "repository": { "Http": { - "url": "https://static.crates.io/crates/ic-utils/0.37.0/download", - "sha256": "2fa832296800758c9c921dd1704985ded6b3e6fbc3aee409727eb1f00d69a595" + "url": "https://static.crates.io/crates/ic-utils/0.39.0/download", + "sha256": "bb1da4a68c45146018b8496c157ad94126b9c202ab4400c6c0a9030c1ef0f0ba" } }, "targets": [ @@ -32681,7 +33992,7 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -32689,7 +34000,7 @@ "target": "futures_util" }, { - "id": "ic-agent 0.37.1", + "id": "ic-agent 0.39.2", "target": "ic_agent" }, { @@ -32701,7 +34012,7 @@ "target": "semver" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -32745,7 +34056,7 @@ ], "selects": {} }, - "version": "0.37.0" + "version": "0.39.0" }, "license": "Apache-2.0", "license_ids": [ @@ -32959,7 +34270,7 @@ "target": "anyhow" }, { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -32975,7 +34286,7 @@ "target": "rustc_demangle" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -33034,11 +34345,11 @@ "deps": { "common": [ { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -33167,6 +34478,47 @@ ], "license_file": "LICENSE" }, + "ic0 0.24.0-alpha.1": { + "name": "ic0", + "version": "0.24.0-alpha.1", + "package_url": "https://github.com/dfinity/cdk-rs", + "repository": { + "Git": { + "remote": "https://github.com/dfinity/cdk-rs.git", + "commitish": { + "Rev": "4e287ce51636b0e70768c193da38d2fc5324ea15" + }, + "strip_prefix": "ic0" + } + }, + "targets": [ + { + "Library": { + "crate_name": "ic0", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "ic0", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "edition": "2021", + "version": "0.24.0-alpha.1" + }, + "license": "Apache-2.0", + "license_ids": [ + "Apache-2.0" + ], + "license_file": "LICENSE" + }, "ic_bls12_381 0.10.0": { "name": "ic_bls12_381", "version": "0.10.0", @@ -33313,7 +34665,7 @@ "target": "data_encoding" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -33375,11 +34727,11 @@ "target": "anyhow" }, { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -33446,7 +34798,7 @@ "target": "anyhow" }, { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -33528,7 +34880,9 @@ "version": "1.5.0" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "icu_locid 1.5.0": { @@ -33600,7 +34954,9 @@ "version": "1.5.0" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "icu_locid_transform 1.5.0": { @@ -33676,7 +35032,9 @@ "version": "1.5.0" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "icu_locid_transform_data 1.5.0": { @@ -33712,7 +35070,9 @@ "version": "1.5.0" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "icu_normalizer 1.5.0": { @@ -33805,7 +35165,9 @@ "version": "1.5.0" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "icu_normalizer_data 1.5.0": { @@ -33841,7 +35203,9 @@ "version": "1.5.0" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "icu_properties 1.5.1": { @@ -33922,7 +35286,9 @@ "version": "1.5.1" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "icu_properties_data 1.5.0": { @@ -33958,7 +35324,9 @@ "version": "1.5.0" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "icu_provider 1.5.0": { @@ -34046,7 +35414,9 @@ "version": "1.5.0" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "icu_provider_macros 1.5.0": { @@ -34099,7 +35469,9 @@ "version": "1.5.0" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "id-arena 2.2.1": { @@ -34651,7 +36023,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -34903,7 +36275,7 @@ "target": "hashbrown" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -35481,7 +36853,7 @@ "target": "rustls_pki_types" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -35551,7 +36923,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -35661,14 +37033,14 @@ ], "license_file": "LICENSE-APACHE" }, - "ipnet 2.8.0": { + "ipnet 2.10.1": { "name": "ipnet", - "version": "2.8.0", + "version": "2.10.1", "package_url": "https://github.com/krisprice/ipnet", "repository": { "Http": { - "url": "https://static.crates.io/crates/ipnet/2.8.0/download", - "sha256": "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" + "url": "https://static.crates.io/crates/ipnet/2.10.1/download", + "sha256": "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" } }, "targets": [ @@ -35693,12 +37065,22 @@ "crate_features": { "common": [ "default", + "serde", "std" ], "selects": {} }, + "deps": { + "common": [ + { + "id": "serde 1.0.217", + "target": "serde" + } + ], + "selects": {} + }, "edition": "2018", - "version": "2.8.0" + "version": "2.10.1" }, "license": "MIT OR Apache-2.0", "license_ids": [ @@ -35746,7 +37128,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -35900,7 +37282,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -36262,14 +37644,14 @@ ], "license_file": "LICENSE-APACHE" }, - "jobserver 0.1.26": { + "jobserver 0.1.32": { "name": "jobserver", - "version": "0.1.26", - "package_url": "https://github.com/alexcrichton/jobserver-rs", + "version": "0.1.32", + "package_url": "https://github.com/rust-lang/jobserver-rs", "repository": { "Http": { - "url": "https://static.crates.io/crates/jobserver/0.1.26/download", - "sha256": "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" + "url": "https://static.crates.io/crates/jobserver/0.1.32/download", + "sha256": "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" } }, "targets": [ @@ -36302,10 +37684,10 @@ ] } }, - "edition": "2018", - "version": "0.1.26" + "edition": "2021", + "version": "0.1.32" }, - "license": "MIT/Apache-2.0", + "license": "MIT OR Apache-2.0", "license_ids": [ "Apache-2.0", "MIT" @@ -36400,7 +37782,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -36460,7 +37842,7 @@ "target": "pest" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -36558,16 +37940,14 @@ "license_ids": [], "license_file": "LICENSE" }, - "jsonrpc 0.12.1": { + "jsonrpc 0.13.0": { "name": "jsonrpc", - "version": "0.12.1", + "version": "0.13.0", "package_url": "https://github.com/apoelstra/rust-jsonrpc/", "repository": { - "Git": { - "remote": "https://github.com/apoelstra/rust-jsonrpc", - "commitish": { - "Rev": "e42044d8e0896317488dfbd65eb5563d76e937fe" - } + "Http": { + "url": "https://static.crates.io/crates/jsonrpc/0.13.0/download", + "sha256": "fd8d6b3f301ba426b30feca834a2a18d48d5b54e5065496b5c1b05537bee3639" } }, "targets": [ @@ -36605,7 +37985,7 @@ "target": "base64" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -36616,7 +37996,7 @@ "selects": {} }, "edition": "2018", - "version": "0.12.1" + "version": "0.13.0" }, "license": "CC0-1.0", "license_ids": [ @@ -36624,14 +38004,14 @@ ], "license_file": "LICENSE" }, - "jsonrpc 0.13.0": { + "jsonrpc 0.18.0": { "name": "jsonrpc", - "version": "0.13.0", + "version": "0.18.0", "package_url": "https://github.com/apoelstra/rust-jsonrpc/", "repository": { "Http": { - "url": "https://static.crates.io/crates/jsonrpc/0.13.0/download", - "sha256": "fd8d6b3f301ba426b30feca834a2a18d48d5b54e5065496b5c1b05537bee3639" + "url": "https://static.crates.io/crates/jsonrpc/0.18.0/download", + "sha256": "3662a38d341d77efecb73caf01420cfa5aa63c0253fd7bc05289ef9f6616e1bf" } }, "targets": [ @@ -36657,6 +38037,8 @@ "common": [ "base64", "default", + "minreq", + "minreq_http", "simple_http", "simple_tcp" ], @@ -36669,7 +38051,11 @@ "target": "base64" }, { - "id": "serde 1.0.214", + "id": "minreq 2.13.0", + "target": "minreq" + }, + { + "id": "serde 1.0.217", "target": "serde" }, { @@ -36680,7 +38066,7 @@ "selects": {} }, "edition": "2018", - "version": "0.13.0" + "version": "0.18.0" }, "license": "CC0-1.0", "license_ids": [ @@ -36688,6 +38074,96 @@ ], "license_file": "LICENSE" }, + "k256 0.11.6": { + "name": "k256", + "version": "0.11.6", + "package_url": "https://github.com/RustCrypto/elliptic-curves/tree/master/k256", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/k256/0.11.6/download", + "sha256": "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" + } + }, + "targets": [ + { + "Library": { + "crate_name": "k256", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "k256", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "arithmetic", + "default", + "digest", + "ecdsa", + "ecdsa-core", + "keccak256", + "pem", + "pkcs8", + "schnorr", + "sha2", + "sha256", + "sha3", + "std" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "cfg-if 1.0.0", + "target": "cfg_if" + }, + { + "id": "ecdsa 0.14.8", + "target": "ecdsa", + "alias": "ecdsa_core" + }, + { + "id": "elliptic-curve 0.12.3", + "target": "elliptic_curve" + }, + { + "id": "sha2 0.10.8", + "target": "sha2" + }, + { + "id": "sha3 0.10.8", + "target": "sha3" + } + ], + "selects": {} + }, + "edition": "2021", + "rustc_flags": { + "common": [ + "-C", + "opt-level=3" + ], + "selects": {} + }, + "version": "0.11.6" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "k256 0.13.4": { "name": "k256", "version": "0.13.4", @@ -36846,7 +38322,7 @@ "target": "build_script_build" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -36928,6 +38404,228 @@ ], "license_file": "LICENSE-APACHE" }, + "keyring 3.4.0": { + "name": "keyring", + "version": "3.4.0", + "package_url": "https://github.com/hwchen/keyring-rs.git", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/keyring/3.4.0/download", + "sha256": "bd3d701d3de5b9c4b0d9d077f8c2c66f0388d75e96932ebbb7cdff8713d7f7c6" + } + }, + "targets": [ + { + "Library": { + "crate_name": "keyring", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "keyring", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "apple-native", + "linux-native", + "sync-secret-service", + "vendored", + "windows-native" + ], + "selects": {} + }, + "deps": { + "common": [], + "selects": { + "aarch64-apple-darwin": [ + { + "id": "security-framework 3.0.1", + "target": "security_framework" + } + ], + "aarch64-apple-ios": [ + { + "id": "security-framework 3.0.1", + "target": "security_framework" + } + ], + "aarch64-apple-ios-sim": [ + { + "id": "security-framework 3.0.1", + "target": "security_framework" + } + ], + "aarch64-pc-windows-msvc": [ + { + "id": "byteorder 1.5.0", + "target": "byteorder" + }, + { + "id": "windows-sys 0.59.0", + "target": "windows_sys" + } + ], + "aarch64-unknown-linux-gnu": [ + { + "id": "dbus-secret-service 4.0.3", + "target": "dbus_secret_service" + }, + { + "id": "linux-keyutils 0.2.4", + "target": "linux_keyutils" + } + ], + "aarch64-unknown-nixos-gnu": [ + { + "id": "dbus-secret-service 4.0.3", + "target": "dbus_secret_service" + }, + { + "id": "linux-keyutils 0.2.4", + "target": "linux_keyutils" + } + ], + "arm-unknown-linux-gnueabi": [ + { + "id": "dbus-secret-service 4.0.3", + "target": "dbus_secret_service" + }, + { + "id": "linux-keyutils 0.2.4", + "target": "linux_keyutils" + } + ], + "armv7-unknown-linux-gnueabi": [ + { + "id": "dbus-secret-service 4.0.3", + "target": "dbus_secret_service" + }, + { + "id": "linux-keyutils 0.2.4", + "target": "linux_keyutils" + } + ], + "i686-apple-darwin": [ + { + "id": "security-framework 3.0.1", + "target": "security_framework" + } + ], + "i686-pc-windows-msvc": [ + { + "id": "byteorder 1.5.0", + "target": "byteorder" + }, + { + "id": "windows-sys 0.59.0", + "target": "windows_sys" + } + ], + "i686-unknown-freebsd": [ + { + "id": "dbus-secret-service 4.0.3", + "target": "dbus_secret_service" + } + ], + "i686-unknown-linux-gnu": [ + { + "id": "dbus-secret-service 4.0.3", + "target": "dbus_secret_service" + }, + { + "id": "linux-keyutils 0.2.4", + "target": "linux_keyutils" + } + ], + "powerpc-unknown-linux-gnu": [ + { + "id": "dbus-secret-service 4.0.3", + "target": "dbus_secret_service" + }, + { + "id": "linux-keyutils 0.2.4", + "target": "linux_keyutils" + } + ], + "s390x-unknown-linux-gnu": [ + { + "id": "dbus-secret-service 4.0.3", + "target": "dbus_secret_service" + }, + { + "id": "linux-keyutils 0.2.4", + "target": "linux_keyutils" + } + ], + "x86_64-apple-darwin": [ + { + "id": "security-framework 3.0.1", + "target": "security_framework" + } + ], + "x86_64-apple-ios": [ + { + "id": "security-framework 3.0.1", + "target": "security_framework" + } + ], + "x86_64-pc-windows-msvc": [ + { + "id": "byteorder 1.5.0", + "target": "byteorder" + }, + { + "id": "windows-sys 0.59.0", + "target": "windows_sys" + } + ], + "x86_64-unknown-freebsd": [ + { + "id": "dbus-secret-service 4.0.3", + "target": "dbus_secret_service" + } + ], + "x86_64-unknown-linux-gnu": [ + { + "id": "dbus-secret-service 4.0.3", + "target": "dbus_secret_service" + }, + { + "id": "linux-keyutils 0.2.4", + "target": "linux_keyutils" + } + ], + "x86_64-unknown-nixos-gnu": [ + { + "id": "dbus-secret-service 4.0.3", + "target": "dbus_secret_service" + }, + { + "id": "linux-keyutils 0.2.4", + "target": "linux_keyutils" + } + ] + } + }, + "edition": "2021", + "version": "3.4.0" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "kube 0.93.1": { "name": "kube", "version": "0.93.1", @@ -37136,7 +38834,7 @@ "target": "secrecy" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -37231,7 +38929,7 @@ "target": "k8s_openapi" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -38451,237 +40149,413 @@ } } ], - "library_target_name": "libc", + "library_target_name": "libc", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "default", + "extra_traits", + "std" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "libc 0.2.158", + "target": "build_script_build" + } + ], + "selects": {} + }, + "edition": "2015", + "version": "0.2.158" + }, + "build_script_attrs": { + "compile_data_glob": [ + "**" + ], + "data_glob": [ + "**" + ] + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, + "libdbus-sys 0.2.5": { + "name": "libdbus-sys", + "version": "0.2.5", + "package_url": "https://github.com/diwic/dbus-rs", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/libdbus-sys/0.2.5/download", + "sha256": "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72" + } + }, + "targets": [ + { + "Library": { + "crate_name": "libdbus_sys", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + }, + { + "BuildScript": { + "crate_name": "build_script_build", + "crate_root": "build.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "libdbus_sys", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "cc", + "default", + "pkg-config", + "vendored" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "libdbus-sys 0.2.5", + "target": "build_script_build" + } + ], + "selects": {} + }, + "edition": "2015", + "version": "0.2.5" + }, + "build_script_attrs": { + "compile_data_glob": [ + "**" + ], + "data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "cc 1.1.37", + "target": "cc" + }, + { + "id": "pkg-config 0.3.27", + "target": "pkg_config" + } + ], + "selects": {} + }, + "links": "dbus" + }, + "license": "Apache-2.0/MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, + "libflate 2.1.0": { + "name": "libflate", + "version": "2.1.0", + "package_url": "https://github.com/sile/libflate", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/libflate/2.1.0/download", + "sha256": "45d9dfdc14ea4ef0900c1cddbc8dcd553fbaacd8a4a282cf4018ae9dd04fb21e" + } + }, + "targets": [ + { + "Library": { + "crate_name": "libflate", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "libflate", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "default", + "std" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "adler32 1.2.0", + "target": "adler32" + }, + { + "id": "core2 0.4.0", + "target": "core2" + }, + { + "id": "crc32fast 1.3.2", + "target": "crc32fast" + }, + { + "id": "dary_heap 0.3.6", + "target": "dary_heap" + }, + { + "id": "libflate_lz77 2.1.0", + "target": "libflate_lz77" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "2.1.0" + }, + "license": "MIT", + "license_ids": [ + "MIT" + ], + "license_file": "LICENSE" + }, + "libflate_lz77 2.1.0": { + "name": "libflate_lz77", + "version": "2.1.0", + "package_url": "https://github.com/sile/libflate", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/libflate_lz77/2.1.0/download", + "sha256": "e6e0d73b369f386f1c44abd9c570d5318f55ccde816ff4b562fa452e5182863d" + } + }, + "targets": [ + { + "Library": { + "crate_name": "libflate_lz77", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "libflate_lz77", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "std" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "core2 0.4.0", + "target": "core2" + }, + { + "id": "hashbrown 0.14.5", + "target": "hashbrown" + }, + { + "id": "rle-decode-fast 1.0.3", + "target": "rle_decode_fast" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "2.1.0" + }, + "license": "MIT", + "license_ids": [ + "MIT" + ], + "license_file": "LICENSE" + }, + "libfuzzer-sys 0.4.7": { + "name": "libfuzzer-sys", + "version": "0.4.7", + "package_url": "https://github.com/rust-fuzz/libfuzzer", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/libfuzzer-sys/0.4.7/download", + "sha256": "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" + } + }, + "targets": [ + { + "Library": { + "crate_name": "libfuzzer_sys", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + }, + { + "BuildScript": { + "crate_name": "build_script_build", + "crate_root": "build.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "libfuzzer_sys", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "arbitrary 1.3.2", + "target": "arbitrary" + }, + { + "id": "libfuzzer-sys 0.4.7", + "target": "build_script_build" + }, + { + "id": "once_cell 1.19.0", + "target": "once_cell" + } + ], + "selects": {} + }, + "edition": "2018", + "version": "0.4.7" + }, + "build_script_attrs": { + "compile_data_glob": [ + "**" + ], + "data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "cc 1.1.37", + "target": "cc" + } + ], + "selects": {} + } + }, + "license": "MIT/Apache-2.0/NCSA", + "license_ids": [ + "Apache-2.0", + "MIT", + "NCSA" + ], + "license_file": "LICENSE-APACHE" + }, + "libloading 0.5.2": { + "name": "libloading", + "version": "0.5.2", + "package_url": "https://github.com/nagisa/rust_libloading/", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/libloading/0.5.2/download", + "sha256": "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" + } + }, + "targets": [ + { + "Library": { + "crate_name": "libloading", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + }, + { + "BuildScript": { + "crate_name": "build_script_build", + "crate_root": "build.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "libloading", "common_attrs": { "compile_data_glob": [ "**" ], - "crate_features": { - "common": [ - "default", - "extra_traits", - "std" - ], - "selects": {} - }, "deps": { "common": [ { - "id": "libc 0.2.158", + "id": "libloading 0.5.2", "target": "build_script_build" } ], - "selects": {} - }, - "edition": "2015", - "version": "0.2.158" - }, - "build_script_attrs": { - "compile_data_glob": [ - "**" - ], - "data_glob": [ - "**" - ] - }, - "license": "MIT OR Apache-2.0", - "license_ids": [ - "Apache-2.0", - "MIT" - ], - "license_file": "LICENSE-APACHE" - }, - "libflate 2.1.0": { - "name": "libflate", - "version": "2.1.0", - "package_url": "https://github.com/sile/libflate", - "repository": { - "Http": { - "url": "https://static.crates.io/crates/libflate/2.1.0/download", - "sha256": "45d9dfdc14ea4ef0900c1cddbc8dcd553fbaacd8a4a282cf4018ae9dd04fb21e" - } - }, - "targets": [ - { - "Library": { - "crate_name": "libflate", - "crate_root": "src/lib.rs", - "srcs": { - "allow_empty": true, - "include": [ - "**/*.rs" - ] - } - } - } - ], - "library_target_name": "libflate", - "common_attrs": { - "compile_data_glob": [ - "**" - ], - "crate_features": { - "common": [ - "default", - "std" - ], - "selects": {} - }, - "deps": { - "common": [ - { - "id": "adler32 1.2.0", - "target": "adler32" - }, - { - "id": "core2 0.4.0", - "target": "core2" - }, - { - "id": "crc32fast 1.3.2", - "target": "crc32fast" - }, - { - "id": "dary_heap 0.3.6", - "target": "dary_heap" - }, - { - "id": "libflate_lz77 2.1.0", - "target": "libflate_lz77" - } - ], - "selects": {} - }, - "edition": "2021", - "version": "2.1.0" - }, - "license": "MIT", - "license_ids": [ - "MIT" - ], - "license_file": "LICENSE" - }, - "libflate_lz77 2.1.0": { - "name": "libflate_lz77", - "version": "2.1.0", - "package_url": "https://github.com/sile/libflate", - "repository": { - "Http": { - "url": "https://static.crates.io/crates/libflate_lz77/2.1.0/download", - "sha256": "e6e0d73b369f386f1c44abd9c570d5318f55ccde816ff4b562fa452e5182863d" - } - }, - "targets": [ - { - "Library": { - "crate_name": "libflate_lz77", - "crate_root": "src/lib.rs", - "srcs": { - "allow_empty": true, - "include": [ - "**/*.rs" - ] - } - } - } - ], - "library_target_name": "libflate_lz77", - "common_attrs": { - "compile_data_glob": [ - "**" - ], - "crate_features": { - "common": [ - "std" - ], - "selects": {} - }, - "deps": { - "common": [ - { - "id": "core2 0.4.0", - "target": "core2" - }, - { - "id": "hashbrown 0.14.5", - "target": "hashbrown" - }, - { - "id": "rle-decode-fast 1.0.3", - "target": "rle_decode_fast" - } - ], - "selects": {} - }, - "edition": "2021", - "version": "2.1.0" - }, - "license": "MIT", - "license_ids": [ - "MIT" - ], - "license_file": "LICENSE" - }, - "libfuzzer-sys 0.4.7": { - "name": "libfuzzer-sys", - "version": "0.4.7", - "package_url": "https://github.com/rust-fuzz/libfuzzer", - "repository": { - "Http": { - "url": "https://static.crates.io/crates/libfuzzer-sys/0.4.7/download", - "sha256": "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" - } - }, - "targets": [ - { - "Library": { - "crate_name": "libfuzzer_sys", - "crate_root": "src/lib.rs", - "srcs": { - "allow_empty": true, - "include": [ - "**/*.rs" - ] - } - } - }, - { - "BuildScript": { - "crate_name": "build_script_build", - "crate_root": "build.rs", - "srcs": { - "allow_empty": true, - "include": [ - "**/*.rs" - ] - } + "selects": { + "cfg(windows)": [ + { + "id": "winapi 0.3.9", + "target": "winapi" + } + ] } - } - ], - "library_target_name": "libfuzzer_sys", - "common_attrs": { - "compile_data_glob": [ - "**" - ], - "deps": { - "common": [ - { - "id": "arbitrary 1.3.2", - "target": "arbitrary" - }, - { - "id": "libfuzzer-sys 0.4.7", - "target": "build_script_build" - }, - { - "id": "once_cell 1.19.0", - "target": "once_cell" - } - ], - "selects": {} }, - "edition": "2018", - "version": "0.4.7" + "edition": "2015", + "version": "0.5.2" }, "build_script_attrs": { "compile_data_glob": [ @@ -38693,20 +40567,18 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" } ], "selects": {} } }, - "license": "MIT/Apache-2.0/NCSA", + "license": "ISC", "license_ids": [ - "Apache-2.0", - "MIT", - "NCSA" + "ISC" ], - "license_file": "LICENSE-APACHE" + "license_file": "LICENSE" }, "libloading 0.7.4": { "name": "libloading", @@ -38975,7 +40847,7 @@ "target": "bindgen" }, { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" }, { @@ -39076,7 +40948,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" }, { @@ -39163,7 +41035,7 @@ "selects": { "cfg(unix)": [ { - "id": "openssl-sys 0.9.102", + "id": "openssl-sys 0.9.104", "target": "openssl_sys" } ] @@ -39182,7 +41054,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" }, { @@ -39209,7 +41081,7 @@ "selects": { "cfg(unix)": [ { - "id": "openssl-sys 0.9.102", + "id": "openssl-sys 0.9.104", "target": "openssl_sys" } ] @@ -39291,7 +41163,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" }, { @@ -39389,7 +41261,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" }, { @@ -39454,6 +41326,65 @@ ], "license_file": "LICENSE-APACHE" }, + "linux-keyutils 0.2.4": { + "name": "linux-keyutils", + "version": "0.2.4", + "package_url": "https://github.com/landhb/linux-keyutils", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/linux-keyutils/0.2.4/download", + "sha256": "761e49ec5fd8a5a463f9b84e877c373d888935b71c6be78f3767fe2ae6bed18e" + } + }, + "targets": [ + { + "Library": { + "crate_name": "linux_keyutils", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "linux_keyutils", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "default", + "std" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "bitflags 2.6.0", + "target": "bitflags" + }, + { + "id": "libc 0.2.158", + "target": "libc" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.2.4" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": null + }, "linux-raw-sys 0.4.13": { "name": "linux-raw-sys", "version": "0.4.13", @@ -39587,7 +41518,9 @@ "version": "0.7.3" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "little-loadshedder 0.2.0": { @@ -39798,7 +41731,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" }, { @@ -40107,8 +42040,6 @@ ], "crate_features": { "common": [ - "max_level_off", - "release_max_level_off", "std" ], "selects": {} @@ -40574,7 +42505,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" }, { @@ -41096,7 +43027,7 @@ "target": "memchr" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -41732,7 +43663,7 @@ "target": "rustls_pemfile" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -42148,6 +44079,93 @@ ], "license_file": "LICENSE" }, + "minreq 2.13.0": { + "name": "minreq", + "version": "2.13.0", + "package_url": "https://github.com/neonmoe/minreq", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/minreq/2.13.0/download", + "sha256": "36a8e50e917e18a37d500d27d40b7bc7d127e71c0c94fb2d83f43b4afd308390" + } + }, + "targets": [ + { + "Library": { + "crate_name": "minreq", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + }, + { + "BuildScript": { + "crate_name": "build_script_build", + "crate_root": "build.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "minreq", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "json-using-serde", + "serde", + "serde_json" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "log 0.4.20", + "target": "log" + }, + { + "id": "minreq 2.13.0", + "target": "build_script_build" + }, + { + "id": "serde 1.0.217", + "target": "serde" + }, + { + "id": "serde_json 1.0.132", + "target": "serde_json" + } + ], + "selects": {} + }, + "edition": "2018", + "version": "2.13.0" + }, + "build_script_attrs": { + "compile_data_glob": [ + "**" + ], + "data_glob": [ + "**" + ] + }, + "license": "ISC", + "license_ids": [ + "ISC" + ], + "license_file": null + }, "mio 0.8.10": { "name": "mio", "version": "0.8.10", @@ -42194,10 +44212,10 @@ "aarch64-apple-ios-sim": [ "os-ext" ], - "aarch64-fuchsia": [ + "aarch64-linux-android": [ "os-ext" ], - "aarch64-linux-android": [ + "aarch64-unknown-fuchsia": [ "os-ext" ], "aarch64-unknown-linux-gnu": [ @@ -42242,15 +44260,15 @@ "x86_64-apple-ios": [ "os-ext" ], - "x86_64-fuchsia": [ - "os-ext" - ], "x86_64-linux-android": [ "os-ext" ], "x86_64-unknown-freebsd": [ "os-ext" ], + "x86_64-unknown-fuchsia": [ + "os-ext" + ], "x86_64-unknown-linux-gnu": [ "os-ext" ], @@ -43261,7 +45279,7 @@ "target": "quote" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -43350,7 +45368,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -43994,6 +46012,82 @@ ], "license_file": null }, + "num 0.4.3": { + "name": "num", + "version": "0.4.3", + "package_url": "https://github.com/rust-num/num", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/num/0.4.3/download", + "sha256": "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" + } + }, + "targets": [ + { + "Library": { + "crate_name": "num", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "num", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "default", + "num-bigint", + "std" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "num-bigint 0.4.6", + "target": "num_bigint" + }, + { + "id": "num-complex 0.4.6", + "target": "num_complex" + }, + { + "id": "num-integer 0.1.46", + "target": "num_integer" + }, + { + "id": "num-iter 0.1.45", + "target": "num_iter" + }, + { + "id": "num-rational 0.4.2", + "target": "num_rational" + }, + { + "id": "num-traits 0.2.19", + "target": "num_traits" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.4.3" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "num-bigint 0.2.6": { "name": "num-bigint", "version": "0.2.6", @@ -44037,6 +46131,7 @@ ], "crate_features": { "common": [ + "default", "std" ], "selects": {} @@ -44133,7 +46228,7 @@ "target": "num_traits" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -44238,7 +46333,7 @@ "target": "rand" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -44270,6 +46365,60 @@ ], "license_file": "LICENSE-APACHE" }, + "num-complex 0.4.6": { + "name": "num-complex", + "version": "0.4.6", + "package_url": "https://github.com/rust-num/num-complex", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/num-complex/0.4.6/download", + "sha256": "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" + } + }, + "targets": [ + { + "Library": { + "crate_name": "num_complex", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "num_complex", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "std" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "num-traits 0.2.19", + "target": "num_traits" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.4.6" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "num-conv 0.1.0": { "name": "num-conv", "version": "0.1.0", @@ -44445,6 +46594,55 @@ "compile_data_glob": [ "**" ], + "crate_features": { + "common": [], + "selects": { + "aarch64-unknown-linux-gnu": [ + "i128", + "std" + ], + "aarch64-unknown-nixos-gnu": [ + "i128", + "std" + ], + "arm-unknown-linux-gnueabi": [ + "i128", + "std" + ], + "armv7-unknown-linux-gnueabi": [ + "i128", + "std" + ], + "i686-unknown-freebsd": [ + "i128", + "std" + ], + "i686-unknown-linux-gnu": [ + "i128", + "std" + ], + "powerpc-unknown-linux-gnu": [ + "i128", + "std" + ], + "s390x-unknown-linux-gnu": [ + "i128", + "std" + ], + "x86_64-unknown-freebsd": [ + "i128", + "std" + ], + "x86_64-unknown-linux-gnu": [ + "i128", + "std" + ], + "x86_64-unknown-nixos-gnu": [ + "i128", + "std" + ] + } + }, "deps": { "common": [ { @@ -44567,6 +46765,70 @@ ], "license_file": "LICENSE-APACHE" }, + "num-rational 0.4.2": { + "name": "num-rational", + "version": "0.4.2", + "package_url": "https://github.com/rust-num/num-rational", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/num-rational/0.4.2/download", + "sha256": "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" + } + }, + "targets": [ + { + "Library": { + "crate_name": "num_rational", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "num_rational", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "num-bigint", + "num-bigint-std", + "std" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "num-bigint 0.4.6", + "target": "num_bigint" + }, + { + "id": "num-integer 0.1.46", + "target": "num_integer" + }, + { + "id": "num-traits 0.2.19", + "target": "num_traits" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.4.2" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "num-traits 0.2.19": { "name": "num-traits", "version": "0.2.19", @@ -44992,14 +47254,14 @@ ], "license_file": "LICENSE-APACHE" }, - "object 0.36.1": { + "object 0.36.7": { "name": "object", - "version": "0.36.1", + "version": "0.36.7", "package_url": "https://github.com/gimli-rs/object", "repository": { "Http": { - "url": "https://static.crates.io/crates/object/0.36.1/download", - "sha256": "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" + "url": "https://static.crates.io/crates/object/0.36.7/download", + "sha256": "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" } }, "targets": [ @@ -45014,6 +47276,18 @@ ] } } + }, + { + "BuildScript": { + "crate_name": "build_script_build", + "crate_root": "build.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } } ], "library_target_name": "object", @@ -45043,7 +47317,7 @@ "target": "crc32fast" }, { - "id": "hashbrown 0.14.5", + "id": "hashbrown 0.15.2", "target": "hashbrown" }, { @@ -45053,12 +47327,24 @@ { "id": "memchr 2.7.4", "target": "memchr" + }, + { + "id": "object 0.36.7", + "target": "build_script_build" } ], "selects": {} }, "edition": "2018", - "version": "0.36.1" + "version": "0.36.7" + }, + "build_script_attrs": { + "compile_data_glob": [ + "**" + ], + "data_glob": [ + "**" + ] }, "license": "Apache-2.0 OR MIT", "license_ids": [ @@ -45482,6 +47768,173 @@ ], "license_file": "LICENSE-APACHE" }, + "openssl 0.10.69": { + "name": "openssl", + "version": "0.10.69", + "package_url": "https://github.com/sfackler/rust-openssl", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/openssl/0.10.69/download", + "sha256": "f5e534d133a060a3c19daec1eb3e98ec6f4685978834f2dbadfe2ec215bab64e" + } + }, + "targets": [ + { + "Library": { + "crate_name": "openssl", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + }, + { + "BuildScript": { + "crate_name": "build_script_build", + "crate_root": "build.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "openssl", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "bitflags 2.6.0", + "target": "bitflags" + }, + { + "id": "cfg-if 1.0.0", + "target": "cfg_if" + }, + { + "id": "foreign-types 0.3.2", + "target": "foreign_types" + }, + { + "id": "libc 0.2.158", + "target": "libc" + }, + { + "id": "once_cell 1.19.0", + "target": "once_cell" + }, + { + "id": "openssl 0.10.69", + "target": "build_script_build" + }, + { + "id": "openssl-sys 0.9.104", + "target": "openssl_sys", + "alias": "ffi" + } + ], + "selects": {} + }, + "edition": "2021", + "proc_macro_deps": { + "common": [ + { + "id": "openssl-macros 0.1.1", + "target": "openssl_macros" + } + ], + "selects": {} + }, + "version": "0.10.69" + }, + "build_script_attrs": { + "compile_data_glob": [ + "**" + ], + "data_glob": [ + "**" + ], + "link_deps": { + "common": [ + { + "id": "openssl-sys 0.9.104", + "target": "openssl_sys", + "alias": "ffi" + } + ], + "selects": {} + } + }, + "license": "Apache-2.0", + "license_ids": [ + "Apache-2.0" + ], + "license_file": "LICENSE" + }, + "openssl-macros 0.1.1": { + "name": "openssl-macros", + "version": "0.1.1", + "package_url": null, + "repository": { + "Http": { + "url": "https://static.crates.io/crates/openssl-macros/0.1.1/download", + "sha256": "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" + } + }, + "targets": [ + { + "ProcMacro": { + "crate_name": "openssl_macros", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "openssl_macros", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "proc-macro2 1.0.89", + "target": "proc_macro2" + }, + { + "id": "quote 1.0.37", + "target": "quote" + }, + { + "id": "syn 2.0.87", + "target": "syn" + } + ], + "selects": {} + }, + "edition": "2018", + "version": "0.1.1" + }, + "license": "MIT/Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "openssl-probe 0.1.5": { "name": "openssl-probe", "version": "0.1.5", @@ -45521,14 +47974,62 @@ ], "license_file": "LICENSE-APACHE" }, - "openssl-sys 0.9.102": { + "openssl-src 300.4.1+3.4.0": { + "name": "openssl-src", + "version": "300.4.1+3.4.0", + "package_url": "https://github.com/alexcrichton/openssl-src-rs", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/openssl-src/300.4.1+3.4.0/download", + "sha256": "faa4eac4138c62414b5622d1b31c5c304f34b406b013c079c2bbc652fdd6678c" + } + }, + "targets": [ + { + "Library": { + "crate_name": "openssl_src", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "openssl_src", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "cc 1.1.37", + "target": "cc" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "300.4.1+3.4.0" + }, + "license": "MIT/Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, + "openssl-sys 0.9.104": { "name": "openssl-sys", - "version": "0.9.102", + "version": "0.9.104", "package_url": "https://github.com/sfackler/rust-openssl", "repository": { "Http": { - "url": "https://static.crates.io/crates/openssl-sys/0.9.102/download", - "sha256": "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" + "url": "https://static.crates.io/crates/openssl-sys/0.9.104/download", + "sha256": "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" } }, "targets": [ @@ -45569,14 +48070,14 @@ "target": "libc" }, { - "id": "openssl-sys 0.9.102", + "id": "openssl-sys 0.9.104", "target": "build_script_main" } ], "selects": {} }, - "edition": "2018", - "version": "0.9.102" + "edition": "2021", + "version": "0.9.104" }, "build_script_attrs": { "compile_data_glob": [ @@ -45588,7 +48089,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" }, { @@ -45957,7 +48458,7 @@ "target": "opentelemetry_proto" }, { - "id": "opentelemetry_sdk 0.27.0", + "id": "opentelemetry_sdk 0.27.1", "target": "opentelemetry_sdk" }, { @@ -46181,7 +48682,7 @@ "target": "opentelemetry" }, { - "id": "opentelemetry_sdk 0.27.0", + "id": "opentelemetry_sdk 0.27.1", "target": "opentelemetry_sdk" }, { @@ -46724,7 +49225,7 @@ "target": "tokio" }, { - "id": "tokio-stream 0.1.16", + "id": "tokio-stream 0.1.17", "target": "tokio_stream" } ], @@ -46748,14 +49249,14 @@ ], "license_file": "LICENSE" }, - "opentelemetry_sdk 0.27.0": { + "opentelemetry_sdk 0.27.1": { "name": "opentelemetry_sdk", - "version": "0.27.0", + "version": "0.27.1", "package_url": "https://github.com/open-telemetry/opentelemetry-rust", "repository": { "Http": { - "url": "https://static.crates.io/crates/opentelemetry_sdk/0.27.0/download", - "sha256": "27b742c1cae4693792cc564e58d75a2a0ba29421a34a85b50da92efa89ecb2bc" + "url": "https://static.crates.io/crates/opentelemetry_sdk/0.27.1/download", + "sha256": "231e9d6ceef9b0b2546ddf52335785ce41252bc7474ee8ba05bfad277be13ab8" } }, "targets": [ @@ -46814,10 +49315,6 @@ "id": "glob 0.3.1", "target": "glob" }, - { - "id": "once_cell 1.19.0", - "target": "once_cell" - }, { "id": "opentelemetry 0.27.0", "target": "opentelemetry" @@ -46843,7 +49340,7 @@ "target": "tokio" }, { - "id": "tokio-stream 0.1.16", + "id": "tokio-stream 0.1.17", "target": "tokio_stream" }, { @@ -46863,7 +49360,7 @@ ], "selects": {} }, - "version": "0.27.0" + "version": "0.27.1" }, "license": "Apache-2.0", "license_ids": [ @@ -47299,7 +49796,7 @@ "target": "byte_slice_cast" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -47789,6 +50286,69 @@ ], "license_file": "LICENSE.txt" }, + "password-hash 0.4.2": { + "name": "password-hash", + "version": "0.4.2", + "package_url": "https://github.com/RustCrypto/traits/tree/master/password-hash", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/password-hash/0.4.2/download", + "sha256": "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" + } + }, + "targets": [ + { + "Library": { + "crate_name": "password_hash", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "password_hash", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "default", + "rand_core" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "base64ct 1.6.0", + "target": "base64ct" + }, + { + "id": "rand_core 0.6.4", + "target": "rand_core" + }, + { + "id": "subtle 2.6.1", + "target": "subtle" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.4.2" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "paste 1.0.15": { "name": "paste", "version": "1.0.15", @@ -47857,6 +50417,54 @@ ], "license_file": "LICENSE-APACHE" }, + "pbkdf2 0.11.0": { + "name": "pbkdf2", + "version": "0.11.0", + "package_url": "https://github.com/RustCrypto/password-hashes/tree/master/pbkdf2", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/pbkdf2/0.11.0/download", + "sha256": "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" + } + }, + "targets": [ + { + "Library": { + "crate_name": "pbkdf2", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "pbkdf2", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "digest 0.10.7", + "target": "digest" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.11.0" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "pbkdf2 0.12.2": { "name": "pbkdf2", "version": "0.12.2", @@ -48038,7 +50646,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" }, { @@ -48196,6 +50804,60 @@ ], "license_file": "LICENSE.md" }, + "pem-rfc7468 0.6.0": { + "name": "pem-rfc7468", + "version": "0.6.0", + "package_url": "https://github.com/RustCrypto/formats/tree/master/pem-rfc7468", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/pem-rfc7468/0.6.0/download", + "sha256": "24d159833a9105500e0398934e205e0773f0b27529557134ecfc51c27646adac" + } + }, + "targets": [ + { + "Library": { + "crate_name": "pem_rfc7468", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "pem_rfc7468", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "alloc" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "base64ct 1.6.0", + "target": "base64ct" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.6.0" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "pem-rfc7468 0.7.0": { "name": "pem-rfc7468", "version": "0.7.0", @@ -49494,6 +52156,116 @@ ], "license_file": "LICENSE-APACHE" }, + "pkcs11 0.5.0": { + "name": "pkcs11", + "version": "0.5.0", + "package_url": "https://github.com/mheese/rust-pkcs11", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/pkcs11/0.5.0/download", + "sha256": "3aca6d67e4c8613bfe455599d0233d00735f85df2001f6bfd9bb7ac0496b10af" + } + }, + "targets": [ + { + "Library": { + "crate_name": "pkcs11", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "pkcs11", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "libloading 0.5.2", + "target": "libloading" + }, + { + "id": "num-bigint 0.2.6", + "target": "num_bigint" + } + ], + "selects": {} + }, + "edition": "2015", + "version": "0.5.0" + }, + "license": "Apache-2.0", + "license_ids": [ + "Apache-2.0" + ], + "license_file": "LICENSE" + }, + "pkcs8 0.9.0": { + "name": "pkcs8", + "version": "0.9.0", + "package_url": "https://github.com/RustCrypto/formats/tree/master/pkcs8", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/pkcs8/0.9.0/download", + "sha256": "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" + } + }, + "targets": [ + { + "Library": { + "crate_name": "pkcs8", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "pkcs8", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "alloc", + "pem" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "der 0.6.1", + "target": "der" + }, + { + "id": "spki 0.6.0", + "target": "spki" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.9.0" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "pkcs8 0.10.2": { "name": "pkcs8", "version": "0.10.2", @@ -49789,7 +52561,7 @@ "target": "base64" }, { - "id": "candid 0.10.10", + "id": "candid 0.10.13", "target": "candid" }, { @@ -49813,7 +52585,7 @@ "target": "schemars" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -50026,6 +52798,69 @@ ], "license_file": "LICENSE-APACHE" }, + "polyval 0.6.2": { + "name": "polyval", + "version": "0.6.2", + "package_url": "https://github.com/RustCrypto/universal-hashes", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/polyval/0.6.2/download", + "sha256": "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" + } + }, + "targets": [ + { + "Library": { + "crate_name": "polyval", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "polyval", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "cfg-if 1.0.0", + "target": "cfg_if" + }, + { + "id": "opaque-debug 0.3.0", + "target": "opaque_debug" + }, + { + "id": "universal-hash 0.5.1", + "target": "universal_hash" + } + ], + "selects": { + "cfg(any(target_arch = \"aarch64\", target_arch = \"x86_64\", target_arch = \"x86\"))": [ + { + "id": "cpufeatures 0.2.9", + "target": "cpufeatures" + } + ] + } + }, + "edition": "2021", + "version": "0.6.2" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "portable-atomic 1.4.1": { "name": "portable-atomic", "version": "1.4.1", @@ -50149,7 +52984,7 @@ "target": "embedded_io" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -51129,7 +53964,7 @@ "target": "build_script_build" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -52928,7 +55763,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" } ], @@ -53104,14 +55939,14 @@ ], "license_file": "LICENSE" }, - "pulley-interpreter 27.0.0": { + "pulley-interpreter 28.0.0": { "name": "pulley-interpreter", - "version": "27.0.0", + "version": "28.0.0", "package_url": "https://github.com/bytecodealliance/wasmtime/tree/main/pulley", "repository": { "Http": { - "url": "https://static.crates.io/crates/pulley-interpreter/27.0.0/download", - "sha256": "a3b8d81cf799e20564931e9867ca32de545188c6ee4c2e0f6e41d32f0c7dc6fb" + "url": "https://static.crates.io/crates/pulley-interpreter/28.0.0/download", + "sha256": "403a1a95f4c18a45c86c7bff13df00347afd0abcbf2e54af273c837339ffcf77" } }, "targets": [ @@ -53136,7 +55971,7 @@ "deps": { "common": [ { - "id": "cranelift-bitset 0.114.0", + "id": "cranelift-bitset 0.115.0", "target": "cranelift_bitset" }, { @@ -53151,7 +55986,7 @@ "selects": {} }, "edition": "2021", - "version": "27.0.0" + "version": "28.0.0" }, "license": "Apache-2.0 WITH LLVM-exception", "license_ids": [ @@ -54142,13 +56977,13 @@ "target": "libc" } ], - "aarch64-fuchsia": [ + "aarch64-linux-android": [ { "id": "libc 0.2.158", "target": "libc" } ], - "aarch64-linux-android": [ + "aarch64-unknown-fuchsia": [ { "id": "libc 0.2.158", "target": "libc" @@ -54238,19 +57073,19 @@ "target": "libc" } ], - "x86_64-fuchsia": [ + "x86_64-linux-android": [ { "id": "libc 0.2.158", "target": "libc" } ], - "x86_64-linux-android": [ + "x86_64-unknown-freebsd": [ { "id": "libc 0.2.158", "target": "libc" } ], - "x86_64-unknown-freebsd": [ + "x86_64-unknown-fuchsia": [ { "id": "libc 0.2.158", "target": "libc" @@ -55690,14 +58525,14 @@ ], "license_file": "LICENSE" }, - "regalloc2 0.10.2": { + "regalloc2 0.11.1": { "name": "regalloc2", - "version": "0.10.2", + "version": "0.11.1", "package_url": "https://github.com/bytecodealliance/regalloc2", "repository": { "Http": { - "url": "https://static.crates.io/crates/regalloc2/0.10.2/download", - "sha256": "12908dbeb234370af84d0579b9f68258a0f67e201412dd9a2814e6f45b2fc0f0" + "url": "https://static.crates.io/crates/regalloc2/0.11.1/download", + "sha256": "145c1c267e14f20fb0f88aa76a1c5ffec42d592c1d28b3cd9148ae35916158d3" } }, "targets": [ @@ -55730,7 +58565,15 @@ "deps": { "common": [ { - "id": "hashbrown 0.14.5", + "id": "allocator-api2 0.2.21", + "target": "allocator_api2" + }, + { + "id": "bumpalo 3.16.0", + "target": "bumpalo" + }, + { + "id": "hashbrown 0.15.2", "target": "hashbrown" }, { @@ -55741,10 +58584,6 @@ "id": "rustc-hash 2.0.0", "target": "rustc_hash" }, - { - "id": "slice-group-by 0.3.1", - "target": "slice_group_by" - }, { "id": "smallvec 1.13.2", "target": "smallvec" @@ -55753,7 +58592,7 @@ "selects": {} }, "edition": "2018", - "version": "0.10.2" + "version": "0.11.1" }, "license": "Apache-2.0 WITH LLVM-exception", "license_ids": [ @@ -56420,7 +59259,7 @@ "target": "http" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -56535,7 +59374,7 @@ "target": "webpki_roots" } ], - "aarch64-fuchsia": [ + "aarch64-linux-android": [ { "id": "async-compression 0.4.3", "target": "async_compression" @@ -56565,7 +59404,7 @@ "target": "webpki_roots" } ], - "aarch64-linux-android": [ + "aarch64-pc-windows-msvc": [ { "id": "async-compression 0.4.3", "target": "async_compression" @@ -56595,7 +59434,7 @@ "target": "webpki_roots" } ], - "aarch64-pc-windows-msvc": [ + "aarch64-unknown-fuchsia": [ { "id": "async-compression 0.4.3", "target": "async_compression" @@ -56823,7 +59662,7 @@ "target": "hyper" }, { - "id": "ipnet 2.8.0", + "id": "ipnet 2.10.1", "target": "ipnet" }, { @@ -57271,7 +60110,7 @@ "target": "webpki_roots" } ], - "x86_64-fuchsia": [ + "x86_64-linux-android": [ { "id": "async-compression 0.4.3", "target": "async_compression" @@ -57301,7 +60140,7 @@ "target": "webpki_roots" } ], - "x86_64-linux-android": [ + "x86_64-pc-windows-msvc": [ { "id": "async-compression 0.4.3", "target": "async_compression" @@ -57331,7 +60170,7 @@ "target": "webpki_roots" } ], - "x86_64-pc-windows-msvc": [ + "x86_64-unknown-freebsd": [ { "id": "async-compression 0.4.3", "target": "async_compression" @@ -57361,7 +60200,7 @@ "target": "webpki_roots" } ], - "x86_64-unknown-freebsd": [ + "x86_64-unknown-fuchsia": [ { "id": "async-compression 0.4.3", "target": "async_compression" @@ -57570,7 +60409,7 @@ "target": "mime_guess" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -57745,7 +60584,7 @@ "target": "webpki_roots" } ], - "aarch64-fuchsia": [ + "aarch64-linux-android": [ { "id": "futures-channel 0.3.31", "target": "futures_channel" @@ -57795,7 +60634,7 @@ "target": "webpki_roots" } ], - "aarch64-linux-android": [ + "aarch64-pc-windows-msvc": [ { "id": "futures-channel 0.3.31", "target": "futures_channel" @@ -57845,7 +60684,7 @@ "target": "webpki_roots" } ], - "aarch64-pc-windows-msvc": [ + "aarch64-unknown-fuchsia": [ { "id": "futures-channel 0.3.31", "target": "futures_channel" @@ -58213,7 +61052,7 @@ "target": "hyper_util" }, { - "id": "ipnet 2.8.0", + "id": "ipnet 2.10.1", "target": "ipnet" }, { @@ -58821,7 +61660,7 @@ "target": "wasm_streams" } ], - "wasm32-wasi": [ + "wasm32-wasip1": [ { "id": "wasm-streams 0.4.0", "target": "wasm_streams" @@ -58927,7 +61766,7 @@ "target": "webpki_roots" } ], - "x86_64-fuchsia": [ + "x86_64-linux-android": [ { "id": "futures-channel 0.3.31", "target": "futures_channel" @@ -58977,7 +61816,7 @@ "target": "webpki_roots" } ], - "x86_64-linux-android": [ + "x86_64-pc-windows-msvc": [ { "id": "futures-channel 0.3.31", "target": "futures_channel" @@ -59027,7 +61866,7 @@ "target": "webpki_roots" } ], - "x86_64-pc-windows-msvc": [ + "x86_64-unknown-freebsd": [ { "id": "futures-channel 0.3.31", "target": "futures_channel" @@ -59077,7 +61916,7 @@ "target": "webpki_roots" } ], - "x86_64-unknown-freebsd": [ + "x86_64-unknown-fuchsia": [ { "id": "futures-channel 0.3.31", "target": "futures_channel" @@ -59348,6 +62187,62 @@ ], "license_file": "LICENSE-APACHE" }, + "rfc6979 0.3.1": { + "name": "rfc6979", + "version": "0.3.1", + "package_url": "https://github.com/RustCrypto/signatures/tree/master/rfc6979", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/rfc6979/0.3.1/download", + "sha256": "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" + } + }, + "targets": [ + { + "Library": { + "crate_name": "rfc6979", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "rfc6979", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "crypto-bigint 0.4.9", + "target": "crypto_bigint" + }, + { + "id": "hmac 0.12.1", + "target": "hmac" + }, + { + "id": "zeroize 1.8.1", + "target": "zeroize" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.3.1" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "rfc6979 0.4.0": { "name": "rfc6979", "version": "0.4.0", @@ -59653,7 +62548,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" } ], @@ -59770,7 +62665,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" } ], @@ -60758,7 +63653,7 @@ "target": "build_script_build" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -61195,7 +64090,7 @@ "time", "use-libc-auxv" ], - "aarch64-fuchsia": [ + "aarch64-linux-android": [ "default", "event", "mm", @@ -61205,7 +64100,7 @@ "time", "use-libc-auxv" ], - "aarch64-linux-android": [ + "aarch64-unknown-fuchsia": [ "default", "event", "mm", @@ -61376,7 +64271,7 @@ "termios", "use-libc-auxv" ], - "wasm32-wasi": [ + "wasm32-wasip1": [ "default", "termios", "use-libc-auxv" @@ -61401,7 +64296,7 @@ "time", "use-libc-auxv" ], - "x86_64-fuchsia": [ + "x86_64-linux-android": [ "default", "event", "mm", @@ -61411,7 +64306,7 @@ "time", "use-libc-auxv" ], - "x86_64-linux-android": [ + "x86_64-unknown-freebsd": [ "default", "event", "mm", @@ -61421,7 +64316,7 @@ "time", "use-libc-auxv" ], - "x86_64-unknown-freebsd": [ + "x86_64-unknown-fuchsia": [ "default", "event", "mm", @@ -61509,17 +64404,6 @@ "target": "libc" } ], - "aarch64-fuchsia": [ - { - "id": "errno 0.3.8", - "target": "errno", - "alias": "libc_errno" - }, - { - "id": "libc 0.2.158", - "target": "libc" - } - ], "aarch64-linux-android": [ { "id": "errno 0.3.8", @@ -61538,6 +64422,17 @@ "alias": "libc_errno" } ], + "aarch64-unknown-fuchsia": [ + { + "id": "errno 0.3.8", + "target": "errno", + "alias": "libc_errno" + }, + { + "id": "libc 0.2.158", + "target": "libc" + } + ], "aarch64-unknown-nto-qnx710": [ { "id": "errno 0.3.8", @@ -61711,7 +64606,7 @@ "target": "libc" } ], - "wasm32-wasi": [ + "wasm32-wasip1": [ { "id": "errno 0.3.8", "target": "errno", @@ -61744,7 +64639,7 @@ "target": "libc" } ], - "x86_64-fuchsia": [ + "x86_64-linux-android": [ { "id": "errno 0.3.8", "target": "errno", @@ -61755,25 +64650,25 @@ "target": "libc" } ], - "x86_64-linux-android": [ + "x86_64-pc-windows-msvc": [ { "id": "errno 0.3.8", "target": "errno", "alias": "libc_errno" - }, - { - "id": "libc 0.2.158", - "target": "libc" } ], - "x86_64-pc-windows-msvc": [ + "x86_64-unknown-freebsd": [ { "id": "errno 0.3.8", "target": "errno", "alias": "libc_errno" + }, + { + "id": "libc 0.2.158", + "target": "libc" } ], - "x86_64-unknown-freebsd": [ + "x86_64-unknown-fuchsia": [ { "id": "errno 0.3.8", "target": "errno", @@ -62244,7 +65139,7 @@ "target": "ring" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -62900,104 +65795,7 @@ "ring", "std" ], - "selects": { - "aarch64-apple-darwin": [ - "default" - ], - "aarch64-apple-ios": [ - "default" - ], - "aarch64-apple-ios-sim": [ - "default" - ], - "aarch64-fuchsia": [ - "default" - ], - "aarch64-linux-android": [ - "default" - ], - "aarch64-pc-windows-msvc": [ - "default" - ], - "aarch64-unknown-linux-gnu": [ - "default" - ], - "aarch64-unknown-nixos-gnu": [ - "default" - ], - "aarch64-unknown-nto-qnx710": [ - "default" - ], - "arm-unknown-linux-gnueabi": [ - "default" - ], - "armv7-linux-androideabi": [ - "default" - ], - "armv7-unknown-linux-gnueabi": [ - "default" - ], - "i686-apple-darwin": [ - "default" - ], - "i686-linux-android": [ - "default" - ], - "i686-pc-windows-msvc": [ - "default" - ], - "i686-unknown-freebsd": [ - "default" - ], - "i686-unknown-linux-gnu": [ - "default" - ], - "powerpc-unknown-linux-gnu": [ - "default" - ], - "riscv32imc-unknown-none-elf": [ - "default" - ], - "riscv64gc-unknown-none-elf": [ - "default" - ], - "s390x-unknown-linux-gnu": [ - "default" - ], - "thumbv7em-none-eabi": [ - "default" - ], - "thumbv8m.main-none-eabi": [ - "default" - ], - "x86_64-apple-darwin": [ - "default" - ], - "x86_64-apple-ios": [ - "default" - ], - "x86_64-fuchsia": [ - "default" - ], - "x86_64-linux-android": [ - "default" - ], - "x86_64-pc-windows-msvc": [ - "default" - ], - "x86_64-unknown-freebsd": [ - "default" - ], - "x86_64-unknown-linux-gnu": [ - "default" - ], - "x86_64-unknown-nixos-gnu": [ - "default" - ], - "x86_64-unknown-none": [ - "default" - ] - } + "selects": {} }, "deps": { "common": [ @@ -63496,7 +66294,7 @@ "target": "build_script_build" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -63893,6 +66691,90 @@ ], "license_file": null }, + "sec1 0.3.0": { + "name": "sec1", + "version": "0.3.0", + "package_url": "https://github.com/RustCrypto/formats/tree/master/sec1", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/sec1/0.3.0/download", + "sha256": "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" + } + }, + "targets": [ + { + "Library": { + "crate_name": "sec1", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "sec1", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "alloc", + "base16ct", + "default", + "der", + "generic-array", + "pem", + "pkcs8", + "point", + "std", + "subtle", + "zeroize" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "base16ct 0.1.1", + "target": "base16ct" + }, + { + "id": "der 0.6.1", + "target": "der" + }, + { + "id": "generic-array 0.14.7", + "target": "generic_array" + }, + { + "id": "pkcs8 0.9.0", + "target": "pkcs8" + }, + { + "id": "subtle 2.6.1", + "target": "subtle" + }, + { + "id": "zeroize 1.8.1", + "target": "zeroize" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.3.0" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "sec1 0.7.3": { "name": "sec1", "version": "0.7.3", @@ -64027,7 +66909,7 @@ "target": "secp256k1_sys" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -64098,7 +66980,7 @@ "target": "secp256k1_sys" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -64113,14 +66995,14 @@ ], "license_file": "LICENSE" }, - "secp256k1 0.29.0": { + "secp256k1 0.29.1": { "name": "secp256k1", - "version": "0.29.0", + "version": "0.29.1", "package_url": "https://github.com/rust-bitcoin/rust-secp256k1/", "repository": { "Http": { - "url": "https://static.crates.io/crates/secp256k1/0.29.0/download", - "sha256": "0e0cc0f1cf93f4969faf3ea1c7d8a9faed25918d96affa959720823dfe86d4f3" + "url": "https://static.crates.io/crates/secp256k1/0.29.1/download", + "sha256": "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" } }, "targets": [ @@ -64145,7 +67027,12 @@ "crate_features": { "common": [ "alloc", - "hashes" + "hashes", + "rand", + "rand-std", + "recovery", + "serde", + "std" ], "selects": {} }, @@ -64156,15 +67043,23 @@ "target": "bitcoin_hashes", "alias": "hashes" }, + { + "id": "rand 0.8.5", + "target": "rand" + }, { "id": "secp256k1-sys 0.10.0", "target": "secp256k1_sys" + }, + { + "id": "serde 1.0.217", + "target": "serde" } ], "selects": {} }, "edition": "2021", - "version": "0.29.0" + "version": "0.29.1" }, "license": "CC0-1.0", "license_ids": [ @@ -64242,7 +67137,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" } ], @@ -64327,7 +67222,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" } ], @@ -64384,7 +67279,9 @@ ], "crate_features": { "common": [ - "alloc" + "alloc", + "recovery", + "std" ], "selects": {} }, @@ -64416,7 +67313,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" } ], @@ -64470,7 +67367,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -64521,16 +67418,91 @@ ], "crate_features": { "common": [ - "OSX_10_10", - "OSX_10_11", + "OSX_10_10", + "OSX_10_11", + "OSX_10_12", + "OSX_10_13", + "OSX_10_14", + "OSX_10_9", + "alpn", + "default", + "serial-number-bigint", + "session-tickets" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "bitflags 2.6.0", + "target": "bitflags" + }, + { + "id": "core-foundation 0.9.4", + "target": "core_foundation" + }, + { + "id": "core-foundation-sys 0.8.7", + "target": "core_foundation_sys" + }, + { + "id": "libc 0.2.158", + "target": "libc" + }, + { + "id": "num-bigint 0.4.6", + "target": "num_bigint" + }, + { + "id": "security-framework-sys 2.12.0", + "target": "security_framework_sys" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "2.11.1" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, + "security-framework 3.0.1": { + "name": "security-framework", + "version": "3.0.1", + "package_url": "https://github.com/kornelski/rust-security-framework", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/security-framework/3.0.1/download", + "sha256": "e1415a607e92bec364ea2cf9264646dcce0f91e6d65281bd6f2819cca3bf39c8" + } + }, + "targets": [ + { + "Library": { + "crate_name": "security_framework", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "security_framework", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ "OSX_10_12", - "OSX_10_13", - "OSX_10_14", - "OSX_10_9", - "alpn", - "default", - "serial-number-bigint", - "session-tickets" + "default" ], "selects": {} }, @@ -64541,7 +67513,7 @@ "target": "bitflags" }, { - "id": "core-foundation 0.9.4", + "id": "core-foundation 0.10.0", "target": "core_foundation" }, { @@ -64552,10 +67524,6 @@ "id": "libc 0.2.158", "target": "libc" }, - { - "id": "num-bigint 0.4.6", - "target": "num_bigint" - }, { "id": "security-framework-sys 2.12.0", "target": "security_framework_sys" @@ -64564,7 +67532,7 @@ "selects": {} }, "edition": "2021", - "version": "2.11.1" + "version": "3.0.1" }, "license": "MIT OR Apache-2.0", "license_ids": [ @@ -64814,7 +67782,7 @@ "target": "build_script_build" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -64838,14 +67806,14 @@ ], "license_file": "LICENSE-APACHE" }, - "serde 1.0.214": { + "serde 1.0.217": { "name": "serde", - "version": "1.0.214", + "version": "1.0.217", "package_url": "https://github.com/serde-rs/serde", "repository": { "Http": { - "url": "https://static.crates.io/crates/serde/1.0.214/download", - "sha256": "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" + "url": "https://static.crates.io/crates/serde/1.0.217/download", + "sha256": "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" } }, "targets": [ @@ -64893,7 +67861,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "build_script_build" } ], @@ -64903,13 +67871,13 @@ "proc_macro_deps": { "common": [ { - "id": "serde_derive 1.0.214", + "id": "serde_derive 1.0.217", "target": "serde_derive" } ], "selects": {} }, - "version": "1.0.214" + "version": "1.0.217" }, "build_script_attrs": { "compile_data_glob": [ @@ -64966,7 +67934,7 @@ "target": "hex" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -65017,7 +67985,7 @@ "target": "ordered_float" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -65068,7 +68036,7 @@ "target": "js_sys" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -65126,7 +68094,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -65185,7 +68153,7 @@ "target": "half" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -65201,14 +68169,14 @@ ], "license_file": "LICENSE-APACHE" }, - "serde_derive 1.0.214": { + "serde_derive 1.0.217": { "name": "serde_derive", - "version": "1.0.214", + "version": "1.0.217", "package_url": "https://github.com/serde-rs/serde", "repository": { "Http": { - "url": "https://static.crates.io/crates/serde_derive/1.0.214/download", - "sha256": "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" + "url": "https://static.crates.io/crates/serde_derive/1.0.217/download", + "sha256": "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" } }, "targets": [ @@ -65254,7 +68222,7 @@ "selects": {} }, "edition": "2015", - "version": "1.0.214" + "version": "1.0.217" }, "license": "MIT OR Apache-2.0", "license_ids": [ @@ -65386,7 +68354,7 @@ "target": "ryu" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -65450,7 +68418,7 @@ "target": "itoa" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -65502,7 +68470,7 @@ "target": "percent_encoding" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -65558,7 +68526,7 @@ "target": "regex" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -65574,14 +68542,14 @@ ], "license_file": "LICENSE-APACHE" }, - "serde_repr 0.1.14": { + "serde_repr 0.1.19": { "name": "serde_repr", - "version": "0.1.14", + "version": "0.1.19", "package_url": "https://github.com/dtolnay/serde-repr", "repository": { "Http": { - "url": "https://static.crates.io/crates/serde_repr/0.1.14/download", - "sha256": "1d89a8107374290037607734c0b73a85db7ed80cae314b3c5791f192a496e731" + "url": "https://static.crates.io/crates/serde_repr/0.1.19/download", + "sha256": "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" } }, "targets": [ @@ -65620,8 +68588,8 @@ ], "selects": {} }, - "edition": "2018", - "version": "0.1.14" + "edition": "2021", + "version": "0.1.19" }, "license": "MIT OR Apache-2.0", "license_ids": [ @@ -65666,7 +68634,7 @@ "target": "proc_macro2" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -65725,7 +68693,7 @@ "target": "quote" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -65788,7 +68756,7 @@ "target": "ryu" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -65844,7 +68812,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -65915,7 +68883,7 @@ "target": "base64" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -66032,7 +69000,7 @@ "deps": { "common": [ { - "id": "darling 0.20.3", + "id": "darling 0.20.10", "target": "darling" }, { @@ -66100,7 +69068,7 @@ "target": "ryu" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -66164,7 +69132,7 @@ "target": "ryu" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -66636,6 +69604,52 @@ ], "license_file": "LICENSE" }, + "shell-words 1.1.0": { + "name": "shell-words", + "version": "1.1.0", + "package_url": "https://github.com/tmiasko/shell-words", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/shell-words/1.1.0/download", + "sha256": "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + } + }, + "targets": [ + { + "Library": { + "crate_name": "shell_words", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "shell_words", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "default", + "std" + ], + "selects": {} + }, + "edition": "2015", + "version": "1.1.0" + }, + "license": "MIT/Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "shlex 1.3.0": { "name": "shlex", "version": "1.3.0", @@ -66878,6 +69892,69 @@ ], "license_file": "LICENSE-APACHE" }, + "signature 1.6.4": { + "name": "signature", + "version": "1.6.4", + "package_url": "https://github.com/RustCrypto/traits/tree/master/signature", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/signature/1.6.4/download", + "sha256": "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" + } + }, + "targets": [ + { + "Library": { + "crate_name": "signature", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "signature", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "digest", + "digest-preview", + "hazmat-preview", + "rand-preview", + "rand_core", + "std" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "digest 0.10.7", + "target": "digest" + }, + { + "id": "rand_core 0.6.4", + "target": "rand_core" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "1.6.4" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "signature 2.2.0": { "name": "signature", "version": "2.2.0", @@ -67395,44 +70472,6 @@ ], "license_file": "LICENSE" }, - "slice-group-by 0.3.1": { - "name": "slice-group-by", - "version": "0.3.1", - "package_url": "https://github.com/Kerollmops/slice-group-by", - "repository": { - "Http": { - "url": "https://static.crates.io/crates/slice-group-by/0.3.1/download", - "sha256": "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" - } - }, - "targets": [ - { - "Library": { - "crate_name": "slice_group_by", - "crate_root": "src/lib.rs", - "srcs": { - "allow_empty": true, - "include": [ - "**/*.rs" - ] - } - } - } - ], - "library_target_name": "slice_group_by", - "common_attrs": { - "compile_data_glob": [ - "**" - ], - "edition": "2018", - "version": "0.3.1" - }, - "license": "MIT", - "license_ids": [ - "MIT" - ], - "license_file": "LICENSE" - }, "slog 2.7.0": { "name": "slog", "version": "2.7.0", @@ -67732,7 +70771,7 @@ "target": "erased_serde" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -68071,7 +71110,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -68416,15 +71455,15 @@ "aarch64-apple-ios-sim": [ "once" ], - "aarch64-fuchsia": [ - "once" - ], "aarch64-linux-android": [ "once" ], "aarch64-pc-windows-msvc": [ "once" ], + "aarch64-unknown-fuchsia": [ + "once" + ], "aarch64-unknown-linux-gnu": [ "once" ], @@ -68470,9 +71509,6 @@ "x86_64-apple-ios": [ "once" ], - "x86_64-fuchsia": [ - "once" - ], "x86_64-linux-android": [ "once" ], @@ -68482,6 +71518,9 @@ "x86_64-unknown-freebsd": [ "once" ], + "x86_64-unknown-fuchsia": [ + "once" + ], "x86_64-unknown-linux-gnu": [ "once" ], @@ -68502,6 +71541,66 @@ ], "license_file": "LICENSE" }, + "spki 0.6.0": { + "name": "spki", + "version": "0.6.0", + "package_url": "https://github.com/RustCrypto/formats/tree/master/spki", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/spki/0.6.0/download", + "sha256": "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" + } + }, + "targets": [ + { + "Library": { + "crate_name": "spki", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "spki", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "alloc", + "base64ct", + "pem" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "base64ct 1.6.0", + "target": "base64ct" + }, + { + "id": "der 0.6.1", + "target": "der" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "0.6.0" + }, + "license": "Apache-2.0 OR MIT", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "spki 0.7.3": { "name": "spki", "version": "0.7.3", @@ -68792,7 +71891,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" } ], @@ -68845,6 +71944,66 @@ ], "license_file": "LICENSE-APACHE" }, + "stop-token 0.7.0": { + "name": "stop-token", + "version": "0.7.0", + "package_url": "https://github.com/async-rs/stop-token", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/stop-token/0.7.0/download", + "sha256": "af91f480ee899ab2d9f8435bfdfc14d08a5754bd9d3fef1f1a1c23336aad6c8b" + } + }, + "targets": [ + { + "Library": { + "crate_name": "stop_token", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "stop_token", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "async-channel 1.9.0", + "target": "async_channel" + }, + { + "id": "cfg-if 1.0.0", + "target": "cfg_if" + }, + { + "id": "futures-core 0.3.31", + "target": "futures_core" + }, + { + "id": "pin-project-lite 0.2.13", + "target": "pin_project_lite" + } + ], + "selects": {} + }, + "edition": "2018", + "version": "0.7.0" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": null + }, "str_stack 0.1.0": { "name": "str_stack", "version": "0.1.0", @@ -68944,7 +72103,7 @@ "target": "precomputed_hash" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -70191,6 +73350,95 @@ ], "license_file": "LICENSE" }, + "syscalls 0.6.18": { + "name": "syscalls", + "version": "0.6.18", + "package_url": "https://github.com/jasonwhite/syscalls", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/syscalls/0.6.18/download", + "sha256": "43d0e35dc7d73976a53c7e6d7d177ef804a0c0ee774ec77bcc520c2216fd7cbe" + } + }, + "targets": [ + { + "Library": { + "crate_name": "syscalls", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + }, + { + "BuildScript": { + "crate_name": "build_script_build", + "crate_root": "build.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "syscalls", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "default", + "serde", + "serde_repr", + "std" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "serde 1.0.217", + "target": "serde" + }, + { + "id": "syscalls 0.6.18", + "target": "build_script_build" + } + ], + "selects": {} + }, + "edition": "2021", + "proc_macro_deps": { + "common": [ + { + "id": "serde_repr 0.1.19", + "target": "serde_repr" + } + ], + "selects": {} + }, + "version": "0.6.18" + }, + "build_script_attrs": { + "compile_data_glob": [ + "**" + ], + "data_glob": [ + "**" + ] + }, + "license": "BSD-2-Clause", + "license_ids": [ + "BSD-2-Clause" + ], + "license_file": "LICENSE" + }, "system-configuration 0.5.1": { "name": "system-configuration", "version": "0.5.1", @@ -70572,13 +73820,13 @@ "target": "xattr" } ], - "aarch64-fuchsia": [ + "aarch64-linux-android": [ { "id": "xattr 0.2.3", "target": "xattr" } ], - "aarch64-linux-android": [ + "aarch64-unknown-fuchsia": [ { "id": "xattr 0.2.3", "target": "xattr" @@ -70674,19 +73922,19 @@ "target": "xattr" } ], - "x86_64-fuchsia": [ + "x86_64-linux-android": [ { "id": "xattr 0.2.3", "target": "xattr" } ], - "x86_64-linux-android": [ + "x86_64-unknown-freebsd": [ { "id": "xattr 0.2.3", "target": "xattr" } ], - "x86_64-unknown-freebsd": [ + "x86_64-unknown-fuchsia": [ { "id": "xattr 0.2.3", "target": "xattr" @@ -70865,7 +74113,7 @@ "target": "rand" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -72193,7 +75441,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" } ], @@ -72312,6 +75560,8 @@ "local-offset", "macros", "parsing", + "serde", + "serde-human-readable", "std" ], "selects": {} @@ -72334,6 +75584,10 @@ "id": "powerfmt 0.2.0", "target": "powerfmt" }, + { + "id": "serde 1.0.217", + "target": "serde" + }, { "id": "time-core 0.1.2", "target": "time_core" @@ -72370,7 +75624,7 @@ "target": "num_threads" } ], - "aarch64-fuchsia": [ + "aarch64-linux-android": [ { "id": "libc 0.2.158", "target": "libc" @@ -72380,7 +75634,7 @@ "target": "num_threads" } ], - "aarch64-linux-android": [ + "aarch64-unknown-fuchsia": [ { "id": "libc 0.2.158", "target": "libc" @@ -72530,7 +75784,7 @@ "target": "num_threads" } ], - "x86_64-fuchsia": [ + "x86_64-linux-android": [ { "id": "libc 0.2.158", "target": "libc" @@ -72540,7 +75794,7 @@ "target": "num_threads" } ], - "x86_64-linux-android": [ + "x86_64-unknown-freebsd": [ { "id": "libc 0.2.158", "target": "libc" @@ -72550,7 +75804,7 @@ "target": "num_threads" } ], - "x86_64-unknown-freebsd": [ + "x86_64-unknown-fuchsia": [ { "id": "libc 0.2.158", "target": "libc" @@ -72672,7 +75926,8 @@ "crate_features": { "common": [ "formatting", - "parsing" + "parsing", + "serde" ], "selects": {} }, @@ -72699,6 +75954,110 @@ ], "license_file": "LICENSE-Apache" }, + "tiny-bip39 1.0.0": { + "name": "tiny-bip39", + "version": "1.0.0", + "package_url": "https://github.com/maciejhirsz/tiny-bip39/", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/tiny-bip39/1.0.0/download", + "sha256": "62cc94d358b5a1e84a5cb9109f559aa3c4d634d2b1b4de3d0fa4adc7c78e2861" + } + }, + "targets": [ + { + "Library": { + "crate_name": "bip39", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "bip39", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "crate_features": { + "common": [ + "chinese-simplified", + "chinese-traditional", + "default", + "french", + "italian", + "japanese", + "korean", + "spanish" + ], + "selects": {} + }, + "deps": { + "common": [ + { + "id": "anyhow 1.0.93", + "target": "anyhow" + }, + { + "id": "hmac 0.12.1", + "target": "hmac" + }, + { + "id": "once_cell 1.19.0", + "target": "once_cell" + }, + { + "id": "pbkdf2 0.11.0", + "target": "pbkdf2" + }, + { + "id": "rand 0.8.5", + "target": "rand" + }, + { + "id": "rustc-hash 1.1.0", + "target": "rustc_hash" + }, + { + "id": "sha2 0.10.8", + "target": "sha2" + }, + { + "id": "thiserror 1.0.68", + "target": "thiserror" + }, + { + "id": "unicode-normalization 0.1.22", + "target": "unicode_normalization" + }, + { + "id": "zeroize 1.8.1", + "target": "zeroize" + } + ], + "selects": { + "cfg(target_arch = \"wasm32\")": [ + { + "id": "wasm-bindgen 0.2.95", + "target": "wasm_bindgen" + } + ] + } + }, + "edition": "2018", + "version": "1.0.0" + }, + "license": "MIT OR Apache-2.0", + "license_ids": [ + "Apache-2.0", + "MIT" + ], + "license_file": "LICENSE-APACHE" + }, "tiny-keccak 2.0.2": { "name": "tiny-keccak", "version": "2.0.2", @@ -72878,7 +76237,9 @@ "version": "0.7.6" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "tinytemplate 1.2.1": { @@ -72913,7 +76274,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -73261,7 +76622,7 @@ "target": "socket2" } ], - "aarch64-fuchsia": [ + "aarch64-linux-android": [ { "id": "libc 0.2.158", "target": "libc" @@ -73275,7 +76636,17 @@ "target": "socket2" } ], - "aarch64-linux-android": [ + "aarch64-pc-windows-msvc": [ + { + "id": "socket2 0.5.7", + "target": "socket2" + }, + { + "id": "windows-sys 0.52.0", + "target": "windows_sys" + } + ], + "aarch64-unknown-fuchsia": [ { "id": "libc 0.2.158", "target": "libc" @@ -73289,16 +76660,6 @@ "target": "socket2" } ], - "aarch64-pc-windows-msvc": [ - { - "id": "socket2 0.5.7", - "target": "socket2" - }, - { - "id": "windows-sys 0.52.0", - "target": "windows_sys" - } - ], "aarch64-unknown-linux-gnu": [ { "id": "libc 0.2.158", @@ -73535,7 +76896,7 @@ "target": "socket2" } ], - "x86_64-fuchsia": [ + "x86_64-linux-android": [ { "id": "libc 0.2.158", "target": "libc" @@ -73549,7 +76910,17 @@ "target": "socket2" } ], - "x86_64-linux-android": [ + "x86_64-pc-windows-msvc": [ + { + "id": "socket2 0.5.7", + "target": "socket2" + }, + { + "id": "windows-sys 0.52.0", + "target": "windows_sys" + } + ], + "x86_64-unknown-freebsd": [ { "id": "libc 0.2.158", "target": "libc" @@ -73563,17 +76934,7 @@ "target": "socket2" } ], - "x86_64-pc-windows-msvc": [ - { - "id": "socket2 0.5.7", - "target": "socket2" - }, - { - "id": "windows-sys 0.52.0", - "target": "windows_sys" - } - ], - "x86_64-unknown-freebsd": [ + "x86_64-unknown-fuchsia": [ { "id": "libc 0.2.158", "target": "libc" @@ -73800,7 +77161,7 @@ "target": "tokio" }, { - "id": "tokio-stream 0.1.16", + "id": "tokio-stream 0.1.17", "target": "tokio_stream" } ], @@ -74071,7 +77432,7 @@ "target": "pin_project" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -74166,14 +77527,14 @@ ], "license_file": "LICENSE" }, - "tokio-stream 0.1.16": { + "tokio-stream 0.1.17": { "name": "tokio-stream", - "version": "0.1.16", + "version": "0.1.17", "package_url": "https://github.com/tokio-rs/tokio", "repository": { "Http": { - "url": "https://static.crates.io/crates/tokio-stream/0.1.16/download", - "sha256": "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" + "url": "https://static.crates.io/crates/tokio-stream/0.1.17/download", + "sha256": "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" } }, "targets": [ @@ -74221,7 +77582,7 @@ "selects": {} }, "edition": "2021", - "version": "0.1.16" + "version": "0.1.17" }, "license": "MIT", "license_ids": [ @@ -74277,7 +77638,7 @@ "target": "tokio" }, { - "id": "tokio-stream 0.1.16", + "id": "tokio-stream 0.1.17", "target": "tokio_stream" } ], @@ -74491,7 +77852,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -74780,7 +78141,7 @@ "target": "tokio" }, { - "id": "tokio-stream 0.1.16", + "id": "tokio-stream 0.1.17", "target": "tokio_stream" }, { @@ -76136,7 +79497,7 @@ "target": "opentelemetry" }, { - "id": "opentelemetry_sdk 0.27.0", + "id": "opentelemetry_sdk 0.27.1", "target": "opentelemetry_sdk" }, { @@ -76214,7 +79575,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -76265,7 +79626,7 @@ "deps": { "common": [ { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -76420,7 +79781,7 @@ "target": "regex" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -76628,7 +79989,7 @@ "target": "idna" }, { - "id": "ipnet 2.8.0", + "id": "ipnet 2.10.1", "target": "ipnet" }, { @@ -77010,7 +80371,7 @@ "target": "tokio" }, { - "id": "tokio-stream 0.1.16", + "id": "tokio-stream 0.1.17", "target": "tokio_stream" }, { @@ -77982,7 +81343,7 @@ "target": "percent_encoding" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -78283,7 +81644,7 @@ "target": "getrandom" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -78519,7 +81880,7 @@ "target": "regex" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -79051,7 +82412,7 @@ "target": "scoped_tls" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -79690,14 +83051,14 @@ ], "license_file": null }, - "wasm-encoder 0.219.1": { + "wasm-encoder 0.221.2": { "name": "wasm-encoder", - "version": "0.219.1", + "version": "0.221.2", "package_url": "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wasm-encoder", "repository": { "Http": { - "url": "https://static.crates.io/crates/wasm-encoder/0.219.1/download", - "sha256": "29cbbd772edcb8e7d524a82ee8cef8dd046fc14033796a754c3ad246d019fa54" + "url": "https://static.crates.io/crates/wasm-encoder/0.221.2/download", + "sha256": "c17a3bd88f2155da63a1f2fcb8a56377a24f0b6dfed12733bb5f544e86f690c5" } }, "targets": [ @@ -79736,7 +83097,7 @@ "selects": {} }, "edition": "2021", - "version": "0.219.1" + "version": "0.221.2" }, "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", "license_ids": [ @@ -80002,7 +83363,7 @@ "target": "semver" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -80079,7 +83440,7 @@ "target": "semver" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" } ], @@ -80095,14 +83456,14 @@ ], "license_file": null }, - "wasmparser 0.219.1": { + "wasmparser 0.221.2": { "name": "wasmparser", - "version": "0.219.1", + "version": "0.221.2", "package_url": "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wasmparser", "repository": { "Http": { - "url": "https://static.crates.io/crates/wasmparser/0.219.1/download", - "sha256": "5c771866898879073c53b565a6c7b49953795159836714ac56a5befb581227c5" + "url": "https://static.crates.io/crates/wasmparser/0.221.2/download", + "sha256": "9845c470a2e10b61dd42c385839cdd6496363ed63b5c9e420b5488b77bd22083" } }, "targets": [ @@ -80117,6 +83478,18 @@ ] } } + }, + { + "BuildScript": { + "crate_name": "build_script_build", + "crate_root": "build.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } } ], "library_target_name": "wasmparser", @@ -80129,6 +83502,7 @@ "component-model", "features", "serde", + "simd", "std", "validate" ], @@ -80136,35 +83510,35 @@ }, "deps": { "common": [ - { - "id": "ahash 0.8.11", - "target": "ahash" - }, { "id": "bitflags 2.6.0", "target": "bitflags" }, - { - "id": "hashbrown 0.14.5", - "target": "hashbrown" - }, - { - "id": "indexmap 2.2.6", - "target": "indexmap" - }, { "id": "semver 1.0.22", "target": "semver" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" + }, + { + "id": "wasmparser 0.221.2", + "target": "build_script_build" } ], "selects": {} }, "edition": "2021", - "version": "0.219.1" + "version": "0.221.2" + }, + "build_script_attrs": { + "compile_data_glob": [ + "**" + ], + "data_glob": [ + "**" + ] }, "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", "license_ids": [ @@ -80229,14 +83603,14 @@ ], "license_file": null }, - "wasmprinter 0.219.1": { + "wasmprinter 0.221.2": { "name": "wasmprinter", - "version": "0.219.1", + "version": "0.221.2", "package_url": "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wasmprinter", "repository": { "Http": { - "url": "https://static.crates.io/crates/wasmprinter/0.219.1/download", - "sha256": "228cdc1f30c27816da225d239ce4231f28941147d34713dee8f1fff7cb330e54" + "url": "https://static.crates.io/crates/wasmprinter/0.221.2/download", + "sha256": "a80742ff1b9e6d8c231ac7c7247782c6fc5bce503af760bca071811e5fc9ee56" } }, "targets": [ @@ -80276,14 +83650,14 @@ "target": "termcolor" }, { - "id": "wasmparser 0.219.1", + "id": "wasmparser 0.221.2", "target": "wasmparser" } ], "selects": {} }, "edition": "2021", - "version": "0.219.1" + "version": "0.221.2" }, "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", "license_ids": [ @@ -80292,14 +83666,14 @@ ], "license_file": null }, - "wasmtime 27.0.0": { + "wasmtime 28.0.0": { "name": "wasmtime", - "version": "27.0.0", + "version": "28.0.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/wasmtime/27.0.0/download", - "sha256": "5b79302e3e084713249cc5622e8608e7410afdeeea8c8026d04f491d1fab0b4b" + "url": "https://static.crates.io/crates/wasmtime/28.0.0/download", + "sha256": "f639ecae347b9a2227e453a7b7671e84370a0b61f47a15e0390fe9b7725e47b3" } }, "targets": [ @@ -80341,6 +83715,7 @@ "once_cell", "parallel-compilation", "runtime", + "signals-based-traps", "std" ], "selects": {} @@ -80384,7 +83759,7 @@ "target": "log" }, { - "id": "object 0.36.1", + "id": "object 0.36.7", "target": "object" }, { @@ -80400,7 +83775,7 @@ "target": "rayon" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -80416,31 +83791,31 @@ "target": "target_lexicon" }, { - "id": "wasmparser 0.219.1", + "id": "wasmparser 0.221.2", "target": "wasmparser" }, { - "id": "wasmtime 27.0.0", + "id": "wasmtime 28.0.0", "target": "build_script_build" }, { - "id": "wasmtime-asm-macros 27.0.0", + "id": "wasmtime-asm-macros 28.0.0", "target": "wasmtime_asm_macros" }, { - "id": "wasmtime-cranelift 27.0.0", + "id": "wasmtime-cranelift 28.0.0", "target": "wasmtime_cranelift" }, { - "id": "wasmtime-environ 27.0.0", + "id": "wasmtime-environ 28.0.0", "target": "wasmtime_environ" }, { - "id": "wasmtime-jit-icache-coherence 27.0.0", + "id": "wasmtime-jit-icache-coherence 28.0.0", "target": "wasmtime_jit_icache_coherence" }, { - "id": "wasmtime-slab 27.0.0", + "id": "wasmtime-slab 28.0.0", "target": "wasmtime_slab" } ], @@ -80467,12 +83842,6 @@ "target": "rustix" } ], - "aarch64-fuchsia": [ - { - "id": "rustix 0.38.32", - "target": "rustix" - } - ], "aarch64-linux-android": [ { "id": "rustix 0.38.32", @@ -80485,6 +83854,12 @@ "target": "windows_sys" } ], + "aarch64-unknown-fuchsia": [ + { + "id": "rustix 0.38.32", + "target": "rustix" + } + ], "aarch64-unknown-linux-gnu": [ { "id": "memfd 0.6.4", @@ -80615,12 +83990,6 @@ "target": "rustix" } ], - "x86_64-fuchsia": [ - { - "id": "rustix 0.38.32", - "target": "rustix" - } - ], "x86_64-linux-android": [ { "id": "rustix 0.38.32", @@ -80639,6 +84008,12 @@ "target": "rustix" } ], + "x86_64-unknown-fuchsia": [ + { + "id": "rustix 0.38.32", + "target": "rustix" + } + ], "x86_64-unknown-linux-gnu": [ { "id": "memfd 0.6.4", @@ -80669,17 +84044,17 @@ "target": "paste" }, { - "id": "serde_derive 1.0.214", + "id": "serde_derive 1.0.217", "target": "serde_derive" }, { - "id": "wasmtime-versioned-export-macros 27.0.0", + "id": "wasmtime-versioned-export-macros 28.0.0", "target": "wasmtime_versioned_export_macros" } ], "selects": {} }, - "version": "27.0.0" + "version": "28.0.0" }, "build_script_attrs": { "compile_data_glob": [ @@ -80691,7 +84066,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" } ], @@ -80700,7 +84075,7 @@ "proc_macro_deps": { "common": [ { - "id": "wasmtime-versioned-export-macros 27.0.0", + "id": "wasmtime-versioned-export-macros 28.0.0", "target": "wasmtime_versioned_export_macros" } ], @@ -80713,14 +84088,14 @@ ], "license_file": "LICENSE" }, - "wasmtime-asm-macros 27.0.0": { + "wasmtime-asm-macros 28.0.0": { "name": "wasmtime-asm-macros", - "version": "27.0.0", + "version": "28.0.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/wasmtime-asm-macros/27.0.0/download", - "sha256": "fe53a24e7016a5222875d8ca3ad6024b464465985693c42098cd0bb710002c28" + "url": "https://static.crates.io/crates/wasmtime-asm-macros/28.0.0/download", + "sha256": "882a18800471cfc063c8b3ccf75723784acc3fd534009ac09421f2fac2fcdcec" } }, "targets": [ @@ -80752,7 +84127,7 @@ "selects": {} }, "edition": "2021", - "version": "27.0.0" + "version": "28.0.0" }, "license": "Apache-2.0 WITH LLVM-exception", "license_ids": [ @@ -80760,14 +84135,14 @@ ], "license_file": null }, - "wasmtime-component-macro 27.0.0": { + "wasmtime-component-macro 28.0.0": { "name": "wasmtime-component-macro", - "version": "27.0.0", + "version": "28.0.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/wasmtime-component-macro/27.0.0/download", - "sha256": "e118acbd2bc09b32ad8606bc7cef793bf5019c1b107772e64dc6c76b5055d40b" + "url": "https://static.crates.io/crates/wasmtime-component-macro/28.0.0/download", + "sha256": "eb5c0a77c9e1927c3d471f53cc13767c3d3438e5d5ffd394e3eb31c86445fd60" } }, "targets": [ @@ -80820,26 +84195,26 @@ "target": "syn" }, { - "id": "wasmtime-component-macro 27.0.0", + "id": "wasmtime-component-macro 28.0.0", "target": "build_script_build" }, { - "id": "wasmtime-component-util 27.0.0", + "id": "wasmtime-component-util 28.0.0", "target": "wasmtime_component_util" }, { - "id": "wasmtime-wit-bindgen 27.0.0", + "id": "wasmtime-wit-bindgen 28.0.0", "target": "wasmtime_wit_bindgen" }, { - "id": "wit-parser 0.219.1", + "id": "wit-parser 0.221.2", "target": "wit_parser" } ], "selects": {} }, "edition": "2021", - "version": "27.0.0" + "version": "28.0.0" }, "build_script_attrs": { "compile_data_glob": [ @@ -80855,14 +84230,14 @@ ], "license_file": null }, - "wasmtime-component-util 27.0.0": { + "wasmtime-component-util 28.0.0": { "name": "wasmtime-component-util", - "version": "27.0.0", + "version": "28.0.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/wasmtime-component-util/27.0.0/download", - "sha256": "4a6db4f3ee18c699629eabb9c64e77efe5a93a5137f098db7cab295037ba41c2" + "url": "https://static.crates.io/crates/wasmtime-component-util/28.0.0/download", + "sha256": "43702ca98bf5162eca0573db691ed9ecd36d716f8c6688410fe26ec16b6f9bcb" } }, "targets": [ @@ -80885,7 +84260,7 @@ "**" ], "edition": "2021", - "version": "27.0.0" + "version": "28.0.0" }, "license": "Apache-2.0 WITH LLVM-exception", "license_ids": [ @@ -80893,14 +84268,14 @@ ], "license_file": null }, - "wasmtime-cranelift 27.0.0": { + "wasmtime-cranelift 28.0.0": { "name": "wasmtime-cranelift", - "version": "27.0.0", + "version": "28.0.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/wasmtime-cranelift/27.0.0/download", - "sha256": "8b87e6c78f562b50aff1afd87ff32a57e241424c846c1c8f3c5fd352d2d62906" + "url": "https://static.crates.io/crates/wasmtime-cranelift/28.0.0/download", + "sha256": "20070aa5b75080a8932ec328419faf841df2bc6ceb16b55b0df2b952098392a2" } }, "targets": [ @@ -80940,23 +84315,23 @@ "target": "cfg_if" }, { - "id": "cranelift-codegen 0.114.0", + "id": "cranelift-codegen 0.115.0", "target": "cranelift_codegen" }, { - "id": "cranelift-control 0.114.0", + "id": "cranelift-control 0.115.0", "target": "cranelift_control" }, { - "id": "cranelift-entity 0.114.0", + "id": "cranelift-entity 0.115.0", "target": "cranelift_entity" }, { - "id": "cranelift-frontend 0.114.0", + "id": "cranelift-frontend 0.115.0", "target": "cranelift_frontend" }, { - "id": "cranelift-native 0.114.0", + "id": "cranelift-native 0.115.0", "target": "cranelift_native" }, { @@ -80972,7 +84347,7 @@ "target": "log" }, { - "id": "object 0.36.1", + "id": "object 0.36.7", "target": "object" }, { @@ -80988,11 +84363,11 @@ "target": "thiserror" }, { - "id": "wasmparser 0.219.1", + "id": "wasmparser 0.221.2", "target": "wasmparser" }, { - "id": "wasmtime-environ 27.0.0", + "id": "wasmtime-environ 28.0.0", "target": "wasmtime_environ" } ], @@ -81002,13 +84377,13 @@ "proc_macro_deps": { "common": [ { - "id": "wasmtime-versioned-export-macros 27.0.0", + "id": "wasmtime-versioned-export-macros 28.0.0", "target": "wasmtime_versioned_export_macros" } ], "selects": {} }, - "version": "27.0.0" + "version": "28.0.0" }, "license": "Apache-2.0 WITH LLVM-exception", "license_ids": [ @@ -81016,14 +84391,14 @@ ], "license_file": "LICENSE" }, - "wasmtime-environ 27.0.0": { + "wasmtime-environ 28.0.0": { "name": "wasmtime-environ", - "version": "27.0.0", + "version": "28.0.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/wasmtime-environ/27.0.0/download", - "sha256": "c25bfeaa16432d59a0706e2463d315ef4c9ebcfaf5605670b99d46373bdf9f27" + "url": "https://static.crates.io/crates/wasmtime-environ/28.0.0/download", + "sha256": "2604ddb24879d4dc1dedcb7081d7a8e017259bce916fdae097a97db52cbaab80" } }, "targets": [ @@ -81061,11 +84436,11 @@ "target": "anyhow" }, { - "id": "cranelift-bitset 0.114.0", + "id": "cranelift-bitset 0.115.0", "target": "cranelift_bitset" }, { - "id": "cranelift-entity 0.114.0", + "id": "cranelift-entity 0.115.0", "target": "cranelift_entity" }, { @@ -81081,7 +84456,7 @@ "target": "log" }, { - "id": "object 0.36.1", + "id": "object 0.36.7", "target": "object" }, { @@ -81089,7 +84464,7 @@ "target": "postcard" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -81101,15 +84476,15 @@ "target": "target_lexicon" }, { - "id": "wasm-encoder 0.219.1", + "id": "wasm-encoder 0.221.2", "target": "wasm_encoder" }, { - "id": "wasmparser 0.219.1", + "id": "wasmparser 0.221.2", "target": "wasmparser" }, { - "id": "wasmprinter 0.219.1", + "id": "wasmprinter 0.221.2", "target": "wasmprinter" } ], @@ -81119,13 +84494,13 @@ "proc_macro_deps": { "common": [ { - "id": "serde_derive 1.0.214", + "id": "serde_derive 1.0.217", "target": "serde_derive" } ], "selects": {} }, - "version": "27.0.0" + "version": "28.0.0" }, "license": "Apache-2.0 WITH LLVM-exception", "license_ids": [ @@ -81133,14 +84508,133 @@ ], "license_file": "LICENSE" }, - "wasmtime-jit-icache-coherence 27.0.0": { + "wasmtime-fiber 28.0.0": { + "name": "wasmtime-fiber", + "version": "28.0.0", + "package_url": "https://github.com/bytecodealliance/wasmtime", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/wasmtime-fiber/28.0.0/download", + "sha256": "98593412d2b167ebe2b59d4a17a184978a72f976b53b3a0ec05629451079ac1d" + } + }, + "targets": [ + { + "Library": { + "crate_name": "wasmtime_fiber", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + }, + { + "BuildScript": { + "crate_name": "build_script_build", + "crate_root": "build.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "wasmtime_fiber", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "anyhow 1.0.93", + "target": "anyhow" + }, + { + "id": "cfg-if 1.0.0", + "target": "cfg_if" + }, + { + "id": "wasmtime-asm-macros 28.0.0", + "target": "wasmtime_asm_macros" + }, + { + "id": "wasmtime-fiber 28.0.0", + "target": "build_script_build" + } + ], + "selects": { + "cfg(unix)": [ + { + "id": "rustix 0.38.32", + "target": "rustix" + } + ], + "cfg(windows)": [ + { + "id": "windows-sys 0.59.0", + "target": "windows_sys" + } + ] + } + }, + "edition": "2021", + "proc_macro_deps": { + "common": [ + { + "id": "wasmtime-versioned-export-macros 28.0.0", + "target": "wasmtime_versioned_export_macros" + } + ], + "selects": {} + }, + "version": "28.0.0" + }, + "build_script_attrs": { + "compile_data_glob": [ + "**" + ], + "data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "cc 1.1.37", + "target": "cc" + } + ], + "selects": {} + }, + "proc_macro_deps": { + "common": [ + { + "id": "wasmtime-versioned-export-macros 28.0.0", + "target": "wasmtime_versioned_export_macros" + } + ], + "selects": {} + } + }, + "license": "Apache-2.0 WITH LLVM-exception", + "license_ids": [ + "Apache-2.0" + ], + "license_file": "LICENSE" + }, + "wasmtime-jit-icache-coherence 28.0.0": { "name": "wasmtime-jit-icache-coherence", - "version": "27.0.0", + "version": "28.0.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/wasmtime-jit-icache-coherence/27.0.0/download", - "sha256": "91b218a92866f74f35162f5d03a4e0f62cd0e1cc624285b1014275e5d4575fad" + "url": "https://static.crates.io/crates/wasmtime-jit-icache-coherence/28.0.0/download", + "sha256": "d40d7722b9e1fbeae135715710a8a2570b1e6cf72b74dd653962d89831c6c70d" } }, "targets": [ @@ -81189,7 +84683,7 @@ } }, "edition": "2021", - "version": "27.0.0" + "version": "28.0.0" }, "license": "Apache-2.0 WITH LLVM-exception", "license_ids": [ @@ -81197,14 +84691,14 @@ ], "license_file": null }, - "wasmtime-slab 27.0.0": { + "wasmtime-slab 28.0.0": { "name": "wasmtime-slab", - "version": "27.0.0", + "version": "28.0.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/wasmtime-slab/27.0.0/download", - "sha256": "4d5f8acf677ee6b3b8ba400dd9753ea4769e56a95c4b30b045ac6d2d54b2f8ea" + "url": "https://static.crates.io/crates/wasmtime-slab/28.0.0/download", + "sha256": "8579c335220b4ece9aa490a0e8b46de78cd342b195ab21ff981d095e14b52383" } }, "targets": [ @@ -81227,7 +84721,7 @@ "**" ], "edition": "2021", - "version": "27.0.0" + "version": "28.0.0" }, "license": "Apache-2.0 WITH LLVM-exception", "license_ids": [ @@ -81235,14 +84729,14 @@ ], "license_file": null }, - "wasmtime-versioned-export-macros 27.0.0": { + "wasmtime-versioned-export-macros 28.0.0": { "name": "wasmtime-versioned-export-macros", - "version": "27.0.0", + "version": "28.0.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/wasmtime-versioned-export-macros/27.0.0/download", - "sha256": "df09be00c38f49172ca9936998938476e3f2df782673a39ae2ef9fb0838341b6" + "url": "https://static.crates.io/crates/wasmtime-versioned-export-macros/28.0.0/download", + "sha256": "d7de0a56fb0a69b185968f2d7a9ba54750920a806470dff7ad8de91ac06d277e" } }, "targets": [ @@ -81282,7 +84776,7 @@ "selects": {} }, "edition": "2021", - "version": "27.0.0" + "version": "28.0.0" }, "license": "Apache-2.0 WITH LLVM-exception", "license_ids": [ @@ -81290,14 +84784,93 @@ ], "license_file": null }, - "wasmtime-wit-bindgen 27.0.0": { + "wasmtime-winch 28.0.0": { + "name": "wasmtime-winch", + "version": "28.0.0", + "package_url": "https://github.com/bytecodealliance/wasmtime", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/wasmtime-winch/28.0.0/download", + "sha256": "abd309943c443f5590d12f9aba9ba63c481091c955a0a14de0c2a9e0e3aaeca9" + } + }, + "targets": [ + { + "Library": { + "crate_name": "wasmtime_winch", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "wasmtime_winch", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "anyhow 1.0.93", + "target": "anyhow" + }, + { + "id": "cranelift-codegen 0.115.0", + "target": "cranelift_codegen" + }, + { + "id": "gimli 0.31.1", + "target": "gimli" + }, + { + "id": "object 0.36.7", + "target": "object" + }, + { + "id": "target-lexicon 0.12.16", + "target": "target_lexicon" + }, + { + "id": "wasmparser 0.221.2", + "target": "wasmparser" + }, + { + "id": "wasmtime-cranelift 28.0.0", + "target": "wasmtime_cranelift" + }, + { + "id": "wasmtime-environ 28.0.0", + "target": "wasmtime_environ" + }, + { + "id": "winch-codegen 28.0.0", + "target": "winch_codegen" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "28.0.0" + }, + "license": "Apache-2.0 WITH LLVM-exception", + "license_ids": [ + "Apache-2.0" + ], + "license_file": "LICENSE" + }, + "wasmtime-wit-bindgen 28.0.0": { "name": "wasmtime-wit-bindgen", - "version": "27.0.0", + "version": "28.0.0", "package_url": "https://github.com/bytecodealliance/wasmtime", "repository": { "Http": { - "url": "https://static.crates.io/crates/wasmtime-wit-bindgen/27.0.0/download", - "sha256": "bf3963c9c29df91564d8bd181eb00d0dbaeafa1b2a01e15952bb7391166b704e" + "url": "https://static.crates.io/crates/wasmtime-wit-bindgen/28.0.0/download", + "sha256": "969f83022dac3435d6469edb582ceed04cfe32aa44dc3ef16e5cb55574633df8" } }, "targets": [ @@ -81334,14 +84907,14 @@ "target": "indexmap" }, { - "id": "wit-parser 0.219.1", + "id": "wit-parser 0.221.2", "target": "wit_parser" } ], "selects": {} }, "edition": "2021", - "version": "27.0.0" + "version": "28.0.0" }, "license": "Apache-2.0 WITH LLVM-exception", "license_ids": [ @@ -82304,6 +85877,109 @@ ], "license_file": null }, + "winch-codegen 28.0.0": { + "name": "winch-codegen", + "version": "28.0.0", + "package_url": "https://github.com/bytecodealliance/wasmtime", + "repository": { + "Http": { + "url": "https://static.crates.io/crates/winch-codegen/28.0.0/download", + "sha256": "9110decc2983ed94de904804dcd979ba59cbabc78a94fec6b1d8468ec513d0f6" + } + }, + "targets": [ + { + "Library": { + "crate_name": "winch_codegen", + "crate_root": "src/lib.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + }, + { + "BuildScript": { + "crate_name": "build_script_build", + "crate_root": "build.rs", + "srcs": { + "allow_empty": true, + "include": [ + "**/*.rs" + ] + } + } + } + ], + "library_target_name": "winch_codegen", + "common_attrs": { + "compile_data_glob": [ + "**" + ], + "deps": { + "common": [ + { + "id": "anyhow 1.0.93", + "target": "anyhow" + }, + { + "id": "cranelift-codegen 0.115.0", + "target": "cranelift_codegen" + }, + { + "id": "gimli 0.31.1", + "target": "gimli" + }, + { + "id": "regalloc2 0.11.1", + "target": "regalloc2" + }, + { + "id": "smallvec 1.13.2", + "target": "smallvec" + }, + { + "id": "target-lexicon 0.12.16", + "target": "target_lexicon" + }, + { + "id": "wasmparser 0.221.2", + "target": "wasmparser" + }, + { + "id": "wasmtime-cranelift 28.0.0", + "target": "wasmtime_cranelift" + }, + { + "id": "wasmtime-environ 28.0.0", + "target": "wasmtime_environ" + }, + { + "id": "winch-codegen 28.0.0", + "target": "build_script_build" + } + ], + "selects": {} + }, + "edition": "2021", + "version": "28.0.0" + }, + "build_script_attrs": { + "compile_data_glob": [ + "**" + ], + "data_glob": [ + "**" + ] + }, + "license": "Apache-2.0 WITH LLVM-exception", + "license_ids": [ + "Apache-2.0" + ], + "license_file": "LICENSE" + }, "windows-core 0.52.0": { "name": "windows-core", "version": "0.52.0", @@ -82790,6 +86466,7 @@ "Win32_Networking", "Win32_Networking_WinSock", "Win32_Security", + "Win32_Security_Credentials", "Win32_Storage", "Win32_Storage_FileSystem", "Win32_System", @@ -84759,14 +88436,14 @@ ], "license_file": "LICENSE" }, - "wit-parser 0.219.1": { + "wit-parser 0.221.2": { "name": "wit-parser", - "version": "0.219.1", + "version": "0.221.2", "package_url": "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wit-parser", "repository": { "Http": { - "url": "https://static.crates.io/crates/wit-parser/0.219.1/download", - "sha256": "4a86f669283257e8e424b9a4fc3518e3ade0b95deb9fbc0f93a1876be3eda598" + "url": "https://static.crates.io/crates/wit-parser/0.221.2/download", + "sha256": "fbe1538eea6ea5ddbe5defd0dc82539ad7ba751e1631e9185d24a931f0a5adc8" } }, "targets": [ @@ -84820,7 +88497,7 @@ "target": "semver" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -84832,7 +88509,7 @@ "target": "unicode_xid" }, { - "id": "wasmparser 0.219.1", + "id": "wasmparser 0.221.2", "target": "wasmparser" } ], @@ -84842,13 +88519,13 @@ "proc_macro_deps": { "common": [ { - "id": "serde_derive 1.0.214", + "id": "serde_derive 1.0.217", "target": "serde_derive" } ], "selects": {} }, - "version": "0.219.1" + "version": "0.221.2" }, "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", "license_ids": [ @@ -84935,7 +88612,9 @@ "version": "0.5.5" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "wsl 0.1.0": { @@ -85060,7 +88739,7 @@ "target": "data_encoding" }, { - "id": "serde 1.0.214", + "id": "serde 1.0.217", "target": "serde" }, { @@ -85598,7 +89277,9 @@ "version": "0.7.4" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "yoke-derive 0.7.4": { @@ -85655,7 +89336,9 @@ "version": "0.7.4" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "zerocopy 0.7.32": { @@ -85821,7 +89504,9 @@ "version": "0.1.4" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "zerofrom-derive 0.1.4": { @@ -85878,7 +89563,9 @@ "version": "0.1.4" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "zeroize 1.8.1": { @@ -86056,7 +89743,9 @@ "version": "0.10.4" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "zerovec-derive 0.10.3": { @@ -86109,7 +89798,9 @@ "version": "0.10.3" }, "license": "Unicode-3.0", - "license_ids": [], + "license_ids": [ + "Unicode-3.0" + ], "license_file": "LICENSE" }, "zstd 0.12.4": { @@ -86467,7 +90158,7 @@ "deps": { "common": [ { - "id": "cc 1.0.83", + "id": "cc 1.1.37", "target": "cc" }, { @@ -86505,9 +90196,6 @@ "aarch64-apple-ios-sim": [ "aarch64-apple-ios-sim" ], - "aarch64-fuchsia": [ - "aarch64-fuchsia" - ], "aarch64-linux-android": [ "aarch64-linux-android" ], @@ -86515,6 +90203,9 @@ "aarch64-pc-windows-msvc": [ "aarch64-pc-windows-msvc" ], + "aarch64-unknown-fuchsia": [ + "aarch64-unknown-fuchsia" + ], "aarch64-unknown-linux-gnu": [ "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu" @@ -86557,10 +90248,10 @@ "cfg(all(not(curve25519_dalek_backend = \"fiat\"), not(curve25519_dalek_backend = \"serial\"), target_arch = \"x86_64\"))": [ "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-pc-windows-msvc", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu", "x86_64-unknown-none" @@ -86578,8 +90269,8 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", + "aarch64-unknown-fuchsia", "aarch64-unknown-nto-qnx710", "armv7-linux-androideabi", "i686-apple-darwin", @@ -86592,12 +90283,12 @@ "thumbv7em-none-eabi", "thumbv8m.main-none-eabi", "wasm32-unknown-unknown", - "wasm32-wasi", + "wasm32-wasip1", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-none" ], "cfg(all(target_arch = \"aarch64\", target_env = \"msvc\", not(windows_raw_dylib)))": [ @@ -86622,7 +90313,7 @@ "wasm32-unknown-unknown" ], "cfg(all(target_arch = \"wasm32\", target_os = \"wasi\"))": [ - "wasm32-wasi" + "wasm32-wasip1" ], "cfg(all(target_arch = \"wasm32\", target_vendor = \"unknown\", target_os = \"unknown\", target_env = \"\"))": [ "wasm32-unknown-unknown" @@ -86650,8 +90341,8 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -86666,14 +90357,14 @@ "s390x-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu" ], "cfg(all(unix, not(target_os = \"android\"), not(target_vendor = \"apple\"), not(target_arch = \"wasm32\")))": [ - "aarch64-fuchsia", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -86683,16 +90374,16 @@ "i686-unknown-linux-gnu", "powerpc-unknown-linux-gnu", "s390x-unknown-linux-gnu", - "x86_64-fuchsia", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu" ], "cfg(all(unix, not(target_os = \"macos\")))": [ "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -86705,9 +90396,9 @@ "powerpc-unknown-linux-gnu", "s390x-unknown-linux-gnu", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu" ], @@ -86716,9 +90407,9 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", "aarch64-pc-windows-msvc", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -86734,10 +90425,10 @@ "thumbv8m.main-none-eabi", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-pc-windows-msvc", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu", "x86_64-unknown-none" @@ -86746,9 +90437,9 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", "aarch64-pc-windows-msvc", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -86759,10 +90450,10 @@ "i686-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-pc-windows-msvc", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu", "x86_64-unknown-none" @@ -86771,9 +90462,9 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", "aarch64-pc-windows-msvc", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -86784,10 +90475,10 @@ "i686-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-pc-windows-msvc", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu", "x86_64-unknown-none" @@ -86797,8 +90488,8 @@ "s390x-unknown-linux-gnu" ], "cfg(any(target_arch = \"x86\", target_arch = \"x86_64\", all(any(target_arch = \"aarch64\", target_arch = \"arm\"), any(target_os = \"android\", target_os = \"fuchsia\", target_os = \"linux\"))))": [ - "aarch64-fuchsia", "aarch64-linux-android", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "arm-unknown-linux-gnueabi", @@ -86811,10 +90502,10 @@ "i686-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-pc-windows-msvc", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu", "x86_64-unknown-none" @@ -86827,10 +90518,10 @@ "i686-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-pc-windows-msvc", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu", "x86_64-unknown-none" @@ -86873,9 +90564,9 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", "aarch64-pc-windows-msvc", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "arm-unknown-linux-gnueabi", @@ -86888,13 +90579,13 @@ "i686-unknown-linux-gnu", "powerpc-unknown-linux-gnu", "s390x-unknown-linux-gnu", - "wasm32-wasi", + "wasm32-wasip1", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-pc-windows-msvc", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu" ], @@ -86948,8 +90639,8 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -86964,9 +90655,9 @@ "s390x-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu" ], @@ -86974,8 +90665,8 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -86990,9 +90681,9 @@ "s390x-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu" ], @@ -87000,8 +90691,8 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -87014,12 +90705,12 @@ "i686-unknown-linux-gnu", "powerpc-unknown-linux-gnu", "s390x-unknown-linux-gnu", - "wasm32-wasi", + "wasm32-wasip1", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu" ], @@ -87028,9 +90719,9 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", "aarch64-pc-windows-msvc", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -87047,20 +90738,20 @@ "riscv64gc-unknown-none-elf", "s390x-unknown-linux-gnu", "wasm32-unknown-unknown", - "wasm32-wasi", + "wasm32-wasip1", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-pc-windows-msvc", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu", "x86_64-unknown-none" ], "cfg(not(any(target_os = \"macos\", target_os = \"ios\", target_os = \"windows\", target_arch = \"wasm32\")))": [ - "aarch64-fuchsia", "aarch64-linux-android", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -87076,9 +90767,9 @@ "s390x-unknown-linux-gnu", "thumbv7em-none-eabi", "thumbv8m.main-none-eabi", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu", "x86_64-unknown-none" @@ -87087,8 +90778,8 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -87107,9 +90798,9 @@ "thumbv8m.main-none-eabi", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu", "x86_64-unknown-none" @@ -87118,8 +90809,8 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -87136,12 +90827,12 @@ "s390x-unknown-linux-gnu", "thumbv7em-none-eabi", "thumbv8m.main-none-eabi", - "wasm32-wasi", + "wasm32-wasip1", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu", "x86_64-unknown-none" @@ -87150,9 +90841,9 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", "aarch64-pc-windows-msvc", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -87172,10 +90863,10 @@ "thumbv8m.main-none-eabi", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-pc-windows-msvc", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu", "x86_64-unknown-none" @@ -87184,9 +90875,9 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", "aarch64-pc-windows-msvc", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -87206,10 +90897,10 @@ "thumbv8m.main-none-eabi", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-pc-windows-msvc", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu", "x86_64-unknown-none" @@ -87218,8 +90909,8 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -87237,12 +90928,12 @@ "thumbv7em-none-eabi", "thumbv8m.main-none-eabi", "wasm32-unknown-unknown", - "wasm32-wasi", + "wasm32-wasip1", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu", "x86_64-unknown-none" @@ -87251,9 +90942,9 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", "aarch64-pc-windows-msvc", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -87272,13 +90963,13 @@ "thumbv7em-none-eabi", "thumbv8m.main-none-eabi", "wasm32-unknown-unknown", - "wasm32-wasi", + "wasm32-wasip1", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-pc-windows-msvc", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu", "x86_64-unknown-none" @@ -87288,16 +90979,16 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", "aarch64-pc-windows-msvc", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710" ], "cfg(target_arch = \"wasm32\")": [ "wasm32-unknown-unknown", - "wasm32-wasi" + "wasm32-wasip1" ], "cfg(target_arch = \"x86\")": [ "i686-apple-darwin", @@ -87309,10 +91000,10 @@ "cfg(target_arch = \"x86_64\")": [ "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-pc-windows-msvc", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu", "x86_64-unknown-none" @@ -87333,8 +91024,8 @@ "cfg(target_os = \"cloudabi\")": [], "cfg(target_os = \"dragonfly\")": [], "cfg(target_os = \"fuchsia\")": [ - "aarch64-fuchsia", - "x86_64-fuchsia" + "aarch64-unknown-fuchsia", + "x86_64-unknown-fuchsia" ], "cfg(target_os = \"haiku\")": [], "cfg(target_os = \"hermit\")": [], @@ -87361,7 +91052,7 @@ ], "cfg(target_os = \"redox\")": [], "cfg(target_os = \"wasi\")": [ - "wasm32-wasi" + "wasm32-wasip1" ], "cfg(target_os = \"windows\")": [ "aarch64-pc-windows-msvc", @@ -87373,8 +91064,8 @@ "aarch64-apple-darwin", "aarch64-apple-ios", "aarch64-apple-ios-sim", - "aarch64-fuchsia", "aarch64-linux-android", + "aarch64-unknown-fuchsia", "aarch64-unknown-linux-gnu", "aarch64-unknown-nixos-gnu", "aarch64-unknown-nto-qnx710", @@ -87389,9 +91080,9 @@ "s390x-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-apple-ios", - "x86_64-fuchsia", "x86_64-linux-android", "x86_64-unknown-freebsd", + "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu" ], @@ -87440,8 +91131,8 @@ "wasm32-unknown-unknown": [ "wasm32-unknown-unknown" ], - "wasm32-wasi": [ - "wasm32-wasi" + "wasm32-wasip1": [ + "wasm32-wasip1" ], "x86_64-apple-darwin": [ "x86_64-apple-darwin" @@ -87449,9 +91140,6 @@ "x86_64-apple-ios": [ "x86_64-apple-ios" ], - "x86_64-fuchsia": [ - "x86_64-fuchsia" - ], "x86_64-linux-android": [ "x86_64-linux-android" ], @@ -87463,6 +91151,9 @@ "x86_64-unknown-freebsd": [ "x86_64-unknown-freebsd" ], + "x86_64-unknown-fuchsia": [ + "x86_64-unknown-fuchsia" + ], "x86_64-unknown-linux-gnu": [ "x86_64-unknown-linux-gnu", "x86_64-unknown-nixos-gnu" @@ -87506,8 +91197,8 @@ "bip32 0.5.1", "bit-vec 0.6.3", "bitcoin 0.28.2", - "bitcoin 0.32.2", - "bitcoincore-rpc 0.15.0", + "bitcoin 0.32.5", + "bitcoincore-rpc 0.19.0", "bitcoind 0.32.0", "bitflags 1.3.2", "bs58 0.5.0", @@ -87520,10 +91211,10 @@ "cached 0.49.2", "canbench 0.1.8", "canbench-rs 0.1.8", - "candid 0.10.10", + "candid 0.10.13", "candid_parser 0.1.2", "cargo_metadata 0.14.2", - "cc 1.0.83", + "cc 1.1.37", "cddl 0.9.4", "cfg-if 1.0.0", "chacha20poly1305 0.10.1", @@ -87545,6 +91236,7 @@ "curve25519-dalek 4.1.3", "cvt 0.1.2", "dashmap 5.5.0", + "dfx-core 0.1.3", "dyn-clone 1.0.14", "ed25519-dalek 2.1.1", "educe 0.4.22", @@ -87580,27 +91272,29 @@ "hyper-rustls 0.27.3", "hyper-socks2 0.9.1", "hyper-util 0.1.10", - "ic-agent 0.37.1", + "ic-agent 0.39.2", "ic-bn-lib 0.1.0", "ic-btc-interface 0.2.2", "ic-canister-log 0.2.0", - "ic-canister-sig-creation 1.0.1", - "ic-cbor 2.6.0", + "ic-canister-sig-creation 1.1.0", + "ic-cbor 3.0.2", "ic-cdk 0.16.0", + "ic-cdk 0.18.0-alpha.1", "ic-cdk-macros 0.9.0", + "ic-cdk-macros 0.18.0-alpha.1", "ic-cdk-timers 0.11.0", - "ic-certificate-verification 2.6.0", - "ic-certification 2.6.0", + "ic-certificate-verification 3.0.2", + "ic-certification 3.0.2", "ic-certified-map 0.3.4", - "ic-http-certification 2.6.0", - "ic-http-gateway 0.0.0", + "ic-http-certification 3.0.2", + "ic-http-gateway 0.1.0", "ic-metrics-encoder 1.1.1", - "ic-response-verification 2.6.0", + "ic-response-verification 3.0.2", "ic-sha3 1.0.0", "ic-stable-structures 0.6.5", "ic-test-state-machine-client 3.0.1", - "ic-transport-types 0.37.1", - "ic-utils 0.37.0", + "ic-transport-types 0.39.2", + "ic-utils 0.39.0", "ic-verify-bls-signature 0.6.0", "ic-wasm 0.8.4", "ic-xrc-types 1.2.0", @@ -87617,7 +91311,7 @@ "insta 1.31.0", "instant-acme 0.7.2", "intmap 1.1.0", - "ipnet 2.8.0", + "ipnet 2.10.1", "isocountry 0.3.2", "itertools 0.12.0", "json-patch 0.2.7", @@ -87660,7 +91354,7 @@ "opentelemetry 0.27.0", "opentelemetry-otlp 0.27.0", "opentelemetry-prometheus 0.13.0", - "opentelemetry_sdk 0.27.0", + "opentelemetry_sdk 0.27.1", "p256 0.13.2", "pairing 0.23.0", "parking_lot 0.12.1", @@ -87724,7 +91418,7 @@ "scraper 0.17.1", "secp256k1 0.22.2", "semver 1.0.22", - "serde 1.0.214", + "serde 1.0.217", "serde-bytes-repr 0.1.5", "serde_bytes 0.11.15", "serde_cbor 0.11.2", @@ -87752,6 +91446,7 @@ "stubborn-io 0.3.2", "subtle 2.6.1", "syn 1.0.109", + "syscalls 0.6.18", "tar 0.4.39", "tarpc 0.34.0", "tempfile 3.12.0", @@ -87770,6 +91465,7 @@ "tokio-rustls 0.26.0", "tokio-serde 0.8.0", "tokio-socks 0.5.2", + "tokio-stream 0.1.17", "tokio-test 0.4.4", "tokio-util 0.7.13", "toml 0.5.11", @@ -87799,8 +91495,8 @@ "wasm-smith 0.212.0", "wasmparser 0.217.0", "wasmprinter 0.217.0", - "wasmtime 27.0.0", - "wasmtime-environ 27.0.0", + "wasmtime 28.0.0", + "wasmtime-environ 28.0.0", "wast 212.0.0", "wat 1.212.0", "wee_alloc 0.4.5", @@ -87813,5 +91509,12 @@ "zeroize 1.8.1", "zstd 0.13.2" ], - "direct_dev_deps": [] + "direct_dev_deps": [], + "unused_patches": [ + { + "name": "jsonrpc", + "version": "0.12.1", + "source": "git+https://github.com/apoelstra/rust-jsonrpc?rev=e42044d#e42044d8e0896317488dfbd65eb5563d76e937fe" + } + ] } diff --git a/Cargo.Bazel.toml.lock b/Cargo.Bazel.toml.lock index ffbbf5c0153..ce32f9db713 100644 --- a/Cargo.Bazel.toml.lock +++ b/Cargo.Bazel.toml.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "abnf" @@ -258,6 +258,31 @@ dependencies = [ "generic-array", ] +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if 1.0.0", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + [[package]] name = "ahash" version = "0.7.8" @@ -338,9 +363,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.16" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "android-tzdata" @@ -433,6 +458,17 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" +[[package]] +name = "argon2" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db4ce4441f99dbd377ca8a8f57b698c44d0d6e712d8329b5040da5a64aa1ce73" +dependencies = [ + "base64ct", + "blake2", + "password-hash", +] + [[package]] name = "arrayvec" version = "0.5.2" @@ -571,6 +607,17 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", +] + [[package]] name = "async-channel" version = "2.3.1" @@ -724,6 +771,15 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "async-watch" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a078faf4e27c0c6cc0efb20e5da59dcccc04968ebf2801d8e0b2195124cdcdb2" +dependencies = [ + "event-listener 2.5.3", +] + [[package]] name = "async-web-client" version = "0.6.2" @@ -965,6 +1021,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d27c3610c36aee21ce8ac510e6224498de4228ad772a171ed65643a24693a5a8" +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + [[package]] name = "base16ct" version = "0.2.0" @@ -1126,17 +1188,35 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "bip32" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b30ed1d6f8437a487a266c8293aeb95b61a23261273e3e02912cdb8b68bf798b" +dependencies = [ + "bs58 0.4.0", + "hmac", + "k256 0.11.6", + "once_cell", + "pbkdf2 0.11.0", + "rand_core 0.6.4", + "ripemd", + "sha2 0.10.8", + "subtle", + "zeroize", +] + [[package]] name = "bip32" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e141fb0f8be1c7b45887af94c88b182472b57c96b56773250ae00cd6a14a164" dependencies = [ - "bs58", + "bs58 0.5.0", "hmac", - "k256", + "k256 0.13.4", "once_cell", - "pbkdf2", + "pbkdf2 0.12.2", "rand_core 0.6.4", "ripemd", "sha2 0.10.8", @@ -1187,9 +1267,9 @@ dependencies = [ [[package]] name = "bitcoin" -version = "0.32.2" +version = "0.32.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea507acc1cd80fc084ace38544bbcf7ced7c2aa65b653b102de0ce718df668f6" +checksum = "ce6bc65742dea50536e35ad42492b234c27904a27f0abdcbce605015cb4ea026" dependencies = [ "base58ck", "bech32 0.11.0", @@ -1199,7 +1279,8 @@ dependencies = [ "bitcoin_hashes 0.14.0", "hex-conservative", "hex_lit", - "secp256k1 0.29.0", + "secp256k1 0.29.1", + "serde", ] [[package]] @@ -1207,6 +1288,9 @@ name = "bitcoin-internals" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30bdbe14aa07b06e6cfeffc529a1f099e5fbe249524f8125358604df99a4bed2" +dependencies = [ + "serde", +] [[package]] name = "bitcoin-io" @@ -1227,6 +1311,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5285c8bcaa25876d07f37e3d30c303f2609179716e11d688f51e8f1fe70063e2" dependencies = [ "bitcoin-internals", + "serde", ] [[package]] @@ -1256,16 +1341,17 @@ checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" dependencies = [ "bitcoin-io", "hex-conservative", + "serde", ] [[package]] name = "bitcoincore-rpc" -version = "0.15.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0e67dbf7a9971e7f4276f6089e9e814ce0f624a03216b7d92d00351ae7fb3e" +checksum = "aedd23ae0fd321affb4bbbc36126c6f49a32818dc6b979395d24da8c9d4e80ee" dependencies = [ "bitcoincore-rpc-json", - "jsonrpc 0.12.1", + "jsonrpc 0.18.0", "log", "serde", "serde_json", @@ -1273,11 +1359,11 @@ dependencies = [ [[package]] name = "bitcoincore-rpc-json" -version = "0.15.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e2ae16202721ba8c3409045681fac790a5ddc791f05731a2df22c0c6bffc0f1" +checksum = "d8909583c5fab98508e80ef73e5592a651c954993dc6b7739963257d19f0e71a" dependencies = [ - "bitcoin 0.28.2", + "bitcoin 0.32.5", "serde", "serde_json", ] @@ -1319,6 +1405,15 @@ dependencies = [ "wyz", ] +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "block-buffer" version = "0.9.0" @@ -1343,7 +1438,7 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" dependencies = [ - "async-channel", + "async-channel 2.3.1", "async-task", "futures-io", "futures-lite", @@ -1426,6 +1521,15 @@ dependencies = [ "alloc-stdlib", ] +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" +dependencies = [ + "sha2 0.9.9", +] + [[package]] name = "bs58" version = "0.5.0" @@ -1511,6 +1615,9 @@ name = "bumpalo" version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +dependencies = [ + "allocator-api2", +] [[package]] name = "by_address" @@ -1611,13 +1718,10 @@ checksum = "4964518bd3b4a8190e832886cdc0da9794f12e8e6c1613a9e90ff331c4c8724b" [[package]] name = "cached" -version = "0.47.0" +version = "0.49.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69b0116662497bc24e4b177c90eaf8870e39e2714c3fcfa296327a93f593fc21" +checksum = "f251fd1e72720ca07bf5d8e310f54a193fd053479a1f6342c6663ee4fa01cf96" dependencies = [ - "ahash 0.8.11", - "cached_proc_macro", - "cached_proc_macro_types", "hashbrown 0.14.5", "instant", "once_cell", @@ -1626,10 +1730,11 @@ dependencies = [ [[package]] name = "cached" -version = "0.49.2" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f251fd1e72720ca07bf5d8e310f54a193fd053479a1f6342c6663ee4fa01cf96" +checksum = "a8466736fe5dbcaf8b8ee24f9bbefe43c884dc3e9ff7178da70f55bffca1133c" dependencies = [ + "ahash 0.8.11", "hashbrown 0.14.5", "instant", "once_cell", @@ -1638,34 +1743,36 @@ dependencies = [ [[package]] name = "cached" -version = "0.52.0" +version = "0.54.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8466736fe5dbcaf8b8ee24f9bbefe43c884dc3e9ff7178da70f55bffca1133c" +checksum = "9718806c4a2fe9e8a56fd736f97b340dd10ed1be8ed733ed50449f351dc33cae" dependencies = [ "ahash 0.8.11", + "cached_proc_macro", + "cached_proc_macro_types", "hashbrown 0.14.5", - "instant", "once_cell", "thiserror 1.0.68", + "web-time", ] [[package]] name = "cached_proc_macro" -version = "0.18.1" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c878c71c2821aa2058722038a59a67583a4240524687c6028571c9b395ded61f" +checksum = "2f42a145ed2d10dce2191e1dcf30cfccfea9026660e143662ba5eec4017d5daa" dependencies = [ - "darling 0.14.4", + "darling 0.20.10", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.87", ] [[package]] name = "cached_proc_macro_types" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a4f925191b4367301851c6d99b09890311d74b0d43f274c0b34c86d308a3663" +checksum = "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0" [[package]] name = "camino" @@ -1723,9 +1830,9 @@ dependencies = [ [[package]] name = "candid" -version = "0.10.10" +version = "0.10.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c30ee7f886f296b6422c0ff017e89dd4f831521dfdcc76f3f71aae1ce817222" +checksum = "a253bab4a9be502c82332b60cbeee6202ad0692834efeec95fae9f29db33d692" dependencies = [ "anyhow", "binread", @@ -1805,12 +1912,13 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.83" +version = "1.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "40545c26d092346d8a8dab71ee48e7685a7a9cba76e634790c215b41a4a7b4cf" dependencies = [ "jobserver", "libc", + "shlex", ] [[package]] @@ -1970,7 +2078,7 @@ checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" dependencies = [ "glob", "libc", - "libloading", + "libloading 0.7.4", ] [[package]] @@ -2278,6 +2386,16 @@ dependencies = [ "libc", ] +[[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" @@ -2330,18 +2448,18 @@ dependencies = [ [[package]] name = "cranelift-bforest" -version = "0.114.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ba4f80548f22dc9c43911907b5e322c5555544ee85f785115701e6a28c9abe1" +checksum = "ac89549be94911dd0e839b4a7db99e9ed29c17517e1c026f61066884c168aa3c" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-bitset" -version = "0.114.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "005884e3649c3e5ff2dc79e8a94b138f11569cc08a91244a292714d2a86e9156" +checksum = "b9bd49369f76c77e34e641af85d0956869237832c118964d08bf5f51f210875a" dependencies = [ "serde", "serde_derive", @@ -2349,9 +2467,9 @@ dependencies = [ [[package]] name = "cranelift-codegen" -version = "0.114.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4036255ec33ce9a37495dfbcfc4e1118fd34e693eff9a1e106336b7cd16a9b" +checksum = "fd96ce9cf8efebd7f5ab8ced5a0ce44250280bbae9f593d74a6d7effc3582a35" dependencies = [ "bumpalo", "cranelift-bforest", @@ -2373,33 +2491,33 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.114.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7ca74f4b68319da11d39e894437cb6e20ec7c2e11fbbda823c3bf207beedff7" +checksum = "5a68e358827afe4bfb6239fcbf6fbd5ac56206ece8a99c8f5f9bbd518773281a" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.114.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897e54f433a0269c4187871aa06d452214d5515d228d5bdc22219585e9eef895" +checksum = "e184c9767afbe73d50c55ec29abcf4c32f9baf0d9d22b86d58c4d55e06dee181" [[package]] name = "cranelift-control" -version = "0.114.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29cb4018f5bf59fb53f515fa9d80e6f8c5ce19f198dc538984ebd23ecf8965ec" +checksum = "5cc7664f2a66f053e33f149e952bb5971d138e3af637f5097727ed6dc0ed95dd" dependencies = [ "arbitrary", ] [[package]] name = "cranelift-entity" -version = "0.114.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "305399fd781a2953ac78c1396f02ff53144f39c33eb7fc7789cf4e8936d13a96" +checksum = "118597e3a9cf86c3556fa579a7a23b955fa18231651a52a77a2475d305a9cf84" dependencies = [ "cranelift-bitset", "serde", @@ -2408,9 +2526,9 @@ dependencies = [ [[package]] name = "cranelift-frontend" -version = "0.114.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9230b460a128d53653456137751d27baf567947a3ab8c0c4d6e31fd08036d81e" +checksum = "7638ea1efb069a0aa18d8ee67401b6b0d19f6bfe5de5e9ede348bfc80bb0d8c7" dependencies = [ "cranelift-codegen", "log", @@ -2420,15 +2538,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.114.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b961e24ae3ec9813a24a15ae64bbd2a42e4de4d79a7f3225a412e3b94e78d1c8" +checksum = "15c53e1152a0b01c4ed2b1e0535602b8e86458777dd9d18b28732b16325c7dc0" [[package]] name = "cranelift-native" -version = "0.114.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d5bd76df6c9151188dfa428c863b33da5b34561b67f43c0cf3f24a794f9fa1f" +checksum = "7b7d8f895444fa52dd7bdd0bed11bf007a7fb43af65a6deac8fcc4094c6372f7" dependencies = [ "cranelift-codegen", "libc", @@ -2569,6 +2687,18 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + [[package]] name = "crypto-bigint" version = "0.5.2" @@ -2636,6 +2766,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + [[package]] name = "ctrlc" version = "3.4.5" @@ -2657,7 +2796,7 @@ dependencies = [ "curve25519-dalek-derive", "digest 0.10.7", "fiat-crypto", - "group", + "group 0.13.0", "rand_core 0.6.4", "rustc_version", "subtle", @@ -2709,22 +2848,12 @@ dependencies = [ [[package]] name = "darling" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" -dependencies = [ - "darling_core 0.14.4", - "darling_macro 0.14.4", -] - -[[package]] -name = "darling" -version = "0.20.3" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ - "darling_core 0.20.3", - "darling_macro 0.20.3", + "darling_core 0.20.10", + "darling_macro 0.20.10", ] [[package]] @@ -2743,29 +2872,15 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim 0.10.0", - "syn 1.0.109", -] - -[[package]] -name = "darling_core" -version = "0.20.3" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "strsim 0.10.0", + "strsim 0.11.1", "syn 2.0.87", ] @@ -2782,22 +2897,11 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.14.4" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ - "darling_core 0.14.4", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "darling_macro" -version = "0.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" -dependencies = [ - "darling_core 0.20.3", + "darling_core 0.20.10", "quote", "syn 2.0.87", ] @@ -2841,6 +2945,31 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" +[[package]] +name = "dbus" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" +dependencies = [ + "libc", + "libdbus-sys", + "winapi 0.3.9", +] + +[[package]] +name = "dbus-secret-service" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42a16374481d92aed73ae45b1f120207d8e71d24fb89f357fadbd8f946fd84b" +dependencies = [ + "dbus", + "futures-util", + "num", + "once_cell", + "openssl", + "rand 0.8.5", +] + [[package]] name = "debugid" version = "0.8.0" @@ -2850,6 +2979,17 @@ dependencies = [ "uuid", ] +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", + "pem-rfc7468 0.6.0", + "zeroize", +] + [[package]] name = "der" version = "0.7.7" @@ -2859,7 +2999,7 @@ dependencies = [ "const-oid", "der_derive", "flagset", - "pem-rfc7468", + "pem-rfc7468 0.7.0", "zeroize", ] @@ -2933,6 +3073,64 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "dfx-core" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4be4fc6929580b0ef8ac3678c03f31b59ffb1aa0d90ab754842d40cfc5b4552" +dependencies = [ + "aes-gcm", + "argon2", + "backoff", + "bip32 0.4.0", + "byte-unit", + "bytes", + "candid", + "clap 4.5.20", + "dialoguer", + "directories-next", + "dunce", + "flate2", + "handlebars", + "hex", + "humantime-serde", + "ic-agent", + "ic-identity-hsm", + "ic-utils", + "itertools 0.10.5", + "k256 0.11.6", + "keyring", + "lazy_static", + "reqwest 0.12.9", + "ring 0.16.20", + "schemars", + "sec1 0.3.0", + "semver", + "serde", + "serde_json", + "sha2 0.10.8", + "slog", + "tar", + "tempfile", + "thiserror 1.0.68", + "time", + "tiny-bip39", + "url", +] + +[[package]] +name = "dialoguer" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de" +dependencies = [ + "console 0.15.7", + "shell-words", + "tempfile", + "thiserror 1.0.68", + "zeroize", +] + [[package]] name = "diff" version = "0.1.13" @@ -2996,14 +3194,14 @@ dependencies = [ "bech32 0.9.1", "bincode", "bindgen 0.65.1", - "bip32", + "bip32 0.5.1", "bit-vec", "bitcoin 0.28.2", - "bitcoin 0.32.2", + "bitcoin 0.32.5", "bitcoincore-rpc", "bitcoind", "bitflags 1.3.2", - "bs58", + "bs58 0.5.0", "build-info", "build-info-build", "by_address", @@ -3038,6 +3236,7 @@ dependencies = [ "curve25519-dalek", "cvt", "dashmap 5.5.0", + "dfx-core", "dyn-clone", "ed25519-dalek", "educe", @@ -3058,7 +3257,7 @@ dependencies = [ "futures-util", "get_if_addrs", "getrandom", - "group", + "group 0.13.0", "hashlink", "hex", "hex-literal", @@ -3080,10 +3279,12 @@ dependencies = [ "ic-canister-sig-creation", "ic-cbor", "ic-cdk 0.16.0", + "ic-cdk 0.18.0-alpha.1", + "ic-cdk-macros 0.18.0-alpha.1", "ic-cdk-macros 0.9.0", "ic-cdk-timers", "ic-certificate-verification", - "ic-certification", + "ic-certification 3.0.2", "ic-certified-map", "ic-http-certification", "ic-http-gateway", @@ -3092,7 +3293,7 @@ dependencies = [ "ic-sha3", "ic-stable-structures", "ic-test-state-machine-client", - "ic-transport-types", + "ic-transport-types 0.39.2", "ic-utils", "ic-verify-bls-signature 0.6.0", "ic-wasm", @@ -3115,7 +3316,7 @@ dependencies = [ "itertools 0.12.0", "json-patch", "json5", - "k256", + "k256 0.13.4", "k8s-openapi", "kube", "lazy_static", @@ -3144,7 +3345,7 @@ dependencies = [ "nix 0.24.3", "num-bigint 0.4.6", "num-bigint-dig", - "num-rational", + "num-rational 0.2.4", "num-traits", "num_cpus", "once_cell", @@ -3153,7 +3354,7 @@ dependencies = [ "opentelemetry 0.27.0", "opentelemetry-otlp", "opentelemetry-prometheus 0.13.0", - "opentelemetry_sdk 0.27.0", + "opentelemetry_sdk 0.27.1", "p256", "pairing", "parking_lot 0.12.1", @@ -3162,7 +3363,7 @@ dependencies = [ "pem 1.1.1", "pin-project-lite", "ping", - "pkcs8", + "pkcs8 0.10.2", "pkg-config", "pprof", "predicates", @@ -3228,7 +3429,7 @@ dependencies = [ "sha2 0.10.8", "sha3", "signal-hook", - "signature", + "signature 2.2.0", "simple_asn1", "simple_moving_average", "slog", @@ -3245,6 +3446,7 @@ dependencies = [ "stubborn-io", "subtle", "syn 1.0.109", + "syscalls", "tar", "tarpc", "tempfile", @@ -3263,6 +3465,7 @@ dependencies = [ "tokio-rustls 0.26.0", "tokio-serde", "tokio-socks", + "tokio-stream", "tokio-test", "tokio-util", "toml", @@ -3307,6 +3510,16 @@ dependencies = [ "zstd 0.13.2", ] +[[package]] +name = "directories-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" +dependencies = [ + "cfg-if 1.0.0", + "dirs-sys-next", +] + [[package]] name = "dirs" version = "2.0.2" @@ -3397,6 +3610,12 @@ dependencies = [ "dtoa", ] +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + [[package]] name = "duration-string" version = "0.3.0" @@ -3412,18 +3631,30 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23d2f3407d9a573d666de4b5bdf10569d73ca9478087346697dcbae6244bfbcd" +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der 0.6.1", + "elliptic-curve 0.12.3", + "rfc6979 0.3.1", + "signature 1.6.4", +] + [[package]] name = "ecdsa" version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ - "der", + "der 0.7.7", "digest 0.10.7", - "elliptic-curve", - "rfc6979", - "signature", - "spki", + "elliptic-curve 0.13.8", + "rfc6979 0.4.0", + "signature 2.2.0", + "spki 0.7.3", ] [[package]] @@ -3432,8 +3663,8 @@ version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ - "pkcs8", - "signature", + "pkcs8 0.10.2", + "signature 2.2.0", ] [[package]] @@ -3463,7 +3694,7 @@ dependencies = [ "rand_core 0.6.4", "serde", "sha2 0.10.8", - "signature", + "signature 2.2.0", "subtle", "zeroize", ] @@ -3492,22 +3723,43 @@ version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct 0.1.1", + "crypto-bigint 0.4.9", + "der 0.6.1", + "digest 0.10.7", + "ff 0.12.1", + "generic-array", + "group 0.12.1", + "pem-rfc7468 0.6.0", + "pkcs8 0.9.0", + "rand_core 0.6.4", + "sec1 0.3.0", + "subtle", + "zeroize", +] + [[package]] name = "elliptic-curve" version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ - "base16ct", - "crypto-bigint", + "base16ct 0.2.0", + "crypto-bigint 0.5.2", "digest 0.10.7", "ff 0.13.0", "generic-array", - "group", - "pem-rfc7468", - "pkcs8", + "group 0.13.0", + "pem-rfc7468 0.7.0", + "pkcs8 0.10.2", "rand_core 0.6.4", - "sec1", + "sec1 0.7.3", "subtle", "zeroize", ] @@ -3754,11 +4006,11 @@ dependencies = [ "arrayvec 0.7.4", "bytes", "chrono", - "elliptic-curve", + "elliptic-curve 0.13.8", "ethabi", "generic-array", "hex", - "k256", + "k256 0.13.4", "num_enum", "open-fastrlp", "rand 0.8.5", @@ -3781,6 +4033,12 @@ dependencies = [ "serde", ] +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + [[package]] name = "event-listener" version = "4.0.3" @@ -3997,6 +4255,27 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -4251,6 +4530,16 @@ dependencies = [ "wasi", ] +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + [[package]] name = "gimli" version = "0.26.2" @@ -4303,6 +4592,17 @@ dependencies = [ "smallvec", ] +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff 0.12.1", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "group" version = "0.13.0" @@ -4358,6 +4658,20 @@ version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" +[[package]] +name = "handlebars" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faa67bab9ff362228eb3d00bd024a4965d8231bbb7921167f0cfa66c6626b225" +dependencies = [ + "log", + "pest", + "pest_derive", + "serde", + "serde_json", + "thiserror 1.0.68", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -4378,6 +4692,16 @@ dependencies = [ "serde", ] +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "foldhash", + "serde", +] + [[package]] name = "hashlink" version = "0.8.3" @@ -4675,17 +4999,6 @@ dependencies = [ "http 1.2.0", ] -[[package]] -name = "http-body-to-bytes" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17a08236c6f51c2ee95d840f45acf8fa9e339390e00b4ef640857b2f2a534d70" -dependencies = [ - "bytes", - "http-body 1.0.1", - "http-body-util", -] - [[package]] name = "http-body-util" version = "0.1.2" @@ -4909,48 +5222,50 @@ dependencies = [ [[package]] name = "ic-agent" -version = "0.37.1" +version = "0.39.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fd3fdf5e5c4f4a9fe5ca612f0febd22dcb161d2f2b75b0142326732be5e4978" +checksum = "1ba408987ca48fc3eee6a613e760d076a9046cccbbb5ba29efbada339ab28ed9" dependencies = [ + "arc-swap", + "async-channel 1.9.0", "async-lock", + "async-trait", + "async-watch", "backoff", "cached 0.52.0", "candid", + "der 0.7.7", + "ecdsa 0.16.9", "ed25519-consensus", + "elliptic-curve 0.13.8", "futures-util", "hex", "http 1.2.0", "http-body 1.0.1", - "http-body-to-bytes", - "http-body-util", - "hyper 1.5.1", - "hyper-rustls 0.27.3", - "hyper-util", - "ic-certification", - "ic-transport-types", + "ic-certification 3.0.2", + "ic-transport-types 0.39.2", "ic-verify-bls-signature 0.5.0", - "k256", + "k256 0.13.4", "leb128", "p256", "pem 3.0.3", - "pkcs8", + "pkcs8 0.10.2", "rand 0.8.5", "rangemap", "reqwest 0.12.9", "ring 0.17.7", - "rustls-webpki 0.102.8", - "sec1", + "sec1 0.7.3", "serde", "serde_bytes", "serde_cbor", "serde_repr", "sha2 0.10.8", "simple_asn1", - "thiserror 1.0.68", + "stop-token", + "thiserror 2.0.3", "time", "tokio", - "tower 0.4.13", + "tower-service", "url", ] @@ -5041,31 +5356,30 @@ dependencies = [ [[package]] name = "ic-canister-sig-creation" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d1fc58d747480967a25810d8a90d460e7e9ea4c669ab0286541a148736513f9" +version = "1.1.0" +source = "git+https://github.com/dfinity/ic-canister-sig-creation?rev=7f9e931954637526295269155881207f6c832d6d#7f9e931954637526295269155881207f6c832d6d" dependencies = [ "candid", "hex", - "ic-cdk 0.14.1", - "ic-certification", + "ic-cdk 0.17.0", + "ic-certification 3.0.2", "ic-representation-independent-hash", "lazy_static", "serde", "serde_bytes", "serde_cbor", "sha2 0.10.8", - "thiserror 1.0.68", + "thiserror 2.0.3", ] [[package]] name = "ic-cbor" -version = "2.6.0" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b0e48b4166c891e79d624f3a184b4a7c145d307576872d9a46dedb8c73ea8f" +checksum = "5500d6e85bc2ca8ea8aaed16cb84811882589244831a2fd8eefe02e90b3006c6" dependencies = [ "candid", - "ic-certification", + "ic-certification 3.0.2", "leb128", "nom", "thiserror 1.0.68", @@ -5086,12 +5400,12 @@ dependencies = [ [[package]] name = "ic-cdk" -version = "0.14.1" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cff1a3c3db565e3384c9c9d6d676b0a3f89a0886f4f787294d9c946d844369f" +checksum = "dd8ecacd682fa05a985253592963306cb9799622d7b1cce4b1edb89c6ec85be1" dependencies = [ "candid", - "ic-cdk-macros 0.14.0", + "ic-cdk-macros 0.16.0", "ic0 0.23.0", "serde", "serde_bytes", @@ -5099,12 +5413,12 @@ dependencies = [ [[package]] name = "ic-cdk" -version = "0.16.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8ecacd682fa05a985253592963306cb9799622d7b1cce4b1edb89c6ec85be1" +checksum = "b2abdf9341da9f9f6b451a40609cb69645a05a8e9eb7784c16209f16f2c0f76f" dependencies = [ "candid", - "ic-cdk-macros 0.16.0", + "ic-cdk-macros 0.17.0", "ic0 0.23.0", "serde", "serde_bytes", @@ -5112,15 +5426,15 @@ dependencies = [ [[package]] name = "ic-cdk" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2abdf9341da9f9f6b451a40609cb69645a05a8e9eb7784c16209f16f2c0f76f" +version = "0.18.0-alpha.1" +source = "git+https://github.com/dfinity/cdk-rs.git?rev=4e287ce51636b0e70768c193da38d2fc5324ea15#4e287ce51636b0e70768c193da38d2fc5324ea15" dependencies = [ "candid", - "ic-cdk-macros 0.17.0", - "ic0 0.23.0", + "ic-cdk-macros 0.18.0-alpha.1", + "ic0 0.24.0-alpha.1", "serde", "serde_bytes", + "thiserror 2.0.3", ] [[package]] @@ -5153,9 +5467,9 @@ dependencies = [ [[package]] name = "ic-cdk-macros" -version = "0.14.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01dc6bc425ec048d6ac4137c7c0f2cfbd6f8b0be8efc568feae2b265f566117c" +checksum = "0d4d857135deef20cc7ea8f3869a30cd9cfeb1392b3a81043790b2cd82adc3e0" dependencies = [ "candid", "proc-macro2", @@ -5167,9 +5481,9 @@ dependencies = [ [[package]] name = "ic-cdk-macros" -version = "0.16.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d4d857135deef20cc7ea8f3869a30cd9cfeb1392b3a81043790b2cd82adc3e0" +checksum = "b8df41980e95dead28735ab0f748c75477b0c5eab37a09a5641c78ec406a1db0" dependencies = [ "candid", "proc-macro2", @@ -5181,9 +5495,8 @@ dependencies = [ [[package]] name = "ic-cdk-macros" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8df41980e95dead28735ab0f748c75477b0c5eab37a09a5641c78ec406a1db0" +version = "0.18.0-alpha.1" +source = "git+https://github.com/dfinity/cdk-rs.git?rev=4e287ce51636b0e70768c193da38d2fc5324ea15#4e287ce51636b0e70768c193da38d2fc5324ea15" dependencies = [ "candid", "proc-macro2", @@ -5209,14 +5522,14 @@ dependencies = [ [[package]] name = "ic-certificate-verification" -version = "2.6.0" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "586e09b06a93d930f6a33f5f909bb11d2e4a06be3635dd5da1eb0e6554b7dae4" +checksum = "2daec653eb7895b5549cdf58d871985711c03cf5e389f7800a970f4f42dc0897" dependencies = [ - "cached 0.47.0", + "cached 0.54.0", "candid", "ic-cbor", - "ic-certification", + "ic-certification 3.0.2", "lazy_static", "leb128", "miracl_core_bls12381", @@ -5238,6 +5551,18 @@ dependencies = [ "sha2 0.10.8", ] +[[package]] +name = "ic-certification" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eae40f26fcac9c141cad54d9aa5f423efffde78ac371057c53d275ebbcad443" +dependencies = [ + "hex", + "serde", + "serde_bytes", + "sha2 0.10.8", +] + [[package]] name = "ic-certified-map" version = "0.3.4" @@ -5251,23 +5576,26 @@ dependencies = [ [[package]] name = "ic-http-certification" -version = "2.6.0" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff0b97e949845039149dc5e7ea6a7c12ee4333bb402e37bc507904643c7b3e41" +checksum = "479941fca8e68c2267cddf686d34ed6fb491168667ff259c08a3d65d28bd26d2" dependencies = [ + "base64 0.22.1", "candid", - "http 0.2.12", - "ic-certification", + "http 1.2.0", + "ic-certification 3.0.2", "ic-representation-independent-hash", "serde", + "serde_cbor", "thiserror 1.0.68", "urlencoding", ] [[package]] name = "ic-http-gateway" -version = "0.0.0" -source = "git+https://github.com/dfinity/http-gateway?tag=0.1.0-b0#3be26b5a2c71bf56e05b910951c1935a1ac550c4" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8b30a8ff19af1a7dc64b1dbe1a38f1b60c7eea566e2049f755ce3bace0e630" dependencies = [ "bytes", "candid", @@ -5282,6 +5610,20 @@ dependencies = [ "thiserror 1.0.68", ] +[[package]] +name = "ic-identity-hsm" +version = "0.39.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ebb94d7cb5be09bed47655008f0c2968a3a3253dcf680297f3e8475e4b317c4" +dependencies = [ + "hex", + "ic-agent", + "pkcs11", + "sha2 0.10.8", + "simple_asn1", + "thiserror 2.0.3", +] + [[package]] name = "ic-metrics-encoder" version = "1.1.1" @@ -5290,9 +5632,9 @@ checksum = "8b5c7628eac357aecda461130f8074468be5aa4d258a002032d82d817f79f1f8" [[package]] name = "ic-representation-independent-hash" -version = "2.6.0" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08ae59483e377cd9aad94ec339ed1d2583b0d5929cab989328dac2d853b2f570" +checksum = "3643f12824280580d31e47d380f1be23abee29944a1430c3ed22b164ac8e68db" dependencies = [ "leb128", "sha2 0.10.8", @@ -5300,18 +5642,18 @@ dependencies = [ [[package]] name = "ic-response-verification" -version = "2.6.0" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bef02ef84189d61a7d39889b7e9a3ae212d45c3df293513f7b2568027fd08a8" +checksum = "2b97514fada84797baf61a6a29f1c71695798c2628cb6013d97a5dd6ecc26df7" dependencies = [ - "base64 0.21.6", + "base64 0.22.1", "candid", "flate2", "hex", - "http 0.2.12", + "http 1.2.0", "ic-cbor", "ic-certificate-verification", - "ic-certification", + "ic-certification 3.0.2", "ic-http-certification", "ic-representation-independent-hash", "leb128", @@ -5360,7 +5702,7 @@ checksum = "875dc4704780383112e8e8b5063a1b98de114321d0c7d3e7f635dcf360a57fba" dependencies = [ "candid", "hex", - "ic-certification", + "ic-certification 2.6.0", "leb128", "serde", "serde_bytes", @@ -5369,11 +5711,29 @@ dependencies = [ "thiserror 1.0.68", ] +[[package]] +name = "ic-transport-types" +version = "0.39.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21e2418868dd5857d2a5bac3f1cb6de1aecf2316d380997ef842aec3d8a79d4e" +dependencies = [ + "candid", + "hex", + "ic-certification 3.0.2", + "leb128", + "serde", + "serde_bytes", + "serde_cbor", + "serde_repr", + "sha2 0.10.8", + "thiserror 2.0.3", +] + [[package]] name = "ic-utils" -version = "0.37.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fa832296800758c9c921dd1704985ded6b3e6fbc3aee409727eb1f00d69a595" +checksum = "bb1da4a68c45146018b8496c157ad94126b9c202ab4400c6c0a9030c1ef0f0ba" dependencies = [ "async-trait", "candid", @@ -5463,6 +5823,11 @@ version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de254dd67bbd58073e23dc1c8553ba12fa1dc610a19de94ad2bbcd0460c067f" +[[package]] +name = "ic0" +version = "0.24.0-alpha.1" +source = "git+https://github.com/dfinity/cdk-rs.git?rev=4e287ce51636b0e70768c193da38d2fc5324ea15#4e287ce51636b0e70768c193da38d2fc5324ea15" + [[package]] name = "ic_bls12_381" version = "0.10.0" @@ -5471,7 +5836,7 @@ checksum = "22c65787944f32af084dffd0c68c1e544237b76e215654ddea8cd9f527dd8b69" dependencies = [ "digest 0.10.7", "ff 0.13.0", - "group", + "group 0.13.0", "pairing", "rand_core 0.6.4", "subtle", @@ -5904,9 +6269,12 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.8.0" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" +dependencies = [ + "serde", +] [[package]] name = "ipnetwork" @@ -5999,9 +6367,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.26" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ "libc", ] @@ -6054,8 +6422,9 @@ dependencies = [ [[package]] name = "jsonrpc" -version = "0.12.1" -source = "git+https://github.com/apoelstra/rust-jsonrpc?rev=e42044d#e42044d8e0896317488dfbd65eb5563d76e937fe" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd8d6b3f301ba426b30feca834a2a18d48d5b54e5065496b5c1b05537bee3639" dependencies = [ "base64 0.13.1", "serde", @@ -6064,15 +6433,29 @@ dependencies = [ [[package]] name = "jsonrpc" -version = "0.13.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd8d6b3f301ba426b30feca834a2a18d48d5b54e5065496b5c1b05537bee3639" +checksum = "3662a38d341d77efecb73caf01420cfa5aa63c0253fd7bc05289ef9f6616e1bf" dependencies = [ "base64 0.13.1", + "minreq", "serde", "serde_json", ] +[[package]] +name = "k256" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" +dependencies = [ + "cfg-if 1.0.0", + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", + "sha2 0.10.8", + "sha3", +] + [[package]] name = "k256" version = "0.13.4" @@ -6080,11 +6463,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" dependencies = [ "cfg-if 1.0.0", - "ecdsa", - "elliptic-curve", + "ecdsa 0.16.9", + "elliptic-curve 0.13.8", "once_cell", "sha2 0.10.8", - "signature", + "signature 2.2.0", ] [[package]] @@ -6109,6 +6492,20 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "keyring" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd3d701d3de5b9c4b0d9d077f8c2c66f0388d75e96932ebbb7cdff8713d7f7c6" +dependencies = [ + "byteorder", + "dbus-secret-service", + "linux-keyutils", + "openssl", + "security-framework 3.0.1", + "windows-sys 0.59.0", +] + [[package]] name = "kube" version = "0.93.1" @@ -6333,6 +6730,16 @@ version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +[[package]] +name = "libdbus-sys" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72" +dependencies = [ + "cc", + "pkg-config", +] + [[package]] name = "libflate" version = "2.1.0" @@ -6368,6 +6775,16 @@ dependencies = [ "once_cell", ] +[[package]] +name = "libloading" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" +dependencies = [ + "cc", + "winapi 0.3.9", +] + [[package]] name = "libloading" version = "0.7.4" @@ -6464,6 +6881,16 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +[[package]] +name = "linux-keyutils" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "761e49ec5fd8a5a463f9b84e877c373d888935b71c6be78f3767fe2ae6bed18e" +dependencies = [ + "bitflags 2.6.0", + "libc", +] + [[package]] name = "linux-raw-sys" version = "0.4.13" @@ -6876,6 +7303,17 @@ dependencies = [ "adler", ] +[[package]] +name = "minreq" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36a8e50e917e18a37d500d27d40b7bc7d127e71c0c94fb2d83f43b4afd308390" +dependencies = [ + "log", + "serde", + "serde_json", +] + [[package]] name = "mio" version = "0.8.10" @@ -7158,6 +7596,20 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint 0.4.6", + "num-complex", + "num-integer", + "num-iter", + "num-rational 0.4.2", + "num-traits", +] + [[package]] name = "num-bigint" version = "0.2.6" @@ -7198,6 +7650,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -7246,6 +7707,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint 0.4.6", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -7313,12 +7785,12 @@ dependencies = [ [[package]] name = "object" -version = "0.36.1" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "crc32fast", - "hashbrown 0.14.5", + "hashbrown 0.15.2", "indexmap 2.2.6", "memchr", ] @@ -7388,20 +7860,56 @@ dependencies = [ "thiserror 1.0.68", ] +[[package]] +name = "openssl" +version = "0.10.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5e534d133a060a3c19daec1eb3e98ec6f4685978834f2dbadfe2ec215bab64e" +dependencies = [ + "bitflags 2.6.0", + "cfg-if 1.0.0", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "openssl-probe" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +[[package]] +name = "openssl-src" +version = "300.4.1+3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faa4eac4138c62414b5622d1b31c5c304f34b406b013c079c2bbc652fdd6678c" +dependencies = [ + "cc", +] + [[package]] name = "openssl-sys" -version = "0.9.102" +version = "0.9.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" dependencies = [ "cc", "libc", + "openssl-src", "pkg-config", "vcpkg", ] @@ -7467,7 +7975,7 @@ dependencies = [ "http 1.2.0", "opentelemetry 0.27.0", "opentelemetry-proto", - "opentelemetry_sdk 0.27.0", + "opentelemetry_sdk 0.27.1", "prost 0.13.3", "thiserror 1.0.68", "tokio", @@ -7508,7 +8016,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6e05acbfada5ec79023c85368af14abd0b307c015e9064d249b2a950ef459a6" dependencies = [ "opentelemetry 0.27.0", - "opentelemetry_sdk 0.27.0", + "opentelemetry_sdk 0.27.1", "prost 0.13.3", "tonic", ] @@ -7615,16 +8123,15 @@ dependencies = [ [[package]] name = "opentelemetry_sdk" -version = "0.27.0" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b742c1cae4693792cc564e58d75a2a0ba29421a34a85b50da92efa89ecb2bc" +checksum = "231e9d6ceef9b0b2546ddf52335785ce41252bc7474ee8ba05bfad277be13ab8" dependencies = [ "async-trait", "futures-channel", "futures-executor", "futures-util", "glob", - "once_cell", "opentelemetry 0.27.0", "percent-encoding", "rand 0.8.5", @@ -7680,8 +8187,8 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" dependencies = [ - "ecdsa", - "elliptic-curve", + "ecdsa 0.16.9", + "elliptic-curve 0.13.8", "primeorder", "sha2 0.10.8", ] @@ -7692,7 +8199,7 @@ version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81fec4625e73cf41ef4bb6846cafa6d44736525f442ba45e407c4a000a13996f" dependencies = [ - "group", + "group 0.13.0", ] [[package]] @@ -7781,12 +8288,32 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "487f2ccd1e17ce8c1bfab3a65c89525af41cfad4c8659021a1e9a2aacd73b89b" +[[package]] +name = "password-hash" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" +dependencies = [ + "base64ct", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "paste" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "pbkdf2" version = "0.12.2" @@ -7844,6 +8371,15 @@ dependencies = [ "serde", ] +[[package]] +name = "pem-rfc7468" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d159833a9105500e0398934e205e0773f0b27529557134ecfc51c27646adac" +dependencies = [ + "base64ct", +] + [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -8069,9 +8605,29 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" dependencies = [ - "der", - "pkcs8", - "spki", + "der 0.7.7", + "pkcs8 0.10.2", + "spki 0.7.3", +] + +[[package]] +name = "pkcs11" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3aca6d67e4c8613bfe455599d0233d00735f85df2001f6bfd9bb7ac0496b10af" +dependencies = [ + "libloading 0.5.2", + "num-bigint 0.2.6", +] + +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der 0.6.1", + "spki 0.6.0", ] [[package]] @@ -8080,8 +8636,8 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der", - "spki", + "der 0.7.7", + "spki 0.7.3", ] [[package]] @@ -8127,8 +8683,8 @@ dependencies = [ "base64 0.13.1", "candid", "hex", - "ic-certification", - "ic-transport-types", + "ic-certification 2.6.0", + "ic-transport-types 0.37.1", "reqwest 0.12.9", "schemars", "serde", @@ -8173,6 +8729,18 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + [[package]] name = "portable-atomic" version = "1.4.1" @@ -8324,7 +8892,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c2fcef82c0ec6eefcc179b978446c399b3cdf73c392c35604e399eee6df1ee3" dependencies = [ - "elliptic-curve", + "elliptic-curve 0.13.8", ] [[package]] @@ -8667,9 +9235,9 @@ dependencies = [ [[package]] name = "pulley-interpreter" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3b8d81cf799e20564931e9867ca32de545188c6ee4c2e0f6e41d32f0c7dc6fb" +checksum = "403a1a95f4c18a45c86c7bff13df00347afd0abcbf2e54af273c837339ffcf77" dependencies = [ "cranelift-bitset", "log", @@ -9078,14 +9646,15 @@ dependencies = [ [[package]] name = "regalloc2" -version = "0.10.2" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12908dbeb234370af84d0579b9f68258a0f67e201412dd9a2814e6f45b2fc0f0" +checksum = "145c1c267e14f20fb0f88aa76a1c5ffec42d592c1d28b3cd9148ae35916158d3" dependencies = [ - "hashbrown 0.14.5", + "allocator-api2", + "bumpalo", + "hashbrown 0.15.2", "log", "rustc-hash 2.0.0", - "slice-group-by", "smallvec", ] @@ -9269,6 +9838,17 @@ dependencies = [ "quick-error", ] +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint 0.4.9", + "hmac", + "zeroize", +] + [[package]] name = "rfc6979" version = "0.4.0" @@ -9413,11 +9993,11 @@ dependencies = [ "num-integer", "num-traits", "pkcs1", - "pkcs8", + "pkcs8 0.10.2", "rand_core 0.6.4", "sha2 0.10.8", - "signature", - "spki", + "signature 2.2.0", + "spki 0.7.3", "subtle", "zeroize", ] @@ -9634,7 +10214,7 @@ dependencies = [ "openssl-probe", "rustls-pemfile 1.0.3", "schannel", - "security-framework", + "security-framework 2.11.1", ] [[package]] @@ -9647,7 +10227,7 @@ dependencies = [ "rustls-pemfile 2.2.0", "rustls-pki-types", "schannel", - "security-framework", + "security-framework 2.11.1", ] [[package]] @@ -9660,7 +10240,7 @@ dependencies = [ "rustls-pemfile 2.2.0", "rustls-pki-types", "schannel", - "security-framework", + "security-framework 2.11.1", ] [[package]] @@ -9693,7 +10273,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4c7dc240fec5517e6c4eab3310438636cfe6391dfc345ba013109909a90d136" dependencies = [ - "core-foundation", + "core-foundation 0.9.4", "core-foundation-sys", "jni", "log", @@ -9702,7 +10282,7 @@ dependencies = [ "rustls-native-certs 0.7.0", "rustls-platform-verifier-android", "rustls-webpki 0.102.8", - "security-framework", + "security-framework 2.11.1", "security-framework-sys", "webpki-root-certs", "windows-sys 0.52.0", @@ -9877,16 +10457,30 @@ version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct 0.1.1", + "der 0.6.1", + "generic-array", + "pkcs8 0.9.0", + "subtle", + "zeroize", +] + [[package]] name = "sec1" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ - "base16ct", - "der", + "base16ct 0.2.0", + "der 0.7.7", "generic-array", - "pkcs8", + "pkcs8 0.10.2", "subtle", "zeroize", ] @@ -9916,12 +10510,14 @@ dependencies = [ [[package]] name = "secp256k1" -version = "0.29.0" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e0cc0f1cf93f4969faf3ea1c7d8a9faed25918d96affa959720823dfe86d4f3" +checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" dependencies = [ "bitcoin_hashes 0.14.0", + "rand 0.8.5", "secp256k1-sys 0.10.0", + "serde", ] [[package]] @@ -9968,13 +10564,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ "bitflags 2.6.0", - "core-foundation", + "core-foundation 0.9.4", "core-foundation-sys", "libc", "num-bigint 0.4.6", "security-framework-sys", ] +[[package]] +name = "security-framework" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1415a607e92bec364ea2cf9264646dcce0f91e6d65281bd6f2819cca3bf39c8" +dependencies = [ + "bitflags 2.6.0", + "core-foundation 0.10.0", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + [[package]] name = "security-framework-sys" version = "2.12.0" @@ -10015,9 +10624,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.214" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] @@ -10075,9 +10684,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.214" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", @@ -10142,9 +10751,9 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.14" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d89a8107374290037607734c0b73a85db7ed80cae314b3c5791f192a496e731" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", @@ -10230,7 +10839,7 @@ version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" dependencies = [ - "darling 0.20.3", + "darling 0.20.10", "proc-macro2", "quote", "syn 2.0.87", @@ -10337,6 +10946,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + [[package]] name = "shlex" version = "1.3.0" @@ -10373,6 +10988,16 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + [[package]] name = "signature" version = "2.2.0" @@ -10454,12 +11079,6 @@ dependencies = [ "autocfg 1.1.0", ] -[[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" @@ -10615,6 +11234,16 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "spki" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der 0.6.1", +] + [[package]] name = "spki" version = "0.7.3" @@ -10622,7 +11251,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", - "der", + "der 0.7.7", ] [[package]] @@ -10668,6 +11297,18 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "stop-token" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af91f480ee899ab2d9f8435bfdfc14d08a5754bd9d3fef1f1a1c23336aad6c8b" +dependencies = [ + "async-channel 1.9.0", + "cfg-if 1.0.0", + "futures-core", + "pin-project-lite", +] + [[package]] name = "str_stack" version = "0.1.0" @@ -10872,6 +11513,16 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "syscalls" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d0e35dc7d73976a53c7e6d7d177ef804a0c0ee774ec77bcc520c2216fd7cbe" +dependencies = [ + "serde", + "serde_repr", +] + [[package]] name = "system-configuration" version = "0.5.1" @@ -10879,7 +11530,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.4", "system-configuration-sys", ] @@ -11224,6 +11875,25 @@ dependencies = [ "time-core", ] +[[package]] +name = "tiny-bip39" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62cc94d358b5a1e84a5cb9109f559aa3c4d634d2b1b4de3d0fa4adc7c78e2861" +dependencies = [ + "anyhow", + "hmac", + "once_cell", + "pbkdf2 0.11.0", + "rand 0.8.5", + "rustc-hash 1.1.0", + "sha2 0.10.8", + "thiserror 1.0.68", + "unicode-normalization", + "wasm-bindgen", + "zeroize", +] + [[package]] name = "tiny-keccak" version = "2.0.2" @@ -11402,9 +12072,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" dependencies = [ "futures-core", "pin-project-lite", @@ -11760,7 +12430,7 @@ dependencies = [ "js-sys", "once_cell", "opentelemetry 0.27.0", - "opentelemetry_sdk 0.27.0", + "opentelemetry_sdk 0.27.1", "smallvec", "tracing", "tracing-core", @@ -12351,12 +13021,12 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.219.1" +version = "0.221.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29cbbd772edcb8e7d524a82ee8cef8dd046fc14033796a754c3ad246d019fa54" +checksum = "c17a3bd88f2155da63a1f2fcb8a56377a24f0b6dfed12733bb5f544e86f690c5" dependencies = [ "leb128", - "wasmparser 0.219.1", + "wasmparser 0.221.2", ] [[package]] @@ -12428,13 +13098,12 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.219.1" +version = "0.221.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c771866898879073c53b565a6c7b49953795159836714ac56a5befb581227c5" +checksum = "9845c470a2e10b61dd42c385839cdd6496363ed63b5c9e420b5488b77bd22083" dependencies = [ - "ahash 0.8.11", "bitflags 2.6.0", - "hashbrown 0.14.5", + "hashbrown 0.15.2", "indexmap 2.2.6", "semver", "serde", @@ -12453,20 +13122,20 @@ dependencies = [ [[package]] name = "wasmprinter" -version = "0.219.1" +version = "0.221.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "228cdc1f30c27816da225d239ce4231f28941147d34713dee8f1fff7cb330e54" +checksum = "a80742ff1b9e6d8c231ac7c7247782c6fc5bce503af760bca071811e5fc9ee56" dependencies = [ "anyhow", "termcolor", - "wasmparser 0.219.1", + "wasmparser 0.221.2", ] [[package]] name = "wasmtime" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b79302e3e084713249cc5622e8608e7410afdeeea8c8026d04f491d1fab0b4b" +checksum = "f639ecae347b9a2227e453a7b7671e84370a0b61f47a15e0390fe9b7725e47b3" dependencies = [ "anyhow", "bitflags 2.6.0", @@ -12480,7 +13149,7 @@ dependencies = [ "log", "mach2", "memfd", - "object 0.36.1", + "object 0.36.7", "once_cell", "paste", "postcard", @@ -12493,31 +13162,33 @@ dependencies = [ "smallvec", "sptr", "target-lexicon", - "wasmparser 0.219.1", + "wasmparser 0.221.2", "wasmtime-asm-macros", "wasmtime-component-macro", "wasmtime-cranelift", "wasmtime-environ", + "wasmtime-fiber", "wasmtime-jit-icache-coherence", "wasmtime-slab", "wasmtime-versioned-export-macros", + "wasmtime-winch", "windows-sys 0.59.0", ] [[package]] name = "wasmtime-asm-macros" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe53a24e7016a5222875d8ca3ad6024b464465985693c42098cd0bb710002c28" +checksum = "882a18800471cfc063c8b3ccf75723784acc3fd534009ac09421f2fac2fcdcec" dependencies = [ "cfg-if 1.0.0", ] [[package]] name = "wasmtime-component-macro" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e118acbd2bc09b32ad8606bc7cef793bf5019c1b107772e64dc6c76b5055d40b" +checksum = "eb5c0a77c9e1927c3d471f53cc13767c3d3438e5d5ffd394e3eb31c86445fd60" dependencies = [ "anyhow", "proc-macro2", @@ -12530,15 +13201,15 @@ dependencies = [ [[package]] name = "wasmtime-component-util" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a6db4f3ee18c699629eabb9c64e77efe5a93a5137f098db7cab295037ba41c2" +checksum = "43702ca98bf5162eca0573db691ed9ecd36d716f8c6688410fe26ec16b6f9bcb" [[package]] name = "wasmtime-cranelift" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b87e6c78f562b50aff1afd87ff32a57e241424c846c1c8f3c5fd352d2d62906" +checksum = "20070aa5b75080a8932ec328419faf841df2bc6ceb16b55b0df2b952098392a2" dependencies = [ "anyhow", "cfg-if 1.0.0", @@ -12550,20 +13221,20 @@ dependencies = [ "gimli 0.31.1", "itertools 0.12.0", "log", - "object 0.36.1", + "object 0.36.7", "smallvec", "target-lexicon", "thiserror 1.0.68", - "wasmparser 0.219.1", + "wasmparser 0.221.2", "wasmtime-environ", "wasmtime-versioned-export-macros", ] [[package]] name = "wasmtime-environ" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c25bfeaa16432d59a0706e2463d315ef4c9ebcfaf5605670b99d46373bdf9f27" +checksum = "2604ddb24879d4dc1dedcb7081d7a8e017259bce916fdae097a97db52cbaab80" dependencies = [ "anyhow", "cranelift-bitset", @@ -12571,22 +13242,37 @@ dependencies = [ "gimli 0.31.1", "indexmap 2.2.6", "log", - "object 0.36.1", + "object 0.36.7", "postcard", "serde", "serde_derive", "smallvec", "target-lexicon", - "wasm-encoder 0.219.1", - "wasmparser 0.219.1", - "wasmprinter 0.219.1", + "wasm-encoder 0.221.2", + "wasmparser 0.221.2", + "wasmprinter 0.221.2", +] + +[[package]] +name = "wasmtime-fiber" +version = "28.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98593412d2b167ebe2b59d4a17a184978a72f976b53b3a0ec05629451079ac1d" +dependencies = [ + "anyhow", + "cc", + "cfg-if 1.0.0", + "rustix", + "wasmtime-asm-macros", + "wasmtime-versioned-export-macros", + "windows-sys 0.59.0", ] [[package]] name = "wasmtime-jit-icache-coherence" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91b218a92866f74f35162f5d03a4e0f62cd0e1cc624285b1014275e5d4575fad" +checksum = "d40d7722b9e1fbeae135715710a8a2570b1e6cf72b74dd653962d89831c6c70d" dependencies = [ "anyhow", "cfg-if 1.0.0", @@ -12596,26 +13282,43 @@ dependencies = [ [[package]] name = "wasmtime-slab" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d5f8acf677ee6b3b8ba400dd9753ea4769e56a95c4b30b045ac6d2d54b2f8ea" +checksum = "8579c335220b4ece9aa490a0e8b46de78cd342b195ab21ff981d095e14b52383" [[package]] name = "wasmtime-versioned-export-macros" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df09be00c38f49172ca9936998938476e3f2df782673a39ae2ef9fb0838341b6" +checksum = "d7de0a56fb0a69b185968f2d7a9ba54750920a806470dff7ad8de91ac06d277e" dependencies = [ "proc-macro2", "quote", "syn 2.0.87", ] +[[package]] +name = "wasmtime-winch" +version = "28.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abd309943c443f5590d12f9aba9ba63c481091c955a0a14de0c2a9e0e3aaeca9" +dependencies = [ + "anyhow", + "cranelift-codegen", + "gimli 0.31.1", + "object 0.36.7", + "target-lexicon", + "wasmparser 0.221.2", + "wasmtime-cranelift", + "wasmtime-environ", + "winch-codegen", +] + [[package]] name = "wasmtime-wit-bindgen" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf3963c9c29df91564d8bd181eb00d0dbaeafa1b2a01e15952bb7391166b704e" +checksum = "969f83022dac3435d6469edb582ceed04cfe32aa44dc3ef16e5cb55574633df8" dependencies = [ "anyhow", "heck 0.5.0", @@ -12755,6 +13458,23 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "winch-codegen" +version = "28.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9110decc2983ed94de904804dcd979ba59cbabc78a94fec6b1d8468ec513d0f6" +dependencies = [ + "anyhow", + "cranelift-codegen", + "gimli 0.31.1", + "regalloc2", + "smallvec", + "target-lexicon", + "wasmparser 0.221.2", + "wasmtime-cranelift", + "wasmtime-environ", +] + [[package]] name = "windows-core" version = "0.52.0" @@ -13038,9 +13758,9 @@ dependencies = [ [[package]] name = "wit-parser" -version = "0.219.1" +version = "0.221.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a86f669283257e8e424b9a4fc3518e3ade0b95deb9fbc0f93a1876be3eda598" +checksum = "fbe1538eea6ea5ddbe5defd0dc82539ad7ba751e1631e9185d24a931f0a5adc8" dependencies = [ "anyhow", "id-arena", @@ -13051,7 +13771,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.219.1", + "wasmparser 0.221.2", ] [[package]] @@ -13105,10 +13825,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94" dependencies = [ "const-oid", - "der", + "der 0.7.7", "sha1", - "signature", - "spki", + "signature 2.2.0", + "spki 0.7.3", "tls_codec", ] @@ -13324,3 +14044,9 @@ dependencies = [ "cc", "pkg-config", ] + +[[patch.unused]] +name = "jsonrpc" +version = "0.12.1" +source = "git+https://github.com/apoelstra/rust-jsonrpc?rev=e42044d#e42044d8e0896317488dfbd65eb5563d76e937fe" + diff --git a/Cargo.lock b/Cargo.lock index 5d86f8deb54..87cc9b44b9f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,7 +39,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "bytes", "futures-core", "futures-sink", @@ -62,7 +62,7 @@ dependencies = [ "actix-utils", "ahash 0.8.11", "base64 0.22.1", - "bitflags 2.6.0", + "bitflags 2.8.0", "brotli 6.0.0", "bytes", "bytestring", @@ -96,7 +96,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" dependencies = [ "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -136,8 +136,8 @@ dependencies = [ "actix-utils", "futures-core", "futures-util", - "mio 1.0.2", - "socket2 0.5.7", + "mio 1.0.3", + "socket2 0.5.8", "tokio", "tracing", ] @@ -200,7 +200,7 @@ dependencies = [ "serde_json", "serde_urlencoded", "smallvec", - "socket2 0.5.7", + "socket2 0.5.8", "time", "url", ] @@ -214,7 +214,7 @@ dependencies = [ "actix-router", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -257,6 +257,31 @@ dependencies = [ "generic-array", ] +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if 1.0.0", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + [[package]] name = "ahash" version = "0.7.8" @@ -292,20 +317,20 @@ dependencies = [ [[package]] name = "aide" -version = "0.13.4" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0e3b97a21e41ec5c19bfd9b4fc1f7086be104f8b988681230247ffc91cc8ed" +checksum = "5678d2978845ddb4bd736a026f467dd652d831e9e6254b0e41b07f7ee7523309" dependencies = [ "axum", "bytes", "cfg-if 1.0.0", "http 1.2.0", - "indexmap 2.6.0", + "indexmap 2.7.1", "schemars", "serde", "serde_json", "serde_qs", - "thiserror 1.0.68", + "thiserror 1.0.69", "tower-layer", "tower-service", "tracing", @@ -337,9 +362,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.18" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "android-tzdata" @@ -375,7 +400,7 @@ dependencies = [ "prometheus", "rand 0.8.5", "rsa", - "thiserror 2.0.3", + "thiserror 2.0.11", "tokio", ] @@ -403,14 +428,14 @@ dependencies = [ "lazy_static", "prometheus", "serde", - "thiserror 2.0.3", + "thiserror 2.0.11", ] [[package]] name = "anstream" -version = "0.6.15" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", @@ -423,49 +448,50 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.4" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "once_cell", + "windows-sys 0.59.0", ] [[package]] name = "anyhow" -version = "1.0.93" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" [[package]] name = "arbitrary" -version = "1.3.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" dependencies = [ "derive_arbitrary", ] @@ -476,6 +502,17 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" +[[package]] +name = "argon2" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db4ce4441f99dbd377ca8a8f57b698c44d0d6e712d8329b5040da5a64aa1ce73" +dependencies = [ + "base64ct", + "blake2", + "password-hash", +] + [[package]] name = "arrayvec" version = "0.5.2" @@ -525,7 +562,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -555,7 +592,7 @@ dependencies = [ "nom", "num-traits", "rusticata-macros", - "thiserror 1.0.68", + "thiserror 1.0.69", "time", ] @@ -567,7 +604,7 @@ checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", "synstructure", ] @@ -579,7 +616,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -614,6 +651,17 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", +] + [[package]] name = "async-channel" version = "2.3.1" @@ -628,9 +676,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.17" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cb8f1d480b0ea3783ab015936d2a55c87e219676f0c0b7dec61494043f21857" +checksum = "df895a515f70646414f4b45c0b79082783b80552b373a68283012928df56f522" dependencies = [ "brotli 7.0.0", "flate2", @@ -657,9 +705,9 @@ dependencies = [ [[package]] name = "async-io" -version = "2.3.4" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" +checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" dependencies = [ "async-lock", "cfg-if 1.0.0", @@ -680,7 +728,7 @@ version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" dependencies = [ - "event-listener", + "event-listener 5.4.0", "event-listener-strategy", "pin-project-lite", ] @@ -713,7 +761,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8da2537846e16b96d2972ee52a3b355663872a1a687ce6d57a3b6f6b6a181c89" dependencies = [ - "thiserror 1.0.68", + "thiserror 1.0.69", "tokio", ] @@ -736,7 +784,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -747,13 +795,22 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.83" +version = "0.1.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", +] + +[[package]] +name = "async-watch" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a078faf4e27c0c6cc0efb20e5da59dcccc04968ebf2801d8e0b2195124cdcdb2" +dependencies = [ + "event-listener 2.5.3", ] [[package]] @@ -770,8 +827,8 @@ dependencies = [ "lazy_static", "log", "rustls-pki-types", - "thiserror 1.0.68", - "webpki-roots 0.26.6", + "thiserror 1.0.69", + "webpki-roots 0.26.7", ] [[package]] @@ -793,13 +850,13 @@ dependencies = [ [[package]] name = "auto_impl" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" +checksum = "e12882f59de5360c748c4cbf569a042d5fb0eb515f7bea9c1f470b47f6ffbd73" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -830,7 +887,7 @@ dependencies = [ "http 1.2.0", "http-body 1.0.1", "http-body-util", - "hyper 1.5.1", + "hyper 1.6.0", "hyper-util", "itoa", "matchit", @@ -843,9 +900,9 @@ dependencies = [ "serde_json", "serde_path_to_error", "serde_urlencoded", - "sync_wrapper 1.0.1", + "sync_wrapper 1.0.2", "tokio", - "tower 0.5.1", + "tower 0.5.2", "tower-layer", "tower-service", "tracing", @@ -866,7 +923,7 @@ dependencies = [ "mime", "pin-project-lite", "rustversion", - "sync_wrapper 1.0.1", + "sync_wrapper 1.0.2", "tower-layer", "tower-service", "tracing", @@ -874,25 +931,26 @@ dependencies = [ [[package]] name = "axum-extra" -version = "0.9.4" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73c3220b188aea709cf1b6c5f9b01c3bd936bb08bd2b5184a12b35ac8131b1f9" +checksum = "c794b30c904f0a1c2fb7740f7df7f7972dfaa14ef6f57cb6178dc63e5dca2f04" dependencies = [ "axum", "axum-core", "bytes", + "fastrand", "futures-util", "headers 0.4.0", "http 1.2.0", "http-body 1.0.1", "http-body-util", "mime", + "multer 3.1.0", "pin-project-lite", "serde", - "tower 0.5.1", + "tower 0.5.2", "tower-layer", "tower-service", - "tracing", ] [[package]] @@ -907,7 +965,7 @@ dependencies = [ "http 1.2.0", "http-body 1.0.1", "http-body-util", - "hyper 1.5.1", + "hyper 1.6.0", "hyper-util", "pin-project-lite", "rustls 0.21.12", @@ -965,6 +1023,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d27c3610c36aee21ce8ac510e6224498de4228ad772a171ed65643a24693a5a8" +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + [[package]] name = "base16ct" version = "0.2.0" @@ -1087,7 +1151,7 @@ version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "cexpr", "clang-sys", "itertools 0.12.1", @@ -1098,7 +1162,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -1124,17 +1188,35 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "bip32" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b30ed1d6f8437a487a266c8293aeb95b61a23261273e3e02912cdb8b68bf798b" +dependencies = [ + "bs58 0.4.0", + "hmac", + "k256 0.11.6", + "once_cell", + "pbkdf2 0.11.0", + "rand_core 0.6.4", + "ripemd", + "sha2 0.10.8", + "subtle", + "zeroize", +] + [[package]] name = "bip32" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa13fae8b6255872fd86f7faf4b41168661d7d78609f7bfe6771b85c6739a15b" dependencies = [ - "bs58", + "bs58 0.5.1", "hmac", - "k256", + "k256 0.13.4", "once_cell", - "pbkdf2", + "pbkdf2 0.12.2", "rand_core 0.6.4", "ripemd", "sha2 0.10.8", @@ -1148,7 +1230,16 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" dependencies = [ - "bit-vec", + "bit-vec 0.6.3", +] + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec 0.8.0", ] [[package]] @@ -1157,6 +1248,12 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + [[package]] name = "bitcoin" version = "0.28.2" @@ -1166,7 +1263,6 @@ dependencies = [ "bech32 0.8.1", "bitcoin_hashes 0.10.0", "secp256k1 0.22.2", - "serde", ] [[package]] @@ -1185,9 +1281,9 @@ dependencies = [ [[package]] name = "bitcoin" -version = "0.32.3" +version = "0.32.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0032b0e8ead7074cda7fc4f034409607e3f03a6f71d66ade8a307f79b4d99e73" +checksum = "ce6bc65742dea50536e35ad42492b234c27904a27f0abdcbce605015cb4ea026" dependencies = [ "base58ck", "bech32 0.11.0", @@ -1198,6 +1294,7 @@ dependencies = [ "hex-conservative", "hex_lit", "secp256k1 0.29.1", + "serde", ] [[package]] @@ -1205,12 +1302,15 @@ name = "bitcoin-internals" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30bdbe14aa07b06e6cfeffc529a1f099e5fbe249524f8125358604df99a4bed2" +dependencies = [ + "serde", +] [[package]] name = "bitcoin-io" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "340e09e8399c7bd8912f495af6aa58bea0c9214773417ffaa8f6460f93aaee56" +checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" [[package]] name = "bitcoin-private" @@ -1225,6 +1325,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5285c8bcaa25876d07f37e3d30c303f2609179716e11d688f51e8f1fe70063e2" dependencies = [ "bitcoin-internals", + "serde", ] [[package]] @@ -1232,9 +1333,6 @@ name = "bitcoin_hashes" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "006cc91e1a1d99819bc5b8214be3555c1f0611b169f527a1fdc54ed1f2b745b0" -dependencies = [ - "serde", -] [[package]] name = "bitcoin_hashes" @@ -1254,16 +1352,17 @@ checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" dependencies = [ "bitcoin-io", "hex-conservative", + "serde", ] [[package]] name = "bitcoincore-rpc" -version = "0.15.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0e67dbf7a9971e7f4276f6089e9e814ce0f624a03216b7d92d00351ae7fb3e" +checksum = "aedd23ae0fd321affb4bbbc36126c6f49a32818dc6b979395d24da8c9d4e80ee" dependencies = [ "bitcoincore-rpc-json", - "jsonrpc 0.12.1", + "jsonrpc 0.18.0", "log", "serde", "serde_json", @@ -1271,11 +1370,11 @@ dependencies = [ [[package]] name = "bitcoincore-rpc-json" -version = "0.15.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e2ae16202721ba8c3409045681fac790a5ddc791f05731a2df22c0c6bffc0f1" +checksum = "d8909583c5fab98508e80ef73e5592a651c954993dc6b7739963257d19f0e71a" dependencies = [ - "bitcoin 0.28.2", + "bitcoin 0.32.5", "serde", "serde_json", ] @@ -1301,9 +1400,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" [[package]] name = "bitvec" @@ -1317,6 +1416,15 @@ dependencies = [ "wyz", ] +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "block-buffer" version = "0.9.0" @@ -1341,7 +1449,7 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" dependencies = [ - "async-channel", + "async-channel 2.3.1", "async-task", "futures-io", "futures-lite", @@ -1350,9 +1458,9 @@ dependencies = [ [[package]] name = "borsh" -version = "1.5.1" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6362ed55def622cddc70a4746a68554d7b687713770de539e59a739b249f8ed" +checksum = "5430e3be710b68d984d1391c854eb431a9d548640711faa54eecb1df93db91cc" dependencies = [ "borsh-derive", "cfg_aliases", @@ -1360,16 +1468,15 @@ dependencies = [ [[package]] name = "borsh-derive" -version = "1.5.1" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3ef8005764f53cd4dca619f5bf64cafd4664dada50ece25e4d81de54c80cc0b" +checksum = "f8b668d39970baad5356d7c83a86fee3a539e6f93bf6764c97368243e17a0487" dependencies = [ "once_cell", "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.87", - "syn_derive", + "syn 2.0.96", ] [[package]] @@ -1396,14 +1503,23 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "4.0.1" +version = "4.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" +checksum = "74fa05ad7d803d413eb8380983b092cbbaf9a85f151b871360e7b00cd7060b37" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", ] +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" +dependencies = [ + "sha2 0.9.9", +] + [[package]] name = "bs58" version = "0.5.1" @@ -1416,12 +1532,12 @@ dependencies = [ [[package]] name = "bstr" -version = "1.10.0" +version = "1.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" +checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" dependencies = [ "memchr", - "regex-automata 0.4.8", + "regex-automata 0.4.9", "serde", ] @@ -1489,6 +1605,9 @@ name = "bumpalo" version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +dependencies = [ + "allocator-api2", +] [[package]] name = "by_address" @@ -1536,9 +1655,9 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.19.0" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" +checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" [[package]] name = "byteorder" @@ -1563,9 +1682,9 @@ checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc" [[package]] name = "bytestring" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d80203ea6b29df88012294f62733de21cfeab47f17b41af3a38bc30a03ee72" +checksum = "e465647ae23b2823b0753f50decb2d5a86d2bb2cac04788fafd1f80e45378e5f" dependencies = [ "bytes", ] @@ -1589,54 +1708,54 @@ checksum = "4964518bd3b4a8190e832886cdc0da9794f12e8e6c1613a9e90ff331c4c8724b" [[package]] name = "cached" -version = "0.47.0" +version = "0.49.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69b0116662497bc24e4b177c90eaf8870e39e2714c3fcfa296327a93f593fc21" +checksum = "8e8e463fceca5674287f32d252fb1d94083758b8709c160efae66d263e5f4eba" dependencies = [ - "ahash 0.8.11", - "cached_proc_macro", - "cached_proc_macro_types", "hashbrown 0.14.5", "instant", "once_cell", - "thiserror 1.0.68", + "thiserror 1.0.69", ] [[package]] name = "cached" -version = "0.49.3" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e8e463fceca5674287f32d252fb1d94083758b8709c160efae66d263e5f4eba" +checksum = "a8466736fe5dbcaf8b8ee24f9bbefe43c884dc3e9ff7178da70f55bffca1133c" dependencies = [ + "ahash 0.8.11", "hashbrown 0.14.5", "instant", "once_cell", - "thiserror 1.0.68", + "thiserror 1.0.69", ] [[package]] name = "cached" -version = "0.52.0" +version = "0.54.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8466736fe5dbcaf8b8ee24f9bbefe43c884dc3e9ff7178da70f55bffca1133c" +checksum = "9718806c4a2fe9e8a56fd736f97b340dd10ed1be8ed733ed50449f351dc33cae" dependencies = [ "ahash 0.8.11", + "cached_proc_macro", + "cached_proc_macro_types", "hashbrown 0.14.5", - "instant", "once_cell", - "thiserror 1.0.68", + "thiserror 1.0.69", + "web-time", ] [[package]] name = "cached_proc_macro" -version = "0.18.1" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c878c71c2821aa2058722038a59a67583a4240524687c6028571c9b395ded61f" +checksum = "2f42a145ed2d10dce2191e1dcf30cfccfea9026660e143662ba5eec4017d5daa" dependencies = [ - "darling 0.14.4", + "darling 0.20.10", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.96", ] [[package]] @@ -1672,12 +1791,12 @@ version = "0.1.0" dependencies = [ "anyhow", "bytes", - "clap 4.5.20", + "clap 4.5.27", "futures-util", "http 1.2.0", "http-body 1.0.1", "http-body-util", - "hyper 1.5.1", + "hyper 1.6.0", "once_cell", "pin-project-lite", "regex", @@ -1689,9 +1808,9 @@ dependencies = [ [[package]] name = "canbench-rs" -version = "0.1.7" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e85a8f1ee95044a770b3d5166a12f55814283cb3aed71b81439dc59960ab76c1" +checksum = "588701e2d05679b79603acca6a6f8b78fe0d21f5f7a9c06fe689f769ae797008" dependencies = [ "canbench-rs-macros", "candid", @@ -1701,9 +1820,9 @@ dependencies = [ [[package]] name = "canbench-rs-macros" -version = "0.1.7" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37aa9dbb190b03569ab14aadf669884a331712d54462c5a6c5b86c9867fe4e65" +checksum = "e05fe21a7dfc85c3be8e40edbbcb3fe23b4c070fac4741eff18129f1d0f11aa9" dependencies = [ "proc-macro2", "quote", @@ -1712,9 +1831,9 @@ dependencies = [ [[package]] name = "candid" -version = "0.10.10" +version = "0.10.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c30ee7f886f296b6422c0ff017e89dd4f831521dfdcc76f3f71aae1ce817222" +checksum = "a253bab4a9be502c82332b60cbeee6202ad0692834efeec95fae9f29db33d692" dependencies = [ "anyhow", "binread", @@ -1730,7 +1849,16 @@ dependencies = [ "serde", "serde_bytes", "stacker", - "thiserror 1.0.68", + "thiserror 1.0.69", +] + +[[package]] +name = "candid-utils" +version = "0.9.0" +dependencies = [ + "candid", + "candid_parser", + "pretty_assertions", ] [[package]] @@ -1742,7 +1870,7 @@ dependencies = [ "lazy_static", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -1761,7 +1889,7 @@ dependencies = [ "logos 0.13.0", "num-bigint 0.4.6", "pretty 0.12.3", - "thiserror 1.0.68", + "thiserror 1.0.69", ] [[package]] @@ -1822,9 +1950,9 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" dependencies = [ "serde", ] @@ -1850,9 +1978,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.1.31" +version = "1.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" +checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229" dependencies = [ "jobserver", "libc", @@ -1902,15 +2030,15 @@ dependencies = [ "candid", "certificate_orchestrator_interface", "chacha20poly1305", - "clap 4.5.20", + "clap 4.5.27", "cloudflare 0.12.0 (git+https://github.com/dfinity/cloudflare-rs.git?rev=a6538a036926bd756986c9c0a5de356daef48881)", "flate2", "futures", "http 1.2.0", - "ic-agent 0.37.1", + "ic-agent", "ic-http-certification", "ic-response-verification", - "ic-utils 0.37.0", + "ic-utils 0.39.3", "idna 1.0.3", "instant-acme", "leb128", @@ -1920,14 +2048,14 @@ dependencies = [ "pem 1.1.1", "prometheus", "rcgen", - "reqwest 0.12.9", + "reqwest 0.12.12", "serde", "serde_cbor", "serde_json", "sha2 0.10.8", - "thiserror 2.0.3", + "thiserror 2.0.11", "tokio", - "tower 0.5.1", + "tower 0.5.2", "tracing", "tracing-subscriber", "trust-dns-resolver", @@ -1957,7 +2085,7 @@ dependencies = [ "serde", "serde_cbor", "sha2 0.10.8", - "thiserror 2.0.3", + "thiserror 2.0.11", ] [[package]] @@ -1969,7 +2097,7 @@ dependencies = [ "ic-stable-structures", "serde", "serde_bytes", - "thiserror 2.0.3", + "thiserror 2.0.11", ] [[package]] @@ -2031,9 +2159,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" dependencies = [ "android-tzdata", "iana-time-zone", @@ -2090,7 +2218,7 @@ checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", - "libloading", + "libloading 0.8.6", ] [[package]] @@ -2112,23 +2240,23 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.20" +version = "4.5.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" +checksum = "769b0145982b4b48713e01ec42d61614425f27b7058bda7180a3a41f30104796" dependencies = [ "clap_builder", - "clap_derive 4.5.18", + "clap_derive 4.5.24", ] [[package]] name = "clap_builder" -version = "4.5.20" +version = "4.5.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" +checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7" dependencies = [ "anstream", "anstyle", - "clap_lex 0.7.2", + "clap_lex 0.7.4", "strsim 0.11.1", ] @@ -2147,14 +2275,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.18" +version = "4.5.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -2168,9 +2296,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "clocksource" @@ -2215,7 +2343,7 @@ dependencies = [ "serde_json", "serde_urlencoded", "serde_with 2.3.3", - "thiserror 1.0.68", + "thiserror 1.0.69", "url", "uuid", ] @@ -2233,7 +2361,7 @@ dependencies = [ "serde_json", "serde_urlencoded", "serde_with 2.3.3", - "thiserror 1.0.68", + "thiserror 1.0.69", "url", "uuid", ] @@ -2251,23 +2379,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" dependencies = [ "termcolor", - "unicode-width", + "unicode-width 0.1.14", ] [[package]] name = "colorchoice" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "colored" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" +checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" dependencies = [ "lazy_static", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -2282,9 +2410,9 @@ dependencies = [ [[package]] name = "comparable" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb513ee8037bf08c5270ecefa48da249f4c58e57a71ccfce0a5b0877d2a20eb2" +checksum = "8606f9aa5b5a2df738584b139c79413d0c1545ed0ffd16e76e0944d1de7388c0" dependencies = [ "comparable_derive", "comparable_helper", @@ -2294,9 +2422,9 @@ dependencies = [ [[package]] name = "comparable_derive" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a54b9c40054eb8999c5d1d36fdc90e4e5f7ff0d1d9621706f360b3cbc8beb828" +checksum = "41f36ea7383b9a2a9ae0a4e225d8a9c1c3aeadde78c59cdc35bad5c02b4dad01" dependencies = [ "convert_case 0.4.0", "proc-macro2", @@ -2306,9 +2434,9 @@ dependencies = [ [[package]] name = "comparable_helper" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5437e327e861081c91270becff184859f706e3e50f5301a9d4dc8eb50752c3" +checksum = "71c9b60259084f32c14d32476f3a299b4997e3c186e1473bd972ff8a8c83d1b4" dependencies = [ "convert_case 0.6.0", "proc-macro2", @@ -2330,7 +2458,7 @@ name = "config" version = "1.0.0" dependencies = [ "anyhow", - "clap 4.5.20", + "clap 4.5.27", "config_types", "ic-types", "macaddr", @@ -2357,7 +2485,7 @@ dependencies = [ "serde_json", "serde_with 1.14.0", "tempfile", - "thiserror 2.0.3", + "thiserror 2.0.11", "url", ] @@ -2387,7 +2515,7 @@ version = "0.9.0" dependencies = [ "anyhow", "canister-test", - "ic-agent 0.37.1", + "ic-agent", "ic-base-types", "ic-interfaces-registry", "ic-nns-common", @@ -2419,7 +2547,7 @@ dependencies = [ "candid", "canister-test", "canister_http", - "ic-agent 0.37.1", + "ic-agent", "ic-base-types", "ic-canister-client", "ic-management-canister-types", @@ -2432,7 +2560,7 @@ dependencies = [ "ic-types", "ic_consensus_system_test_utils", "ic_consensus_threshold_sig_system_test_utils", - "prost 0.13.3", + "prost 0.13.4", "serde", "serde_json", "slog", @@ -2447,7 +2575,7 @@ dependencies = [ "canister-test", "chrono", "futures", - "ic-agent 0.37.1", + "ic-agent", "ic-base-types", "ic-config", "ic-limits", @@ -2466,7 +2594,7 @@ dependencies = [ "rand 0.8.5", "rand_chacha 0.3.1", "registry-canister", - "reqwest 0.12.9", + "reqwest 0.12.12", "serde_json", "slog", "tokio", @@ -2480,7 +2608,7 @@ dependencies = [ "anyhow", "candid", "futures", - "ic-agent 0.37.1", + "ic-agent", "ic-canister-client", "ic-management-canister-types", "ic-nervous-system-common-test-keys", @@ -2496,34 +2624,51 @@ dependencies = [ "tokio", ] +[[package]] +name = "consensus-vetkd-system-tests" +version = "0.9.0" +dependencies = [ + "anyhow", + "canister-test", + "ic-management-canister-types", + "ic-nns-constants", + "ic-registry-subnet-type", + "ic-system-test-driver", + "ic-types", + "ic_consensus_system_test_utils", + "ic_consensus_threshold_sig_system_test_utils", + "slog", + "tokio", +] + [[package]] name = "console" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c0994e656bba7b922d8dd1245db90672ffb701e684e45be58f20719d69abc5a" dependencies = [ - "encode_unicode", + "encode_unicode 0.3.6", "lazy_static", "libc", "regex", "terminal_size", "termios", - "unicode-width", + "unicode-width 0.1.14", "winapi 0.3.9", "winapi-util", ] [[package]] name = "console" -version = "0.15.8" +version = "0.15.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +checksum = "ea3c6ecd8059b57859df5c69830340ed3c41d30e3da0c1cbed90a96ac853041b" dependencies = [ - "encode_unicode", - "lazy_static", + "encode_unicode 1.0.0", "libc", - "unicode-width", - "windows-sys 0.52.0", + "once_cell", + "unicode-width 0.2.0", + "windows-sys 0.59.0", ] [[package]] @@ -2538,14 +2683,14 @@ dependencies = [ [[package]] name = "const-hex" -version = "1.13.1" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0121754e84117e65f9d90648ee6aa4882a6e63110307ab73967a4c5e7e69e586" +checksum = "4b0485bab839b018a8f1723fc5391819fea5f8f0f32288ef8a735fd096b6160c" dependencies = [ "cfg-if 1.0.0", "cpufeatures", "hex", - "proptest", + "proptest 1.6.0", "serde", ] @@ -2555,6 +2700,26 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "const_format" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + [[package]] name = "convert_case" version = "0.4.0" @@ -2591,6 +2756,16 @@ dependencies = [ "libc", ] +[[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" @@ -2634,27 +2809,27 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] [[package]] name = "cranelift-bforest" -version = "0.114.0" +version = "0.115.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ba4f80548f22dc9c43911907b5e322c5555544ee85f785115701e6a28c9abe1" +checksum = "88c1d02b72b6c411c0a2e92b25ed791ad5d071184193c08a34aa0fdcdf000b72" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-bitset" -version = "0.114.0" +version = "0.115.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "005884e3649c3e5ff2dc79e8a94b138f11569cc08a91244a292714d2a86e9156" +checksum = "720b93bd86ebbb23ebfb2db1ed44d54b2ecbdbb2d034d485bc64aa605ee787ab" dependencies = [ "serde", "serde_derive", @@ -2662,9 +2837,9 @@ dependencies = [ [[package]] name = "cranelift-codegen" -version = "0.114.0" +version = "0.115.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4036255ec33ce9a37495dfbcfc4e1118fd34e693eff9a1e106336b7cd16a9b" +checksum = "aed3d2d9914d30b460eedd7fd507720203023997bef71452ce84873f9c93537c" dependencies = [ "bumpalo", "cranelift-bforest", @@ -2678,7 +2853,7 @@ dependencies = [ "hashbrown 0.14.5", "log", "regalloc2", - "rustc-hash 2.0.0", + "rustc-hash 2.1.0", "serde", "smallvec", "target-lexicon", @@ -2686,33 +2861,33 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.114.0" +version = "0.115.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7ca74f4b68319da11d39e894437cb6e20ec7c2e11fbbda823c3bf207beedff7" +checksum = "888c188d32263ec9e048873ff0b68c700933600d553f4412417916828be25f8e" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.114.0" +version = "0.115.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897e54f433a0269c4187871aa06d452214d5515d228d5bdc22219585e9eef895" +checksum = "4ddd5f4114d04ce7e073dd74e2ad16541fc61970726fcc8b2d5644a154ee4127" [[package]] name = "cranelift-control" -version = "0.114.0" +version = "0.115.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29cb4018f5bf59fb53f515fa9d80e6f8c5ce19f198dc538984ebd23ecf8965ec" +checksum = "92cc4c98d6a4256a1600d93ccd3536f3e77da9b4ca2c279de786ac22876e67d6" dependencies = [ "arbitrary", ] [[package]] name = "cranelift-entity" -version = "0.114.0" +version = "0.115.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "305399fd781a2953ac78c1396f02ff53144f39c33eb7fc7789cf4e8936d13a96" +checksum = "760af4b5e051b5f82097a27274b917e3751736369fa73660513488248d27f23d" dependencies = [ "cranelift-bitset", "serde", @@ -2721,9 +2896,9 @@ dependencies = [ [[package]] name = "cranelift-frontend" -version = "0.114.0" +version = "0.115.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9230b460a128d53653456137751d27baf567947a3ab8c0c4d6e31fd08036d81e" +checksum = "c0bf77ec0f470621655ec7539860b5c620d4f91326654ab21b075b83900f8831" dependencies = [ "cranelift-codegen", "log", @@ -2733,15 +2908,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.114.0" +version = "0.115.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b961e24ae3ec9813a24a15ae64bbd2a42e4de4d79a7f3225a412e3b94e78d1c8" +checksum = "4b665d0a6932c421620be184f9fc7f7adaf1b0bc2fa77bb7ac5177c49abf645b" [[package]] name = "cranelift-native" -version = "0.114.0" +version = "0.115.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d5bd76df6c9151188dfa428c863b33da5b34561b67f43c0cf3f24a794f9fa1f" +checksum = "bb2e75d1bd43dfec10924798f15e6474f1dbf63b0024506551aa19394dbe72ab" dependencies = [ "cranelift-codegen", "libc", @@ -2766,7 +2941,7 @@ dependencies = [ "anes", "cast", "ciborium", - "clap 4.5.20", + "clap 4.5.27", "criterion-plot", "futures", "is-terminal", @@ -2818,18 +2993,18 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.13" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -2846,18 +3021,18 @@ dependencies = [ [[package]] name = "crossbeam-queue" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crossterm" @@ -2865,7 +3040,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "crossterm_winapi", "libc", "mio 0.8.11", @@ -2886,9 +3061,21 @@ dependencies = [ [[package]] name = "crunchy" -version = "0.2.2" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" + +[[package]] +name = "crypto-bigint" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] [[package]] name = "crypto-bigint" @@ -2922,7 +3109,7 @@ dependencies = [ "cssparser-macros", "dtoa-short", "itoa", - "phf 0.11.2", + "phf 0.11.3", "smallvec", ] @@ -2933,14 +3120,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] name = "csv" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" +checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf" dependencies = [ "csv-core", "itoa", @@ -2957,6 +3144,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + [[package]] name = "ctrlc" version = "3.4.5" @@ -2978,7 +3174,7 @@ dependencies = [ "curve25519-dalek-derive", "digest 0.10.7", "fiat-crypto", - "group", + "group 0.13.0", "rand_core 0.6.4", "rustc_version", "subtle", @@ -2993,7 +3189,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -3040,6 +3236,7 @@ dependencies = [ "ic-ledger-core", "ic-management-canister-types", "ic-metrics-encoder", + "ic-nervous-system-clients", "ic-nervous-system-common", "ic-nervous-system-common-build-metadata", "ic-nervous-system-governance", @@ -3052,10 +3249,12 @@ dependencies = [ "icp-ledger", "icrc-ledger-types", "lazy_static", + "maplit", "on_wire", - "prost 0.13.3", + "prost 0.13.4", "rand 0.8.5", "serde", + "serde_bytes", "serde_cbor", "sha2 0.10.8", "yansi 0.5.1", @@ -3094,16 +3293,6 @@ dependencies = [ "darling_macro 0.13.4", ] -[[package]] -name = "darling" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" -dependencies = [ - "darling_core 0.14.4", - "darling_macro 0.14.4", -] - [[package]] name = "darling" version = "0.20.10" @@ -3128,20 +3317,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "darling_core" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim 0.10.0", - "syn 1.0.109", -] - [[package]] name = "darling_core" version = "0.20.10" @@ -3153,7 +3328,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -3167,17 +3342,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "darling_macro" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" -dependencies = [ - "darling_core 0.14.4", - "quote", - "syn 1.0.109", -] - [[package]] name = "darling_macro" version = "0.20.10" @@ -3186,7 +3350,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core 0.20.10", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -3224,9 +3388,34 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.6.0" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e60eed09d8c01d3cee5b7d30acb059b76614c918fa0f992e0dd6eeb10daad6f" + +[[package]] +name = "dbus" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" +dependencies = [ + "libc", + "libdbus-sys", + "winapi 0.3.9", +] + +[[package]] +name = "dbus-secret-service" +version = "4.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" +checksum = "b42a16374481d92aed73ae45b1f120207d8e71d24fb89f357fadbd8f946fd84b" +dependencies = [ + "dbus", + "futures-util", + "num", + "once_cell", + "openssl", + "rand 0.8.5", +] [[package]] name = "debugid" @@ -3244,6 +3433,17 @@ dependencies = [ "cargo_metadata", ] +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", + "pem-rfc7468 0.6.0", + "zeroize", +] + [[package]] name = "der" version = "0.7.9" @@ -3253,7 +3453,7 @@ dependencies = [ "const-oid", "der_derive", "flagset", - "pem-rfc7468", + "pem-rfc7468 0.7.0", "zeroize", ] @@ -3279,7 +3479,7 @@ checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -3300,18 +3500,18 @@ checksum = "2cdc8d50f426189eef89dac62fabfa0abb27d5cc008f25bf4156a0203325becc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] name = "derive_arbitrary" -version = "1.3.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" +checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -3324,7 +3524,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -3344,7 +3544,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -3352,11 +3552,11 @@ name = "deterministic_ips" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.5.20", + "clap 4.5.27", "config_types", "ic-crypto-sha2", "macaddr", - "thiserror 2.0.3", + "thiserror 2.0.11", ] [[package]] @@ -3364,7 +3564,7 @@ name = "dflate" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.5.20", + "clap 4.5.27", "libc", "tar", ] @@ -3438,7 +3638,65 @@ name = "dfn_protobuf" version = "0.9.0" dependencies = [ "on_wire", - "prost 0.13.3", + "prost 0.13.4", +] + +[[package]] +name = "dfx-core" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4be4fc6929580b0ef8ac3678c03f31b59ffb1aa0d90ab754842d40cfc5b4552" +dependencies = [ + "aes-gcm", + "argon2", + "backoff", + "bip32 0.4.0", + "byte-unit", + "bytes", + "candid", + "clap 4.5.27", + "dialoguer", + "directories-next", + "dunce", + "flate2", + "handlebars", + "hex", + "humantime-serde", + "ic-agent", + "ic-identity-hsm", + "ic-utils 0.39.3", + "itertools 0.10.5", + "k256 0.11.6", + "keyring", + "lazy_static", + "reqwest 0.12.12", + "ring 0.16.20", + "schemars", + "sec1 0.3.0", + "semver", + "serde", + "serde_json", + "sha2 0.10.8", + "slog", + "tar", + "tempfile", + "thiserror 1.0.69", + "time", + "tiny-bip39", + "url", +] + +[[package]] +name = "dialoguer" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de" +dependencies = [ + "console 0.15.10", + "shell-words", + "tempfile", + "thiserror 1.0.69", + "zeroize", ] [[package]] @@ -3474,12 +3732,22 @@ dependencies = [ "subtle", ] +[[package]] +name = "directories-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" +dependencies = [ + "cfg-if 1.0.0", + "dirs-sys-next", +] + [[package]] name = "diroid" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.5.20", + "clap 4.5.27", "walkdir", ] @@ -3512,7 +3780,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -3563,24 +3831,42 @@ dependencies = [ "dtoa", ] +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + [[package]] name = "dyn-clone" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der 0.6.1", + "elliptic-curve 0.12.3", + "rfc6979 0.3.1", + "signature 1.6.4", +] + [[package]] name = "ecdsa" version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ - "der", + "der 0.7.9", "digest 0.10.7", - "elliptic-curve", - "rfc6979", - "signature", - "spki", + "elliptic-curve 0.13.8", + "rfc6979 0.4.0", + "signature 2.2.0", + "spki 0.7.3", ] [[package]] @@ -3602,8 +3888,8 @@ version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ - "pkcs8", - "signature", + "pkcs8 0.10.2", + "signature 2.2.0", ] [[package]] @@ -3617,7 +3903,7 @@ dependencies = [ "rand_core 0.6.4", "serde", "sha2 0.9.9", - "thiserror 1.0.68", + "thiserror 1.0.69", "zeroize", ] @@ -3633,7 +3919,7 @@ dependencies = [ "rand_core 0.6.4", "serde", "sha2 0.10.8", - "signature", + "signature 2.2.0", "subtle", "zeroize", ] @@ -3662,22 +3948,43 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct 0.1.1", + "crypto-bigint 0.4.9", + "der 0.6.1", + "digest 0.10.7", + "ff 0.12.1", + "generic-array", + "group 0.12.1", + "pem-rfc7468 0.6.0", + "pkcs8 0.9.0", + "rand_core 0.6.4", + "sec1 0.3.0", + "subtle", + "zeroize", +] + [[package]] name = "elliptic-curve" version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ - "base16ct", - "crypto-bigint", + "base16ct 0.2.0", + "crypto-bigint 0.5.5", "digest 0.10.7", - "ff", + "ff 0.13.0", "generic-array", - "group", - "pem-rfc7468", - "pkcs8", + "group 0.13.0", + "pem-rfc7468 0.7.0", + "pkcs8 0.10.2", "rand_core 0.6.4", - "sec1", + "sec1 0.7.3", "subtle", "zeroize", ] @@ -3721,11 +4028,17 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + [[package]] name = "encoding_rs" -version = "0.8.34" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ "cfg-if 1.0.0", ] @@ -3751,7 +4064,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -3764,7 +4077,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -3780,18 +4093,18 @@ dependencies = [ [[package]] name = "env_filter" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" dependencies = [ "log", ] [[package]] name = "env_logger" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0" dependencies = [ "env_filter", "log", @@ -3814,7 +4127,7 @@ checksum = "3bf679796c0322556351f287a51b49e48f7c4986e727b5dd78c972d30e2e16cc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -3845,12 +4158,12 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3865,9 +4178,9 @@ dependencies = [ [[package]] name = "escargot" -version = "0.5.12" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c000f23e9d459aef148b7267e02b03b94a0aaacf4ec64c65612f67e02f525fb6" +checksum = "05a3ac187a16b5382fef8c69fd1bad123c67b7cf3932240a2d43dcdd32cded88" dependencies = [ "log", "once_cell", @@ -3888,7 +4201,7 @@ dependencies = [ "serde", "serde_json", "sha3", - "thiserror 1.0.68", + "thiserror 1.0.69", "uint", ] @@ -3933,10 +4246,10 @@ dependencies = [ "bytes", "chrono", "const-hex", - "elliptic-curve", + "elliptic-curve 0.13.8", "ethabi", "generic-array", - "k256", + "k256 0.13.4", "num_enum", "open-fastrlp", "rand 0.8.5", @@ -3945,7 +4258,7 @@ dependencies = [ "serde_json", "strum", "tempfile", - "thiserror 1.0.68", + "thiserror 1.0.69", "tiny-keccak", "unicode-xid", ] @@ -3961,9 +4274,15 @@ dependencies = [ [[package]] name = "event-listener" -version = "5.3.1" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "event-listener" +version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" dependencies = [ "concurrent-queue", "parking", @@ -3972,11 +4291,11 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" dependencies = [ - "event-listener", + "event-listener 5.4.0", "pin-project-lite", ] @@ -3991,7 +4310,7 @@ dependencies = [ "ic-cdk 0.16.0", "mockall", "serde", - "thiserror 2.0.3", + "thiserror 2.0.11", "tokio", ] @@ -4007,7 +4326,7 @@ dependencies = [ "num-bigint 0.4.6", "serde", "strum", - "thiserror 1.0.68", + "thiserror 1.0.69", "url", ] @@ -4059,7 +4378,7 @@ dependencies = [ "bitcoincore-rpc", "candid", "futures", - "ic-agent 0.37.1", + "ic-agent", "ic-base-types", "ic-cdk 0.16.0", "ic-config", @@ -4074,11 +4393,11 @@ dependencies = [ "ic-types", "ic-types-test-utils", "ic-universal-canister", - "ic-utils 0.37.0", + "ic-utils 0.39.3", "lazy_static", "rand 0.8.5", "rand_chacha 0.3.1", - "reqwest 0.12.9", + "reqwest 0.12.12", "serde_cbor", "slog", "tokio", @@ -4115,9 +4434,9 @@ checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" [[package]] name = "fastrand" -version = "2.1.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fe-derive" @@ -4131,6 +4450,16 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "ff" version = "0.13.0" @@ -4218,9 +4547,9 @@ checksum = "b3ea1ec5f8307826a5b71094dd91fc04d4ae75d5709b20ad351c7fb4815c86ec" [[package]] name = "flate2" -version = "1.0.34" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", "miniz_oxide", @@ -4228,9 +4557,9 @@ dependencies = [ [[package]] name = "float-cmp" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8" dependencies = [ "num-traits", ] @@ -4243,9 +4572,24 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foldhash" -version = "0.1.3" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + +[[package]] +name = "foreign-types" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" @@ -4263,7 +4607,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8835f84f38484cc86f110a805655697908257fb9a7af005234060891557198e9" dependencies = [ "nonempty", - "thiserror 1.0.68", + "thiserror 1.0.69", ] [[package]] @@ -4274,9 +4618,9 @@ checksum = "eb540cf7bc4fe6df9d8f7f0c974cfd0dce8ed4e9e8884e73433b503ee78b4e7d" [[package]] name = "fqdn" -version = "0.4.1" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eeee501d87b436020fcd3065cc981b5e4d22f2066735268b36b9d513d23e553" +checksum = "3e7cf4b6cb33615d9adab21d74fd820753c532ef7c15ff556e382abde22e4023" [[package]] name = "fragile" @@ -4362,9 +4706,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" -version = "2.3.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" dependencies = [ "fastrand", "futures-core", @@ -4381,7 +4725,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -4391,7 +4735,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f2f12607f92c69b12ed746fabf9ca4f5c482cba46679c1a75b874ed7c26adb" dependencies = [ "futures-io", - "rustls 0.23.19", + "rustls 0.23.21", "rustls-pki-types", ] @@ -4446,6 +4790,19 @@ version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" +[[package]] +name = "generator" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6bd114ceda131d3b1d665eba35788690ad37f5916457286b32ab6fd3c438dd" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "log", + "rustversion", + "windows", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -4485,7 +4842,7 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" dependencies = [ - "unicode-width", + "unicode-width 0.1.14", ] [[package]] @@ -4501,6 +4858,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + [[package]] name = "gimli" version = "0.26.2" @@ -4519,15 +4886,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" dependencies = [ "fallible-iterator 0.3.0", - "indexmap 2.6.0", + "indexmap 2.7.1", "stable_deref_trait", ] [[package]] name = "glob" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "governor" @@ -4549,13 +4916,24 @@ dependencies = [ "spinning_top", ] +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff 0.12.1", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "group" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ - "ff", + "ff 0.13.0", "rand_core 0.6.4", "subtle", ] @@ -4565,7 +4943,7 @@ name = "guestos_tool" version = "1.0.0" dependencies = [ "anyhow", - "clap 4.5.20", + "clap 4.5.27", "config", "indoc", "itertools 0.12.1", @@ -4586,7 +4964,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.6.0", + "indexmap 2.7.1", "slab", "tokio", "tokio-util", @@ -4595,9 +4973,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" dependencies = [ "atomic-waker", "bytes", @@ -4605,7 +4983,7 @@ dependencies = [ "futures-core", "futures-sink", "http 1.2.0", - "indexmap 2.6.0", + "indexmap 2.7.1", "slab", "tokio", "tokio-util", @@ -4628,6 +5006,20 @@ dependencies = [ "crunchy", ] +[[package]] +name = "handlebars" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faa67bab9ff362228eb3d00bd024a4965d8231bbb7921167f0cfa66c6626b225" +dependencies = [ + "log", + "pest", + "pest_derive", + "serde", + "serde_json", + "thiserror 1.0.69", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -4650,11 +5042,12 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" dependencies = [ "foldhash", + "serde", ] [[package]] @@ -4804,9 +5197,9 @@ checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" [[package]] name = "hickory-proto" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07698b8420e2f0d6447a436ba999ec85d8fbf2a398bbd737b82cac4a2e96e512" +checksum = "447afdcdb8afb9d0a852af6dc65d9b285ce720ed7a59e42a8bf2e931c67bc1b5" dependencies = [ "async-trait", "bytes", @@ -4818,14 +5211,14 @@ dependencies = [ "futures-util", "h2 0.3.26", "http 0.2.12", - "idna 0.4.0", + "idna 1.0.3", "ipnet", "once_cell", "rand 0.8.5", "ring 0.16.20", "rustls 0.21.12", "rustls-pemfile 1.0.4", - "thiserror 1.0.68", + "thiserror 1.0.69", "tinyvec", "tokio", "tokio-rustls 0.24.1", @@ -4836,9 +5229,9 @@ dependencies = [ [[package]] name = "hickory-resolver" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28757f23aa75c98f254cf0405e6d8c25b831b32921b050a66692427679b1f243" +checksum = "0a2e2aba9c389ce5267d31cf1e4dace82390ae276b0b364ea55630b1fa1b44b4" dependencies = [ "cfg-if 1.0.0", "futures-util", @@ -4851,7 +5244,7 @@ dependencies = [ "resolv-conf", "rustls 0.21.12", "smallvec", - "thiserror 1.0.68", + "thiserror 1.0.69", "tokio", "tokio-rustls 0.24.1", "tracing", @@ -4878,11 +5271,11 @@ dependencies = [ [[package]] name = "home" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -4901,7 +5294,7 @@ name = "hostos_tool" version = "1.0.0" dependencies = [ "anyhow", - "clap 4.5.20", + "clap 4.5.27", "config", "config_types", "deterministic_ips", @@ -4966,17 +5359,6 @@ dependencies = [ "http 1.2.0", ] -[[package]] -name = "http-body-to-bytes" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17a08236c6f51c2ee95d840f45acf8fa9e339390e00b4ef640857b2f2a534d70" -dependencies = [ - "bytes", - "http-body 1.0.1", - "http-body-util", -] - [[package]] name = "http-body-util" version = "0.1.2" @@ -5003,24 +5385,24 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.5" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" +checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a" [[package]] name = "httpbin-rs" version = "0.9.0" dependencies = [ "axum", - "clap 4.5.20", - "hyper 1.5.1", + "clap 4.5.27", + "hyper 1.6.0", "hyper-util", - "rustls 0.23.19", + "rustls 0.23.21", "rustls-pemfile 2.2.0", "serde_json", "tokio", - "tokio-rustls 0.26.0", - "tower 0.5.1", + "tokio-rustls 0.26.1", + "tower 0.5.2", ] [[package]] @@ -5056,9 +5438,9 @@ dependencies = [ [[package]] name = "hyper" -version = "0.14.31" +version = "0.14.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c08302e8fa335b151b788c775ff56e7a03ae64ff85c548ee820fecb70356e85" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" dependencies = [ "bytes", "futures-channel", @@ -5071,7 +5453,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.5.7", + "socket2 0.5.8", "tokio", "tower-service", "tracing", @@ -5080,14 +5462,14 @@ dependencies = [ [[package]] name = "hyper" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.6", + "h2 0.4.7", "http 1.2.0", "http-body 1.0.1", "httparse", @@ -5109,13 +5491,13 @@ dependencies = [ "futures-util", "headers 0.4.0", "http 1.2.0", - "hyper 1.5.1", - "hyper-rustls 0.27.3", + "hyper 1.6.0", + "hyper-rustls 0.27.5", "hyper-util", "pin-project-lite", "rustls-native-certs 0.7.3", "tokio", - "tokio-rustls 0.26.0", + "tokio-rustls 0.26.1", "tower-service", ] @@ -5127,7 +5509,7 @@ checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http 0.2.12", - "hyper 0.14.31", + "hyper 0.14.32", "rustls 0.21.12", "tokio", "tokio-rustls 0.24.1", @@ -5135,22 +5517,22 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.3" +version = "0.27.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" dependencies = [ "futures-util", "http 1.2.0", - "hyper 1.5.1", + "hyper 1.6.0", "hyper-util", "log", - "rustls 0.23.19", - "rustls-native-certs 0.8.0", + "rustls 0.23.21", + "rustls-native-certs 0.8.1", "rustls-pki-types", "tokio", - "tokio-rustls 0.26.0", + "tokio-rustls 0.26.1", "tower-service", - "webpki-roots 0.26.6", + "webpki-roots 0.26.7", ] [[package]] @@ -5161,20 +5543,20 @@ checksum = "51c227614c208f7e7c2e040526912604a1a957fe467c9c2f5b06c5d032337dab" dependencies = [ "async-socks5", "http 1.2.0", - "hyper 1.5.1", + "hyper 1.6.0", "hyper-util", - "thiserror 1.0.68", + "thiserror 1.0.69", "tokio", "tower-service", ] [[package]] name = "hyper-timeout" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" dependencies = [ - "hyper 1.5.1", + "hyper 1.6.0", "hyper-util", "pin-project-lite", "tokio", @@ -5192,9 +5574,9 @@ dependencies = [ "futures-util", "http 1.2.0", "http-body 1.0.1", - "hyper 1.5.1", + "hyper 1.6.0", "pin-project-lite", - "socket2 0.5.7", + "socket2 0.5.8", "tokio", "tower-service", "tracing", @@ -5211,7 +5593,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows-core", + "windows-core 0.52.0", ] [[package]] @@ -5235,7 +5617,7 @@ dependencies = [ "slog", "tokio", "tonic", - "tower 0.5.1", + "tower 0.5.2", ] [[package]] @@ -5256,8 +5638,8 @@ dependencies = [ name = "ic-adapter-metrics-service" version = "0.9.0" dependencies = [ - "prost 0.13.3", - "prost-build 0.13.3", + "prost 0.13.4", + "prost-build 0.13.4", "tonic", "tonic-build", ] @@ -5272,7 +5654,7 @@ dependencies = [ "base64 0.13.1", "candid", "chrono", - "clap 4.5.20", + "clap 4.5.27", "cycles-minting-canister", "futures", "hex", @@ -5316,12 +5698,12 @@ dependencies = [ "ic-sns-swap", "ic-sns-wasm", "ic-types", - "indexmap 2.6.0", + "indexmap 2.7.1", "itertools 0.12.1", "maplit", "pocket-ic", "pretty_assertions", - "prost 0.13.3", + "prost 0.13.4", "registry-canister", "serde", "serde_json", @@ -5342,89 +5724,47 @@ dependencies = [ [[package]] name = "ic-agent" -version = "0.37.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fd3fdf5e5c4f4a9fe5ca612f0febd22dcb161d2f2b75b0142326732be5e4978" -dependencies = [ - "async-lock", - "backoff", - "cached 0.52.0", - "candid", - "ed25519-consensus", - "futures-util", - "hex", - "http 1.2.0", - "http-body 1.0.1", - "http-body-to-bytes", - "http-body-util", - "hyper 1.5.1", - "hyper-rustls 0.27.3", - "hyper-util", - "ic-certification 2.6.0", - "ic-transport-types 0.37.1", - "ic-verify-bls-signature 0.5.0", - "k256", - "leb128", - "p256", - "pem 3.0.4", - "pkcs8", - "rand 0.8.5", - "rangemap", - "reqwest 0.12.9", - "ring 0.17.8", - "rustls-webpki 0.102.8", - "sec1", - "serde", - "serde_bytes", - "serde_cbor", - "serde_repr", - "sha2 0.10.8", - "simple_asn1", - "thiserror 1.0.68", - "time", - "tokio", - "tower 0.4.13", - "url", -] - -[[package]] -name = "ic-agent" -version = "0.39.1" +version = "0.39.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "158138fcb769fe6288e63d5db221c904e472cfb7d376aba13a38c060f2984e63" +checksum = "820d65a05258f2fdff326c65561b1ddc7ec54e5d43a4b1203b25eb83075c83d4" dependencies = [ + "arc-swap", + "async-channel 1.9.0", "async-lock", "async-trait", + "async-watch", "backoff", "cached 0.52.0", "candid", - "der", - "ecdsa", + "der 0.7.9", + "ecdsa 0.16.9", "ed25519-consensus", - "elliptic-curve", + "elliptic-curve 0.13.8", "futures-util", "hex", "http 1.2.0", "http-body 1.0.1", - "ic-certification 2.6.0", - "ic-transport-types 0.39.1", + "ic-certification 3.0.2", + "ic-transport-types", "ic-verify-bls-signature 0.5.0", - "k256", + "k256 0.13.4", "leb128", "p256", "pem 3.0.4", - "pkcs8", + "pkcs8 0.10.2", "rand 0.8.5", "rangemap", - "reqwest 0.12.9", - "sec1", + "reqwest 0.12.12", + "ring 0.17.8", + "sec1 0.7.3", "serde", "serde_bytes", "serde_cbor", "serde_repr", "sha2 0.10.8", "simple_asn1", - "thiserror 1.0.68", + "stop-token", + "thiserror 2.0.11", "time", "tokio", "tower-service", @@ -5442,6 +5782,7 @@ dependencies = [ "futures", "http-body-util", "ic-base-types", + "ic-canister-client-sender", "ic-consensus-manager", "ic-interfaces", "ic-logger", @@ -5457,12 +5798,12 @@ dependencies = [ "mockall", "phantom_newtype", "prometheus", - "prost 0.13.3", + "prost 0.13.4", "rand 0.8.5", "slog", - "thiserror 2.0.3", + "thiserror 2.0.11", "tokio", - "tower 0.5.1", + "tower 0.5.2", "tracing", ] @@ -5471,8 +5812,10 @@ name = "ic-artifact-manager" version = "0.9.0" dependencies = [ "assert_matches", + "futures", "ic-artifact-pool", "ic-config", + "ic-consensus-manager", "ic-interfaces", "ic-metrics", "ic-protobuf", @@ -5480,6 +5823,7 @@ dependencies = [ "ic-types", "prometheus", "tokio", + "tokio-stream", "tracing", ] @@ -5489,7 +5833,7 @@ version = "0.9.0" dependencies = [ "bincode", "byteorder", - "clap 4.5.20", + "clap 4.5.27", "criterion", "ic-config", "ic-crypto-test-utils-canister-threshold-sigs", @@ -5512,7 +5856,7 @@ dependencies = [ "lmdb-rkv-sys", "nix 0.24.3", "prometheus", - "prost 0.13.3", + "prost 0.13.4", "rand 0.8.5", "rocksdb", "serde", @@ -5532,7 +5876,7 @@ version = "0.9.0" dependencies = [ "anyhow", "chrono", - "clap 4.5.20", + "clap 4.5.27", "ic-config", "ic-crypto-utils-threshold-sig-der", "ic-logger", @@ -5544,7 +5888,7 @@ dependencies = [ "ic-test-utilities-tmpdir", "ic-types", "rand 0.8.5", - "reqwest 0.12.9", + "reqwest 0.12.12", "serde", "serde_json", "slog", @@ -5570,9 +5914,9 @@ dependencies = [ "ic-protobuf", "ic-test-utilities-compare-dirs", "phantom_newtype", - "proptest", + "proptest 1.6.0", "proptest-derive", - "prost 0.13.3", + "prost 0.13.4", "serde", "serde_cbor", "strum", @@ -5585,7 +5929,7 @@ name = "ic-base-types-protobuf-generator" version = "0.9.0" dependencies = [ "ic-utils-rustfmt", - "prost-build 0.13.3", + "prost-build 0.13.4", ] [[package]] @@ -5593,7 +5937,7 @@ name = "ic-bitcoin-canister-mock" version = "0.9.0" dependencies = [ "bech32 0.9.1", - "bitcoin 0.28.2", + "bitcoin 0.32.5", "candid", "candid_parser", "hex", @@ -5626,11 +5970,11 @@ dependencies = [ "base64 0.22.1", "bytes", "chacha20poly1305", - "clap 4.5.20", - "clap_derive 4.5.18", + "clap 4.5.27", + "clap_derive 4.5.24", "cloudflare 0.12.0 (git+https://github.com/cloudflare/cloudflare-rs.git?rev=f14720e42184ee176a97676e85ef2d2d85bc3aae)", "derive-new", - "fqdn 0.4.1", + "fqdn 0.4.5", "futures", "futures-util", "hickory-proto", @@ -5639,18 +5983,18 @@ dependencies = [ "http-body 1.0.1", "http-body-util", "humantime", - "hyper 1.5.1", + "hyper 1.6.0", "hyper-util", "instant-acme", "moka", "parse-size", "prometheus", - "prost 0.13.3", - "prost-types 0.13.3", + "prost 0.13.4", + "prost-types 0.13.4", "rand 0.8.5", "rcgen", - "reqwest 0.12.9", - "rustls 0.23.19", + "reqwest 0.12.12", + "rustls 0.23.21", "rustls-acme", "rustls-pemfile 2.2.0", "rustls-platform-verifier", @@ -5661,12 +6005,12 @@ dependencies = [ "strum", "strum_macros", "systemstat", - "thiserror 2.0.3", + "thiserror 2.0.11", "tokio", "tokio-io-timeout", - "tokio-rustls 0.26.0", + "tokio-rustls 0.26.1", "tokio-util", - "tower 0.5.1", + "tower 0.5.2", "tower-service", "tracing", "url", @@ -5690,7 +6034,7 @@ dependencies = [ "axum-extra", "bytes", "candid", - "clap 4.5.20", + "clap 4.5.27", "criterion", "dashmap 6.1.0", "ethnum", @@ -5700,7 +6044,7 @@ dependencies = [ "http 1.2.0", "http-body 1.0.1", "humantime", - "ic-agent 0.39.1", + "ic-agent", "ic-base-types", "ic-bn-lib", "ic-canister-client", @@ -5729,6 +6073,7 @@ dependencies = [ "ic-registry-subnet-type", "ic-types", "indoc", + "ipnet", "lazy_static", "maxminddb", "mockall", @@ -5741,8 +6086,8 @@ dependencies = [ "ratelimit", "rcgen", "regex", - "reqwest 0.12.9", - "rustls 0.23.19", + "reqwest 0.12.12", + "rustls 0.23.21", "rustls-pemfile 2.2.0", "serde", "serde_bytes", @@ -5754,12 +6099,12 @@ dependencies = [ "slog", "strum", "tempfile", - "thiserror 2.0.3", + "thiserror 2.0.11", "tikv-jemalloc-ctl", "tikv-jemallocator", "tokio", "tokio-util", - "tower 0.5.1", + "tower 0.5.2", "tower-http 0.6.2", "tower_governor", "tracing", @@ -5778,7 +6123,7 @@ dependencies = [ "candid", "certificate_orchestrator_interface", "chacha20poly1305", - "ic-agent 0.37.1", + "ic-agent", "ic-interfaces-registry", "ic-protobuf", "ic-registry-keys", @@ -5787,11 +6132,11 @@ dependencies = [ "ic-registry-subnet-type", "ic-system-test-driver", "indoc", - "k256", + "k256 0.13.4", "pem 1.1.1", "rand 0.8.5", "rand_chacha 0.3.1", - "reqwest 0.12.9", + "reqwest 0.12.12", "serde_json", "slog", "tokio", @@ -5803,12 +6148,12 @@ version = "0.9.0" dependencies = [ "anyhow", "futures", - "ic-agent 0.37.1", + "ic-agent", "ic-boundary-nodes-system-test-utils", "ic-crypto-tree-hash", "ic-system-test-driver", "ic-types", - "reqwest 0.12.9", + "reqwest 0.12.12", "serde", "serde_cbor", "slog", @@ -5822,7 +6167,7 @@ version = "0.9.0" dependencies = [ "anyhow", "candid", - "ic-agent 0.37.1", + "ic-agent", "ic-boundary-nodes-system-test-utils", "ic-protobuf", "ic-registry-keys", @@ -5830,9 +6175,9 @@ dependencies = [ "ic-registry-routing-table", "ic-registry-subnet-type", "ic-system-test-driver", - "prost 0.13.3", + "prost 0.13.4", "rand 0.8.5", - "reqwest 0.12.9", + "reqwest 0.12.12", "slog", "tokio", ] @@ -5843,7 +6188,7 @@ version = "0.9.0" dependencies = [ "anyhow", "futures", - "ic-agent 0.37.1", + "ic-agent", "ic-base-types", "ic-interfaces-registry", "ic-protobuf", @@ -5853,7 +6198,7 @@ dependencies = [ "ic-registry-subnet-type", "ic-system-test-driver", "ic-types", - "ic-utils 0.37.0", + "ic-utils 0.39.3", "slog", "url", ] @@ -5862,10 +6207,10 @@ dependencies = [ name = "ic-btc-adapter" version = "0.9.0" dependencies = [ - "bitcoin 0.28.2", + "bitcoin 0.32.5", "bitcoincore-rpc", "bitcoind", - "clap 4.5.20", + "clap 4.5.27", "criterion", "futures", "hashlink", @@ -5886,19 +6231,20 @@ dependencies = [ "ic-metrics", "ic-test-utilities-logger", "parking_lot 0.12.3", + "primitive-types", "prometheus", - "prost 0.13.3", + "prost 0.13.4", "rand 0.8.5", "serde", "serde_json", "slog", "slog-async", "tempfile", - "thiserror 2.0.3", + "thiserror 2.0.11", "tokio", "tokio-socks", "tonic", - "tower 0.5.1", + "tower 0.5.2", ] [[package]] @@ -5920,7 +6266,7 @@ dependencies = [ "slog", "tokio", "tonic", - "tower 0.5.1", + "tower 0.5.2", "tracing", ] @@ -5928,7 +6274,7 @@ dependencies = [ name = "ic-btc-adapter-test-utils" version = "0.9.0" dependencies = [ - "bitcoin 0.28.2", + "bitcoin 0.32.5", "flate2", "hex", "rand 0.8.5", @@ -5942,7 +6288,7 @@ version = "0.9.0" dependencies = [ "askama", "base64 0.13.1", - "bitcoin 0.32.3", + "bitcoin 0.32.5", "candid", "candid_parser", "ciborium", @@ -5953,14 +6299,14 @@ dependencies = [ "ic-canister-log 0.2.0", "ic-canisters-http-types", "ic-cdk 0.16.0", + "ic-metrics-assert", "ic-metrics-encoder", "ic-stable-structures", "ic-test-utilities-load-wasm", "ic-types", "ic-universal-canister", - "num-traits", "pocket-ic", - "proptest", + "proptest 1.6.0", "scraper", "serde", "serde_json", @@ -5997,10 +6343,10 @@ dependencies = [ "ic-types", "mockall", "prometheus", - "proptest", - "prost 0.13.3", + "proptest 1.6.0", + "prost 0.13.4", "slog", - "thiserror 2.0.3", + "thiserror 2.0.11", ] [[package]] @@ -6030,7 +6376,7 @@ dependencies = [ name = "ic-btc-service" version = "0.9.0" dependencies = [ - "prost 0.13.3", + "prost 0.13.4", "tonic", "tonic-build", ] @@ -6039,8 +6385,10 @@ dependencies = [ name = "ic-btc-validation" version = "0.9.0" dependencies = [ - "bitcoin 0.28.2", + "bitcoin 0.32.5", "csv", + "hex", + "proptest 0.9.6", ] [[package]] @@ -6051,8 +6399,8 @@ dependencies = [ "futures-util", "hex", "http-body-util", - "hyper 1.5.1", - "hyper-rustls 0.27.3", + "hyper 1.6.0", + "hyper-rustls 0.27.5", "hyper-util", "ic-canister-client-sender", "ic-canonical-state", @@ -6071,15 +6419,15 @@ dependencies = [ "ic-types", "ic-validator", "itertools 0.12.1", - "prost 0.13.3", + "prost 0.13.4", "rand 0.8.5", "rand_chacha 0.3.1", - "rustls 0.23.19", + "rustls 0.23.21", "serde", "serde_cbor", "tokio", "tokio-test", - "tower 0.5.1", + "tower 0.5.2", "tree-deserializer", "url", ] @@ -6188,20 +6536,19 @@ dependencies = [ [[package]] name = "ic-canister-sig-creation" version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5db33deb06e0edb366d8d86ef67d7bc1e1759bc7046b0323a33b85b21b8d8d87" +source = "git+https://github.com/dfinity/ic-canister-sig-creation?rev=7f9e931954637526295269155881207f6c832d6d#7f9e931954637526295269155881207f6c832d6d" dependencies = [ "candid", "hex", - "ic-cdk 0.14.1", - "ic-certification 2.6.0", + "ic-cdk 0.17.1", + "ic-certification 3.0.2", "ic-representation-independent-hash", "lazy_static", "serde", "serde_bytes", "serde_cbor", "sha2 0.10.8", - "thiserror 1.0.68", + "thiserror 2.0.11", ] [[package]] @@ -6244,7 +6591,7 @@ dependencies = [ "leb128", "maplit", "phantom_newtype", - "proptest", + "proptest 1.6.0", "serde", "serde_bytes", "serde_cbor", @@ -6264,11 +6611,11 @@ dependencies = [ "ic-crypto-tree-hash-test-utils", "itertools 0.12.1", "leb128", - "proptest", + "proptest 1.6.0", "rand 0.8.5", "rand_chacha 0.3.1", "scoped_threadpool", - "thiserror 2.0.3", + "thiserror 2.0.11", ] [[package]] @@ -6283,15 +6630,15 @@ dependencies = [ [[package]] name = "ic-cbor" -version = "2.6.0" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b0e48b4166c891e79d624f3a184b4a7c145d307576872d9a46dedb8c73ea8f" +checksum = "5500d6e85bc2ca8ea8aaed16cb84811882589244831a2fd8eefe02e90b3006c6" dependencies = [ "candid", - "ic-certification 2.6.0", + "ic-certification 3.0.2", "leb128", "nom", - "thiserror 1.0.68", + "thiserror 1.0.69", ] [[package]] @@ -6322,12 +6669,12 @@ dependencies = [ [[package]] name = "ic-cdk" -version = "0.14.1" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cff1a3c3db565e3384c9c9d6d676b0a3f89a0886f4f787294d9c946d844369f" +checksum = "dd8ecacd682fa05a985253592963306cb9799622d7b1cce4b1edb89c6ec85be1" dependencies = [ "candid", - "ic-cdk-macros 0.14.0", + "ic-cdk-macros 0.16.0", "ic0 0.23.0", "serde", "serde_bytes", @@ -6335,17 +6682,30 @@ dependencies = [ [[package]] name = "ic-cdk" -version = "0.16.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8ecacd682fa05a985253592963306cb9799622d7b1cce4b1edb89c6ec85be1" +checksum = "122efbcb0af5280d408a75a57b7dc6e9d92893bf6ed9cc98fe4dcff51f18b67c" dependencies = [ "candid", - "ic-cdk-macros 0.16.0", + "ic-cdk-macros 0.17.1", "ic0 0.23.0", "serde", "serde_bytes", ] +[[package]] +name = "ic-cdk" +version = "0.18.0-alpha.1" +source = "git+https://github.com/dfinity/cdk-rs?rev=4e287ce51636b0e70768c193da38d2fc5324ea15#4e287ce51636b0e70768c193da38d2fc5324ea15" +dependencies = [ + "candid", + "ic-cdk-macros 0.18.0-alpha.1", + "ic0 0.24.0-alpha.1", + "serde", + "serde_bytes", + "thiserror 2.0.11", +] + [[package]] name = "ic-cdk-macros" version = "0.8.4" @@ -6390,30 +6750,43 @@ dependencies = [ [[package]] name = "ic-cdk-macros" -version = "0.14.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01dc6bc425ec048d6ac4137c7c0f2cfbd6f8b0be8efc568feae2b265f566117c" +checksum = "0d4d857135deef20cc7ea8f3869a30cd9cfeb1392b3a81043790b2cd82adc3e0" dependencies = [ "candid", "proc-macro2", "quote", "serde", "serde_tokenstream 0.2.2", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] name = "ic-cdk-macros" -version = "0.16.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d4d857135deef20cc7ea8f3869a30cd9cfeb1392b3a81043790b2cd82adc3e0" +checksum = "c792bf0d1621c893ccf2bcdeac4ee70121103a03030a1827031a6b3c60488944" +dependencies = [ + "candid", + "proc-macro2", + "quote", + "serde", + "serde_tokenstream 0.2.2", + "syn 2.0.96", +] + +[[package]] +name = "ic-cdk-macros" +version = "0.18.0-alpha.1" +source = "git+https://github.com/dfinity/cdk-rs?rev=4e287ce51636b0e70768c193da38d2fc5324ea15#4e287ce51636b0e70768c193da38d2fc5324ea15" dependencies = [ "candid", "proc-macro2", "quote", "serde", "serde_tokenstream 0.2.2", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -6432,21 +6805,21 @@ dependencies = [ [[package]] name = "ic-certificate-verification" -version = "2.6.0" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "586e09b06a93d930f6a33f5f909bb11d2e4a06be3635dd5da1eb0e6554b7dae4" +checksum = "2daec653eb7895b5549cdf58d871985711c03cf5e389f7800a970f4f42dc0897" dependencies = [ - "cached 0.47.0", + "cached 0.54.0", "candid", "ic-cbor", - "ic-certification 2.6.0", + "ic-certification 3.0.2", "lazy_static", "leb128", "miracl_core_bls12381", "nom", "parking_lot 0.12.3", "sha2 0.10.8", - "thiserror 1.0.68", + "thiserror 1.0.69", ] [[package]] @@ -6473,9 +6846,9 @@ dependencies = [ [[package]] name = "ic-certification" -version = "2.6.0" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64ee3d8b6e81b51f245716d3e0badb63c283c00f3c9fb5d5219afc30b5bf821" +checksum = "9eae40f26fcac9c141cad54d9aa5f423efffde78ac371057c53d275ebbcad443" dependencies = [ "hex", "serde", @@ -6523,7 +6896,7 @@ name = "ic-ckbtc-agent" version = "0.9.0" dependencies = [ "candid", - "ic-agent 0.37.1", + "ic-agent", "ic-canisters-http-types", "ic-ckbtc-minter", "ic-icrc1", @@ -6564,14 +6937,14 @@ dependencies = [ "async-trait", "bech32 0.9.1", "bitcoin 0.28.2", - "bs58", + "bs58 0.5.1", "candid", "candid_parser", "canister-test", "ciborium", "flate2", "hex", - "ic-agent 0.37.1", + "ic-agent", "ic-base-types", "ic-bitcoin-canister-mock", "ic-btc-checker", @@ -6588,6 +6961,7 @@ dependencies = [ "ic-icrc1-ledger", "ic-ledger-core", "ic-management-canister-types", + "ic-metrics-assert", "ic-metrics-encoder", "ic-stable-structures", "ic-state-machine-tests", @@ -6603,8 +6977,7 @@ dependencies = [ "minicbor-derive", "mockall", "num-traits", - "proptest", - "regex", + "proptest 1.6.0", "ripemd", "scopeguard", "serde", @@ -6629,7 +7002,7 @@ dependencies = [ "futures", "hex", "hex-literal", - "ic-agent 0.37.1", + "ic-agent", "ic-base-types", "ic-canister-log 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "ic-canisters-http-types", @@ -6657,7 +7030,7 @@ dependencies = [ "num-bigint 0.4.6", "num-traits", "phantom_newtype", - "proptest", + "proptest 1.6.0", "rand 0.8.5", "rlp", "scopeguard", @@ -6668,7 +7041,7 @@ dependencies = [ "strum", "strum_macros", "tempfile", - "thiserror 2.0.3", + "thiserror 2.0.11", "thousands", "time", "tokio", @@ -6715,7 +7088,7 @@ dependencies = [ "ic-sys", "ic-types", "json5", - "proptest", + "proptest 1.6.0", "proptest-derive", "serde", "tempfile", @@ -6731,6 +7104,7 @@ dependencies = [ "ic-btc-replica-types", "ic-config", "ic-consensus", + "ic-consensus-dkg", "ic-consensus-mocks", "ic-consensus-utils", "ic-crypto", @@ -6779,11 +7153,10 @@ dependencies = [ "num-traits", "phantom_newtype", "prometheus", - "proptest", - "prost 0.13.3", + "proptest 1.6.0", + "prost 0.13.4", "rand 0.8.5", "rand_chacha 0.3.1", - "rayon", "rstest", "serde_cbor", "slog", @@ -6796,6 +7169,35 @@ dependencies = [ "tokio", ] +[[package]] +name = "ic-consensus-dkg" +version = "0.9.0" +dependencies = [ + "ic-artifact-pool", + "ic-consensus-mocks", + "ic-consensus-utils", + "ic-crypto-test-utils-ni-dkg", + "ic-interfaces", + "ic-interfaces-registry", + "ic-interfaces-state-manager", + "ic-logger", + "ic-metrics", + "ic-protobuf", + "ic-registry-client-helpers", + "ic-replicated-state", + "ic-test-artifact-pool", + "ic-test-utilities", + "ic-test-utilities-consensus", + "ic-test-utilities-logger", + "ic-test-utilities-registry", + "ic-test-utilities-state", + "ic-test-utilities-types", + "ic-types", + "prometheus", + "rayon", + "slog", +] + [[package]] name = "ic-consensus-manager" version = "0.9.0" @@ -6818,12 +7220,12 @@ dependencies = [ "mockall", "phantom_newtype", "prometheus", - "prost 0.13.3", + "prost 0.13.4", "rand 0.8.5", "slog", "tokio", "tokio-util", - "tower 0.5.1", + "tower 0.5.2", "tracing", "turmoil", ] @@ -6866,6 +7268,7 @@ dependencies = [ "ic-interfaces-registry", "ic-logger", "ic-management-canister-types", + "ic-metrics", "ic-protobuf", "ic-registry-client-helpers", "ic-replicated-state", @@ -6875,6 +7278,7 @@ dependencies = [ "ic-test-utilities-time", "ic-test-utilities-types", "ic-types", + "prometheus", "rand 0.8.5", "slog", ] @@ -6886,7 +7290,7 @@ dependencies = [ "assert_matches", "async-trait", "bincode", - "clap 4.5.20", + "clap 4.5.27", "criterion", "hex", "ic-adapter-metrics-server", @@ -6902,6 +7306,7 @@ dependencies = [ "ic-crypto-internal-basic-sig-ecdsa-secp256r1", "ic-crypto-internal-basic-sig-ed25519", "ic-crypto-internal-basic-sig-rsa-pkcs1", + "ic-crypto-internal-bls12-381-vetkd", "ic-crypto-internal-csp", "ic-crypto-internal-csp-proptest-utils", "ic-crypto-internal-csp-test-utils", @@ -6955,17 +7360,18 @@ dependencies = [ "ic-test-utilities-time", "ic-types", "ic-types-test-utils", - "k256", + "ic-vetkd-utils", + "k256 0.13.4", "maplit", "mockall", "parking_lot 0.12.3", - "proptest", + "proptest 1.6.0", "proptest-derive", - "prost 0.13.3", + "prost 0.13.4", "rand 0.8.5", "rand_chacha 0.3.1", "rsa", - "rustls 0.23.19", + "rustls 0.23.21", "serde", "sha2 0.10.8", "simple_asn1", @@ -7009,7 +7415,7 @@ dependencies = [ "pem 1.1.1", "rand 0.8.5", "rand_chacha 0.3.1", - "thiserror 2.0.3", + "thiserror 2.0.11", "wycheproof", "zeroize", ] @@ -7139,7 +7545,7 @@ dependencies = [ "ic-protobuf", "ic-types", "num-bigint 0.4.6", - "proptest", + "proptest 1.6.0", "proptest-derive", "rand 0.8.5", "rand_chacha 0.3.1", @@ -7198,7 +7604,7 @@ dependencies = [ "ic-types", "num-bigint 0.4.6", "num-traits", - "pkcs8", + "pkcs8 0.10.2", "rsa", "serde", "serde_json", @@ -7236,7 +7642,7 @@ dependencies = [ "ic-crypto-test-utils-reproducible-rng", "ic-sha3 1.0.0", "rand 0.8.5", - "zeroize", + "rand_chacha 0.3.1", ] [[package]] @@ -7298,9 +7704,9 @@ dependencies = [ "mockall", "num_cpus", "parking_lot 0.12.3", - "proptest", + "proptest 1.6.0", "proptest-derive", - "prost 0.13.3", + "prost 0.13.4", "rand 0.8.5", "rand_chacha 0.3.1", "rayon", @@ -7315,7 +7721,7 @@ dependencies = [ "stubborn-io", "tarpc", "tempfile", - "thiserror 2.0.3", + "thiserror 2.0.11", "time", "tokio", "tokio-serde", @@ -7342,7 +7748,7 @@ dependencies = [ "ic-protobuf", "ic-types", "paste", - "proptest", + "proptest 1.6.0", "strum", ] @@ -7351,7 +7757,7 @@ name = "ic-crypto-internal-csp-protobuf-generator" version = "0.9.0" dependencies = [ "ic-utils-rustfmt", - "prost-build 0.13.3", + "prost-build 0.13.4", ] [[package]] @@ -7404,7 +7810,7 @@ dependencies = [ "ic-crypto-test-utils-reproducible-rng", "ic-protobuf", "ic-types", - "proptest", + "proptest 1.6.0", "rand 0.8.5", "rand_chacha 0.3.1", "serde", @@ -7457,7 +7863,7 @@ dependencies = [ "ic-types", "lazy_static", "parking_lot 0.12.3", - "proptest", + "proptest 1.6.0", "proptest-derive", "rand 0.8.5", "rand_chacha 0.3.1", @@ -7474,12 +7880,12 @@ name = "ic-crypto-internal-threshold-sig-canister-threshold-sig" version = "0.9.0" dependencies = [ "assert_matches", - "bip32", + "bip32 0.5.2", "criterion", "curve25519-dalek", "ed25519-dalek", "fe-derive", - "group", + "group 0.13.0", "hex", "hex-literal", "ic-crypto-internal-hmac", @@ -7490,7 +7896,7 @@ dependencies = [ "ic-crypto-sha2", "ic-crypto-test-utils-reproducible-rng", "ic-types", - "k256", + "k256 0.13.4", "lazy_static", "num-traits", "p256", @@ -7516,7 +7922,7 @@ dependencies = [ "ic-crypto-internal-threshold-sig-canister-threshold-sig", "ic-crypto-sha2", "ic-types", - "k256", + "k256 0.13.4", "p256", "rand 0.8.5", "secp256k1 0.22.2", @@ -7557,7 +7963,7 @@ dependencies = [ "serde_cbor", "strum", "strum_macros", - "thiserror 2.0.3", + "thiserror 2.0.11", "zeroize", ] @@ -7634,12 +8040,12 @@ dependencies = [ name = "ic-crypto-secp256k1" version = "0.9.0" dependencies = [ - "bip32", + "bip32 0.5.2", "bitcoin 0.28.2", "hex", "hex-literal", "hmac", - "k256", + "k256 0.13.4", "lazy_static", "num-bigint 0.4.6", "pem 1.1.1", @@ -7726,7 +8132,7 @@ dependencies = [ "ic-types-test-utils", "rand 0.8.5", "rand_chacha 0.3.1", - "rustls 0.23.19", + "rustls 0.23.21", "tempfile", "tokio", ] @@ -7905,7 +8311,7 @@ version = "0.9.0" dependencies = [ "ic-types", "mockall", - "thiserror 2.0.3", + "thiserror 2.0.11", ] [[package]] @@ -7924,13 +8330,13 @@ dependencies = [ "ic-registry-keys", "ic-registry-proto-data-provider", "ic-types", - "pkcs8", + "pkcs8 0.10.2", "rand 0.8.5", - "rustls 0.23.19", - "signature", + "rustls 0.23.21", + "signature 2.2.0", "time", "tokio", - "tokio-rustls 0.26.0", + "tokio-rustls 0.26.1", "x509-cert", ] @@ -7962,9 +8368,9 @@ dependencies = [ "ic-types", "json5", "maplit", - "rustls 0.23.19", + "rustls 0.23.21", "serde", - "thiserror 2.0.3", + "thiserror 2.0.11", "x509-parser", ] @@ -7975,7 +8381,7 @@ dependencies = [ "ic-base-types", "ic-crypto-tls-interfaces", "mockall", - "rustls 0.23.19", + "rustls 0.23.21", ] [[package]] @@ -7990,13 +8396,13 @@ dependencies = [ "ic-crypto-tree-hash-test-utils", "ic-protobuf", "maplit", - "proptest", - "prost 0.13.3", + "proptest 1.6.0", + "prost 0.13.4", "rand 0.8.5", "serde", "serde_bytes", "serde_cbor", - "thiserror 2.0.3", + "thiserror 2.0.11", ] [[package]] @@ -8006,9 +8412,9 @@ dependencies = [ "assert_matches", "ic-crypto-test-utils-reproducible-rng", "ic-crypto-tree-hash", - "proptest", + "proptest 1.6.0", "rand 0.8.5", - "thiserror 2.0.3", + "thiserror 2.0.11", ] [[package]] @@ -8075,7 +8481,7 @@ name = "ic-crypto-utils-tls" version = "0.9.0" dependencies = [ "ic-base-types", - "thiserror 2.0.3", + "thiserror 2.0.11", "x509-parser", ] @@ -8089,8 +8495,8 @@ dependencies = [ "ic-registry-keys", "ic-registry-nns-data-provider", "ic-types", - "prost 0.13.3", - "reqwest 0.12.9", + "prost 0.13.4", + "reqwest 0.12.12", "tokio", ] @@ -8160,7 +8566,7 @@ dependencies = [ name = "ic-drun" version = "0.9.0" dependencies = [ - "clap 4.5.20", + "clap 4.5.27", "futures", "hex", "ic-canister-sandbox-backend-lib", @@ -8192,7 +8598,7 @@ dependencies = [ "slog", "slog-term", "tokio", - "tower 0.5.1", + "tower 0.5.2", "wasmparser 0.217.0", ] @@ -8205,7 +8611,7 @@ dependencies = [ "bincode", "candid", "canister-test", - "clap 4.5.20", + "clap 4.5.27", "criterion", "embedders_bench", "ic-base-types", @@ -8243,7 +8649,7 @@ dependencies = [ "num-traits", "pretty_assertions", "prometheus", - "proptest", + "proptest 1.6.0", "rayon", "rustc-demangle", "serde", @@ -8280,7 +8686,7 @@ dependencies = [ "ic-sha3 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "minicbor", "minicbor-derive", - "proptest", + "proptest 1.6.0", "serde", "serde_json", ] @@ -8336,6 +8742,7 @@ dependencies = [ "ic-universal-canister", "ic-utils 0.9.0", "ic-utils-lru-cache", + "ic-utils-thread", "ic-wasm-transform", "ic-wasm-types", "itertools 0.12.1", @@ -8344,12 +8751,13 @@ dependencies = [ "maplit", "memory_tracker", "more-asserts", - "num-rational", + "num-rational 0.2.4", "num-traits", "phantom_newtype", "prometheus", - "proptest", + "proptest 1.6.0", "rand 0.8.5", + "regex", "scoped_threadpool", "serde", "serde_bytes", @@ -8360,7 +8768,7 @@ dependencies = [ "test-strategy 0.3.1", "threadpool", "tokio", - "tower 0.5.1", + "tower 0.5.2", "tracing", "wasmparser 0.217.0", "wat", @@ -8382,27 +8790,30 @@ dependencies = [ "anyhow", "assert_cmd", "assert_matches", - "clap 4.5.20", + "clap 4.5.27", "ic-crypto-test-utils-reproducible-rng", "ic-sys", "maplit", "predicates", "rand 0.8.5", + "regex", "tempfile", ] [[package]] name = "ic-http-certification" -version = "2.6.0" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff0b97e949845039149dc5e7ea6a7c12ee4333bb402e37bc507904643c7b3e41" +checksum = "479941fca8e68c2267cddf686d34ed6fb491168667ff259c08a3d65d28bd26d2" dependencies = [ + "base64 0.22.1", "candid", - "http 0.2.12", - "ic-certification 2.6.0", + "http 1.2.0", + "ic-certification 3.0.2", "ic-representation-independent-hash", "serde", - "thiserror 1.0.68", + "serde_cbor", + "thiserror 1.0.69", "urlencoding", ] @@ -8416,7 +8827,7 @@ dependencies = [ "bytes", "futures", "futures-util", - "hyper 1.5.1", + "hyper 1.6.0", "rand 0.8.5", "slog", "tokio", @@ -8433,12 +8844,12 @@ dependencies = [ "ic-metrics", "ic-test-utilities-logger", "prometheus", - "reqwest 0.12.9", + "reqwest 0.12.12", "slog", - "thiserror 2.0.3", + "thiserror 2.0.11", "tokio", "tokio-io-timeout", - "tower 0.5.1", + "tower 0.5.2", ] [[package]] @@ -8458,7 +8869,7 @@ dependencies = [ "http 1.2.0", "http-body 1.0.1", "http-body-util", - "hyper 1.5.1", + "hyper 1.6.0", "hyper-util", "ic-canister-client", "ic-canister-client-sender", @@ -8498,17 +8909,17 @@ dependencies = [ "ic-tracing", "ic-types", "ic-validator", - "inferno 0.12.0", + "inferno 0.12.1", "maplit", "mockall", "pretty_assertions", "prometheus", - "proptest", - "prost 0.13.3", + "proptest 1.6.0", + "prost 0.13.4", "rand 0.8.5", - "reqwest 0.12.9", + "reqwest 0.12.12", "rstest", - "rustls 0.23.19", + "rustls 0.23.21", "serde", "serde_bytes", "serde_cbor", @@ -8516,9 +8927,9 @@ dependencies = [ "tempfile", "tokio", "tokio-io-timeout", - "tokio-rustls 0.26.0", + "tokio-rustls 0.26.1", "tokio-util", - "tower 0.5.1", + "tower 0.5.2", "tower-http 0.6.2", "tower-test", "tracing-flame", @@ -8532,7 +8943,7 @@ dependencies = [ "axum", "bytes", "crossbeam-channel", - "hyper 1.5.1", + "hyper 1.6.0", "hyper-util", "ic-config", "ic-crypto-tls-interfaces", @@ -8554,21 +8965,22 @@ dependencies = [ "ic-types", "maplit", "prometheus", - "prost 0.13.3", - "reqwest 0.12.9", + "prost 0.13.4", + "reqwest 0.12.12", "serde", "serde_json", "slog", "tokio", - "tokio-rustls 0.26.0", - "tower 0.5.1", + "tokio-rustls 0.26.1", + "tower 0.5.2", "url", ] [[package]] name = "ic-http-gateway" -version = "0.0.0" -source = "git+https://github.com/dfinity/http-gateway?tag=0.1.0-b0#3be26b5a2c71bf56e05b910951c1935a1ac550c4" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8b30a8ff19af1a7dc64b1dbe1a38f1b60c7eea566e2049f755ce3bace0e630" dependencies = [ "bytes", "candid", @@ -8576,11 +8988,11 @@ dependencies = [ "http 1.2.0", "http-body 1.0.1", "http-body-util", - "ic-agent 0.37.1", + "ic-agent", "ic-http-certification", "ic-response-verification", - "ic-utils 0.37.0", - "thiserror 1.0.68", + "ic-utils 0.39.3", + "thiserror 1.0.69", ] [[package]] @@ -8595,7 +9007,7 @@ dependencies = [ "ic-logger", "ic-test-utilities-in-memory-logger", "mockito", - "reqwest 0.12.9", + "reqwest 0.12.12", "slog", "tar", "tempfile", @@ -8610,12 +9022,12 @@ dependencies = [ "async-stream", "byte-unit", "bytes", - "clap 4.5.20", + "clap 4.5.27", "futures", "http 1.2.0", "http-body-util", - "hyper 1.5.1", - "hyper-rustls 0.27.3", + "hyper 1.6.0", + "hyper-rustls 0.27.5", "hyper-socks2", "hyper-util", "ic-adapter-metrics-server", @@ -8625,20 +9037,21 @@ dependencies = [ "ic-logger", "ic-metrics", "once_cell", + "parking_lot 0.12.3", "prometheus", "rand 0.8.5", "rstest", - "rustls 0.23.19", + "rustls 0.23.21", "rustls-pemfile 2.2.0", "serde", "serde_json", "slog", "tempfile", - "thiserror 2.0.3", + "thiserror 2.0.11", "tokio", - "tokio-rustls 0.26.0", + "tokio-rustls 0.26.1", "tonic", - "tower 0.5.1", + "tower 0.5.2", "uuid", "warp", ] @@ -8668,7 +9081,7 @@ dependencies = [ "slog", "tokio", "tonic", - "tower 0.5.1", + "tower 0.5.2", "tower-test", "tracing", ] @@ -8702,7 +9115,7 @@ dependencies = [ "ic-types", "mockall", "prometheus", - "proptest", + "proptest 1.6.0", "rand 0.8.5", "rand_chacha 0.3.1", "slog", @@ -8712,7 +9125,7 @@ dependencies = [ name = "ic-https-outcalls-service" version = "0.9.0" dependencies = [ - "prost 0.13.3", + "prost 0.13.4", "tonic", "tonic-build", ] @@ -8762,13 +9175,14 @@ dependencies = [ "ic-crypto-secp256k1", "ic-icp-rosetta-runner", "ic-ledger-test-utils", + "ic-nns-governance-api", "ic-rosetta-api", "ic-rosetta-test-utils", "icp-ledger", "icrc-ledger-types", "num-bigint 0.4.6", "pocket-ic", - "reqwest 0.12.9", + "reqwest 0.12.12", "rosetta-core", "serde", "tempfile", @@ -8785,7 +9199,7 @@ dependencies = [ "ic-rosetta-test-utils", "icp-ledger", "pocket-ic", - "reqwest 0.12.9", + "reqwest 0.12.12", "tempfile", "tokio", ] @@ -8798,10 +9212,10 @@ dependencies = [ "axum", "candid", "ciborium", - "clap 4.5.20", + "clap 4.5.27", "futures", "hex", - "ic-agent 0.37.1", + "ic-agent", "ic-base-types", "ic-icrc-rosetta-client", "ic-icrc-rosetta-runner", @@ -8818,7 +9232,7 @@ dependencies = [ "ic-rosetta-test-utils", "ic-sys", "ic-test-utilities-load-wasm", - "ic-utils 0.37.0", + "ic-utils 0.39.3", "icrc-ledger-agent", "icrc-ledger-types", "indicatif", @@ -8827,9 +9241,9 @@ dependencies = [ "num-traits", "once_cell", "pocket-ic", - "proptest", + "proptest 1.6.0", "rand 0.8.5", - "reqwest 0.12.9", + "reqwest 0.12.12", "rolling-file", "rosetta-core", "rusqlite", @@ -8855,9 +9269,9 @@ version = "0.1.0" dependencies = [ "anyhow", "candid", - "clap 4.5.20", + "clap 4.5.27", "hex", - "ic-agent 0.37.1", + "ic-agent", "ic-crypto-ed25519", "ic-crypto-secp256k1", "ic-icrc-rosetta", @@ -8866,7 +9280,7 @@ dependencies = [ "icrc-ledger-types", "num-bigint 0.4.6", "pocket-ic", - "reqwest 0.12.9", + "reqwest 0.12.12", "rosetta-core", "serde", "tokio", @@ -8881,7 +9295,7 @@ dependencies = [ "candid", "icrc-ledger-types", "pocket-ic", - "reqwest 0.12.9", + "reqwest 0.12.12", "tempfile", "tokio", ] @@ -8915,11 +9329,11 @@ dependencies = [ "leb128", "num-bigint 0.4.6", "num-traits", - "proptest", + "proptest 1.6.0", "rand 0.8.5", "serde", "serde_bytes", - "thiserror 2.0.3", + "thiserror 2.0.11", ] [[package]] @@ -8943,95 +9357,8 @@ dependencies = [ "ic-stable-structures", "ic-state-machine-tests", "ic-test-utilities-load-wasm", - "icrc-ledger-types", - "serde", -] - -[[package]] -name = "ic-icrc1-benchmark-generator" -version = "0.9.0" -dependencies = [ - "async-trait", - "candid", - "dfn_http_metrics", - "futures", - "getrandom", - "ic-base-types", - "ic-cdk 0.16.0", - "ic-cdk-macros 0.9.0", - "ic-icrc1-benchmark-worker", - "ic-icrc1-index", - "ic-ledger-core", - "ic-metrics-encoder", - "icrc-ledger-client-cdk", - "icrc-ledger-types", - "rand 0.8.5", - "rand_chacha 0.3.1", - "serde", - "serde_bytes", -] - -[[package]] -name = "ic-icrc1-benchmark-worker" -version = "0.9.0" -dependencies = [ - "async-trait", - "candid", - "ciborium", - "dfn_http_metrics", - "futures", - "getrandom", - "hex", - "ic-base-types", - "ic-cdk 0.16.0", - "ic-cdk-macros 0.9.0", - "ic-crypto-tree-hash", - "ic-icrc1", - "ic-ledger-canister-core", - "ic-ledger-core", - "ic-metrics-encoder", - "icrc-ledger-client", - "icrc-ledger-client-cdk", - "icrc-ledger-types", - "num-traits", - "rand 0.8.5", - "rand_chacha 0.3.1", - "serde", - "serde_bytes", -] - -[[package]] -name = "ic-icrc1-index" -version = "0.9.0" -dependencies = [ - "assert_matches", - "async-trait", - "candid", - "candid_parser", - "ciborium", - "ic-base-types", - "ic-canister-profiler", - "ic-canisters-http-types", - "ic-cdk 0.16.0", - "ic-cdk-macros 0.9.0", - "ic-cdk-timers", - "ic-icrc1", - "ic-icrc1-ledger", - "ic-icrc1-tokens-u64", - "ic-ledger-canister-core", - "ic-ledger-core", - "ic-ledger-hash-of", - "ic-ledger-suite-state-machine-tests", - "ic-metrics-encoder", - "ic-rosetta-test-utils", - "ic-state-machine-tests", - "ic-test-utilities-load-wasm", - "icrc-ledger-types", - "num-traits", - "proptest", - "scopeguard", + "icrc-ledger-types", "serde", - "serde_json", ] [[package]] @@ -9041,7 +9368,7 @@ dependencies = [ "candid", "candid_parser", "ciborium", - "ic-agent 0.37.1", + "ic-agent", "ic-base-types", "ic-canister-log 0.2.0", "ic-canister-profiler", @@ -9051,7 +9378,6 @@ dependencies = [ "ic-cdk-timers", "ic-crypto-sha2", "ic-icrc1", - "ic-icrc1-index", "ic-icrc1-ledger", "ic-icrc1-test-utils", "ic-icrc1-tokens-u256", @@ -9068,7 +9394,7 @@ dependencies = [ "ic-types", "icrc-ledger-types", "num-traits", - "proptest", + "proptest 1.6.0", "scopeguard", "serde", "serde_bytes", @@ -9087,14 +9413,14 @@ dependencies = [ "cddl", "ciborium", "hex", - "ic-agent 0.37.1", + "ic-agent", "ic-base-types", "ic-canister-log 0.2.0", "ic-canisters-http-types", "ic-cdk 0.16.0", "ic-cdk-macros 0.9.0", "ic-cdk-timers", - "ic-certification 2.6.0", + "ic-certification 3.0.2", "ic-crypto-tree-hash", "ic-icrc1", "ic-icrc1-test-utils", @@ -9113,7 +9439,7 @@ dependencies = [ "minicbor", "num-bigint 0.4.6", "num-traits", - "proptest", + "proptest 1.6.0", "serde", "serde_bytes", ] @@ -9123,7 +9449,7 @@ name = "ic-icrc1-test-utils" version = "0.9.0" dependencies = [ "candid", - "ic-agent 0.37.1", + "ic-agent", "ic-crypto-ed25519", "ic-crypto-secp256k1", "ic-crypto-test-utils-reproducible-rng", @@ -9133,7 +9459,7 @@ dependencies = [ "ic-types", "icrc-ledger-types", "num-traits", - "proptest", + "proptest 1.6.0", "rand 0.8.5", "rand_chacha 0.3.1", "rosetta-core", @@ -9155,7 +9481,7 @@ dependencies = [ "minicbor", "num-bigint 0.4.6", "num-traits", - "proptest", + "proptest 1.6.0", "serde", ] @@ -9169,10 +9495,24 @@ dependencies = [ "ic-stable-structures", "minicbor", "num-traits", - "proptest", + "proptest 1.6.0", "serde", ] +[[package]] +name = "ic-identity-hsm" +version = "0.39.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e294ee038eb647cd9cce78b5961533be7f883494e6ea1faece52459bce2d29e9" +dependencies = [ + "hex", + "ic-agent", + "pkcs11", + "sha2 0.10.8", + "simple_asn1", + "thiserror 2.0.11", +] + [[package]] name = "ic-image-upgrader" version = "0.9.0" @@ -9219,7 +9559,7 @@ dependencies = [ "ic-validator", "pprof", "prometheus", - "proptest", + "proptest 1.6.0", "rand 0.8.5", "slog", "tokio", @@ -9242,13 +9582,13 @@ dependencies = [ "ic-types", "ic-wasm-types", "phantom_newtype", - "proptest", - "prost 0.13.3", + "proptest 1.6.0", + "prost 0.13.4", "serde", "strum", "strum_macros", - "thiserror 2.0.3", - "tower 0.5.1", + "thiserror 2.0.11", + "tower 0.5.2", ] [[package]] @@ -9256,7 +9596,7 @@ name = "ic-interfaces-adapter-client" version = "0.9.0" dependencies = [ "strum_macros", - "thiserror 2.0.3", + "thiserror 2.0.11", ] [[package]] @@ -9290,7 +9630,7 @@ name = "ic-interfaces-registry" version = "0.9.0" dependencies = [ "ic-types", - "prost 0.13.3", + "prost 0.13.4", "serde", ] @@ -9311,7 +9651,7 @@ dependencies = [ "ic-crypto-tree-hash", "ic-types", "phantom_newtype", - "thiserror 2.0.3", + "thiserror 2.0.11", ] [[package]] @@ -9335,7 +9675,7 @@ dependencies = [ "chrono", "ciborium", "dfn_protobuf", - "ic-agent 0.37.1", + "ic-agent", "ic-certification 0.9.0", "ic-crypto-sha2", "ic-ledger-canister-blocks-synchronizer-test-utils", @@ -9345,7 +9685,8 @@ dependencies = [ "ic-types", "icp-ledger", "on_wire", - "proptest", + "proptest 1.6.0", + "reqwest 0.12.12", "rusqlite", "serde", "tokio", @@ -9392,7 +9733,7 @@ dependencies = [ "ic-stable-structures", "minicbor", "num-traits", - "proptest", + "proptest 1.6.0", "serde", "serde_bytes", ] @@ -9439,7 +9780,7 @@ dependencies = [ "mockall", "num-traits", "paste", - "proptest", + "proptest 1.6.0", "scopeguard", "scraper", "serde", @@ -9461,13 +9802,14 @@ dependencies = [ "ic-icrc1-ledger", "ic-ledger-suite-orchestrator", "ic-management-canister-types", + "ic-metrics-assert", "ic-state-machine-tests", "ic-test-utilities-load-wasm", "ic-types", "ic-universal-canister", "icrc-ledger-types", "paste", - "proptest", + "proptest 1.6.0", ] [[package]] @@ -9481,7 +9823,7 @@ dependencies = [ "cddl", "futures", "hex", - "ic-agent 0.37.1", + "ic-agent", "ic-base-types", "ic-canisters-http-types", "ic-config", @@ -9505,7 +9847,7 @@ dependencies = [ "icrc1-test-env", "icrc1-test-suite", "num-traits", - "proptest", + "proptest 1.6.0", ] [[package]] @@ -9570,7 +9912,7 @@ dependencies = [ "ic-types", "ic_consensus_system_test_utils", "icp-ledger", - "reqwest 0.12.9", + "reqwest 0.12.12", "serde", "slog", "url", @@ -9584,11 +9926,11 @@ dependencies = [ "assert_matches", "candid", "candid_parser", - "clap 4.5.20", + "clap 4.5.27", "futures", "hex", "maplit", - "reqwest 0.12.9", + "reqwest 0.12.12", "serde", "serde_json", "sha2 0.10.8", @@ -9629,7 +9971,7 @@ dependencies = [ "ic-quic-transport", "ic-types", "tokio", - "tower 0.5.1", + "tower 0.5.2", ] [[package]] @@ -9701,13 +10043,14 @@ dependencies = [ "mockall", "pretty_assertions", "prometheus", - "proptest", + "proptest 1.6.0", "rand 0.8.5", "rand_chacha 0.3.1", "random-traffic-test", "serde", "slog", "tempfile", + "test-strategy 0.3.1", "tracing", "xnet-test", ] @@ -9728,6 +10071,17 @@ dependencies = [ "tokio-metrics", ] +[[package]] +name = "ic-metrics-assert" +version = "0.1.0" +dependencies = [ + "candid", + "pocket-ic", + "regex", + "serde", + "serde_bytes", +] + [[package]] name = "ic-metrics-encoder" version = "1.1.1" @@ -9739,7 +10093,7 @@ name = "ic-metrics-tool" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.5.20", + "clap 4.5.27", ] [[package]] @@ -9748,20 +10102,22 @@ version = "0.0.1" dependencies = [ "anyhow", "candid", - "ic-agent 0.37.1", + "cycles-minting-canister", + "ic-agent", "ic-base-types", "ic-nervous-system-clients", "ic-nns-common", "ic-nns-constants", "ic-nns-governance-api", - "ic-sns-governance", + "ic-sns-governance-api", "ic-sns-root", "ic-sns-swap", "ic-sns-wasm", "pocket-ic", + "registry-canister", "serde", "tempfile", - "thiserror 2.0.3", + "thiserror 2.0.11", "tokio", ] @@ -9843,8 +10199,8 @@ dependencies = [ "mockall", "num-traits", "priority-queue", - "proptest", - "prost 0.13.3", + "proptest 1.6.0", + "prost 0.13.4", "rust_decimal", "serde", "serde_bytes", @@ -9991,6 +10347,7 @@ dependencies = [ "ic-nervous-system-clients", "ic-nervous-system-common", "ic-nervous-system-common-test-keys", + "ic-nervous-system-common-test-utils", "ic-nervous-system-proto", "ic-nervous-system-root", "ic-nervous-system-runtime", @@ -10008,6 +10365,7 @@ dependencies = [ "ic-registry-subnet-type", "ic-registry-transport", "ic-sns-governance", + "ic-sns-governance-api", "ic-sns-init", "ic-sns-root", "ic-sns-swap", @@ -10026,7 +10384,7 @@ dependencies = [ "maplit", "num-traits", "pocket-ic", - "prost 0.13.3", + "prost 0.13.4", "registry-canister", "rust_decimal", "rust_decimal_macros", @@ -10077,7 +10435,7 @@ dependencies = [ "ic-base-types", "ic-nervous-system-proto-protobuf-generator", "ic-test-utilities-compare-dirs", - "prost 0.13.3", + "prost 0.13.4", "rust_decimal", "serde", "tempfile", @@ -10088,7 +10446,7 @@ name = "ic-nervous-system-proto-protobuf-generator" version = "0.9.0" dependencies = [ "ic-utils-rustfmt", - "prost-build 0.13.3", + "prost-build 0.13.4", ] [[package]] @@ -10151,7 +10509,7 @@ name = "ic-networking-subnet-update-workload" version = "0.9.0" dependencies = [ "anyhow", - "ic-agent 0.37.1", + "ic-agent", "ic-interfaces-registry", "ic-protobuf", "ic-registry-canister-api", @@ -10176,7 +10534,7 @@ dependencies = [ "assert_matches", "ic-nervous-system-common", "lazy_static", - "proptest", + "proptest 1.6.0", "rust_decimal", "rust_decimal_macros", "serde", @@ -10190,7 +10548,7 @@ version = "0.9.0" dependencies = [ "candid", "colored", - "ic-agent 0.37.1", + "ic-agent", "ic-neurons-fund", "ic-sns-governance", "ic-sns-swap", @@ -10222,7 +10580,7 @@ dependencies = [ "lazy_static", "num-traits", "on_wire", - "prost 0.13.3", + "prost 0.13.4", "serde", "serde_bytes", "sha2 0.10.8", @@ -10234,7 +10592,7 @@ name = "ic-nns-common-protobuf-generator" version = "0.9.0" dependencies = [ "ic-utils-rustfmt", - "prost-build 0.13.3", + "prost-build 0.13.4", ] [[package]] @@ -10316,8 +10674,8 @@ dependencies = [ "on_wire", "pretty_assertions", "prometheus-parse", - "proptest", - "prost 0.13.3", + "proptest 1.6.0", + "prost 0.13.4", "rand 0.8.5", "rand_chacha 0.3.1", "registry-canister", @@ -10356,7 +10714,7 @@ dependencies = [ "ic-utils 0.9.0", "icp-ledger", "itertools 0.12.1", - "prost 0.13.3", + "prost 0.13.4", "serde", "serde_bytes", "strum", @@ -10384,7 +10742,7 @@ name = "ic-nns-governance-protobuf-generator" version = "0.9.0" dependencies = [ "ic-utils-rustfmt", - "prost-build 0.13.3", + "prost-build 0.13.4", ] [[package]] @@ -10416,7 +10774,7 @@ dependencies = [ "ic-test-utilities-compare-dirs", "icp-ledger", "lazy_static", - "prost 0.13.3", + "prost 0.13.4", "rand 0.8.5", "serde", "sha3", @@ -10432,7 +10790,7 @@ name = "ic-nns-gtc-protobuf-generator" version = "0.9.0" dependencies = [ "ic-utils-rustfmt", - "prost-build 0.13.3", + "prost-build 0.13.4", ] [[package]] @@ -10455,11 +10813,11 @@ dependencies = [ "candid_parser", "canister-test", "dfn_candid", - "dfn_core", - "dfn_macro", "hex", "ic-base-types", "ic-canisters-http-types", + "ic-cdk 0.16.0", + "ic-cdk-macros 0.9.0", "ic-crypto-sha2", "ic-management-canister-types", "ic-metrics-encoder", @@ -10486,7 +10844,7 @@ dependencies = [ "maplit", "on_wire", "pretty_assertions", - "prost 0.13.3", + "prost 0.13.4", "registry-canister", "serde", "serde_bytes", @@ -10500,9 +10858,8 @@ version = "0.1.0" dependencies = [ "async-trait", "candid", - "dfn_candid", - "dfn_core", "ic-base-types", + "ic-cdk 0.16.0", "ic-nervous-system-clients", "ic-nns-constants", "serde", @@ -10513,7 +10870,7 @@ name = "ic-nns-handler-root-protobuf-generator" version = "0.9.0" dependencies = [ "ic-utils-rustfmt", - "prost-build 0.13.3", + "prost-build 0.13.4", ] [[package]] @@ -10522,7 +10879,7 @@ version = "0.9.0" dependencies = [ "candid", "canister-test", - "clap 4.5.20", + "clap 4.5.27", "ic-base-types", "ic-canister-client", "ic-interfaces-registry", @@ -10536,32 +10893,12 @@ dependencies = [ "ic-sys", "ic-test-identity", "icp-ledger", - "prost 0.13.3", + "prost 0.13.4", "tempfile", "tokio", "url", ] -[[package]] -name = "ic-nns-inspector" -version = "0.1.0" -dependencies = [ - "clap 4.5.20", - "csv", - "hex", - "ic-base-types", - "ic-ledger-canister-core", - "ic-nns-constants", - "ic-nns-governance-api", - "ic-nns-gtc", - "icp-ledger", - "ledger-canister", - "prost 0.13.3", - "serde", - "serde_cbor", - "stable_reader", -] - [[package]] name = "ic-nns-integration-tests" version = "0.9.0" @@ -10587,7 +10924,7 @@ dependencies = [ "ic-cdk-macros 0.9.0", "ic-cdk-timers", "ic-certificate-verification", - "ic-certification 2.6.0", + "ic-certification 3.0.2", "ic-config", "ic-crypto", "ic-crypto-sha2", @@ -10624,6 +10961,7 @@ dependencies = [ "ic-stable-structures", "ic-state-machine-tests", "ic-test-utilities", + "ic-test-utilities-metrics", "ic-types", "ic-types-test-utils", "ic-xrc-types", @@ -10638,7 +10976,7 @@ dependencies = [ "pocket-ic", "pretty_assertions", "prometheus-parse", - "prost 0.13.3", + "prost 0.13.4", "rand 0.8.5", "registry-canister", "rustc-hash 1.1.0", @@ -10714,7 +11052,7 @@ dependencies = [ "num-traits", "on_wire", "prometheus-parse", - "prost 0.13.3", + "prost 0.13.4", "rand 0.8.5", "registry-canister", "serde", @@ -10781,11 +11119,12 @@ dependencies = [ "quinn", "quinn-udp", "rcgen", - "rustls 0.23.19", + "rustls 0.23.21", "serde", "slog", "tempfile", "tokio", + "tokio-stream", "turmoil", ] @@ -10818,7 +11157,7 @@ dependencies = [ "pprof", "prost 0.12.6", "regex", - "thiserror 2.0.3", + "thiserror 2.0.11", "tokio", ] @@ -10829,7 +11168,7 @@ dependencies = [ "anyhow", "assert_matches", "base64 0.13.1", - "clap 4.5.20", + "clap 4.5.27", "fs_extra", "ic-config", "ic-crypto-node-key-generation", @@ -10858,14 +11197,14 @@ dependencies = [ "json5", "maplit", "pretty_assertions", - "prost 0.13.3", + "prost 0.13.4", "rand 0.8.5", - "reqwest 0.12.9", + "reqwest 0.12.12", "serde", "serde_json", "slog", "tempfile", - "thiserror 2.0.3", + "thiserror 2.0.11", "url", "x509-cert", ] @@ -10881,7 +11220,7 @@ dependencies = [ "ic-protobuf-generator", "ic-test-utilities-compare-dirs", "maplit", - "prost 0.13.3", + "prost 0.13.4", "serde", "serde_json", "slog", @@ -10893,7 +11232,7 @@ name = "ic-protobuf-generator" version = "0.9.0" dependencies = [ "ic-utils-rustfmt", - "prost-build 0.13.3", + "prost-build 0.13.4", ] [[package]] @@ -10942,18 +11281,18 @@ dependencies = [ "ic-types-test-utils", "phantom_newtype", "prometheus", - "prost 0.13.3", + "prost 0.13.4", "quinn", "rstest", - "rustls 0.23.19", + "rustls 0.23.21", "slog", - "socket2 0.5.7", + "socket2 0.5.8", "static_assertions", - "thiserror 2.0.3", + "thiserror 2.0.11", "tokio", "tokio-metrics", "tokio-util", - "tower 0.5.1", + "tower 0.5.2", "tracing", "turmoil", ] @@ -10977,7 +11316,7 @@ name = "ic-recovery" version = "0.9.0" dependencies = [ "base64 0.13.1", - "clap 4.5.20", + "clap 4.5.27", "futures", "hex", "ic-artifact-pool", @@ -11006,8 +11345,8 @@ dependencies = [ "ic-test-utilities-tmpdir", "ic-test-utilities-types", "ic-types", - "prost 0.13.3", - "reqwest 0.12.9", + "prost 0.13.4", + "reqwest 0.12.12", "serde", "serde_cbor", "serde_json", @@ -11027,7 +11366,7 @@ version = "0.9.0" dependencies = [ "anyhow", "base64 0.13.1", - "clap 4.5.20", + "clap 4.5.27", "ic-base-types", "ic-crypto-sha2", "ic-crypto-utils-threshold-sig-der", @@ -11041,11 +11380,11 @@ dependencies = [ "ic-registry-provisional-whitelist", "ic-registry-subnet-type", "ic-types", - "prost 0.13.3", + "prost 0.13.4", "serde", "serde_json", "tempfile", - "thiserror 2.0.3", + "thiserror 2.0.11", "tokio", "url", ] @@ -11057,7 +11396,7 @@ dependencies = [ "candid", "ic-base-types", "serde", - "thiserror 2.0.3", + "thiserror 2.0.11", ] [[package]] @@ -11070,6 +11409,23 @@ dependencies = [ "ic-types", ] +[[package]] +name = "ic-registry-canister-data-provider" +version = "0.9.0" +dependencies = [ + "anyhow", + "candid", + "ic-cdk 0.16.0", + "ic-interfaces-registry", + "ic-nns-common", + "ic-nns-constants", + "ic-registry-canister-client", + "ic-registry-transport", + "ic-stable-structures", + "ic-types", + "itertools 0.12.1", +] + [[package]] name = "ic-registry-client" version = "0.9.0" @@ -11115,7 +11471,7 @@ dependencies = [ "ic-registry-subnet-features", "ic-types", "serde_cbor", - "thiserror 2.0.3", + "thiserror 2.0.11", ] [[package]] @@ -11124,7 +11480,7 @@ version = "0.9.0" dependencies = [ "ic-registry-common-proto-generator", "ic-test-utilities-compare-dirs", - "prost 0.13.3", + "prost 0.13.4", "tempfile", ] @@ -11133,7 +11489,7 @@ name = "ic-registry-common-proto-generator" version = "0.9.0" dependencies = [ "ic-utils-rustfmt", - "prost-build 0.13.3", + "prost-build 0.13.4", ] [[package]] @@ -11164,7 +11520,7 @@ dependencies = [ "ic-registry-transport", "ic-types", "tempfile", - "thiserror 2.0.3", + "thiserror 2.0.11", "tokio", "url", ] @@ -11178,7 +11534,7 @@ dependencies = [ "ic-registry-local-store-artifacts", "ic-sys", "ic-types", - "prost 0.13.3", + "prost 0.13.4", "rand 0.8.5", "tempfile", ] @@ -11201,7 +11557,7 @@ dependencies = [ "ic-registry-transport", "ic-types", "leb128", - "prost 0.13.3", + "prost 0.13.4", "rand 0.8.5", "serde", "tree-deserializer", @@ -11237,7 +11593,7 @@ dependencies = [ "ic-registry-transport", "ic-sys", "ic-types", - "thiserror 2.0.3", + "thiserror 2.0.11", ] [[package]] @@ -11252,7 +11608,7 @@ dependencies = [ name = "ic-registry-replicator" version = "0.9.0" dependencies = [ - "clap 4.5.20", + "clap 4.5.27", "ic-config", "ic-crypto-utils-threshold-sig-der", "ic-http-endpoints-metrics", @@ -11268,7 +11624,7 @@ dependencies = [ "ic-registry-routing-table", "ic-types", "prometheus", - "prost 0.13.3", + "prost 0.13.4", "slog", "tempfile", "tokio", @@ -11319,7 +11675,7 @@ dependencies = [ "ic-registry-keys", "ic-registry-transport-protobuf-generator", "ic-test-utilities-compare-dirs", - "prost 0.13.3", + "prost 0.13.4", "serde", "tempfile", ] @@ -11329,7 +11685,7 @@ name = "ic-registry-transport-protobuf-generator" version = "0.9.0" dependencies = [ "ic-utils-rustfmt", - "prost-build 0.13.3", + "prost-build 0.13.4", ] [[package]] @@ -11337,13 +11693,14 @@ name = "ic-replay" version = "0.9.0" dependencies = [ "candid", - "clap 4.5.20", + "clap 4.5.27", "hex", "ic-artifact-pool", "ic-canister-client", "ic-canister-sandbox-backend-lib", "ic-config", "ic-consensus", + "ic-consensus-dkg", "ic-consensus-utils", "ic-crypto-for-verification-only", "ic-cycles-account-manager", @@ -11372,7 +11729,7 @@ dependencies = [ "ic-test-utilities-types", "ic-types", "icp-ledger", - "prost 0.13.3", + "prost 0.13.4", "rand 0.8.5", "serde", "serde_json", @@ -11380,7 +11737,7 @@ dependencies = [ "slog-async", "tempfile", "tokio", - "tower 0.5.1", + "tower 0.5.2", "url", ] @@ -11390,7 +11747,7 @@ version = "0.9.0" dependencies = [ "assert_cmd", "canister-test", - "clap 4.5.20", + "clap 4.5.27", "criterion", "hex", "ic-artifact-pool", @@ -11398,6 +11755,7 @@ dependencies = [ "ic-btc-consensus", "ic-config", "ic-consensus", + "ic-consensus-dkg", "ic-crypto", "ic-crypto-sha2", "ic-cycles-account-manager", @@ -11459,6 +11817,7 @@ dependencies = [ "ic-artifact-pool", "ic-config", "ic-consensus", + "ic-consensus-dkg", "ic-consensus-manager", "ic-consensus-utils", "ic-crypto-interfaces-sig-verification", @@ -11525,14 +11884,14 @@ dependencies = [ "ic-types", "ic-utils 0.9.0", "maplit", - "prost 0.13.3", + "prost 0.13.4", "rand 0.8.5", "slog", "slog-scope", "tempfile", "tokio", "tonic", - "tower 0.5.1", + "tower 0.5.2", "tracing-subscriber", "wat", ] @@ -11543,7 +11902,7 @@ version = "0.9.0" dependencies = [ "arbitrary", "assert_matches", - "bit-vec", + "bit-vec 0.6.3", "criterion", "criterion-time", "cvt", @@ -11583,8 +11942,8 @@ dependencies = [ "nix 0.24.3", "phantom_newtype", "prometheus", - "proptest", - "prost 0.13.3", + "proptest 1.6.0", + "prost 0.13.4", "rand 0.8.5", "rand_chacha 0.3.1", "rayon", @@ -11602,9 +11961,9 @@ dependencies = [ [[package]] name = "ic-representation-independent-hash" -version = "2.6.0" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08ae59483e377cd9aad94ec339ed1d2583b0d5929cab989328dac2d853b2f570" +checksum = "3643f12824280580d31e47d380f1be23abee29944a1430c3ed22b164ac8e68db" dependencies = [ "leb128", "sha2 0.10.8", @@ -11612,25 +11971,25 @@ dependencies = [ [[package]] name = "ic-response-verification" -version = "2.6.0" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bef02ef84189d61a7d39889b7e9a3ae212d45c3df293513f7b2568027fd08a8" +checksum = "2b97514fada84797baf61a6a29f1c71695798c2628cb6013d97a5dd6ecc26df7" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "candid", "flate2", "hex", - "http 0.2.12", + "http 1.2.0", "ic-cbor", "ic-certificate-verification", - "ic-certification 2.6.0", + "ic-certification 3.0.2", "ic-http-certification", "ic-representation-independent-hash", "leb128", "log", "nom", "sha2 0.10.8", - "thiserror 1.0.68", + "thiserror 1.0.69", "urlencoding", ] @@ -11644,12 +12003,12 @@ dependencies = [ "async-trait", "base64 0.13.1", "candid", - "clap 4.5.20", + "clap 4.5.27", "dfn_candid", "dfn_protobuf", "futures", "hex", - "ic-agent 0.37.1", + "ic-agent", "ic-base-types", "ic-crypto-ed25519", "ic-crypto-sha2", @@ -11669,7 +12028,6 @@ dependencies = [ "ic-limits", "ic-nns-common", "ic-nns-constants", - "ic-nns-governance", "ic-nns-governance-api", "ic-nns-governance-init", "ic-nns-handler-root", @@ -11686,12 +12044,12 @@ dependencies = [ "on_wire", "pocket-ic", "prometheus", - "proptest", - "prost 0.13.3", + "proptest 1.6.0", + "prost 0.13.4", "rand 0.8.5", "rand_chacha 0.3.1", "registry-canister", - "reqwest 0.12.9", + "reqwest 0.12.12", "rolling-file", "rosetta-core", "rusqlite", @@ -11724,7 +12082,7 @@ dependencies = [ "icp-ledger", "nix 0.24.3", "rand 0.8.5", - "reqwest 0.12.9", + "reqwest 0.12.12", "rosetta-core", "serde", "serde_bytes", @@ -11770,7 +12128,7 @@ dependencies = [ "assert_matches", "hex", "ic-canister-sig-creation", - "ic-certification 2.6.0", + "ic-certification 3.0.2", "ic-crypto-internal-types", "ic-crypto-test-utils-canister-sigs", "ic-crypto-test-utils-reproducible-rng", @@ -11791,7 +12149,7 @@ dependencies = [ "candid", "colored", "csv", - "ic-agent 0.37.1", + "ic-agent", "ic-base-types", "ic-nervous-system-agent", "ic-nervous-system-common-test-keys", @@ -11805,7 +12163,7 @@ dependencies = [ "serde", "serde_json", "textplots", - "thiserror 2.0.3", + "thiserror 2.0.11", "tokio", ] @@ -11816,12 +12174,16 @@ dependencies = [ "anyhow", "base64 0.13.1", "candid", - "clap 4.5.20", + "candid-utils", + "clap 4.5.27", + "cycles-minting-canister", + "dfx-core", "futures", "hex", - "ic-agent 0.37.1", + "ic-agent", "ic-base-types", "ic-crypto-sha2", + "ic-management-canister-types", "ic-nervous-system-agent", "ic-nervous-system-common", "ic-nervous-system-common-test-keys", @@ -11830,19 +12192,21 @@ dependencies = [ "ic-nns-common", "ic-nns-constants", "ic-nns-governance-api", - "ic-sns-governance", + "ic-sns-governance-api", "ic-sns-init", "ic-sns-root", "ic-sns-wasm", + "ic-wasm", "itertools 0.12.1", "json-patch", "lazy_static", "pretty_assertions", "serde", + "serde_cbor", "serde_json", "serde_yaml", "tempfile", - "thiserror 2.0.3", + "thiserror 2.0.11", "tokio", ] @@ -11858,7 +12222,7 @@ dependencies = [ "canbench-rs", "candid", "candid_parser", - "clap 4.5.20", + "clap 4.5.27", "comparable", "futures", "hex", @@ -11910,15 +12274,16 @@ dependencies = [ "maplit", "num-traits", "pretty_assertions", - "proptest", - "prost 0.13.3", - "prost-build 0.13.3", + "proptest 1.6.0", + "prost 0.13.4", + "prost-build 0.13.4", "rand 0.8.5", "rand_chacha 0.3.1", "rust_decimal", "rust_decimal_macros", "serde", "serde_bytes", + "serde_json", "strum", "strum_macros", "tempfile", @@ -11932,7 +12297,7 @@ version = "0.9.0" dependencies = [ "bytes", "candid", - "clap 4.5.20", + "clap 4.5.27", "comparable", "ic-base-types", "ic-nervous-system-proto", @@ -11942,7 +12307,7 @@ dependencies = [ "ic-utils 0.9.0", "icp-ledger", "itertools 0.12.1", - "prost 0.13.3", + "prost 0.13.4", "serde", "serde_bytes", "strum", @@ -11976,7 +12341,7 @@ name = "ic-sns-governance-protobuf-generator" version = "0.9.0" dependencies = [ "ic-utils-rustfmt", - "prost-build 0.13.3", + "prost-build 0.13.4", ] [[package]] @@ -12029,7 +12394,7 @@ dependencies = [ "lazy_static", "maplit", "num-traits", - "prost 0.13.3", + "prost 0.13.4", "serde", "serde_yaml", "tempfile", @@ -12040,7 +12405,7 @@ name = "ic-sns-init-protobuf-generator" version = "0.9.0" dependencies = [ "ic-utils-rustfmt", - "prost-build 0.13.3", + "prost-build 0.13.4", ] [[package]] @@ -12095,8 +12460,8 @@ dependencies = [ "on_wire", "pretty-bytes", "pretty_assertions", - "proptest", - "prost 0.13.3", + "proptest 1.6.0", + "prost 0.13.4", "rand 0.8.5", "rust_decimal", "rust_decimal_macros", @@ -12136,7 +12501,7 @@ dependencies = [ "ic-test-utilities-compare-dirs", "icrc-ledger-types", "maplit", - "prost 0.13.3", + "prost 0.13.4", "serde", "tempfile", "tokio", @@ -12147,7 +12512,7 @@ name = "ic-sns-root-protobuf-generator" version = "0.9.0" dependencies = [ "ic-utils-rustfmt", - "prost-build 0.13.3", + "prost-build 0.13.4", ] [[package]] @@ -12190,8 +12555,8 @@ dependencies = [ "lazy_static", "maplit", "pretty_assertions", - "proptest", - "prost 0.13.3", + "proptest 1.6.0", + "prost 0.13.4", "rust_decimal", "rust_decimal_macros", "serde", @@ -12210,7 +12575,7 @@ dependencies = [ "ic-base-types", "ic-nervous-system-proto", "ic-utils 0.9.0", - "prost 0.13.3", + "prost 0.13.4", "serde", "serde_bytes", ] @@ -12220,7 +12585,7 @@ name = "ic-sns-swap-protobuf-generator" version = "0.9.0" dependencies = [ "ic-utils-rustfmt", - "prost-build 0.13.3", + "prost-build 0.13.4", ] [[package]] @@ -12265,7 +12630,7 @@ dependencies = [ "maplit", "num-traits", "on_wire", - "prost 0.13.3", + "prost 0.13.4", "tokio", ] @@ -12316,7 +12681,7 @@ dependencies = [ "icrc-ledger-types", "maplit", "pretty_assertions", - "prost 0.13.3", + "prost 0.13.4", "registry-canister", "serde", "serde_bytes", @@ -12330,14 +12695,14 @@ name = "ic-sns-wasm-protobuf-generator" version = "0.9.0" dependencies = [ "ic-utils-rustfmt", - "prost-build 0.13.3", + "prost-build 0.13.4", ] [[package]] name = "ic-stable-structures" -version = "0.6.5" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03f3044466a69802de74e710dc0300b706a05696a0531c942ca856751a13b0db" +checksum = "b492c5a16455ae78623eaa12ead96dda6c69a83c535b1b00789f19b381c8a24c" dependencies = [ "ic_principal", ] @@ -12347,7 +12712,7 @@ name = "ic-starter" version = "0.9.0" dependencies = [ "anyhow", - "clap 4.5.20", + "clap 4.5.27", "ic-config", "ic-logger", "ic-management-canister-types", @@ -12387,8 +12752,8 @@ dependencies = [ "itertools 0.12.1", "libc", "prometheus", - "proptest", - "prost 0.13.3", + "proptest 1.6.0", + "prost 0.13.4", "scoped_threadpool", "slog", ] @@ -12399,7 +12764,7 @@ version = "0.9.0" dependencies = [ "candid", "ciborium", - "clap 4.5.20", + "clap 4.5.27", "hex", "ic-artifact-pool", "ic-base-types", @@ -12453,7 +12818,7 @@ dependencies = [ "ic-universal-canister", "ic-xnet-payload-builder", "maplit", - "proptest", + "proptest 1.6.0", "rand 0.8.5", "rcgen", "serde", @@ -12465,7 +12830,7 @@ dependencies = [ "tempfile", "tokio", "tokio-util", - "tower 0.5.1", + "tower 0.5.2", "wat", ] @@ -12474,7 +12839,7 @@ name = "ic-state-manager" version = "0.9.0" dependencies = [ "assert_matches", - "bit-vec", + "bit-vec 0.6.3", "criterion", "criterion-time", "crossbeam-channel", @@ -12519,8 +12884,8 @@ dependencies = [ "nix 0.24.3", "parking_lot 0.12.3", "prometheus", - "proptest", - "prost 0.13.3", + "proptest 1.6.0", + "prost 0.13.4", "rand 0.8.5", "rand_chacha 0.3.1", "scoped_threadpool", @@ -12556,10 +12921,10 @@ dependencies = [ "ic-types-test-utils", "mockall", "prometheus", - "prost 0.13.3", + "prost 0.13.4", "rand 0.8.5", "slog", - "thiserror 2.0.3", + "thiserror 2.0.11", "tokio", "tokio-metrics", "tokio-util", @@ -12571,7 +12936,7 @@ dependencies = [ name = "ic-state-tool" version = "0.9.0" dependencies = [ - "clap 4.5.20", + "clap 4.5.27", "hex", "ic-config", "ic-logger", @@ -12581,11 +12946,12 @@ dependencies = [ "ic-registry-subnet-type", "ic-replicated-state", "ic-state-layout", + "ic-state-machine-tests", "ic-state-manager", "ic-sys", "ic-types", "ic-utils 0.9.0", - "prost 0.13.3", + "prost 0.13.4", "slog", "slog-term", "tempfile", @@ -12595,9 +12961,9 @@ dependencies = [ name = "ic-subnet-splitting" version = "0.9.0" dependencies = [ - "clap 4.5.20", + "clap 4.5.27", "hex", - "ic-agent 0.37.1", + "ic-agent", "ic-base-types", "ic-crypto-utils-threshold-sig", "ic-crypto-utils-threshold-sig-der", @@ -12631,10 +12997,10 @@ dependencies = [ "libc", "nix 0.24.3", "phantom_newtype", - "prost 0.13.3", + "prost 0.13.4", "rand 0.8.5", "tempfile", - "thiserror 2.0.3", + "thiserror 2.0.11", "tokio", "wsl", ] @@ -12691,7 +13057,7 @@ dependencies = [ "candid", "canister-test", "chrono", - "clap 4.5.20", + "clap 4.5.27", "config_types", "crossbeam-channel", "cycles-minting-canister", @@ -12704,8 +13070,8 @@ dependencies = [ "http 1.2.0", "humantime", "humantime-serde", - "hyper 1.5.1", - "ic-agent 0.37.1", + "hyper 1.6.0", + "ic-agent", "ic-artifact-pool", "ic-base-types", "ic-btc-interface", @@ -12769,13 +13135,13 @@ dependencies = [ "ic-types", "ic-types-test-utils", "ic-universal-canister", - "ic-utils 0.37.0", + "ic-utils 0.39.3", "ic-wasm-types", "icp-ledger", "icrc-ledger-types", "itertools 0.12.1", "json5", - "k256", + "k256 0.13.4", "k8s-openapi", "kube", "lazy_static", @@ -12789,15 +13155,15 @@ dependencies = [ "once_cell", "pem 1.1.1", "phantom_newtype", - "proptest", - "prost 0.13.3", + "proptest 1.6.0", + "prost 0.13.4", "rand 0.8.5", "rand_chacha 0.3.1", "rayon", "rcgen", "regex", "registry-canister", - "reqwest 0.12.9", + "reqwest 0.12.12", "ring 0.17.8", "rosetta-core", "rsa", @@ -12815,7 +13181,7 @@ dependencies = [ "strum", "strum_macros", "tempfile", - "thiserror 2.0.3", + "thiserror 2.0.11", "time", "tokio", "tokio-util", @@ -12835,6 +13201,7 @@ dependencies = [ "ic-artifact-pool", "ic-config", "ic-consensus", + "ic-consensus-dkg", "ic-consensus-utils", "ic-interfaces", "ic-interfaces-registry", @@ -12912,14 +13279,14 @@ dependencies = [ "nix 0.24.3", "parking_lot 0.12.3", "rand 0.8.5", - "rusty-fork", + "rusty-fork 0.3.0", "serde", "serde_cbor", "slog", - "socket2 0.5.7", + "socket2 0.5.8", "tempfile", "tokio", - "tower 0.5.1", + "tower 0.5.2", "wasmprinter 0.217.0", "wat", ] @@ -12941,7 +13308,7 @@ dependencies = [ "ic-types", "mockall", "phantom_newtype", - "prost 0.13.3", + "prost 0.13.4", "serde", ] @@ -13070,7 +13437,7 @@ version = "0.9.0" dependencies = [ "assert_matches", "ic-protobuf", - "prost 0.13.3", + "prost 0.13.4", "serde", "serde_cbor", "serde_json", @@ -13092,7 +13459,7 @@ dependencies = [ "ic-types", "ic-wasm-types", "mockall", - "proptest", + "proptest 1.6.0", "rand 0.8.5", "rand_chacha 0.3.1", "strum", @@ -13117,7 +13484,6 @@ dependencies = [ name = "ic-test-utilities-types" version = "0.9.0" dependencies = [ - "assert_matches", "bincode", "ic-canister-client-sender", "ic-crypto-ed25519", @@ -13137,7 +13503,7 @@ dependencies = [ "candid", "canister-test", "dfn_candid", - "ic-agent 0.37.1", + "ic-agent", "ic-base-types", "ic-btc-checker", "ic-btc-interface", @@ -13162,7 +13528,7 @@ dependencies = [ "icp-ledger", "icrc-ledger-agent", "icrc-ledger-types", - "k256", + "k256 0.13.4", "rand 0.8.5", "rand_chacha 0.3.1", "registry-canister", @@ -13172,6 +13538,43 @@ dependencies = [ "tokio", ] +[[package]] +name = "ic-tests-cross-chain" +version = "0.9.0" +dependencies = [ + "anyhow", + "candid", + "canister-test", + "dfn_candid", + "futures", + "hex-literal", + "ic-base-types", + "ic-canister-client", + "ic-cketh-minter", + "ic-ethereum-types", + "ic-icrc1-index-ng", + "ic-icrc1-ledger", + "ic-ledger-suite-orchestrator", + "ic-management-canister-types", + "ic-nervous-system-clients", + "ic-nervous-system-common-test-keys", + "ic-nervous-system-root", + "ic-nns-common", + "ic-nns-constants", + "ic-nns-governance-api", + "ic-nns-test-utils", + "ic-registry-subnet-type", + "ic-system-test-driver", + "ic-types", + "ic-wasm-types", + "ic_consensus_system_test_utils", + "ic_consensus_threshold_sig_system_test_utils", + "icrc-ledger-types", + "reqwest 0.12.12", + "serde_json", + "slog", +] + [[package]] name = "ic-tracing" version = "0.9.0" @@ -13185,9 +13588,9 @@ name = "ic-tracing-jaeger-exporter" version = "0.9.0" dependencies = [ "anyhow", - "opentelemetry 0.27.0", + "opentelemetry 0.27.1", "opentelemetry-otlp", - "opentelemetry_sdk 0.27.0", + "opentelemetry_sdk 0.27.1", "tokio", "tracing-opentelemetry 0.28.0", "tracing-subscriber", @@ -13207,37 +13610,20 @@ dependencies = [ [[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 2.6.0", - "leb128", - "serde", - "serde_bytes", - "serde_repr", - "sha2 0.10.8", - "thiserror 1.0.68", -] - -[[package]] -name = "ic-transport-types" -version = "0.39.1" +version = "0.39.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d8789a5c176bb1b925fa58ca97c651a3995d504e76101e93d2a17f558bdcf66" +checksum = "979ee7bee5a67150a4c090fb012c93c294a528b4a867bad9a15cc6d01cb4227f" dependencies = [ "candid", "hex", - "ic-certification 2.6.0", + "ic-certification 3.0.2", "leb128", "serde", "serde_bytes", "serde_cbor", "serde_repr", "sha2 0.10.8", - "thiserror 1.0.68", + "thiserror 2.0.11", ] [[package]] @@ -13271,12 +13657,12 @@ dependencies = [ "once_cell", "phantom_newtype", "pretty_assertions", - "proptest", + "proptest 1.6.0", "proptest-derive", - "prost 0.13.3", + "prost 0.13.4", "rand 0.8.5", "rand_chacha 0.3.1", - "rusty-fork", + "rusty-fork 0.3.0", "serde", "serde_bytes", "serde_cbor", @@ -13284,7 +13670,7 @@ dependencies = [ "serde_with 1.14.0", "strum", "strum_macros", - "thiserror 2.0.3", + "thiserror 2.0.11", "thousands", ] @@ -13294,7 +13680,7 @@ version = "0.9.0" dependencies = [ "ic-protobuf", "ic-types", - "proptest", + "proptest 1.6.0", "strum", ] @@ -13325,14 +13711,14 @@ dependencies = [ [[package]] name = "ic-utils" -version = "0.37.0" +version = "0.39.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fa832296800758c9c921dd1704985ded6b3e6fbc3aee409727eb1f00d69a595" +checksum = "cd4274ab690a646a4fb4105428617e9c622255903aad34183bdc892ad8a7cc48" dependencies = [ "async-trait", "candid", "futures-util", - "ic-agent 0.37.1", + "ic-agent", "once_cell", "semver", "serde", @@ -13340,7 +13726,7 @@ dependencies = [ "sha2 0.10.8", "strum", "strum_macros", - "thiserror 1.0.68", + "thiserror 2.0.11", "time", "tokio", ] @@ -13355,6 +13741,7 @@ version = "0.9.0" dependencies = [ "ic-types", "lru", + "proptest 1.6.0", ] [[package]] @@ -13364,6 +13751,9 @@ version = "0.9.0" [[package]] name = "ic-utils-thread" version = "0.9.0" +dependencies = [ + "crossbeam-channel", +] [[package]] name = "ic-validate-eq" @@ -13400,7 +13790,7 @@ dependencies = [ "ic-types", "mockall", "rand 0.8.5", - "thiserror 2.0.3", + "thiserror 2.0.11", ] [[package]] @@ -13532,12 +13922,12 @@ checksum = "19fabaeecfe37f24b433c62489242fc54503d98d4cc8d0f9ef7544dfdfc0ddcb" dependencies = [ "anyhow", "candid", - "clap 4.5.20", + "clap 4.5.27", "libflate", "rustc-demangle", "serde", "serde_json", - "thiserror 1.0.68", + "thiserror 1.0.69", "walrus", ] @@ -13572,7 +13962,7 @@ dependencies = [ "byte-unit", "candid", "chrono", - "clap 4.5.20", + "clap 4.5.27", "console 0.11.3", "futures", "hex", @@ -13604,14 +13994,14 @@ dependencies = [ name = "ic-xnet-hyper" version = "0.9.0" dependencies = [ - "hyper 1.5.1", - "hyper-rustls 0.27.3", + "hyper 1.6.0", + "hyper-rustls 0.27.5", "hyper-util", "ic-crypto-tls-interfaces", "ic-xnet-uri", "tokio", - "tokio-rustls 0.26.0", - "tower 0.5.1", + "tokio-rustls 0.26.1", + "tower 0.5.2", ] [[package]] @@ -13622,7 +14012,7 @@ dependencies = [ "async-trait", "axum", "http-body-util", - "hyper 1.5.1", + "hyper 1.6.0", "hyper-util", "ic-base-types", "ic-canonical-state", @@ -13662,12 +14052,12 @@ dependencies = [ "mockall", "nix 0.24.3", "prometheus", - "proptest", + "proptest 1.6.0", "rand 0.8.5", - "reqwest 0.12.9", + "reqwest 0.12.12", "slog", "tempfile", - "thiserror 2.0.3", + "thiserror 2.0.11", "tokio", "url", ] @@ -13708,6 +14098,11 @@ version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de254dd67bbd58073e23dc1c8553ba12fa1dc610a19de94ad2bbcd0460c067f" +[[package]] +name = "ic0" +version = "0.24.0-alpha.1" +source = "git+https://github.com/dfinity/cdk-rs?rev=4e287ce51636b0e70768c193da38d2fc5324ea15#4e287ce51636b0e70768c193da38d2fc5324ea15" + [[package]] name = "ic_bls12_381" version = "0.10.0" @@ -13715,8 +14110,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22c65787944f32af084dffd0c68c1e544237b76e215654ddea8cd9f527dd8b69" dependencies = [ "digest 0.10.7", - "ff", - "group", + "ff 0.13.0", + "group 0.13.0", "pairing", "rand_core 0.6.4", "subtle", @@ -13728,9 +14123,11 @@ name = "ic_boundary_node_system_tests" version = "0.9.0" dependencies = [ "anyhow", + "async-trait", "candid", + "canister-test", "certificate_orchestrator_interface", - "ic-agent 0.37.1", + "ic-agent", "ic-base-types", "ic-boundary-nodes-integration-test-common", "ic-boundary-nodes-performance-test-common", @@ -13745,7 +14142,7 @@ dependencies = [ "ic-registry-subnet-type", "ic-system-test-driver", "itertools 0.12.1", - "k256", + "k256 0.13.4", "rand 0.8.5", "rand_chacha 0.3.1", "rate-limits-api", @@ -13774,7 +14171,7 @@ version = "0.9.0" dependencies = [ "anyhow", "futures", - "ic-agent 0.37.1", + "ic-agent", "ic-base-types", "ic-system-test-driver", "ic-types", @@ -13793,7 +14190,7 @@ dependencies = [ "canister-test", "chrono", "futures", - "ic-agent 0.37.1", + "ic-agent", "ic-base-types", "ic-canister-client", "ic-config", @@ -13812,10 +14209,10 @@ dependencies = [ "ic_consensus_threshold_sig_system_test_utils", "leb128", "openssh-keys", - "prost 0.13.3", + "prost 0.13.4", "rand 0.8.5", "registry-canister", - "reqwest 0.12.9", + "reqwest 0.12.12", "rsa", "serde_json", "slog", @@ -13833,7 +14230,7 @@ dependencies = [ "candid", "canister-test", "futures", - "ic-agent 0.37.1", + "ic-agent", "ic-base-types", "ic-crypto-test-utils-reproducible-rng", "ic-management-canister-types", @@ -13850,11 +14247,11 @@ dependencies = [ "ic_consensus_system_test_catch_up_test_common", "ic_consensus_system_test_liveness_test_common", "ic_consensus_system_test_utils", - "k256", + "k256 0.13.4", "rand 0.8.5", "rand_chacha 0.3.1", "registry-canister", - "reqwest 0.12.9", + "reqwest 0.12.12", "serde_cbor", "serde_json", "slog", @@ -13871,7 +14268,7 @@ dependencies = [ "candid", "canister-test", "ed25519-dalek", - "ic-agent 0.37.1", + "ic-agent", "ic-base-types", "ic-canister-client", "ic-config", @@ -13888,7 +14285,7 @@ dependencies = [ "ic-system-test-driver", "ic-types", "ic-types-test-utils", - "k256", + "k256 0.13.4", "registry-canister", "serde", "sha2 0.10.8", @@ -13902,7 +14299,7 @@ version = "0.9.0" dependencies = [ "anyhow", "candid", - "ic-agent 0.37.1", + "ic-agent", "ic-crypto-test-utils-reproducible-rng", "ic-fstrim-tool", "ic-registry-subnet-type", @@ -13910,9 +14307,9 @@ dependencies = [ "ic-types", "ic-universal-canister", "ic_consensus_system_test_utils", - "k256", + "k256 0.13.4", "rand 0.8.5", - "reqwest 0.12.9", + "reqwest 0.12.12", "serde_bytes", "serde_cbor", "slog", @@ -13930,14 +14327,14 @@ dependencies = [ "data-encoding", "serde", "sha2 0.10.8", - "thiserror 1.0.68", + "thiserror 1.0.69", ] [[package]] name = "icp-config" version = "0.9.0" dependencies = [ - "clap 4.5.20", + "clap 4.5.27", "eyre", "ic-config", "ic-replicated-state", @@ -13982,8 +14379,8 @@ dependencies = [ "maplit", "on_wire", "pocket-ic", - "proptest", - "prost 0.13.3", + "proptest 1.6.0", + "prost 0.13.4", "rand 0.8.5", "rand_chacha 0.3.1", "serde", @@ -14000,7 +14397,7 @@ version = "0.9.0" dependencies = [ "anyhow", "candid", - "ic-agent 0.37.1", + "ic-agent", "ic-icrc-rosetta", "ic-icrc-rosetta-client", "ic-ledger-test-utils", @@ -14028,7 +14425,7 @@ dependencies = [ "minicbor", "num-bigint 0.4.6", "num-traits", - "proptest", + "proptest 1.6.0", ] [[package]] @@ -14038,9 +14435,9 @@ dependencies = [ "candid", "ciborium", "hex", - "ic-agent 0.37.1", + "ic-agent", "ic-cbor", - "ic-certification 2.6.0", + "ic-certification 3.0.2", "icrc-ledger-types", "leb128", ] @@ -14067,7 +14464,7 @@ dependencies = [ [[package]] name = "icrc-ledger-types" -version = "0.1.6" +version = "0.1.8" dependencies = [ "assert_matches", "base32", @@ -14080,7 +14477,7 @@ dependencies = [ "minicbor", "num-bigint 0.4.6", "num-traits", - "proptest", + "proptest 1.6.0", "serde", "serde_bytes", "sha2 0.10.8", @@ -14098,7 +14495,7 @@ dependencies = [ "async-trait", "candid", "serde", - "thiserror 1.0.68", + "thiserror 1.0.69", ] [[package]] @@ -14227,7 +14624,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -14257,26 +14654,6 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "idna" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "idna" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "idna" version = "1.0.3" @@ -14309,9 +14686,9 @@ dependencies = [ [[package]] name = "impl-more" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aae21c3177a27788957044151cc2800043d127acaa460a47ebb9b84dfa2c6aa0" +checksum = "e8a5a9a0ff0086c7a148acb942baaabeadf9504d10400b5a05645853729b9cd2" [[package]] name = "impl-rlp" @@ -14333,13 +14710,13 @@ dependencies = [ [[package]] name = "impl-trait-for-tuples" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.96", ] [[package]] @@ -14361,26 +14738,26 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.6.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" dependencies = [ "equivalent", - "hashbrown 0.15.0", + "hashbrown 0.15.2", "serde", ] [[package]] name = "indicatif" -version = "0.17.8" +version = "0.17.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" +checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235" dependencies = [ - "console 0.15.8", - "instant", + "console 0.15.10", "number_prefix", "portable-atomic", - "unicode-width", + "unicode-width 0.2.0", + "web-time", ] [[package]] @@ -14396,7 +14773,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "232929e1d75fe899576a3d5c7416ad0d88dbfbb3c3d6aa00873a7408a50ddb88" dependencies = [ "ahash 0.8.11", - "indexmap 2.6.0", + "indexmap 2.7.1", "is-terminal", "itoa", "log", @@ -14409,22 +14786,22 @@ dependencies = [ [[package]] name = "inferno" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75a5d75fee4d36809e6b021e4b96b686e763d365ffdb03af2bd00786353f84fe" +checksum = "692eda1cc790750b9f5a5e3921ef9c117fd5498b97cfacbc910693e5b29002dc" dependencies = [ "ahash 0.8.11", - "clap 4.5.20", + "clap 4.5.27", "crossbeam-channel", "crossbeam-utils", "dashmap 6.1.0", "env_logger", - "indexmap 2.6.0", + "indexmap 2.7.1", "itoa", "log", "num-format", "once_cell", - "quick-xml 0.37.1", + "quick-xml 0.37.2", "rgb", "str_stack", ] @@ -14434,7 +14811,7 @@ name = "inject-files" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.5.20", + "clap 4.5.27", "partition_tools", "tempfile", "tokio", @@ -14451,13 +14828,14 @@ dependencies = [ [[package]] name = "insta" -version = "1.40.0" +version = "1.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6593a41c7a73841868772495db7dc1e8ecab43bb5c0b6da2059246c4b506ab60" +checksum = "71c1b125e30d93896b365e156c33dadfffab45ee8400afcbba4752f59de08a86" dependencies = [ - "console 0.15.8", - "lazy_static", + "console 0.15.10", "linked-hash-map", + "once_cell", + "pin-project", "similar", ] @@ -14482,14 +14860,14 @@ dependencies = [ "http 1.2.0", "http-body 1.0.1", "http-body-util", - "hyper 1.5.1", - "hyper-rustls 0.27.3", + "hyper 1.6.0", + "hyper-rustls 0.27.5", "hyper-util", "ring 0.17.8", "rustls-pki-types", "serde", "serde_json", - "thiserror 1.0.68", + "thiserror 1.0.69", ] [[package]] @@ -14507,7 +14885,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ - "socket2 0.5.7", + "socket2 0.5.8", "widestring", "windows-sys 0.48.0", "winreg", @@ -14515,9 +14893,12 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.10.1" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +dependencies = [ + "serde", +] [[package]] name = "ipnetwork" @@ -14530,13 +14911,13 @@ dependencies = [ [[package]] name = "is-terminal" -version = "0.4.13" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +checksum = "e19b23d53f35ce9f56aebc7d1bb4e6ac1e9c0db7ac85c8d1760c04379edced37" dependencies = [ "hermit-abi 0.4.0", "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -14552,7 +14933,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ea1dc4bf0fb4904ba83ffdb98af3d9c325274e92e6e295e4151e86c96363e04" dependencies = [ "serde", - "thiserror 1.0.68", + "thiserror 1.0.69", ] [[package]] @@ -14593,9 +14974,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "jni" @@ -14607,7 +14988,7 @@ dependencies = [ "combine", "jni-sys", "log", - "thiserror 1.0.68", + "thiserror 1.0.69", "walkdir", ] @@ -14628,10 +15009,11 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.72" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -14669,13 +15051,14 @@ dependencies = [ "pest_derive", "regex", "serde_json", - "thiserror 1.0.68", + "thiserror 1.0.69", ] [[package]] name = "jsonrpc" -version = "0.12.1" -source = "git+https://github.com/apoelstra/rust-jsonrpc?rev=e42044d#e42044d8e0896317488dfbd65eb5563d76e937fe" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd8d6b3f301ba426b30feca834a2a18d48d5b54e5065496b5c1b05537bee3639" dependencies = [ "base64 0.13.1", "serde", @@ -14684,15 +15067,29 @@ dependencies = [ [[package]] name = "jsonrpc" -version = "0.13.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd8d6b3f301ba426b30feca834a2a18d48d5b54e5065496b5c1b05537bee3639" +checksum = "3662a38d341d77efecb73caf01420cfa5aa63c0253fd7bc05289ef9f6616e1bf" dependencies = [ "base64 0.13.1", + "minreq", "serde", "serde_json", ] +[[package]] +name = "k256" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" +dependencies = [ + "cfg-if 1.0.0", + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", + "sha2 0.10.8", + "sha3", +] + [[package]] name = "k256" version = "0.13.4" @@ -14700,11 +15097,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" dependencies = [ "cfg-if 1.0.0", - "ecdsa", - "elliptic-curve", + "ecdsa 0.16.9", + "elliptic-curve 0.13.8", "once_cell", "sha2 0.10.8", - "signature", + "signature 2.2.0", ] [[package]] @@ -14729,6 +15126,22 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "keyring" +version = "3.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f8fe839464d4e4b37d756d7e910063696af79a7e877282cb1825e4ec5f10833" +dependencies = [ + "byteorder", + "dbus-secret-service", + "linux-keyutils", + "log", + "openssl", + "security-framework 2.11.1", + "security-framework 3.2.0", + "windows-sys 0.59.0", +] + [[package]] name = "kube" version = "0.93.1" @@ -14755,22 +15168,22 @@ dependencies = [ "http 1.2.0", "http-body 1.0.1", "http-body-util", - "hyper 1.5.1", + "hyper 1.6.0", "hyper-http-proxy", - "hyper-rustls 0.27.3", + "hyper-rustls 0.27.5", "hyper-timeout", "hyper-util", "jsonpath-rust", "k8s-openapi", "kube-core", "pem 3.0.4", - "rustls 0.23.19", + "rustls 0.23.21", "rustls-pemfile 2.2.0", "secrecy", "serde", "serde_json", "serde_yaml", - "thiserror 1.0.68", + "thiserror 1.0.69", "tokio", "tokio-util", "tower 0.4.13", @@ -14790,7 +15203,7 @@ dependencies = [ "k8s-openapi", "serde", "serde_json", - "thiserror 1.0.68", + "thiserror 1.0.69", ] [[package]] @@ -14815,7 +15228,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a1cbf952127589f2851ab2046af368fd20645491bb4b376f04b7f94d7a9837b" dependencies = [ "ascii-canvas", - "bit-set", + "bit-set 0.5.3", "diff", "ena", "is-terminal", @@ -14837,7 +15250,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55cb077ad656299f160924eb2912aa147d7339ea7d69e1b5517326fdcec3c1ca" dependencies = [ "ascii-canvas", - "bit-set", + "bit-set 0.5.3", "ena", "itertools 0.11.0", "lalrpop-util 0.20.2", @@ -14867,7 +15280,7 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" dependencies = [ - "regex-automata 0.4.8", + "regex-automata 0.4.9", ] [[package]] @@ -14880,12 +15293,12 @@ checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" name = "launch-single-vm" version = "0.1.0" dependencies = [ - "clap 4.5.20", + "clap 4.5.27", "ic-prep", "ic-registry-subnet-type", "ic-system-test-driver", "ic-types", - "reqwest 0.12.9", + "reqwest 0.12.12", "serde", "slog", "slog-async", @@ -14954,7 +15367,7 @@ dependencies = [ "dfn_http_metrics", "dfn_protobuf", "hex", - "ic-agent 0.37.1", + "ic-agent", "ic-base-types", "ic-canister-log 0.2.0", "ic-cdk 0.16.0", @@ -14978,7 +15391,7 @@ dependencies = [ "minicbor", "num-traits", "on_wire", - "proptest", + "proptest 1.6.0", "serde", "serde_bytes", "serde_cbor", @@ -14989,7 +15402,7 @@ name = "ledger-canister-protobuf-generator" version = "0.9.0" dependencies = [ "ic-utils-rustfmt", - "prost-build 0.13.3", + "prost-build 0.13.4", ] [[package]] @@ -15058,9 +15471,19 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.161" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + +[[package]] +name = "libdbus-sys" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" +checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72" +dependencies = [ + "cc", + "pkg-config", +] [[package]] name = "libflate" @@ -15088,9 +15511,19 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.5" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" +dependencies = [ + "cc", + "winapi 0.3.9", +] + +[[package]] +name = "libloading" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if 1.0.0", "windows-targets 0.52.6", @@ -15098,9 +15531,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.8" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" [[package]] name = "libnss" @@ -15119,9 +15552,9 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "libc", - "redox_syscall 0.5.7", + "redox_syscall 0.5.8", ] [[package]] @@ -15177,9 +15610,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.20" +version = "1.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472" +checksum = "df9b68e50e6e0b26f672573834882eb57759f6db9b3be2ea3c35c91188bb4eaa" dependencies = [ "cc", "libc", @@ -15209,11 +15642,21 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +[[package]] +name = "linux-keyutils" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "761e49ec5fd8a5a463f9b84e877c373d888935b71c6be78f3767fe2ae6bed18e" +dependencies = [ + "bitflags 2.8.0", + "libc", +] + [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux_kernel_command_line" @@ -15221,9 +15664,9 @@ version = "0.9.0" [[package]] name = "litemap" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" [[package]] name = "lmdb-rkv" @@ -15276,7 +15719,7 @@ checksum = "612ed4ea9ce5acfb5d26339302528a5e1e59dfed95e9e11af3c083236ff1d15d" dependencies = [ "libc", "neli", - "thiserror 1.0.68", + "thiserror 1.0.69", "windows-sys 0.48.0", ] @@ -15305,9 +15748,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" [[package]] name = "logos" @@ -15338,7 +15781,7 @@ dependencies = [ "proc-macro2", "quote", "regex-syntax 0.6.29", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -15364,6 +15807,19 @@ dependencies = [ "logos-codegen", ] +[[package]] +name = "loom" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" +dependencies = [ + "cfg-if 1.0.0", + "generator", + "scoped-tls", + "tracing", + "tracing-subscriber", +] + [[package]] name = "lru" version = "0.7.8" @@ -15542,7 +15998,7 @@ dependencies = [ name = "memory_tracker" version = "0.9.0" dependencies = [ - "bit-vec", + "bit-vec 0.6.3", "criterion", "ic-logger", "ic-replicated-state", @@ -15551,7 +16007,7 @@ dependencies = [ "lazy_static", "libc", "nix 0.24.3", - "proptest", + "proptest 1.6.0", "slog", "tempfile", ] @@ -15582,13 +16038,13 @@ dependencies = [ "candid", "canister-test", "dfn_candid", - "ic-agent 0.37.1", + "ic-agent", "ic-base-types", "ic-cdk 0.16.0", "ic-registry-subnet-type", "ic-system-test-driver", "ic-types", - "ic-utils 0.37.0", + "ic-utils 0.39.3", "itertools 0.12.1", "rand 0.8.5", "rand_chacha 0.3.1", @@ -15658,11 +16114,22 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.0" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" +dependencies = [ + "adler2", +] + +[[package]] +name = "minreq" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "36a8e50e917e18a37d500d27d40b7bc7d127e71c0c94fb2d83f43b4afd308390" dependencies = [ - "adler2", + "log", + "serde", + "serde_json", ] [[package]] @@ -15679,11 +16146,10 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ - "hermit-abi 0.3.9", "libc", "log", "wasi", @@ -15698,9 +16164,9 @@ checksum = "d07cbe42e2a8dd41df582fb8e00fc24d920b5561cc301fcb6d14e2e0434b500f" [[package]] name = "mockall" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c28b3fb6d753d28c20e826cd46ee611fda1cf3cde03a443a974043247c065a" +checksum = "39a6bfcc6c8c7eed5ee98b9c3e33adc726054389233e201c95dab2d41a3839d2" dependencies = [ "cfg-if 1.0.0", "downcast", @@ -15712,21 +16178,21 @@ dependencies = [ [[package]] name = "mockall_derive" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "341014e7f530314e9a1fdbc7400b244efea7122662c96bfa248c31da5bfb2020" +checksum = "25ca3004c2efe9011bd4e461bd8256445052b9615405b4f7ea43fc8ca5c20898" dependencies = [ "cfg-if 1.0.0", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] name = "mockito" -version = "1.5.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b34bd91b9e5c5b06338d392463e1318d683cf82ec3d3af4014609be6e2108d" +checksum = "652cd6d169a36eaf9d1e6bce1a221130439a966d7f27858af66a33a66e9c4ee2" dependencies = [ "assert-json-diff", "bytes", @@ -15735,7 +16201,7 @@ dependencies = [ "http 1.2.0", "http-body 1.0.1", "http-body-util", - "hyper 1.5.1", + "hyper 1.6.0", "hyper-util", "log", "rand 0.8.5", @@ -15748,25 +16214,23 @@ dependencies = [ [[package]] name = "moka" -version = "0.12.8" +version = "0.12.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32cf62eb4dd975d2dde76432fb1075c49e3ee2331cf36f1f8fd4b66550d32b6f" +checksum = "a9321642ca94a4282428e6ea4af8cc2ca4eac48ac7a6a4ea8f33f76d0ce70926" dependencies = [ "async-lock", - "async-trait", "crossbeam-channel", "crossbeam-epoch", "crossbeam-utils", - "event-listener", + "event-listener 5.4.0", "futures-util", - "once_cell", + "loom", "parking_lot 0.12.3", - "quanta", + "portable-atomic", "rustc_version", "smallvec", "tagptr", - "thiserror 1.0.68", - "triomphe", + "thiserror 1.0.69", "uuid", ] @@ -15794,6 +16258,23 @@ dependencies = [ "version_check", ] +[[package]] +name = "multer" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" +dependencies = [ + "bytes", + "encoding_rs", + "futures-util", + "http 1.2.0", + "httparse", + "memchr", + "mime", + "spin 0.9.8", + "version_check", +] + [[package]] name = "multimap" version = "0.10.0" @@ -15802,9 +16283,9 @@ checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" [[package]] name = "neli" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1100229e06604150b3becd61a4965d5c70f3be1759544ea7274166f4be41ef43" +checksum = "93062a0dce6da2517ea35f301dfc88184ce18d3601ec786a727a87bf535deca9" dependencies = [ "byteorder", "libc", @@ -15814,9 +16295,9 @@ dependencies = [ [[package]] name = "neli-proc-macros" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c168194d373b1e134786274020dae7fc5513d565ea2ebb9bc9ff17ffb69106d4" +checksum = "0c8034b7fbb6f9455b2a96c19e6edf8dc9fc34c70449938d8ee3b4df363f61fe" dependencies = [ "either", "proc-macro2", @@ -15840,6 +16321,7 @@ dependencies = [ "ic-types", "ic_consensus_system_test_utils", "slog", + "url", ] [[package]] @@ -15870,7 +16352,7 @@ dependencies = [ "canister_http", "cloner-canister-types", "dfn_candid", - "ic-agent 0.37.1", + "ic-agent", "ic-base-types", "ic-cdk 0.16.0", "ic-limits", @@ -15886,12 +16368,12 @@ dependencies = [ "ic-test-utilities", "ic-test-utilities-types", "ic-types", - "ic-utils 0.37.0", + "ic-utils 0.39.3", "proxy_canister", "rand 0.8.5", "rand_chacha 0.3.1", "registry-canister", - "reqwest 0.12.9", + "reqwest 0.12.12", "slog", "tokio", "url", @@ -15908,7 +16390,7 @@ name = "nft_exporter" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.5.20", + "clap 4.5.27", "serde", "serde_json", ] @@ -15924,7 +16406,7 @@ dependencies = [ "serde_path_to_error", "strum", "strum_macros", - "thiserror 1.0.68", + "thiserror 1.0.69", ] [[package]] @@ -15956,7 +16438,7 @@ version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "cfg-if 1.0.0", "libc", "memoffset 0.9.1", @@ -15968,7 +16450,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "cfg-if 1.0.0", "cfg_aliases", "libc", @@ -15986,7 +16468,7 @@ dependencies = [ "cycles_minting", "dfn_candid", "futures", - "ic-agent 0.37.1", + "ic-agent", "ic-base-types", "ic-btc-interface", "ic-canister-client", @@ -16016,9 +16498,9 @@ dependencies = [ "nns_dapp", "num-traits", "on_wire", - "prost 0.13.3", + "prost 0.13.4", "registry-canister", - "reqwest 0.12.9", + "reqwest 0.12.12", "serde_cbor", "slog", "tokio", @@ -16058,7 +16540,7 @@ version = "0.9.0" dependencies = [ "anyhow", "candid", - "ic-agent 0.37.1", + "ic-agent", "ic-base-types", "ic-nns-constants", "ic-registry-canister-api", @@ -16066,7 +16548,7 @@ dependencies = [ "ic-system-test-driver", "ic_consensus_system_test_utils", "indoc", - "k256", + "k256 0.13.4", "registry-canister", "slog", "tokio", @@ -16121,6 +16603,20 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint 0.4.6", + "num-complex", + "num-integer", + "num-iter", + "num-rational 0.4.2", + "num-traits", +] + [[package]] name = "num-bigint" version = "0.2.6" @@ -16161,6 +16657,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -16209,6 +16714,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint 0.4.6", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -16247,7 +16763,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -16267,13 +16783,13 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "object" -version = "0.36.5" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "crc32fast", - "hashbrown 0.15.0", - "indexmap 2.6.0", + "hashbrown 0.15.2", + "indexmap 2.7.1", "memchr", ] @@ -16343,14 +16859,49 @@ dependencies = [ "byteorder", "md-5", "sha2 0.9.9", - "thiserror 1.0.68", + "thiserror 1.0.69", +] + +[[package]] +name = "openssl" +version = "0.10.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5e534d133a060a3c19daec1eb3e98ec6f4685978834f2dbadfe2ec215bab64e" +dependencies = [ + "bitflags 2.8.0", + "cfg-if 1.0.0", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", ] [[package]] name = "openssl-probe" -version = "0.1.5" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-src" +version = "300.4.1+3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +checksum = "faa4eac4138c62414b5622d1b31c5c304f34b406b013c079c2bbc652fdd6678c" +dependencies = [ + "cc", +] [[package]] name = "openssl-sys" @@ -16360,6 +16911,7 @@ checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" dependencies = [ "cc", "libc", + "openssl-src", "pkg-config", "vcpkg", ] @@ -16386,16 +16938,16 @@ dependencies = [ [[package]] name = "opentelemetry" -version = "0.27.0" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3cebff57f7dbd1255b44d8bddc2cebeb0ea677dbaa2e25a3070a91b318f660" +checksum = "ab70038c28ed37b97d8ed414b6429d343a8bbf44c9f79ec854f3a643029ba6d7" dependencies = [ "futures-core", "futures-sink", "js-sys", - "once_cell", "pin-project-lite", - "thiserror 1.0.68", + "thiserror 1.0.69", + "tracing", ] [[package]] @@ -16407,11 +16959,11 @@ dependencies = [ "async-trait", "futures-core", "http 1.2.0", - "opentelemetry 0.27.0", + "opentelemetry 0.27.1", "opentelemetry-proto", - "opentelemetry_sdk 0.27.0", - "prost 0.13.3", - "thiserror 1.0.68", + "opentelemetry_sdk 0.27.1", + "prost 0.13.4", + "thiserror 1.0.69", "tokio", "tonic", "tracing", @@ -16436,9 +16988,9 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6e05acbfada5ec79023c85368af14abd0b307c015e9064d249b2a950ef459a6" dependencies = [ - "opentelemetry 0.27.0", - "opentelemetry_sdk 0.27.0", - "prost 0.13.3", + "opentelemetry 0.27.1", + "opentelemetry_sdk 0.27.1", + "prost 0.13.4", "tonic", ] @@ -16454,7 +17006,7 @@ dependencies = [ "js-sys", "once_cell", "pin-project-lite", - "thiserror 1.0.68", + "thiserror 1.0.69", ] [[package]] @@ -16469,7 +17021,7 @@ dependencies = [ "js-sys", "once_cell", "pin-project-lite", - "thiserror 1.0.68", + "thiserror 1.0.69", "urlencoding", ] @@ -16488,7 +17040,7 @@ dependencies = [ "opentelemetry_api 0.18.0", "percent-encoding", "rand 0.8.5", - "thiserror 1.0.68", + "thiserror 1.0.69", ] [[package]] @@ -16508,26 +17060,25 @@ dependencies = [ "percent-encoding", "rand 0.8.5", "regex", - "thiserror 1.0.68", + "thiserror 1.0.69", ] [[package]] name = "opentelemetry_sdk" -version = "0.27.0" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b742c1cae4693792cc564e58d75a2a0ba29421a34a85b50da92efa89ecb2bc" +checksum = "231e9d6ceef9b0b2546ddf52335785ce41252bc7474ee8ba05bfad277be13ab8" dependencies = [ "async-trait", "futures-channel", "futures-executor", "futures-util", "glob", - "once_cell", - "opentelemetry 0.27.0", + "opentelemetry 0.27.1", "percent-encoding", "rand 0.8.5", "serde_json", - "thiserror 1.0.68", + "thiserror 1.0.69", "tokio", "tokio-stream", "tracing", @@ -16540,14 +17091,14 @@ dependencies = [ "async-trait", "backoff", "candid", - "clap 4.5.20", + "clap 4.5.27", "env-file-reader", "exec", "get_if_addrs", "hex", "http-body-util", - "hyper 1.5.1", - "hyper-rustls 0.27.3", + "hyper 1.6.0", + "hyper-rustls 0.27.5", "hyper-util", "ic-canister-client", "ic-canister-client-sender", @@ -16594,7 +17145,7 @@ dependencies = [ "mockall", "nix 0.24.3", "prometheus", - "prost 0.13.3", + "prost 0.13.4", "rand 0.8.5", "serde", "serde_cbor", @@ -16628,9 +17179,9 @@ dependencies = [ [[package]] name = "ordered-float" -version = "4.5.0" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c65ee1f9701bf938026630b455d5315f490640234259037edb259798b3bcf85e" +checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" dependencies = [ "num-traits", ] @@ -16650,7 +17201,7 @@ dependencies = [ "ic-types", "ic_consensus_system_test_utils", "itertools 0.12.1", - "reqwest 0.12.9", + "reqwest 0.12.12", "serde", "slog", "tokio", @@ -16676,8 +17227,8 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" dependencies = [ - "ecdsa", - "elliptic-curve", + "ecdsa 0.16.9", + "elliptic-curve 0.13.8", "primeorder", "sha2 0.10.8", ] @@ -16688,33 +17239,35 @@ version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81fec4625e73cf41ef4bb6846cafa6d44736525f442ba45e407c4a000a13996f" dependencies = [ - "group", + "group 0.13.0", ] [[package]] name = "parity-scale-codec" -version = "3.6.12" +version = "3.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee" +checksum = "b91c2d9a6a6004e205b7e881856fb1a0f5022d382acc2c01b52185f7b6f65997" dependencies = [ "arrayvec 0.7.6", "bitvec", "byte-slice-cast", + "const_format", "impl-trait-for-tuples", "parity-scale-codec-derive", + "rustversion", "serde", ] [[package]] name = "parity-scale-codec-derive" -version = "3.6.12" +version = "3.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" +checksum = "77555fd9d578b6470470463fded832619a5fec5ad6cbc551fe4d7507ce50cd3a" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.96", ] [[package]] @@ -16766,7 +17319,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall 0.5.7", + "redox_syscall 0.5.8", "smallvec", "windows-targets 0.52.6", ] @@ -16791,12 +17344,32 @@ dependencies = [ "which 6.0.3", ] +[[package]] +name = "password-hash" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" +dependencies = [ + "base64ct", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "paste" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "pbkdf2" version = "0.12.2" @@ -16854,6 +17427,15 @@ dependencies = [ "serde", ] +[[package]] +name = "pem-rfc7468" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d159833a9105500e0398934e205e0773f0b27529557134ecfc51c27646adac" +dependencies = [ + "base64ct", +] + [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -16871,20 +17453,20 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.14" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" +checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" dependencies = [ "memchr", - "thiserror 1.0.68", + "thiserror 2.0.11", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.7.14" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d214365f632b123a47fd913301e14c946c61d1c183ee245fa76eb752e59a02dd" +checksum = "816518421cfc6887a0d62bf441b6ffb4536fcc926395a69e1a85852d4363f57e" dependencies = [ "pest", "pest_generator", @@ -16892,22 +17474,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.14" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb55586734301717aea2ac313f50b2eb8f60d2fc3dc01d190eefa2e625f60c4e" +checksum = "7d1396fd3a870fc7838768d171b4616d5c91f6cc25e377b673d714567d99377b" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] name = "pest_meta" -version = "2.7.14" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b75da2a70cf4d9cb76833c990ac9cd3923c9a8905a8929789ce347c84564d03d" +checksum = "e1e58089ea25d717bfd31fb534e4f3afcc2cc569c70de3e239778991ea3b7dea" dependencies = [ "once_cell", "pest", @@ -16916,9 +17498,9 @@ dependencies = [ [[package]] name = "pest_vm" -version = "2.7.14" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5385573c124b12495734797b8b427832b6a4182ac313c50dd09fe360795840e2" +checksum = "a8151168a80801131f6e0e79d6c84fa337ccd2493c99e59de027354c3e6fca0b" dependencies = [ "pest", "pest_meta", @@ -16931,7 +17513,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.6.0", + "indexmap 2.7.1", ] [[package]] @@ -16956,12 +17538,12 @@ dependencies = [ [[package]] name = "phf" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ "phf_macros", - "phf_shared 0.11.2", + "phf_shared 0.11.3", ] [[package]] @@ -16986,25 +17568,25 @@ dependencies = [ [[package]] name = "phf_generator" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ - "phf_shared 0.11.2", + "phf_shared 0.11.3", "rand 0.8.5", ] [[package]] name = "phf_macros" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" dependencies = [ - "phf_generator 0.11.2", - "phf_shared 0.11.2", + "phf_generator 0.11.3", + "phf_shared 0.11.3", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -17013,16 +17595,16 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" dependencies = [ - "siphasher", + "siphasher 0.3.11", ] [[package]] name = "phf_shared" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" dependencies = [ - "siphasher", + "siphasher 1.0.1", ] [[package]] @@ -17033,29 +17615,29 @@ checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" [[package]] name = "pin-project" -version = "1.1.6" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf123a161dde1e524adf36f90bc5d8d3462824a9c43553ad07a8183161189ec" +checksum = "1e2ec53ad785f4d35dac0adea7f7dc6f1bb277ad84a680c7afefeae05d1f5916" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.6" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4502d8515ca9f32f1fb543d987f63d95a14934883db45bdb48060b6b69257f8" +checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -17071,7 +17653,7 @@ checksum = "122ee1f5a6843bec84fcbd5c6ba3622115337a6b8965b93a61aad347648f4e8d" dependencies = [ "rand 0.8.5", "socket2 0.4.10", - "thiserror 1.0.68", + "thiserror 1.0.69", ] [[package]] @@ -17091,9 +17673,29 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" dependencies = [ - "der", - "pkcs8", - "spki", + "der 0.7.9", + "pkcs8 0.10.2", + "spki 0.7.3", +] + +[[package]] +name = "pkcs11" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3aca6d67e4c8613bfe455599d0233d00735f85df2001f6bfd9bb7ac0496b10af" +dependencies = [ + "libloading 0.5.2", + "num-bigint 0.2.6", +] + +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der 0.6.1", + "spki 0.6.0", ] [[package]] @@ -17102,8 +17704,8 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der", - "spki", + "der 0.7.9", + "spki 0.7.3", ] [[package]] @@ -17157,18 +17759,19 @@ version = "6.0.0" dependencies = [ "backoff", "base64 0.13.1", + "bitcoin 0.28.2", "candid", "candid_parser", "ed25519-dalek", "flate2", "hex", "ic-cdk 0.16.0", - "ic-certification 2.6.0", + "ic-certification 3.0.2", "ic-error-types", - "ic-transport-types 0.37.1", - "k256", + "ic-transport-types", + "k256 0.13.4", "lazy_static", - "reqwest 0.12.9", + "reqwest 0.12.12", "schemars", "serde", "serde_bytes", @@ -17178,7 +17781,7 @@ dependencies = [ "slog", "strum", "strum_macros", - "thiserror 2.0.3", + "thiserror 2.0.11", "tokio", "tracing", "tracing-appender", @@ -17199,11 +17802,11 @@ dependencies = [ "axum-server", "backoff", "base64 0.13.1", - "bitcoin 0.28.2", + "bitcoin 0.32.5", "bitcoincore-rpc", "bytes", "candid", - "clap 4.5.20", + "clap 4.5.27", "ctrlc", "flate2", "form_urlencoded", @@ -17212,9 +17815,9 @@ dependencies = [ "hex", "http 1.2.0", "http-body-util", - "hyper 1.5.1", + "hyper 1.6.0", "hyper-util", - "ic-agent 0.37.1", + "ic-agent", "ic-bn-lib", "ic-boundary", "ic-btc-adapter", @@ -17252,7 +17855,7 @@ dependencies = [ "ic-test-utilities", "ic-test-utilities-registry", "ic-types", - "ic-utils 0.37.0", + "ic-utils 0.39.3", "ic-utils-thread", "ic-validator-ingress-message", "itertools 0.12.1", @@ -17261,7 +17864,7 @@ dependencies = [ "rand 0.8.5", "rcgen", "registry-canister", - "reqwest 0.12.9", + "reqwest 0.12.12", "serde", "serde_cbor", "serde_json", @@ -17272,7 +17875,7 @@ dependencies = [ "tokio", "tokio-util", "tonic", - "tower 0.5.1", + "tower 0.5.2", "tower-http 0.6.2", "tracing", "tracing-appender", @@ -17292,9 +17895,9 @@ dependencies = [ [[package]] name = "polling" -version = "3.7.3" +version = "3.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511" +checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" dependencies = [ "cfg-if 1.0.0", "concurrent-queue", @@ -17316,17 +17919,29 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + [[package]] name = "portable-atomic" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" +checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" [[package]] name = "postcard" -version = "1.0.10" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f7f0a8d620d71c457dd1d47df76bb18960378da56af4527aaa10f515eee732e" +checksum = "170a2601f67cc9dba8edd8c4870b15f71a6a2dc196daec8c83f72b59dff628a8" dependencies = [ "cobs", "embedded-io 0.4.0", @@ -17364,7 +17979,7 @@ dependencies = [ "smallvec", "symbolic-demangle", "tempfile", - "thiserror 1.0.68", + "thiserror 1.0.69", ] [[package]] @@ -17384,9 +17999,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "predicates" -version = "3.1.2" +version = "3.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97" +checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573" dependencies = [ "anstyle", "difflib", @@ -17398,15 +18013,15 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931" +checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" [[package]] name = "predicates-tree" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" +checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" dependencies = [ "predicates-core", "termtree", @@ -17432,7 +18047,7 @@ checksum = "b55c4d17d994b637e2f4daf6e5dc5d660d209d5642377d675d7a1c3ab69fa579" dependencies = [ "arrayvec 0.5.2", "typed-arena", - "unicode-width", + "unicode-width 0.1.14", ] [[package]] @@ -17457,12 +18072,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.24" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "910d41a655dac3b764f1ade94821093d3610248694320cd072303a8eedcf221d" +checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" dependencies = [ "proc-macro2", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -17471,7 +18086,7 @@ version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" dependencies = [ - "elliptic-curve", + "elliptic-curve 0.13.8", ] [[package]] @@ -17540,9 +18155,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.89" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] @@ -17567,7 +18182,7 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "731e0d9356b0c25f16f33b5be79b1c57b562f141ebfcdb0ad8ac2c13a24293b4" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "hex", "lazy_static", "procfs-core", @@ -17580,7 +18195,7 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d3554923a69f4ce04c4a754260c338f505ce22642d3830e049a399fc2059a29" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "hex", ] @@ -17598,7 +18213,7 @@ dependencies = [ "parking_lot 0.12.3", "procfs 0.16.0", "protobuf", - "thiserror 1.0.68", + "thiserror 1.0.69", ] [[package]] @@ -17615,33 +18230,53 @@ dependencies = [ [[package]] name = "proptest" -version = "1.5.0" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01c477819b845fe023d33583ebf10c9f62518c8d79a0960ba5c36d6ac8a55a5b" +dependencies = [ + "bit-set 0.5.3", + "bitflags 1.3.2", + "byteorder", + "lazy_static", + "num-traits", + "quick-error", + "rand 0.6.5", + "rand_chacha 0.1.1", + "rand_xorshift 0.1.1", + "regex-syntax 0.6.29", + "rusty-fork 0.2.2", + "tempfile", +] + +[[package]] +name = "proptest" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" +checksum = "14cae93065090804185d3b75f0bf93b8eeda30c7a9b4a33d3bdb3988d6229e50" dependencies = [ - "bit-set", - "bit-vec", - "bitflags 2.6.0", + "bit-set 0.8.0", + "bit-vec 0.8.0", + "bitflags 2.8.0", "lazy_static", "num-traits", "rand 0.8.5", "rand_chacha 0.3.1", "rand_xorshift 0.3.0", "regex-syntax 0.8.5", - "rusty-fork", + "rusty-fork 0.3.0", "tempfile", "unarray", ] [[package]] name = "proptest-derive" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff7ff745a347b87471d859a377a9a404361e7efc2a971d73424a6d183c0fc77" +checksum = "4ee1c9ac207483d5e7db4940700de86a9aae46ef90c48b57f99fe7edb8345e49" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -17656,12 +18291,12 @@ dependencies = [ [[package]] name = "prost" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0487d90e047de87f984913713b85c601c05609aad5b0df4b4573fbf69aa13f" +checksum = "2c0fef6c4230e4ccf618a35c59d7ede15dea37de8427500f50aff708806e42ec" dependencies = [ "bytes", - "prost-derive 0.13.3", + "prost-derive 0.13.4", ] [[package]] @@ -17681,17 +18316,16 @@ dependencies = [ "prost 0.12.6", "prost-types 0.12.6", "regex", - "syn 2.0.87", + "syn 2.0.96", "tempfile", ] [[package]] name = "prost-build" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c1318b19085f08681016926435853bbf7858f9c082d0999b80550ff5d9abe15" +checksum = "d0f3e5beed80eb580c68e2c600937ac2c4eedabdfd5ef1e5b7ea4f3fba84497b" dependencies = [ - "bytes", "heck 0.5.0", "itertools 0.13.0", "log", @@ -17699,10 +18333,10 @@ dependencies = [ "once_cell", "petgraph", "prettyplease", - "prost 0.13.3", - "prost-types 0.13.3", + "prost 0.13.4", + "prost-types 0.13.4", "regex", - "syn 2.0.87", + "syn 2.0.96", "tempfile", ] @@ -17716,20 +18350,20 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] name = "prost-derive" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5" +checksum = "157c5a9d7ea5c2ed2d9fb8f495b64759f7816c7eaea54ba3978f0d63000162e3" dependencies = [ "anyhow", "itertools 0.13.0", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -17743,11 +18377,11 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4759aa0d3a6232fb8dbdb97b61de2c20047c68aca932c7ed76da9d788508d670" +checksum = "cc2f1e56baa61e93533aebc21af4d2134b70f66275e0fcdf3cbe43d77ff7e8fc" dependencies = [ - "prost 0.13.3", + "prost 0.13.4", ] [[package]] @@ -17775,9 +18409,9 @@ checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" [[package]] name = "psm" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa37f80ca58604976033fae9515a8a2989fc13797d953f7c04fb8fa36a11f205" +checksum = "200b9ff220857e53e184257720a14553b2f4aa02577d2ed9842d45d4b9654810" dependencies = [ "cc", ] @@ -17804,19 +18438,19 @@ dependencies = [ [[package]] name = "publicsuffix" -version = "2.2.3" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96a8c1bda5ae1af7f99a2962e49df150414a43d62404644d98dd5c3a93d07457" +checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf" dependencies = [ - "idna 0.3.0", + "idna 1.0.3", "psl-types", ] [[package]] name = "pulley-interpreter" -version = "27.0.0" +version = "28.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3b8d81cf799e20564931e9867ca32de545188c6ee4c2e0f6e41d32f0c7dc6fb" +checksum = "8324e531de91a3c25021a30fb7862d39cc516b61fbb801176acb5ff279ea887b" dependencies = [ "cranelift-bitset", "log", @@ -17825,9 +18459,9 @@ dependencies = [ [[package]] name = "quanta" -version = "0.12.3" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5167a477619228a0b284fac2674e3c388cba90631d7b7de620e6f1fcd08da5" +checksum = "3bd1fe6824cea6538803de3ff1bc0cf3949024db3d43c9643024bfb33a807c0e" dependencies = [ "crossbeam-utils", "libc", @@ -17855,66 +18489,70 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.37.1" +version = "0.37.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f22f29bdff3987b4d8632ef95fd6424ec7e4e0a57e2f4fc63e489e75357f6a03" +checksum = "165859e9e55f79d67b96c5d96f4e88b6f2695a1972849c15a6a3f5c59fc2c003" dependencies = [ "memchr", ] [[package]] name = "quinn" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684" +checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef" dependencies = [ "bytes", "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash 2.0.0", - "rustls 0.23.19", - "socket2 0.5.7", - "thiserror 1.0.68", + "rustc-hash 2.1.0", + "rustls 0.23.21", + "socket2 0.5.8", + "thiserror 2.0.11", "tokio", "tracing", ] [[package]] name = "quinn-proto" -version = "0.11.8" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" +checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d" dependencies = [ "bytes", + "getrandom", "rand 0.8.5", "ring 0.17.8", - "rustc-hash 2.0.0", - "rustls 0.23.19", + "rustc-hash 2.1.0", + "rustls 0.23.21", + "rustls-pki-types", "slab", - "thiserror 1.0.68", + "thiserror 2.0.11", "tinyvec", "tracing", + "web-time", ] [[package]] name = "quinn-udp" -version = "0.5.5" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fe68c2e9e1a1234e218683dbdf9f9dfcb094113c5ac2b938dfcb9bab4c4140b" +checksum = "1c40286217b4ba3a71d644d752e6a0b71f13f1b6a2c5311acfcbe0c2418ed904" dependencies = [ + "cfg_aliases", "libc", "once_cell", - "socket2 0.5.7", + "socket2 0.5.8", "tracing", "windows-sys 0.59.0", ] [[package]] name = "quote" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] @@ -18096,12 +18734,13 @@ dependencies = [ "candid", "futures", "ic-base-types", - "ic-cdk 0.16.0", - "ic-cdk-macros 0.9.0", + "ic-cdk 0.18.0-alpha.1", + "ic-cdk-macros 0.18.0-alpha.1", "ic-error-types", "ic-types", "rand 0.8.5", "serde", + "serde_bytes", ] [[package]] @@ -18136,9 +18775,9 @@ version = "0.9.0" dependencies = [ "anyhow", "candid", - "clap 4.5.20", - "ic-agent 0.37.1", - "k256", + "clap 4.5.27", + "ic-agent", + "k256 0.13.4", "rate-limits-api", "regex", "serde", @@ -18157,6 +18796,7 @@ dependencies = [ "humantime", "ic-bn-lib", "indoc", + "ipnet", "regex", "serde", "serde_bytes", @@ -18171,8 +18811,9 @@ version = "0.9.0" dependencies = [ "anyhow", "candid", + "candid_parser", "getrandom", - "hex", + "ic-canister-log 0.2.0", "ic-canisters-http-types", "ic-cdk 0.16.0", "ic-cdk-macros 0.9.0", @@ -18188,7 +18829,7 @@ dependencies = [ "serde_cbor", "serde_json", "strum", - "thiserror 2.0.3", + "thiserror 2.0.11", "uuid", ] @@ -18200,16 +18841,16 @@ checksum = "6c1bb13e2dcfa2232ac6887157aad8d9b3fe4ca57f7c8d4938ff5ea9be742300" dependencies = [ "clocksource", "parking_lot 0.12.3", - "thiserror 1.0.68", + "thiserror 1.0.69", ] [[package]] name = "raw-cpuid" -version = "11.2.0" +version = "11.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ab240315c661615f2ee9f0f2cd32d5a7343a84d5ebcccb99d46e6637565e7b0" +checksum = "c6928fa44c097620b706542d428957635951bade7143269085389d42c8a4927e" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", ] [[package]] @@ -18234,9 +18875,9 @@ dependencies = [ [[package]] name = "rcgen" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54077e1872c46788540de1ea3d7f4ccb1983d12f9aa909b234468676c1a36779" +checksum = "75e669e5202259b5314d1ea5397316ad400819437857b90861765f24c4cf80a2" dependencies = [ "pem 3.0.4", "ring 0.17.8", @@ -18266,11 +18907,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", ] [[package]] @@ -18281,31 +18922,32 @@ checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ "getrandom", "libredox", - "thiserror 1.0.68", + "thiserror 1.0.69", ] [[package]] name = "regalloc2" -version = "0.10.2" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12908dbeb234370af84d0579b9f68258a0f67e201412dd9a2814e6f45b2fc0f0" +checksum = "145c1c267e14f20fb0f88aa76a1c5ffec42d592c1d28b3cd9148ae35916158d3" dependencies = [ - "hashbrown 0.14.5", + "allocator-api2", + "bumpalo", + "hashbrown 0.15.2", "log", - "rustc-hash 2.0.0", - "slice-group-by", + "rustc-hash 2.1.0", "smallvec", ] [[package]] name = "regex" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.8", + "regex-automata 0.4.9", "regex-syntax 0.8.5", ] @@ -18320,9 +18962,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -18367,6 +19009,7 @@ dependencies = [ "dfn_core", "dfn_http_metrics", "futures", + "getrandom", "ic-base-types", "ic-canister-client-sender", "ic-cdk 0.16.0", @@ -18384,6 +19027,7 @@ dependencies = [ "ic-management-canister-types", "ic-metrics-encoder", "ic-nervous-system-canisters", + "ic-nervous-system-clients", "ic-nervous-system-common", "ic-nervous-system-common-build-metadata", "ic-nervous-system-common-test-keys", @@ -18416,7 +19060,7 @@ dependencies = [ "leb128", "maplit", "on_wire", - "prost 0.13.3", + "prost 0.13.4", "rand 0.8.5", "rand_distr", "registry-canister-protobuf-generator", @@ -18432,7 +19076,7 @@ name = "registry-canister-protobuf-generator" version = "0.9.0" dependencies = [ "ic-utils-rustfmt", - "prost-build 0.13.3", + "prost-build 0.13.4", ] [[package]] @@ -18455,6 +19099,28 @@ version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" +[[package]] +name = "release-runscript" +version = "0.9.0" +dependencies = [ + "anyhow", + "candid", + "colored", + "futures", + "ic-agent", + "ic-base-types", + "ic-nervous-system-agent", + "ic-nervous-system-clients", + "ic-nervous-system-common-test-keys", + "ic-nns-common", + "ic-nns-constants", + "rgb", + "serde", + "serde_json", + "tempfile", + "tokio", +] + [[package]] name = "rend" version = "0.4.2" @@ -18479,7 +19145,7 @@ dependencies = [ "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", - "hyper 0.14.31", + "hyper 0.14.32", "hyper-rustls 0.24.2", "ipnet", "js-sys", @@ -18509,22 +19175,22 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.9" +version = "0.12.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" +checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" dependencies = [ "base64 0.22.1", "bytes", "futures-channel", "futures-core", "futures-util", - "h2 0.4.6", + "h2 0.4.7", "hickory-resolver", "http 1.2.0", "http-body 1.0.1", "http-body-util", - "hyper 1.5.1", - "hyper-rustls 0.27.3", + "hyper 1.6.0", + "hyper-rustls 0.27.5", "hyper-util", "ipnet", "js-sys", @@ -18535,25 +19201,26 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.19", - "rustls-native-certs 0.8.0", + "rustls 0.23.21", + "rustls-native-certs 0.8.1", "rustls-pemfile 2.2.0", "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper 1.0.1", + "sync_wrapper 1.0.2", "tokio", - "tokio-rustls 0.26.0", + "tokio-rustls 0.26.1", "tokio-socks", "tokio-util", + "tower 0.5.2", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 0.26.6", + "webpki-roots 0.26.7", "windows-registry", ] @@ -18587,6 +19254,17 @@ dependencies = [ "serde", ] +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint 0.4.9", + "hmac", + "zeroize", +] + [[package]] name = "rfc6979" version = "0.4.0" @@ -18728,14 +19406,14 @@ dependencies = [ "anyhow", "candid", "hex", - "ic-agent 0.37.1", + "ic-agent", "ic-crypto-ed25519", "ic-crypto-secp256k1", "ic-types", "icp-ledger", "icrc-ledger-types", "num-bigint 0.4.6", - "proptest", + "proptest 1.6.0", "serde", "serde_bytes", "serde_json", @@ -18750,7 +19428,7 @@ dependencies = [ "assert-json-diff", "canister-test", "dfn_protobuf", - "ic-agent 0.37.1", + "ic-agent", "ic-canister-client", "ic-ledger-canister-blocks-synchronizer-test-utils", "ic-ledger-core", @@ -18783,7 +19461,7 @@ dependencies = [ "canister-test", "dfn_protobuf", "hex", - "ic-agent 0.37.1", + "ic-agent", "ic-base-types", "ic-canister-client", "ic-canister-client-sender", @@ -18802,9 +19480,9 @@ dependencies = [ "icp-ledger", "lazy_static", "on_wire", - "prost 0.13.3", + "prost 0.13.4", "rand 0.8.5", - "reqwest 0.12.9", + "reqwest 0.12.12", "rosetta-core", "serde", "serde_json", @@ -18815,9 +19493,9 @@ dependencies = [ [[package]] name = "rsa" -version = "0.9.6" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" +checksum = "47c75d7c5c6b673e58bf54d8544a9f432e3a925b0e80f7cd3602ab5c50c55519" dependencies = [ "const-oid", "digest 0.10.7", @@ -18825,11 +19503,11 @@ dependencies = [ "num-integer", "num-traits", "pkcs1", - "pkcs8", + "pkcs8 0.10.2", "rand_core 0.6.4", "sha2 0.10.8", - "signature", - "spki", + "signature 2.2.0", + "spki 0.7.3", "subtle", "zeroize", ] @@ -18859,7 +19537,7 @@ dependencies = [ "regex", "relative-path", "rustc_version", - "syn 2.0.87", + "syn 2.0.96", "unicode-ident", ] @@ -18952,9 +19630,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc-hash" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" +checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" [[package]] name = "rustc-hex" @@ -18982,15 +19660,15 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.37" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.6.0", - "errno 0.3.9", + "bitflags 2.8.0", + "errno 0.3.10", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -19021,9 +19699,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.19" +version = "0.23.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1" +checksum = "8f287924602bf649d949c63dc8ac8b235fa5387d394020705b80c4eb597ce5b8" dependencies = [ "brotli 7.0.0", "brotli-decompressor", @@ -19057,8 +19735,8 @@ dependencies = [ "ring 0.17.8", "serde", "serde_json", - "thiserror 2.0.3", - "webpki-roots 0.26.6", + "thiserror 2.0.11", + "webpki-roots 0.26.7", "x509-parser", ] @@ -19072,20 +19750,19 @@ dependencies = [ "rustls-pemfile 2.2.0", "rustls-pki-types", "schannel", - "security-framework", + "security-framework 2.11.1", ] [[package]] name = "rustls-native-certs" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcaf18a4f2be7326cd874a5fa579fae794320a0f388d365dca7e480e55f83f8a" +checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" dependencies = [ "openssl-probe", - "rustls-pemfile 2.2.0", "rustls-pki-types", "schannel", - "security-framework", + "security-framework 3.2.0", ] [[package]] @@ -19108,9 +19785,12 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" +checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" +dependencies = [ + "web-time", +] [[package]] name = "rustls-platform-verifier" @@ -19118,16 +19798,16 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4c7dc240fec5517e6c4eab3310438636cfe6391dfc345ba013109909a90d136" dependencies = [ - "core-foundation", + "core-foundation 0.9.4", "core-foundation-sys", "jni", "log", "once_cell", - "rustls 0.23.19", + "rustls 0.23.21", "rustls-native-certs 0.7.3", "rustls-platform-verifier-android", "rustls-webpki 0.102.8", - "security-framework", + "security-framework 2.11.1", "security-framework-sys", "webpki-root-certs", "windows-sys 0.52.0", @@ -19162,9 +19842,21 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.18" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" + +[[package]] +name = "rusty-fork" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" +checksum = "3dd93264e10c577503e926bd1430193eeb5d21b059148910082245309b424fae" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] [[package]] name = "rusty-fork" @@ -19180,9 +19872,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" [[package]] name = "same-file" @@ -19195,9 +19887,9 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.11.4" +version = "2.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22760a375f81a31817aeaf6f5081e9ccb7ffd7f2da1809a6e3fc82b6656f10d5" +checksum = "346a3b32eba2640d17a9cb5927056b08f3de90f65b72fe09402c2ad07d684d0b" dependencies = [ "cfg-if 1.0.0", "derive_more 1.0.0", @@ -19207,21 +19899,21 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.11.4" +version = "2.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abc61ebe25a5c410c0e245028fc9934bf8fa4817199ef5a24a68092edfd34614" +checksum = "c6630024bf739e2179b91fb424b28898baf819414262c5d376677dbff1fe7ebf" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.96", ] [[package]] name = "schannel" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" dependencies = [ "windows-sys 0.59.0", ] @@ -19233,7 +19925,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92" dependencies = [ "dyn-clone", - "indexmap 2.6.0", + "indexmap 2.7.1", "schemars_derive", "serde", "serde_json", @@ -19248,7 +19940,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -19307,7 +19999,7 @@ dependencies = [ "ic-system-test-driver", "ic_consensus_system_test_utils", "nns_dapp", - "reqwest 0.12.9", + "reqwest 0.12.12", "serde_json", "slog", ] @@ -19318,16 +20010,30 @@ version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct 0.1.1", + "der 0.6.1", + "generic-array", + "pkcs8 0.9.0", + "subtle", + "zeroize", +] + [[package]] name = "sec1" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ - "base16ct", - "der", + "base16ct 0.2.0", + "der 0.7.9", "generic-array", - "pkcs8", + "pkcs8 0.10.2", "subtle", "zeroize", ] @@ -19340,7 +20046,6 @@ checksum = "295642060261c80709ac034f52fca8e5a9fa2c7d341ded5cdb164b7c33768b2a" dependencies = [ "rand 0.6.5", "secp256k1-sys 0.5.2", - "serde", ] [[package]] @@ -19362,7 +20067,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" dependencies = [ "bitcoin_hashes 0.14.0", + "rand 0.8.5", "secp256k1-sys 0.10.1", + "serde", ] [[package]] @@ -19408,19 +20115,32 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.6.0", - "core-foundation", + "bitflags 2.8.0", + "core-foundation 0.9.4", "core-foundation-sys", "libc", "num-bigint 0.4.6", "security-framework-sys", ] +[[package]] +name = "security-framework" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" +dependencies = [ + "bitflags 2.8.0", + "core-foundation 0.10.0", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + [[package]] name = "security-framework-sys" -version = "2.12.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" dependencies = [ "core-foundation-sys", "libc", @@ -19432,7 +20152,7 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4eb30575f3638fc8f6815f448d50cb1a2e255b0897985c8c59f4d37b72a07b06" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "cssparser", "derive_more 0.99.18", "fxhash", @@ -19447,18 +20167,18 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.23" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" dependencies = [ "serde", ] [[package]] name = "serde" -version = "1.0.215" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] @@ -19516,13 +20236,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.215" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -19533,14 +20253,14 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] name = "serde_json" -version = "1.0.132" +version = "1.0.138" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" dependencies = [ "itoa", "memchr", @@ -19568,7 +20288,7 @@ dependencies = [ "futures", "percent-encoding", "serde", - "thiserror 1.0.68", + "thiserror 1.0.69", ] [[package]] @@ -19589,7 +20309,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -19612,7 +20332,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -19674,7 +20394,7 @@ dependencies = [ "darling 0.20.10", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -19683,7 +20403,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.7.1", "itoa", "ryu", "serde", @@ -19704,7 +20424,7 @@ name = "setupos-disable-checks" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.5.20", + "clap 4.5.27", "indoc", "linux_kernel_command_line", "partition_tools", @@ -19718,7 +20438,7 @@ name = "setupos-inject-configuration" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.5.20", + "clap 4.5.27", "config", "partition_tools", "serde", @@ -19734,7 +20454,7 @@ name = "setupos_tool" version = "1.0.0" dependencies = [ "anyhow", - "clap 4.5.20", + "clap 4.5.27", "config", "config_types", "deterministic_ips", @@ -19796,6 +20516,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + [[package]] name = "shlex" version = "1.3.0" @@ -19832,6 +20558,16 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + [[package]] name = "signature" version = "2.2.0" @@ -19850,19 +20586,19 @@ checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" [[package]] name = "similar" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e" +checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" [[package]] name = "simple_asn1" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" +checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" dependencies = [ "num-bigint 0.4.6", "num-traits", - "thiserror 1.0.68", + "thiserror 2.0.11", "time", ] @@ -19892,6 +20628,12 @@ version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + [[package]] name = "slab" version = "0.4.9" @@ -19901,12 +20643,6 @@ dependencies = [ "autocfg 1.4.0", ] -[[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" @@ -20027,7 +20763,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -20036,7 +20772,7 @@ version = "0.9.0" dependencies = [ "anyhow", "candid", - "ic-agent 0.37.1", + "ic-agent", "ic-base-types", "ic-canister-client", "ic-canister-client-sender", @@ -20055,7 +20791,7 @@ dependencies = [ "ic-system-test-driver", "ic-types", "ic-universal-canister", - "ic-utils 0.37.0", + "ic-utils 0.39.3", "ic_consensus_system_test_utils", "icp-ledger", "icrc-ledger-agent", @@ -20095,9 +20831,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", "windows-sys 0.52.0", @@ -20113,7 +20849,7 @@ dependencies = [ "ic-registry-subnet-type", "ic-system-test-driver", "ic-types", - "reqwest 0.12.9", + "reqwest 0.12.12", "slog", ] @@ -20138,6 +20874,16 @@ dependencies = [ "lock_api", ] +[[package]] +name = "spki" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der 0.6.1", +] + [[package]] name = "spki" version = "0.7.3" @@ -20145,7 +20891,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", - "der", + "der 0.7.9", ] [[package]] @@ -20228,6 +20974,18 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "stop-token" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af91f480ee899ab2d9f8435bfdfc14d08a5754bd9d3fef1f1a1c23336aad6c8b" +dependencies = [ + "async-channel 1.9.0", + "cfg-if 1.0.0", + "futures-core", + "pin-project-lite", +] + [[package]] name = "str_stack" version = "0.1.0" @@ -20281,7 +21039,7 @@ dependencies = [ "proc-macro2", "quote", "structmeta-derive 0.2.0", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -20293,7 +21051,7 @@ dependencies = [ "proc-macro2", "quote", "structmeta-derive 0.3.0", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -20304,7 +21062,7 @@ checksum = "a60bcaff7397072dca0017d1db428e30d5002e00b6847703e2e42005c95fbe00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -20315,7 +21073,7 @@ checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -20337,7 +21095,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -20365,9 +21123,9 @@ checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" [[package]] name = "symbolic-common" -version = "12.12.0" +version = "12.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "366f1b4c6baf6cfefc234bbd4899535fca0b06c74443039a73f6dfb2fad88d77" +checksum = "13a4dfe4bbeef59c1f32fc7524ae7c95b9e1de5e79a43ce1604e181081d71b0c" dependencies = [ "debugid", "memmap2", @@ -20377,9 +21135,9 @@ dependencies = [ [[package]] name = "symbolic-demangle" -version = "12.12.0" +version = "12.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aba05ba5b9962ea5617baf556293720a8b2d0a282aa14ee4bf10e22efc7da8c8" +checksum = "98cf6a95abff97de4d7ff3473f33cacd38f1ddccad5c1feab435d6760300e3b6" dependencies = [ "rustc-demangle", "symbolic-common", @@ -20398,27 +21156,15 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.87" +version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] -[[package]] -name = "syn_derive" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.87", -] - [[package]] name = "sync-with-released-nervous-system-wasms" version = "0.9.0" @@ -20427,7 +21173,7 @@ dependencies = [ "candid", "colored", "futures", - "ic-agent 0.37.1", + "ic-agent", "ic-base-types", "ic-nervous-system-agent", "ic-nervous-system-clients", @@ -20449,9 +21195,9 @@ checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" [[package]] name = "sync_wrapper" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" dependencies = [ "futures-core", ] @@ -20464,7 +21210,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -20474,7 +21220,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.4", "system-configuration-sys", ] @@ -20495,20 +21241,20 @@ dependencies = [ "anyhow", "async-trait", "axum", - "clap 4.5.20", + "clap 4.5.27", "http 1.2.0", "itertools 0.12.1", - "reqwest 0.12.9", - "thiserror 2.0.3", + "reqwest 0.12.12", + "thiserror 2.0.11", "tokio", "url", ] [[package]] name = "systemstat" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a24aec24a9312c83999a28e3ef9db7e2afd5c64bf47725b758cdc1cafd5b0bd2" +checksum = "668a4db78b439df482c238f559e4ea869017f9e62ef0a059c8bfcd841a4df544" dependencies = [ "bytesize", "lazy_static", @@ -20552,9 +21298,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tar" -version = "0.4.42" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ff6c40d3aedb5e06b57c6f669ad17ab063dd1e63d977c6a88e7f4dfa4f04020" +checksum = "c65998313f8e17d0d553d28f91a0df93e4dbbbf770279c7bc21ca0f09ea1a1f6" dependencies = [ "filetime", "libc", @@ -20583,7 +21329,7 @@ dependencies = [ "serde", "static_assertions", "tarpc-plugins", - "thiserror 1.0.68", + "thiserror 1.0.69", "tokio", "tokio-serde", "tokio-util", @@ -20604,12 +21350,13 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.13.0" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" dependencies = [ "cfg-if 1.0.0", "fastrand", + "getrandom", "once_cell", "rustix", "windows-sys 0.59.0", @@ -20667,9 +21414,9 @@ dependencies = [ [[package]] name = "termtree" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" +checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" [[package]] name = "test-strategy" @@ -20680,7 +21427,7 @@ dependencies = [ "proc-macro2", "quote", "structmeta 0.2.0", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -20692,7 +21439,7 @@ dependencies = [ "proc-macro2", "quote", "structmeta 0.3.0", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -20701,7 +21448,7 @@ version = "0.9.0" dependencies = [ "anyhow", "candid", - "ic-agent 0.37.1", + "ic-agent", "ic-prep", "ic-regedit", "ic-registry-local-store", @@ -20732,7 +21479,7 @@ dependencies = [ "ic_consensus_system_test_utils", "nns_dapp", "os_qualification_utils", - "reqwest 0.12.9", + "reqwest 0.12.12", "serde", "serde_json", "slog", @@ -20758,42 +21505,42 @@ checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" [[package]] name = "thiserror" -version = "1.0.68" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02dd99dc800bbb97186339685293e1cc5d9df1f8fae2d0aecd9ff1c77efea892" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl 1.0.68", + "thiserror-impl 1.0.69", ] [[package]] name = "thiserror" -version = "2.0.3" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" dependencies = [ - "thiserror-impl 2.0.3", + "thiserror-impl 2.0.11", ] [[package]] name = "thiserror-impl" -version = "1.0.68" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7c61ec9a6f64d2793d8a45faba21efbe3ced62a886d44c36a009b2b519b4c7e" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] name = "thiserror-impl" -version = "2.0.3" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -20854,9 +21601,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.36" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" dependencies = [ "deranged", "itoa", @@ -20877,14 +21624,33 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" dependencies = [ "num-conv", "time-core", ] +[[package]] +name = "tiny-bip39" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62cc94d358b5a1e84a5cb9109f559aa3c4d634d2b1b4de3d0fa4adc7c78e2861" +dependencies = [ + "anyhow", + "hmac", + "once_cell", + "pbkdf2 0.11.0", + "rand 0.8.5", + "rustc-hash 1.1.0", + "sha2 0.10.8", + "thiserror 1.0.69", + "unicode-normalization", + "wasm-bindgen", + "zeroize", +] + [[package]] name = "tiny-keccak" version = "2.0.2" @@ -20916,9 +21682,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8" dependencies = [ "tinyvec_macros", ] @@ -20933,6 +21699,7 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" name = "tla_instrumentation" version = "0.9.0" dependencies = [ + "async-trait", "candid", "local_key", "sha2 0.10.8", @@ -20968,23 +21735,23 @@ checksum = "8d9ef545650e79f30233c0003bcc2504d7efac6dad25fca40744de773fe2049c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] name = "tokio" -version = "1.42.0" +version = "1.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" +checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" dependencies = [ "backtrace", "bytes", "libc", - "mio 1.0.2", + "mio 1.0.3", "parking_lot 0.12.3", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.7", + "socket2 0.5.8", "tokio-macros", "windows-sys 0.52.0", ] @@ -21001,13 +21768,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -21045,12 +21812,11 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" dependencies = [ - "rustls 0.23.19", - "rustls-pki-types", + "rustls 0.23.21", "tokio", ] @@ -21078,15 +21844,15 @@ checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f" dependencies = [ "either", "futures-util", - "thiserror 1.0.68", + "thiserror 1.0.69", "tokio", ] [[package]] name = "tokio-stream" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" dependencies = [ "futures-core", "pin-project-lite", @@ -21147,7 +21913,7 @@ version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.7.1", "toml_datetime", "winnow", ] @@ -21163,17 +21929,17 @@ dependencies = [ "axum", "base64 0.22.1", "bytes", - "h2 0.4.6", + "h2 0.4.7", "http 1.2.0", "http-body 1.0.1", "http-body-util", - "hyper 1.5.1", + "hyper 1.6.0", "hyper-timeout", "hyper-util", "percent-encoding", "pin-project", - "prost 0.13.3", - "socket2 0.5.7", + "prost 0.13.4", + "socket2 0.5.8", "tokio", "tokio-stream", "tower 0.4.13", @@ -21190,10 +21956,10 @@ checksum = "9557ce109ea773b399c9b9e5dca39294110b74f1f342cb347a80d1fce8c26a11" dependencies = [ "prettyplease", "proc-macro2", - "prost-build 0.13.3", - "prost-types 0.13.3", + "prost-build 0.13.4", + "prost-types 0.13.4", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -21218,17 +21984,17 @@ dependencies = [ [[package]] name = "tower" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", "hdrhistogram", - "indexmap 2.6.0", + "indexmap 2.7.1", "pin-project-lite", "slab", - "sync_wrapper 0.1.2", + "sync_wrapper 1.0.2", "tokio", "tokio-util", "tower-layer", @@ -21243,7 +22009,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" dependencies = [ "base64 0.21.7", - "bitflags 2.6.0", + "bitflags 2.8.0", "bytes", "http 1.2.0", "http-body 1.0.1", @@ -21262,7 +22028,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697" dependencies = [ "async-compression", - "bitflags 2.6.0", + "bitflags 2.8.0", "bytes", "futures-core", "http 1.2.0", @@ -21271,7 +22037,7 @@ dependencies = [ "pin-project-lite", "tokio", "tokio-util", - "tower 0.5.1", + "tower 0.5.2", "tower-layer", "tower-service", "tracing", @@ -21327,8 +22093,8 @@ dependencies = [ "governor", "http 1.2.0", "pin-project", - "thiserror 1.0.68", - "tower 0.5.1", + "thiserror 1.0.69", + "tower 0.5.2", "tracing", ] @@ -21351,7 +22117,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" dependencies = [ "crossbeam-channel", - "thiserror 1.0.68", + "thiserror 1.0.69", "time", "tracing-subscriber", ] @@ -21364,7 +22130,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -21420,8 +22186,8 @@ checksum = "97a971f6058498b5c0f1affa23e7ea202057a7301dbff68e968b2d578bcbd053" dependencies = [ "js-sys", "once_cell", - "opentelemetry 0.27.0", - "opentelemetry_sdk 0.27.0", + "opentelemetry 0.27.1", + "opentelemetry_sdk 0.27.1", "smallvec", "tracing", "tracing-core", @@ -21490,7 +22256,7 @@ dependencies = [ "ic-crypto-tree-hash", "leb128", "maplit", - "proptest", + "proptest 1.6.0", "proptest-derive", "serde", ] @@ -21504,12 +22270,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "triomphe" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859eb650cfee7434994602c3a68b25d77ad9e68c8a6cd491616ef86661382eb3" - [[package]] name = "trust-dns-proto" version = "0.22.0" @@ -21528,7 +22288,7 @@ dependencies = [ "lazy_static", "rand 0.8.5", "smallvec", - "thiserror 1.0.68", + "thiserror 1.0.69", "tinyvec", "tokio", "tracing", @@ -21549,7 +22309,7 @@ dependencies = [ "parking_lot 0.12.3", "resolv-conf", "smallvec", - "thiserror 1.0.68", + "thiserror 1.0.69", "tokio", "tracing", "trust-dns-proto", @@ -21575,7 +22335,7 @@ dependencies = [ "log", "rand 0.8.5", "sha1", - "thiserror 1.0.68", + "thiserror 1.0.69", "url", "utf-8", ] @@ -21632,11 +22392,10 @@ dependencies = [ [[package]] name = "ulid" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04f903f293d11f31c0c29e4148f6dc0d033a7f80cebc0282bea147611667d289" +checksum = "f294bff79170ed1c5633812aff1e565c35d993a36e757f9bc0accf5eec4e6045" dependencies = [ - "getrandom", "rand 0.8.5", "web-time", ] @@ -21649,21 +22408,21 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicase" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicode-bidi" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" [[package]] name = "unicode-normalization" @@ -21686,6 +22445,12 @@ 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" @@ -21742,9 +22507,9 @@ dependencies = [ [[package]] name = "url" -version = "2.5.3" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna 1.0.3", @@ -21797,9 +22562,9 @@ dependencies = [ [[package]] name = "uuid" -version = "1.11.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b" dependencies = [ "getrandom", "serde", @@ -21807,9 +22572,9 @@ dependencies = [ [[package]] name = "valuable" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "vcpkg" @@ -21834,7 +22599,7 @@ dependencies = [ "chrono", "lalrpop 0.20.2", "once_cell", - "ordered-float 4.5.0", + "ordered-float 4.6.0", "regex", "serde", "serde_json", @@ -21856,7 +22621,7 @@ dependencies = [ name = "vsock_guest" version = "1.0.0" dependencies = [ - "clap 4.5.20", + "clap 4.5.27", "vsock_lib", ] @@ -21873,7 +22638,7 @@ version = "1.0.0" dependencies = [ "anyhow", "regex", - "reqwest 0.12.9", + "reqwest 0.12.12", "rusb", "serde", "serde_json", @@ -21957,11 +22722,11 @@ dependencies = [ "futures-util", "headers 0.3.9", "http 0.2.12", - "hyper 0.14.31", + "hyper 0.14.32", "log", "mime", "mime_guess", - "multer", + "multer 2.1.0", "percent-encoding", "pin-project", "rustls-pemfile 2.2.0", @@ -21985,47 +22750,48 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if 1.0.0", "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.45" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if 1.0.0", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -22033,22 +22799,25 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "wasm-encoder" @@ -22071,19 +22840,29 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.219.1" +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-encoder" +version = "0.224.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29cbbd772edcb8e7d524a82ee8cef8dd046fc14033796a754c3ad246d019fa54" +checksum = "b7249cf8cb0c6b9cb42bce90c0a5feb276fbf963fa385ff3d818ab3d90818ed6" dependencies = [ "leb128", - "wasmparser 0.219.1", + "wasmparser 0.224.0", ] [[package]] name = "wasm-streams" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e072d4e72f700fb3443d8fe94a39315df013eef1104903cdb0a2abd322bbecd" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" dependencies = [ "futures-util", "js-sys", @@ -22099,9 +22878,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d28bc49ba1e5c5b61ffa7a2eace10820443c4b7d1c0b144109261d14570fdf8" dependencies = [ "ahash 0.8.11", - "bitflags 2.6.0", + "bitflags 2.8.0", "hashbrown 0.14.5", - "indexmap 2.6.0", + "indexmap 2.7.1", "semver", "serde", ] @@ -22113,27 +22892,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca917a21307d3adf2b9857b94dd05ebf8496bdcff4437a9b9fb3899d3e6c74e7" dependencies = [ "ahash 0.8.11", - "bitflags 2.6.0", + "bitflags 2.8.0", "hashbrown 0.14.5", - "indexmap 2.6.0", + "indexmap 2.7.1", "semver", "serde", ] [[package]] name = "wasmparser" -version = "0.219.1" +version = "0.221.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c771866898879073c53b565a6c7b49953795159836714ac56a5befb581227c5" +checksum = "9845c470a2e10b61dd42c385839cdd6496363ed63b5c9e420b5488b77bd22083" dependencies = [ - "ahash 0.8.11", - "bitflags 2.6.0", - "hashbrown 0.14.5", - "indexmap 2.6.0", + "bitflags 2.8.0", + "hashbrown 0.15.2", + "indexmap 2.7.1", "semver", "serde", ] +[[package]] +name = "wasmparser" +version = "0.224.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65881a664fdd43646b647bb27bf186ab09c05bf56779d40aed4c6dce47d423f5" +dependencies = [ + "bitflags 2.8.0", + "indexmap 2.7.1", + "semver", +] + [[package]] name = "wasmprinter" version = "0.217.0" @@ -22147,28 +22936,28 @@ dependencies = [ [[package]] name = "wasmprinter" -version = "0.219.1" +version = "0.221.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "228cdc1f30c27816da225d239ce4231f28941147d34713dee8f1fff7cb330e54" +checksum = "a80742ff1b9e6d8c231ac7c7247782c6fc5bce503af760bca071811e5fc9ee56" dependencies = [ "anyhow", "termcolor", - "wasmparser 0.219.1", + "wasmparser 0.221.2", ] [[package]] name = "wasmtime" -version = "27.0.0" +version = "28.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b79302e3e084713249cc5622e8608e7410afdeeea8c8026d04f491d1fab0b4b" +checksum = "edd30973c65eceb0f37dfcc430d83abd5eb24015fdfcab6912f52949287e04f0" dependencies = [ "anyhow", - "bitflags 2.6.0", + "bitflags 2.8.0", "bumpalo", "cc", "cfg-if 1.0.0", "hashbrown 0.14.5", - "indexmap 2.6.0", + "indexmap 2.7.1", "libc", "libm", "log", @@ -22187,36 +22976,38 @@ dependencies = [ "smallvec", "sptr", "target-lexicon", - "wasmparser 0.219.1", + "wasmparser 0.221.2", "wasmtime-asm-macros", "wasmtime-component-macro", "wasmtime-cranelift", "wasmtime-environ", + "wasmtime-fiber", "wasmtime-jit-icache-coherence", "wasmtime-slab", "wasmtime-versioned-export-macros", + "wasmtime-winch", "windows-sys 0.59.0", ] [[package]] name = "wasmtime-asm-macros" -version = "27.0.0" +version = "28.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe53a24e7016a5222875d8ca3ad6024b464465985693c42098cd0bb710002c28" +checksum = "c6c21dd30d1f3f93ee390ac1a7ec304ecdbfdab6390e1add41a1f52727b0992b" dependencies = [ "cfg-if 1.0.0", ] [[package]] name = "wasmtime-component-macro" -version = "27.0.0" +version = "28.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e118acbd2bc09b32ad8606bc7cef793bf5019c1b107772e64dc6c76b5055d40b" +checksum = "9f948a6ef3119d52c9f12936970de28ddf3f9bea04bc65571f4a92d2e5ab38f4" dependencies = [ "anyhow", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", "wasmtime-component-util", "wasmtime-wit-bindgen", "wit-parser", @@ -22224,15 +23015,15 @@ dependencies = [ [[package]] name = "wasmtime-component-util" -version = "27.0.0" +version = "28.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a6db4f3ee18c699629eabb9c64e77efe5a93a5137f098db7cab295037ba41c2" +checksum = "b9275aa01ceaaa2fa6c0ecaa5267518d80b9d6e9ae7c7ea42f4c6e073e6a69ef" [[package]] name = "wasmtime-cranelift" -version = "27.0.0" +version = "28.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b87e6c78f562b50aff1afd87ff32a57e241424c846c1c8f3c5fd352d2d62906" +checksum = "0701a44a323267aae4499672dae422b266cee3135a23b640972ec8c0e10a44a2" dependencies = [ "anyhow", "cfg-if 1.0.0", @@ -22247,23 +23038,23 @@ dependencies = [ "object", "smallvec", "target-lexicon", - "thiserror 1.0.68", - "wasmparser 0.219.1", + "thiserror 1.0.69", + "wasmparser 0.221.2", "wasmtime-environ", "wasmtime-versioned-export-macros", ] [[package]] name = "wasmtime-environ" -version = "27.0.0" +version = "28.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c25bfeaa16432d59a0706e2463d315ef4c9ebcfaf5605670b99d46373bdf9f27" +checksum = "264c968c1b81d340355ece2be0bc31a10f567ccb6ce08512c3b7d10e26f3cbe5" dependencies = [ "anyhow", "cranelift-bitset", "cranelift-entity", "gimli 0.31.1", - "indexmap 2.6.0", + "indexmap 2.7.1", "log", "object", "postcard", @@ -22271,16 +23062,31 @@ dependencies = [ "serde_derive", "smallvec", "target-lexicon", - "wasm-encoder 0.219.1", - "wasmparser 0.219.1", - "wasmprinter 0.219.1", + "wasm-encoder 0.221.2", + "wasmparser 0.221.2", + "wasmprinter 0.221.2", +] + +[[package]] +name = "wasmtime-fiber" +version = "28.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78505221fd5bd7b07b4e1fa2804edea49dc231e626ad6861adc8f531812973e6" +dependencies = [ + "anyhow", + "cc", + "cfg-if 1.0.0", + "rustix", + "wasmtime-asm-macros", + "wasmtime-versioned-export-macros", + "windows-sys 0.59.0", ] [[package]] name = "wasmtime-jit-icache-coherence" -version = "27.0.0" +version = "28.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91b218a92866f74f35162f5d03a4e0f62cd0e1cc624285b1014275e5d4575fad" +checksum = "9bedb677ca1b549d98f95e9e1f9251b460090d99a2c196a0614228c064bf2e59" dependencies = [ "anyhow", "cfg-if 1.0.0", @@ -22290,30 +23096,47 @@ dependencies = [ [[package]] name = "wasmtime-slab" -version = "27.0.0" +version = "28.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d5f8acf677ee6b3b8ba400dd9753ea4769e56a95c4b30b045ac6d2d54b2f8ea" +checksum = "564905638c132c275d365c1fa074f0b499790568f43148d29de84ccecfb5cb31" [[package]] name = "wasmtime-versioned-export-macros" -version = "27.0.0" +version = "28.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df09be00c38f49172ca9936998938476e3f2df782673a39ae2ef9fb0838341b6" +checksum = "1e91092e6cf77390eeccee273846a9327f3e8f91c3c6280f60f37809f0e62d29" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", +] + +[[package]] +name = "wasmtime-winch" +version = "28.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b111d909dc604c741bd8ac2f4af373eaa5c68c34b5717271bcb687688212cef8" +dependencies = [ + "anyhow", + "cranelift-codegen", + "gimli 0.31.1", + "object", + "target-lexicon", + "wasmparser 0.221.2", + "wasmtime-cranelift", + "wasmtime-environ", + "winch-codegen", ] [[package]] name = "wasmtime-wit-bindgen" -version = "27.0.0" +version = "28.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf3963c9c29df91564d8bd181eb00d0dbaeafa1b2a01e15952bb7391166b704e" +checksum = "5f38f7a5eb2f06f53fe943e7fb8bf4197f7cf279f1bc52c0ce56e9d3ffd750a4" dependencies = [ "anyhow", "heck 0.5.0", - "indexmap 2.6.0", + "indexmap 2.7.1", "wit-parser", ] @@ -22326,37 +23149,37 @@ dependencies = [ "bumpalo", "leb128", "memchr", - "unicode-width", + "unicode-width 0.1.14", "wasm-encoder 0.212.0", ] [[package]] name = "wast" -version = "219.0.1" +version = "224.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f79a9d9df79986a68689a6b40bcc8d5d40d807487b235bebc2ac69a242b54a1" +checksum = "d722a51e62b669d17e5a9f6bc8ec210178b37d869114355aa46989686c5c6391" dependencies = [ "bumpalo", "leb128", "memchr", - "unicode-width", - "wasm-encoder 0.219.1", + "unicode-width 0.2.0", + "wasm-encoder 0.224.0", ] [[package]] name = "wat" -version = "1.219.1" +version = "1.224.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bc3cf014fb336883a411cd662f987abf6a1d2a27f2f0008616a0070bbf6bd0d" +checksum = "71dece6a7dd5bcbcf8d256606c7fb3faa36286d46bf3f98185407719a5ceede2" dependencies = [ - "wast 219.0.1", + "wast 224.0.0", ] [[package]] name = "web-sys" -version = "0.3.72" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", @@ -22374,9 +23197,9 @@ dependencies = [ [[package]] name = "webpki-root-certs" -version = "0.26.6" +version = "0.26.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c6dfa3ac045bc517de14c7b1384298de1dbd229d38e08e169d9ae8c170937c" +checksum = "9cd5da49bdf1f30054cfe0b8ce2958b8fbeb67c4d82c8967a598af481bef255c" dependencies = [ "rustls-pki-types", ] @@ -22389,9 +23212,9 @@ checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "webpki-roots" -version = "0.26.6" +version = "0.26.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" +checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" dependencies = [ "rustls-pki-types", ] @@ -22475,6 +23298,33 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "winch-codegen" +version = "28.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6232f40a795be2ce10fc761ed3b403825126a60d12491ac556ea104a932fd18a" +dependencies = [ + "anyhow", + "cranelift-codegen", + "gimli 0.31.1", + "regalloc2", + "smallvec", + "target-lexicon", + "wasmparser 0.221.2", + "wasmtime-cranelift", + "wasmtime-environ", +] + +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core 0.58.0", + "windows-targets 0.52.6", +] + [[package]] name = "windows-core" version = "0.52.0" @@ -22484,6 +23334,41 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + [[package]] name = "windows-registry" version = "0.2.0" @@ -22664,9 +23549,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.20" +version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +checksum = "ad699df48212c6cc6eb4435f35500ac6fd3b9913324f938aea302022ce19d310" dependencies = [ "memchr", ] @@ -22689,20 +23574,20 @@ checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" [[package]] name = "wit-parser" -version = "0.219.1" +version = "0.221.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a86f669283257e8e424b9a4fc3518e3ade0b95deb9fbc0f93a1876be3eda598" +checksum = "fbe1538eea6ea5ddbe5defd0dc82539ad7ba751e1631e9185d24a931f0a5adc8" dependencies = [ "anyhow", "id-arena", - "indexmap 2.6.0", + "indexmap 2.7.1", "log", "semver", "serde", "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.219.1", + "wasmparser 0.221.2", ] [[package]] @@ -22756,10 +23641,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94" dependencies = [ "const-oid", - "der", + "der 0.7.9", "sha1", - "signature", - "spki", + "signature 2.2.0", + "spki 0.7.3", "tls_codec", ] @@ -22776,15 +23661,15 @@ dependencies = [ "nom", "oid-registry", "rusticata-macros", - "thiserror 1.0.68", + "thiserror 1.0.69", "time", ] [[package]] name = "xattr" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" +checksum = "e105d177a3871454f754b33bb0ee637ecaaac997446375fd3e5d43a2ed00c909" dependencies = [ "libc", "linux-raw-sys", @@ -22863,9 +23748,9 @@ dependencies = [ [[package]] name = "yoke" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" dependencies = [ "serde", "stable_deref_trait", @@ -22875,13 +23760,13 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", "synstructure", ] @@ -22903,27 +23788,27 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] name = "zerofrom" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", "synstructure", ] @@ -22944,7 +23829,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -22966,7 +23851,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -22996,3 +23881,8 @@ dependencies = [ "cc", "pkg-config", ] + +[[patch.unused]] +name = "jsonrpc" +version = "0.12.1" +source = "git+https://github.com/apoelstra/rust-jsonrpc?rev=e42044d#e42044d8e0896317488dfbd65eb5563d76e937fe" diff --git a/Cargo.toml b/Cargo.toml index 676d8a12099..b17630a8d17 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ members = [ "packages/icrc-ledger-client-cdk", "packages/icrc-ledger-types", "packages/ic-ethereum-types", + "packages/ic-metrics-assert", "packages/ic-sha3", "packages/ic-ledger-hash-of", "packages/ic-signature-verification", @@ -172,6 +173,7 @@ members = [ "rs/monitoring/tracing/jaeger_exporter", "rs/monitoring/tracing/logging_layer", "rs/nervous_system/agent", + "rs/nervous_system/candid_utils", "rs/nervous_system/clients", "rs/nervous_system/collections/union_multi_map", "rs/nervous_system/common", @@ -197,6 +199,7 @@ members = [ "rs/nervous_system/runtime", "rs/nervous_system/string", "rs/nervous_system/temporary", + "rs/nervous_system/tools/release-runscript", "rs/nervous_system/tools/sync-with-released-nervous-system-wasms", "rs/nns/constants", "rs/nns/common", @@ -213,7 +216,6 @@ members = [ "rs/nns/handlers/root/interface", "rs/nns/identity", "rs/nns/init", - "rs/nns/inspector", "rs/nns/integration_tests", "rs/nns/nns-ui", "rs/nns/test_utils", @@ -247,6 +249,7 @@ members = [ "rs/registry/canister", "rs/registry/canister/api", "rs/registry/canister/protobuf_generator", + "rs/registry/canister_data_provider", "rs/registry/client", "rs/registry/canister-client", "rs/registry/fake", @@ -287,9 +290,6 @@ members = [ "rs/rosetta-api/icp/ledger_canister_blocks_synchronizer", "rs/rosetta-api/icp/ledger_canister_blocks_synchronizer/test_utils", "rs/ledger_suite/icrc1", - "rs/ledger_suite/icrc1/benchmark/generator", - "rs/ledger_suite/icrc1/benchmark/worker", - "rs/ledger_suite/icrc1/index", "rs/ledger_suite/icrc1/index-ng", "rs/ledger_suite/icrc1/ledger", "rs/ledger_suite/tests/sm-tests", @@ -377,6 +377,8 @@ members = [ "rs/tests/consensus/tecdsa", "rs/tests/consensus/upgrade", "rs/tests/consensus/utils", + "rs/tests/consensus/vetkd", + "rs/tests/cross_chain", "rs/tests/crypto", "rs/tests/driver", "rs/tests/execution", @@ -506,7 +508,8 @@ axum = "0.7.9" backoff = "0.4" base64 = { version = "0.13.1" } bincode = "1.3.3" -bitcoin = { version = "0.28.2", features = ["default", "use-serde", "rand"] } +bitcoin = { version = "0.32.5", features = ["default", "rand", "serde"] } +bitcoincore-rpc = "0.19.0" # build-info and build-info-build MUST be kept in sync! build-info = { git = "https://github.com/dfinity-lab/build-info", rev = "701a696844fba5c87df162fbbc1ccef96f27c9d7" } build-info-build = { git = "https://github.com/dfinity-lab/build-info", rev = "701a696844fba5c87df162fbbc1ccef96f27c9d7", default-features = false } @@ -519,7 +522,7 @@ chrono = { version = "0.4.38", default-features = false, features = [ "serde", ] } ciborium = "0.2.1" -clap = { version = "4.5.18", features = ["derive", "string"] } +clap = { version = "4.5.20", features = ["derive", "string"] } # cloudflare v0.12 is broken, master is partly fixed but unreleased yet. # see: # - https://github.com/cloudflare/cloudflare-rs/issues/222 @@ -531,6 +534,7 @@ curve25519-dalek = { version = "4.1.3", features = [ "group", "precomputed-tables", ] } +dfx-core = { version = "0.1.3" } ed25519-dalek = { version = "2.1.1", features = [ "std", "zeroize", @@ -559,26 +563,23 @@ hyper-rustls = { version = "0.27.3", default-features = false, features = [ ] } hyper-socks2 = { version = "0.9.1", default-features = false } hyper-util = { version = "0.1.10", features = ["full"] } -ic-agent = { version = "0.37.1", features = [ - "experimental_sync_call", - "hyper", - "reqwest", - "pem", -] } +ic-agent = { version = "0.39.2", features = ["pem", "ring"] } ic-bn-lib = { git = "https://github.com/dfinity/ic-bn-lib", rev = "d74a6527fbaf8a2c1a7076983cc84f5c5a727923" } ic-btc-interface = "0.2.2" -ic-cbor = "2.6.0" +ic-canister-sig-creation = { git = "https://github.com/dfinity/ic-canister-sig-creation", rev = "7f9e931954637526295269155881207f6c832d6d" } +ic-cbor = "3" ic-cdk = "0.16.0" ic-cdk-macros = "0.9.0" ic-cdk-timers = "0.7.0" -ic-certificate-verification = "2.6.0" -ic-certification = "2.6.0" -ic-http-certification = "2.6.0" -ic-response-verification = "2.6.0" +ic-certificate-verification = "3" +ic-certification = "3" +ic-http-certification = "3" +ic-http-gateway = "0.1" +ic-response-verification = "3" ic-sha3 = "1.0.0" ic-stable-structures = "0.6.5" -ic-transport-types = { version = "0.37.1" } -ic-utils = { version = "0.37.0", features = ["raw"] } +ic-transport-types = { version = "0.39.2" } +ic-utils = { version = "0.39", features = ["raw"] } ic_bls12_381 = { version = "0.10.0", default-features = false, features = [ "groups", "pairings", @@ -589,6 +590,7 @@ ic_bls12_381 = { version = "0.10.0", default-features = false, features = [ ic_principal = { version = "0.1.1", default-features = false } idna = "1.0.2" inferno = "0.12.0" +ipnet = { version = "2.10.1", features = ["serde"] } itertools = "0.12.0" k256 = { version = "0.13.4", default-features = false, features = [ "arithmetic", @@ -613,7 +615,7 @@ minicbor-derive = "0.13.0" mockall = "0.13.0" mockito = "1.2.0" nftables = "0.4" -nix = "0.24.3" +nix = { version = "0.24.3", features = ["ptrace"] } num_cpus = "1.16.0" num-bigint = "0.4.6" num-traits = { version = "0.2.12", features = ["libm"] } @@ -714,6 +716,7 @@ tokio-metrics = "0.4.0" tokio-rustls = { version = "0.26.0", default-features = false, features = [ "ring", ] } +tokio-stream = "0.1.17" tokio-test = "0.4.4" tokio-util = { version = "0.7.13", features = ["full"] } tonic = "0.12.3" diff --git a/MODULE.bazel b/MODULE.bazel index d0384918b72..dc08205f892 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -174,6 +174,16 @@ oci.pull( ) use_repo(oci, "bitcoind", "bitcoind_linux_amd64") +# foundry container used in test +oci.pull( + name = "foundry", + image = "ghcr.io/foundry-rs/foundry@sha256:d2d6a8064db8659ab4d835bd36ee4ba19a781cbe62a9729a261d7cedf3774cdd", + platforms = [ + "linux/amd64", + ], +) +use_repo(oci, "foundry", "foundry_linux_amd64") + # nginx-proxy container used in test oci.pull( name = "nginx-proxy", @@ -508,6 +518,15 @@ http_file( url = "https://github.com/dfinity/sdk/raw/0.14.2/src/distributed/assetstorage.wasm.gz", ) +# Asset canister that certifies long assets chunk-wise + +http_file( + name = "long_asset_canister", + downloaded_file_path = "http_gateway_canister_custom_assets.wasm.gz", + sha256 = "eedcbf986c67fd4ebe3042094604a9a5703e825e56433e2509a6a4d0384ccf95", + url = "https://github.com/dfinity/http-gateway/raw/refs/heads/main/examples/http-gateway/canister/http_gateway_canister_custom_assets.wasm.gz", +) + # Old version of wallet canister http_file( @@ -827,6 +846,11 @@ http_file( http_file( name = "pocket-ic-mainnet-gz", + # Calculate this hash using: + # git checkout $revision + # ci/container/container-run.sh + # bazel build //publish/binaries:pocket-ic.gz + # sha256sum bazel-bin/publish/binaries/pocket-ic.gz sha256 = "0935ee6ece312719aae4eabddec2dfc6af34d5edbddf4d3af53bccd1b3636044", url = "https://download.dfinity.systems/ic/172f8c78653c93ad101af75b94251439b4ccf098/binaries/x86_64-linux/pocket-ic.gz", ) @@ -844,8 +868,8 @@ http_file( http_file( name = "management_canister_did", downloaded_file_path = "ic.did", - sha256 = "31d4654d60b364420a2e52f546f06b2255dc78ac8c2d768271f004b8946e92cb", - url = "https://raw.githubusercontent.com/dfinity/portal/407ec5b92d06618c4df9f52e98514c5f4f44313e/docs/references/_attachments/ic.did", + sha256 = "0e92d8b9c2cf3d3fca166b76b2d3b8a2464d9b2b61117d8b2f63222b388d8dd1", + url = "https://raw.githubusercontent.com/dfinity/portal/78c93aa37ef17dc67484079d1a4bf58a10a63106/docs/references/_attachments/ic.did", ) # Mozilla CA certificate store in PEM format diff --git a/WORKSPACE.bazel b/WORKSPACE.bazel index dd128089dd9..af1a4a161e3 100644 --- a/WORKSPACE.bazel +++ b/WORKSPACE.bazel @@ -17,6 +17,8 @@ canisters( "registry": "registry-canister.wasm.gz", "governance": "governance-canister.wasm.gz", "ledger": "ledger-canister_notify-method.wasm.gz", + "ledger_v1": "ledger-canister_notify-method.wasm.gz", + "ledger_v2": "ledger-canister_notify-method.wasm.gz", "archive": "ledger-archive-node-canister.wasm.gz", "index": "ic-icp-index-canister.wasm.gz", "root": "root-canister.wasm.gz", @@ -26,9 +28,17 @@ canisters( "sns-wasm": "sns-wasm-canister.wasm.gz", "ck_btc_archive": "ic-icrc1-archive.wasm.gz", "ck_btc_ledger": "ic-icrc1-ledger.wasm.gz", + "ck_btc_ledger_v1": "ic-icrc1-ledger.wasm.gz", + "ck_btc_ledger_v2": "ic-icrc1-ledger.wasm.gz", + "ck_btc_ledger_v2_noledgerversion": "ic-icrc1-ledger.wasm.gz", + "ck_btc_ledger_v3": "ic-icrc1-ledger.wasm.gz", "ck_btc_index": "ic-icrc1-index-ng.wasm.gz", "ck_eth_archive": "ic-icrc1-archive-u256.wasm.gz", "ck_eth_ledger": "ic-icrc1-ledger-u256.wasm.gz", + "ck_eth_ledger_v1": "ic-icrc1-ledger-u256.wasm.gz", + "ck_eth_ledger_v2": "ic-icrc1-ledger-u256.wasm.gz", + "ck_eth_ledger_v2_noledgerversion": "ic-icrc1-ledger-u256.wasm.gz", + "ck_eth_ledger_v3": "ic-icrc1-ledger-u256.wasm.gz", "ck_eth_index": "ic-icrc1-index-ng-u256.wasm.gz", "sns_root": "sns-root-canister.wasm.gz", "sns_governance": "sns-governance-canister.wasm.gz", @@ -43,6 +53,8 @@ canisters( "registry": "mainnet_nns_registry_canister", "governance": "mainnet_nns_governance_canister", "ledger": "mainnet_icp_ledger_canister", + "ledger_v1": "mainnet_icp_ledger_canister-v1", + "ledger_v2": "mainnet_icp_ledger_canister-v2", "archive": "mainnet_icp_ledger-archive-node-canister", "index": "mainnet_icp_index_canister", "root": "mainnet_nns_root-canister", @@ -52,9 +64,17 @@ canisters( "sns-wasm": "mainnet_nns_sns-wasm-canister", "ck_btc_archive": "mainnet_ckbtc_ic-icrc1-archive", "ck_btc_ledger": "mainnet_ckbtc_ic-icrc1-ledger", + "ck_btc_ledger_v1": "mainnet_ckbtc_ic-icrc1-ledger-v1", + "ck_btc_ledger_v2": "mainnet_ckbtc_ic-icrc1-ledger-v2", + "ck_btc_ledger_v2_noledgerversion": "mainnet_ckbtc_ic-icrc1-ledger-v2-noledgerversion", + "ck_btc_ledger_v3": "mainnet_ckbtc_ic-icrc1-ledger-v3", "ck_btc_index": "mainnet_ckbtc-index-ng", "ck_eth_archive": "mainnet_cketh_ic-icrc1-archive-u256", "ck_eth_ledger": "mainnet_cketh_ic-icrc1-ledger-u256", + "ck_eth_ledger_v1": "mainnet_cketh_ic-icrc1-ledger-u256-v1", + "ck_eth_ledger_v2": "mainnet_cketh_ic-icrc1-ledger-u256-v2", + "ck_eth_ledger_v2_noledgerversion": "mainnet_cketh_ic-icrc1-ledger-u256-v2-noledgerversion", + "ck_eth_ledger_v3": "mainnet_cketh_ic-icrc1-ledger-u256-v3", "ck_eth_index": "mainnet_cketh-index-ng", "sns_root": "mainnet_sns-root-canister", "sns_governance": "mainnet_sns-governance-canister", @@ -88,11 +108,8 @@ sol_register_toolchains( http_archive( name = "rules_rust", - # Back-ported fix: https://github.com/bazelbuild/rules_rust/pull/2981 - patch_args = ["-p1"], - patches = ["//bazel:rules_rust.patch"], - sha256 = "85e2013727ab26fb22abdffe4b2ac0c27a2d5b6296167ba63d8f6e13140f51f9", - urls = ["https://github.com/bazelbuild/rules_rust/releases/download/0.53.0/rules_rust-v0.53.0.tar.gz"], + integrity = "sha256-8TBqrAsli3kN8BrZq8arsN8LZUFsdLTvJ/Sqsph4CmQ=", + urls = ["https://github.com/bazelbuild/rules_rust/releases/download/0.56.0/rules_rust-0.56.0.tar.gz"], ) load("@rules_rust//rust:repositories.bzl", "rules_rust_dependencies", "rust_register_toolchains") @@ -107,12 +124,12 @@ rust_register_toolchains( # The nightly version is required to compile fuzz tests from Bazel. # The version below is chosen so that it is in sync with the non-nightly version. versions = [ - "1.82.0", + "1.84.0", # Use the nightly version from the day before the branch. # - # NB! Due to a regression in the compiler https://github.com/rust-lang/rust/issues/128895, that's # only back-ported to stable, we use a more recent version. - "nightly/2024-09-23", + "nightly/2024-11-21", ], ) diff --git a/bazel/canisters.bzl b/bazel/canisters.bzl index 8985effd90d..21d899fbb3e 100644 --- a/bazel/canisters.bzl +++ b/bazel/canisters.bzl @@ -25,6 +25,13 @@ def _wasm_rust_transition_impl(_settings, attr): "-C", "lto", "-C", + # If combined with -C lto, -C embed-bitcode=no will cause rustc to abort at start-up, + # because the combination is invalid. + # See: https://doc.rust-lang.org/rustc/codegen-options/index.html#embed-bitcode + # + # embed-bitcode is disabled by default by rules_rust. + "embed-bitcode=yes", + "-C", "target-feature=+bulk-memory", ], } @@ -128,12 +135,14 @@ def rust_canister(name, service_file, visibility = ["//visibility:public"], test native.alias( name = name, actual = name + ".wasm", + visibility = visibility, ) # DID service related targets native.alias( name = name + ".didfile", actual = service_file, + visibility = visibility, ) did_git_test( name = name + "_did_git_test", diff --git a/bazel/cc_rs.patch b/bazel/cc_rs.patch deleted file mode 100644 index 94e65f91142..00000000000 --- a/bazel/cc_rs.patch +++ /dev/null @@ -1,18 +0,0 @@ -diff --git a/src/lib.rs b/src/lib.rs -index 2fe30b9..88cd566 100644 ---- a/src/lib.rs -+++ b/src/lib.rs -@@ -1121,6 +1121,13 @@ impl Build { - .ok_or_else(|| Error::new(ErrorKind::InvalidArgument, "parent() failure"))? - .to_string_lossy(); - let mut hasher = hash_map::DefaultHasher::new(); -+ let out_dir = self.get_out_dir().expect("Could not get out dir"); -+ -+ let prefix = out_dir.parent().expect("Could not get parent"); -+ let prefix: &str = &prefix.to_string_lossy(); -+ -+ let err = format!("could not strip prefix {prefix} from {dirname}"); -+ let dirname = dirname.strip_prefix(prefix).expect(&err); - hasher.write(dirname.to_string().as_bytes()); - dst.join(format!("{:016x}-{}", hasher.finish(), basename)) - .with_extension("o") diff --git a/bazel/conf/.bazelrc.internal b/bazel/conf/.bazelrc.internal index fc1cd1aafbe..216fd4b161f 100644 --- a/bazel/conf/.bazelrc.internal +++ b/bazel/conf/.bazelrc.internal @@ -3,7 +3,6 @@ # Build event upload configuration build:bes --bes_results_url=https://dash.idx.dfinity.network/invocation/ build:bes --bes_backend=bes.idx.dfinity.network -build:bes --bes_upload_mode=wait_for_upload_complete build:bes --bes_timeout=180s # Default is no timeout. build:bes --remote_build_event_upload=minimal diff --git a/bazel/external_crates.bzl b/bazel/external_crates.bzl index 41bfff7825b..58fbb24dedb 100644 --- a/bazel/external_crates.bzl +++ b/bazel/external_crates.bzl @@ -27,12 +27,6 @@ def external_crates_repository(name, cargo_lockfile, lockfile, sanitizers_enable "canbench": [crate.annotation( gen_binaries = True, )], - "cc": [crate.annotation( - # Patch for determinism issues - # https://github.com/rust-lang/cc-rs/issues/1271 - patch_args = ["-p1"], - patches = ["@@//bazel:cc_rs.patch"], - )], "libssh2-sys": [crate.annotation( # Patch for determinism issues patch_args = ["-p1"], @@ -272,12 +266,16 @@ def external_crates_repository(name, cargo_lockfile, lockfile, sanitizers_enable "bit-vec": crate.spec( version = "^0.6.3", ), - "bitcoin-0-32": crate.spec( - package = "bitcoin", - version = "^0.32.2", - default_features = False, - ), "bitcoin": crate.spec( + version = "^0.32.5", + features = [ + "default", + "rand", + "serde", + ], + ), + "bitcoin-0-28": crate.spec( + package = "bitcoin", version = "^0.28.2", features = [ "default", @@ -286,7 +284,7 @@ def external_crates_repository(name, cargo_lockfile, lockfile, sanitizers_enable ], ), "bitcoincore-rpc": crate.spec( - version = "^0.15.0", + version = "^0.19.0", ), "bitcoind": crate.spec( version = "^0.32.0", @@ -340,7 +338,7 @@ def external_crates_repository(name, cargo_lockfile, lockfile, sanitizers_enable version = "^0.1.8", ), "candid": crate.spec( - version = "^0.10.6", + version = "^0.10.13", ), "cargo_metadata": crate.spec( version = "^0.14.2", @@ -349,7 +347,7 @@ def external_crates_repository(name, cargo_lockfile, lockfile, sanitizers_enable version = "^0.1.2", ), "cc": crate.spec( - version = "^1.0", + version = "=1.1.37", ), "cddl": crate.spec( version = "^0.9.4", @@ -436,6 +434,9 @@ def external_crates_repository(name, cargo_lockfile, lockfile, sanitizers_enable "dashmap": crate.spec( version = "^5.3.4", ), + "dfx-core": crate.spec( + version = "^0.1.3", + ), "dyn-clone": crate.spec( version = "^1.0.14", ), @@ -569,13 +570,8 @@ def external_crates_repository(name, cargo_lockfile, lockfile, sanitizers_enable version = "^0.18.11", ), "ic-agent": crate.spec( - version = "^0.37.1", - features = [ - "experimental_sync_call", - "hyper", - "reqwest", - "pem", - ], + version = "^0.39.2", + features = ["pem", "ring"], ), "ic-bn-lib": crate.spec( git = "https://github.com/dfinity/ic-bn-lib", @@ -588,10 +584,11 @@ def external_crates_repository(name, cargo_lockfile, lockfile, sanitizers_enable version = "^0.2.0", ), "ic-canister-sig-creation": crate.spec( - version = "^1.0.1", + git = "https://github.com/dfinity/ic-canister-sig-creation", + rev = "7f9e931954637526295269155881207f6c832d6d", ), "ic-cbor": crate.spec( - version = "2.6.0", + version = "3.0.2", ), "ic-cdk": crate.spec( version = "^0.16.0", @@ -602,21 +599,30 @@ def external_crates_repository(name, cargo_lockfile, lockfile, sanitizers_enable "ic-cdk-macros": crate.spec( version = "^0.9.0", ), + "ic-cdk-macros-next": crate.spec( + package = "ic-cdk-macros", + git = "https://github.com/dfinity/cdk-rs.git", + rev = "4e287ce51636b0e70768c193da38d2fc5324ea15", + ), + "ic-cdk-next": crate.spec( + package = "ic-cdk", + git = "https://github.com/dfinity/cdk-rs.git", + rev = "4e287ce51636b0e70768c193da38d2fc5324ea15", + ), "ic-certified-map": crate.spec( version = "^0.3.1", ), "ic-certification": crate.spec( - version = "2.6.0", + version = "3.0.2", ), "ic-certificate-verification": crate.spec( - version = "2.6.0", + version = "3.0.2", ), "ic-http-certification": crate.spec( - version = "2.6.0", + version = "3.0.2", ), "ic-http-gateway": crate.spec( - git = "https://github.com/dfinity/http-gateway", - tag = "0.1.0-b0", + version = "0.1.0", ), "ic-metrics-encoder": crate.spec( version = "^1.1.1", @@ -626,7 +632,7 @@ def external_crates_repository(name, cargo_lockfile, lockfile, sanitizers_enable default_features = False, ), "ic-response-verification": crate.spec( - version = "2.6.0", + version = "3.0.2", ), "ic-sha3": crate.spec( version = "^1.0.0", @@ -646,10 +652,10 @@ def external_crates_repository(name, cargo_lockfile, lockfile, sanitizers_enable version = "^3.0.0", ), "ic-transport-types": crate.spec( - version = "^0.37.1", + version = "^0.39.2", ), "ic-utils": crate.spec( - version = "^0.37.0", + version = "^0.39.0", features = ["raw"], ), "ic-verify-bls-signature": crate.spec( @@ -695,7 +701,8 @@ def external_crates_repository(name, cargo_lockfile, lockfile, sanitizers_enable features = ["serde"], ), "ipnet": crate.spec( - version = "^2.5.0", + version = "^2.10.1", + features = ["serde"], ), "isocountry": crate.spec( version = "^0.3.2", @@ -829,6 +836,9 @@ def external_crates_repository(name, cargo_lockfile, lockfile, sanitizers_enable ), "nix": crate.spec( version = "^0.24.3", + features = [ + "ptrace", + ], ), "num-bigint": crate.spec( version = "^0.4.6", @@ -878,7 +888,7 @@ def external_crates_repository(name, cargo_lockfile, lockfile, sanitizers_enable ], ), "opentelemetry_sdk": crate.spec( - version = "^0.27.0", + version = "^0.27.1", features = [ "trace", "rt-tokio", @@ -1244,6 +1254,9 @@ def external_crates_repository(name, cargo_lockfile, lockfile, sanitizers_enable "full", ], ), + "syscalls": crate.spec( + version = "^0.6.18", + ), "tar": crate.spec( version = "^0.4.38", ), @@ -1300,6 +1313,9 @@ def external_crates_repository(name, cargo_lockfile, lockfile, sanitizers_enable "ring", ], ), + "tokio-stream": crate.spec( + version = "^0.1.17", + ), "tokio-serde": crate.spec( version = "^0.8", features = [ @@ -1434,7 +1450,7 @@ def external_crates_repository(name, cargo_lockfile, lockfile, sanitizers_enable version = "^0.217.0", ), "wasmtime": crate.spec( - version = "^27.0.0", + version = "^28.0.0", default_features = False, features = [ "cranelift", @@ -1445,7 +1461,7 @@ def external_crates_repository(name, cargo_lockfile, lockfile, sanitizers_enable ], ), "wasmtime-environ": crate.spec( - version = "^27.0.0", + version = "^28.0.0", ), "wast": crate.spec( version = "^212.0.0", diff --git a/bazel/rules_rust.patch b/bazel/rules_rust.patch deleted file mode 100644 index d9252428a0b..00000000000 --- a/bazel/rules_rust.patch +++ /dev/null @@ -1,45 +0,0 @@ -# Backports for https://github.com/bazelbuild/rules_rust/issues/2974 and https://github.com/bazelbuild/rules_rust/pull/2981 -diff --git a/cargo/cargo_build_script_runner/bin.rs b/cargo/cargo_build_script_runner/bin.rs -index 2dab3578..b5bb4fca 100644 ---- a/cargo/cargo_build_script_runner/bin.rs -+++ b/cargo/cargo_build_script_runner/bin.rs -@@ -187,9 +187,9 @@ fn run_buildrs() -> Result<(), String> { - .as_bytes(), - ) - .unwrap_or_else(|_| panic!("Unable to write file {:?}", output_dep_env_path)); -- write(&stdout_path, process_output.stdout) -+ write(&stdout_path, "") - .unwrap_or_else(|_| panic!("Unable to write file {:?}", stdout_path)); -- write(&stderr_path, process_output.stderr) -+ write(&stderr_path, "") - .unwrap_or_else(|_| panic!("Unable to write file {:?}", stderr_path)); - - let CompileAndLinkFlags { -diff --git a/crate_universe/private/crate.bzl b/crate_universe/private/crate.bzl -index c493e9a6..ad317abf 100644 ---- a/crate_universe/private/crate.bzl -+++ b/crate_universe/private/crate.bzl -@@ -230,7 +230,22 @@ def _stringify_label(value): - def _stringify_list(values): - if not values: - return values -- return [str(x) for x in values] -+ -+ if type(values) == "list": -+ return [str(x) for x in values] -+ -+ -+ -+ -+ if type(values) == "struct" and type(values.selects) != "NoneType": -+ new_selects = {} -+ -+ for k, v in values.selects.items(): -+ new_selects[k] = [str(x) for x in values.selects[k]] -+ -+ return struct(common = [str(x) for x in values.common], selects = new_selects) -+ -+ fail("Cannot stringify unknown type for list '{}'".format(values)) - - def _select(common, selects): - """A Starlark Select for `crate.annotation()`. diff --git a/bazel/sanitizers_enabled_env/defs.bzl b/bazel/sanitizers_enabled_env/defs.bzl index 091354e0076..a6e65bef9a2 100644 --- a/bazel/sanitizers_enabled_env/defs.bzl +++ b/bazel/sanitizers_enabled_env/defs.bzl @@ -8,7 +8,7 @@ def _impl(repository_ctx): ) repository_ctx.file( "defs.bzl", - content = "SANITIZERS_ENABLED=" + repository_ctx.os.environ.get("SANITIZERS_ENABLED", "0") + "\n", + content = "SANITIZERS_ENABLED=" + repository_ctx.getenv("SANITIZERS_ENABLED", "0") + "\n", executable = False, ) @@ -16,6 +16,5 @@ def sanitizers_enabled_env(name = None): rule = repository_rule( implementation = _impl, local = True, - environ = ["SANITIZERS_ENABLED"], ) rule(name = name) diff --git a/bin/.gitignore b/bin/.gitignore deleted file mode 100644 index 2078d0d0768..00000000000 --- a/bin/.gitignore +++ /dev/null @@ -1 +0,0 @@ -checksum.txt \ No newline at end of file diff --git a/bin/dotbootstrap.sh b/bin/dotbootstrap.sh deleted file mode 100755 index 33691106fc8..00000000000 --- a/bin/dotbootstrap.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash - -curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh -curl --proto =https --tlsv1.2 -sSf https://sh.rustup.rs -echo "scp -r jplevyak@jplevyak.homeip.net:.vim ~/.vim" diff --git a/bin/ict b/bin/ict index 9f3dfb12e04..a7ee61f4d86 100755 --- a/bin/ict +++ b/bin/ict @@ -1,62 +1,24 @@ #!/usr/bin/env bash -set -Eou pipefail +set -eou pipefail -GREEN="\x1b[32m" -RED="\x1b[31m" -BOLD="\x1b[1m" -NC="\x1b[0m" +# convenience wrapper around 'ict' binary -CURRENT_SCRIPT_DIR=$(dirname "${BASH_SOURCE[0]}") -ICT_DIR="${CURRENT_SCRIPT_DIR}/../rs/tests/ict" -CHECKSUM_FILE="${CURRENT_SCRIPT_DIR}/checksum.txt" # holds the resulting hash of all files in the ict directory -BAZEL_ICT_TARGET="//rs/tests/ict:ict" -# Path to this binary is hard coded, as getting it from Bazel is a bit slow (~0.5 sec) for a good interactivity. -ICT_BIN="$CURRENT_SCRIPT_DIR/../bazel-out/k8-opt/bin/rs/tests/ict/ict_/ict" +ICT_TARGET="//rs/tests/ict" -if [ "$(hostname)" != "devenv-container" ]; then - echo -e "${BOLD}${RED}This script can only be executed within a devenv-container. Make sure you first executed:\n/ic$ ./ci/container/container-run.sh${NC}" +ICT_BUILD_CMD='bazel build "$ICT_TARGET"' +GIT_TOPLEVEL=$(git rev-parse --show-toplevel) + +# Bazel 8 has a '--quiet' flag to suppress "info" output: +# https://github.com/bazelbuild/bazel/issues/4867 +# +# Once we support bazel 8, we can simply run 'bazel --quiet run ...' +# +# Until then, we perform the build separately and pipe the output +# to /dev/null +if ! eval "$ICT_BUILD_CMD" &> /dev/null ; then + echo "could not build $ICT_TARGET" + echo "try running: $ICT_BUILD_CMD" exit 1 fi -compile_ict() { - bazel build $BAZEL_ICT_TARGET --config=local >/dev/null 2>&1 - CODE="$?" - if [ "${CODE}" != "0" ]; then - echo -e "${BOLD}${RED}ict compilation failed with code=${CODE}${NC}" - echo "Try running the build manually: bazel build ${BAZEL_ICT_TARGET}" - exit 1 - fi -} - -# Check if Bazel server is already running. If not print a message for user and start Bazel. -pgrep -fi bazel >/dev/null >&1 -CODE="$?" -if [ "${CODE}" != "0" ]; then - echo -e "${BOLD}${GREEN}Starting Bazel server ...${NC}" - bazel >/dev/null 2>&1 - CODE="$?" - if [ "${CODE}" != "0" ]; then - echo -e "${BOLD}${RED}Failed to start Bazel server${NC}" - echo "Try starting Bazel manually: $ bazel" - exit 1 - fi -fi -# Check whether ict binary exists/up-to-date. If not compile/recompile + handle errors. -# Take all files in the ict/* for checksum computation. -checksum=$(find ${ICT_DIR} -type f -exec sha256sum {} \; | sort -z | sha1sum | head -c 40) -compile_msg="" -if [ ! -f "$ICT_BIN" ]; then - compile_msg="Compiling ict binary ..." -else - if [ ! -f "${CHECKSUM_FILE}" ] || [ $(<$CHECKSUM_FILE) != "$checksum" ]; then - compile_msg="ict source file/s changed, recompiling ict ..." - fi -fi -if [ ! -z "${compile_msg}" ]; then - echo -e "${BOLD}${GREEN}${compile_msg}${NC}" - compile_ict - # write checksum into a file - echo ${checksum} >${CHECKSUM_FILE} -fi -# Invoke binary with the arguments. -"$ICT_BIN" "$@" +"$GIT_TOPLEVEL"/bazel-bin/rs/tests/ict/ict_/ict "$@" diff --git a/bin/run-all-fuzzers.sh b/bin/run-all-fuzzers.sh index 1fb2b6c670d..6ca68771c96 100755 --- a/bin/run-all-fuzzers.sh +++ b/bin/run-all-fuzzers.sh @@ -24,7 +24,7 @@ EOF done LIST_OF_FUZZERS=$(bazel query 'attr(tags, "sandbox_libfuzzer", //rs/...)') for FUZZER in $LIST_OF_FUZZERS; do - bazel run --config=sandbox_fuzzing $FUZZER -- -runs=$MAX_EXECUTIONS + bazel run --config=bes --config=sandbox_fuzzing $FUZZER -- -runs=$MAX_EXECUTIONS done ;; diff --git a/ci/bazel-scripts/main.sh b/ci/bazel-scripts/main.sh index 4da4bafd554..52c5dc5adef 100755 --- a/ci/bazel-scripts/main.sh +++ b/ci/bazel-scripts/main.sh @@ -58,8 +58,9 @@ AWS_CREDS="${HOME}/.aws/credentials" mkdir -p "$(dirname "${AWS_CREDS}")" # add aws credentials file if it's set -if [ -n "${AWS_SHARED_CREDENTIALS_CONTENT+x}" ]; then - echo "$AWS_SHARED_CREDENTIALS_CONTENT" >"$AWS_CREDS" +if [ -n "${CLOUD_CREDENTIALS_CONTENT+x}" ]; then + echo "$CLOUD_CREDENTIALS_CONTENT" >"$AWS_CREDS" + unset CLOUD_CREDENTIALS_CONTENT fi if [ -n "${GITHUB_OUTPUT:-}" ]; then @@ -92,7 +93,7 @@ stream_awk_program=' # shellcheck disable=SC2086 # ${BAZEL_...} variables are expected to contain several arguments. We have `set -f` set above to disable globbing (and therefore only allow splitting)" -buildevents cmd "${CI_RUN_ID}" "${CI_JOB_NAME}" "${CI_JOB_NAME}-bazel-cmd" -- bazel \ +bazel \ ${BAZEL_STARTUP_ARGS} \ ${BAZEL_COMMAND} \ --color=yes \ diff --git a/ci/container/README.md b/ci/container/README.md index eea758cd7e7..ebb11b1e490 100644 --- a/ci/container/README.md +++ b/ci/container/README.md @@ -86,7 +86,7 @@ sudo podman run --pids-limit=-1 -it --rm --privileged --network=host --cgroupns= --mount type=bind,source=/home/john/.local/share/fish,target=/home/ubuntu/.local/share/fish \ --mount type=bind,source=/home/john/.zsh_history,target=/home/ubuntu/.zsh_history \ -v /tmp/ssh-XXXXQAO7kF/agent.113731:/ssh-agent -e SSH_AUTH_SOCK=/ssh-agent -w /ic \ - docker.io/dfinity/ic-build:221b79c4f4a966eae67a3f9ef7f20f4c5583d5bc38df17c94128804687a84c29 /usr/bin/fish + ghcr.io/dfinity/ic-build:221b79c4f4a966eae67a3f9ef7f20f4c5583d5bc38df17c94128804687a84c29 /usr/bin/fish ``` ### How to use custom config diff --git a/ci/container/TAG b/ci/container/TAG index 1b88ba01cf3..510327d7002 100644 --- a/ci/container/TAG +++ b/ci/container/TAG @@ -1 +1 @@ -7beeb6e69346fd10ff4421688aec841d072ac4f8c4c80d3b089d64aa280c0894 +b3869ba20c4e9ba2915be302f5b5a4b23da0545af2f1cb896441055372950389 diff --git a/ci/container/build-image.sh b/ci/container/build-image.sh index 401fc422196..2e4b95803d8 100755 --- a/ci/container/build-image.sh +++ b/ci/container/build-image.sh @@ -38,9 +38,8 @@ fi DOCKER_BUILDKIT=1 docker "${ARGS[@]}" build "${BUILD_ARGS[@]}" \ -t ic-build:"$DOCKER_IMG_TAG" \ - -t docker.io/dfinity/ic-build:"$DOCKER_IMG_TAG" \ - -t docker.io/dfinity/ic-build:latest \ -t ghcr.io/dfinity/ic-build:"$DOCKER_IMG_TAG" \ + -t ghcr.io/dfinity/ic-build:latest \ --build-arg RUST_VERSION="$RUST_VERSION" \ -f ci/container/Dockerfile . diff --git a/ci/scripts/docker-login.sh b/ci/scripts/docker-login.sh deleted file mode 100755 index 5834e932887..00000000000 --- a/ci/scripts/docker-login.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash - -set -eEuo pipefail - -# login to docker hub to avoid rate limit disruptions -if which docker 2>/dev/null; then - docker login -u "$DOCKER_HUB_USER" -p "$DOCKER_HUB_PASSWORD_RO" -fi -# docker-bin used by container_pull in WORKSPACES.bazel -if which docker-bin 2>/dev/null; then - # save auth to user's .docker/config.json - docker-bin login -u "$DOCKER_HUB_USER" -p "$DOCKER_HUB_PASSWORD_RO" - # save auth to root's .docker/config.json - sudo docker-bin login -u "$DOCKER_HUB_USER" -p "$DOCKER_HUB_PASSWORD_RO" -fi diff --git a/ci/src/artifacts/upload.bzl b/ci/src/artifacts/upload.bzl index 979d171bba0..cb885c2e5ca 100644 --- a/ci/src/artifacts/upload.bzl +++ b/ci/src/artifacts/upload.bzl @@ -40,10 +40,9 @@ def _upload_artifact_impl(ctx): for f in allinputs: filename = ctx.label.name + "_" + f.basename url = ctx.actions.declare_file(filename + ".url") - proxy_cache_url = ctx.actions.declare_file(filename + ".proxy-cache-url") ctx.actions.run( executable = ctx.file._artifacts_uploader, - arguments = [f.path, url.path, proxy_cache_url.path], + arguments = [f.path, url.path], env = { "RCLONE_S3_ENDPOINT": rclone_endpoint, "RCLONE": ctx.file._rclone.path, @@ -54,10 +53,10 @@ def _upload_artifact_impl(ctx): "FAKE_IC_VERSION": FAKE_IC_VERSION, }, inputs = [f, ctx.version_file, rclone_config, ctx.file._version_txt], - outputs = [url, proxy_cache_url], + outputs = [url], tools = [ctx.file._rclone], ) - fileurl.extend([url, proxy_cache_url]) + fileurl.extend([url]) urls = ctx.actions.declare_file(ctx.label.name + ".urls") ctx.actions.run_shell( diff --git a/ci/src/artifacts/upload.sh b/ci/src/artifacts/upload.sh index e4ca2c57aed..1503f8ebf7c 100755 --- a/ci/src/artifacts/upload.sh +++ b/ci/src/artifacts/upload.sh @@ -37,7 +37,6 @@ fi # of /opt/namespace) we simply skip the upload. if [ -d /opt/namespace ]; then touch "$2" - touch "$3" exit 0 fi @@ -66,4 +65,3 @@ AWS_PROFILE=cf "$RCLONE" \ URL_PATH="ic/${VERSION}/$REMOTE_SUBDIR/$(basename $f)" echo "https://download.dfinity.systems/${URL_PATH}" >"$2" -echo "http://download.proxy-global.dfinity.network:8080/${URL_PATH}" >"$3" diff --git a/ci/src/dependencies/data_source/jira_finding_data_source.py b/ci/src/dependencies/data_source/jira_finding_data_source.py index 039b40d2e21..12c32d2a71b 100644 --- a/ci/src/dependencies/data_source/jira_finding_data_source.py +++ b/ci/src/dependencies/data_source/jira_finding_data_source.py @@ -68,6 +68,7 @@ Team.EXECUTION_TEAM: {"name": "dept-Execution"}, Team.NNS_TEAM: {"name": "dept-NNS"}, Team.CRYPTO_TEAM: {"name": "dept-Crypto Library"}, + Team.IDENTITY_TEAM: {"name": "dept-Identity"}, } JIRA_LABEL_PATCH_VULNDEP_PUBLISHED = "patch_published_vulndep" JIRA_LABEL_PATCH_ALLDEP_PUBLISHED = "patch_published_alldep" diff --git a/ci/src/dependencies/integration/slack/slack_default_notification_handler.py b/ci/src/dependencies/integration/slack/slack_default_notification_handler.py index 3eb46f53062..93ced5997da 100644 --- a/ci/src/dependencies/integration/slack/slack_default_notification_handler.py +++ b/ci/src/dependencies/integration/slack/slack_default_notification_handler.py @@ -15,6 +15,7 @@ ScanJobSucceededNotificationEvent, ) from notification.notification_handler import NotificationHandler +from scanner.scanner_job_type import ScannerJobType SLACK_CHANNEL_ID = "C04815E0T16" SLACK_CHANNEL = "#security-vulnerability-management" @@ -72,9 +73,10 @@ def __handle_scan_job_succeeded(self, event: ScanJobSucceededNotificationEvent): ) def __handle_scan_job_failed_(self, event: ScanJobFailedNotificationEvent): - self.slack_api.send_message( - f'Scan Job with type {event.job_type.name} and ID {event.scanner_id} failed with reason "{event.reason}" in CI Pipeline {event.ci_job_url} {APP_OWNERS}' - ) + msg = f'Scan Job with type {event.job_type.name} and ID {event.scanner_id} failed with reason "{event.reason}" in CI Pipeline {event.ci_job_url}' + if event.job_type != ScannerJobType.MERGE_SCAN: + msg += f" {APP_OWNERS}" + self.slack_api.send_message(msg) def __get_risk_assessors(self, finding: Finding) -> str: if finding.risk_assessor is None or len(finding.risk_assessor) == 0: diff --git a/ci/src/dependencies/integration/slack/slack_default_notification_handler_test.py b/ci/src/dependencies/integration/slack/slack_default_notification_handler_test.py index 55e17c10c53..07018e5c6d3 100644 --- a/ci/src/dependencies/integration/slack/slack_default_notification_handler_test.py +++ b/ci/src/dependencies/integration/slack/slack_default_notification_handler_test.py @@ -140,7 +140,10 @@ def test_scan_job_failed_event(selected_job_type): assert "some error reason" in api.messages[0] assert selected_job_type.name in api.messages[0] assert "http://ci.job/123" in api.messages[0] - assert APP_OWNERS in api.messages[0] + if selected_job_type == ScannerJobType.MERGE_SCAN: + assert APP_OWNERS not in api.messages[0] + else: + assert APP_OWNERS in api.messages[0] def test_finding_needs_risk_assessment_event(): diff --git a/ci/src/dependencies/job/bazel_rust_ic_scanner_periodic_job.py b/ci/src/dependencies/job/bazel_rust_ic_scanner_periodic_job.py index a00665aafc6..938119e3ca6 100644 --- a/ci/src/dependencies/job/bazel_rust_ic_scanner_periodic_job.py +++ b/ci/src/dependencies/job/bazel_rust_ic_scanner_periodic_job.py @@ -25,7 +25,7 @@ Repository( "internet-identity", "https://github.com/dfinity/internet-identity", - [Project(name="internet-identity", path="internet-identity", owner=Team.GIX_TEAM)], + [Project(name="internet-identity", path="internet-identity", owner=Team.IDENTITY_TEAM)], ), Repository( "response-verification", diff --git a/ci/src/dependencies/job/npm_scanner_periodic_job.py b/ci/src/dependencies/job/npm_scanner_periodic_job.py index f723a45d80e..1afb1dec2e3 100644 --- a/ci/src/dependencies/job/npm_scanner_periodic_job.py +++ b/ci/src/dependencies/job/npm_scanner_periodic_job.py @@ -22,7 +22,7 @@ Project( name="frontend", path="nns-dapp/frontend", - owner=Team.GIX_TEAM, + owner=Team.NNS_TEAM, ) ], "18.17.1", @@ -34,7 +34,7 @@ Project( name="internet-identity", path="internet-identity", - owner=Team.GIX_TEAM, + owner=Team.IDENTITY_TEAM, ) ], DEFAULT_NODE_VERSION, diff --git a/ci/src/dependencies/model/team.py b/ci/src/dependencies/model/team.py index 891875c46ff..36d62a1a487 100644 --- a/ci/src/dependencies/model/team.py +++ b/ci/src/dependencies/model/team.py @@ -11,3 +11,4 @@ class Team(IntEnum): EXECUTION_TEAM = 7 NNS_TEAM = 8 CRYPTO_TEAM = 9 + IDENTITY_TEAM = 10 diff --git a/ci/tools/download b/ci/tools/download deleted file mode 100755 index aac83309278..00000000000 --- a/ci/tools/download +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -print_usage() { - cat >&2 <<-USAGE - usage: $0 [-h] [-c ] [-u ] files... - Download files from https://download.dfinity.systems - Options: - -h - print this help message - -c - git revision to use, default to autodetect from \$CI_COMMIT_SHA or from the current working tree - -u - base url to download from. Default is to autodetect. - files... - list of files to download. -USAGE -} - -download_file() { - if curl --fail --retry 5 --retry-delay 10 "${BASE_URL}/ic/${GIT_COMMIT}/$1"; then - return - fi - echo "Can't download $1" >&2 - exit 1 -} - -GIT_COMMIT="${CI_COMMIT_SHA:-$(git rev-parse HEAD)}" - -while getopts 'hc:u:' flag; do - case "${flag}" in - c) - GIT_COMMIT="${OPTARG}" - ;; - u) - BASE_URL="${OPTARG}" - ;; - h | *) - print_usage - exit 1 - ;; - esac -done -shift $(($OPTIND - 1)) - -if [ -z "${GIT_COMMIT:-}" ]; then - echo "Can't detect git revision" >&2 - print_usage - exit 1 -fi - -if [ -z "${BASE_URL:-}" ]; then - BASE_URL="https://download.dfinity.systems" - PROXY_URL="http://download.proxy-global.dfinity.network:8080" - if curl --connect-timeout 5 --head "${PROXY_URL}" >&2; then - BASE_URL="${PROXY_URL}" - fi -fi - -for F in $@; do - download_file "${F}" -done diff --git a/ci/tools/repro-check.sh b/ci/tools/repro-check.sh index f348304cca7..06c78eb19cf 100755 --- a/ci/tools/repro-check.sh +++ b/ci/tools/repro-check.sh @@ -107,10 +107,11 @@ check_ic_repo() { git_remote="$(git config --get remote.origin.url)" log_debug "Check the repository is an IC repository" - # Possible values of `git_remote` are listed below - # git@github.com:dfinity/ic.git - # https://github.com/dfinity/ic.git - if [[ "$git_remote" == */ic.git ]] || [[ "$git_remote" == */ic ]]; then + # Some of the possible values of `git_remote` that should be matched: + # git@github.com:dfinity/ic.git, https://github.com/dfinity/ic.git + # git@github.com:dfinity/ic-private.git, https://github.com/dfinity/ic-private.git + # git@github.com:/ic.git, https://github.com//ic.git + if [[ "$git_remote" == */ic* ]]; then log_debug "Inside IC repository" else error "When not specifying any option please run this script inside an IC git repository" diff --git a/hs/spec_compliance/src/IC/Test/Agent.hs b/hs/spec_compliance/src/IC/Test/Agent.hs index cde54340f2f..47434404c57 100644 --- a/hs/spec_compliance/src/IC/Test/Agent.hs +++ b/hs/spec_compliance/src/IC/Test/Agent.hs @@ -249,7 +249,6 @@ makeAgentConfig allow_self_signed_certs ep' subnets httpbin_proto httpbin' to = `catch` (\(HUnitFailure _ r) -> putStrLn r >> exitFailure) putStrLn $ "Spec version tested: " ++ T.unpack specVersion - putStrLn $ "Spec version claimed: " ++ T.unpack (status_api_version s) return AgentConfig @@ -991,20 +990,17 @@ runGet a b = case Get.runGetOrFail (a <* done) b of -- * Status endpoint parsing data StatusResponse = StatusResponse - { status_api_version :: T.Text, - status_root_key :: Blob - } + {status_root_key :: Blob} statusResponse :: (HasCallStack) => GenR -> IO StatusResponse statusResponse = asExceptT . record do - v <- field text "ic_api_version" _ <- optionalField text "impl_source" _ <- optionalField text "impl_version" _ <- optionalField text "impl_revision" pk <- field blob "root_key" swallowAllFields -- More fields are explicitly allowed - return StatusResponse {status_api_version = v, status_root_key = pk} + return StatusResponse {status_root_key = pk} -- * Interacting with aaaaa-aa (via HTTP) diff --git a/hs/spec_compliance/src/IC/Test/Spec.hs b/hs/spec_compliance/src/IC/Test/Spec.hs index 8512be18c94..8379bd5d8d2 100644 --- a/hs/spec_compliance/src/IC/Test/Spec.hs +++ b/hs/spec_compliance/src/IC/Test/Spec.hs @@ -754,7 +754,7 @@ icTests my_sub other_sub conf = ic_canister_status'' anonymousUser cid >>= isErrOrReject [3, 5] ic_canister_status'' secp256k1User cid >>= isErrOrReject [3, 5], simpleTestCase "> 10 controllers" ecid $ \cid -> do - ic_create_with_controllers' (ic00viaWithCycles cid 20_000_000_000_000) ecid (replicate 11 cid) >>= isReject [3, 5] + ic_create_with_controllers' (ic00viaWithCycles cid 20_000_000_000_000) ecid (replicate 11 cid) >>= isReject [4] ic_set_controllers' ic00 cid (replicate 11 cid) >>= isReject [4], simpleTestCase "No controller" ecid $ \cid -> do cid2 <- ic_create_with_controllers (ic00viaWithCycles cid 20_000_000_000_000) ecid [] diff --git a/hs/spec_compliance/src/IC/Test/Spec/CanisterVersion.hs b/hs/spec_compliance/src/IC/Test/Spec/CanisterVersion.hs index ec02cc52c20..96fda47f73a 100644 --- a/hs/spec_compliance/src/IC/Test/Spec/CanisterVersion.hs +++ b/hs/spec_compliance/src/IC/Test/Spec/CanisterVersion.hs @@ -44,9 +44,9 @@ canister_version_tests ecid = ctr <- callToQuery'' cid (replyData canister_version) >>= is2xx >>= isReply >>= asWord64 ctr @?= 1 ctr <- callToQuery'' cid (replyData canister_version) >>= is2xx >>= isReply >>= asWord64 - ctr @?= 1 + ctr @?= 2 ctr <- callToQuery'' cid (replyData canister_version) >>= is2xx >>= isReply >>= asWord64 - ctr @?= 1, + ctr @?= 3, simpleTestCase "in update" ecid $ \cid -> do ctr <- call cid (replyData canister_version) >>= asWord64 ctr @?= 1 diff --git a/ic-os/boundary-guestos/context/Dockerfile b/ic-os/boundary-guestos/context/Dockerfile index 57a95ec8e8f..af748384095 100644 --- a/ic-os/boundary-guestos/context/Dockerfile +++ b/ic-os/boundary-guestos/context/Dockerfile @@ -2,7 +2,7 @@ # # Build step for example: # - `docker build --pull -t dfinity/boundaryos-main --build-arg BUILD_TYPE=dev \ ` -# `--build-arg BASE_IMAGE=docker.io/dfinity/boundaryos-base@sha256:dc1a2892b0241131dd97ddd4dce560ab274d00a90110a4b5fc4cb2245ff1f0db -f Dockerfile .` +# `--build-arg BASE_IMAGE=ghcr.io/dfinity/boundaryos-base@sha256:dc1a2892b0241131dd97ddd4dce560ab274d00a90110a4b5fc4cb2245ff1f0db -f Dockerfile .` # # # The base images are defined in docker-base.prod and docker-base.dev. Update @@ -25,8 +25,8 @@ WORKDIR /tmp # Download and verify ic-gateway RUN \ - curl -L -O https://github.com/dfinity/ic-gateway/releases/download/v0.1.61/ic-gateway_0.1.61_amd64.deb && \ - echo "dcde4601c7e57372a9488265152ed71366cc710d31f9b1219a197ffa23680fdc ic-gateway_0.1.61_amd64.deb" | sha256sum -c + curl -L -O https://github.com/dfinity/ic-gateway/releases/download/v0.1.64/ic-gateway_0.1.64_amd64.deb && \ + echo "386ba2466454181fa4c3e8459945bcb86ee4e741ea69fb1ae11eb7a452366331 ic-gateway_0.1.64_amd64.deb" | sha256sum -c # # Second build stage: @@ -56,9 +56,9 @@ FROM image-${BUILD_TYPE} USER root:root -COPY --from=download /tmp/ic-gateway_0.1.61_amd64.deb /tmp/ic-gateway_0.1.61_amd64.deb -RUN dpkg -i --force-confold /tmp/ic-gateway_0.1.61_amd64.deb && \ - rm /tmp/ic-gateway_0.1.61_amd64.deb +COPY --from=download /tmp/ic-gateway_0.1.64_amd64.deb /tmp/ic-gateway_0.1.64_amd64.deb +RUN dpkg -i --force-confold /tmp/ic-gateway_0.1.64_amd64.deb && \ + rm /tmp/ic-gateway_0.1.64_amd64.deb RUN mkdir -p /boot/config \ /boot/efi \ diff --git a/ic-os/boundary-guestos/context/README.adoc b/ic-os/boundary-guestos/context/README.adoc index a235505e96c..6de8f7c3d5a 100644 --- a/ic-os/boundary-guestos/context/README.adoc +++ b/ic-os/boundary-guestos/context/README.adoc @@ -46,7 +46,7 @@ serve as a guide on how to add further actions. === ssh key generation -The `setup-ssh-keys` (and corresponding shell script) service performs one of +The `generate-host-ssh-keys` (and corresponding shell script) service performs one of two things: If this is the first boot ever (on a newly installed system), it generates ssh keys and stashes them away in a location that is preserved across reboots and in the future upgrades. diff --git a/ic-os/boundary-guestos/context/docker-base.prod b/ic-os/boundary-guestos/context/docker-base.prod index 9f5b493a1ea..d1a94191615 100644 --- a/ic-os/boundary-guestos/context/docker-base.prod +++ b/ic-os/boundary-guestos/context/docker-base.prod @@ -1 +1 @@ -ghcr.io/dfinity/boundaryos-base@sha256:aa1aa1db151974aaa5128772d4707ee975d1d2d79d8f6be0b348bad997b417c3 +ghcr.io/dfinity/boundaryos-base@sha256:c1080a96793e270901e15c78ba17cb3298dc7cfbbb51c6989fed072d5445c512 diff --git a/ic-os/boundary-guestos/docs/Boot.adoc b/ic-os/boundary-guestos/docs/Boot.adoc index 7080e087e8e..a8f681acb5b 100644 --- a/ic-os/boundary-guestos/docs/Boot.adoc +++ b/ic-os/boundary-guestos/docs/Boot.adoc @@ -20,7 +20,7 @@ service are started in the IC-OS boot sequence: - Config injection -- Set up ssh account keys +- Set up ssh user keys - Generate network configuration @@ -53,7 +53,7 @@ Relevant information can be found in the guestos link:../../guestos/docs/Boot.ad == Set up ssh host keys -Service: `setup-ssh-keys.service`, script: `/opt/ic/bin/setup-ssh-keys.sh`, +Service: `generate-host-ssh-keys.service`, script: `/opt/ic/bin/generate-host-ssh-keys.sh`, depends on `/boot/config` mount. This checks if ssh host keys for the system exist in the `config` partition @@ -83,9 +83,9 @@ USB stick" attached to the VM that contains a tar file with initial configuratio for parts of the system (see link:ConfigStore{outfilesuffix}[config store] for a description). Required files in the `config` partition as well as payload store are created. -== Set up ssh account keys +== Set up ssh user keys -Service: `setup-ssh-account-keys.services`, script `/opt/ic/bin/setup-ssh-account-keys.sh`. +Service: `setup-ssh-user-keys.services`, script `/opt/ic/bin/setup-ssh-user-keys.sh`. Depends on `bootstrap-ic-node.service`. The `authorized_keys` files for the role accounts are taken from the @@ -108,16 +108,6 @@ Depends on `bootstrap-ic-node.service`, runs before `systemd-networkd.service ` Sets hostname as defined in the `config` partition. -== IPv6 address monitor / retry - -Service: `retry-ipv6-config.service`, script `/opt/ic/bin/retry-ipv6-config.sh`. - -Periodically checks whether an IPv6 address has been assigned to the primary -interface and issues `networkctl reconfigure` as needed. The reason is that -`systemd-networkd` gives up on trying SLAAC autoconfiguration after a while, -so systems will fail to receive network configuration under certain conditions -if the router in their network is down at boot. - == Start node_exporter Service: `node_exporter.service`. Depends on `setup-node_exporter-keys.service`. diff --git a/ic-os/components/boundary-guestos.bzl b/ic-os/components/boundary-guestos.bzl index c77f1564c62..630f9ec3779 100644 --- a/ic-os/components/boundary-guestos.bzl +++ b/ic-os/components/boundary-guestos.bzl @@ -44,7 +44,6 @@ component_files = { Label("boundary-guestos/etc/systemd/system/logrotate.timer"): "/etc/systemd/system/logrotate.timer", Label("boundary-guestos/etc/systemd/system/nftables.service.d/override.conf"): "/etc/systemd/system/nftables.service.d/override.conf", Label("boundary-guestos/etc/systemd/system/node_exporter.service"): "/etc/systemd/system/node_exporter.service", - Label("boundary-guestos/etc/systemd/system/retry-ipv6-config.service"): "/etc/systemd/system/retry-ipv6-config.service", Label("boundary-guestos/etc/systemd/system/save-machine-id.service"): "/etc/systemd/system/save-machine-id.service", Label("boundary-guestos/etc/systemd/system/setup-canary-proxy.service"): "/etc/systemd/system/setup-canary-proxy.service", Label("boundary-guestos/etc/systemd/system/setup-certificate-issuer.service"): "/etc/systemd/system/setup-certificate-issuer.service", @@ -56,8 +55,8 @@ component_files = { Label("boundary-guestos/etc/systemd/system/setup-ic-gateway.service"): "/etc/systemd/system/setup-ic-gateway.service", Label("boundary-guestos/etc/systemd/system/setup-lvs.service"): "/etc/systemd/system/setup-lvs.service", Label("boundary-guestos/etc/systemd/system/setup-nftables.service"): "/etc/systemd/system/setup-nftables.service", - Label("boundary-guestos/etc/systemd/system/setup-ssh-account-keys.service"): "/etc/systemd/system/setup-ssh-account-keys.service", - Label("boundary-guestos/etc/systemd/system/setup-ssh-keys.service"): "/etc/systemd/system/setup-ssh-keys.service", + Label("boundary-guestos/etc/systemd/system/setup-ssh-user-keys.service"): "/etc/systemd/system/setup-ssh-user-keys.service", + Label("boundary-guestos/etc/systemd/system/generate-host-ssh-keys.service"): "/etc/systemd/system/generate-host-ssh-keys.service", Label("boundary-guestos/etc/systemd/system/setup-var-log.service"): "/etc/systemd/system/setup-var-log.service", Label("boundary-guestos/etc/systemd/system/setup-vector.service"): "/etc/systemd/system/setup-vector.service", Label("boundary-guestos/etc/systemd/system/setup-version-metric.service"): "/etc/systemd/system/setup-version-metric.service", @@ -74,7 +73,6 @@ component_files = { Label("boundary-guestos/opt/ic/bin/install-upgrade.sh"): "/opt/ic/bin/install-upgrade.sh", Label("boundary-guestos/opt/ic/bin/manageboot.sh"): "/opt/ic/bin/manageboot.sh", Label("boundary-guestos/opt/ic/bin/metrics.sh"): "/opt/ic/bin/metrics.sh", - Label("boundary-guestos/opt/ic/bin/retry-ipv6-config.sh"): "/opt/ic/bin/retry-ipv6-config.sh", Label("boundary-guestos/opt/ic/bin/save-machine-id.sh"): "/opt/ic/bin/save-machine-id.sh", Label("boundary-guestos/opt/ic/bin/setup-canary-proxy.sh"): "/opt/ic/bin/setup-canary-proxy.sh", Label("boundary-guestos/opt/ic/bin/setup-certificate-issuer.sh"): "/opt/ic/bin/setup-certificate-issuer.sh", @@ -86,8 +84,8 @@ component_files = { Label("boundary-guestos/opt/ic/bin/setup-ic-gateway.sh"): "/opt/ic/bin/setup-ic-gateway.sh", Label("boundary-guestos/opt/ic/bin/setup-lvs.sh"): "/opt/ic/bin/setup-lvs.sh", Label("boundary-guestos/opt/ic/bin/setup-nftables.sh"): "/opt/ic/bin/setup-nftables.sh", - Label("boundary-guestos/opt/ic/bin/setup-ssh-account-keys.sh"): "/opt/ic/bin/setup-ssh-account-keys.sh", - Label("boundary-guestos/opt/ic/bin/setup-ssh-keys.sh"): "/opt/ic/bin/setup-ssh-keys.sh", + Label("boundary-guestos/opt/ic/bin/setup-ssh-user-keys.sh"): "/opt/ic/bin/setup-ssh-user-keys.sh", + Label("boundary-guestos/opt/ic/bin/generate-host-ssh-keys.sh"): "/opt/ic/bin/generate-host-ssh-keys.sh", Label("boundary-guestos/opt/ic/bin/setup-var-encryption.sh"): "/opt/ic/bin/setup-var-encryption.sh", Label("boundary-guestos/opt/ic/bin/setup-var-log.sh"): "/opt/ic/bin/setup-var-log.sh", Label("boundary-guestos/opt/ic/bin/setup-vector.sh"): "/opt/ic/bin/setup-vector.sh", diff --git a/ic-os/components/boundary-guestos/etc/systemd/system/bootstrap-ic-node.service b/ic-os/components/boundary-guestos/etc/systemd/system/bootstrap-ic-node.service index 83a8a65fa51..1cf204ffc21 100644 --- a/ic-os/components/boundary-guestos/etc/systemd/system/bootstrap-ic-node.service +++ b/ic-os/components/boundary-guestos/etc/systemd/system/bootstrap-ic-node.service @@ -2,11 +2,11 @@ Description=Bootstrap the IC node Requires=var-log.mount After=var-log.mount -Before=setup-ssh-account-keys.service +Before=setup-ssh-user-keys.service [Install] WantedBy=multi-user.target -RequiredBy=setup-ssh-account-keys.service +RequiredBy=setup-ssh-user-keys.service [Service] Type=oneshot diff --git a/ic-os/components/ssh/setup-ssh-keys/setup-ssh-keys.service b/ic-os/components/boundary-guestos/etc/systemd/system/generate-host-ssh-keys.service similarity index 84% rename from ic-os/components/ssh/setup-ssh-keys/setup-ssh-keys.service rename to ic-os/components/boundary-guestos/etc/systemd/system/generate-host-ssh-keys.service index b96b685dee3..fc87e3faf42 100644 --- a/ic-os/components/ssh/setup-ssh-keys/setup-ssh-keys.service +++ b/ic-os/components/boundary-guestos/etc/systemd/system/generate-host-ssh-keys.service @@ -8,7 +8,7 @@ Before=ssh.service [Service] Type=oneshot RemainAfterExit=true -ExecStart=/opt/ic/bin/setup-ssh-keys.sh +ExecStart=/opt/ic/bin/generate-host-ssh-keys.sh [Install] WantedBy=multi-user.target diff --git a/ic-os/components/boundary-guestos/etc/systemd/system/retry-ipv6-config.service b/ic-os/components/boundary-guestos/etc/systemd/system/retry-ipv6-config.service deleted file mode 100644 index 3810adb8835..00000000000 --- a/ic-os/components/boundary-guestos/etc/systemd/system/retry-ipv6-config.service +++ /dev/null @@ -1,10 +0,0 @@ -[Unit] -Description=Monitor and retry IPv6 configuration -After=systemd-networkd.service - -[Service] -Type=simple -ExecStart=/opt/ic/bin/retry-ipv6-config.sh - -[Install] -WantedBy=multi-user.target diff --git a/ic-os/components/boundary-guestos/etc/systemd/system/setup-canary-proxy.service b/ic-os/components/boundary-guestos/etc/systemd/system/setup-canary-proxy.service index 5d11767083c..2dd6617e941 100644 --- a/ic-os/components/boundary-guestos/etc/systemd/system/setup-canary-proxy.service +++ b/ic-os/components/boundary-guestos/etc/systemd/system/setup-canary-proxy.service @@ -2,7 +2,7 @@ Description=Setup Canary Proxy DefaultDependencies=no After=bootstrap-ic-node.service -Requires=bootstrap-ic-node.service +Wants=bootstrap-ic-node.service [Service] Type=oneshot diff --git a/ic-os/components/boundary-guestos/etc/systemd/system/setup-certificate-issuer.service b/ic-os/components/boundary-guestos/etc/systemd/system/setup-certificate-issuer.service index 80635eb252b..8cabc04aabe 100644 --- a/ic-os/components/boundary-guestos/etc/systemd/system/setup-certificate-issuer.service +++ b/ic-os/components/boundary-guestos/etc/systemd/system/setup-certificate-issuer.service @@ -2,7 +2,7 @@ Description=Setup Certificate Issuer DefaultDependencies=no After=bootstrap-ic-node.service -Requires=bootstrap-ic-node.service +Wants=bootstrap-ic-node.service [Service] Type=oneshot diff --git a/ic-os/components/boundary-guestos/etc/systemd/system/setup-crowdsec.service b/ic-os/components/boundary-guestos/etc/systemd/system/setup-crowdsec.service index 6ce29953676..ec4b3f77218 100644 --- a/ic-os/components/boundary-guestos/etc/systemd/system/setup-crowdsec.service +++ b/ic-os/components/boundary-guestos/etc/systemd/system/setup-crowdsec.service @@ -2,7 +2,7 @@ Description=Create configuration for Crowdsec DefaultDependencies=no After=bootstrap-ic-node.service -Requires=bootstrap-ic-node.service +Wants=bootstrap-ic-node.service [Service] Type=oneshot diff --git a/ic-os/components/boundary-guestos/etc/systemd/system/setup-ic-boundary.service b/ic-os/components/boundary-guestos/etc/systemd/system/setup-ic-boundary.service index afde457469a..a7d27353639 100644 --- a/ic-os/components/boundary-guestos/etc/systemd/system/setup-ic-boundary.service +++ b/ic-os/components/boundary-guestos/etc/systemd/system/setup-ic-boundary.service @@ -2,7 +2,7 @@ Description=Setup ic-boundary DefaultDependencies=no After=bootstrap-ic-node.service -Requires=bootstrap-ic-node.service +Wants=bootstrap-ic-node.service [Service] Type=oneshot diff --git a/ic-os/components/boundary-guestos/etc/systemd/system/setup-ic-gateway.service b/ic-os/components/boundary-guestos/etc/systemd/system/setup-ic-gateway.service index ee35f04691c..67b83acf93e 100644 --- a/ic-os/components/boundary-guestos/etc/systemd/system/setup-ic-gateway.service +++ b/ic-os/components/boundary-guestos/etc/systemd/system/setup-ic-gateway.service @@ -2,7 +2,7 @@ Description=Setup environment for ic-gateway DefaultDependencies=no After=bootstrap-ic-node.service -Requires=bootstrap-ic-node.service +Wants=bootstrap-ic-node.service [Service] Type=oneshot diff --git a/ic-os/components/boundary-guestos/etc/systemd/system/setup-nftables.service b/ic-os/components/boundary-guestos/etc/systemd/system/setup-nftables.service index 77f7f0918c6..e78f9e449d6 100644 --- a/ic-os/components/boundary-guestos/etc/systemd/system/setup-nftables.service +++ b/ic-os/components/boundary-guestos/etc/systemd/system/setup-nftables.service @@ -2,7 +2,7 @@ Description=Setup variable files for nftables DefaultDependencies=no After=bootstrap-ic-node.service -Requires=bootstrap-ic-node.service +Wants=bootstrap-ic-node.service [Service] Type=oneshot diff --git a/ic-os/components/ssh/setup-ssh-account-keys/setup-ssh-account-keys.service b/ic-os/components/boundary-guestos/etc/systemd/system/setup-ssh-user-keys.service similarity index 82% rename from ic-os/components/ssh/setup-ssh-account-keys/setup-ssh-account-keys.service rename to ic-os/components/boundary-guestos/etc/systemd/system/setup-ssh-user-keys.service index 2a862b3f2c4..3fdbbaa3db0 100644 --- a/ic-os/components/ssh/setup-ssh-account-keys/setup-ssh-account-keys.service +++ b/ic-os/components/boundary-guestos/etc/systemd/system/setup-ssh-user-keys.service @@ -1,5 +1,5 @@ [Unit] -Description=Set up ssh account keys +Description=Set up ssh user keys Before=ssh.service # bootstrap-ic-node.service (if it exists) lists this service as a reverse dependency @@ -9,7 +9,7 @@ WantedBy=multi-user.target [Service] Type=oneshot RemainAfterExit=true -ExecStart=/opt/ic/bin/setup-ssh-account-keys.sh +ExecStart=/opt/ic/bin/setup-ssh-user-keys.sh # All services that networking depends on log their outputs to the console # and are piped to the host terminal if the verbose flag is enabled. diff --git a/ic-os/components/boundary-guestos/etc/systemd/system/setup-vector.service b/ic-os/components/boundary-guestos/etc/systemd/system/setup-vector.service index edb31a5745f..4207175e8f2 100644 --- a/ic-os/components/boundary-guestos/etc/systemd/system/setup-vector.service +++ b/ic-os/components/boundary-guestos/etc/systemd/system/setup-vector.service @@ -2,7 +2,7 @@ Description=Set variable files (i.e. ELASTICSEARCH_URL) for vector DefaultDependencies=no After=bootstrap-ic-node.service -Requires=bootstrap-ic-node.service +Wants=bootstrap-ic-node.service [Service] Type=oneshot diff --git a/ic-os/components/boundary-guestos/etc/systemd/system/setup-version-metric.service b/ic-os/components/boundary-guestos/etc/systemd/system/setup-version-metric.service index 46b516e31f8..1dc246b4a10 100644 --- a/ic-os/components/boundary-guestos/etc/systemd/system/setup-version-metric.service +++ b/ic-os/components/boundary-guestos/etc/systemd/system/setup-version-metric.service @@ -2,7 +2,7 @@ Description=Move version metric file to the metrics directory DefaultDependencies=no After=bootstrap-ic-node.service -Requires=bootstrap-ic-node.service +Wants=bootstrap-ic-node.service [Service] Type=oneshot diff --git a/ic-os/components/boundary-guestos/opt/ic/bin/setup-ssh-keys.sh b/ic-os/components/boundary-guestos/opt/ic/bin/generate-host-ssh-keys.sh similarity index 100% rename from ic-os/components/boundary-guestos/opt/ic/bin/setup-ssh-keys.sh rename to ic-os/components/boundary-guestos/opt/ic/bin/generate-host-ssh-keys.sh diff --git a/ic-os/components/boundary-guestos/opt/ic/bin/retry-ipv6-config.sh b/ic-os/components/boundary-guestos/opt/ic/bin/retry-ipv6-config.sh deleted file mode 100755 index d727e5ebeef..00000000000 --- a/ic-os/components/boundary-guestos/opt/ic/bin/retry-ipv6-config.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash - -# Get address of interface -# -# Arguments: -# - $1: address family (4 or 6 for IPv4 or IPv6) -# - $2: interface name -function get_if_address() { - local FAMILY=-"$1" - local INTERFACE="$2" - ip -o "${FAMILY}" addr show up primary scope global "${INTERFACE}" | while read -r num dev family addr options; do - echo ${addr%/*} - break - done -} - -# Get address of interface -# -# Arguments: -# - $1: address family (4 or 6 for IPv4 or IPv6) -# - $2: interface name -function check_retry_interface_config() { - local FAMILY=-"$1" - local INTERFACE="$2" - local ADDR=$(get_if_address "${FAMILY}" "${INTERFACE}") - - if [ "${ADDR}" == "" ]; then - echo "Interface ${INTERFACE} not configured properly, forcing reconfiguration" - networkctl reconfigure "${INTERFACE}" - fi -} - -while true; do - check_retry_interface_config 6 enp1s0 - sleep 10 -done diff --git a/ic-os/components/boundary-guestos/opt/ic/bin/setup-ic-gateway.sh b/ic-os/components/boundary-guestos/opt/ic/bin/setup-ic-gateway.sh index a89ca3edd54..f18ec038651 100755 --- a/ic-os/components/boundary-guestos/opt/ic/bin/setup-ic-gateway.sh +++ b/ic-os/components/boundary-guestos/opt/ic/bin/setup-ic-gateway.sh @@ -133,6 +133,7 @@ POLICY_PRE_ISOLATION_CANISTERS="${RUN_DIR}/pre_isolation_canisters.txt" POLICY_DENYLIST_ALLOWLIST="${RUN_DIR}/allowlist.txt" POLICY_DENYLIST_SEED="${RUN_DIR}/denylist.json" DOMAIN_CANISTER_ALIAS="identity:rdmx6-jaaaa-aaaaa-aaadq-cai,nns:qoctq-giaaa-aaaaa-aaaea-cai" +DOMAIN_CUSTOM_PROVIDER="https://boundary.caffeine.ai/subdomains,https://beta.boundary.caffeine.ai/subdomains" GEOIP_DB="${RUN_DIR}/GeoLite2-Country.mmdb" IC_URL="http://127.0.0.1:9000" IC_ROOT_KEY="${ROOT_KEY}" @@ -148,9 +149,6 @@ CACHE_XFETCH_BETA="3.0" SHED_SYSTEM_EWMA="0.9" SHED_SYSTEM_CPU="0.95" SHED_SYSTEM_MEMORY="0.95" -SHED_SHARDED_EWMA="0.6" -SHED_SHARDED_PASSTHROUGH="20000" -SHED_SHARDED_LATENCY="query:1s,call:1s,sync_call:13s,read_state:1s,read_state_subnet:1s,status:100ms,health:100ms,registrations:5s,http:5s" EOF if [ ! -z "${DENYLIST_URL:-}" ]; then @@ -166,10 +164,6 @@ EOF if [ ! -z "${MAX_CONCURRENCY:-}" ]; then echo "LOAD_MAX_CONCURRENCY=\"${MAX_CONCURRENCY}\"" >>"${ENV_FILE}" fi - - if [ ! -z "${SHED_EWMA_PARAM:-}" ]; then - echo "LOAD_SHED_EWMA_PARAM=\"${SHED_EWMA_PARAM}\"" >>"${ENV_FILE}" - fi } function setup_pre_isolation_canisters() { diff --git a/ic-os/components/boundary-guestos/opt/ic/bin/setup-ssh-account-keys.sh b/ic-os/components/boundary-guestos/opt/ic/bin/setup-ssh-user-keys.sh similarity index 100% rename from ic-os/components/boundary-guestos/opt/ic/bin/setup-ssh-account-keys.sh rename to ic-os/components/boundary-guestos/opt/ic/bin/setup-ssh-user-keys.sh diff --git a/ic-os/components/early-boot/setup-hostname/hostos/setup-hostname.service b/ic-os/components/early-boot/setup-hostname/hostos/setup-hostname.service index c9dcdab46bc..d37cb1ff51c 100644 --- a/ic-os/components/early-boot/setup-hostname/hostos/setup-hostname.service +++ b/ic-os/components/early-boot/setup-hostname/hostos/setup-hostname.service @@ -10,6 +10,8 @@ After=dev-ipmi0.device Type=oneshot RemainAfterExit=true ExecStart=/opt/ic/bin/setup-hostname.sh --type=host +StandardOutput=journal+console +StandardError=journal+console [Install] WantedBy=multi-user.target diff --git a/ic-os/components/guestos.bzl b/ic-os/components/guestos.bzl index a97124f7c3b..e33afa30e6d 100644 --- a/ic-os/components/guestos.bzl +++ b/ic-os/components/guestos.bzl @@ -63,6 +63,8 @@ component_files = { Label("misc/guestos/sysctl.d/privileged-ports.conf"): "/etc/sysctl.d/privileged-ports.conf", Label("misc/guestos/sysfs.d/hugepage.conf"): "/etc/sysfs.d/hugepage.conf", Label("misc/guestos/hsm/pcscd"): "/etc/default/pcscd", + Label("misc/log-config/log-config-guestos.service"): "/etc/systemd/system/log-config.service", + Label("misc/log-config/log-config.sh"): "/opt/ic/bin/log-config.sh", # monitoring Label("monitoring/filebeat/setup-filebeat-permissions.sh"): "/opt/ic/bin/setup-filebeat-permissions.sh", @@ -94,8 +96,6 @@ component_files = { # networking Label("networking/generate-network-config/guestos/generate-network-config.service"): "/etc/systemd/system/generate-network-config.service", - Label("networking/retry-ipv6-config/retry-ipv6-config.sh"): "/opt/ic/bin/retry-ipv6-config.sh", - Label("networking/retry-ipv6-config/retry-ipv6-config.service"): "/etc/systemd/system/retry-ipv6-config.service", Label("networking/nftables/reload_nftables.path"): "/etc/systemd/system/reload_nftables.path", Label("networking/nftables/reload_nftables.service"): "/etc/systemd/system/reload_nftables.service", Label("networking/nftables/nftables-empty.conf"): "/etc/nftables.conf", @@ -138,10 +138,10 @@ component_files = { # ssh Label("ssh/provision-ssh-keys.sh"): "/opt/ic/bin/provision-ssh-keys.sh", - Label("ssh/setup-ssh-keys/setup-ssh-keys.sh"): "/opt/ic/bin/setup-ssh-keys.sh", - Label("ssh/setup-ssh-keys/setup-ssh-keys.service"): "/etc/systemd/system/setup-ssh-keys.service", - Label("ssh/setup-ssh-account-keys/setup-ssh-account-keys.sh"): "/opt/ic/bin/setup-ssh-account-keys.sh", - Label("ssh/setup-ssh-account-keys/setup-ssh-account-keys.service"): "/etc/systemd/system/setup-ssh-account-keys.service", + Label("ssh/generate-host-ssh-keys/generate-host-ssh-keys.sh"): "/opt/ic/bin/generate-host-ssh-keys.sh", + Label("ssh/generate-host-ssh-keys/generate-host-ssh-keys.service"): "/etc/systemd/system/generate-host-ssh-keys.service", + Label("ssh/setup-ssh-user-keys/setup-ssh-user-keys.sh"): "/opt/ic/bin/setup-ssh-user-keys.sh", + Label("ssh/setup-ssh-user-keys/setup-ssh-user-keys.service"): "/etc/systemd/system/setup-ssh-user-keys.service", Label("ssh/read-ssh-keys.sh"): "/opt/ic/bin/read-ssh-keys.sh", # upgrade @@ -162,6 +162,6 @@ component_files = { # fstrim Label("fstrim/sync_fstrim.sh"): "/opt/ic/bin/sync_fstrim.sh", - # TODO(NODE-1519): delete update-config.service after switch to new icos config + # TODO(NODE-1518): delete update-config.service after switch to new icos config Label("misc/update-config/update-guestos-config.service"): "/etc/systemd/system/update-config.service", } diff --git a/ic-os/components/hostos-scripts/guestos/guestos.service b/ic-os/components/hostos-scripts/guestos/guestos.service index 36b46864b4e..04586adfb36 100644 --- a/ic-os/components/hostos-scripts/guestos/guestos.service +++ b/ic-os/components/hostos-scripts/guestos/guestos.service @@ -13,6 +13,7 @@ ExecStart=/opt/ic/bin/start-guestos.sh ExecStartPost=/opt/ic/bin/manageboot.sh hostos confirm ExecStop=/opt/ic/bin/stop-guestos.sh Restart=on-failure +RestartSec=300 [Install] WantedBy=multi-user.target diff --git a/ic-os/components/hostos-scripts/guestos/start-guestos.sh b/ic-os/components/hostos-scripts/guestos/start-guestos.sh index dec1c9ac938..3dab15b4855 100755 --- a/ic-os/components/hostos-scripts/guestos/start-guestos.sh +++ b/ic-os/components/hostos-scripts/guestos/start-guestos.sh @@ -32,6 +32,14 @@ done # Set arguments if undefined CONFIG="${CONFIG:=/var/lib/libvirt/guestos.xml}" +write_tty1_log() { + local message=$1 + + echo "${SCRIPT} ${message}" >/dev/tty1 + + logger -t "${SCRIPT}" "${message}" +} + function define_guestos() { if [ "$(virsh list --all | grep 'guestos')" ]; then write_log "GuestOS virtual machine is already defined." @@ -40,8 +48,8 @@ function define_guestos() { "GuestOS virtual machine define state" \ "gauge" else - virsh define ${CONFIG} write_log "Defining GuestOS virtual machine." + virsh define ${CONFIG} write_metric "hostos_guestos_service_define" \ "1" \ "GuestOS virtual machine define state" \ @@ -57,7 +65,50 @@ function start_guestos() { "GuestOS virtual machine start state" \ "gauge" else - virsh start guestos + write_log "Starting GuestOS virtual machine." + # Attempt to start; if it fails, dump logs. + if ! virsh start guestos; then + # The sleep below gives QEMU time to clear the console so that + # error messages won't be immediately overwritten. + sleep 10 + + write_tty1_log "ERROR: Failed to start GuestOS virtual machine." + + write_tty1_log "#################################################" + write_tty1_log "### LOGGING GUESTOS.SERVICE LOGS... ###" + write_tty1_log "#################################################" + journalctl -u guestos.service >/dev/tty1 + + write_tty1_log "#################################################" + write_tty1_log "### TROUBLESHOOTING INFO... ###" + write_tty1_log "#################################################" + host_ipv6_address="$(/opt/ic/bin/hostos_tool generate-ipv6-address --node-type HostOS 2>/dev/null)" + write_tty1_log "Host IPv6 address: $host_ipv6_address" + + if [ -f /var/log/libvirt/qemu/guestos-serial.log ]; then + write_tty1_log "#################################################" + write_tty1_log "### LOGGING GUESTOS CONSOLE LOGS, IF ANY... ###" + write_tty1_log "#################################################" + tail -n 30 /var/log/libvirt/qemu/guestos-serial.log | while IFS= read -r line; do + write_tty1_log "$line" + done + else + write_tty1_log "No /var/log/libvirt/qemu/guestos-serial.log file found." + fi + + write_tty1_log "Exiting start-guestos.sh so that systemd can restart guestos.service in 5 minutes." + exit 1 + fi + + sleep 10 + write_tty1_log "" + write_tty1_log "#################################################" + write_tty1_log "GuestOS virtual machine launched" + write_tty1_log "IF ONBOARDING, please wait for up to 10 MINUTES for a 'Join request successful!' message" + host_ipv6_address="$(/opt/ic/bin/hostos_tool generate-ipv6-address --node-type HostOS 2>/dev/null)" + write_tty1_log "Host IPv6 address: $host_ipv6_address" + write_tty1_log "#################################################" + write_log "Starting GuestOS virtual machine." write_metric "hostos_guestos_service_start" \ "1" \ diff --git a/ic-os/components/hostos-scripts/monitoring/monitor-guestos.service b/ic-os/components/hostos-scripts/monitoring/monitor-guestos.service index 695273624ef..29f5c9be0cd 100644 --- a/ic-os/components/hostos-scripts/monitoring/monitor-guestos.service +++ b/ic-os/components/hostos-scripts/monitoring/monitor-guestos.service @@ -1,10 +1,7 @@ [Unit] Description=Monitor GuestOS virtual machine -Requires=libvirtd.service -After=libvirtd.service -Requires=generate-guestos-config.service -After=generate-guestos-config.service After=guestos.service +Wants=guestos.service [Service] Type=oneshot diff --git a/ic-os/components/hostos-scripts/verbose-logging/verbose-logging.service b/ic-os/components/hostos-scripts/verbose-logging/verbose-logging.service index ce22f7579cd..09bee517f73 100644 --- a/ic-os/components/hostos-scripts/verbose-logging/verbose-logging.service +++ b/ic-os/components/hostos-scripts/verbose-logging/verbose-logging.service @@ -1,6 +1,6 @@ [Unit] Description=If verbose flag enabled, pipe GuestOS console to the Host terminal -Requires=guestos.service +Wants=guestos.service After=guestos.service [Service] diff --git a/ic-os/components/hostos.bzl b/ic-os/components/hostos.bzl index ab2acb54659..542958caece 100644 --- a/ic-os/components/hostos.bzl +++ b/ic-os/components/hostos.bzl @@ -29,8 +29,6 @@ component_files = { Label("hostos-scripts/verbose-logging/verbose-logging.sh"): "/opt/ic/bin/verbose-logging.sh", Label("hostos-scripts/verbose-logging/verbose-logging.service"): "/etc/systemd/system/verbose-logging.service", Label("hostos-scripts/verbose-logging/logrotate.d/verbose-logging"): "/etc/logrotate.d/verbose-logging", - Label("hostos-scripts/log-config/log-config.service"): "/etc/systemd/system/log-config.service", - Label("hostos-scripts/log-config/log-config.sh"): "/opt/ic/bin/log-config.sh", # early-boot Label("early-boot/relabel-machine-id/relabel-machine-id.sh"): "/opt/ic/bin/relabel-machine-id.sh", @@ -73,6 +71,8 @@ component_files = { Label("monitoring/metrics-proxy/metrics-proxy.service"): "/etc/systemd/system/metrics-proxy.service", Label("monitoring/journald.conf"): "/etc/systemd/journald.conf", Label("monitoring/logrotate/override.conf"): "/etc/systemd/system/logrotate.service.d/override.conf", + Label("misc/log-config/log-config-hostos.service"): "/etc/systemd/system/log-config.service", + Label("misc/log-config/log-config.sh"): "/opt/ic/bin/log-config.sh", # networking Label("networking/setup-networking/setup-networking-hostos.service"): "/etc/systemd/system/setup-networking.service", @@ -87,12 +87,10 @@ component_files = { Label("networking/hosts"): "/etc/hosts", # ssh - Label("ssh/setup-ssh-keys/setup-ssh-keys.sh"): "/opt/ic/bin/setup-ssh-keys.sh", - Label("ssh/setup-ssh-keys/setup-ssh-keys.service"): "/etc/systemd/system/setup-ssh-keys.service", - Label("ssh/setup-ssh-account-keys/setup-ssh-account-keys.sh"): "/opt/ic/bin/setup-ssh-account-keys.sh", - Label("ssh/setup-ssh-account-keys/setup-ssh-account-keys.service"): "/etc/systemd/system/setup-ssh-account-keys.service", - Label("ssh/deploy-updated-ssh-account-keys/deploy-updated-ssh-account-keys.sh"): "/opt/ic/bin/deploy-updated-ssh-account-keys.sh", - Label("ssh/deploy-updated-ssh-account-keys/deploy-updated-ssh-account-keys.service"): "/etc/systemd/system/deploy-updated-ssh-account-keys.service", + Label("ssh/generate-host-ssh-keys/generate-host-ssh-keys.sh"): "/opt/ic/bin/generate-host-ssh-keys.sh", + Label("ssh/generate-host-ssh-keys/generate-host-ssh-keys.service"): "/etc/systemd/system/generate-host-ssh-keys.service", + Label("ssh/setup-ssh-user-keys/setup-ssh-user-keys.sh"): "/opt/ic/bin/setup-ssh-user-keys.sh", + Label("ssh/setup-ssh-user-keys/setup-ssh-user-keys.service"): "/etc/systemd/system/setup-ssh-user-keys.service", # upgrade Label("upgrade/manageboot/manageboot.sh"): "/opt/ic/bin/manageboot.sh", @@ -100,6 +98,6 @@ component_files = { Label("upgrade/systemd-generators/systemd-gpt-auto-generator"): "/etc/systemd/system-generators/systemd-gpt-auto-generator", Label("upgrade/install-upgrade.sh"): "/opt/ic/bin/install-upgrade.sh", - # TODO(NODE-1519): delete update-config.service after switch to new icos config + # TODO(NODE-1518): delete update-config.service after switch to new icos config Label("misc/update-config/update-hostos-config.service"): "/etc/systemd/system/update-config.service", } diff --git a/ic-os/components/ic/generate-ic-config/generate-ic-config.service b/ic-os/components/ic/generate-ic-config/generate-ic-config.service index 3d6d08311f3..1090975dfca 100644 --- a/ic-os/components/ic/generate-ic-config/generate-ic-config.service +++ b/ic-os/components/ic/generate-ic-config/generate-ic-config.service @@ -20,3 +20,8 @@ ExecStart=/opt/ic/bin/generate-ic-config.sh -n /boot/config/network.conf -c /boo [Install] WantedBy=multi-user.target + +# All services that networking depends on log their outputs to the console +# and are piped to the host terminal if the verbose flag is enabled. +StandardOutput=journal+console +StandardError=journal+console \ No newline at end of file diff --git a/ic-os/components/ic/generate-ic-config/generate-ic-config.sh b/ic-os/components/ic/generate-ic-config/generate-ic-config.sh index 3798cabef20..89acb7053a5 100755 --- a/ic-os/components/ic/generate-ic-config/generate-ic-config.sh +++ b/ic-os/components/ic/generate-ic-config/generate-ic-config.sh @@ -6,7 +6,7 @@ function usage() { cat <"${OUT_FILE}" diff --git a/ic-os/components/ic/generate-ic-config/ic.json5.template b/ic-os/components/ic/generate-ic-config/ic.json5.template index 43b662488d4..9bb90f72b34 100644 --- a/ic-os/components/ic/generate-ic-config/ic.json5.template +++ b/ic-os/components/ic/generate-ic-config/ic.json5.template @@ -505,6 +505,7 @@ table ip6 filter {\n\ }, registration: { + node_reward_type: "{{ node_reward_type }}", nns_url: "{{ nns_urls }}", nns_pub_key_pem: "/var/lib/ic/data/nns_public_key.pem", node_operator_pem: "/var/lib/ic/data/node_operator_private_key.pem" diff --git a/ic-os/components/ic/setup-permissions/erestorecon.sh b/ic-os/components/ic/setup-permissions/erestorecon.sh index 3cc9a4192e5..876c59f9af5 100755 --- a/ic-os/components/ic/setup-permissions/erestorecon.sh +++ b/ic-os/components/ic/setup-permissions/erestorecon.sh @@ -5,4 +5,4 @@ set -e # erestorecon (easy prestorecon) uses UNIX tools to parallelize restorecon, # instead of the cpp based prestorecon. -find $@ -print0 | xargs -0 -P 0 restorecon +find $@ -print0 | xargs -0 -P 0 restorecon -F diff --git a/ic-os/components/ic/setup-permissions/setup-permissions.sh b/ic-os/components/ic/setup-permissions/setup-permissions.sh index 8576d1e1e60..9b073af4954 100755 --- a/ic-os/components/ic/setup-permissions/setup-permissions.sh +++ b/ic-os/components/ic/setup-permissions/setup-permissions.sh @@ -24,8 +24,8 @@ function make_group_owned_and_sticky() { local GROUP="$3" mkdir -p "${TARGET_DIR}" - chown -R "${USER}:${GROUP}" "${TARGET_DIR}" - chmod u=rwX,g=rX,o= -R "${TARGET_DIR}" + find "${TARGET_DIR}" -print0 | xargs -0 -P 0 chown "${USER}:${GROUP}" + find "${TARGET_DIR}" -print0 | xargs -0 -P 0 chmod u=rwX,g=rX,o= find "${TARGET_DIR}" -type d | xargs chmod g+s } diff --git a/ic-os/components/ic/share/ic-boundary.env b/ic-os/components/ic/share/ic-boundary.env index 570be9bb8ae..52089c0d9df 100644 --- a/ic-os/components/ic/share/ic-boundary.env +++ b/ic-os/components/ic/share/ic-boundary.env @@ -6,6 +6,8 @@ HTTP_CLIENT_TIMEOUT_CONNECT="3s" REGISTRY_DISABLE_REPLICATOR="true" REGISTRY_LOCAL_STORE_PATH="/var/lib/ic/data/ic_registry_local_store" OBS_LOG_ANONYMIZATION_CANISTER_ID="uz2z3-qyaaa-aaaaq-qaacq-cai" +RATE_LIMIT_GENERIC_CANISTER_ID="u637p-5aaaa-aaaaq-qaaca-cai" +RATE_LIMIT_GENERIC_AUTOSCALE="true" OBS_LOG_JOURNALD="true" OBS_METRICS_ADDR="[::]:9324" RATE_LIMIT_PER_SECOND_PER_SUBNET="1000" diff --git a/ic-os/components/init/README.adoc b/ic-os/components/init/README.adoc index d7daa555fe6..5b79ee3587c 100644 --- a/ic-os/components/init/README.adoc +++ b/ic-os/components/init/README.adoc @@ -6,7 +6,7 @@ serve as a guide on how to add further actions. == ssh key generation -The +ssh/setup-ssh-keys+ (and corresponding shell script) service performs one of +The +ssh/generate-host-ssh-keys+ (and corresponding shell script) service performs one of two things: If this is the first boot ever (on a newly installed system), it generates ssh keys and stashes them away in a location that is preserved across upgrades. On first boot after an upgrade, it integrates the keys from their diff --git a/ic-os/components/init/bootstrap-ic-node/bootstrap-ic-node.service b/ic-os/components/init/bootstrap-ic-node/bootstrap-ic-node.service index 78c040b79ce..055d549b920 100644 --- a/ic-os/components/init/bootstrap-ic-node/bootstrap-ic-node.service +++ b/ic-os/components/init/bootstrap-ic-node/bootstrap-ic-node.service @@ -7,11 +7,11 @@ Requires=var-lib-ic-crypto.mount After=var-lib-ic-crypto.mount Requires=var-lib-ic-backup.mount After=var-lib-ic-backup.mount -Before=setup-ssh-account-keys.service +Before=setup-ssh-user-keys.service [Install] WantedBy=multi-user.target -RequiredBy=setup-ssh-account-keys.service +RequiredBy=setup-ssh-user-keys.service [Service] Type=oneshot diff --git a/ic-os/components/misc/config/setupos/config.sh b/ic-os/components/misc/config/setupos/config.sh new file mode 100644 index 00000000000..630b5ab8ff5 --- /dev/null +++ b/ic-os/components/misc/config/setupos/config.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +# Shared config utilities. + +# Retrieves a value from the config.json file using a JSON path. +# Arguments: +# $1 - JSON path to the desired value (e.g., '.icos_settings.nns_urls') +# Returns: +# If key is not found or value is "null", returns empty string. +# Otherwise, returns value. +function get_config_value() { + local CONFIG_FILE="/var/ic/config/config.json" + local key=$1 + + local value=$(jq -r "${key}" "${CONFIG_FILE}") + + if [[ "${value}" == "null" ]]; then + echo "" + else + echo "${value}" + fi +} diff --git a/ic-os/components/misc/log-config/log-config-guestos.service b/ic-os/components/misc/log-config/log-config-guestos.service new file mode 100644 index 00000000000..c0de49d7eac --- /dev/null +++ b/ic-os/components/misc/log-config/log-config-guestos.service @@ -0,0 +1,14 @@ +[Unit] +Description=Log config partition +After=bootstrap-ic-node.service +Wants=bootstrap-ic-node.service +After=update-config.service +Wants=update-config.service + +[Service] +Type=oneshot +ExecStart=/opt/ic/bin/log-config.sh +RemainAfterExit=true + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/ic-os/components/hostos-scripts/log-config/log-config.service b/ic-os/components/misc/log-config/log-config-hostos.service similarity index 58% rename from ic-os/components/hostos-scripts/log-config/log-config.service rename to ic-os/components/misc/log-config/log-config-hostos.service index b5e319f5a6f..90bb67bbe41 100644 --- a/ic-os/components/hostos-scripts/log-config/log-config.service +++ b/ic-os/components/misc/log-config/log-config-hostos.service @@ -1,5 +1,7 @@ [Unit] -Description=Log HostOS config partition +Description=Log config partition +After=update-config.service +Wants=update-config.service [Service] Type=oneshot diff --git a/ic-os/components/hostos-scripts/log-config/log-config.sh b/ic-os/components/misc/log-config/log-config.sh similarity index 82% rename from ic-os/components/hostos-scripts/log-config/log-config.sh rename to ic-os/components/misc/log-config/log-config.sh index 5f8399c90b4..afb4655e306 100644 --- a/ic-os/components/hostos-scripts/log-config/log-config.sh +++ b/ic-os/components/misc/log-config/log-config.sh @@ -1,8 +1,7 @@ #!/bin/bash CONFIG_DIR="/boot/config" -CONFIG="/boot/config/config.ini" -DEPLOYMENT="/boot/config/deployment.json" +CONFIG="/boot/config/config.json" log_directory_structure() { local dir=$1 @@ -28,7 +27,6 @@ log_file_contents() { fi } -echo "Logging HostOS config partition" +echo "Logging config partition" log_directory_structure "$CONFIG_DIR" log_file_contents "$CONFIG" -log_file_contents "$DEPLOYMENT" diff --git a/ic-os/components/misc/update-config/update-guestos-config.service b/ic-os/components/misc/update-config/update-guestos-config.service index 5e92de2d043..317b84839ad 100644 --- a/ic-os/components/misc/update-config/update-guestos-config.service +++ b/ic-os/components/misc/update-config/update-guestos-config.service @@ -1,7 +1,7 @@ [Unit] Description=Update GuestOS Configuration After=bootstrap-ic-node.service -Requires=bootstrap-ic-node.service +Wants=bootstrap-ic-node.service [Service] Type=oneshot diff --git a/ic-os/components/monitoring/node_exporter/node_exporter.service b/ic-os/components/monitoring/node_exporter/node_exporter.service index f011883d9b5..cbf5961cce5 100644 --- a/ic-os/components/monitoring/node_exporter/node_exporter.service +++ b/ic-os/components/monitoring/node_exporter/node_exporter.service @@ -1,6 +1,7 @@ [Unit] Description=Node Exporter After=network.target +Wants=setup-node_exporter-keys.service [Service] User=node_exporter diff --git a/ic-os/components/networking/generate-network-config/guestos/generate-network-config.service b/ic-os/components/networking/generate-network-config/guestos/generate-network-config.service index 33f6fc57ef4..98e00f951d9 100644 --- a/ic-os/components/networking/generate-network-config/guestos/generate-network-config.service +++ b/ic-os/components/networking/generate-network-config/guestos/generate-network-config.service @@ -1,7 +1,7 @@ [Unit] Description=Generate network config After=bootstrap-ic-node.service -Requires=bootstrap-ic-node.service +Wants=bootstrap-ic-node.service Before=systemd-networkd.service [Install] diff --git a/ic-os/components/networking/nftables/hostos/nftables.template b/ic-os/components/networking/nftables/hostos/nftables.template index 04d7acb4798..065ac59bbc0 100644 --- a/ic-os/components/networking/nftables/hostos/nftables.template +++ b/ic-os/components/networking/nftables/hostos/nftables.template @@ -92,7 +92,6 @@ table ip6 filter { 2001:920:401a:1710::/64, # BR1 2001:920:401a:1706::/64, # BR2 2a04:9dc0:0:108::/64, # BU1 - 2607:f6f0:3004::/48, # CH1-old 2602:fb2b:120::/48, # CH1 InfraDC prefix 2604:7e00:50::/64, # CH2 2607:ff70:3:2::/64, # CH3 @@ -101,7 +100,6 @@ table ip6 filter { 2604:6800:258:1::/64, # DM1 InfraDC annex 2600:3000:1300:1300::/64, # DN1 2001:470:1:c76::/64, # FM1 - 2001:4d78:40d::/48, # FR1-old 2602:fb2b:110::/48, # FR1 InfraDC prefix 2001:4d78:400:10a::/64, # FR2 2604:1380:4091:3000::/56, # FR2 Equinix boundary @@ -122,11 +120,11 @@ table ip6 filter { 2610:190:6000:1::/64, # PH1 2600:3004:1200:1200::/56, # PL1 2600:c00:2:100::/64, # SE1 InfraDC annex - 2602:fb2b:100::/48, # SF1 InfraDC prefix 2401:3f00:1000:24::/64, # SG1 2604:1380:40e1:4700::/56, # SG1 Equinix boundary 2401:3f00:1000:22::/64, # SG2 2401:3f00:1000:23::/64, # SG3 + 2001:4c08:2003:b09::/64, # SH1 2600:c02:b002:15::/64, # SJ1 2610:190:df01:5::/64, # ST1 2604:1380:45e1:a600::/56, # SV15 Equinix boundary diff --git a/ic-os/components/networking/nftables/hostos/setup-nftables.service b/ic-os/components/networking/nftables/hostos/setup-nftables.service index 00237de5698..edb65c6c398 100644 --- a/ic-os/components/networking/nftables/hostos/setup-nftables.service +++ b/ic-os/components/networking/nftables/hostos/setup-nftables.service @@ -1,9 +1,5 @@ [Unit] Description=Generate Firewall Configuration -# We must wait for IC bootstrap to complete: It writes various -# state files and may also be needed to obtain network config. -After=bootstrap-ic-node.service -Wants=bootstrap-ic-node.service # We must also wait for storage permission fixup to have finished. After=setup-permissions.service Wants=setup-permissions.service diff --git a/ic-os/components/networking/retry-ipv6-config/retry-ipv6-config.service b/ic-os/components/networking/retry-ipv6-config/retry-ipv6-config.service deleted file mode 100644 index 3810adb8835..00000000000 --- a/ic-os/components/networking/retry-ipv6-config/retry-ipv6-config.service +++ /dev/null @@ -1,10 +0,0 @@ -[Unit] -Description=Monitor and retry IPv6 configuration -After=systemd-networkd.service - -[Service] -Type=simple -ExecStart=/opt/ic/bin/retry-ipv6-config.sh - -[Install] -WantedBy=multi-user.target diff --git a/ic-os/components/networking/retry-ipv6-config/retry-ipv6-config.sh b/ic-os/components/networking/retry-ipv6-config/retry-ipv6-config.sh deleted file mode 100755 index f61ffe592a0..00000000000 --- a/ic-os/components/networking/retry-ipv6-config/retry-ipv6-config.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash - -# Get address of interface -# -# Arguments: -# - $1: address family (4 or 6 for IPv4 or IPv6) -# - $2: interface name -function get_if_address() { - local FAMILY=-"$1" - local INTERFACE="$2" - ip -o "${FAMILY}" addr show up primary scope global "${INTERFACE}" | while read -r num dev family addr options; do - echo ${addr%/*} - break - done -} - -# Get address of interface -# -# Arguments: -# - $1: address family (4 or 6 for IPv4 or IPv6) -# - $2: interface name -function check_retry_interface_config() { - local FAMILY=-"$1" - local INTERFACE="$2" - local ADDR=$(get_if_address "${FAMILY}" "${INTERFACE}") - - if [ "${ADDR}" == "" ]; then - echo "Interface ${INTERFACE} not configured properly, forcing reconfiguration" - networkctl reconfigure "${INTERFACE}" - fi -} - -INTERFACE=($(find /sys/class/net -type l -not -lname '*virtual*' -exec basename '{}' ';')) -while true; do - check_retry_interface_config 6 ${INTERFACE} - sleep 10 -done diff --git a/ic-os/components/setupos-scripts/check-config.sh b/ic-os/components/setupos-scripts/check-config.sh new file mode 100644 index 00000000000..06a61de1ce5 --- /dev/null +++ b/ic-os/components/setupos-scripts/check-config.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash + +# check-config.sh verifies the existence of the configuration JSON file created by config.service, +# halting the installation if not found. + +set -o nounset +set -o pipefail + +SHELL="/bin/bash" +PATH="/sbin:/bin:/usr/sbin:/usr/bin" + +source /opt/ic/bin/functions.sh + +check_config_file() { + echo "* Checking Config..." + local CONFIG_FILE="/var/ic/config/config.json" + + if [ -f "${CONFIG_FILE}" ]; then + local config_contents=$(cat "${CONFIG_FILE}") + echo -e "Configuration file '${CONFIG_FILE}' exists.\n" + echo -e "File contents:\n${config_contents}" + else + local service_logs=$(journalctl -u config.service --no-pager) + local log_message="Error creating SetupOS configuration. Configuration file '${CONFIG_FILE}' does not exist.\n\nConfig.service logs:\n${service_logs}" + + log_and_halt_installation_on_error 1 "${log_message}" + fi +} + +# Establish run order +main() { + log_start "$(basename $0)" + check_config_file + log_end "$(basename $0)" +} + +main diff --git a/ic-os/components/setupos-scripts/check-hardware.sh b/ic-os/components/setupos-scripts/check-hardware.sh index 99f647018b0..67261c63216 100644 --- a/ic-os/components/setupos-scripts/check-hardware.sh +++ b/ic-os/components/setupos-scripts/check-hardware.sh @@ -32,8 +32,6 @@ GEN2_MINIMUM_AGGREGATE_DISK_SIZE=32000000000000 GEN1_MINIMUM_DISK_SIZE=3200000000000 GEN1_MINIMUM_AGGREGATE_DISK_SIZE=32000000000000 -CONFIG_DIR="/var/ic/config" - function check_generation() { echo "* Checking Generation..." @@ -249,6 +247,7 @@ function verify_disks() { function verify_deployment_path() { echo "* Verifying deployment path..." + if [[ ${GENERATION} == 2 ]] && [[ ! -f "${CONFIG_DIR}/node_operator_private_key.pem" ]]; then echo -e "\n\n\n\n\n\n" echo -e "\033[1;31mWARNING: Gen2 hardware detected but no Node Operator Private Key found.\033[0m" @@ -261,32 +260,6 @@ function verify_deployment_path() { fi } -# TODO(NODE-1477): delete in configuration revamp integration -CONFIG="${CONFIG:=/var/ic/config/config.ini}" - -function read_variables() { - # Read limited set of keys. Be extra-careful quoting values as it could - # otherwise lead to executing arbitrary shell code! - while IFS="=" read -r key value; do - case "$key" in - "node_reward_type") node_reward_type="${value}" ;; - esac - done <"${CONFIG}" -} - -function validate_node_reward() { - read_variables - if [[ -z "$node_reward_type" ]]; then - log_and_halt_installation_on_error 1 "Configuration error: node_reward_type is not set" - fi - - if [[ ! "$node_reward_type" =~ ^type[0-9]+(\.[0-9])?$ ]]; then - log_and_halt_installation_on_error 1 "Configuration error: node_reward_type is invalid: ${node_reward_type}" - fi - - echo "Valid node reward type: ${node_reward_type}" -} - # Establish run order main() { log_start "$(basename $0)" @@ -296,7 +269,6 @@ main() { verify_memory verify_disks verify_deployment_path - validate_node_reward else echo "* Hardware checks skipped by request via kernel command line" GENERATION=2 diff --git a/ic-os/components/setupos-scripts/check-network.sh b/ic-os/components/setupos-scripts/check-network.sh index 094cb5e6db4..67ac7e614c9 100755 --- a/ic-os/components/setupos-scripts/check-network.sh +++ b/ic-os/components/setupos-scripts/check-network.sh @@ -6,24 +6,16 @@ set -o pipefail SHELL="/bin/bash" PATH="/sbin:/bin:/usr/sbin:/usr/bin" +source /opt/ic/bin/config.sh source /opt/ic/bin/functions.sh -CONFIG="${CONFIG:=/var/ic/config/config.ini}" -DEPLOYMENT="${DEPLOYMENT:=/data/deployment.json}" - -function read_variables() { - # Read limited set of keys. Be extra-careful quoting values as it could - # otherwise lead to executing arbitrary shell code! - while IFS="=" read -r key value; do - case "$key" in - "ipv6_prefix") ipv6_prefix="${value}" ;; - "ipv6_gateway") ipv6_gateway="${value}" ;; - "ipv4_address") ipv4_address="${value}" ;; - "ipv4_prefix_length") ipv4_prefix_length="${value}" ;; - "ipv4_gateway") ipv4_gateway="${value}" ;; - "domain") domain="${value}" ;; - esac - done <"${CONFIG}" +function read_config_variables() { + ipv6_prefix=$(get_config_value '.network_settings.ipv6_config.Deterministic.prefix') + ipv6_gateway=$(get_config_value '.network_settings.ipv6_config.Deterministic.gateway') + ipv4_address=$(get_config_value '.network_settings.ipv4_config.address') + ipv4_prefix_length=$(get_config_value '.network_settings.ipv4_config.prefix_length') + ipv4_gateway=$(get_config_value '.network_settings.ipv4_config.gateway') + domain_name=$(get_config_value '.network_settings.domain_name') } # WARNING: Uses 'eval' for command execution. @@ -109,11 +101,14 @@ function print_network_settings() { echo "* Printing user defined network settings..." echo " IPv6 Prefix : ${ipv6_prefix}" echo " IPv6 Gateway: ${ipv6_gateway}" - if [[ -v ipv4_address && -n ${ipv4_address} && -v ipv4_prefix_length && -n ${ipv4_prefix_length} && -v ipv4_gateway && -n ${ipv4_gateway} && -v domain && -n ${domain} ]]; then + + if [[ -n ${ipv4_address} && -n ${ipv4_prefix_length} && -n ${ipv4_gateway} ]]; then echo " IPv4 Address: ${ipv4_address}" echo " IPv4 Prefix Length: ${ipv4_prefix_length}" echo " IPv4 Gateway: ${ipv4_gateway}" - echo " Domain name : ${domain}" + fi + if [[ -n ${domain_name} ]]; then + echo " Domain name: ${domain_name}" fi echo " " @@ -134,10 +129,10 @@ function validate_domain_name() { local domain_part local -a domain_parts - IFS='.' read -ra domain_parts <<<"${domain}" + IFS='.' read -ra domain_parts <<<"${domain_name}" if [ ${#domain_parts[@]} -lt 2 ]; then - log_and_halt_installation_on_error 1 "Domain validation error: less than two domain parts in domain: ${domain}" + log_and_halt_installation_on_error 1 "Domain validation error: less than two domain parts in domain: ${domain_name}" fi for domain_part in "${domain_parts[@]}"; do @@ -184,17 +179,13 @@ function ping_ipv6_gateway() { echo " " } -function assemble_nns_nodes_list() { - NNS_URL_STRING=$(/opt/ic/bin/fetch-property.sh --key=.nns.url --config=${DEPLOYMENT}) - IFS=',' read -r -a NNS_URL_LIST <<<"$NNS_URL_STRING" -} - function query_nns_nodes() { echo "* Querying NNS nodes..." + local nns_url_list=($(get_config_value '.icos_settings.nns_urls' | jq -r '.[]')) local success=false - # At least one of the provided URLs needs to work. - for url in "${NNS_URL_LIST[@]}"; do + + for url in "${nns_url_list[@]}"; do # When running against testnets, we need to ignore self signed certs # with `--insecure`. This check is only meant to confirm from SetupOS # that NNS urls are reachable, so we do not mind that it is "weak". @@ -218,18 +209,20 @@ function query_nns_nodes() { main() { log_start "$(basename $0)" if kernel_cmdline_bool_default_true ic.setupos.check_network; then - read_variables + read_config_variables get_network_settings print_network_settings - if [[ -n ${ipv4_address} && -n ${ipv4_prefix_length} && -n ${ipv4_gateway} ]]; then + if [[ -n ${domain_name} ]]; then validate_domain_name + fi + + if [[ -n ${ipv4_address} && -n ${ipv4_prefix_length} && -n ${ipv4_gateway} ]]; then setup_ipv4_network ping_ipv4_gateway fi ping_ipv6_gateway - assemble_nns_nodes_list query_nns_nodes else echo "* Network checks skipped by request via kernel command line" diff --git a/ic-os/components/setupos-scripts/check-ntp.sh b/ic-os/components/setupos-scripts/check-ntp.sh new file mode 100644 index 00000000000..fa74d8632d7 --- /dev/null +++ b/ic-os/components/setupos-scripts/check-ntp.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash + +set -o nounset +set -o pipefail + +SHELL="/bin/bash" +PATH="/sbin:/bin:/usr/sbin:/usr/bin" + +source /opt/ic/bin/functions.sh + +function check_ntp() { + echo "* Checking Chrony status..." + + systemctl is-active --quiet chrony + log_and_halt_installation_on_error "$?" "Chrony service not running or not active." + + retries=0 + max_retries=30 + while [ "$(timedatectl show -p NTPSynchronized --value)" != "yes" ]; do + if [ $retries -ge $max_retries ]; then + local service_logs=$(journalctl -u chrony.service --no-pager) + local log_message="System clock is not synchronized.\n\nChrony service logs:\n${service_logs}" + log_and_halt_installation_on_error 1 "${log_message}" + fi + + echo "* Chrony not yet synchronized. Waiting 2 seconds before retry..." + sleep 2 + ((retries++)) + done + + echo "* Chrony is running and time is in sync." +} + +function set_hwclock_utc() { + echo "* Setting hardware clock to UTC..." + timedatectl set-local-rtc 0 +} + +main() { + log_start "$(basename $0)" + check_ntp + set_hwclock_utc + log_end "$(basename $0)" +} + +main diff --git a/ic-os/components/setupos-scripts/config.service b/ic-os/components/setupos-scripts/config.service index b5b74035583..07066ba7632 100644 --- a/ic-os/components/setupos-scripts/config.service +++ b/ic-os/components/setupos-scripts/config.service @@ -6,9 +6,9 @@ Before=setupos.service [Service] Type=oneshot RemainAfterExit=true -ExecStart=/opt/ic/bin/output-wrapper.sh /dev/ttyS0 /opt/ic/bin/config.sh -StandardOutput=tty -StandardError=tty +ExecStart=/opt/ic/bin/config create-setupos-config +StandardOutput=journal+console +StandardError=journal+console [Install] WantedBy=multi-user.target diff --git a/ic-os/components/setupos-scripts/config.sh b/ic-os/components/setupos-scripts/config.sh deleted file mode 100755 index d4219d95637..00000000000 --- a/ic-os/components/setupos-scripts/config.sh +++ /dev/null @@ -1,121 +0,0 @@ -#!/usr/bin/env bash - -set -o nounset -set -o pipefail - -SHELL="/bin/bash" -PATH="/sbin:/bin:/usr/sbin:/usr/bin" - -CONFIG_DIR="/config" -CONFIG_TMP="/var/ic/config" -CONFIG_INI="${CONFIG_DIR}/config.ini" -CONFIG_INI_CLONE="${CONFIG_TMP}/config.ini" -SSH_AUTHORIZED_KEYS="${CONFIG_DIR}/ssh_authorized_keys" -SSH_AUTHORIZED_KEYS_CLONE="${CONFIG_TMP}/ssh_authorized_keys" - -source /opt/ic/bin/functions.sh - -# Define empty variables so they are not unset -ipv6_prefix="" -ipv6_gateway="" - -function print_config_file() { - if [ -e "${CONFIG_INI}" ]; then - echo "Found ${CONFIG_INI}. Contents:" - cat "${CONFIG_INI}" - else - log_and_halt_installation_on_error "1" "config.ini not found. Please copy a valid config.ini to the SetupOS installer config partition." - fi - -} - -function create_config_tmp() { - if [ ! -e "${CONFIG_TMP}" ]; then - # Create fresh config tmp directory - mkdir -p "${CONFIG_TMP}" - log_and_halt_installation_on_error "${?}" "Unable to create new '${CONFIG_TMP}' directory." - fi -} - -function clone_config() { - cp "${CONFIG_INI}" "${CONFIG_INI_CLONE}" - log_and_halt_installation_on_error "${?}" "Unable to copy 'config.ini' configuration file." - - if [ ! -f "${CONFIG_INI_CLONE}" ]; then - log_and_halt_installation_on_error "1" "Cloned 'config.ini' configuration file does not exist." - fi - - if [ -f "${CONFIG_DIR}/node_operator_private_key.pem" ]; then - cp ${CONFIG_DIR}/node_operator_private_key.pem ${CONFIG_TMP}/node_operator_private_key.pem - log_and_halt_installation_on_error "${?}" "Unable to copy 'node_operator_private_key.pem' configuration file." - fi - - if [ -d "${SSH_AUTHORIZED_KEYS}" ]; then - cp -r "${SSH_AUTHORIZED_KEYS}" "${CONFIG_TMP}" - log_and_halt_installation_on_error "${?}" "Unable to copy 'ssh_authorized_keys' directory." - else - log_and_halt_installation_on_error "1" "Unable to read 'ssh_authorized_keys' directory." - fi - - if [ ! -d "${SSH_AUTHORIZED_KEYS_CLONE}" ]; then - log_and_halt_installation_on_error "1" "Cloned 'ssh_authorized_keys' directory does not exist." - fi -} - -function normalize_config() { - CONFIG_VAR=$(cat "${CONFIG_INI_CLONE}" | tr '\r' '\n') - echo "${CONFIG_VAR}" >"${CONFIG_INI_CLONE}" - - sed -i 's/#.*$//g' "${CONFIG_INI_CLONE}" - log_and_halt_installation_on_error "${?}" "Unable to remove comments from 'config.ini'." - - sed -i 's/"//g' "${CONFIG_INI_CLONE}" - log_and_halt_installation_on_error "${?}" "Unable to replace double-quote characters in 'config.ini'." - - sed -i "s/'//g" "${CONFIG_INI_CLONE}" - log_and_halt_installation_on_error "${?}" "Unable to replace single-quote characters in 'config.ini'." - - sed -i 's/.*/\L&/' "${CONFIG_INI_CLONE}" - log_and_halt_installation_on_error "${?}" "Unable to convert upper- to lower-case in 'config.ini'." - - sed -i '/^$/d' "${CONFIG_INI_CLONE}" - log_and_halt_installation_on_error "${?}" "Unable to remove empty lines in 'config.ini'." - - echo -e '\n' >>"${CONFIG_INI_CLONE}" - log_and_halt_installation_on_error "${?}" "Unable to inject extra new-line at the end of 'config.ini'." -} - -function read_variables() { - # Read limited set of keys. Be extra-careful quoting values as it could - # otherwise lead to executing arbitrary shell code! - while IFS="=" read -r key value; do - case "$key" in - "ipv6_prefix") ipv6_prefix="${value}" ;; - "ipv6_gateway") ipv6_gateway="${value}" ;; - esac - done <"${CONFIG_INI_CLONE}" -} - -function verify_variables() { - if [ -z "${ipv6_prefix}" ]; then - log_and_halt_installation_on_error "1" "Variable 'ipv6_prefix' is not defined in 'config.ini'." - fi - - if [ -z "${ipv6_gateway}" ]; then - log_and_halt_installation_on_error "1" "Variable 'ipv6_gateway' is not defined in 'config.ini'." - fi -} - -# Establish run order -main() { - log_start "$(basename $0)" - print_config_file - create_config_tmp - clone_config - normalize_config - read_variables - verify_variables - log_end "$(basename $0)" -} - -main diff --git a/ic-os/components/setupos-scripts/setup-hostos-config.sh b/ic-os/components/setupos-scripts/setup-hostos-config.sh index 0893a0c69aa..113e635e6cf 100755 --- a/ic-os/components/setupos-scripts/setup-hostos-config.sh +++ b/ic-os/components/setupos-scripts/setup-hostos-config.sh @@ -5,8 +5,9 @@ set -o pipefail SHELL="/bin/bash" PATH="/sbin:/bin:/usr/sbin:/usr/bin" -CONFIG_DIR="/var/ic/config" +CONFIG_DIR="/config" +source /opt/ic/bin/config.sh source /opt/ic/bin/functions.sh function mount_config_partition() { @@ -20,6 +21,7 @@ function mount_config_partition() { } function copy_config_files() { + # TODO(NODE-1518): delete config.ini copying after switch to new icos config echo "* Copying 'config.ini' to hostOS config partition..." if [ -f "${CONFIG_DIR}/config.ini" ]; then cp ${CONFIG_DIR}/config.ini /media/ @@ -28,29 +30,61 @@ function copy_config_files() { log_and_halt_installation_on_error "1" "Configuration file 'config.ini' does not exist." fi + # TODO(NODE-1518): delete deployment.json copying after switch to new icos config + echo "* Copying deployment.json to config partition..." + cp /data/deployment.json /media/ + log_and_halt_installation_on_error "${?}" "Unable to copy deployment.json to hostOS config partition." + echo "* Copying SSH authorized keys..." - if [ -d "${CONFIG_DIR}/ssh_authorized_keys" ]; then - cp -r ${CONFIG_DIR}/ssh_authorized_keys /media/ - log_and_halt_installation_on_error "${?}" "Unable to copy SSH authorized keys to hostOS config partition." + use_ssh_authorized_keys=$(get_config_value '.icos_settings.use_ssh_authorized_keys') + if [[ "${use_ssh_authorized_keys,,}" == "true" ]]; then + if [ -d "${CONFIG_DIR}/ssh_authorized_keys" ]; then + cp -a "${CONFIG_DIR}/ssh_authorized_keys" /media/ + log_and_halt_installation_on_error "${?}" "Unable to copy SSH authorized keys to hostOS config partition." + else + log_and_halt_installation_on_error "1" "use_ssh_authorized_keys set to true but not found" + fi else - log_and_halt_installation_on_error "1" "Directory 'ssh_authorized_keys' does not exist." + echo >&2 "SSH keys not in use." fi echo "* Copying node operator private key..." - if [ -f "${CONFIG_DIR}/node_operator_private_key.pem" ]; then - cp ${CONFIG_DIR}/node_operator_private_key.pem /media/ - log_and_halt_installation_on_error "${?}" "Unable to copy node operator private key to hostOS config partition." + use_node_operator_private_key=$(get_config_value '.icos_settings.use_node_operator_private_key') + if [[ "${use_node_operator_private_key,,}" == "true" ]]; then + if [ -f "${CONFIG_DIR}/node_operator_private_key.pem" ]; then + cp "${CONFIG_DIR}/node_operator_private_key.pem" /media/ + log_and_halt_installation_on_error "${?}" "Unable to copy node operator private key to hostOS config partition." + else + log_and_halt_installation_on_error "1" "use_node_operator_private_key set to true but not found" + fi else - echo "node_operator_private_key.pem does not exist, requiring HSM." + echo >&2 "Warning: node_operator_private_key.pem does not exist, requiring HSM." fi - echo "* Copying deployment.json to config partition..." - cp /data/deployment.json /media/ - log_and_halt_installation_on_error "${?}" "Unable to copy deployment.json to hostOS config partition." - echo "* Copying NNS public key to hostOS config partition..." - cp /data/nns_public_key.pem /media/ - log_and_halt_installation_on_error "${?}" "Unable to copy NNS public key to hostOS config partition." + use_nns_public_key=$(get_config_value '.icos_settings.use_nns_public_key') + if [[ "${use_nns_public_key,,}" == "true" ]]; then + if [ -f "/data/nns_public_key.pem" ]; then + cp /data/nns_public_key.pem /media/ + log_and_halt_installation_on_error "${?}" "Unable to copy NNS public key to hostOS config partition." + else + log_and_halt_installation_on_error "1" "use_nns_public_key set to true but not found." + fi + else + log_and_halt_installation_on_error "1" "use_nns_public_key must be set to true." + fi + + echo "* Converting 'config.json' to hostOS config file 'config-hostos.json'..." + /opt/ic/bin/config generate-hostos-config + log_and_halt_installation_on_error "${?}" "Unable to generate hostos configuration." + + echo "* Copying 'config-hostos.json' to hostOS config partition..." + if [ -f "/var/ic/config/config-hostos.json" ]; then + cp /var/ic/config/config-hostos.json /media/config.json + log_and_halt_installation_on_error "${?}" "Unable to copy 'config-hostos.json' to hostOS config partition." + else + log_and_halt_installation_on_error "1" "Configuration file 'config-hostos.json' does not exist." + fi } function insert_hsm_if_necessary() { diff --git a/ic-os/components/setupos-scripts/setupos.sh b/ic-os/components/setupos-scripts/setupos.sh index 70c860207bf..bc4065653ff 100755 --- a/ic-os/components/setupos-scripts/setupos.sh +++ b/ic-os/components/setupos-scripts/setupos.sh @@ -39,8 +39,10 @@ main() { log_start "$(basename $0)" start_setupos /opt/ic/bin/check-setupos-age.sh + /opt/ic/bin/check-config.sh /opt/ic/bin/check-hardware.sh /opt/ic/bin/check-network.sh + /opt/ic/bin/check-ntp.sh if kernel_cmdline_bool_default_true ic.setupos.perform_installation; then true else diff --git a/ic-os/components/setupos.bzl b/ic-os/components/setupos.bzl index 5b3ea9428ec..8919eb624fa 100644 --- a/ic-os/components/setupos.bzl +++ b/ic-os/components/setupos.bzl @@ -8,7 +8,7 @@ component_files = { # setupos-scripts Label("//ic-os/components/setupos-scripts:check-setupos-age.sh"): "/opt/ic/bin/check-setupos-age.sh", - Label("//ic-os/components/setupos-scripts:config.sh"): "/opt/ic/bin/config.sh", + Label("//ic-os/components/setupos-scripts:check-config.sh"): "/opt/ic/bin/check-config.sh", Label("//ic-os/components/setupos-scripts:setup-hostos-config.sh"): "/opt/ic/bin/setup-hostos-config.sh", Label("//ic-os/components/setupos-scripts:setup-disk.sh"): "/opt/ic/bin/setup-disk.sh", Label("//ic-os/components/setupos-scripts:functions.sh"): "/opt/ic/bin/functions.sh", @@ -16,6 +16,7 @@ component_files = { Label("//ic-os/components/setupos-scripts:check-hardware.sh"): "/opt/ic/bin/check-hardware.sh", Label("//ic-os/components/setupos-scripts:install-hostos.sh"): "/opt/ic/bin/install-hostos.sh", Label("//ic-os/components/setupos-scripts:check-network.sh"): "/opt/ic/bin/check-network.sh", + Label("//ic-os/components/setupos-scripts:check-ntp.sh"): "/opt/ic/bin/check-ntp.sh", Label("//ic-os/components/setupos-scripts:output-wrapper.sh"): "/opt/ic/bin/output-wrapper.sh", Label("//ic-os/components/setupos-scripts:setupos.sh"): "/opt/ic/bin/setupos.sh", Label("//ic-os/components/setupos-scripts:config.service"): "/etc/systemd/system/config.service", @@ -31,9 +32,9 @@ component_files = { # misc Label("misc/logging.sh"): "/opt/ic/bin/logging.sh", + Label("misc/config/setupos/config.sh"): "/opt/ic/bin/config.sh", Label("misc/chrony/chrony.conf"): "/etc/chrony/chrony.conf", Label("misc/chrony/chrony-var.service"): "/etc/systemd/system/chrony-var.service", - Label("misc/fetch-property.sh"): "/opt/ic/bin/fetch-property.sh", Label("misc/serial-getty@/setupos/override.conf"): "/etc/systemd/system/serial-getty@.service.d/override.conf", Label("monitoring/journald.conf"): "/etc/systemd/journald.conf", diff --git a/ic-os/components/ssh/deploy-updated-ssh-account-keys/deploy-updated-ssh-account-keys.service b/ic-os/components/ssh/deploy-updated-ssh-account-keys/deploy-updated-ssh-account-keys.service deleted file mode 100644 index cc1b47ef93e..00000000000 --- a/ic-os/components/ssh/deploy-updated-ssh-account-keys/deploy-updated-ssh-account-keys.service +++ /dev/null @@ -1,12 +0,0 @@ -[Unit] -Description=Update ssh account keys -Before=setup-ssh-account-keys.service - -[Service] -Type=oneshot -RemainAfterExit=true -ExecStart=/opt/ic/bin/deploy-updated-ssh-account-keys.sh - -[Install] -RequiredBy=setup-ssh-account-keys.service -WantedBy=multi-user.target diff --git a/ic-os/components/ssh/deploy-updated-ssh-account-keys/deploy-updated-ssh-account-keys.sh b/ic-os/components/ssh/deploy-updated-ssh-account-keys/deploy-updated-ssh-account-keys.sh deleted file mode 100755 index 6bf2b739bb5..00000000000 --- a/ic-os/components/ssh/deploy-updated-ssh-account-keys/deploy-updated-ssh-account-keys.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash - -set -e - -# Update configured ssh keys for the role accounts if a newer version -# is available. - -# Only update readonly and backup keys. -for ACCOUNT in backup readonly; do - echo "Checking authorized keys for ${ACCOUNT}" - ORIGIN="/opt/ic/share/authorized_keys/${ACCOUNT}" - if [ ! -r "${ORIGIN}" ]; then - continue - fi - TARGET="/boot/config/ssh_authorized_keys/${ACCOUNT}" - if [ ! -r "${TARGET}" ]; then - echo "${ORIGIN} keys exist, but no ${TARGET} keys are present, skipping" - continue - fi - ORIGIN_ENV=$(head -n 1 $ORIGIN) - TARGET_ENV=$(head -n 1 $TARGET) - if [ "${TARGET_ENV:0:1}" != "#" ]; then - echo "Authorized keys for ${ACCOUNT} have no environment header, updating" - cp "${ORIGIN}" "${TARGET}" - elif [ "${TARGET_ENV}" = "${ORIGIN_ENV}" ]; then - # Target's environment matches, check which is newer. - ORIGIN_TIME=$(head -n 2 $ORIGIN | tail -n 1 | cut -c 3- | date -f - +%s) - TARGET_TIME=$(head -n 2 $TARGET | tail -n 1 | cut -c 3- | date -f - +%s) - if [ $ORIGIN_TIME -gt $TARGET_TIME ]; then - echo "Authorized keys for ${ACCOUNT} are too old, updating: ${ORIGIN_TIME} vs ${TARGET_TIME}" - cp "${ORIGIN}" "${TARGET}" - fi - else - echo "The environments do not match, skipping: ${ORIGIN_ENV} vs ${TARGET_ENV}" - fi -done diff --git a/ic-os/components/boundary-guestos/etc/systemd/system/setup-ssh-keys.service b/ic-os/components/ssh/generate-host-ssh-keys/generate-host-ssh-keys.service similarity index 84% rename from ic-os/components/boundary-guestos/etc/systemd/system/setup-ssh-keys.service rename to ic-os/components/ssh/generate-host-ssh-keys/generate-host-ssh-keys.service index b96b685dee3..fc87e3faf42 100644 --- a/ic-os/components/boundary-guestos/etc/systemd/system/setup-ssh-keys.service +++ b/ic-os/components/ssh/generate-host-ssh-keys/generate-host-ssh-keys.service @@ -8,7 +8,7 @@ Before=ssh.service [Service] Type=oneshot RemainAfterExit=true -ExecStart=/opt/ic/bin/setup-ssh-keys.sh +ExecStart=/opt/ic/bin/generate-host-ssh-keys.sh [Install] WantedBy=multi-user.target diff --git a/ic-os/components/ssh/setup-ssh-keys/setup-ssh-keys.sh b/ic-os/components/ssh/generate-host-ssh-keys/generate-host-ssh-keys.sh similarity index 100% rename from ic-os/components/ssh/setup-ssh-keys/setup-ssh-keys.sh rename to ic-os/components/ssh/generate-host-ssh-keys/generate-host-ssh-keys.sh diff --git a/ic-os/components/boundary-guestos/etc/systemd/system/setup-ssh-account-keys.service b/ic-os/components/ssh/setup-ssh-user-keys/setup-ssh-user-keys.service similarity index 82% rename from ic-os/components/boundary-guestos/etc/systemd/system/setup-ssh-account-keys.service rename to ic-os/components/ssh/setup-ssh-user-keys/setup-ssh-user-keys.service index 2a862b3f2c4..3fdbbaa3db0 100644 --- a/ic-os/components/boundary-guestos/etc/systemd/system/setup-ssh-account-keys.service +++ b/ic-os/components/ssh/setup-ssh-user-keys/setup-ssh-user-keys.service @@ -1,5 +1,5 @@ [Unit] -Description=Set up ssh account keys +Description=Set up ssh user keys Before=ssh.service # bootstrap-ic-node.service (if it exists) lists this service as a reverse dependency @@ -9,7 +9,7 @@ WantedBy=multi-user.target [Service] Type=oneshot RemainAfterExit=true -ExecStart=/opt/ic/bin/setup-ssh-account-keys.sh +ExecStart=/opt/ic/bin/setup-ssh-user-keys.sh # All services that networking depends on log their outputs to the console # and are piped to the host terminal if the verbose flag is enabled. diff --git a/ic-os/components/ssh/setup-ssh-account-keys/setup-ssh-account-keys.sh b/ic-os/components/ssh/setup-ssh-user-keys/setup-ssh-user-keys.sh similarity index 100% rename from ic-os/components/ssh/setup-ssh-account-keys/setup-ssh-account-keys.sh rename to ic-os/components/ssh/setup-ssh-user-keys/setup-ssh-user-keys.sh diff --git a/ic-os/defs.bzl b/ic-os/defs.bzl index a7484c2d786..79868126843 100644 --- a/ic-os/defs.bzl +++ b/ic-os/defs.bzl @@ -9,7 +9,7 @@ load("//ci/src/artifacts:upload.bzl", "upload_artifacts") load("//ic-os/bootloader:defs.bzl", "build_grub_partition") load("//ic-os/components:boundary-guestos.bzl", boundary_component_files = "component_files") load("//ic-os/components/conformance_tests:defs.bzl", "component_file_references_test") -load("//toolchains/sysimage:toolchain.bzl", "build_container_base_image", "build_container_filesystem", "disk_image", "disk_image_no_tar", "ext4_image", "inject_files", "sha256sum", "tar_extract", "tree_hash", "upgrade_image") +load("//toolchains/sysimage:toolchain.bzl", "build_container_base_image", "build_container_filesystem", "disk_image", "disk_image_no_tar", "ext4_image", "sha256sum", "tar_extract", "tree_hash", "upgrade_image") def icos_build( name, @@ -139,37 +139,53 @@ def icos_build( # -------------------- Extract root partition -------------------- + # Note that we defer injecting the files from images_deps["rootfs"]. These are mostly slower to build. + + # NOTE: e2fsdroid does not support filenames with spaces, fortunately, + # these only occur in firmware that we do not use. + PARTITION_ROOT_STRIP_PATHS = [ + "/run", + "/boot", + "/var", + "/usr/lib/firmware/brcm/brcmfmac43241b4-sdio.Intel Corp.-VALLEYVIEW C0 PLATFORM.txt.zst", + "/usr/lib/firmware/brcm/brcmfmac43340-sdio.ASUSTeK COMPUTER INC.-TF103CE.txt.zst", + "/usr/lib/firmware/brcm/brcmfmac43362-sdio.ASUSTeK COMPUTER INC.-ME176C.txt.zst", + "/usr/lib/firmware/brcm/brcmfmac43430a0-sdio.ONDA-V80 PLUS.txt.zst", + "/usr/lib/firmware/brcm/brcmfmac43455-sdio.MINIX-NEO Z83-4.txt.zst", + "/usr/lib/firmware/brcm/brcmfmac43455-sdio.Raspberry Pi Foundation-Raspberry Pi 4 Model B.txt.zst", + "/usr/lib/firmware/brcm/brcmfmac43455-sdio.Raspberry Pi Foundation-Raspberry Pi Compute Module 4.txt.zst", + "/usr/lib/firmware/brcm/brcmfmac4356-pcie.Intel Corporation-CHERRYVIEW D1 PLATFORM.txt.zst", + "/usr/lib/firmware/brcm/brcmfmac4356-pcie.Xiaomi Inc-Mipad2.txt.zst", + ] ext4_image( - name = "static-partition-root-unsigned.tzst", + name = "partition-root-unsigned.tzst", + testonly = malicious, src = ":rootfs-tree.tar", file_contexts = ":file_contexts", partition_size = image_deps["rootfs_size"], - # NOTE: e2fsdroid does not support filenames with spaces, fortunately, - # there are only two in our build. - strip_paths = [ - "/run", - "/boot", - "/var", - "/usr/lib/firmware/brcm/brcmfmac43241b4-sdio.Intel Corp.-VALLEYVIEW C0 PLATFORM.txt.zst", - "/usr/lib/firmware/brcm/brcmfmac43340-sdio.ASUSTeK COMPUTER INC.-TF103CE.txt.zst", - "/usr/lib/firmware/brcm/brcmfmac43362-sdio.ASUSTeK COMPUTER INC.-ME176C.txt.zst", - "/usr/lib/firmware/brcm/brcmfmac43430a0-sdio.ONDA-V80 PLUS.txt.zst", - "/usr/lib/firmware/brcm/brcmfmac43455-sdio.MINIX-NEO Z83-4.txt.zst", - "/usr/lib/firmware/brcm/brcmfmac43455-sdio.Raspberry Pi Foundation-Raspberry Pi 4 Model B.txt.zst", - "/usr/lib/firmware/brcm/brcmfmac43455-sdio.Raspberry Pi Foundation-Raspberry Pi Compute Module 4.txt.zst", - "/usr/lib/firmware/brcm/brcmfmac4356-pcie.Intel Corporation-CHERRYVIEW D1 PLATFORM.txt.zst", - "/usr/lib/firmware/brcm/brcmfmac4356-pcie.Xiaomi Inc-Mipad2.txt.zst", - ], + strip_paths = PARTITION_ROOT_STRIP_PATHS, + extra_files = { + k: v + for k, v in (image_deps["rootfs"].items() + [(":version.txt", "/opt/ic/share/version.txt:0644")]) + }, target_compatible_with = [ "@platforms//os:linux", ], - tags = ["manual"], + tags = ["manual", "no-cache"], + ) + + component_file_references_test( + name = name + "_component_file_references_test", + image = ":partition-root-unsigned.tzst", + component_files = image_deps["component_files"].keys(), + # Inherit tags for this test, to avoid triggering builds for local base images + tags = tags, ) # -------------------- Extract boot partition -------------------- ext4_image( - name = "static-partition-boot.tzst", + name = "partition-boot.tzst", src = ":rootfs-tree.tar", file_contexts = ":file_contexts", partition_size = image_deps["bootfs_size"], @@ -177,52 +193,43 @@ def icos_build( target_compatible_with = [ "@platforms//os:linux", ], - tags = ["manual"], - ) - - # Defer injection to this point to allow caching most of the built images - # -------------------- Inject extra files -------------------- - - inject_files( - name = "partition-root-unsigned.tzst", - testonly = malicious, - base = "static-partition-root-unsigned.tzst", - file_contexts = ":file_contexts", extra_files = { k: v - for k, v in (image_deps["rootfs"].items() + [(":version.txt", "/opt/ic/share/version.txt:0644")]) + for k, v in ( + image_deps["bootfs"].items() + [ + (":version.txt", "/version.txt:0644"), + (":extra_boot_args", "/extra_boot_args:0644"), + ] + ) }, - tags = ["manual"], - ) - - # Inherit tags for this test, to avoid triggering builds for local base images - component_file_references_test( - name = name + "_component_file_references_test", - image = ":partition-root-unsigned.tzst", - component_files = image_deps["component_files"].keys(), - tags = tags, + tags = ["manual", "no-cache"], ) if upgrades: - inject_files( + ext4_image( name = "partition-root-test-unsigned.tzst", testonly = malicious, - base = "static-partition-root-unsigned.tzst", + src = ":rootfs-tree.tar", file_contexts = ":file_contexts", + partition_size = image_deps["rootfs_size"], + strip_paths = PARTITION_ROOT_STRIP_PATHS, extra_files = { k: v for k, v in (image_deps["rootfs"].items() + [(":version-test.txt", "/opt/ic/share/version.txt:0644")]) }, - tags = ["manual"], + target_compatible_with = [ + "@platforms//os:linux", + ], + tags = ["manual", "no-cache"], ) # When boot_args are fixed, don't bother signing if "boot_args_template" not in image_deps: - native.alias(name = "partition-root.tzst", actual = ":partition-root-unsigned.tzst", tags = ["manual"]) + native.alias(name = "partition-root.tzst", actual = ":partition-root-unsigned.tzst", tags = ["manual", "no-cache"]) native.alias(name = "extra_boot_args", actual = image_deps["extra_boot_args"], tags = ["manual"]) if upgrades: - native.alias(name = "partition-root-test.tzst", actual = ":partition-root-test-unsigned.tzst", tags = ["manual"]) + native.alias(name = "partition-root-test.tzst", actual = ":partition-root-test-unsigned.tzst", tags = ["manual", "no-cache"]) native.alias(name = "extra_boot_test_args", actual = image_deps["extra_boot_args"], tags = ["manual"]) else: native.alias(name = "extra_boot_args_template", actual = image_deps["boot_args_template"], tags = ["manual"]) @@ -235,7 +242,7 @@ def icos_build( cmd = "$(location //toolchains/sysimage:proc_wrapper) $(location //toolchains/sysimage:verity_sign.py) -i $< -o $(location :partition-root.tzst) -r $(location partition-root-hash) --dflate $(location //rs/ic_os/build_tools/dflate)", executable = False, tools = ["//toolchains/sysimage:proc_wrapper", "//toolchains/sysimage:verity_sign.py", "//rs/ic_os/build_tools/dflate"], - tags = ["manual"], + tags = ["manual", "no-cache"], ) native.genrule( @@ -257,7 +264,7 @@ def icos_build( outs = ["partition-root-test.tzst", "partition-root-test-hash"], cmd = "$(location //toolchains/sysimage:proc_wrapper) $(location //toolchains/sysimage:verity_sign.py) -i $< -o $(location :partition-root-test.tzst) -r $(location partition-root-test-hash) --dflate $(location //rs/ic_os/build_tools/dflate)", tools = ["//toolchains/sysimage:proc_wrapper", "//toolchains/sysimage:verity_sign.py", "//rs/ic_os/build_tools/dflate"], - tags = ["manual"], + tags = ["manual", "no-cache"], ) native.genrule( @@ -271,29 +278,16 @@ def icos_build( tags = ["manual"], ) - inject_files( - name = "partition-boot.tzst", - base = "static-partition-boot.tzst", - file_contexts = ":file_contexts", - prefix = "/boot", - extra_files = { - k: v - for k, v in ( - image_deps["bootfs"].items() + [ - (":version.txt", "/version.txt:0644"), - (":extra_boot_args", "/extra_boot_args:0644"), - ] - ) - }, - tags = ["manual"], - ) - if upgrades: - inject_files( + ext4_image( name = "partition-boot-test.tzst", - base = "static-partition-boot.tzst", + src = ":rootfs-tree.tar", file_contexts = ":file_contexts", - prefix = "/boot", + partition_size = image_deps["bootfs_size"], + subdir = "boot", + target_compatible_with = [ + "@platforms//os:linux", + ], extra_files = { k: v for k, v in ( @@ -303,7 +297,7 @@ def icos_build( ] ) }, - tags = ["manual"], + tags = ["manual", "no-cache"], ) # -------------------- Assemble disk partitions --------------- @@ -326,7 +320,7 @@ def icos_build( ":partition-root.tzst", ] + custom_partitions, expanded_size = image_deps.get("expanded_size", default = None), - tags = ["manual"], + tags = ["manual", "no-cache"], target_compatible_with = [ "@platforms//os:linux", ], @@ -343,7 +337,7 @@ def icos_build( ":partition-root.tzst", ] + custom_partitions, expanded_size = image_deps.get("expanded_size", default = None), - tags = ["manual", "no-remote-cache"], + tags = ["manual", "no-cache"], target_compatible_with = [ "@platforms//os:linux", ], @@ -363,7 +357,7 @@ def icos_build( name = "update-img.tar", boot_partition = ":partition-boot.tzst", root_partition = ":partition-root.tzst", - tags = ["manual"], + tags = ["manual", "no-cache"], target_compatible_with = [ "@platforms//os:linux", ], @@ -381,7 +375,7 @@ def icos_build( name = "update-img-test.tar", boot_partition = ":partition-boot-test.tzst", root_partition = ":partition-root-test.tzst", - tags = ["manual"], + tags = ["manual", "no-cache"], target_compatible_with = [ "@platforms//os:linux", ], @@ -717,20 +711,13 @@ EOF ) ext4_image( - name = "static-partition-boot.tzst", + name = "partition-boot.tzst", src = ":rootfs-tree.tar", partition_size = "1G", subdir = "boot/", target_compatible_with = [ "@platforms//os:linux", ], - tags = ["manual"], - ) - - inject_files( - name = "partition-boot.tzst", - base = "static-partition-boot.tzst", - prefix = "/boot", extra_files = { k: v for k, v in ( @@ -740,31 +727,25 @@ EOF ] ) }, - tags = ["manual"], + tags = ["manual", "no-cache"], ) ext4_image( - name = "static-partition-root-unsigned.tzst", + name = "partition-root-unsigned.tzst", src = ":rootfs-tree.tar", partition_size = "3G", strip_paths = [ "/run", "/boot", ], - tags = ["manual"], - target_compatible_with = [ - "@platforms//os:linux", - ], - ) - - inject_files( - name = "partition-root-unsigned.tzst", - base = "static-partition-root-unsigned.tzst", extra_files = { k: v for k, v in (image_deps["rootfs"].items() + [(":version.txt", "/opt/ic/share/version.txt:0644")]) }, - tags = ["manual"], + tags = ["manual", "no-cache"], + target_compatible_with = [ + "@platforms//os:linux", + ], ) native.genrule( @@ -774,7 +755,7 @@ EOF cmd = "$(location //toolchains/sysimage:proc_wrapper) $(location //toolchains/sysimage:verity_sign.py) -i $< -o $(location :partition-root.tzst) -r $(location partition-root-hash) --dflate $(location //rs/ic_os/build_tools/dflate)", executable = False, tools = ["//toolchains/sysimage:proc_wrapper", "//toolchains/sysimage:verity_sign.py", "//rs/ic_os/build_tools/dflate"], - tags = ["manual"], + tags = ["manual", "no-cache"], ) native.genrule( @@ -799,7 +780,7 @@ EOF ":partition-root.tzst", ], expanded_size = "50G", - tags = ["manual"], + tags = ["manual", "no-cache"], target_compatible_with = [ "@platforms//os:linux", ], diff --git a/ic-os/docs/Configuration.adoc b/ic-os/docs/Configuration.adoc index 6167fbdcf9e..9f6ef0dcdd2 100644 --- a/ic-os/docs/Configuration.adoc +++ b/ic-os/docs/Configuration.adoc @@ -14,7 +14,7 @@ In production, configuration is propagated from a partition on the USB installer === SetupOS -> HostOS SetupOS validates, sanitizes, and copies all of its configuration files to the HostOS config partition: - config.ini # Data center-specific network settings + config.ini # Data center and node-specific network settings ssh_authorized_keys # SSH private keys node_operator_private_key.pem # Node Operator private key created in the Node Provider onboarding deployment.json # Deployment-specific configurations diff --git a/ic-os/guestos/context/docker-base.dev b/ic-os/guestos/context/docker-base.dev index 186bb872351..53e062f3749 100644 --- a/ic-os/guestos/context/docker-base.dev +++ b/ic-os/guestos/context/docker-base.dev @@ -1 +1 @@ -ghcr.io/dfinity/guestos-base-dev@sha256:02fb794026866e0c67636e6abb46bec04a3cd2b38990ae401c18bbcb6290dfbd +ghcr.io/dfinity/guestos-base-dev@sha256:39c8ec70837845b404e0f72290358415eeca396d28fa56af771cc6bf8a554367 diff --git a/ic-os/guestos/context/docker-base.prod b/ic-os/guestos/context/docker-base.prod index 0be051a0045..6bbdc82dba6 100644 --- a/ic-os/guestos/context/docker-base.prod +++ b/ic-os/guestos/context/docker-base.prod @@ -1 +1 @@ -ghcr.io/dfinity/guestos-base@sha256:72bda2acd0ce517f9f6a6f3900fc6b0c07a0a0a26b34b718ad04cc61e1eef1ea +ghcr.io/dfinity/guestos-base@sha256:018372b40febd969e14152e693d67dd6536ad062728eb6b75b90f24e383b23d3 diff --git a/ic-os/guestos/defs.bzl b/ic-os/guestos/defs.bzl index 1d4f3c570c2..aa2c7e36260 100644 --- a/ic-os/guestos/defs.bzl +++ b/ic-os/guestos/defs.bzl @@ -40,6 +40,7 @@ def image_deps(mode, malicious = False): "//publish/binaries:ic-boundary-tls": "/opt/ic/bin/ic-boundary:0755", # API boundary node binary, required by the IC protocol. The same GuestOS is used both for the replica and API boundary nodes. "//publish/binaries:ic-consensus-pool-util": "/opt/ic/bin/ic-consensus-pool-util:0755", # May be used during recoveries to export/import consensus pool artifacts. "//publish/binaries:ic-recovery": "/opt/ic/bin/ic-recovery:0755", # Required for performing subnet recoveries on the node directly. + "//publish/binaries:ic-admin": "/opt/ic/bin/ic-admin:0755", # Required for issuing recovery proposals directly from the node (primarily used for system tests). "//publish/binaries:state-tool": "/opt/ic/bin/state-tool:0755", # May be used during recoveries for calculating the state hash and inspecting the state more generally. "//publish/binaries:ic-regedit": "/opt/ic/bin/ic-regedit:0755", # May be used for inspecting and recovering the registry. # Required by the GuestOS @@ -54,7 +55,7 @@ def image_deps(mode, malicious = False): # additional libraries to install "//rs/ic_os/release:nss_icos": "/usr/lib/x86_64-linux-gnu/libnss_icos.so.2:0644", # Allows referring to the guest IPv6 by name guestos from host, and host as hostos from guest. - # TODO(NODE-1519): delete config tool from guestos after switch to new icos config + # TODO(NODE-1518): delete config tool from guestos after switch to new icos config "//rs/ic_os/release:config": "/opt/ic/bin/config:0755", }, diff --git a/ic-os/guestos/docs/Boot.adoc b/ic-os/guestos/docs/Boot.adoc index 6c94e394e59..d2fea4056d5 100644 --- a/ic-os/guestos/docs/Boot.adoc +++ b/ic-os/guestos/docs/Boot.adoc @@ -30,7 +30,7 @@ service are started in the IC-OS boot sequence: - IC node config injection -- Set up ssh account keys +- Set up ssh user keys - Generate network configuration @@ -57,7 +57,7 @@ not held in +/etc/fstab+ but is generated by the shell script Afterwards, the first three partitions are mounted as +/boot/efi+, +/boot/grub+ and +/boot/config+, respectively. The +config+ partition is used as (small) store for data that is preserved across upgrades -and is available at early boot time already (see link:ConfigStore{outfilesuffix}[config store]). +and is available at early boot time already. == Save machine-id @@ -111,7 +111,7 @@ system will set up its own +/var+ filesystem correctly again. == Set up ssh host keys -Service: +setup-ssh-keys.service+, script: +/opt/ic/bin/setup-ssh-keys.sh+, +Service: +generate-host-ssh-keys.service+, script: +/opt/ic/bin/generate-host-ssh-keys.sh+, depends on +/boot/config+ mount. This checks if ssh host keys for the system exist in the +config+ partition @@ -167,21 +167,12 @@ depends on mount of all filesystems. This is only executed once on first boot after provisioning. It looks for a "virtual USB stick" attached to the VM that contains a tar file with initial configuration -for parts of the system (see link:ConfigStore{outfilesuffix}[config store] for a description). Required -files in the +config+ partition as well as payload store are created. +for parts of the system. Required files in the +config+ partition as well as +payload store are created. -== Deploy updated ssh account keys +== Set up ssh user keys -Service: +deploy-updated-ssh-account-keys.service+, +deploy-updated-ssh-account-keys.sh+. -Depends on +bootstrap-ic-node.service+, runs before +setup-ssh-account-keys.service+. - -Changes the keys held in the +config+ partition for the +backup+ and +readonly+ user. This -is a work-around due to not having a key management solution that updated keys are -deployed via system upgrades. - -== Set up ssh account keys - -Service: +setup-ssh-account-keys.services+, script +/opt/ic/bin/setup-ssh-account-keys.sh+. +Service: +setup-ssh-user-keys.services+, script +/opt/ic/bin/setup-ssh-user-keys.sh+. Depends on +bootstrap-ic-node.service+. The +authorized_keys+ files for the role accounts are taken from the @@ -221,16 +212,6 @@ This lets systemd monitor the contents of the +nftables.conf+ ruleset file (dynamically generated by IC stack depending on registry) and issues a reload command to the nftables subsystem in order to activate the ruleset. -== IPv6 address monitor / retry - -Service: +retry-ipv6-config.service+, script +/opt/ic/bin/retry-ipv6-config.sh+. - -Periodically checks whether an IPv6 address has been assigned to the primary -interface and issues +networkctl reconfigure+ as needed. The reason is that -+systemd-networkd+ gives up on trying SLAAC autoconfiguration after a while, -so systems will fail to receive network configuration under certain conditions -if the router in their network is down at boot. - == Start filebeat Service: +filebeat.service+. Pre-exec script +/opt/ic/bin/generate-filebeat-config.sh+. diff --git a/ic-os/guestos/docs/DiskLayout.adoc b/ic-os/guestos/docs/DiskLayout.adoc index 8a690680b9d..13dbcbd99f5 100644 --- a/ic-os/guestos/docs/DiskLayout.adoc +++ b/ic-os/guestos/docs/DiskLayout.adoc @@ -41,8 +41,6 @@ tampering). == *config* System config store Contains the config store persisted across system upgrades. -See link:ConfigStore{outfilesuffix}[config store] for a -specification of its contents. == *A_boot* / *B_boot* Boot partition for system A/B diff --git a/ic-os/guestos/docs/README.adoc b/ic-os/guestos/docs/README.adoc index 4caa2719d2c..92e95a07169 100644 --- a/ic-os/guestos/docs/README.adoc +++ b/ic-os/guestos/docs/README.adoc @@ -3,7 +3,6 @@ Refer to detailed documentation on: * link:DiskLayout{outfilesuffix}[Disk layout] -* link:ConfigStore{outfilesuffix}[GuestOS config store] * link:Boot{outfilesuffix}[Boot sequence] * link:SELinux{outfilesuffix}[SELinux security policy] * link:Interface{outfilesuffix}[GuestOS input/output interface] \ No newline at end of file diff --git a/ic-os/guestos/envs/local-base-dev/BUILD.bazel b/ic-os/guestos/envs/local-base-dev/BUILD.bazel index ab0d2b6e912..983caaffd07 100644 --- a/ic-os/guestos/envs/local-base-dev/BUILD.bazel +++ b/ic-os/guestos/envs/local-base-dev/BUILD.bazel @@ -10,7 +10,10 @@ icos_build( build_local_base_image = True, ic_version = "//bazel:rc_only_version.txt", image_deps_func = image_deps, - tags = ["manual"], + tags = [ + "manual", + "no-cache", + ], upload_prefix = None, # Do not upload locally built base images visibility = ["//rs:ic-os-pkg"], ) diff --git a/ic-os/guestos/envs/local-base-prod/BUILD.bazel b/ic-os/guestos/envs/local-base-prod/BUILD.bazel index 021e01ce1ed..eaf48fd86cb 100644 --- a/ic-os/guestos/envs/local-base-prod/BUILD.bazel +++ b/ic-os/guestos/envs/local-base-prod/BUILD.bazel @@ -9,7 +9,10 @@ icos_build( name = "local-base-prod", build_local_base_image = True, image_deps_func = image_deps, - tags = ["manual"], + tags = [ + "manual", + "no-cache", + ], upload_prefix = None, # Do not upload locally built base images visibility = ["//rs:ic-os-pkg"], ) diff --git a/ic-os/hostos/context/docker-base.dev b/ic-os/hostos/context/docker-base.dev index 5b16144300a..fbc1bbbb2c9 100644 --- a/ic-os/hostos/context/docker-base.dev +++ b/ic-os/hostos/context/docker-base.dev @@ -1 +1 @@ -ghcr.io/dfinity/hostos-base-dev@sha256:bdb7f1419c1621fbd283603d1b191120b56c9b489bc26e133b1a867b4ae05fbb +ghcr.io/dfinity/hostos-base-dev@sha256:e591ca7461a5c3a774554899d8d42b7983e322ec87acf947744e5c6e829232df diff --git a/ic-os/hostos/context/docker-base.prod b/ic-os/hostos/context/docker-base.prod index 2e4702ceeec..4945c295ce8 100644 --- a/ic-os/hostos/context/docker-base.prod +++ b/ic-os/hostos/context/docker-base.prod @@ -1 +1 @@ -ghcr.io/dfinity/hostos-base@sha256:4ec891f7e7fc0409c736520016a4d3f752c2557ea0b4cd62d517ff2113cfe3e6 +ghcr.io/dfinity/hostos-base@sha256:cdd22abe85fab02972242760d82855278eae58c45c31b31fd35026e713b3b0ab diff --git a/ic-os/hostos/defs.bzl b/ic-os/hostos/defs.bzl index 114e6cb361a..f60ab207c02 100644 --- a/ic-os/hostos/defs.bzl +++ b/ic-os/hostos/defs.bzl @@ -97,7 +97,7 @@ def _custom_partitions(): vg_name = "hostlvm", vg_uuid = "4c7GVZ-Df82-QEcJ-xXtV-JgRL-IjLE-hK0FgA", pv_uuid = "eu0VQE-HlTi-EyRc-GceP-xZtn-3j6t-iqEwyv", - tags = ["manual"], + tags = ["manual", "no-cache"], target_compatible_with = [ "@platforms//os:linux", ], diff --git a/ic-os/hostos/envs/local-base-dev/BUILD.bazel b/ic-os/hostos/envs/local-base-dev/BUILD.bazel index 9d5e2c98427..b179b8ce556 100644 --- a/ic-os/hostos/envs/local-base-dev/BUILD.bazel +++ b/ic-os/hostos/envs/local-base-dev/BUILD.bazel @@ -10,7 +10,10 @@ icos_build( build_local_base_image = True, ic_version = "//bazel:rc_only_version.txt", image_deps_func = image_deps, - tags = ["manual"], + tags = [ + "manual", + "no-cache", + ], upload_prefix = None, # Do not upload locally built base images visibility = ["//rs:ic-os-pkg"], vuln_scan = False, diff --git a/ic-os/hostos/envs/local-base-prod/BUILD.bazel b/ic-os/hostos/envs/local-base-prod/BUILD.bazel index 34c7e5523a3..afb5e210401 100644 --- a/ic-os/hostos/envs/local-base-prod/BUILD.bazel +++ b/ic-os/hostos/envs/local-base-prod/BUILD.bazel @@ -9,7 +9,10 @@ icos_build( name = "local-base-prod", build_local_base_image = True, image_deps_func = image_deps, - tags = ["manual"], + tags = [ + "manual", + "no-cache", + ], upload_prefix = None, # Do not upload locally built base images visibility = ["//rs:ic-os-pkg"], vuln_scan = False, diff --git a/ic-os/setupos/README.adoc b/ic-os/setupos/README.adoc index 157782daac3..39eee13b533 100644 --- a/ic-os/setupos/README.adoc +++ b/ic-os/setupos/README.adoc @@ -63,8 +63,8 @@ The sequence of the scripts is defined in the main installation script, `setupos == Node Providers, Node Technicians, and Node Operators * *Node Provider*: An entity that purchases and owns the node hardware. Node Providers are rewarded for their node's useful work. -* *Node Technician*: These are the 'hired hands' or 'remote hands' employed by the Node Providers to maintain and manage the node. They do not necessarily own the hardware, but are responsible for its operation. Note that a single node will often have more than one Node Technician—the Node Technician can be thought of as the individual currently operating the node, so this role can cycle among several parties. -** Note: While it is possible, it is uncommon for a Node Provider to also act as their own Node Technician. +* *Node Technician*: Node Technicians are responsible for the physical installation, maintenance, and repair of the node. These are typically 'hired hands' or 'remote hands' employed by the Node Providers to maintain and manage the node. A single node may have more than one Node Technician — the Node Technician can be thought of as the individual currently operating the node, so this role can cycle among several parties. The information about the Node Technician is currently not stored in the NNS. +** Note: It is possible that a Node Provider and Node Technician are the same entity. * *Node Operator*: ** The term "Node Operator" refers not to a specific individual or group, but to: *** A specific record within the NNS registry—the *Node Operator record*, and to: diff --git a/ic-os/setupos/config/config.ini b/ic-os/setupos/config/config.ini index 14475fb8ee1..9d088d9959f 100644 --- a/ic-os/setupos/config/config.ini +++ b/ic-os/setupos/config/config.ini @@ -2,16 +2,6 @@ # # Update these settings according to your specific network and node requirements. -# ------------------------------ -# Node Reward Settings (required) -# ------------------------------ -# Update node_reward_type as per your node configuration. -# Node rewards setting should be of the form: -# node_reward_type=typeX or node_reward_type=typeX.Y -# Example: -# node_reward_type=type3.1 -node_reward_type= - # ------------------------------ # IPv6 Settings (required) # ------------------------------ @@ -23,6 +13,18 @@ ipv6_prefix=2a00:fb01:400:44 # Define the gateway address for your IPv6 network. Ensure it's within your network range: ipv6_gateway=2a00:fb01:400:44::1 +# ------------------------------ +# Node Reward Settings (Required for the ICP Mainnet) +# ----------------------------- +# +# Defines the node reward type that the operator expects for this node. +# The value must correspond to a community-approved type and operator configuration in the NNS. +# See https://wiki.internetcomputer.org/wiki/Node_Deployment_config.ini#node_reward_type_Documentation for more info. +# +# To find the rewardable node types for the node operator, run: +# ic-admin --nns-url https://ic0.app get-node-operator +# +# node_reward_type=type3.1 # ------------------------------ # IPv4 Settings (Optional) diff --git a/ic-os/setupos/context/docker-base.dev b/ic-os/setupos/context/docker-base.dev index 81392bdce94..69dc1bfcdee 100644 --- a/ic-os/setupos/context/docker-base.dev +++ b/ic-os/setupos/context/docker-base.dev @@ -1 +1 @@ -ghcr.io/dfinity/setupos-base-dev@sha256:47121f9cda6f228db91d50a0e736e815b88ed7707cf93ce267dd4fd9535ca103 +ghcr.io/dfinity/setupos-base-dev@sha256:b1bb85afcf7f2fa4071b34bdd60d6340ceaa7ad3bac3fe851dff68a4a443ac5b diff --git a/ic-os/setupos/context/docker-base.prod b/ic-os/setupos/context/docker-base.prod index 03cbcd1b529..5387da68cfd 100644 --- a/ic-os/setupos/context/docker-base.prod +++ b/ic-os/setupos/context/docker-base.prod @@ -1 +1 @@ -ghcr.io/dfinity/setupos-base@sha256:e5b31b465ce413f4ec5e938bf310ef7bd0704f9bf41aaa49e4b4cdc6cc2ea212 +ghcr.io/dfinity/setupos-base@sha256:ecf40c35c59bae2782a00d4a8a643341139b6a8e863d01cb6249308e0b7bc05f diff --git a/ic-os/setupos/defs.bzl b/ic-os/setupos/defs.bzl index 325ebb995fa..294c14cb3b6 100644 --- a/ic-os/setupos/defs.bzl +++ b/ic-os/setupos/defs.bzl @@ -31,6 +31,7 @@ def image_deps(mode, _malicious = False): "bootfs": {}, "rootfs": { "//rs/ic_os/release:setupos_tool": "/opt/ic/bin/setupos_tool:0755", + "//rs/ic_os/release:config": "/opt/ic/bin/config:0755", }, # Set various configuration values @@ -80,11 +81,11 @@ def _custom_partitions(mode): if mode == "dev": guest_image = Label("//ic-os/guestos/envs/dev:disk-img.tar.zst") host_image = Label("//ic-os/hostos/envs/dev:disk-img.tar.zst") - nns_url = "https://wiki.internetcomputer.org" + nns_url = "https://cloudflare.com/cdn-cgi/trace" elif mode == "local-base-dev": guest_image = Label("//ic-os/guestos/envs/local-base-dev:disk-img.tar.zst") host_image = Label("//ic-os/hostos/envs/local-base-dev:disk-img.tar.zst") - nns_url = "https://wiki.internetcomputer.org" + nns_url = "https://cloudflare.com/cdn-cgi/trace" elif mode == "local-base-prod": guest_image = Label("//ic-os/guestos/envs/local-base-prod:disk-img.tar.zst") host_image = Label("//ic-os/hostos/envs/local-base-prod:disk-img.tar.zst") @@ -101,7 +102,7 @@ def _custom_partitions(mode): src = guest_image, out = "guest-os.img.tar.zst", allow_symlink = True, - tags = ["manual"], + tags = ["manual", "no-cache"], ) copy_file( @@ -109,7 +110,7 @@ def _custom_partitions(mode): src = host_image, out = "host-os.img.tar.zst", allow_symlink = True, - tags = ["manual"], + tags = ["manual", "no-cache"], ) config_dict = { @@ -158,7 +159,7 @@ def _custom_partitions(mode): ], mode = "0644", package_dir = "data", - tags = ["manual"], + tags = ["manual", "no-cache"], ) ext4_image( @@ -169,7 +170,7 @@ def _custom_partitions(mode): target_compatible_with = [ "@platforms//os:linux", ], - tags = ["manual"], + tags = ["manual", "no-cache"], ) return [ diff --git a/ic-os/setupos/envs/local-base-dev/BUILD.bazel b/ic-os/setupos/envs/local-base-dev/BUILD.bazel index 12ca12c676f..66712587e07 100644 --- a/ic-os/setupos/envs/local-base-dev/BUILD.bazel +++ b/ic-os/setupos/envs/local-base-dev/BUILD.bazel @@ -11,7 +11,10 @@ icos_build( build_local_base_image = True, ic_version = "//bazel:rc_only_version.txt", image_deps_func = image_deps, - tags = ["manual"], + tags = [ + "manual", + "no-cache", + ], upgrades = False, upload_prefix = None, # Do not upload locally built base images visibility = ["//rs:ic-os-pkg"], diff --git a/ic-os/setupos/envs/local-base-prod/BUILD.bazel b/ic-os/setupos/envs/local-base-prod/BUILD.bazel index 3b564fe8583..948df7e85c3 100644 --- a/ic-os/setupos/envs/local-base-prod/BUILD.bazel +++ b/ic-os/setupos/envs/local-base-prod/BUILD.bazel @@ -10,7 +10,10 @@ icos_build( name = "local-base-prod", build_local_base_image = True, image_deps_func = image_deps, - tags = ["manual"], + tags = [ + "manual", + "no-cache", + ], upgrades = False, upload_prefix = None, # Do not upload locally built base images vuln_scan = False, diff --git a/mainnet-canister-revisions.json b/mainnet-canister-revisions.json index f044f082ec7..97762a62a50 100644 --- a/mainnet-canister-revisions.json +++ b/mainnet-canister-revisions.json @@ -4,40 +4,72 @@ "sha256": "92622a35dc03651a046ef02d48c3d3c3383a25307d719e4a7657601486fa9f4f" }, "ck_btc_archive": { - "rev": "2190613d3b5bcd9b74c382b22d151580b8ac271a", - "sha256": "f94cf1db965b7042197e5894fef54f5f413bb2ebc607ff0fb59c9d4dfd3babea" + "rev": "c741e349451edf0c9792149ad439bb32a0161371", + "sha256": "2b0970a84976bc2eb9591b68d44501566937994fa5594972f8aac9c8b058672f" }, "ck_btc_index": { - "rev": "2190613d3b5bcd9b74c382b22d151580b8ac271a", - "sha256": "2adc74fe5667f26ea4c4006309d99b1dfa71787aa43a5c168cb08ec725677996" + "rev": "c741e349451edf0c9792149ad439bb32a0161371", + "sha256": "e155db9d06b6147ece4f9defe599844f132a7db21693265671aa6ac60912935f" }, "ck_btc_ledger": { + "rev": "c741e349451edf0c9792149ad439bb32a0161371", + "sha256": "3b03d1bb1145edbcd11101ab2788517bc0f427c3bd7b342b9e3e7f42e29d5822" + }, + "ck_btc_ledger_v1": { + "rev": "d4ee25b0865e89d3eaac13a60f0016d5e3296b31", + "sha256": "a170bfdce5d66e751a3cc03747cb0f06b450af500e75e15976ec08a3f5691f4c" + }, + "ck_btc_ledger_v2": { + "rev": "e54d3fa34ded227c885d04e64505fa4b5d564743", + "sha256": "3d808fa63a3d8ebd4510c0400aa078e99a31afaa0515f0b68778f929ce4b2a46" + }, + "ck_btc_ledger_v2_noledgerversion": { + "rev": "aba60ffbc46acfc8990bf4d5685c1360bd7026b9", + "sha256": "67cfcbabb79e683b6fc855450d9972c9efaa7a1cd28c6387965616fbead191ea" + }, + "ck_btc_ledger_v3": { "rev": "2190613d3b5bcd9b74c382b22d151580b8ac271a", "sha256": "25071c2c55ad4571293e00d8e277f442aec7aed88109743ac52df3125209ff45" }, "ck_eth_archive": { - "rev": "2190613d3b5bcd9b74c382b22d151580b8ac271a", - "sha256": "2d25f7831894100d48aa9043c65e87c293487523f0958c15760027d004fbbda9" + "rev": "c741e349451edf0c9792149ad439bb32a0161371", + "sha256": "d2170c173f814fafc909737ec82f098714d44aa98ebd4f6dbf4e175160e1200f" }, "ck_eth_index": { - "rev": "2190613d3b5bcd9b74c382b22d151580b8ac271a", - "sha256": "d615ea66e7ec7e39a3912889ffabfabb9b6f200584b9656789c3578fae1afac7" + "rev": "c741e349451edf0c9792149ad439bb32a0161371", + "sha256": "d21d059962144c835c8b291af3033e1f1c835af2350a5cd92b3cf8d687a1a7be" }, "ck_eth_ledger": { + "rev": "c741e349451edf0c9792149ad439bb32a0161371", + "sha256": "8b2e3e596a147780b0e99ce36d0b8f1f3ba41a98b819b42980a7c08c309b44c1" + }, + "ck_eth_ledger_v1": { + "rev": "d4ee25b0865e89d3eaac13a60f0016d5e3296b31", + "sha256": "e6072806ae22868ee09c07923d093b1b0b687dba540d22cfc1e1a5392bfcca46" + }, + "ck_eth_ledger_v2": { + "rev": "e54d3fa34ded227c885d04e64505fa4b5d564743", + "sha256": "98a7b7391608dc4a554d6964bad24157b6aaf890a05bbaad3fcc92033d9c7b02" + }, + "ck_eth_ledger_v2_noledgerversion": { + "rev": "aba60ffbc46acfc8990bf4d5685c1360bd7026b9", + "sha256": "73d0c5f057aaf33004218ce588780e1b454c717c702b1cf47532f32c23515f1e" + }, + "ck_eth_ledger_v3": { "rev": "2190613d3b5bcd9b74c382b22d151580b8ac271a", "sha256": "9637743e1215a4db376a62ee807a0986faf20833be2b332df09b3d5dbdd7339e" }, "cycles-minting": { - "rev": "ee52ab3056cf5f39b09b08de70bdd20485c8b2dc", - "sha256": "bbb8995cb749ba9e2c721ff507f5e5313f32e69b1adf3df20e3901ed56a70b42" + "rev": "b5192581ccd35b67fe5a1f795ead9cbcd25956d6", + "sha256": "11c8dedd11741f05990498c90f925e9e37ad60647a65ef47caa59cdba234be6f" }, "genesis-token": { "rev": "4bed17bfc82cddc5691743db6228992cdc2740f4", "sha256": "fd25a4e2e283b498c3be1aaf63cc9b2726264d78a12b12f43ad453ceeb575e7c" }, "governance": { - "rev": "ee52ab3056cf5f39b09b08de70bdd20485c8b2dc", - "sha256": "a23918c2c5d1302e5d1149f557b0fb913ab65931c1bce3ffc94a48e3d14ecbac" + "rev": "03393bc817da78cdc27190eaa388b8f6f8990365", + "sha256": "542cb59d9ba2b4ba43b0690a839cf1d51dd49582a53e55fbf535eb08a23a14c4" }, "index": { "rev": "7c6309cb5bec7ab28ed657ac7672af08a59fc1ba", @@ -47,48 +79,56 @@ "rev": "7c6309cb5bec7ab28ed657ac7672af08a59fc1ba", "sha256": "a9ed1cb9dda555e0fc1038825eb7b3a6b366f17aa4b88575184c7537e864e551" }, + "ledger_v1": { + "rev": "6dcfafb491092704d374317d9a72a7ad2475d7c9", + "sha256": "4fe38a91a3130e9d8b39e3413ae3b3f46c40d3fbd507df1b6092f962d970a7ea" + }, + "ledger_v2": { + "rev": "dac2f36f96d7549d82fa8e3c714979255ce57afd", + "sha256": "50c05fd687883fe788c0bb91996de358d8f856ba56088c6ff47767ea853001d7" + }, "lifeline": { - "rev": "a0207146be211cdff83321c99e9e70baa62733c7", - "sha256": "76978515223287ece643bc7ca087eb310412b737e2382a73b8ae55fcb458da5b" + "rev": "b5192581ccd35b67fe5a1f795ead9cbcd25956d6", + "sha256": "8c8eb285de53ca5609abd7dc41ba3ec8eeb67708b81469311fd670e6738d7d0a" }, "registry": { - "rev": "86229594d61b433c39fc5331ab818ccb6c6aa6a7", - "sha256": "b0b2a7f37e76fcbab20a861fdf65c34d7ac2ca84a5190d204dfe5e1c50fb383e" + "rev": "a5878586e47536d4cd47f0aadb66b73df8131d2b", + "sha256": "f0fb8fa545b2cc68f030b040e1182a8d004c4d4f4bb4341c9f1b432642c85bef" }, "root": { - "rev": "c494c2af8bfc70a6501448dc73bf806477388738", - "sha256": "657010591182ce758c86f020d1eade5f7a188072cf0de9c41e2f9d577849c964" + "rev": "b5192581ccd35b67fe5a1f795ead9cbcd25956d6", + "sha256": "d3c702648ca4fb232f349bad7533c400c474a528abf62c05d4b100b4cdb91ce2" }, "sns-wasm": { "rev": "25c1bb0227d9970f5673b908817d7c4962b29911", "sha256": "68f9fb37341d14a35735e10a0eb2471721b4cd75b6552ac11124aac559b05736" }, "sns_archive": { - "rev": "2190613d3b5bcd9b74c382b22d151580b8ac271a", - "sha256": "f94cf1db965b7042197e5894fef54f5f413bb2ebc607ff0fb59c9d4dfd3babea" + "rev": "c741e349451edf0c9792149ad439bb32a0161371", + "sha256": "2b0970a84976bc2eb9591b68d44501566937994fa5594972f8aac9c8b058672f" }, "sns_governance": { - "rev": "25c1bb0227d9970f5673b908817d7c4962b29911", - "sha256": "51fd3d1a529f3f7bad808b19074e761ce3538282ac8189bd7067b4156360c279" + "rev": "03393bc817da78cdc27190eaa388b8f6f8990365", + "sha256": "5c43913c77f922a21f54b3422abf7cb43d369677e7668b7c8f91a429acd5c864" }, "sns_index": { - "rev": "2190613d3b5bcd9b74c382b22d151580b8ac271a", - "sha256": "2adc74fe5667f26ea4c4006309d99b1dfa71787aa43a5c168cb08ec725677996" + "rev": "c741e349451edf0c9792149ad439bb32a0161371", + "sha256": "e155db9d06b6147ece4f9defe599844f132a7db21693265671aa6ac60912935f" }, "sns_ledger": { - "rev": "2190613d3b5bcd9b74c382b22d151580b8ac271a", - "sha256": "25071c2c55ad4571293e00d8e277f442aec7aed88109743ac52df3125209ff45" + "rev": "c741e349451edf0c9792149ad439bb32a0161371", + "sha256": "3b03d1bb1145edbcd11101ab2788517bc0f427c3bd7b342b9e3e7f42e29d5822" }, "sns_ledger_v2": { "rev": "e54d3fa34ded227c885d04e64505fa4b5d564743", "sha256": "3d808fa63a3d8ebd4510c0400aa078e99a31afaa0515f0b68778f929ce4b2a46" }, "sns_root": { - "rev": "aa91ecacdf3824e193e21b70e0127e8d3edab51a", - "sha256": "431cb333feb3f762f742b0dea58745633a2a2ca41075e9933183d850b4ddb259" + "rev": "a5878586e47536d4cd47f0aadb66b73df8131d2b", + "sha256": "dc243135057d13c48f71d2f0a4b8f5fc43ed525d579d97dde23e052dca15bf96" }, "swap": { - "rev": "aa91ecacdf3824e193e21b70e0127e8d3edab51a", - "sha256": "8313ac22d2ef0a0c1290a85b47f235cfa24ca2c96d095b8dbed5502483b9cd18" + "rev": "a5878586e47536d4cd47f0aadb66b73df8131d2b", + "sha256": "45408ed654561dfb17c84b86948dda9498aa0ba8ee669ae774e5faca830c4c24" } } \ No newline at end of file diff --git a/mainnet-subnet-revisions.json b/mainnet-subnet-revisions.json index c9c2bc7fd77..c2a305644ca 100644 --- a/mainnet-subnet-revisions.json +++ b/mainnet-subnet-revisions.json @@ -1,6 +1,6 @@ { "subnets": { - "tdb26-jop6k-aogll-7ltgs-eruif-6kk7m-qpktf-gdiqx-mxtrf-vb5e6-eqe": "d9fe2076f677a08734bed90c67b1c3f4056ed621", - "io67a-2jmkw-zup3h-snbwi-g6a5n-rm5dn-b6png-lvdpl-nqnto-yih6l-gqe": "3e24396441e4c7380928d4e8b4ccff7de77d0e7e" + "tdb26-jop6k-aogll-7ltgs-eruif-6kk7m-qpktf-gdiqx-mxtrf-vb5e6-eqe": "233c1ee2ef68c1c8800b8151b2b9f38e17b8440a", + "io67a-2jmkw-zup3h-snbwi-g6a5n-rm5dn-b6png-lvdpl-nqnto-yih6l-gqe": "4ba583480e05a518aa2bcf36f5a0e48475e8edc2" } } \ No newline at end of file diff --git a/packages/ic-ethereum-types/src/serde_data/mod.rs b/packages/ic-ethereum-types/src/serde_data/mod.rs index 59902e43923..50b13ef7977 100644 --- a/packages/ic-ethereum-types/src/serde_data/mod.rs +++ b/packages/ic-ethereum-types/src/serde_data/mod.rs @@ -30,7 +30,7 @@ where { struct HexStrVisitor(PhantomData); - impl<'de, T> Visitor<'de> for HexStrVisitor + impl Visitor<'_> for HexStrVisitor where T: FromHex, ::Error: fmt::Display, diff --git a/packages/ic-ledger-hash-of/src/lib.rs b/packages/ic-ledger-hash-of/src/lib.rs index 584a9830fad..03716b1577d 100644 --- a/packages/ic-ledger-hash-of/src/lib.rs +++ b/packages/ic-ledger-hash-of/src/lib.rs @@ -93,7 +93,7 @@ impl<'de, T> Deserialize<'de> for HashOf { phantom: PhantomData, } - impl<'de, T> Visitor<'de> for HashOfVisitor { + impl Visitor<'_> for HashOfVisitor { type Value = HashOf; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/packages/ic-metrics-assert/BUILD.bazel b/packages/ic-metrics-assert/BUILD.bazel new file mode 100644 index 00000000000..5ee574d5a81 --- /dev/null +++ b/packages/ic-metrics-assert/BUILD.bazel @@ -0,0 +1,41 @@ +load("@rules_rust//rust:defs.bzl", "rust_doc", "rust_doc_test", "rust_library") + +package(default_visibility = ["//visibility:public"]) + +[ + rust_library( + name = "ic-metrics-assert" + name_suffix, + srcs = glob(["src/**/*.rs"]), + crate_features = features, + crate_name = "ic_metrics_assert", + deps = [ + # Keep sorted. + "@crate_index//:candid", + "@crate_index//:regex", + "@crate_index//:serde", + "@crate_index//:serde_bytes", + ] + extra_deps, + ) + for (name_suffix, features, extra_deps) in [ + [ + "", + [], + [], + ], + [ + "_pocket_ic", + ["pocket_ic"], + ["//packages/pocket-ic"], + ], + ] +] + +rust_doc( + name = "doc", + crate = ":ic-metrics-assert", +) + +rust_doc_test( + name = "doc_test", + crate = ":ic-metrics-assert_pocket_ic", +) diff --git a/packages/ic-metrics-assert/CHANGELOG.md b/packages/ic-metrics-assert/CHANGELOG.md new file mode 100644 index 00000000000..11bddf32c5b --- /dev/null +++ b/packages/ic-metrics-assert/CHANGELOG.md @@ -0,0 +1,8 @@ +# 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] diff --git a/packages/ic-metrics-assert/Cargo.toml b/packages/ic-metrics-assert/Cargo.toml new file mode 100644 index 00000000000..bcf398c76d1 --- /dev/null +++ b/packages/ic-metrics-assert/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "ic-metrics-assert" +version = "0.1.0" +description = "Fluent assertions for metrics" +license = "Apache-2.0" +readme = "README.md" +include = ["src", "Cargo.toml", "CHANGELOG.md", "LICENSE", "README.md"] +repository = "https://github.com/dfinity/ic" +authors.workspace = true +edition.workspace = true +documentation.workspace = true + +[dependencies] +candid = { workspace = true } +pocket-ic = { path = "../../packages/pocket-ic", optional = true } +regex = "1.11.0" +serde = { workspace = true } +serde_bytes = { workspace = true } + +[features] +pocket_ic = ["dep:pocket-ic"] diff --git a/packages/ic-metrics-assert/LICENSE b/packages/ic-metrics-assert/LICENSE new file mode 120000 index 00000000000..c87d0654aa6 --- /dev/null +++ b/packages/ic-metrics-assert/LICENSE @@ -0,0 +1 @@ +../../licenses/Apache-2.0.txt \ No newline at end of file diff --git a/packages/ic-metrics-assert/README.md b/packages/ic-metrics-assert/README.md new file mode 100644 index 00000000000..c8aa2c45cc8 --- /dev/null +++ b/packages/ic-metrics-assert/README.md @@ -0,0 +1,3 @@ +# IC Metrics Assert + +This package defines test utilities to perform assertions on canister metrics collected by Prometheus. diff --git a/packages/ic-metrics-assert/src/lib.rs b/packages/ic-metrics-assert/src/lib.rs new file mode 100644 index 00000000000..173fb85fbfb --- /dev/null +++ b/packages/ic-metrics-assert/src/lib.rs @@ -0,0 +1,180 @@ +//! Fluent assertions for metrics. + +#![forbid(missing_docs)] + +use candid::{CandidType, Decode, Deserialize, Encode}; +use regex::Regex; +use std::fmt::Debug; + +/// Provides fluent test assertions for metrics. +/// +/// # Examples +/// +/// ```rust +/// use ic_metrics_assert::{MetricsAssert, PocketIcHttpQuery}; +/// use pocket_ic::{management_canister::CanisterId, PocketIc}; +/// +/// struct Setup { +/// env: PocketIc, +/// canister_id : CanisterId, +/// } +/// +/// impl Setup { +/// pub fn check_metrics(self) -> MetricsAssert { +/// MetricsAssert::from_http_query(self) +/// } +/// } +/// +/// impl PocketIcHttpQuery for Setup { +/// fn get_pocket_ic(&self) -> &PocketIc { +/// &self.env +/// } +/// +/// fn get_canister_id(&self) -> CanisterId { +/// self.canister_id +/// } +/// } +/// +/// fn assert_metrics () { +/// use pocket_ic::PocketIcBuilder; +/// use candid::Principal; +/// +/// let env = PocketIcBuilder::new().build(); +/// let canister_id = Principal::from_text("7hfb6-caaaa-aaaar-qadga-cai").unwrap(); +/// let setup = Setup {env, canister_id}; +/// +/// setup +/// .check_metrics() +/// .assert_contains_metric_matching("started action \\d+") +/// .assert_contains_metric_matching("completed action 1") +/// .assert_does_not_contain_metric_matching(".*trap.*"); +/// } +pub struct MetricsAssert { + actual: T, + metrics: Vec, +} + +impl MetricsAssert { + /// Initializes an instance of [MetricsAssert] by querying the metrics from the `/metrics` + /// endpoint of a canister via the [CanisterHttpQuery::http_query] method. + pub fn from_http_query(actual: T) -> Self + where + T: CanisterHttpQuery, + E: Debug, + { + let request = http::HttpRequest { + method: "GET".to_string(), + url: "/metrics".to_string(), + headers: Default::default(), + body: Default::default(), + }; + let response = Decode!( + &actual + .http_query(Encode!(&request).expect("failed to encode HTTP request")) + .expect("failed to retrieve metrics"), + http::HttpResponse + ) + .unwrap(); + assert_eq!(response.status_code, 200_u16); + let metrics = String::from_utf8_lossy(response.body.as_slice()) + .trim() + .split('\n') + .map(|line| line.to_string()) + .collect::>(); + Self { metrics, actual } + } + + /// Returns the internal instance being tested. + pub fn into(self) -> T { + self.actual + } + + /// Asserts that the metrics contain at least one entry matching the given Regex pattern. + pub fn assert_contains_metric_matching(self, pattern: &str) -> Self { + assert!( + !self.find_metrics_matching(pattern).is_empty(), + "Expected to find metric matching '{}', but none matched in:\n{:?}", + pattern, + self.metrics + ); + self + } + + /// Asserts that the metrics do not contain any entries matching the given Regex pattern. + pub fn assert_does_not_contain_metric_matching(self, pattern: &str) -> Self { + let matches = self.find_metrics_matching(pattern); + assert!( + matches.is_empty(), + "Expected not to find any metric matching '{}', but found the following matches:\n{:?}", + pattern, + matches + ); + self + } + + fn find_metrics_matching(&self, pattern: &str) -> Vec { + let regex = Regex::new(pattern).unwrap_or_else(|_| panic!("Invalid regex: {}", pattern)); + self.metrics + .iter() + .filter(|line| regex.is_match(line)) + .cloned() + .collect() + } +} + +/// Trait providing the ability to perform an HTTP request to a canister. +pub trait CanisterHttpQuery { + /// Sends a serialized HTTP request to a canister and returns the serialized HTTP response. + fn http_query(&self, request: Vec) -> Result, E>; +} + +#[cfg(feature = "pocket_ic")] +pub use pocket_ic_query_call::PocketIcHttpQuery; + +#[cfg(feature = "pocket_ic")] +mod pocket_ic_query_call { + use super::*; + use candid::Principal; + use pocket_ic::{management_canister::CanisterId, PocketIc, RejectResponse}; + + /// Provides an implementation of the [CanisterHttpQuery] trait in the case where the canister + /// HTTP requests are made through an instance of [PocketIc]. + pub trait PocketIcHttpQuery { + /// Returns a reference to the instance of [PocketIc] through which the HTTP requests are made. + fn get_pocket_ic(&self) -> &PocketIc; + + /// Returns the ID of the canister to which HTTP requests will be made. + fn get_canister_id(&self) -> CanisterId; + } + + impl CanisterHttpQuery for T { + fn http_query(&self, request: Vec) -> Result, RejectResponse> { + self.get_pocket_ic().query_call( + self.get_canister_id(), + Principal::anonymous(), + "http_request", + request, + ) + } + } +} + +mod http { + use super::*; + use serde_bytes::ByteBuf; + + #[derive(Clone, Debug, CandidType, Deserialize)] + pub struct HttpRequest { + pub method: String, + pub url: String, + pub headers: Vec<(String, String)>, + pub body: ByteBuf, + } + + #[derive(Clone, Debug, CandidType, Deserialize)] + pub struct HttpResponse { + pub status_code: u16, + pub headers: Vec<(String, String)>, + pub body: ByteBuf, + } +} diff --git a/packages/ic-sha3/BUILD.bazel b/packages/ic-sha3/BUILD.bazel index 65f411d555a..641d0acc48d 100644 --- a/packages/ic-sha3/BUILD.bazel +++ b/packages/ic-sha3/BUILD.bazel @@ -28,7 +28,7 @@ rust_test_suite( srcs = glob( ["tests/*.rs"], ), - data = [ + compile_data = [ "test_resources/SHAKE256ShortMsg_subset.rsp", "test_resources/SHAKE256VariableOut_subset.rsp", ], diff --git a/packages/ic-signature-verification/Cargo.toml b/packages/ic-signature-verification/Cargo.toml index b0ef343ccee..d3a4d919490 100644 --- a/packages/ic-signature-verification/Cargo.toml +++ b/packages/ic-signature-verification/Cargo.toml @@ -13,7 +13,7 @@ documentation = "https://docs.rs/ic-signature-verification" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ic-canister-sig-creation = "1.0" +ic-canister-sig-creation = { workspace = true } ic-certification = { workspace = true } ic-verify-bls-signature = { version = "0.6", default-features = false, features = [ "alloc", diff --git a/packages/icrc-cbor/CHANGELOG.md b/packages/icrc-cbor/CHANGELOG.md index 0d523739a83..35d42ec0e9a 100644 --- a/packages/icrc-cbor/CHANGELOG.md +++ b/packages/icrc-cbor/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## 0.1.0 + ### Added - Initial version of the library diff --git a/packages/icrc-ledger-types/CHANGELOG.md b/packages/icrc-ledger-types/CHANGELOG.md index 4d367351f7c..f434ae8fc19 100644 --- a/packages/icrc-ledger-types/CHANGELOG.md +++ b/packages/icrc-ledger-types/CHANGELOG.md @@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## 0.1.8 + +### Added + +- Add default encoding and decoding of a Principal in a Subaccount. + +## 0.1.7 + +### Added + +- Rustdoc. + ## 0.1.6 ### Added diff --git a/packages/icrc-ledger-types/Cargo.toml b/packages/icrc-ledger-types/Cargo.toml index cac51dc29ab..3164c145c7f 100644 --- a/packages/icrc-ledger-types/Cargo.toml +++ b/packages/icrc-ledger-types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "icrc-ledger-types" -version = "0.1.6" +version = "0.1.8" description = "Types for interacting with DFINITY's implementation of the ICRC-1 fungible token standard." license = "Apache-2.0" readme = "README.md" @@ -16,7 +16,7 @@ candid = { workspace = true } crc32fast = "1.2.0" hex = { workspace = true } ic-stable-structures = { workspace = true } -icrc-cbor = { path = "../icrc-cbor" } +icrc-cbor = { path = "../icrc-cbor", version = "0.1.0" } itertools = { workspace = true } minicbor = { workspace = true } num-bigint = { workspace = true } diff --git a/packages/icrc-ledger-types/src/icrc/generic_metadata_value.rs b/packages/icrc-ledger-types/src/icrc/generic_metadata_value.rs index a46c9c33bc8..bb5f4d169cd 100644 --- a/packages/icrc-ledger-types/src/icrc/generic_metadata_value.rs +++ b/packages/icrc-ledger-types/src/icrc/generic_metadata_value.rs @@ -2,7 +2,12 @@ use candid::{CandidType, Deserialize, Int, Nat}; use serde::Serialize; use serde_bytes::ByteBuf; -/// Variant type for the `metadata` endpoint values. +/// Variant type for the `icrc1_metadata` endpoint values. The corresponding metadata keys are +/// arbitrary Unicode strings and must follow the pattern `:`, where `` +/// is a string not containing colons. The namespace `icrc1` is reserved for keys defined in the +/// ICRC-1 standard. For more information, see the +/// [documentation of Metadata in the ICRC-1 standard](https://github.com/dfinity/ICRC-1/tree/main/standards/ICRC-1#metadata). +/// Note that the `MetadataValue` type is a subset of the [`icrc_ledger_types::icrc::generic_value::ICRC3Value`] type. #[derive(CandidType, Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub enum MetadataValue { Nat(Nat), @@ -12,6 +17,7 @@ pub enum MetadataValue { } impl MetadataValue { + /// Create a `(String, MetadataValue)` tuple for use in metadata maps. pub fn entry(key: impl ToString, val: impl Into) -> (String, Self) { (key.to_string(), val.into()) } diff --git a/packages/icrc-ledger-types/src/icrc/generic_value.rs b/packages/icrc-ledger-types/src/icrc/generic_value.rs index c7994cd193f..47b931e0037 100644 --- a/packages/icrc-ledger-types/src/icrc/generic_value.rs +++ b/packages/icrc-ledger-types/src/icrc/generic_value.rs @@ -15,6 +15,7 @@ pub type Map = BTreeMap; pub type ICRC3Map = BTreeMap; pub type Hash = [u8; 32]; +/// A value defined in [the ICRC-3 standard](https://github.com/dfinity/ICRC-1/blob/main/standards/ICRC-3/README.md#value). #[derive(CandidType, Serialize, Deserialize, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum ICRC3Value { Blob(ByteBuf), @@ -33,6 +34,7 @@ impl std::fmt::Display for ICRC3Value { } impl ICRC3Value { + /// Compute [the hash of an ICRC-3 value](https://github.com/dfinity/ICRC-1/blob/main/standards/ICRC-3/README.md#value). pub fn hash(self) -> Hash { // TODO(FI-1263): copy the value hash function to avoid cloning self Value::from(self).hash() diff --git a/packages/icrc-ledger-types/src/icrc/mod.rs b/packages/icrc-ledger-types/src/icrc/mod.rs index 299fac452e6..3ed31f22887 100644 --- a/packages/icrc-ledger-types/src/icrc/mod.rs +++ b/packages/icrc-ledger-types/src/icrc/mod.rs @@ -1,3 +1,5 @@ +//! Types for ICRC standards. + pub mod generic_metadata_value; pub mod generic_value; pub mod generic_value_predicate; diff --git a/packages/icrc-ledger-types/src/icrc1/account.rs b/packages/icrc-ledger-types/src/icrc1/account.rs index 96254ffbd17..6eacf6406a5 100644 --- a/packages/icrc-ledger-types/src/icrc1/account.rs +++ b/packages/icrc-ledger-types/src/icrc1/account.rs @@ -15,7 +15,8 @@ pub type Subaccount = [u8; 32]; pub const DEFAULT_SUBACCOUNT: &Subaccount = &[0; 32]; -// Account representation of ledgers supporting the ICRC1 standard +/// [Account](https://github.com/dfinity/ICRC-1/blob/main/standards/ICRC-3/README.md#value) +/// representation of ledgers supporting the ICRC-1 standard. #[derive(Serialize, CandidType, Deserialize, Clone, Debug, Copy, Encode, Decode)] pub struct Account { #[cbor(n(0), with = "icrc_cbor::principal")] @@ -25,6 +26,8 @@ pub struct Account { } impl Account { + /// The effective subaccount of an account - the subaccount if it is set, otherwise the default + /// subaccount of all zeroes. #[inline] pub fn effective_subaccount(&self) -> &Subaccount { self.subaccount.as_ref().unwrap_or(DEFAULT_SUBACCOUNT) @@ -188,6 +191,23 @@ impl Storable for Account { const BOUND: Bound = Bound::Unbounded; } +/// Maps a `Principal` to a `Subaccount`. +/// Can be used to create a separate `Subaccount` for each `Principal`. +pub fn principal_to_subaccount(principal: Principal) -> Subaccount { + let mut subaccount = [0; 32]; + let principal = principal.as_slice(); + subaccount[0] = principal.len().try_into().unwrap(); + subaccount[1..1 + principal.len()].copy_from_slice(principal); + subaccount +} + +/// Maps a `Subaccount` to a `Principal`. +/// Reverse of `principal_to_subaccount` above. +pub fn subaccount_to_principal(subaccount: Subaccount) -> Principal { + let len = subaccount[0] as usize; + Principal::from_slice(&subaccount[1..len + 1]) +} + #[cfg(test)] mod tests { use assert_matches::assert_matches; @@ -199,7 +219,9 @@ mod tests { use candid::Principal; - use crate::icrc1::account::{Account, ICRC1TextReprError}; + use crate::icrc1::account::{ + principal_to_subaccount, subaccount_to_principal, Account, ICRC1TextReprError, + }; pub fn principal_strategy() -> impl Strategy { let bytes_strategy = prop::collection::vec(0..=255u8, 29); @@ -356,6 +378,15 @@ mod tests { }) } + #[test] + fn test_principal_to_subaccount() { + use proptest::{prop_assert_eq, proptest}; + proptest!(|(principal in principal_strategy())| { + let subaccount = principal_to_subaccount(principal); + prop_assert_eq!(subaccount_to_principal(subaccount), principal); + }) + } + #[test] fn test_account_serialization_stability() { let owner = diff --git a/packages/icrc-ledger-types/src/icrc1/mod.rs b/packages/icrc-ledger-types/src/icrc1/mod.rs index 365317be2b5..9e2517c5dd8 100644 --- a/packages/icrc-ledger-types/src/icrc1/mod.rs +++ b/packages/icrc-ledger-types/src/icrc1/mod.rs @@ -1,2 +1,5 @@ +//! The [ICRC-1](https://github.com/dfinity/ICRC-1/blob/main/standards/ICRC-1/README.md) +//! token standard. + pub mod account; pub mod transfer; diff --git a/packages/icrc-ledger-types/src/icrc1/transfer.rs b/packages/icrc-ledger-types/src/icrc1/transfer.rs index ba1e3b62a2e..961d98f8199 100644 --- a/packages/icrc-ledger-types/src/icrc1/transfer.rs +++ b/packages/icrc-ledger-types/src/icrc1/transfer.rs @@ -7,6 +7,7 @@ use std::fmt; pub type NumTokens = Nat; pub type BlockIndex = Nat; +/// The arguments for the [ICRC-1 `transfer`](https://github.com/dfinity/ICRC-1/blob/main/standards/ICRC-1/README.md#icrc1_transfer-) endpoint. #[derive(CandidType, Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub struct TransferArg { #[serde(default)] @@ -21,6 +22,9 @@ pub struct TransferArg { pub amount: NumTokens, } +/// The [`Memo`](https://github.com/dfinity/ICRC-1/blob/main/standards/ICRC-1/README.md#icrc1_transfer-) +/// is an arbitrary blob that has no meaning to the ledger. The ledger SHOULD allow memos of at +/// least 32 bytes in length. #[derive( Serialize, Deserialize, CandidType, Clone, Hash, Debug, PartialEq, Eq, PartialOrd, Ord, Default, )] @@ -51,6 +55,9 @@ impl From for ByteBuf { } } +/// Errors defined for the +/// [ICRC-1 `transfer`](https://github.com/dfinity/ICRC-1/blob/main/standards/ICRC-1/README.md#icrc1_transfer-) +/// endpoint. #[derive(CandidType, Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub enum TransferError { BadFee { expected_fee: NumTokens }, diff --git a/packages/icrc-ledger-types/src/icrc2/allowance.rs b/packages/icrc-ledger-types/src/icrc2/allowance.rs index aebda165fbb..72f9f6a36d8 100644 --- a/packages/icrc-ledger-types/src/icrc2/allowance.rs +++ b/packages/icrc-ledger-types/src/icrc2/allowance.rs @@ -3,12 +3,18 @@ use serde::Serialize; use super::super::icrc1::account::Account; +/// The arguments for the +/// [ICRC-2 `allowance`](https://github.com/dfinity/ICRC-1/blob/main/standards/ICRC-2/README.md#icrc2_allowance) +/// endpoint. #[derive(CandidType, Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub struct AllowanceArgs { pub account: Account, pub spender: Account, } +/// The `Allowance` response type for the +/// [ICRC-2 `allowance`](https://github.com/dfinity/ICRC-1/blob/main/standards/ICRC-2/README.md#icrc2_allowance) +/// endpoint. #[derive(CandidType, Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub struct Allowance { pub allowance: Nat, diff --git a/packages/icrc-ledger-types/src/icrc2/approve.rs b/packages/icrc-ledger-types/src/icrc2/approve.rs index b85f625fbdf..b1d68f9bd56 100644 --- a/packages/icrc-ledger-types/src/icrc2/approve.rs +++ b/packages/icrc-ledger-types/src/icrc2/approve.rs @@ -5,6 +5,9 @@ use std::fmt; use super::super::icrc1::account::{Account, Subaccount}; use super::super::icrc1::transfer::Memo; +/// The arguments for the +/// [ICRC-2 `approve`](https://github.com/dfinity/ICRC-1/blob/main/standards/ICRC-2/README.md#icrc2_approve) +/// endpoint. #[derive(CandidType, Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub struct ApproveArgs { #[serde(default)] @@ -23,6 +26,9 @@ pub struct ApproveArgs { pub created_at_time: Option, } +/// The error return type for the +/// [ICRC-2 `approve`](https://github.com/dfinity/ICRC-1/blob/main/standards/ICRC-2/README.md#icrc2_approve) +/// endpoint. #[derive(CandidType, Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub enum ApproveError { BadFee { expected_fee: Nat }, diff --git a/packages/icrc-ledger-types/src/icrc2/mod.rs b/packages/icrc-ledger-types/src/icrc2/mod.rs index 29e6531d0d6..6d59e037dbd 100644 --- a/packages/icrc-ledger-types/src/icrc2/mod.rs +++ b/packages/icrc-ledger-types/src/icrc2/mod.rs @@ -1,3 +1,5 @@ +//! The [ICRC-2](https://github.com/dfinity/ICRC-1/blob/main/standards/ICRC-2/README.md) +//! Approve and Transfer From standard. pub mod allowance; pub mod approve; pub mod transfer_from; diff --git a/packages/icrc-ledger-types/src/icrc2/transfer_from.rs b/packages/icrc-ledger-types/src/icrc2/transfer_from.rs index 33d599fbbd0..f4136e1d216 100644 --- a/packages/icrc-ledger-types/src/icrc2/transfer_from.rs +++ b/packages/icrc-ledger-types/src/icrc2/transfer_from.rs @@ -5,6 +5,9 @@ use std::fmt; use super::super::icrc1::account::{Account, Subaccount}; use super::super::icrc1::transfer::Memo; +/// The arguments for the +/// [ICRC-2 `transfer_from`](https://github.com/dfinity/ICRC-1/blob/main/standards/ICRC-2/README.md#icrc2_transfer_from) +/// endpoint. #[derive(CandidType, Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub struct TransferFromArgs { #[serde(default)] @@ -20,6 +23,9 @@ pub struct TransferFromArgs { pub created_at_time: Option, } +/// The error return type for the +/// [ICRC-2 `transfer_from`](https://github.com/dfinity/ICRC-1/blob/main/standards/ICRC-2/README.md#icrc2_transfer_from) +/// endpoint. #[derive(CandidType, Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub enum TransferFromError { BadFee { expected_fee: Nat }, diff --git a/packages/icrc-ledger-types/src/icrc21/mod.rs b/packages/icrc-ledger-types/src/icrc21/mod.rs index 7017638351d..3b4af7afacc 100644 --- a/packages/icrc-ledger-types/src/icrc21/mod.rs +++ b/packages/icrc-ledger-types/src/icrc21/mod.rs @@ -1,3 +1,6 @@ +//! The [ICRC-21](https://github.com/dfinity/wg-identity-authentication/blob/main/topics/ICRC-21/icrc_21_consent_msg.md) +//! Canister Call Consent Messages standard. + pub mod errors; pub mod lib; pub mod requests; diff --git a/packages/icrc-ledger-types/src/icrc3/archive.rs b/packages/icrc-ledger-types/src/icrc3/archive.rs index ddc69ba4d57..91ad27c9799 100644 --- a/packages/icrc-ledger-types/src/icrc3/archive.rs +++ b/packages/icrc-ledger-types/src/icrc3/archive.rs @@ -8,6 +8,10 @@ use candid::{CandidType, Deserialize, Nat, Principal}; use serde::Serialize; use std::marker::PhantomData; +/// Deprecated. The information in the `ArchivedRange` struct is returned as part of the return value +/// of [`crate::icrc3::blocks::GetBlocksResult`] from the +/// [`icrc3_get_blocks`](https://github.com/dfinity/ICRC-1/blob/main/standards/ICRC-3/README.md) +/// endpoint. #[derive(CandidType, Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub struct ArchivedRange { pub start: Nat, @@ -15,6 +19,10 @@ pub struct ArchivedRange { pub callback: Callback, } +/// Details on the callback function using which archived blocks can be retrieved. Returned as part +/// of [`crate::icrc3::blocks::GetBlocksResult`] from the +/// [`icrc3_get_blocks`](https://github.com/dfinity/ICRC-1/blob/main/standards/ICRC-3/README.md) +/// endpoint. #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(try_from = "candid::types::reference::Func")] pub struct QueryArchiveFn { @@ -108,6 +116,7 @@ impl CandidType for QueryArchiveFn; pub type QueryTxArchiveFn = QueryArchiveFn; +/// The argument for the +/// [`icrc3_get_archives`](https://github.com/dfinity/ICRC-1/blob/main/standards/ICRC-3/README.md) +/// endpoint. #[derive(CandidType, Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub struct GetArchivesArgs { // The last archive seen by the client. @@ -126,6 +138,9 @@ pub struct GetArchivesArgs { pub from: Option, } +/// The information returned as part of the return value for the +/// [`icrc3_get_archives`](https://github.com/dfinity/ICRC-1/blob/main/standards/ICRC-3/README.md) +/// endpoint. #[derive(CandidType, Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub struct ICRC3ArchiveInfo { // The id of the archive @@ -138,4 +153,7 @@ pub struct ICRC3ArchiveInfo { pub end: Nat, } +/// The return value for the +/// [`icrc3_get_archives`](https://github.com/dfinity/ICRC-1/blob/main/standards/ICRC-3/README.md) +/// endpoint. pub type GetArchivesResult = Vec; diff --git a/packages/icrc-ledger-types/src/icrc3/blocks.rs b/packages/icrc-ledger-types/src/icrc3/blocks.rs index 3d44dc263bf..fb4873e54d3 100644 --- a/packages/icrc-ledger-types/src/icrc3/blocks.rs +++ b/packages/icrc-ledger-types/src/icrc3/blocks.rs @@ -8,7 +8,7 @@ use serde_bytes::ByteBuf; use super::archive::QueryArchiveFn; -/// Deprecated, use `ICRC3GenericBlock` instead +/// Deprecated, use [`ICRC3GenericBlock`] instead pub type GenericBlock = Value; pub type ICRC3GenericBlock = ICRC3Value; @@ -22,18 +22,23 @@ pub struct GetBlocksResponse { pub archived_blocks: Vec>, } +/// A block with an ID. Returned as part of [`GetBlocksResult`]. #[derive(Debug, CandidType, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct BlockWithId { pub id: Nat, pub block: ICRC3GenericBlock, } +/// Information about where to find archived blocks. Returned as part of [`GetBlocksResult`]. #[derive(Debug, CandidType, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct ArchivedBlocks { pub args: Vec, pub callback: QueryArchiveFn, GetBlocksResult>, } +/// The result type for the +/// [ICRC-3 `icrc3_get_blocks`](https://github.com/dfinity/ICRC-1/blob/main/standards/ICRC-3/README.md#icrc3_get_blocks) +/// endpoint. #[derive(Debug, CandidType, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct GetBlocksResult { pub log_length: Nat, @@ -41,12 +46,16 @@ pub struct GetBlocksResult { pub archived_blocks: Vec, } +/// The arguments for the +/// [ICRC-3 `icrc3_get_blocks`](https://github.com/dfinity/ICRC-1/blob/main/standards/ICRC-3/README.md#icrc3_get_blocks) +/// endpoint. #[derive(CandidType, Serialize, Deserialize, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct GetBlocksRequest { pub start: BlockIndex, pub length: Nat, } +/// The return type for the deprecated `get_blocks` endpoint of the archive canister. #[derive(CandidType, Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub struct BlockRange { pub blocks: Vec, @@ -81,12 +90,18 @@ pub struct DataCertificate { pub hash_tree: serde_bytes::ByteBuf, } +/// The data certificate returned from the +/// [ICRC-3 `icrc3_get_tip_certificate`](https://github.com/dfinity/ICRC-1/blob/main/standards/ICRC-3/README.md#icrc3_get_tip_certificate) +/// endpoint. #[derive(Debug, CandidType, Serialize, Deserialize)] pub struct ICRC3DataCertificate { pub certificate: serde_bytes::ByteBuf, pub hash_tree: serde_bytes::ByteBuf, } +/// The return type of the +/// [ICRC-3 `icrc3_supported_block_types`](https://github.com/dfinity/ICRC-1/blob/main/standards/ICRC-3/README.md#icrc3_supported_block_types) +/// endpoint. #[derive(Debug, CandidType, Serialize, Deserialize)] pub struct SupportedBlockType { pub block_type: String, diff --git a/packages/icrc-ledger-types/src/icrc3/mod.rs b/packages/icrc-ledger-types/src/icrc3/mod.rs index db50aef6ccf..fd1572dd8ed 100644 --- a/packages/icrc-ledger-types/src/icrc3/mod.rs +++ b/packages/icrc-ledger-types/src/icrc3/mod.rs @@ -1,3 +1,6 @@ +//! The [ICRC-3](https://github.com/dfinity/ICRC-1/blob/main/standards/ICRC-3/README.md) +//! Block Log standard. + pub mod archive; pub mod blocks; pub mod schema; diff --git a/packages/icrc-ledger-types/src/icrc3/schema.rs b/packages/icrc-ledger-types/src/icrc3/schema.rs index 95b1fa75b03..3ebd7453728 100644 --- a/packages/icrc-ledger-types/src/icrc3/schema.rs +++ b/packages/icrc-ledger-types/src/icrc3/schema.rs @@ -8,6 +8,7 @@ use crate::icrc::{ }, }; +/// Validate if a block is compatible with the ICRC-3 schema. // TODO(FI-1241): make it compatible with the final ICRC-3 schema pub fn validate(block: &Value) -> Result<(), ValuePredicateFailures> { use ItemRequirement::*; diff --git a/packages/icrc-ledger-types/src/icrc3/transactions.rs b/packages/icrc-ledger-types/src/icrc3/transactions.rs index ff6fd3575d7..e6f9f06141b 100644 --- a/packages/icrc-ledger-types/src/icrc3/transactions.rs +++ b/packages/icrc-ledger-types/src/icrc3/transactions.rs @@ -113,6 +113,9 @@ impl Transaction { } } +/// Deprecated. Use [`GetBlocksResponse`] returned from the +/// [`icrc3_get_blocks`](https://github.com/dfinity/ICRC-1/blob/main/standards/ICRC-3/README.md) +/// endpoint instead. #[derive(CandidType, Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub struct GetTransactionsResponse { pub log_length: Nat, @@ -121,6 +124,7 @@ pub struct GetTransactionsResponse { pub archived_transactions: Vec>, } +/// Deprecated. Use Vec<[`ICRC3GenericBlock`]> instead #[derive(CandidType, Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub struct TransactionRange { pub transactions: Vec, diff --git a/packages/icrc-ledger-types/src/lib.rs b/packages/icrc-ledger-types/src/lib.rs index f532acb4880..454b5d6b2cb 100644 --- a/packages/icrc-ledger-types/src/lib.rs +++ b/packages/icrc-ledger-types/src/lib.rs @@ -1,3 +1,5 @@ +//! A library of types to communicate with a ledger canister that implements ICRC standards. + pub mod icrc; pub mod icrc1; pub mod icrc2; diff --git a/packages/pocket-ic/BUILD.bazel b/packages/pocket-ic/BUILD.bazel index 56cbb3fed7e..3cbe06bfe13 100644 --- a/packages/pocket-ic/BUILD.bazel +++ b/packages/pocket-ic/BUILD.bazel @@ -1,4 +1,4 @@ -load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test", "rust_test_suite") +load("@rules_rust//rust:defs.bzl", "rust_doc_test", "rust_library", "rust_test", "rust_test_suite") package(default_visibility = ["//visibility:public"]) @@ -33,6 +33,8 @@ MACRO_DEPENDENCIES = [ TEST_DEPENDENCIES = [ # Keep sorted. "//rs/types/error_types", + #TODO: try upgrading this to the latest bitcion crate + "@crate_index//:bitcoin_0_28", "@crate_index//:candid_parser", "@crate_index//:ed25519-dalek", "@crate_index//:flate2", @@ -50,6 +52,11 @@ rust_library( deps = DEPENDENCIES, ) +rust_doc_test( + name = "pocket-ic-doc-test", + crate = ":pocket-ic", +) + rust_test( name = "pocket-ic-test", srcs = glob(["src/**/*.rs"]), diff --git a/packages/pocket-ic/CHANGELOG.md b/packages/pocket-ic/CHANGELOG.md index 1bf0fe02b18..1f73264f836 100644 --- a/packages/pocket-ic/CHANGELOG.md +++ b/packages/pocket-ic/CHANGELOG.md @@ -10,9 +10,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - The function `PocketIcBuilder::with_bitcoind_addrs` to specify multiple addresses and ports at which `bitcoind` processes are listening. - The function `PocketIc::query_call_with_effective_principal` for making generic query calls (including management canister query calls). -- The function `PocketIc::ingress_status` to fetch the status of an update call submitted through an ingress message (`None` means that the status is unknown yet). +- The function `PocketIc::ingress_status` to fetch the status of an update call submitted through an ingress message. +- The function `PocketIc::ingress_status_as` to fetch the status of an update call submitted through an ingress message. + If the status of the update call is known, but the update call was submitted by a different caller, then an error is returned. - The function `PocketIc::await_call_no_ticks` to await the status of an update call (submitted through an ingress message) becoming known without triggering round execution (round execution must be triggered separarely, e.g., on a "live" instance or by separate PocketIC library calls). +- The function `PocketIc::set_certified_time` to set the current certified time on all subnets of the PocketIC instance. + +### Changed +- The response types `pocket_ic::WasmResult`, `pocket_ic::UserError`, and `pocket_ic::CallError` are replaced by a single reject response type `pocket_ic::RejectResponse`. diff --git a/packages/pocket-ic/Cargo.toml b/packages/pocket-ic/Cargo.toml index 01e3ad0f479..2f607c8ed66 100644 --- a/packages/pocket-ic/Cargo.toml +++ b/packages/pocket-ic/Cargo.toml @@ -46,6 +46,8 @@ tracing-subscriber = { workspace = true } wslpath = "0.0.2" [dev-dependencies] +#TODO: try upgrading this to the latest bitcion crate +bitcoin = { version = "0.28.2" } candid_parser = { workspace = true } ed25519-dalek = { workspace = true } flate2 = { workspace = true } diff --git a/packages/pocket-ic/HOWTO.md b/packages/pocket-ic/HOWTO.md index e1244a3cfcf..e3e6b8dc6ff 100644 --- a/packages/pocket-ic/HOWTO.md +++ b/packages/pocket-ic/HOWTO.md @@ -242,23 +242,22 @@ Here is a sketch of a test for a canister making canister HTTP outcalls: fn test_canister_http() { let pic = PocketIc::new(); - // Create a canister and charge it with 100T cycles. - let can_id = pic.create_canister(); - pic.add_cycles(can_id, 100_000_000_000_000); + // Create a canister and charge it with 2T cycles. + let canister_id = pic.create_canister(); + pic.add_cycles(canister_id, 2_000_000_000_000); // Install the test canister wasm file on the canister. - let test_wasm = [...]; - pic.install_canister(can_id, test_wasm, vec![], None); + let test_wasm = todo!(); + pic.install_canister(canister_id, test_wasm, vec![], None); // Submit an update call to the test canister making a canister http outcall // and mock a canister http outcall response. - let arg_bytes = Encode!(&()).unwrap(); let call_id = pic .submit_call( - can_id, + canister_id, Principal::anonymous(), "canister_http", - arg_bytes, + encode_one(()).unwrap(), ) .unwrap(); @@ -283,22 +282,16 @@ fn test_canister_http() { }; pic.mock_canister_http_response(mock_canister_http_response); - // Now the test canister will receive the http outcall response - // and reply to the ingress message from the test driver - // relaying the received http outcall response. - let reply = pic.await_call(call_id).unwrap(); - match reply { - WasmResult::Reply(data) => { - let http_response: Result = - decode_one(&data).unwrap(); - assert_eq!(http_response.unwrap().body, body); - } - WasmResult::Reject(msg) => panic!("Unexpected reject {}", msg), - }; - // There should be no more pending canister http outcalls. let canister_http_requests = pic.get_canister_http(); assert_eq!(canister_http_requests.len(), 0); + + // Now the test canister will receive the http outcall response + // and reply to the ingress message from the test driver. + let reply = pic.await_call(call_id).unwrap(); + let http_response: Result = + decode_one(&reply).unwrap(); + assert_eq!(http_response.unwrap().body, body); } ``` @@ -332,18 +325,12 @@ e.g., 13 for a regular application subnet. // and reply to the ingress message from the test driver // relaying the error. let reply = pic.await_call(call_id).unwrap(); - match reply { - WasmResult::Reply(data) => { - let http_response: Result = - decode_one(&data).unwrap(); - let (reject_code, err) = http_response.unwrap_err(); - assert_eq!(reject_code, RejectionCode::SysTransient); - assert!( - err.contains("No consensus could be reached. Replicas had different responses.") - ); - } - WasmResult::Reject(msg) => panic!("Unexpected reject {}", msg), - }; + let http_response: Result = + decode_one(&reply).unwrap(); + let (reject_code, err) = http_response.unwrap_err(); + assert!(matches!(reject_code, RejectionCode::SysTransient)); + let expected = "No consensus could be reached. Replicas had different responses. Details: request_id: 0, timeout: 1620328930000000005, hashes: [98387cc077af9cff2ef439132854e91cb074035bb76e2afb266960d8e3beaf11: 2], [6a2fa8e54fb4bbe62cde29f7531223d9fcf52c21c03500c1060a5f893ed32d2e: 2], [3e9ec98abf56ef680bebb14309858ede38f6fde771cd4c04cda8f066dc2810db: 2], [2c14e77f18cd990676ae6ce0d7eb89c0af9e1a66e17294b5f0efa68422bba4cb: 2], [2843e4133f673571ff919808d3ca542cc54aaf288c702944e291f0e4fafffc69: 2], [1c4ad84926c36f1fbc634a0dc0535709706f7c48f0c6ebd814fe514022b90671: 2], [7bf80e2f02011ab0a7836b526546e75203b94e856d767c9df4cb0c19baf34059: 1]"; + assert_eq!(err, expected); ``` In the live mode (see the section "Live Mode" for more details), the canister HTTP outcalls are processed @@ -598,9 +585,20 @@ To mine blocks with rewards credited to a given `bitcoin_address: String`, you c .unwrap(); let mut n = 101; // must be more than 100 (Coinbase maturity rule) - btc_rpc - .generate_to_address(n, &Address::from_str(&bitcoin_address).unwrap()) - .unwrap(); + // retry generating blocks until the bitcoind is up and running + let start = std::time::Instant::now(); + loop { + match btc_rpc.generate_to_address(n, &Address::from_str(&bitcoin_address).unwrap()) { + Ok(_) => break, + Err(bitcoincore_rpc::Error::JsonRpc(err)) => { + if start.elapsed() > std::time::Duration::from_secs(30) { + panic!("Timed out when waiting for bitcoind; last error: {}", err); + } + std::thread::sleep(std::time::Duration::from_millis(100)); + } + Err(err) => panic!("Unexpected error when talking to bitcoind: {}", err), + } + } ``` For an example of a test canister that can be deployed to an application subnet of the PocketIC instance, diff --git a/packages/pocket-ic/README.md b/packages/pocket-ic/README.md index 3bc229280c3..e09ef9cc0d2 100644 --- a/packages/pocket-ic/README.md +++ b/packages/pocket-ic/README.md @@ -7,28 +7,43 @@ With PocketIC Rust, testing canisters is as simple as calling Rust functions. Here is a simple example: ```rust -use candid::encode_one; +use candid::{Principal, encode_one}; use pocket_ic::PocketIc; - #[test] - fn test_counter_canister() { +// 2T cycles +const INIT_CYCLES: u128 = 2_000_000_000_000; + +#[test] +fn test_counter_canister() { let pic = PocketIc::new(); - // Create an empty canister as the anonymous principal and add cycles. + + // Create a canister and charge it with 2T cycles. let canister_id = pic.create_canister(); - pic.add_cycles(canister_id, 2_000_000_000_000); - - let wasm_bytes = load_counter_wasm(...); - pic.install_canister(canister_id, wasm_bytes, vec![], None); - // 'inc' is a counter canister method. - call_counter_canister(&pic, canister_id, "inc"); - // Check if it had the desired effect. - let reply = call_counter_canister(&pic, canister_id, "read"); - assert_eq!(reply, WasmResult::Reply(vec![0, 0, 0, 1])); - } - -fn call_counter_canister(pic: &PocketIc, canister_id: CanisterId, method: &str) -> WasmResult { - pic.update_call(canister_id, Principal::anonymous(), method, encode_one(()).unwrap()) - .expect("Failed to call counter canister") + pic.add_cycles(canister_id, INIT_CYCLES); + + // Install the counter canister wasm file on the canister. + let counter_wasm = todo!(); + pic.install_canister(canister_id, counter_wasm, vec![], None); + + // Make some calls to the canister. + let reply = call_counter_can(&pic, canister_id, "read"); + assert_eq!(reply, vec![0, 0, 0, 0]); + let reply = call_counter_can(&pic, canister_id, "write"); + assert_eq!(reply, vec![1, 0, 0, 0]); + let reply = call_counter_can(&pic, canister_id, "write"); + assert_eq!(reply, vec![2, 0, 0, 0]); + let reply = call_counter_can(&pic, canister_id, "read"); + assert_eq!(reply, vec![2, 0, 0, 0]); +} + +fn call_counter_can(pic: &PocketIc, canister_id: Principal, method: &str) -> Vec { + pic.update_call( + canister_id, + Principal::anonymous(), + method, + encode_one(()).unwrap(), + ) + .expect("Failed to call counter canister") } ``` diff --git a/packages/pocket-ic/src/common/rest.rs b/packages/pocket-ic/src/common/rest.rs index b33c5f54a6f..0413b7bb5ba 100644 --- a/packages/pocket-ic/src/common/rest.rs +++ b/packages/pocket-ic/src/common/rest.rs @@ -2,7 +2,7 @@ //! The types in this module are used to serialize and deserialize data //! from and to JSON, and are used by both crates. -use crate::UserError; +use crate::RejectResponse; use candid::Principal; use hex; use reqwest::Response; @@ -123,9 +123,9 @@ pub struct RawMessageId { } #[derive(Clone, Serialize, Deserialize, Debug, JsonSchema)] -pub enum RawSubmitIngressResult { - Ok(RawMessageId), - Err(UserError), +pub struct RawIngressStatusArgs { + pub raw_message_id: RawMessageId, + pub raw_caller: Option, } #[derive(Clone, Serialize, Deserialize, Debug, JsonSchema)] @@ -145,21 +145,30 @@ pub struct RawCanisterCall { #[derive(Clone, Serialize, Deserialize, Debug, JsonSchema)] pub enum RawCanisterResult { - Ok(RawWasmResult), - Err(UserError), -} - -#[derive(Clone, Serialize, Deserialize, Debug, JsonSchema)] -pub enum RawWasmResult { - /// Raw response, returned in a "happy" case - Reply( + Ok( #[serde(deserialize_with = "base64::deserialize")] #[serde(serialize_with = "base64::serialize")] Vec, ), - /// Returned with an error message when the canister decides to reject the - /// message - Reject(String), + Err(RejectResponse), +} + +impl From, RejectResponse>> for RawCanisterResult { + fn from(result: Result, RejectResponse>) -> Self { + match result { + Ok(data) => RawCanisterResult::Ok(data), + Err(reject_response) => RawCanisterResult::Err(reject_response), + } + } +} + +impl From for Result, RejectResponse> { + fn from(result: RawCanisterResult) -> Self { + match result { + RawCanisterResult::Ok(data) => Ok(data), + RawCanisterResult::Err(reject_response) => Err(reject_response), + } + } } #[derive(Clone, Serialize, Deserialize, Debug, JsonSchema)] diff --git a/packages/pocket-ic/src/lib.rs b/packages/pocket-ic/src/lib.rs index e647de89523..d7483a4692e 100644 --- a/packages/pocket-ic/src/lib.rs +++ b/packages/pocket-ic/src/lib.rs @@ -1,39 +1,57 @@ #![allow(clippy::test_attr_in_doctest)] -//! # PocketIC: A Canister Testing Platform -//! -//! PocketIC is the local canister smart contract testing platform for the [Internet Computer](https://internetcomputer.org/). -//! -//! It consists of the PocketIC server, which can run many independent IC instances, and a client library (this crate), which provides an interface to your IC instances. -//! -//! With PocketIC, testing canisters is as simple as calling rust functions. Here is a minimal example: -//! -//! ```rust -//! use candid::{Principal, encode_one}; -//! use pocket_ic::{WasmResult, PocketIc}; -//! -//! #[test] -//! fn test_counter_canister() { -//! let pic = PocketIc::new(); -//! // Create an empty canister as the anonymous principal and add cycles. -//! let canister_id = pic.create_canister(); -//! pic.add_cycles(canister_id, 2_000_000_000_000); -//! -//! let wasm_bytes = todo!(); -//! pic.install_canister(canister_id, wasm_bytes, vec![], None); -//! // 'inc' is a counter canister method. -//! call_counter_canister(&pic, canister_id, "inc"); -//! // Check if it had the desired effect. -//! let reply = call_counter_canister(&pic, canister_id, "read"); -//! assert_eq!(reply, WasmResult::Reply(vec![0, 0, 0, 1])); -//! } -//! -//! fn call_counter_canister(pic: &PocketIc, canister_id: Principal, method: &str) -> WasmResult { -//! pic.update_call(canister_id, Principal::anonymous(), method, encode_one(()).unwrap()) -//! .expect("Failed to call counter canister") -//! } -//! ``` -//! For more information, see the [README](https://crates.io/crates/pocket-ic). -//! +/// # PocketIC: A Canister Testing Platform +/// +/// PocketIC is the local canister smart contract testing platform for the [Internet Computer](https://internetcomputer.org/). +/// +/// It consists of the PocketIC server, which can run many independent IC instances, and a client library (this crate), which provides an interface to your IC instances. +/// +/// With PocketIC, testing canisters is as simple as calling rust functions. Here is a minimal example: +/// +/// ```rust +/// use candid::{Principal, encode_one}; +/// use pocket_ic::PocketIc; +/// +/// // 2T cycles +/// const INIT_CYCLES: u128 = 2_000_000_000_000; +/// +/// // Create a counter canister and charge it with 2T cycles. +/// fn deploy_counter_canister(pic: &PocketIc) -> Principal { +/// let canister_id = pic.create_canister(); +/// pic.add_cycles(canister_id, INIT_CYCLES); +/// let counter_wasm = todo!(); +/// pic.install_canister(canister_id, counter_wasm, vec![], None); +/// canister_id +/// } +/// +/// // Call a method on the counter canister as the anonymous principal. +/// fn call_counter_canister(pic: &PocketIc, canister_id: Principal, method: &str) -> Vec { +/// pic.update_call( +/// canister_id, +/// Principal::anonymous(), +/// method, +/// encode_one(()).unwrap(), +/// ) +/// .expect("Failed to call counter canister") +/// } +/// +/// #[test] +/// fn test_counter_canister() { +/// let pic = PocketIc::new(); +/// let canister_id = deploy_counter_canister(&pic); +/// +/// // Make some calls to the counter canister. +/// let reply = call_counter_canister(&pic, canister_id, "read"); +/// assert_eq!(reply, vec![0, 0, 0, 0]); +/// let reply = call_counter_canister(&pic, canister_id, "write"); +/// assert_eq!(reply, vec![1, 0, 0, 0]); +/// let reply = call_counter_canister(&pic, canister_id, "write"); +/// assert_eq!(reply, vec![2, 0, 0, 0]); +/// let reply = call_counter_canister(&pic, canister_id, "read"); +/// assert_eq!(reply, vec![2, 0, 0, 0]); +/// } +/// ``` +/// For more information, see the [README](https://crates.io/crates/pocket-ic). +/// use crate::{ common::rest::{ BlobCompression, BlobId, CanisterHttpRequest, ExtendedSubnetConfigSet, HttpsConfig, @@ -601,6 +619,13 @@ impl PocketIc { runtime.block_on(async { self.pocket_ic.set_time(time).await }) } + /// Set the current certified time of the IC, on all subnets. + #[instrument(skip(self), fields(instance_id=self.pocket_ic.instance_id, time = ?time))] + pub fn set_certified_time(&self, time: SystemTime) { + let runtime = self.runtime.clone(); + runtime.block_on(async { self.pocket_ic.set_certified_time(time).await }) + } + /// Advance the time on the IC on all subnets by some nanoseconds. #[instrument(skip(self), fields(instance_id=self.pocket_ic.instance_id, duration = ?duration))] pub fn advance_time(&self, duration: Duration) { @@ -637,7 +662,7 @@ impl PocketIc { sender: Principal, method: &str, payload: Vec, - ) -> Result { + ) -> Result { let runtime = self.runtime.clone(); runtime.block_on(async { self.pocket_ic @@ -654,7 +679,7 @@ impl PocketIc { sender: Principal, method: &str, payload: Vec, - ) -> Result { + ) -> Result { let runtime = self.runtime.clone(); runtime.block_on(async { self.pocket_ic @@ -670,26 +695,39 @@ impl PocketIc { } /// Await an update call submitted previously by `submit_call` or `submit_call_with_effective_principal`. - pub fn await_call(&self, message_id: RawMessageId) -> Result { + pub fn await_call(&self, message_id: RawMessageId) -> Result, RejectResponse> { let runtime = self.runtime.clone(); runtime.block_on(async { self.pocket_ic.await_call(message_id).await }) } /// Fetch the status of an update call submitted previously by `submit_call` or `submit_call_with_effective_principal`. /// Note that the status of the update call can only change if the PocketIC instance is in live mode - /// or a round has been executed due to a separate PocketIC library call. + /// or a round has been executed due to a separate PocketIC library call, e.g., `PocketIc::tick()`. pub fn ingress_status( &self, message_id: RawMessageId, - ) -> Option> { + ) -> Option, RejectResponse>> { let runtime = self.runtime.clone(); runtime.block_on(async { self.pocket_ic.ingress_status(message_id).await }) } + /// Fetch the status of an update call submitted previously by `submit_call` or `submit_call_with_effective_principal`. + /// Note that the status of the update call can only change if the PocketIC instance is in live mode + /// or a round has been executed due to a separate PocketIC library call, e.g., `PocketIc::tick()`. + /// If the status of the update call is known, but the update call was submitted by a different caller, then an error is returned. + pub fn ingress_status_as( + &self, + message_id: RawMessageId, + caller: Principal, + ) -> IngressStatusResult { + let runtime = self.runtime.clone(); + runtime.block_on(async { self.pocket_ic.ingress_status_as(message_id, caller).await }) + } + /// Await an update call submitted previously by `submit_call` or `submit_call_with_effective_principal`. - /// This function does not execute rounds and thus should only be called on a "live" PocketIC instance - /// or if rounds are executed due to separate PocketIC library calls. - pub fn await_call_no_ticks(&self, message_id: RawMessageId) -> Result { + /// Note that the status of the update call can only change if the PocketIC instance is in live mode + /// or a round has been executed due to a separate PocketIC library call, e.g., `PocketIc::tick()`. + pub fn await_call_no_ticks(&self, message_id: RawMessageId) -> Result, RejectResponse> { let runtime = self.runtime.clone(); runtime.block_on(async { self.pocket_ic.await_call_no_ticks(message_id).await }) } @@ -702,7 +740,7 @@ impl PocketIc { sender: Principal, method: &str, payload: Vec, - ) -> Result { + ) -> Result, RejectResponse> { let runtime = self.runtime.clone(); runtime.block_on(async { self.pocket_ic @@ -719,7 +757,7 @@ impl PocketIc { sender: Principal, method: &str, payload: Vec, - ) -> Result { + ) -> Result, RejectResponse> { let runtime = self.runtime.clone(); runtime.block_on(async { self.pocket_ic @@ -733,7 +771,7 @@ impl PocketIc { &self, canister_id: CanisterId, sender: Principal, - ) -> Result, CallError> { + ) -> Result, RejectResponse> { let runtime = self.runtime.clone(); runtime.block_on(async { self.pocket_ic @@ -748,7 +786,7 @@ impl PocketIc { &self, canister_id: CanisterId, sender: Option, - ) -> Result { + ) -> Result { let runtime = self.runtime.clone(); runtime.block_on(async { self.pocket_ic.canister_status(canister_id, sender).await }) } @@ -821,7 +859,7 @@ impl PocketIc { canister_id: CanisterId, sender: Option, chunk: Vec, - ) -> Result, CallError> { + ) -> Result, RejectResponse> { let runtime = self.runtime.clone(); runtime.block_on(async { self.pocket_ic @@ -836,7 +874,7 @@ impl PocketIc { &self, canister_id: CanisterId, sender: Option, - ) -> Result>, CallError> { + ) -> Result>, RejectResponse> { let runtime = self.runtime.clone(); runtime.block_on(async { self.pocket_ic.stored_chunks(canister_id, sender).await }) } @@ -847,7 +885,7 @@ impl PocketIc { &self, canister_id: CanisterId, sender: Option, - ) -> Result<(), CallError> { + ) -> Result<(), RejectResponse> { let runtime = self.runtime.clone(); runtime.block_on(async { self.pocket_ic.clear_chunk_store(canister_id, sender).await }) } @@ -863,7 +901,7 @@ impl PocketIc { chunk_hashes_list: Vec>, wasm_module_hash: Vec, arg: Vec, - ) -> Result<(), CallError> { + ) -> Result<(), RejectResponse> { let runtime = self.runtime.clone(); runtime.block_on(async { self.pocket_ic @@ -905,7 +943,7 @@ impl PocketIc { wasm_module: Vec, arg: Vec, sender: Option, - ) -> Result<(), CallError> { + ) -> Result<(), RejectResponse> { let runtime = self.runtime.clone(); runtime.block_on(async { self.pocket_ic @@ -922,7 +960,7 @@ impl PocketIc { wasm_module: Vec, arg: Vec, sender: Option, - ) -> Result<(), CallError> { + ) -> Result<(), RejectResponse> { let runtime = self.runtime.clone(); runtime.block_on(async { self.pocket_ic @@ -937,7 +975,7 @@ impl PocketIc { &self, canister_id: CanisterId, sender: Option, - ) -> Result<(), CallError> { + ) -> Result<(), RejectResponse> { let runtime = self.runtime.clone(); runtime.block_on(async { self.pocket_ic.uninstall_canister(canister_id, sender).await }) } @@ -949,7 +987,7 @@ impl PocketIc { canister_id: CanisterId, sender: Option, replace_snapshot: Option>, - ) -> Result { + ) -> Result { let runtime = self.runtime.clone(); runtime.block_on(async { self.pocket_ic @@ -965,7 +1003,7 @@ impl PocketIc { canister_id: CanisterId, sender: Option, snapshot_id: Vec, - ) -> Result<(), CallError> { + ) -> Result<(), RejectResponse> { let runtime = self.runtime.clone(); runtime.block_on(async { self.pocket_ic @@ -980,7 +1018,7 @@ impl PocketIc { &self, canister_id: CanisterId, sender: Option, - ) -> Result, CallError> { + ) -> Result, RejectResponse> { let runtime = self.runtime.clone(); runtime.block_on(async { self.pocket_ic @@ -996,7 +1034,7 @@ impl PocketIc { canister_id: CanisterId, sender: Option, snapshot_id: Vec, - ) -> Result<(), CallError> { + ) -> Result<(), RejectResponse> { let runtime = self.runtime.clone(); runtime.block_on(async { self.pocket_ic @@ -1012,7 +1050,7 @@ impl PocketIc { canister_id: CanisterId, sender: Option, settings: CanisterSettings, - ) -> Result<(), CallError> { + ) -> Result<(), RejectResponse> { let runtime = self.runtime.clone(); runtime.block_on(async { self.pocket_ic @@ -1028,7 +1066,7 @@ impl PocketIc { canister_id: CanisterId, sender: Option, new_controllers: Vec, - ) -> Result<(), CallError> { + ) -> Result<(), RejectResponse> { let runtime = self.runtime.clone(); runtime.block_on(async { self.pocket_ic @@ -1043,7 +1081,7 @@ impl PocketIc { &self, canister_id: CanisterId, sender: Option, - ) -> Result<(), CallError> { + ) -> Result<(), RejectResponse> { let runtime = self.runtime.clone(); runtime.block_on(async { self.pocket_ic.start_canister(canister_id, sender).await }) } @@ -1054,7 +1092,7 @@ impl PocketIc { &self, canister_id: CanisterId, sender: Option, - ) -> Result<(), CallError> { + ) -> Result<(), RejectResponse> { let runtime = self.runtime.clone(); runtime.block_on(async { self.pocket_ic.stop_canister(canister_id, sender).await }) } @@ -1065,7 +1103,7 @@ impl PocketIc { &self, canister_id: CanisterId, sender: Option, - ) -> Result<(), CallError> { + ) -> Result<(), RejectResponse> { let runtime = self.runtime.clone(); runtime.block_on(async { self.pocket_ic.delete_canister(canister_id, sender).await }) } @@ -1098,7 +1136,7 @@ impl PocketIc { sender: Principal, method: &str, payload: Vec, - ) -> Result { + ) -> Result, RejectResponse> { let runtime = self.runtime.clone(); runtime.block_on(async { self.pocket_ic @@ -1124,7 +1162,7 @@ impl PocketIc { sender: Principal, method: &str, payload: Vec, - ) -> Result { + ) -> Result, RejectResponse> { let runtime = self.runtime.clone(); runtime.block_on(async { self.pocket_ic @@ -1198,7 +1236,7 @@ pub fn call_candid_as( sender: Principal, method: &str, input: Input, -) -> Result +) -> Result where Input: ArgumentEncoder, Output: for<'a> ArgumentDecoder<'a>, @@ -1222,7 +1260,7 @@ pub fn call_candid( effective_principal: RawEffectivePrincipal, method: &str, input: Input, -) -> Result +) -> Result where Input: ArgumentEncoder, Output: for<'a> ArgumentDecoder<'a>, @@ -1243,7 +1281,7 @@ pub fn query_candid( canister_id: CanisterId, method: &str, input: Input, -) -> Result +) -> Result where Input: ArgumentEncoder, Output: for<'a> ArgumentDecoder<'a>, @@ -1259,7 +1297,7 @@ pub fn query_candid_as( sender: Principal, method: &str, input: Input, -) -> Result +) -> Result where Input: ArgumentEncoder, Output: for<'a> ArgumentDecoder<'a>, @@ -1275,7 +1313,7 @@ pub fn update_candid( canister_id: CanisterId, method: &str, input: Input, -) -> Result +) -> Result where Input: ArgumentEncoder, Output: for<'a> ArgumentDecoder<'a>, @@ -1291,7 +1329,7 @@ pub fn update_candid_as( sender: Principal, method: &str, input: Input, -) -> Result +) -> Result where Input: ArgumentEncoder, Output: for<'a> ArgumentDecoder<'a>, @@ -1305,15 +1343,15 @@ where /// [`query_candid`]. pub fn with_candid( input: Input, - f: impl FnOnce(Vec) -> Result, -) -> Result + f: impl FnOnce(Vec) -> Result, RejectResponse>, +) -> Result where Input: ArgumentEncoder, Output: for<'a> ArgumentDecoder<'a>, { let in_bytes = encode_args(input).expect("failed to encode args"); - match f(in_bytes) { - Ok(WasmResult::Reply(out_bytes)) => Ok(decode_args(&out_bytes).unwrap_or_else(|e| { + f(in_bytes).map(|out_bytes| { + decode_args(&out_bytes).unwrap_or_else(|e| { panic!( "Failed to decode response as candid type {}:\nerror: {}\nbytes: {:?}\nutf8: {}", std::any::type_name::(), @@ -1321,10 +1359,8 @@ where out_bytes, String::from_utf8_lossy(&out_bytes), ) - })), - Ok(WasmResult::Reject(message)) => Err(CallError::Reject(message)), - Err(user_error) => Err(CallError::UserError(user_error)), - } + }) + }) } /// Error type for [`TryFrom`]. @@ -1498,42 +1534,74 @@ impl std::fmt::Display for ErrorCode { } } -/// The error that is sent back to users from the IC if something goes -/// wrong. It's designed to be copyable and serializable so that we -/// can persist it in the ingress history. +/// User-facing reject codes. +/// +/// They can be derived from the most significant digit of the +/// corresponding error code. #[derive( - PartialOrd, Ord, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema, + PartialOrd, + Ord, + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + Serialize, + Deserialize, + JsonSchema, + EnumIter, )] -pub struct UserError { - /// The error code. - pub code: ErrorCode, - /// A human-readable description of the error. - pub description: String, +pub enum RejectCode { + SysFatal = 1, + SysTransient = 2, + DestinationInvalid = 3, + CanisterReject = 4, + CanisterError = 5, + SysUnknown = 6, } -impl std::fmt::Display for UserError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - // E.g. "IC0301: Canister 42 not found" - write!(f, "{}: {}", self.code, self.description) +impl TryFrom for RejectCode { + type Error = TryFromError; + fn try_from(err: u64) -> Result { + match err { + 1 => Ok(RejectCode::SysFatal), + 2 => Ok(RejectCode::SysTransient), + 3 => Ok(RejectCode::DestinationInvalid), + 4 => Ok(RejectCode::CanisterReject), + 5 => Ok(RejectCode::CanisterError), + 6 => Ok(RejectCode::SysUnknown), + _ => Err(TryFromError::ValueOutOfRange(err)), + } } } -/// This enum describes the different error types when invoking a canister. -#[derive(Debug, Serialize, Deserialize)] -pub enum CallError { - Reject(String), - UserError(UserError), +/// User-facing type describing an unsuccessful (also called reject) call response. +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +pub struct RejectResponse { + pub reject_code: RejectCode, + pub reject_message: String, + pub error_code: ErrorCode, + pub certified: bool, } -/// This struct describes the different types that executing a WASM function in -/// a canister can produce. -#[derive(PartialOrd, Ord, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub enum WasmResult { - /// Raw response, returned in a successful case. - Reply(#[serde(with = "serde_bytes")] Vec), - /// Returned with an error message when the canister decides to reject the - /// message. - Reject(String), +impl std::fmt::Display for RejectResponse { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + // Follows [agent-rs](https://github.com/dfinity/agent-rs/blob/a651dbbe69e61d4e8508c144cd60cfa3118eeb3a/ic-agent/src/agent/agent_error.rs#L54) + write!(f, "PocketIC returned a rejection error: reject code {:?}, reject message {}, error code {:?}", self.reject_code, self.reject_message, self.error_code) + } +} + +/// This enum describes the result of retrieving ingress status. +/// The `IngressStatusResult::Forbidden` variant is produced +/// if an optional caller is provided and a corresponding read state request +/// for the status of the same update call signed by that specified caller +/// was rejected because the update call was submitted by a different caller. +#[derive(Debug, Serialize, Deserialize)] +pub enum IngressStatusResult { + NotAvailable, + Forbidden(String), + Success(Result, RejectResponse>), } #[cfg(windows)] @@ -1624,6 +1692,9 @@ To download the binary, please visit https://github.com/dfinity/pocketic." cmd.stderr(std::process::Stdio::null()); } } + + // TODO: SDK-1936 + #[allow(clippy::zombie_processes)] cmd.spawn() .unwrap_or_else(|_| panic!("Failed to start PocketIC binary ({:?})", bin_path)); @@ -1678,9 +1749,18 @@ pub fn get_default_effective_canister_id( #[cfg(test)] mod test { - use crate::ErrorCode; + use crate::{ErrorCode, RejectCode}; use strum::IntoEnumIterator; + #[test] + fn reject_code_round_trip() { + for initial in RejectCode::iter() { + let round_trip = RejectCode::try_from(initial as u64).unwrap(); + + assert_eq!(initial, round_trip); + } + } + #[test] fn error_code_round_trip() { for initial in ErrorCode::iter() { @@ -1690,6 +1770,21 @@ mod test { } } + #[test] + fn reject_code_matches_ic_error_code() { + assert_eq!( + RejectCode::iter().len(), + ic_error_types::RejectCode::iter().len() + ); + for ic_reject_code in ic_error_types::RejectCode::iter() { + let reject_code: RejectCode = (ic_reject_code as u64).try_into().unwrap(); + assert_eq!( + format!("{:?}", reject_code), + format!("{:?}", ic_reject_code) + ); + } + } + #[test] fn error_code_matches_ic_error_code() { assert_eq!( diff --git a/packages/pocket-ic/src/management_canister.rs b/packages/pocket-ic/src/management_canister.rs index 1878e6728e2..e76660c5237 100644 --- a/packages/pocket-ic/src/management_canister.rs +++ b/packages/pocket-ic/src/management_canister.rs @@ -1,6 +1,7 @@ use candid::{CandidType, Deserialize, Principal}; pub type CanisterId = Principal; +pub type SubnetId = Principal; #[derive(CandidType, Deserialize, Debug, Clone)] pub struct CanisterIdRecord { @@ -234,6 +235,11 @@ pub struct CanisterInfoArgs { pub num_requested_changes: Option, } +#[derive(CandidType, Deserialize, Debug, Clone)] +pub struct SubnetInfoArgs { + pub subnet_id: SubnetId, +} + #[derive(CandidType, Deserialize, Debug, Clone)] pub enum ChangeOrigin { #[serde(rename = "from_user")] @@ -292,6 +298,11 @@ pub struct CanisterInfoResult { pub total_num_changes: u64, } +#[derive(CandidType, Deserialize, Debug, Clone)] +pub struct SubnetInfoResult { + pub replica_version: String, +} + // raw randomness pub type RawRandResult = Vec; @@ -455,6 +466,18 @@ pub struct SignWithSchnorrArgs { pub key_id: SignWithSchnorrArgsKeyId, pub derivation_path: Vec>, pub message: Vec, + pub aux: Option, +} + +#[derive(CandidType, Deserialize, Debug, Clone)] +pub enum SignWithSchnorrAux { + #[serde(rename = "bip341")] + Bip341(SignWithBip341Aux), +} + +#[derive(CandidType, Deserialize, Debug, Clone)] +pub struct SignWithBip341Aux { + pub merkle_root_hash: Vec, } #[derive(CandidType, Deserialize, Debug, Clone)] diff --git a/packages/pocket-ic/src/nonblocking.rs b/packages/pocket-ic/src/nonblocking.rs index 9f7fbcdb13e..763574b4651 100644 --- a/packages/pocket-ic/src/nonblocking.rs +++ b/packages/pocket-ic/src/nonblocking.rs @@ -3,9 +3,9 @@ use crate::common::rest::{ CreateHttpGatewayResponse, CreateInstanceResponse, ExtendedSubnetConfigSet, HttpGatewayBackend, HttpGatewayConfig, HttpGatewayInfo, HttpsConfig, InstanceConfig, InstanceId, MockCanisterHttpResponse, RawAddCycles, RawCanisterCall, RawCanisterHttpRequest, RawCanisterId, - RawCanisterResult, RawCycles, RawEffectivePrincipal, RawMessageId, RawMockCanisterHttpResponse, - RawPrincipalId, RawSetStableMemory, RawStableMemory, RawSubmitIngressResult, RawSubnetId, - RawTime, RawVerifyCanisterSigArg, RawWasmResult, SubnetId, Topology, + RawCanisterResult, RawCycles, RawEffectivePrincipal, RawIngressStatusArgs, RawMessageId, + RawMockCanisterHttpResponse, RawPrincipalId, RawSetStableMemory, RawStableMemory, RawSubnetId, + RawTime, RawVerifyCanisterSigArg, SubnetId, Topology, }; use crate::management_canister::{ CanisterId, CanisterIdRecord, CanisterInstallMode, CanisterInstallModeUpgradeInner, @@ -16,7 +16,7 @@ use crate::management_canister::{ TakeCanisterSnapshotArgs, UpdateSettingsArgs, UploadChunkArgs, UploadChunkResult, }; pub use crate::DefaultEffectiveCanisterIdError; -use crate::{CallError, PocketIcBuilder, UserError, WasmResult}; +use crate::{IngressStatusResult, PocketIcBuilder, RejectResponse}; use backoff::backoff::Backoff; use backoff::{ExponentialBackoff, ExponentialBackoffBuilder}; use candid::{ @@ -302,7 +302,7 @@ impl PocketIc { #[instrument(skip(self), fields(instance_id=self.instance_id))] pub async fn auto_progress(&self) -> Url { let now = std::time::SystemTime::now(); - self.set_time(now).await; + self.set_certified_time(now).await; let endpoint = "auto_progress"; let auto_progress_config = AutoProgressConfig { artificial_delay_ms: None, @@ -485,6 +485,22 @@ impl PocketIc { .await; } + /// Set the current certified time of the IC, on all subnets. + #[instrument(skip(self), fields(instance_id=self.instance_id, time = ?time))] + pub async fn set_certified_time(&self, time: SystemTime) { + let endpoint = "update/set_certified_time"; + self.post::<(), _>( + endpoint, + RawTime { + nanos_since_epoch: time + .duration_since(SystemTime::UNIX_EPOCH) + .expect("Time went backwards") + .as_nanos() as u64, + }, + ) + .await; + } + /// Advance the time on the IC on all subnets by some nanoseconds. #[instrument(skip(self), fields(instance_id=self.instance_id, duration = ?duration))] pub async fn advance_time(&self, duration: Duration) { @@ -546,7 +562,7 @@ impl PocketIc { sender: Principal, method: &str, payload: Vec, - ) -> Result { + ) -> Result { self.submit_call_with_effective_principal( canister_id, RawEffectivePrincipal::CanisterId(canister_id.as_slice().to_vec()), @@ -565,7 +581,7 @@ impl PocketIc { sender: Principal, method: &str, payload: Vec, - ) -> Result { + ) -> Result { let endpoint = "update/submit_ingress_message"; let raw_canister_call = RawCanisterCall { sender: sender.as_slice().to_vec(), @@ -574,51 +590,76 @@ impl PocketIc { payload, effective_principal, }; - let res: RawSubmitIngressResult = self.post(endpoint, raw_canister_call).await; - match res { - RawSubmitIngressResult::Ok(message_id) => Ok(message_id), - RawSubmitIngressResult::Err(user_error) => Err(user_error), - } + self.post(endpoint, raw_canister_call).await } /// Await an update call submitted previously by `submit_call` or `submit_call_with_effective_principal`. - pub async fn await_call(&self, message_id: RawMessageId) -> Result { + pub async fn await_call(&self, message_id: RawMessageId) -> Result, RejectResponse> { let endpoint = "update/await_ingress_message"; let result: RawCanisterResult = self.post(endpoint, message_id).await; - match result { - RawCanisterResult::Ok(raw_wasm_result) => match raw_wasm_result { - RawWasmResult::Reply(data) => Ok(WasmResult::Reply(data)), - RawWasmResult::Reject(text) => Ok(WasmResult::Reject(text)), - }, - RawCanisterResult::Err(user_error) => Err(user_error), - } + result.into() } /// Fetch the status of an update call submitted previously by `submit_call` or `submit_call_with_effective_principal`. /// Note that the status of the update call can only change if the PocketIC instance is in live mode - /// or a round has been executed due to a separate PocketIC library call. + /// or a round has been executed due to a separate PocketIC library call, e.g., `PocketIc::tick()`. pub async fn ingress_status( &self, - message_id: RawMessageId, - ) -> Option> { + raw_message_id: RawMessageId, + ) -> Option, RejectResponse>> { + let status = self.ingress_status_as_caller(raw_message_id, None).await; + match status { + IngressStatusResult::NotAvailable => None, + IngressStatusResult::Success(status) => Some(status), + IngressStatusResult::Forbidden(err) => panic!( + "Retrieving ingress status was forbidden: {}. This is a bug!", + err + ), + } + } + + /// Fetch the status of an update call submitted previously by `submit_call` or `submit_call_with_effective_principal`. + /// Note that the status of the update call can only change if the PocketIC instance is in live mode + /// or a round has been executed due to a separate PocketIC library call, e.g., `PocketIc::tick()`. + /// If the status of the update call is known, but the update call was submitted by a different caller, then an error is returned. + pub async fn ingress_status_as( + &self, + raw_message_id: RawMessageId, + caller: Principal, + ) -> IngressStatusResult { + self.ingress_status_as_caller(raw_message_id, Some(caller)) + .await + } + + async fn ingress_status_as_caller( + &self, + raw_message_id: RawMessageId, + caller: Option, + ) -> IngressStatusResult { let endpoint = "read/ingress_status"; - let result: Option = self.post(endpoint, message_id).await; - result.map(|result| match result { - RawCanisterResult::Ok(raw_wasm_result) => match raw_wasm_result { - RawWasmResult::Reply(data) => Ok(WasmResult::Reply(data)), - RawWasmResult::Reject(text) => Ok(WasmResult::Reject(text)), - }, - RawCanisterResult::Err(user_error) => Err(user_error), - }) + let raw_ingress_status_args = RawIngressStatusArgs { + raw_message_id, + raw_caller: caller.map(|caller| caller.into()), + }; + let result: Result, (StatusCode, String)> = + self.try_post(endpoint, raw_ingress_status_args).await; + match result { + Ok(None) => IngressStatusResult::NotAvailable, + Ok(Some(result)) => IngressStatusResult::Success(result.into()), + Err((status, message)) => { + assert_eq!(status, StatusCode::FORBIDDEN, "HTTP error code {} for /read/ingress_status is not StatusCode::FORBIDDEN. This is a bug!", status); + IngressStatusResult::Forbidden(message) + } + } } /// Await an update call submitted previously by `submit_call` or `submit_call_with_effective_principal`. - /// This function does not execute rounds and thus should only be called on a "live" PocketIC instance - /// or if rounds are executed due to separate PocketIC library calls. + /// Note that the status of the update call can only change if the PocketIC instance is in live mode + /// or a round has been executed due to a separate PocketIC library call. pub async fn await_call_no_ticks( &self, message_id: RawMessageId, - ) -> Result { + ) -> Result, RejectResponse> { let mut retry_policy: ExponentialBackoff = ExponentialBackoffBuilder::new() .with_initial_interval(Duration::from_millis(10)) .with_max_interval(Duration::from_secs(1)) @@ -640,7 +681,7 @@ impl PocketIc { sender: Principal, method: &str, payload: Vec, - ) -> Result { + ) -> Result, RejectResponse> { self.update_call_with_effective_principal( canister_id, RawEffectivePrincipal::CanisterId(canister_id.as_slice().to_vec()), @@ -659,7 +700,7 @@ impl PocketIc { sender: Principal, method: &str, payload: Vec, - ) -> Result { + ) -> Result, RejectResponse> { self.query_call_with_effective_principal( canister_id, RawEffectivePrincipal::CanisterId(canister_id.as_slice().to_vec()), @@ -681,7 +722,7 @@ impl PocketIc { sender: Principal, method: &str, payload: Vec, - ) -> Result { + ) -> Result, RejectResponse> { let endpoint = "read/query"; self.canister_call( endpoint, @@ -699,7 +740,7 @@ impl PocketIc { &self, canister_id: CanisterId, sender: Principal, - ) -> Result, CallError> { + ) -> Result, RejectResponse> { with_candid::<_, (FetchCanisterLogsResult,), _>( (CanisterIdRecord { canister_id },), |payload| async { @@ -723,7 +764,7 @@ impl PocketIc { &self, canister_id: CanisterId, sender: Option, - ) -> Result { + ) -> Result { call_candid_as::<(CanisterIdRecord,), (CanisterStatusResult,)>( self, Principal::management_canister(), @@ -856,7 +897,7 @@ impl PocketIc { canister_id: CanisterId, sender: Option, chunk: Vec, - ) -> Result, CallError> { + ) -> Result, RejectResponse> { call_candid_as::<_, (UploadChunkResult,)>( self, Principal::management_canister(), @@ -875,7 +916,7 @@ impl PocketIc { &self, canister_id: CanisterId, sender: Option, - ) -> Result>, CallError> { + ) -> Result>, RejectResponse> { call_candid_as::<_, (StoredChunksResult,)>( self, Principal::management_canister(), @@ -894,7 +935,7 @@ impl PocketIc { &self, canister_id: CanisterId, sender: Option, - ) -> Result<(), CallError> { + ) -> Result<(), RejectResponse> { call_candid_as( self, Principal::management_canister(), @@ -917,7 +958,7 @@ impl PocketIc { chunk_hashes_list: Vec>, wasm_module_hash: Vec, arg: Vec, - ) -> Result<(), CallError> { + ) -> Result<(), RejectResponse> { call_candid_as( self, Principal::management_canister(), @@ -947,7 +988,7 @@ impl PocketIc { wasm_module: Vec, arg: Vec, sender: Option, - ) -> Result<(), CallError> { + ) -> Result<(), RejectResponse> { if wasm_module.len() + arg.len() < INSTALL_CHUNKED_CODE_THRESHOLD { call_candid_as::<(InstallCodeArgs,), ()>( self, @@ -1019,7 +1060,7 @@ impl PocketIc { wasm_module: Vec, arg: Vec, sender: Option, - ) -> Result<(), CallError> { + ) -> Result<(), RejectResponse> { self.install_canister_helper( CanisterInstallMode::Upgrade(Some(CanisterInstallModeUpgradeInner { wasm_memory_persistence: Some( @@ -1043,7 +1084,7 @@ impl PocketIc { wasm_module: Vec, arg: Vec, sender: Option, - ) -> Result<(), CallError> { + ) -> Result<(), RejectResponse> { self.install_canister_helper( CanisterInstallMode::Reinstall, canister_id, @@ -1060,7 +1101,7 @@ impl PocketIc { &self, canister_id: CanisterId, sender: Option, - ) -> Result<(), CallError> { + ) -> Result<(), RejectResponse> { call_candid_as::<(CanisterIdRecord,), ()>( self, Principal::management_canister(), @@ -1079,7 +1120,7 @@ impl PocketIc { canister_id: CanisterId, sender: Option, replace_snapshot: Option>, - ) -> Result { + ) -> Result { call_candid_as::<_, (Snapshot,)>( self, Principal::management_canister(), @@ -1102,7 +1143,7 @@ impl PocketIc { canister_id: CanisterId, sender: Option, snapshot_id: Vec, - ) -> Result<(), CallError> { + ) -> Result<(), RejectResponse> { call_candid_as( self, Principal::management_canister(), @@ -1124,7 +1165,7 @@ impl PocketIc { &self, canister_id: CanisterId, sender: Option, - ) -> Result, CallError> { + ) -> Result, RejectResponse> { call_candid_as::<_, (Vec,)>( self, Principal::management_canister(), @@ -1144,7 +1185,7 @@ impl PocketIc { canister_id: CanisterId, sender: Option, snapshot_id: Vec, - ) -> Result<(), CallError> { + ) -> Result<(), RejectResponse> { call_candid_as( self, Principal::management_canister(), @@ -1166,7 +1207,7 @@ impl PocketIc { canister_id: CanisterId, sender: Option, settings: CanisterSettings, - ) -> Result<(), CallError> { + ) -> Result<(), RejectResponse> { call_candid_as::<_, ()>( self, Principal::management_canister(), @@ -1189,7 +1230,7 @@ impl PocketIc { canister_id: CanisterId, sender: Option, new_controllers: Vec, - ) -> Result<(), CallError> { + ) -> Result<(), RejectResponse> { let settings = CanisterSettings { controllers: Some(new_controllers), ..CanisterSettings::default() @@ -1215,7 +1256,7 @@ impl PocketIc { &self, canister_id: CanisterId, sender: Option, - ) -> Result<(), CallError> { + ) -> Result<(), RejectResponse> { call_candid_as::<(CanisterIdRecord,), ()>( self, Principal::management_canister(), @@ -1233,7 +1274,7 @@ impl PocketIc { &self, canister_id: CanisterId, sender: Option, - ) -> Result<(), CallError> { + ) -> Result<(), RejectResponse> { call_candid_as::<(CanisterIdRecord,), ()>( self, Principal::management_canister(), @@ -1251,7 +1292,7 @@ impl PocketIc { &self, canister_id: CanisterId, sender: Option, - ) -> Result<(), CallError> { + ) -> Result<(), RejectResponse> { call_candid_as::<(CanisterIdRecord,), ()>( self, Principal::management_canister(), @@ -1361,12 +1402,29 @@ impl PocketIc { self.request(HttpMethod::Post, endpoint, body).await } + async fn try_post( + &self, + endpoint: &str, + body: B, + ) -> Result { + self.try_request(HttpMethod::Post, endpoint, body).await + } + async fn request( &self, http_method: HttpMethod, endpoint: &str, body: B, ) -> T { + self.try_request(http_method, endpoint, body).await.unwrap() + } + + async fn try_request( + &self, + http_method: HttpMethod, + endpoint: &str, + body: B, + ) -> Result { // we may have to try several times if the instance is busy let start = std::time::SystemTime::now(); loop { @@ -1377,9 +1435,10 @@ impl PocketIc { HttpMethod::Post => reqwest_client.post(url).json(&body), }; let result = builder.send().await.expect("HTTP failure"); + let status = result.status(); match ApiResponse::<_>::from_response(result).await { - ApiResponse::Success(t) => break t, - ApiResponse::Error { message } => panic!("{}", message), + ApiResponse::Success(t) => break Ok(t), + ApiResponse::Error { message } => break Err((status, message)), ApiResponse::Busy { state_label, op_id } => { debug!( "instance_id={} Instance is busy (with a different computation): state_label: {}, op_id: {}", @@ -1404,24 +1463,31 @@ impl PocketIc { .send() .await .expect("HTTP failure"); - match ApiResponse::<_>::from_response(result).await { - ApiResponse::Error { message } => { - debug!("Polling has not succeeded yet: {}", message) - } - ApiResponse::Success(t) => { - return t; - } - ApiResponse::Started { state_label, op_id } => { - warn!( - "instance_id={} unexpected Started({} {})", - self.instance_id, state_label, op_id - ); - } - ApiResponse::Busy { state_label, op_id } => { - warn!( - "instance_id={} unexpected Busy({} {})", - self.instance_id, state_label, op_id - ); + if result.status() == reqwest::StatusCode::NOT_FOUND { + let message = + String::from_utf8(result.bytes().await.unwrap().to_vec()).unwrap(); + debug!("Polling has not succeeded yet: {}", message); + } else { + let status = result.status(); + match ApiResponse::<_>::from_response(result).await { + ApiResponse::Error { message } => { + return Err((status, message)); + } + ApiResponse::Success(t) => { + return Ok(t); + } + ApiResponse::Started { state_label, op_id } => { + warn!( + "instance_id={} unexpected Started({} {})", + self.instance_id, state_label, op_id + ); + } + ApiResponse::Busy { state_label, op_id } => { + warn!( + "instance_id={} unexpected Busy({} {})", + self.instance_id, state_label, op_id + ); + } } } if let Some(max_request_time_ms) = self.max_request_time_ms { @@ -1450,7 +1516,7 @@ impl PocketIc { sender: Principal, method: &str, payload: Vec, - ) -> Result { + ) -> Result, RejectResponse> { let raw_canister_call = RawCanisterCall { sender: sender.as_slice().to_vec(), canister_id: canister_id.as_slice().to_vec(), @@ -1460,13 +1526,7 @@ impl PocketIc { }; let result: RawCanisterResult = self.post(endpoint, raw_canister_call).await; - match result { - RawCanisterResult::Ok(raw_wasm_result) => match raw_wasm_result { - RawWasmResult::Reply(data) => Ok(WasmResult::Reply(data)), - RawWasmResult::Reject(text) => Ok(WasmResult::Reject(text)), - }, - RawCanisterResult::Err(user_error) => Err(user_error), - } + result.into() } pub(crate) async fn update_call_with_effective_principal( @@ -1476,7 +1536,7 @@ impl PocketIc { sender: Principal, method: &str, payload: Vec, - ) -> Result { + ) -> Result, RejectResponse> { let message_id = self .submit_call_with_effective_principal( canister_id, @@ -1530,7 +1590,7 @@ pub async fn call_candid_as( sender: Principal, method: &str, input: Input, -) -> Result +) -> Result where Input: ArgumentEncoder, Output: for<'a> ArgumentDecoder<'a>, @@ -1556,7 +1616,7 @@ pub async fn call_candid( effective_principal: RawEffectivePrincipal, method: &str, input: Input, -) -> Result +) -> Result where Input: ArgumentEncoder, Output: for<'a> ArgumentDecoder<'a>, @@ -1578,7 +1638,7 @@ pub async fn query_candid( canister_id: CanisterId, method: &str, input: Input, -) -> Result +) -> Result where Input: ArgumentEncoder, Output: for<'a> ArgumentDecoder<'a>, @@ -1594,7 +1654,7 @@ pub async fn query_candid_as( sender: Principal, method: &str, input: Input, -) -> Result +) -> Result where Input: ArgumentEncoder, Output: for<'a> ArgumentDecoder<'a>, @@ -1611,7 +1671,7 @@ pub async fn update_candid( canister_id: CanisterId, method: &str, input: Input, -) -> Result +) -> Result where Input: ArgumentEncoder, Output: for<'a> ArgumentDecoder<'a>, @@ -1627,7 +1687,7 @@ pub async fn update_candid_as( sender: Principal, method: &str, input: Input, -) -> Result +) -> Result where Input: ArgumentEncoder, Output: for<'a> ArgumentDecoder<'a>, @@ -1643,15 +1703,15 @@ where pub async fn with_candid( input: Input, f: impl FnOnce(Vec) -> Fut, -) -> Result +) -> Result where Input: ArgumentEncoder, Output: for<'a> ArgumentDecoder<'a>, - Fut: Future>, + Fut: Future, RejectResponse>>, { let in_bytes = encode_args(input).expect("failed to encode args"); - match f(in_bytes).await { - Ok(WasmResult::Reply(out_bytes)) => Ok(decode_args(&out_bytes).unwrap_or_else(|e| { + f(in_bytes).await.map(|out_bytes| { + decode_args(&out_bytes).unwrap_or_else(|e| { panic!( "Failed to decode response as candid type {}:\nerror: {}\nbytes: {:?}\nutf8: {}", std::any::type_name::(), @@ -1659,10 +1719,8 @@ where out_bytes, String::from_utf8_lossy(&out_bytes), ) - })), - Ok(WasmResult::Reject(message)) => Err(CallError::Reject(message)), - Err(user_error) => Err(CallError::UserError(user_error)), - } + }) + }) } fn setup_tracing(pid: u32) -> Option { diff --git a/packages/pocket-ic/test_canister/canister.did b/packages/pocket-ic/test_canister/canister.did index 242f3de8c78..f0e2766bcc7 100644 --- a/packages/pocket-ic/test_canister/canister.did +++ b/packages/pocket-ic/test_canister/canister.did @@ -114,4 +114,8 @@ service : { execute_many_instructions : (nat64) -> (); canister_log : (text) -> (); time : () -> (nat64) query; + reject_query : () -> () query; + reject_update : () -> (); + trap_query : () -> () query; + trap_update : () -> (); } diff --git a/packages/pocket-ic/test_canister/src/canister.rs b/packages/pocket-ic/test_canister/src/canister.rs index 53cd4ffad6c..239803ea47b 100644 --- a/packages/pocket-ic/test_canister/src/canister.rs +++ b/packages/pocket-ic/test_canister/src/canister.rs @@ -1,5 +1,5 @@ use candid::{define_function, CandidType, Principal}; -use ic_cdk::api::call::RejectionCode; +use ic_cdk::api::call::{accept_message, arg_data_raw, reject, RejectionCode}; use ic_cdk::api::instruction_counter; use ic_cdk::api::management_canister::ecdsa::{ ecdsa_public_key as ic_cdk_ecdsa_public_key, sign_with_ecdsa as ic_cdk_sign_with_ecdsa, @@ -9,7 +9,7 @@ use ic_cdk::api::management_canister::http_request::{ http_request as canister_http_outcall, CanisterHttpRequestArgument, HttpMethod, HttpResponse, TransformArgs, TransformContext, TransformFunc, }; -use ic_cdk::{query, update}; +use ic_cdk::{inspect_message, query, trap, update}; use serde::{Deserialize, Serialize}; use serde_bytes::ByteBuf; @@ -109,6 +109,18 @@ struct SignWithSchnorrArgument { pub message: Vec, pub derivation_path: Vec>, pub key_id: SchnorrKeyId, + pub aux: Option, +} + +#[derive(CandidType, Serialize, Deserialize, Debug)] +pub enum SignWithSchnorrAux { + #[serde(rename = "bip341")] + Bip341(SignWithBip341Aux), +} + +#[derive(CandidType, Serialize, Deserialize, Debug)] +pub struct SignWithBip341Aux { + pub merkle_root_hash: ByteBuf, } #[derive(CandidType, Deserialize, Debug)] @@ -144,11 +156,13 @@ async fn sign_with_schnorr( message: Vec, derivation_path: Vec>, key_id: SchnorrKeyId, + aux: Option, ) -> Result, String> { let internal_request = SignWithSchnorrArgument { message, derivation_path, key_id, + aux, }; let (internal_reply,): (SignWithSchnorrResponse,) = ic_cdk::api::call::call_with_payment( @@ -300,4 +314,37 @@ fn time() -> u64 { ic_cdk::api::time() } +// reject responses + +#[inspect_message] +fn inspect_message() { + let arg_data = arg_data_raw(); + if arg_data == b"trap" { + trap("trap in inspect message"); + } else if arg_data == b"skip" { + } else { + accept_message(); + } +} + +#[query(manual_reply = true)] +fn reject_query() { + reject("reject in query method"); +} + +#[update(manual_reply = true)] +fn reject_update() { + reject("reject in update method"); +} + +#[query] +fn trap_query() { + trap("trap in query method"); +} + +#[update] +fn trap_update() { + trap("trap in update method"); +} + fn main() {} diff --git a/packages/pocket-ic/tests/management_canister.rs b/packages/pocket-ic/tests/management_canister.rs index b6e813729eb..fe1c68e261e 100644 --- a/packages/pocket-ic/tests/management_canister.rs +++ b/packages/pocket-ic/tests/management_canister.rs @@ -61,6 +61,11 @@ fn canister_info(_: CanisterInfoArgs) -> CanisterInfoResult { unreachable!() } +#[update] +fn subnet_info(_: SubnetInfoArgs) -> SubnetInfoResult { + unreachable!() +} + #[update] fn delete_canister(_: CanisterIdRecord) { unreachable!() diff --git a/packages/pocket-ic/tests/slow.rs b/packages/pocket-ic/tests/slow.rs index 25e94b02537..126cb7db213 100644 --- a/packages/pocket-ic/tests/slow.rs +++ b/packages/pocket-ic/tests/slow.rs @@ -1,5 +1,5 @@ use candid::{Encode, Principal}; -use pocket_ic::{PocketIc, PocketIcBuilder, UserError, WasmResult}; +use pocket_ic::{PocketIc, PocketIcBuilder, RejectResponse}; use std::time::Duration; // 200T cycles @@ -15,7 +15,7 @@ fn execute_many_instructions( instructions: u64, dts_rounds: u64, system_subnet: bool, -) -> Result { +) -> Result, RejectResponse> { // Create a canister. let t0 = pic.get_time(); let can_id = pic.create_canister(); @@ -90,7 +90,7 @@ fn instruction_limit_exceeded() { let instructions = 42_000_000_000_u64; let dts_rounds = 20; // instruction limit exceeded after 20 rounds let res = execute_many_instructions(&pic, instructions, dts_rounds, false).unwrap_err(); - assert!(res.description.contains( + assert!(res.reject_message.contains( "Canister exceeded the limit of 40000000000 instructions for single message execution." )); } diff --git a/packages/pocket-ic/tests/tests.rs b/packages/pocket-ic/tests/tests.rs index 56389585740..21ac2e65250 100644 --- a/packages/pocket-ic/tests/tests.rs +++ b/packages/pocket-ic/tests/tests.rs @@ -1,19 +1,23 @@ use candid::{decode_one, encode_one, CandidType, Decode, Deserialize, Encode, Principal}; +use ic_certification::Label; +use ic_transport_types::Envelope; +use ic_transport_types::EnvelopeContent::ReadState; use pocket_ic::management_canister::{ - CanisterId, CanisterIdRecord, CanisterInstallMode, CanisterSettings, EcdsaPublicKeyResult, + CanisterIdRecord, CanisterInstallMode, CanisterSettings, EcdsaPublicKeyResult, HttpRequestResult, ProvisionalCreateCanisterWithCyclesArgs, SchnorrAlgorithm, - SchnorrPublicKeyArgsKeyId, SchnorrPublicKeyResult, + SchnorrPublicKeyArgsKeyId, SchnorrPublicKeyResult, SignWithBip341Aux, SignWithSchnorrAux, }; use pocket_ic::{ common::rest::{ BlobCompression, CanisterHttpReply, CanisterHttpResponse, MockCanisterHttpResponse, RawEffectivePrincipal, SubnetKind, }, - query_candid, update_candid, DefaultEffectiveCanisterIdError, ErrorCode, PocketIc, - PocketIcBuilder, WasmResult, + query_candid, update_candid, DefaultEffectiveCanisterIdError, ErrorCode, IngressStatusResult, + PocketIc, PocketIcBuilder, RejectCode, }; #[cfg(unix)] use reqwest::blocking::Client; +use serde::Serialize; use sha2::{Digest, Sha256}; use std::{io::Read, time::SystemTime}; @@ -31,39 +35,48 @@ enum RejectionCode { Unknown, } +// Create a counter canister and charge it with 2T cycles. +fn deploy_counter_canister(pic: &PocketIc) -> Principal { + let canister_id = pic.create_canister(); + pic.add_cycles(canister_id, INIT_CYCLES); + pic.install_canister(canister_id, counter_wasm(), vec![], None); + canister_id +} + +// Call a method on the counter canister as the anonymous principal. +fn call_counter_canister(pic: &PocketIc, canister_id: Principal, method: &str) -> Vec { + pic.update_call( + canister_id, + Principal::anonymous(), + method, + encode_one(()).unwrap(), + ) + .expect("Failed to call counter canister") +} + #[test] fn test_counter_canister() { let pic = PocketIc::new(); + let canister_id = deploy_counter_canister(&pic); - // Create a canister and charge it with 2T cycles. - let can_id = pic.create_canister(); - pic.add_cycles(can_id, INIT_CYCLES); - - // Install the counter canister wasm file on the canister. - let counter_wasm = counter_wasm(); - pic.install_canister(can_id, counter_wasm, vec![], None); - - // Make some calls to the canister. - let reply = call_counter_can(&pic, can_id, "read"); - assert_eq!(reply, WasmResult::Reply(vec![0, 0, 0, 0])); - let reply = call_counter_can(&pic, can_id, "write"); - assert_eq!(reply, WasmResult::Reply(vec![1, 0, 0, 0])); - let reply = call_counter_can(&pic, can_id, "write"); - assert_eq!(reply, WasmResult::Reply(vec![2, 0, 0, 0])); - let reply = call_counter_can(&pic, can_id, "read"); - assert_eq!(reply, WasmResult::Reply(vec![2, 0, 0, 0])); + // Make some calls to the counter canister. + let reply = call_counter_canister(&pic, canister_id, "read"); + assert_eq!(reply, vec![0, 0, 0, 0]); + let reply = call_counter_canister(&pic, canister_id, "write"); + assert_eq!(reply, vec![1, 0, 0, 0]); + let reply = call_counter_canister(&pic, canister_id, "write"); + assert_eq!(reply, vec![2, 0, 0, 0]); + let reply = call_counter_canister(&pic, canister_id, "read"); + assert_eq!(reply, vec![2, 0, 0, 0]); } fn counter_wasm() -> Vec { const COUNTER_WAT: &str = r#" (module (import "ic0" "msg_reply" (func $msg_reply)) - (import "ic0" "msg_reply_data_append" - (func $msg_reply_data_append (param i32 i32))) + (import "ic0" "msg_reply_data_append" (func $msg_reply_data_append (param i32 i32))) (func $write - (i32.store - (i32.const 0) - (i32.add (i32.load (i32.const 0)) (i32.const 1))) + (i32.store (i32.const 0) (i32.add (i32.load (i32.const 0)) (i32.const 1))) (call $read)) (func $read (call $msg_reply_data_append @@ -77,16 +90,6 @@ fn counter_wasm() -> Vec { wat::parse_str(COUNTER_WAT).unwrap() } -fn call_counter_can(ic: &PocketIc, can_id: CanisterId, method: &str) -> WasmResult { - ic.update_call( - can_id, - Principal::anonymous(), - method, - encode_one(()).unwrap(), - ) - .expect("Failed to call counter canister") -} - #[test] fn test_create_canister_with_id() { let pic = PocketIcBuilder::new() @@ -279,22 +282,20 @@ fn test_routing_with_multiple_subnets() { let canister_id_2 = pic.create_canister_on_subnet(None, None, subnet_id_2); pic.add_cycles(canister_id_1, INIT_CYCLES); pic.add_cycles(canister_id_2, INIT_CYCLES); - - let counter_wasm = counter_wasm(); - pic.install_canister(canister_id_1, counter_wasm.clone(), vec![], None); - pic.install_canister(canister_id_2, counter_wasm.clone(), vec![], None); + pic.install_canister(canister_id_1, counter_wasm(), vec![], None); + pic.install_canister(canister_id_2, counter_wasm(), vec![], None); // Call canister 1 on subnet 1. - let reply = call_counter_can(&pic, canister_id_1, "read"); - assert_eq!(reply, WasmResult::Reply(vec![0, 0, 0, 0])); - let reply = call_counter_can(&pic, canister_id_1, "write"); - assert_eq!(reply, WasmResult::Reply(vec![1, 0, 0, 0])); + let reply = call_counter_canister(&pic, canister_id_1, "read"); + assert_eq!(reply, vec![0, 0, 0, 0]); + let reply = call_counter_canister(&pic, canister_id_1, "write"); + assert_eq!(reply, vec![1, 0, 0, 0]); // Call canister 2 on subnet 2. - let reply = call_counter_can(&pic, canister_id_2, "read"); - assert_eq!(reply, WasmResult::Reply(vec![0, 0, 0, 0])); - let reply = call_counter_can(&pic, canister_id_2, "write"); - assert_eq!(reply, WasmResult::Reply(vec![1, 0, 0, 0])); + let reply = call_counter_canister(&pic, canister_id_2, "read"); + assert_eq!(reply, vec![0, 0, 0, 0]); + let reply = call_counter_canister(&pic, canister_id_2, "write"); + assert_eq!(reply, vec![1, 0, 0, 0]); // Creating a canister without specifying a subnet should still work. let _canister_id = pic.create_canister(); @@ -329,7 +330,7 @@ fn test_multiple_large_xnet_payloads() { // Self-calls with 10M and xnet-calls with up to 2M arguments work just fine // and return the length of the blob sent in the inter-canister call. match xnet_result { - Ok(WasmResult::Reply(reply)) => { + Ok(reply) => { let blob_len = Decode!(&reply, usize).unwrap(); assert_eq!(blob_len, size); } @@ -338,8 +339,8 @@ fn test_multiple_large_xnet_payloads() { } else { // An inter-canister call to a different subnet with 10M argument traps. match xnet_result { - Err(user_error) => { - assert_eq!(user_error.code, ErrorCode::CanisterCalledTrap); + Err(reject_response) => { + assert_eq!(reject_response.error_code, ErrorCode::CanisterCalledTrap); } _ => panic!("Unexpected update call result: {:?}", xnet_result), }; @@ -349,40 +350,6 @@ fn test_multiple_large_xnet_payloads() { } } -#[test] -fn test_get_and_set_and_advance_time() { - let pic = PocketIc::new(); - - let unix_time_secs = 1630328630; - let set_time = SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(unix_time_secs); - pic.set_time(set_time); - assert_eq!(pic.get_time(), set_time); - pic.tick(); - assert_eq!(pic.get_time(), set_time); - - pic.advance_time(std::time::Duration::from_secs(420)); - assert_eq!( - pic.get_time(), - set_time + std::time::Duration::from_secs(420) - ); - pic.tick(); - assert_eq!( - pic.get_time(), - set_time + std::time::Duration::from_secs(420) - ); -} - -#[test] -#[should_panic(expected = "SettingTimeIntoPast")] -fn set_time_into_past() { - let pic = PocketIc::new(); - - let now = SystemTime::now(); - pic.set_time(now + std::time::Duration::from_secs(1)); - - pic.set_time(now); -} - fn query_and_check_time(pic: &PocketIc, test_canister: Principal) { let current_time = pic .get_time() @@ -401,7 +368,7 @@ fn query_and_check_time(pic: &PocketIc, test_canister: Principal) { } #[test] -fn query_call_after_advance_time() { +fn test_get_and_set_and_advance_time() { let pic = PocketIc::new(); // We create a test canister. @@ -409,17 +376,54 @@ fn query_call_after_advance_time() { pic.add_cycles(canister, INIT_CYCLES); pic.install_canister(canister, test_canister_wasm(), vec![], None); + let unix_time_secs = 1650000000; + let time = SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(unix_time_secs); + pic.set_time(time); + // time is not certified so `query_and_check_time` would fail here + assert_eq!(pic.get_time(), time); + pic.tick(); query_and_check_time(&pic, canister); - - pic.advance_time(std::time::Duration::from_secs(420)); + assert_eq!(pic.get_time(), time); pic.tick(); - query_and_check_time(&pic, canister); + assert_eq!(pic.get_time(), time + std::time::Duration::from_nanos(1)); - pic.advance_time(std::time::Duration::from_secs(0)); + let unix_time_secs = 1700000000; + let time = SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(unix_time_secs); + pic.set_certified_time(time); + query_and_check_time(&pic, canister); + assert_eq!(pic.get_time(), time); + pic.tick(); + query_and_check_time(&pic, canister); + assert_eq!(pic.get_time(), time + std::time::Duration::from_nanos(1)); pic.tick(); + query_and_check_time(&pic, canister); + assert_eq!(pic.get_time(), time + std::time::Duration::from_nanos(2)); + let time = pic.get_time(); + pic.advance_time(std::time::Duration::from_secs(420)); + // time is not certified so `query_and_check_time` would fail here + assert_eq!(pic.get_time(), time + std::time::Duration::from_secs(420)); + pic.tick(); query_and_check_time(&pic, canister); + assert_eq!(pic.get_time(), time + std::time::Duration::from_secs(420)); + pic.tick(); + query_and_check_time(&pic, canister); + assert_eq!( + pic.get_time(), + time + std::time::Duration::from_secs(420) + std::time::Duration::from_nanos(1) + ); +} + +#[test] +#[should_panic(expected = "SettingTimeIntoPast")] +fn set_time_into_past() { + let pic = PocketIc::new(); + + let now = SystemTime::now(); + pic.set_time(now + std::time::Duration::from_secs(1)); + + pic.set_time(now); } #[test] @@ -510,11 +514,7 @@ fn test_get_subnet_of_canister() { #[test] fn test_set_and_get_stable_memory_not_compressed() { let pic = PocketIc::new(); - let canister_id = pic.create_canister(); - pic.add_cycles(canister_id, INIT_CYCLES); - - let counter_wasm = counter_wasm(); - pic.install_canister(canister_id, counter_wasm, vec![], None); + let canister_id = deploy_counter_canister(&pic); let data = "deadbeef".as_bytes().to_vec(); pic.set_stable_memory(canister_id, data.clone(), BlobCompression::NoCompression); @@ -526,10 +526,7 @@ fn test_set_and_get_stable_memory_not_compressed() { #[test] fn test_set_and_get_stable_memory_compressed() { let pic = PocketIc::new(); - let canister_id = pic.create_canister(); - pic.add_cycles(canister_id, INIT_CYCLES); - let counter_wasm = counter_wasm(); - pic.install_canister(canister_id, counter_wasm, vec![], None); + let canister_id = deploy_counter_canister(&pic); let data = "decafbad".as_bytes().to_vec(); let mut compressed_data = Vec::new(); @@ -661,11 +658,7 @@ fn test_inspect_message() { #[test] fn test_too_large_call() { let pic = PocketIc::new(); - - let canister_id = pic.create_canister(); - pic.add_cycles(canister_id, INIT_CYCLES); - let counter_wasm = counter_wasm(); - pic.install_canister(canister_id, counter_wasm, vec![], None); + let canister_id = deploy_counter_canister(&pic); const MAX_INGRESS_MESSAGE_ARG_SIZE: usize = 2097152; pic.update_call( @@ -696,26 +689,23 @@ async fn test_create_and_drop_instances_async() { async fn test_counter_canister_async() { let pic = pocket_ic::nonblocking::PocketIc::new().await; - // Create a canister and charge it with 2T cycles. - let can_id = pic.create_canister().await; - pic.add_cycles(can_id, INIT_CYCLES).await; - - // Install the counter canister wasm file on the canister. - let counter_wasm = counter_wasm(); - pic.install_canister(can_id, counter_wasm, vec![], None) + // Create a counter canister and charge it with 2T cycles. + let canister_id = pic.create_canister().await; + pic.add_cycles(canister_id, INIT_CYCLES).await; + pic.install_canister(canister_id, counter_wasm(), vec![], None) .await; // Make some calls to the canister. let reply = pic .update_call( - can_id, + canister_id, Principal::anonymous(), "read", encode_one(()).unwrap(), ) .await .expect("Failed to call counter canister"); - assert_eq!(reply, WasmResult::Reply(vec![0, 0, 0, 0])); + assert_eq!(reply, vec![0, 0, 0, 0]); // Drop the PocketIc instance. pic.drop().await; @@ -749,48 +739,38 @@ fn install_very_large_wasm() { let pic = PocketIcBuilder::new().with_application_subnet().build(); // Create a canister. - let can_id = pic.create_canister(); + let canister_id = pic.create_canister(); // Charge the canister with 2T cycles. - pic.add_cycles(can_id, 100 * INIT_CYCLES); + pic.add_cycles(canister_id, 100 * INIT_CYCLES); // Install the very large canister wasm on the canister. let wasm_module = very_large_wasm(5_000_000); assert!(wasm_module.len() >= 5_000_000); - pic.install_canister(can_id, wasm_module, vec![], None); + pic.install_canister(canister_id, wasm_module, vec![], None); // Update call on the newly installed canister should succeed // and return 4 bytes of the large data section. let res = pic - .update_call(can_id, Principal::anonymous(), "read", vec![]) + .update_call(canister_id, Principal::anonymous(), "read", vec![]) .unwrap(); - match res { - WasmResult::Reply(data) => assert_eq!(data, vec![b'X'; 4]), - _ => panic!("Unexpected update call response: {:?}", res), - }; + assert_eq!(res, vec![b'X'; 4]); } #[test] fn test_uninstall_canister() { let pic = PocketIc::new(); - - // Create a canister and charge it with 2T cycles. - let can_id = pic.create_canister(); - pic.add_cycles(can_id, INIT_CYCLES); - - // Install the counter canister wasm file on the canister. - let counter_wasm = counter_wasm(); - pic.install_canister(can_id, counter_wasm, vec![], None); + let canister_id = deploy_counter_canister(&pic); // The module hash should be set after the canister is installed. - let status = pic.canister_status(can_id, None).unwrap(); + let status = pic.canister_status(canister_id, None).unwrap(); assert!(status.module_hash.is_some()); // Uninstall the canister. - pic.uninstall_canister(can_id, None).unwrap(); + pic.uninstall_canister(canister_id, None).unwrap(); // The module hash should be unset after the canister is uninstalled. - let status = pic.canister_status(can_id, None).unwrap(); + let status = pic.canister_status(canister_id, None).unwrap(); assert!(status.module_hash.is_none()); } @@ -799,11 +779,11 @@ fn test_update_canister_settings() { let pic = PocketIc::new(); // Create a canister and charge it with 200T cycles. - let can_id = pic.create_canister(); - pic.add_cycles(can_id, 100 * INIT_CYCLES); + let canister_id = pic.create_canister(); + pic.add_cycles(canister_id, 100 * INIT_CYCLES); // The compute allocation of the canister should be zero. - let status = pic.canister_status(can_id, None).unwrap(); + let status = pic.canister_status(canister_id, None).unwrap(); let zero: candid::Nat = 0_u64.into(); assert_eq!(status.settings.compute_allocation, zero); @@ -813,11 +793,11 @@ fn test_update_canister_settings() { compute_allocation: Some(new_compute_allocation.clone()), ..Default::default() }; - pic.update_canister_settings(can_id, None, settings) + pic.update_canister_settings(canister_id, None, settings) .unwrap(); // Check that the compute allocation has been set. - let status = pic.canister_status(can_id, None).unwrap(); + let status = pic.canister_status(canister_id, None).unwrap(); assert_eq!(status.settings.compute_allocation, new_compute_allocation); } @@ -891,7 +871,7 @@ fn test_xnet_call_and_create_canister_with_specified_id() { Encode!(&canister_b).unwrap(), ); match xnet_result { - Ok(WasmResult::Reply(reply)) => { + Ok(reply) => { let identity = Decode!(&reply, String).unwrap(); assert_eq!(identity, canister_b.to_string()); } @@ -942,53 +922,96 @@ fn test_schnorr() { // We define the message, derivation path, and ECDSA key ID to use in this test. let message = b"Hello, world!==================="; // must be of length 32 bytes for BIP340 let derivation_path = vec!["my message".as_bytes().to_vec()]; + let some_aux: Option = + Some(SignWithSchnorrAux::Bip341(SignWithBip341Aux { + merkle_root_hash: b"Hello, aux!=====================".to_vec(), + })); for algorithm in [SchnorrAlgorithm::Bip340Secp256K1, SchnorrAlgorithm::Ed25519] { for name in ["key_1", "test_key_1", "dfx_test_key"] { - let key_id = SchnorrPublicKeyArgsKeyId { - algorithm: algorithm.clone(), - name: name.to_string(), - }; - - // We get the Schnorr public key and signature via update calls to the test canister. - let schnorr_public_key = update_candid::< - (Option, _, _), - (Result,), - >( - &pic, - canister, - "schnorr_public_key", - (None, derivation_path.clone(), key_id.clone()), - ) - .unwrap() - .0 - .unwrap(); - let schnorr_signature = update_candid::<_, (Result, String>,)>( - &pic, - canister, - "sign_with_schnorr", - (message, derivation_path.clone(), key_id.clone()), - ) - .unwrap() - .0 - .unwrap(); + for aux in [None, some_aux.clone()] { + let key_id = SchnorrPublicKeyArgsKeyId { + algorithm: algorithm.clone(), + name: name.to_string(), + }; - // We verify the Schnorr signature. - match key_id.algorithm { - SchnorrAlgorithm::Bip340Secp256K1 => { - use k256::ecdsa::signature::hazmat::PrehashVerifier; - use k256::schnorr::{Signature, VerifyingKey}; - let vk = VerifyingKey::from_bytes(&schnorr_public_key.public_key[1..]).unwrap(); - let sig = Signature::try_from(schnorr_signature.as_slice()).unwrap(); - vk.verify_prehash(message, &sig).unwrap(); - } - SchnorrAlgorithm::Ed25519 => { - use ed25519_dalek::{Signature, Verifier, VerifyingKey}; - let pk: [u8; 32] = schnorr_public_key.public_key.try_into().unwrap(); - let vk = VerifyingKey::from_bytes(&pk).unwrap(); - let signature = Signature::from_slice(&schnorr_signature).unwrap(); - vk.verify(message, &signature).unwrap(); - } - }; + // We get the Schnorr public key and signature via update calls to the test canister. + let schnorr_public_key = update_candid::< + (Option, _, _), + (Result,), + >( + &pic, + canister, + "schnorr_public_key", + (None, derivation_path.clone(), key_id.clone()), + ) + .unwrap() + .0 + .unwrap(); + let schnorr_signature_result = update_candid::<_, (Result, String>,)>( + &pic, + canister, + "sign_with_schnorr", + ( + message, + derivation_path.clone(), + key_id.clone(), + aux.clone(), + ), + ) + .unwrap() + .0; + + // We verify the Schnorr signature. + match key_id.algorithm { + SchnorrAlgorithm::Bip340Secp256K1 => { + use k256::ecdsa::signature::hazmat::PrehashVerifier; + use k256::schnorr::{Signature, VerifyingKey}; + let bip340_public_key = schnorr_public_key.public_key[1..].to_vec(); + let public_key = match aux { + None => bip340_public_key, + Some(SignWithSchnorrAux::Bip341(bip341_aux)) => { + use bitcoin::hashes::Hash; + use bitcoin::schnorr::TapTweak; + let xonly = bitcoin::util::key::XOnlyPublicKey::from_slice( + bip340_public_key.as_slice(), + ) + .unwrap(); + let merkle_root = + bitcoin::util::taproot::TapBranchHash::from_slice( + &bip341_aux.merkle_root_hash, + ) + .unwrap(); + let secp256k1_engine = bitcoin::secp256k1::Secp256k1::new(); + xonly + .tap_tweak(&secp256k1_engine, Some(merkle_root)) + .0 + .to_inner() + .serialize() + .to_vec() + } + }; + let vk = VerifyingKey::from_bytes(&public_key).unwrap(); + let sig = Signature::try_from(schnorr_signature_result.unwrap().as_slice()) + .unwrap(); + + vk.verify_prehash(message, &sig).unwrap(); + } + SchnorrAlgorithm::Ed25519 => { + use ed25519_dalek::{Signature, Verifier, VerifyingKey}; + let pk: [u8; 32] = schnorr_public_key.public_key.try_into().unwrap(); + let vk = VerifyingKey::from_bytes(&pk).unwrap(); + let verification_result = schnorr_signature_result.map(|signature| { + let s = Signature::from_slice(&signature).unwrap(); + vk.verify(message, &s).unwrap(); + }); + assert!( + verification_result.is_ok() == aux.is_none(), + "{:?}", + verification_result + ); + } + }; + } } } } @@ -1121,18 +1144,18 @@ fn test_canister_http() { let pic = PocketIc::new(); // Create a canister and charge it with 2T cycles. - let can_id = pic.create_canister(); - pic.add_cycles(can_id, INIT_CYCLES); + let canister_id = pic.create_canister(); + pic.add_cycles(canister_id, INIT_CYCLES); // Install the test canister wasm file on the canister. let test_wasm = test_canister_wasm(); - pic.install_canister(can_id, test_wasm, vec![], None); + pic.install_canister(canister_id, test_wasm, vec![], None); // Submit an update call to the test canister making a canister http outcall // and mock a canister http outcall response. let call_id = pic .submit_call( - can_id, + canister_id, Principal::anonymous(), "canister_http", encode_one(()).unwrap(), @@ -1167,14 +1190,9 @@ fn test_canister_http() { // Now the test canister will receive the http outcall response // and reply to the ingress message from the test driver. let reply = pic.await_call(call_id).unwrap(); - match reply { - WasmResult::Reply(data) => { - let http_response: Result = - decode_one(&data).unwrap(); - assert_eq!(http_response.unwrap().body, body); - } - WasmResult::Reject(msg) => panic!("Unexpected reject {}", msg), - }; + let http_response: Result = + decode_one(&reply).unwrap(); + assert_eq!(http_response.unwrap().body, body); } #[test] @@ -1182,12 +1200,12 @@ fn test_canister_http_with_transform() { let pic = PocketIc::new(); // Create a canister and charge it with 2T cycles. - let can_id = pic.create_canister(); - pic.add_cycles(can_id, INIT_CYCLES); + let canister_id = pic.create_canister(); + pic.add_cycles(canister_id, INIT_CYCLES); // Install the test canister wasm file on the canister. let test_wasm = test_canister_wasm(); - pic.install_canister(can_id, test_wasm, vec![], None); + pic.install_canister(canister_id, test_wasm, vec![], None); // Submit an update call to the test canister making a canister http outcall // with a transform function (clearing http response headers and setting @@ -1195,7 +1213,7 @@ fn test_canister_http_with_transform() { // and mock a canister http outcall response. let call_id = pic .submit_call( - can_id, + canister_id, Principal::anonymous(), "canister_http_with_transform", encode_one(()).unwrap(), @@ -1229,17 +1247,12 @@ fn test_canister_http_with_transform() { // Now the test canister will receive the http outcall response // and reply to the ingress message from the test driver. let reply = pic.await_call(call_id).unwrap(); - match reply { - WasmResult::Reply(data) => { - let http_response: HttpRequestResult = decode_one(&data).unwrap(); - // http response headers are cleared by the transform function - assert!(http_response.headers.is_empty()); - // mocked non-empty response body is transformed to the transform context - // by the transform function - assert_eq!(http_response.body, b"this is my transform context".to_vec()); - } - WasmResult::Reject(msg) => panic!("Unexpected reject {}", msg), - }; + let http_response: HttpRequestResult = decode_one(&reply).unwrap(); + // http response headers are cleared by the transform function + assert!(http_response.headers.is_empty()); + // mocked non-empty response body is transformed to the transform context + // by the transform function + assert_eq!(http_response.body, b"this is my transform context".to_vec()); } #[test] @@ -1247,18 +1260,18 @@ fn test_canister_http_with_diverging_responses() { let pic = PocketIc::new(); // Create a canister and charge it with 2T cycles. - let can_id = pic.create_canister(); - pic.add_cycles(can_id, INIT_CYCLES); + let canister_id = pic.create_canister(); + pic.add_cycles(canister_id, INIT_CYCLES); // Install the test canister wasm file on the canister. let test_wasm = test_canister_wasm(); - pic.install_canister(can_id, test_wasm, vec![], None); + pic.install_canister(canister_id, test_wasm, vec![], None); // Submit an update call to the test canister making a canister http outcall // and mock diverging canister http outcall responses. let call_id = pic .submit_call( - can_id, + canister_id, Principal::anonymous(), "canister_http", encode_one(()).unwrap(), @@ -1296,17 +1309,12 @@ fn test_canister_http_with_diverging_responses() { // and reply to the ingress message from the test driver // relaying the error. let reply = pic.await_call(call_id).unwrap(); - match reply { - WasmResult::Reply(data) => { - let http_response: Result = - decode_one(&data).unwrap(); - let (reject_code, err) = http_response.unwrap_err(); - assert!(matches!(reject_code, RejectionCode::SysTransient)); - let expected = "No consensus could be reached. Replicas had different responses. Details: request_id: 0, timeout: 1620328930000000005, hashes: [98387cc077af9cff2ef439132854e91cb074035bb76e2afb266960d8e3beaf11: 2], [6a2fa8e54fb4bbe62cde29f7531223d9fcf52c21c03500c1060a5f893ed32d2e: 2], [3e9ec98abf56ef680bebb14309858ede38f6fde771cd4c04cda8f066dc2810db: 2], [2c14e77f18cd990676ae6ce0d7eb89c0af9e1a66e17294b5f0efa68422bba4cb: 2], [2843e4133f673571ff919808d3ca542cc54aaf288c702944e291f0e4fafffc69: 2], [1c4ad84926c36f1fbc634a0dc0535709706f7c48f0c6ebd814fe514022b90671: 2], [7bf80e2f02011ab0a7836b526546e75203b94e856d767c9df4cb0c19baf34059: 1]"; - assert_eq!(err, expected); - } - WasmResult::Reject(msg) => panic!("Unexpected reject {}", msg), - }; + let http_response: Result = + decode_one(&reply).unwrap(); + let (reject_code, err) = http_response.unwrap_err(); + assert!(matches!(reject_code, RejectionCode::SysTransient)); + let expected = "No consensus could be reached. Replicas had different responses. Details: request_id: 0, timeout: 1620328930000000005, hashes: [98387cc077af9cff2ef439132854e91cb074035bb76e2afb266960d8e3beaf11: 2], [6a2fa8e54fb4bbe62cde29f7531223d9fcf52c21c03500c1060a5f893ed32d2e: 2], [3e9ec98abf56ef680bebb14309858ede38f6fde771cd4c04cda8f066dc2810db: 2], [2c14e77f18cd990676ae6ce0d7eb89c0af9e1a66e17294b5f0efa68422bba4cb: 2], [2843e4133f673571ff919808d3ca542cc54aaf288c702944e291f0e4fafffc69: 2], [1c4ad84926c36f1fbc634a0dc0535709706f7c48f0c6ebd814fe514022b90671: 2], [7bf80e2f02011ab0a7836b526546e75203b94e856d767c9df4cb0c19baf34059: 1]"; + assert_eq!(err, expected); } #[test] @@ -1315,17 +1323,17 @@ fn test_canister_http_with_one_additional_response() { let pic = PocketIc::new(); // Create a canister and charge it with 2T cycles. - let can_id = pic.create_canister(); - pic.add_cycles(can_id, INIT_CYCLES); + let canister_id = pic.create_canister(); + pic.add_cycles(canister_id, INIT_CYCLES); // Install the test canister wasm file on the canister. let test_wasm = test_canister_wasm(); - pic.install_canister(can_id, test_wasm, vec![], None); + pic.install_canister(canister_id, test_wasm, vec![], None); // Submit an update call to the test canister making a canister http outcall // and mock diverging canister http outcall responses. pic.submit_call( - can_id, + canister_id, Principal::anonymous(), "canister_http", encode_one(()).unwrap(), @@ -1363,18 +1371,18 @@ fn test_canister_http_timeout() { let pic = PocketIc::new(); // Create a canister and charge it with 2T cycles. - let can_id = pic.create_canister(); - pic.add_cycles(can_id, INIT_CYCLES); + let canister_id = pic.create_canister(); + pic.add_cycles(canister_id, INIT_CYCLES); // Install the test canister wasm file on the canister. let test_wasm = test_canister_wasm(); - pic.install_canister(can_id, test_wasm, vec![], None); + pic.install_canister(canister_id, test_wasm, vec![], None); // Submit an update call to the test canister making a canister http outcall // and mock a canister http outcall response. let call_id = pic .submit_call( - can_id, + canister_id, Principal::anonymous(), "canister_http", encode_one(()).unwrap(), @@ -1399,24 +1407,18 @@ fn test_canister_http_timeout() { // Now the test canister will receive the http outcall response // and reply to the ingress message from the test driver. let reply = pic.await_call(call_id).unwrap(); - match reply { - WasmResult::Reply(data) => { - let http_response: Result = - decode_one(&data).unwrap(); - let (reject_code, err) = http_response.unwrap_err(); - match reject_code { - RejectionCode::SysTransient => (), - _ => panic!("Unexpected reject code {:?}", reject_code), - }; - assert_eq!(err, "Canister http request timed out"); - } - WasmResult::Reject(msg) => panic!("Unexpected reject {}", msg), + let http_response: Result = + decode_one(&reply).unwrap(); + let (reject_code, err) = http_response.unwrap_err(); + match reject_code { + RejectionCode::SysTransient => (), + _ => panic!("Unexpected reject code {:?}", reject_code), }; + assert_eq!(err, "Canister http request timed out"); } #[test] fn subnet_metrics() { - const INIT_CYCLES: u128 = 2_000_000_000_000; let pic = PocketIcBuilder::new().with_application_subnet().build(); let topology = pic.topology(); @@ -1426,17 +1428,13 @@ fn subnet_metrics() { .get_subnet_metrics(Principal::management_canister()) .is_none()); - let canister_id = pic.create_canister(); - pic.add_cycles(canister_id, INIT_CYCLES); - pic.install_canister(canister_id, counter_wasm(), vec![], None); + deploy_counter_canister(&pic); let metrics = pic.get_subnet_metrics(app_subnet).unwrap(); assert_eq!(metrics.num_canisters, 1); assert!((1 << 16) < metrics.canister_state_bytes && metrics.canister_state_bytes < (1 << 17)); - let canister_id = pic.create_canister(); - pic.add_cycles(canister_id, INIT_CYCLES); - pic.install_canister(canister_id, counter_wasm(), vec![], None); + let canister_id = deploy_counter_canister(&pic); let metrics = pic.get_subnet_metrics(app_subnet).unwrap(); assert_eq!(metrics.num_canisters, 2); @@ -1742,17 +1740,13 @@ fn get_controllers_of_nonexisting_canister() { #[test] fn test_canister_snapshots() { let pic = PocketIc::new(); - - // We deploy the counter canister. - let canister_id = pic.create_canister(); - pic.add_cycles(canister_id, INIT_CYCLES); - pic.install_canister(canister_id, counter_wasm(), vec![], None); + let canister_id = deploy_counter_canister(&pic); // We bump the counter to make the counter different from its initial value. - let reply = call_counter_can(&pic, canister_id, "write"); - assert_eq!(reply, WasmResult::Reply(1_u32.to_le_bytes().to_vec())); - let reply = call_counter_can(&pic, canister_id, "read"); - assert_eq!(reply, WasmResult::Reply(1_u32.to_le_bytes().to_vec())); + let reply = call_counter_canister(&pic, canister_id, "write"); + assert_eq!(reply, 1_u32.to_le_bytes().to_vec()); + let reply = call_counter_canister(&pic, canister_id, "read"); + assert_eq!(reply, 1_u32.to_le_bytes().to_vec()); // We haven't taken any snapshot so far and thus listing snapshots yields an empty result. let snapshots = pic.list_canister_snapshots(canister_id, None).unwrap(); @@ -1774,10 +1768,10 @@ fn test_canister_snapshots() { ); // We bump the counter once more to test loading snapshots in a subsequent step. - let reply = call_counter_can(&pic, canister_id, "write"); - assert_eq!(reply, WasmResult::Reply(2_u32.to_le_bytes().to_vec())); - let reply = call_counter_can(&pic, canister_id, "read"); - assert_eq!(reply, WasmResult::Reply(2_u32.to_le_bytes().to_vec())); + let reply = call_counter_canister(&pic, canister_id, "write"); + assert_eq!(reply, 2_u32.to_le_bytes().to_vec()); + let reply = call_counter_canister(&pic, canister_id, "read"); + assert_eq!(reply, 2_u32.to_le_bytes().to_vec()); // We load the snapshot (it is recommended to only load a snapshot on a stopped canister). pic.stop_canister(canister_id, None).unwrap(); @@ -1786,14 +1780,14 @@ fn test_canister_snapshots() { pic.start_canister(canister_id, None).unwrap(); // We verify that the snapshot was successfully loaded. - let reply = call_counter_can(&pic, canister_id, "read"); - assert_eq!(reply, WasmResult::Reply(1_u32.to_le_bytes().to_vec())); + let reply = call_counter_canister(&pic, canister_id, "read"); + assert_eq!(reply, 1_u32.to_le_bytes().to_vec()); // We bump the counter again. - let reply = call_counter_can(&pic, canister_id, "write"); - assert_eq!(reply, WasmResult::Reply(2_u32.to_le_bytes().to_vec())); - let reply = call_counter_can(&pic, canister_id, "read"); - assert_eq!(reply, WasmResult::Reply(2_u32.to_le_bytes().to_vec())); + let reply = call_counter_canister(&pic, canister_id, "write"); + assert_eq!(reply, 2_u32.to_le_bytes().to_vec()); + let reply = call_counter_canister(&pic, canister_id, "read"); + assert_eq!(reply, 2_u32.to_le_bytes().to_vec()); // We take one more snapshot: since we already have an active snapshot, // taking another snapshot fails unless we specify the active snapshot to be replaced. @@ -1963,40 +1957,33 @@ fn make_live_twice() { #[test] fn create_instance_from_existing() { let pic = PocketIc::new(); - - // Create a canister and charge it with 2T cycles. - let can_id = pic.create_canister(); - pic.add_cycles(can_id, INIT_CYCLES); - - // Install the counter canister wasm file on the canister. - let counter_wasm = counter_wasm(); - pic.install_canister(can_id, counter_wasm, vec![], None); + let canister_id = deploy_counter_canister(&pic); // Bump and check the counter value; - let reply = call_counter_can(&pic, can_id, "write"); - assert_eq!(reply, WasmResult::Reply(vec![1, 0, 0, 0])); - let reply = call_counter_can(&pic, can_id, "read"); - assert_eq!(reply, WasmResult::Reply(vec![1, 0, 0, 0])); + let reply = call_counter_canister(&pic, canister_id, "write"); + assert_eq!(reply, vec![1, 0, 0, 0]); + let reply = call_counter_canister(&pic, canister_id, "read"); + assert_eq!(reply, vec![1, 0, 0, 0]); // Create a new PocketIC handle to the existing PocketIC instance. let pic_handle = PocketIc::new_from_existing_instance(pic.get_server_url(), pic.instance_id(), None); // Bump and check the counter value; - let reply = call_counter_can(&pic_handle, can_id, "write"); - assert_eq!(reply, WasmResult::Reply(vec![2, 0, 0, 0])); - let reply = call_counter_can(&pic_handle, can_id, "read"); - assert_eq!(reply, WasmResult::Reply(vec![2, 0, 0, 0])); + let reply = call_counter_canister(&pic_handle, canister_id, "write"); + assert_eq!(reply, vec![2, 0, 0, 0]); + let reply = call_counter_canister(&pic_handle, canister_id, "read"); + assert_eq!(reply, vec![2, 0, 0, 0]); // Drop the newly created PocketIC handle. // This should not delete the existing PocketIC instance. drop(pic_handle); // Bump and check the counter value; - let reply = call_counter_can(&pic, can_id, "write"); - assert_eq!(reply, WasmResult::Reply(vec![3, 0, 0, 0])); - let reply = call_counter_can(&pic, can_id, "read"); - assert_eq!(reply, WasmResult::Reply(vec![3, 0, 0, 0])); + let reply = call_counter_canister(&pic, canister_id, "write"); + assert_eq!(reply, vec![3, 0, 0, 0]); + let reply = call_counter_canister(&pic, canister_id, "read"); + assert_eq!(reply, vec![3, 0, 0, 0]); } #[test] @@ -2009,25 +1996,82 @@ fn ingress_status() { pic.add_cycles(canister_id, INIT_CYCLES); pic.install_canister(canister_id, test_canister_wasm(), vec![], None); + let caller = Principal::from_slice(&[0xFF; 29]); let msg_id = pic - .submit_call( - canister_id, - Principal::anonymous(), - "whoami", - encode_one(()).unwrap(), - ) + .submit_call(canister_id, caller, "whoami", encode_one(()).unwrap()) .unwrap(); assert!(pic.ingress_status(msg_id.clone()).is_none()); + // since the ingress status is not available, any caller can attempt to retrieve it + match pic.ingress_status_as(msg_id.clone(), Principal::anonymous()) { + IngressStatusResult::NotAvailable => (), + status => panic!("Unexpected ingress status: {:?}", status), + } + pic.tick(); - let ingress_status = pic.ingress_status(msg_id).unwrap().unwrap(); - let principal = match ingress_status { - WasmResult::Reply(data) => Decode!(&data, String).unwrap(), - WasmResult::Reject(err) => panic!("Unexpected reject: {}", err), - }; + let reply = pic.ingress_status(msg_id.clone()).unwrap().unwrap(); + let principal = Decode!(&reply, String).unwrap(); assert_eq!(principal, canister_id.to_string()); + + // now that the ingress status is available, the caller must match + let expected_err = "The user tries to access Request ID not signed by the caller."; + match pic.ingress_status_as(msg_id.clone(), Principal::anonymous()) { + IngressStatusResult::Forbidden(msg) => assert_eq!(msg, expected_err,), + status => panic!("Unexpected ingress status: {:?}", status), + } + + // confirm the behavior of read state requests + let resp = read_state_request_status(&pic, canister_id, msg_id.message_id.as_slice()); + assert_eq!(resp.status(), reqwest::StatusCode::FORBIDDEN); + assert_eq!( + String::from_utf8(resp.bytes().unwrap().to_vec()).unwrap(), + expected_err + ); +} + +fn read_state_request_status( + pic: &PocketIc, + canister_id: Principal, + msg_id: &[u8], +) -> reqwest::blocking::Response { + let path = vec!["request_status".into(), Label::from_bytes(msg_id)]; + let paths = vec![path.clone()]; + let content = ReadState { + ingress_expiry: pic + .get_time() + .duration_since(std::time::SystemTime::UNIX_EPOCH) + .unwrap() + .as_nanos() as u64 + + 240_000_000_000, + sender: Principal::anonymous(), + paths, + }; + let envelope = Envelope { + content: std::borrow::Cow::Borrowed(&content), + sender_pubkey: None, + sender_sig: None, + sender_delegation: None, + }; + + let mut serialized_bytes = Vec::new(); + let mut serializer = serde_cbor::Serializer::new(&mut serialized_bytes); + serializer.self_describe().unwrap(); + envelope.serialize(&mut serializer).unwrap(); + + let endpoint = format!( + "instances/{}/api/v2/canister/{}/read_state", + pic.instance_id(), + canister_id.to_text() + ); + let client = reqwest::blocking::Client::new(); + client + .post(pic.get_server_url().join(&endpoint).unwrap()) + .header(reqwest::header::CONTENT_TYPE, "application/cbor") + .body(serialized_bytes) + .send() + .unwrap() } #[test] @@ -2052,9 +2096,121 @@ fn await_call_no_ticks() { .unwrap(); let result = pic.await_call_no_ticks(msg_id).unwrap(); - let principal = match result { - WasmResult::Reply(data) => Decode!(&data, String).unwrap(), - WasmResult::Reject(err) => panic!("Unexpected reject: {}", err), - }; + let principal = Decode!(&result, String).unwrap(); assert_eq!(principal, canister_id.to_string()); } + +#[test] +fn many_intersubnet_calls() { + let pic = PocketIcBuilder::new() + .with_application_subnet() + .with_application_subnet() + .build(); + let canister_1 = pic.create_canister_on_subnet(None, None, pic.topology().get_app_subnets()[0]); + pic.add_cycles(canister_1, 100_000_000_000_000_000); + pic.install_canister(canister_1, test_canister_wasm(), vec![], None); + let canister_2 = pic.create_canister_on_subnet(None, None, pic.topology().get_app_subnets()[1]); + pic.add_cycles(canister_2, 100_000_000_000_000_000); + pic.install_canister(canister_2, test_canister_wasm(), vec![], None); + + let mut msg_ids = vec![]; + let num_msgs: usize = 500; + let msg_size: usize = 10000; + for _ in 0..num_msgs { + let msg_id = pic + .submit_call( + canister_1, + Principal::anonymous(), + "call_with_large_blob", + Encode!(&canister_2, &msg_size).unwrap(), + ) + .unwrap(); + msg_ids.push(msg_id); + } + for msg_id in msg_ids { + pic.await_call(msg_id).unwrap(); + } +} + +#[test] +fn test_reject_response_type() { + let pic = PocketIc::new(); + + // We create a test canister. + let canister = pic.create_canister(); + pic.add_cycles(canister, INIT_CYCLES); + pic.install_canister(canister, test_canister_wasm(), vec![], None); + + for certified in [true, false] { + for action in ["reject", "trap"] { + for method in ["query", "update"] { + // updates are always certified + if !certified && method == "update" { + continue; + } + let method_name = format!("{}_{}", action, method); + let (err, msg_id) = if certified { + let msg_id = pic + .submit_call( + canister, + Principal::anonymous(), + &method_name, + Encode!(&()).unwrap(), + ) + .unwrap(); + let err = pic.await_call(msg_id.clone()).unwrap_err(); + (err, Some(msg_id)) + } else { + let err = pic + .query_call( + canister, + Principal::anonymous(), + &method_name, + Encode!(&()).unwrap(), + ) + .unwrap_err(); + (err, None) + }; + if let Some(msg_id) = msg_id { + let ingress_status_err = pic.ingress_status(msg_id).unwrap().unwrap_err(); + assert_eq!(ingress_status_err, err); + } + if action == "reject" { + assert_eq!(err.reject_code, RejectCode::CanisterReject); + assert_eq!(err.error_code, ErrorCode::CanisterRejectedMessage); + } else { + assert_eq!(action, "trap"); + assert_eq!(err.reject_code, RejectCode::CanisterError); + assert_eq!(err.error_code, ErrorCode::CanisterCalledTrap); + } + assert!(err + .reject_message + .contains(&format!("{} in {} method", action, method))); + assert_eq!(err.certified, certified); + } + } + } + + for action in [b"trap", b"skip"] { + let err = pic + .submit_call( + canister, + Principal::anonymous(), + "trap_update", + action.to_vec(), + ) + .unwrap_err(); + if action == b"trap" { + assert_eq!(err.reject_code, RejectCode::CanisterError); + assert!(err.reject_message.contains("trap in inspect message")); + assert_eq!(err.error_code, ErrorCode::CanisterCalledTrap); + } else { + assert_eq!(action, b"skip"); + assert_eq!(err.reject_code, RejectCode::CanisterReject); + assert!(err.reject_message.contains("Canister rejected the message")); + assert_eq!(err.error_code, ErrorCode::CanisterRejectedMessage); + } + // inspect message is always uncertified + assert!(!err.certified); + } +} diff --git a/publish/canisters/BUILD.bazel b/publish/canisters/BUILD.bazel index b4f480f7003..c3c75e835c2 100644 --- a/publish/canisters/BUILD.bazel +++ b/publish/canisters/BUILD.bazel @@ -1,5 +1,7 @@ load("@bazel_skylib//rules:copy_file.bzl", "copy_file") load("//ci/src/artifacts:upload.bzl", "upload_artifacts") +load("//rs/nns:nns.bzl", NNS_CANISTER_NAME_TO_MAX_COMPRESSED_WASM_SIZE_E5_BYTES = "CANISTER_NAME_TO_MAX_COMPRESSED_WASM_SIZE_E5_BYTES") +load("//rs/sns:sns.bzl", SNS_CANISTER_NAME_TO_MAX_COMPRESSED_WASM_SIZE_E5_BYTES = "CANISTER_NAME_TO_MAX_COMPRESSED_WASM_SIZE_E5_BYTES") # The list of canisters that are being published/uploaded as artifacts to our CDN (download.dfinity.systems) include # 1. Officially released canisters that can be downloaded/verified by 3rd parties - https://github.com/dfinity/ic/blob/master/.github/workflows/tag-release.yml @@ -18,7 +20,6 @@ CANISTERS = { "ic-ckbtc-minter_debug.wasm.gz": "//rs/bitcoin/ckbtc/minter:ckbtc_minter_debug", "ic-ckbtc-kyt.wasm.gz": "//rs/bitcoin/ckbtc/kyt:kyt_canister", "ic-cketh-minter.wasm.gz": "//rs/ethereum/cketh/minter:cketh_minter", - "ic-icrc1-index.wasm.gz": "//rs/ledger_suite/icrc1/index:index_canister", "ic-icrc1-index-ng.wasm.gz": "//rs/ledger_suite/icrc1/index-ng:index_ng_canister", "ic-icrc1-index-ng-u256.wasm.gz": "//rs/ledger_suite/icrc1/index-ng:index_ng_canister_u256", "ic-icrc1-ledger.wasm.gz": "//rs/ledger_suite/icrc1/ledger:ledger_canister", @@ -66,8 +67,8 @@ CANISTERS_MAX_SIZE_COMPRESSED_E5_BYTES = { "ic-icrc1-ledger-u256.wasm.gz": "7", # Size when constraint addded: 841_234 bytes "ledger-canister.wasm.gz": "9", - # Size when constraint addded: 847_186 bytes - "ledger-canister_notify-method.wasm.gz": "9", + # Size when constraint addded: 906_940 bytes + "ledger-canister_notify-method.wasm.gz": "10", # -- XC team -- # Size when constraint addded: 447_593 bytes @@ -79,43 +80,17 @@ CANISTERS_MAX_SIZE_COMPRESSED_E5_BYTES = { # The orchestrator needs to embed 3 wasms at compile time # (ICRC1 index, ICRC1 ledger, and ICRC1 archive) and size is # therefore strictly controlled. - # Size when constraint addded: 1_655_752 bytes - "ic-ledger-suite-orchestrator-canister.wasm.gz": "17", + # Size when constraint addded: 1_704_979 bytes + "ic-ledger-suite-orchestrator-canister.wasm.gz": "18", # -- BN team -- # Size when constraint addded: 540_349 bytes "rate-limit-canister.wasm.gz": "7", } -# How these limits were chosen: -# -# 1. Temporarily set value to "0". -# 2. Run the test: bazel test //publish/canisters:all -# 3. Fail message reports size it sees. -# 4. Pick a number that gives at least 20% headroom. -# -# This way, there is some room to grow, but an alarm eventually gets triggered after a "significant" -# amount of growth happens. - -NNS_CANISTERS_MAX_SIZE_COMPRESSED_E5_BYTES = { - "cycles-minting-canister.wasm.gz": "5", - "genesis-token-canister.wasm.gz": "3", - "governance-canister.wasm.gz": "17", - "governance-canister_test.wasm.gz": "17", - "registry-canister.wasm.gz": "14", - "root-canister.wasm.gz": "4", -} - -SNS_CANISTERS_MAX_SIZE_COMPRESSED_E5_BYTES = { - "sns-governance-canister.wasm.gz": "13", - "sns-governance-canister_test.wasm.gz": "13", - "sns-root-canister.wasm.gz": "4", - "sns-swap-canister.wasm.gz": "7", -} - -CANISTERS_MAX_SIZE_COMPRESSED_E5_BYTES.update(NNS_CANISTERS_MAX_SIZE_COMPRESSED_E5_BYTES) +CANISTERS_MAX_SIZE_COMPRESSED_E5_BYTES.update(NNS_CANISTER_NAME_TO_MAX_COMPRESSED_WASM_SIZE_E5_BYTES) -CANISTERS_MAX_SIZE_COMPRESSED_E5_BYTES.update(SNS_CANISTERS_MAX_SIZE_COMPRESSED_E5_BYTES) +CANISTERS_MAX_SIZE_COMPRESSED_E5_BYTES.update(SNS_CANISTER_NAME_TO_MAX_COMPRESSED_WASM_SIZE_E5_BYTES) [ sh_test( diff --git a/rs/artifact_pool/src/consensus_pool_cache.rs b/rs/artifact_pool/src/consensus_pool_cache.rs index 5b5ef9462b3..5de2a1d307d 100644 --- a/rs/artifact_pool/src/consensus_pool_cache.rs +++ b/rs/artifact_pool/src/consensus_pool_cache.rs @@ -120,7 +120,7 @@ impl<'a> CachedChainIterator<'a> { } } -impl<'a> Iterator for CachedChainIterator<'a> { +impl Iterator for CachedChainIterator<'_> { type Item = Block; fn next(&mut self) -> Option { diff --git a/rs/artifact_pool/src/idkg_pool.rs b/rs/artifact_pool/src/idkg_pool.rs index aaa3c7f4a2f..b28484165b2 100644 --- a/rs/artifact_pool/src/idkg_pool.rs +++ b/rs/artifact_pool/src/idkg_pool.rs @@ -12,17 +12,14 @@ use crate::{ IntoInner, }; use ic_config::artifact_pool::{ArtifactPoolConfig, PersistentPoolBackend}; +use ic_interfaces::idkg::{ + IDkgChangeAction, IDkgChangeSet, IDkgPool, IDkgPoolSection, IDkgPoolSectionOp, + IDkgPoolSectionOps, MutableIDkgPoolSection, +}; use ic_interfaces::p2p::consensus::{ ArtifactTransmit, ArtifactTransmits, ArtifactWithOpt, MutablePool, UnvalidatedArtifact, ValidatedPoolReader, }; -use ic_interfaces::{ - idkg::{ - IDkgChangeAction, IDkgChangeSet, IDkgPool, IDkgPoolSection, IDkgPoolSectionOp, - IDkgPoolSectionOps, MutableIDkgPoolSection, - }, - time_source::TimeSource, -}; use ic_logger::{info, warn, ReplicaLogger}; use ic_metrics::MetricsRegistry; use ic_types::consensus::{ @@ -380,11 +377,7 @@ impl IDkgPoolImpl { } // Populates the unvalidated pool with the initial dealings from the CUP. - pub fn add_initial_dealings( - &mut self, - catch_up_package: &CatchUpPackage, - time_source: &dyn TimeSource, - ) { + pub fn add_initial_dealings(&mut self, catch_up_package: &CatchUpPackage) { let block = catch_up_package.content.block.get_value(); let mut initial_dealings = Vec::new(); @@ -413,7 +406,7 @@ impl IDkgPoolImpl { self.insert(UnvalidatedArtifact { message: IDkgMessage::Dealing(signed_dealing.clone()), peer_id: signed_dealing.dealer_id(), - timestamp: time_source.get_relative_time(), + timestamp: block.context.time, }) } } diff --git a/rs/artifact_pool/src/lmdb_iterator.rs b/rs/artifact_pool/src/lmdb_iterator.rs index 59437b6c1b8..36924fc0fec 100644 --- a/rs/artifact_pool/src/lmdb_iterator.rs +++ b/rs/artifact_pool/src/lmdb_iterator.rs @@ -43,7 +43,7 @@ pub(crate) struct LMDBIterator<'a, F> { db_env: Arc, } -impl<'a, F> LMDBIterator<'a, F> { +impl LMDBIterator<'_, F> { /// Return a new iterator that will iterator through DB objects between /// min_key and max_key (inclusive) that are deserialized using the /// given deserialize function. @@ -71,7 +71,7 @@ impl<'a, F> LMDBIterator<'a, F> { } } -impl<'a, T, F: Fn(&RoTransaction<'_>, &[u8]) -> lmdb::Result> Iterator for LMDBIterator<'a, F> { +impl, &[u8]) -> lmdb::Result> Iterator for LMDBIterator<'_, F> { type Item = T; fn next(&mut self) -> Option { @@ -106,7 +106,7 @@ pub(crate) struct LMDBIDkgIterator<'a, F> { _db_env: Arc, } -impl<'a, F> LMDBIDkgIterator<'a, F> { +impl LMDBIDkgIterator<'_, F> { pub fn new( db_env: Arc, db: Database, @@ -136,7 +136,7 @@ impl<'a, F> LMDBIDkgIterator<'a, F> { } } -impl<'a, K, T, F: Fn(&[u8], &[u8]) -> Option<(K, T)>> Iterator for LMDBIDkgIterator<'a, F> { +impl Option<(K, T)>> Iterator for LMDBIDkgIterator<'_, F> { type Item = (K, T); fn next(&mut self) -> Option { diff --git a/rs/artifact_pool/src/rocksdb_iterator.rs b/rs/artifact_pool/src/rocksdb_iterator.rs index b61483cb1d0..55936a2e0ec 100644 --- a/rs/artifact_pool/src/rocksdb_iterator.rs +++ b/rs/artifact_pool/src/rocksdb_iterator.rs @@ -68,7 +68,7 @@ enum Status { Stopped, } -impl<'a, F> StandaloneIterator<'a, F> { +impl StandaloneIterator<'_, F> { /// Create an iterator for the given column family 'name' of the given 'db' /// starting from 'start_key' pub fn new( diff --git a/rs/artifact_pool/src/rocksdb_pool.rs b/rs/artifact_pool/src/rocksdb_pool.rs index ab35775a2f0..0c89925857b 100755 --- a/rs/artifact_pool/src/rocksdb_pool.rs +++ b/rs/artifact_pool/src/rocksdb_pool.rs @@ -568,7 +568,7 @@ fn deserialize_consensus_artifact( impl PoolSection for PersistentHeightIndexedPool { fn contains(&self, msg_id: &ConsensusMessageId) -> bool { - self.lookup_key(msg_id).map_or(false, |key| { + self.lookup_key(msg_id).is_some_and(|key| { let info = info_for_msg_id(msg_id); let cf_handle = check_not_none_uw!(self.db.cf_handle(info.name)); check_ok_uw!(self.db.get_pinned_cf(cf_handle, &key)).is_some() diff --git a/rs/backup/BUILD.bazel b/rs/backup/BUILD.bazel index a792b7b5e34..32709db022a 100644 --- a/rs/backup/BUILD.bazel +++ b/rs/backup/BUILD.bazel @@ -61,7 +61,7 @@ rust_binary( rust_test( name = "backup_test", + compile_data = ["test_data/fake_input_config.json.template"], crate = ":backup", - data = ["test_data/fake_input_config.json.template"], deps = DEPENDENCIES + DEV_DEPENDENCIES, ) diff --git a/rs/bitcoin/adapter/BUILD.bazel b/rs/bitcoin/adapter/BUILD.bazel index 2050fc86d39..6ffc2252fde 100644 --- a/rs/bitcoin/adapter/BUILD.bazel +++ b/rs/bitcoin/adapter/BUILD.bazel @@ -1,8 +1,6 @@ load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library", "rust_test", "rust_test_suite") load("//bazel:defs.bzl", "rust_bench") -package(default_visibility = ["//visibility:private"]) - DEPENDENCIES = [ # Keep sorted. "//rs/bitcoin/service", @@ -12,7 +10,6 @@ DEPENDENCIES = [ "//rs/monitoring/logger", "//rs/monitoring/metrics", "@crate_index//:bitcoin", - "@crate_index//:clap", "@crate_index//:futures", "@crate_index//:hashlink", "@crate_index//:hex", @@ -50,50 +47,56 @@ DEV_DEPENDENCIES = [ MACRO_DEV_DEPENDENCIES = [] -ALIASES = {} - rust_library( name = "adapter", - srcs = glob(["src/**"]), - aliases = ALIASES, + srcs = glob( + ["src/**"], + exclude = [ + "src/main.rs", + "src/cli.rs", + "src/stress_test.rs", + ], + ), crate_name = "ic_btc_adapter", proc_macro_deps = MACRO_DEPENDENCIES, - version = "0.1.0", visibility = ["//rs/pocket_ic_server:__subpackages__"], deps = DEPENDENCIES, ) rust_binary( name = "ic-btc-adapter", - srcs = ["src/main.rs"], - aliases = ALIASES, + srcs = [ + "src/cli.rs", + "src/main.rs", + ], proc_macro_deps = MACRO_DEPENDENCIES, visibility = ["//rs:release-pkg"], deps = DEPENDENCIES + [ ":adapter", "//rs/monitoring/adapter_metrics/server", + "@crate_index//:clap", ], ) rust_binary( name = "adapter-stress-test", srcs = ["src/stress_test.rs"], - aliases = ALIASES, proc_macro_deps = MACRO_DEPENDENCIES, deps = DEPENDENCIES + [ ":adapter", "//rs/monitoring/adapter_metrics/server", + "@crate_index//:clap", "@crate_index//:hyper-util", ], ) rust_test( name = "adapter_test", - crate = ":adapter", - data = [ + compile_data = [ "test_data/first_2500_mainnet_headers.json", "test_data/first_2500_testnet_headers.json", ], + crate = ":adapter", tags = ["requires-network"], deps = DEV_DEPENDENCIES, ) @@ -102,7 +105,6 @@ rust_test_suite( name = "adapter_integration", timeout = "long", srcs = glob(["tests/**/*.rs"]), - aliases = ALIASES, data = [ # Keep sorted. "//:bitcoind", @@ -127,7 +129,6 @@ rust_bench( name = "e2e_bench", testonly = True, srcs = ["benches/e2e.rs"], - aliases = ALIASES, proc_macro_deps = MACRO_DEPENDENCIES + MACRO_DEV_DEPENDENCIES, deps = DEPENDENCIES + DEV_DEPENDENCIES + [":adapter"], ) diff --git a/rs/bitcoin/adapter/Cargo.toml b/rs/bitcoin/adapter/Cargo.toml index 38c50a38463..41e657a1a14 100644 --- a/rs/bitcoin/adapter/Cargo.toml +++ b/rs/bitcoin/adapter/Cargo.toml @@ -22,6 +22,7 @@ ic-config = { path = "../../config" } ic-logger = { path = "../../monitoring/logger" } ic-metrics = { path = "../../monitoring/metrics" } parking_lot = { workspace = true } +primitive-types = "0.12" prometheus = { workspace = true } prost = { workspace = true } rand = { workspace = true } @@ -36,7 +37,7 @@ tonic = { workspace = true } tower = { workspace = true, optional = true } [dev-dependencies] -bitcoincore-rpc = "0.15.0" +bitcoincore-rpc = { workspace = true } bitcoind = "0.32.0" criterion = { workspace = true } ic-btc-adapter-client = { path = "../client" } diff --git a/rs/bitcoin/adapter/benches/e2e.rs b/rs/bitcoin/adapter/benches/e2e.rs index 6702e2657b7..02c74aca800 100644 --- a/rs/bitcoin/adapter/benches/e2e.rs +++ b/rs/bitcoin/adapter/benches/e2e.rs @@ -1,8 +1,8 @@ -use bitcoin::{blockdata::constants::genesis_block, BlockHash, BlockHeader, Network}; +use bitcoin::{ + block::Header as BlockHeader, blockdata::constants::genesis_block, BlockHash, Network, +}; use criterion::{criterion_group, criterion_main, Criterion}; -use ic_btc_adapter::config::Config; -use ic_btc_adapter::config::IncomingSource; -use ic_btc_adapter::start_server; +use ic_btc_adapter::{start_server, Config, IncomingSource}; use ic_btc_adapter_client::setup_bitcoin_adapter_clients; use ic_btc_adapter_test_utils::generate_headers; use ic_btc_replica_types::BitcoinAdapterRequestWrapper; diff --git a/rs/bitcoin/adapter/src/addressbook.rs b/rs/bitcoin/adapter/src/addressbook.rs index eb67a39402f..370c109647f 100644 --- a/rs/bitcoin/adapter/src/addressbook.rs +++ b/rs/bitcoin/adapter/src/addressbook.rs @@ -1,5 +1,6 @@ use crate::config::Config; -use bitcoin::network::{constants::ServiceFlags, Address}; +use bitcoin::p2p::Address; +use bitcoin::p2p::ServiceFlags; use ic_logger::{info, ReplicaLogger}; use rand::{ prelude::{IteratorRandom, SliceRandom, StdRng}, diff --git a/rs/bitcoin/adapter/src/blockchainmanager.rs b/rs/bitcoin/adapter/src/blockchainmanager.rs index ccc3ffc2063..56e7a0eca2e 100644 --- a/rs/bitcoin/adapter/src/blockchainmanager.rs +++ b/rs/bitcoin/adapter/src/blockchainmanager.rs @@ -5,11 +5,13 @@ use crate::{ Channel, Command, ProcessBitcoinNetworkMessageError, }; use bitcoin::{ - network::{ + block::Header as BlockHeader, + hashes::Hash as _, + p2p::{ message::{NetworkMessage, MAX_INV_SIZE}, message_blockdata::{GetHeadersMessage, Inventory}, }, - Block, BlockHash, BlockHeader, + Block, BlockHash, }; use hashlink::{LinkedHashMap, LinkedHashSet}; use ic_btc_validation::ValidateHeaderError; @@ -389,7 +391,7 @@ impl BlockchainManager { if headers.len() < MAX_HEADERS_SIZE { None } else { - Some((vec![last.header.block_hash()], BlockHash::default())) + Some((vec![last.header.block_hash()], BlockHash::all_zeros())) } } else { None @@ -472,7 +474,7 @@ impl BlockchainManager { tip: initial_hash, }, ); - let locators = (locator_hashes, BlockHash::default()); + let locators = (locator_hashes, BlockHash::all_zeros()); self.send_getheaders(channel, addr, locators); } @@ -687,7 +689,7 @@ impl BlockchainManager { if !self.getheaders_requests.contains_key(&addr) && self.catchup_headers.contains(&addr) { let locators = self.blockchain.lock().unwrap().locator_hashes(); - self.send_getheaders(channel, &addr, (locators, BlockHash::default())); + self.send_getheaders(channel, &addr, (locators, BlockHash::all_zeros())); self.catchup_headers.remove(&addr); } } @@ -777,9 +779,7 @@ pub mod test { use bitcoin::blockdata::constants::genesis_block; use bitcoin::consensus::deserialize; use bitcoin::Network; - use bitcoin::{ - network::message::NetworkMessage, network::message_blockdata::Inventory, BlockHash, - }; + use bitcoin::{p2p::message::NetworkMessage, BlockHash}; use hex::FromHex; use ic_btc_adapter_test_utils::{ generate_headers, generate_large_block_blockchain, BLOCK_1_ENCODED, BLOCK_2_ENCODED, @@ -815,7 +815,7 @@ pub mod test { assert_eq!(channel.command_count(), 1); - let locators = (vec![genesis_hash], BlockHash::default()); + let locators = (vec![genesis_hash], BlockHash::all_zeros()); blockchain_manager.send_getheaders(&mut channel, &addr, locators.clone()); assert!(blockchain_manager.getheaders_requests.contains_key(&addr)); let request = blockchain_manager.getheaders_requests.get(&addr).unwrap(); @@ -824,7 +824,7 @@ pub mod test { let command = channel.pop_front().expect("command not found"); assert!(matches!(command.address, Some(address) if address == addr)); assert!( - matches!(&command.message, NetworkMessage::GetHeaders(GetHeadersMessage { version: _, locator_hashes: _, stop_hash }) if *stop_hash == BlockHash::default()) + matches!(&command.message, NetworkMessage::GetHeaders(GetHeadersMessage { version: _, locator_hashes: _, stop_hash }) if *stop_hash == BlockHash::all_zeros()) ); assert!( matches!(&command.message, NetworkMessage::GetHeaders(GetHeadersMessage { version, locator_hashes: _, stop_hash: _ }) if *version == MINIMUM_VERSION_NUMBER) @@ -894,7 +894,7 @@ pub mod test { _ => GetHeadersMessage { version: 0, locator_hashes: vec![], - stop_hash: BlockHash::default(), + stop_hash: BlockHash::all_zeros(), }, }; assert_eq!( @@ -904,7 +904,7 @@ pub mod test { ); assert_eq!( get_headers_message.stop_hash, - BlockHash::default(), + BlockHash::all_zeros(), "Didn't send the right stop hash for initial syncing" ); @@ -932,7 +932,7 @@ pub mod test { _ => GetHeadersMessage { version: 0, locator_hashes: vec![], - stop_hash: BlockHash::default(), + stop_hash: BlockHash::all_zeros(), }, }; assert_eq!( @@ -941,7 +941,7 @@ pub mod test { ); assert_eq!( get_headers_message.stop_hash, - BlockHash::default(), + BlockHash::all_zeros(), "Didn't send the right stop hash for initial syncing" ); } diff --git a/rs/bitcoin/adapter/src/blockchainstate.rs b/rs/bitcoin/adapter/src/blockchainstate.rs index 1bda79b46ad..b0711f5e3b1 100644 --- a/rs/bitcoin/adapter/src/blockchainstate.rs +++ b/rs/bitcoin/adapter/src/blockchainstate.rs @@ -1,14 +1,16 @@ //! The module is responsible for keeping track of the blockchain state. //! use crate::{common::BlockHeight, config::Config, metrics::BlockchainStateMetrics}; -use bitcoin::{blockdata::constants::genesis_block, Block, BlockHash, BlockHeader, Network}; +use bitcoin::{ + block::Header as BlockHeader, blockdata::constants::genesis_block, Block, BlockHash, Network, +}; + use ic_btc_validation::{validate_header, HeaderStore, ValidateHeaderError}; use ic_metrics::MetricsRegistry; use std::collections::HashMap; use thiserror::Error; -/// This field contains the datatype used to store "work" of a Bitcoin blockchain -pub type Work = bitcoin::util::uint::Uint256; +use bitcoin::Work; /// Contains the necessary information about a tip. #[derive(Clone, Debug)] @@ -328,7 +330,9 @@ impl BlockchainState { /// Returns the current size of the block cache. pub fn get_block_cache_size(&self) -> usize { - self.block_cache.values().fold(0, |sum, b| b.size() + sum) + self.block_cache + .values() + .fold(0, |sum, b| b.total_size() + sum) } } @@ -338,13 +342,13 @@ impl HeaderStore for BlockchainState { .map(|cached| (cached.header, cached.height)) } - fn get_height(&self) -> BlockHeight { - self.get_active_chain_tip().height - } - fn get_initial_hash(&self) -> BlockHash { self.genesis().block_hash() } + + fn get_height(&self) -> BlockHeight { + self.get_active_chain_tip().height + } } #[cfg(test)] @@ -538,7 +542,7 @@ mod test { let initial_header = state.genesis(); let mut chain = generate_headers(initial_header.block_hash(), initial_header.time, 16, &[]); let last_header = chain.get_mut(10).unwrap(); - last_header.prev_blockhash = BlockHash::default(); + last_header.prev_blockhash = BlockHash::from_raw_hash(bitcoin::hashes::Hash::all_zeros()); let chain_hashes: Vec = chain.iter().map(|header| header.block_hash()).collect(); let last_hash = chain_hashes[10]; @@ -575,7 +579,8 @@ mod test { assert!(matches!(result, Ok(()))); // Make a block 2's merkle root invalid and try to add the block to the cache. - block_2.header.merkle_root = TxMerkleNode::default(); + block_2.header.merkle_root = + TxMerkleNode::from_raw_hash(bitcoin::hashes::Hash::all_zeros()); // Block 2's hash will now be changed because of the merkle root change. let block_2_hash = block_2.block_hash(); let result = state.add_block(block_2); @@ -632,7 +637,7 @@ mod test { state.add_block(test_state.block_1.clone()).unwrap(); state.add_block(test_state.block_2.clone()).unwrap(); - let expected_cache_size = test_state.block_1.size() + test_state.block_2.size(); + let expected_cache_size = test_state.block_1.total_size() + test_state.block_2.total_size(); let block_cache_size = state.get_block_cache_size(); assert_eq!(expected_cache_size, block_cache_size); @@ -685,7 +690,7 @@ mod test { /// Test header store `get_header` function. #[test] - fn test_headerstore_get_header() { + fn test_headerstore_get_cached_header() { let config = ConfigBuilder::new().with_network(Network::Regtest).build(); let mut state = BlockchainState::new(&config, &MetricsRegistry::default()); @@ -700,9 +705,10 @@ mod test { if h == 0 { assert_eq!(state.get_initial_hash(), state.genesis().block_hash(),); } else { + let header_node = state.get_cached_header(&header.block_hash()).unwrap(); assert_eq!( - state.get_header(&header.block_hash()).unwrap(), - (chain[h], (h + 1) as u32), + (header_node.header, header_node.height), + (chain[h], (h + 1) as u32) ); } } diff --git a/rs/bitcoin/adapter/src/cli.rs b/rs/bitcoin/adapter/src/cli.rs index 397ce60eff3..659cd3097b3 100644 --- a/rs/bitcoin/adapter/src/cli.rs +++ b/rs/bitcoin/adapter/src/cli.rs @@ -1,7 +1,7 @@ //! A parser for the command line flags and configuration file. -use crate::config::{address_limits, Config}; use clap::Parser; use http::Uri; +use ic_btc_adapter::{address_limits, Config}; use std::{fs::File, io, path::PathBuf}; use thiserror::Error; @@ -54,7 +54,7 @@ impl Cli { #[cfg(test)] pub mod test { use super::*; - use crate::config::IncomingSource; + use crate::IncomingSource; use bitcoin::Network; use std::io::Write; use std::path::PathBuf; diff --git a/rs/bitcoin/adapter/src/config.rs b/rs/bitcoin/adapter/src/config.rs index 9c69afd12f4..50bbe9ead7a 100644 --- a/rs/bitcoin/adapter/src/config.rs +++ b/rs/bitcoin/adapter/src/config.rs @@ -58,12 +58,13 @@ fn default_idle_seconds() -> u64 { /// This function is used to get the address limits for the `AddressBook` /// based on the provided `Network`. -pub(crate) fn address_limits(network: Network) -> (usize, usize) { +pub fn address_limits(network: Network) -> (usize, usize) { match network { Network::Bitcoin => (500, 2000), Network::Testnet => (100, 1000), Network::Signet => (1, 1), Network::Regtest => (1, 1), + other => unreachable!("Unsupported network: {:?}", other), } } diff --git a/rs/bitcoin/adapter/src/connection.rs b/rs/bitcoin/adapter/src/connection.rs index d5cc54493b9..3fabee72f98 100644 --- a/rs/bitcoin/adapter/src/connection.rs +++ b/rs/bitcoin/adapter/src/connection.rs @@ -1,5 +1,5 @@ use crate::addressbook::AddressEntry; -use bitcoin::network::message::NetworkMessage; +use bitcoin::p2p::message::NetworkMessage; use std::time::{Duration, SystemTime}; use thiserror::Error; use tokio::{sync::mpsc::UnboundedSender, task::JoinHandle}; diff --git a/rs/bitcoin/adapter/src/connectionmanager.rs b/rs/bitcoin/adapter/src/connectionmanager.rs index 91b5d605ba7..eeb9f8eaad6 100644 --- a/rs/bitcoin/adapter/src/connectionmanager.rs +++ b/rs/bitcoin/adapter/src/connectionmanager.rs @@ -4,11 +4,12 @@ use std::{ time::{Duration, SystemTime, UNIX_EPOCH}, }; -use bitcoin::network::{ - constants::ServiceFlags, +use bitcoin::p2p::ServiceFlags; + +use bitcoin::p2p::{ message::{CommandString, NetworkMessage}, message_network::VersionMessage, - Address, + Address, Magic, }; use ic_logger::{error, info, trace, warn, ReplicaLogger}; use rand::prelude::*; @@ -81,7 +82,7 @@ pub struct ConnectionManager { logger: ReplicaLogger, /// This field is used to provide the magic value to the raw network message. /// The magic number is used to identity the type of Bitcoin network being accessed. - magic: u32, + magic: Magic, /// This field contains the number of connections the connection manager can manage at one time. max_connections: usize, /// This field contains the number of connections the connection manager must have in order to send messages. @@ -693,7 +694,8 @@ fn connection_limits(address_book: &AddressBook) -> (usize, usize) { mod test { use super::*; use crate::config::test::ConfigBuilder; - use bitcoin::{network::constants::ServiceFlags, Network}; + use bitcoin::p2p::ServiceFlags; + use bitcoin::Network; use ic_logger::replica_logger::no_op_logger; use ic_metrics::MetricsRegistry; use std::str::FromStr; diff --git a/rs/bitcoin/adapter/src/get_successors_handler.rs b/rs/bitcoin/adapter/src/get_successors_handler.rs index a9c1a137f11..d3ddc79e9ea 100644 --- a/rs/bitcoin/adapter/src/get_successors_handler.rs +++ b/rs/bitcoin/adapter/src/get_successors_handler.rs @@ -3,7 +3,7 @@ use std::{ sync::{Arc, Mutex}, }; -use bitcoin::{Block, BlockHash, BlockHeader, Network}; +use bitcoin::{block::Header as BlockHeader, Block, BlockHash, Network}; use ic_metrics::MetricsRegistry; use tokio::sync::mpsc::Sender; use tonic::Status; @@ -168,7 +168,7 @@ fn get_successor_blocks( // Retrieve the block from the cache. match state.get_block(block_hash) { Some(block) => { - let block_size = block.size(); + let block_size = block.total_size(); if response_block_size == 0 || (response_block_size + block_size <= MAX_BLOCKS_BYTES && successor_blocks.len() < MAX_BLOCKS_LENGTH @@ -238,6 +238,7 @@ fn are_multiple_blocks_allowed(network: Network, anchor_height: BlockHeight) -> match network { Network::Bitcoin => anchor_height <= MAINNET_MAX_MULTI_BLOCK_ANCHOR_HEIGHT, Network::Testnet | Network::Signet | Network::Regtest => true, + other => unreachable!("Unsupported network: {:?}", other), } } diff --git a/rs/bitcoin/adapter/src/lib.rs b/rs/bitcoin/adapter/src/lib.rs index ffec373df3f..bddbe914e4f 100644 --- a/rs/bitcoin/adapter/src/lib.rs +++ b/rs/bitcoin/adapter/src/lib.rs @@ -1,10 +1,11 @@ -#![warn(missing_docs)] +#![cfg_attr(not(test), warn(missing_docs))] //! The Bitcoin adapter interacts with the Bitcoin P2P network to obtain blocks //! and publish transactions. Moreover, it interacts with the Bitcoin system //! component to provide blocks and collect outgoing transactions. -use bitcoin::{network::message::NetworkMessage, BlockHash, BlockHeader}; +use bitcoin::p2p::message::NetworkMessage; +use bitcoin::{block::Header as BlockHeader, BlockHash}; use ic_logger::ReplicaLogger; use ic_metrics::MetricsRegistry; use std::{ @@ -24,8 +25,6 @@ mod addressbook; mod blockchainmanager; /// This module contains the data structure for storing the current state of the Bitcoin ledger mod blockchainstate; -/// This module contains command line arguments parser. -pub mod cli; /// This module contains constants and types that are shared by many modules. mod common; /// This module contains the basic configuration struct used to start up an @@ -52,6 +51,8 @@ mod transaction_store; // malicious fork can be prioritized by a DFS, thus potentially ignoring honest forks). mod get_successors_handler; +pub use config::{address_limits, Config, IncomingSource}; + use crate::{ blockchainstate::BlockchainState, get_successors_handler::GetSuccessorsHandler, router::start_main_event_loop, rpc_server::start_grpc_server, stream::StreamEvent, diff --git a/rs/bitcoin/adapter/src/main.rs b/rs/bitcoin/adapter/src/main.rs index 3929c0f7ee3..e09112cceee 100644 --- a/rs/bitcoin/adapter/src/main.rs +++ b/rs/bitcoin/adapter/src/main.rs @@ -1,7 +1,6 @@ use clap::Parser; use ic_adapter_metrics_server::start_metrics_grpc; -use ic_btc_adapter::config::IncomingSource; -use ic_btc_adapter::{cli::Cli, start_server}; +use ic_btc_adapter::{start_server, IncomingSource}; use ic_http_endpoints_async_utils::abort_on_panic; use ic_http_endpoints_async_utils::incoming_from_nth_systemd_socket; use ic_http_endpoints_async_utils::shutdown_signal; @@ -9,6 +8,8 @@ use ic_logger::{info, new_replica_logger_from_config}; use ic_metrics::MetricsRegistry; use serde_json::to_string_pretty; +mod cli; + #[tokio::main] pub async fn main() { // We abort the whole program with a core dump if a single thread panics. @@ -16,7 +17,7 @@ pub async fn main() { // happens. abort_on_panic(); - let cli = Cli::parse(); + let cli = cli::Cli::parse(); let config = match cli.get_config() { Ok(config) => config, Err(err) => { diff --git a/rs/bitcoin/adapter/src/router.rs b/rs/bitcoin/adapter/src/router.rs index a4f10ad02da..a74d1b61fa8 100644 --- a/rs/bitcoin/adapter/src/router.rs +++ b/rs/bitcoin/adapter/src/router.rs @@ -7,7 +7,7 @@ use crate::{ Channel, ProcessBitcoinNetworkMessage, ProcessBitcoinNetworkMessageError, ProcessEvent, TransactionManagerRequest, }; -use bitcoin::network::message::NetworkMessage; +use bitcoin::p2p::message::NetworkMessage; use ic_logger::ReplicaLogger; use ic_metrics::MetricsRegistry; use std::net::SocketAddr; diff --git a/rs/bitcoin/adapter/src/rpc_server.rs b/rs/bitcoin/adapter/src/rpc_server.rs index 50827394cdb..26416410e06 100644 --- a/rs/bitcoin/adapter/src/rpc_server.rs +++ b/rs/bitcoin/adapter/src/rpc_server.rs @@ -1,9 +1,9 @@ use crate::{ blockchainstate::BlockchainState, - config::{Config, IncomingSource}, get_successors_handler::{GetSuccessorsRequest, GetSuccessorsResponse}, metrics::{ServiceMetrics, LABEL_GET_SUCCESSOR, LABEL_SEND_TRANSACTION}, - BlockchainManagerRequest, GetSuccessorsHandler, TransactionManagerRequest, + BlockchainManagerRequest, Config, GetSuccessorsHandler, IncomingSource, + TransactionManagerRequest, }; use bitcoin::{consensus::Encodable, hashes::Hash, BlockHash}; use ic_btc_service::{ diff --git a/rs/bitcoin/adapter/src/stream.rs b/rs/bitcoin/adapter/src/stream.rs index 7aab1e945ca..46252773590 100644 --- a/rs/bitcoin/adapter/src/stream.rs +++ b/rs/bitcoin/adapter/src/stream.rs @@ -1,7 +1,9 @@ +use bitcoin::io as bitcoin_io; use bitcoin::{ consensus::serialize, - network::message::RawNetworkMessage, - {consensus::encode, network::message::NetworkMessage}, + p2p::message::RawNetworkMessage, + p2p::Magic, + {consensus::encode, p2p::message::NetworkMessage}, }; use futures::TryFutureExt; use http::Uri; @@ -69,7 +71,7 @@ pub struct StreamConfig { pub logger: ReplicaLogger, /// This field is used to provide the magic value to the raw network message. /// The magic number is used to identity the type of Bitcoin network being accessed. - pub magic: u32, + pub magic: Magic, /// This field is used to receive network messages to send out to the connected /// BTC node. pub network_message_receiver: UnboundedReceiver, @@ -116,8 +118,8 @@ pub struct Stream { read_half: OwnedReadHalf, write_half: OwnedWriteHalf, /// This field is used to provide the magic value to the raw network message. - /// The magic number is used to identity the type of Bitcoin network being accessed. - magic: u32, + /// The magic number is used to identify the type of Bitcoin network being accessed. + magic: Magic, /// This field contains the receiver used to intake messages that are to be /// sent to the connected node. network_message_receiver: UnboundedReceiver, @@ -228,7 +230,9 @@ impl Stream { // If the read successfully received bytes, then the bytes are added to the // unparsed buffer to attempt another deserialize call. If no bytes found, // return the unexpected end-of-file error. - Err(encode::Error::Io(ref err)) if err.kind() == io::ErrorKind::UnexpectedEof => { + Err(encode::Error::Io(ref err)) + if err.kind() == bitcoin_io::ErrorKind::UnexpectedEof => + { let count = self .read_half .try_read(&mut self.data) @@ -248,7 +252,7 @@ impl Stream { // and then re-wrap it into a StreamError. Err(err) => { return Err(match err { - encode::Error::Io(err) => StreamError::Io(err), + encode::Error::Io(err) => StreamError::Io(err.into()), err => StreamError::Encode(err), }); } @@ -265,10 +269,7 @@ impl Stream { /// This function is used to write a network message to the connected Bitcoin /// node. async fn write_message(&mut self, network_message: NetworkMessage) -> StreamResult<()> { - let raw_network_message = RawNetworkMessage { - magic: self.magic, - payload: network_message, - }; + let raw_network_message = RawNetworkMessage::new(self.magic, network_message); let bytes = serialize(&raw_network_message); self.write_half .write_all(bytes.as_slice()) @@ -290,7 +291,7 @@ impl Stream { let raw_message = self.read_message()?; let result = self .network_message_sender - .send((self.address, raw_message.payload)) + .send((self.address, raw_message.payload().clone())) .await; if result.is_err() { return Err(StreamError::UnableToReceiveMessages); @@ -411,10 +412,10 @@ pub mod test { // Send message that exceeds size limit. tokio::spawn(async move { let (mut socket, _addr) = listener.accept().await.unwrap(); - let addr = RawNetworkMessage { - magic: network.magic(), - payload: NetworkMessage::Alert(vec![0; MAX_RAW_MESSAGE_SIZE + 10]), - }; + let addr = RawNetworkMessage::new( + network.magic(), + NetworkMessage::Alert(vec![0; MAX_RAW_MESSAGE_SIZE + 10]), + ); let mut buf = Vec::new(); let raw_addr = addr.consensus_encode(&mut buf).unwrap(); socket.write_all(&buf[..raw_addr]).await.unwrap(); @@ -492,18 +493,18 @@ pub mod test { ); // Large messgage just below limit. - let payload_large = RawNetworkMessage { - magic: network.magic(), - payload: NetworkMessage::Alert(vec![0; MAX_RAW_MESSAGE_SIZE - 30]), - }; + let payload_large = RawNetworkMessage::new( + network.magic(), + NetworkMessage::Alert(vec![0; MAX_RAW_MESSAGE_SIZE - 30]), + ); let mut buf_large = Vec::new(); let _ = payload_large.consensus_encode(&mut buf_large).unwrap(); // Message that crosses the boundary limit. - let payload_small = RawNetworkMessage { - magic: network.magic(), - payload: NetworkMessage::Alert(vec![0; 31 + STREAM_BUFFER_SIZE]), - }; + let payload_small = RawNetworkMessage::new( + network.magic(), + NetworkMessage::Alert(vec![0; 31 + STREAM_BUFFER_SIZE]), + ); let mut buf_small = Vec::new(); let _ = payload_small.consensus_encode(&mut buf_small).unwrap(); @@ -515,11 +516,11 @@ pub mod test { assert_eq!( net_rx.recv().await.unwrap(), - (address, payload_large.payload) + (address, payload_large.payload().clone()) ); assert_eq!( net_rx.recv().await.unwrap(), - (address, payload_small.payload) + (address, payload_small.payload().clone()) ); } } diff --git a/rs/bitcoin/adapter/src/stress_test.rs b/rs/bitcoin/adapter/src/stress_test.rs index 2ea6e3767d4..1c65b3876a7 100644 --- a/rs/bitcoin/adapter/src/stress_test.rs +++ b/rs/bitcoin/adapter/src/stress_test.rs @@ -1,5 +1,6 @@ use std::{convert::TryFrom, path::PathBuf, time::Duration}; +use bitcoin::Network; use bitcoin::{blockdata::constants::genesis_block, consensus::Decodable, Block, BlockHash}; use clap::Parser; use ic_btc_service::{ @@ -13,8 +14,6 @@ use tokio::{ use tonic::transport::{Channel, Endpoint, Uri}; use tower::service_fn; -use ic_btc_adapter::{cli::Cli, config::IncomingSource}; - async fn setup_channel(uds_path: PathBuf) -> Channel { Endpoint::try_from("http://[::]:50051") .expect("failed to make endpoint") @@ -36,29 +35,35 @@ async fn setup_client(uds_path: PathBuf) -> BtcServiceClient { BtcServiceClient::new(channel) } +/// This struct is use to provide a command line interface to the adapter. +#[derive(Parser)] +#[clap(version = "0.0.0", author = "DFINITY team ")] +pub struct Cli { + /// This field contains the path to the config file. + pub network: Network, + pub uds_path: PathBuf, +} + #[tokio::main] async fn main() { let cli = Cli::parse(); - let config = cli.get_config().expect("Error while reading config file."); - let uds_path = if let IncomingSource::Path(uds_path) = config.incoming_source { - uds_path - } else { - panic!("Cannot use systemd as a incoming source."); - }; let interval_sleep_ms = Duration::from_millis(1000); let request_timeout_ms = Duration::from_millis(50); - let block_0 = genesis_block(config.network); + let block_0 = genesis_block(cli.network); let mut total_processed_block_hashes: usize = 0; let mut processed_block_hashes: Vec = vec![]; let mut current_anchor = block_0.block_hash(); - let mut rpc_client = setup_client(uds_path).await; + let mut rpc_client = setup_client(cli.uds_path).await; let total_timer = Instant::now(); loop { let mut request = tonic::Request::new(BtcServiceGetSuccessorsRequest { - processed_block_hashes: processed_block_hashes.iter().map(|h| h.to_vec()).collect(), - anchor: current_anchor.to_vec(), + processed_block_hashes: processed_block_hashes + .iter() + .map(|h| h[..].to_vec()) + .collect(), + anchor: current_anchor[..].to_vec(), }); request.set_timeout(request_timeout_ms); @@ -79,7 +84,11 @@ async fn main() { let block_hashes = inner .blocks .iter() - .map(|b| Block::consensus_decode(b.as_slice()).unwrap().block_hash()) + .map(|b| { + Block::consensus_decode(&mut b.as_slice()) + .unwrap() + .block_hash() + }) .collect::>(); current_anchor = *block_hashes.last().expect("failed to get last block hash"); diff --git a/rs/bitcoin/adapter/src/transaction_store.rs b/rs/bitcoin/adapter/src/transaction_store.rs index e25d1274c04..29e2e743fa4 100644 --- a/rs/bitcoin/adapter/src/transaction_store.rs +++ b/rs/bitcoin/adapter/src/transaction_store.rs @@ -4,8 +4,8 @@ use std::{time::Duration, time::SystemTime}; use bitcoin::consensus::deserialize; use bitcoin::{ - blockdata::transaction::Transaction, hash_types::Txid, network::message::NetworkMessage, - network::message_blockdata::Inventory, + blockdata::transaction::Transaction, hash_types::Txid, p2p::message::NetworkMessage, + p2p::message_blockdata::Inventory, }; use hashlink::LinkedHashMap; use ic_logger::{debug, info, trace, ReplicaLogger}; @@ -86,7 +86,7 @@ impl TransactionStore { .txn_ops .with_label_values(&["insert", "enqueued"]) .inc(); - let txid = transaction.txid(); + let txid = transaction.compute_txid(); trace!(self.logger, "Received {} from the system component", txid); // If hashmap has `TX_CACHE_SIZE` values we remove the oldest transaction in the cache. if self.transactions.len() == TX_CACHE_SIZE { @@ -203,7 +203,10 @@ mod test { use super::*; use crate::common::test_common::TestChannel; use bitcoin::{ - blockdata::constants::genesis_block, consensus::serialize, Network, Transaction, + absolute::{LockTime, LOCK_TIME_THRESHOLD}, + blockdata::constants::genesis_block, + consensus::serialize, + Network, Transaction, }; use ic_logger::replica_logger::no_op_logger; use std::str::FromStr; @@ -245,7 +248,7 @@ mod test { let info = manager .transactions - .get_mut(&transaction.txid()) + .get_mut(&transaction.compute_txid()) .expect("transaction should be map"); info.ttl = SystemTime::now() - Duration::from_secs(TX_CACHE_TIMEOUT_PERIOD_SECS); manager.advertise_txids(&mut channel); @@ -264,12 +267,12 @@ mod test { let mut manager = make_transaction_manager(); let transaction = get_transaction(); let raw_tx = serialize(&transaction); - let txid = transaction.txid(); + let txid = transaction.compute_txid(); manager.enqueue_transaction(&raw_tx); assert_eq!(manager.transactions.len(), 1); let info = manager .transactions - .get(&transaction.txid()) + .get(&transaction.compute_txid()) .expect("transaction should be map"); assert!(info.advertised.is_empty()); // Initial broadcast @@ -304,7 +307,7 @@ mod test { // Send one transaction. This transaction should be removed first if we are at capacity. let mut first_tx = get_transaction(); - first_tx.lock_time = u32::MAX; + first_tx.lock_time = LockTime::from_height(LOCK_TIME_THRESHOLD - 1).unwrap(); let raw_tx = serialize(&first_tx); manager.enqueue_transaction(&raw_tx); @@ -312,12 +315,12 @@ mod test { // First regtest genesis transaction. let mut transaction = get_transaction(); // Alter transaction such that we get a different `txid` - transaction.lock_time = i.try_into().unwrap(); + transaction.lock_time = LockTime::from_height(i.try_into().unwrap()).unwrap(); let raw_tx = serialize(&transaction); manager.enqueue_transaction(&raw_tx); } assert_eq!(manager.transactions.len(), TX_CACHE_SIZE); - assert!(manager.transactions.get(&first_tx.txid()).is_none()); + assert!(manager.transactions.get(&first_tx.compute_txid()).is_none()); } /// This function tests that we don't readvertise transactions that were already advertised. @@ -333,7 +336,7 @@ mod test { let mut manager = make_transaction_manager(); let mut transaction = get_transaction(); - transaction.lock_time = 0; + transaction.lock_time = LockTime::ZERO; let raw_tx = serialize(&transaction); manager.enqueue_transaction(&raw_tx); manager.advertise_txids(&mut channel); @@ -344,7 +347,7 @@ mod test { .process_bitcoin_network_message( &mut channel, address, - &NetworkMessage::GetData(vec![Inventory::Transaction(transaction.txid())]), + &NetworkMessage::GetData(vec![Inventory::Transaction(transaction.compute_txid())]), ) .unwrap(); // Send transaction @@ -357,7 +360,7 @@ mod test { assert_eq!( manager .transactions - .get(&transaction.txid()) + .get(&transaction.compute_txid()) .unwrap() .advertised .len(), @@ -366,7 +369,7 @@ mod test { assert_eq!( manager .transactions - .get(&transaction.txid()) + .get(&transaction.compute_txid()) .unwrap() .advertised .get(&address), @@ -388,7 +391,7 @@ mod test { let mut manager = make_transaction_manager(); let mut transaction = get_transaction(); - transaction.lock_time = 0; + transaction.lock_time = LockTime::ZERO; let raw_tx = serialize(&transaction); manager.enqueue_transaction(&raw_tx); manager.advertise_txids(&mut channel); @@ -402,7 +405,7 @@ mod test { .process_bitcoin_network_message( &mut channel, address1, - &NetworkMessage::GetData(vec![Inventory::Transaction(transaction.txid())]), + &NetworkMessage::GetData(vec![Inventory::Transaction(transaction.compute_txid())]), ) .unwrap(); // Send transaction to peer 1 @@ -429,7 +432,7 @@ mod test { // 1. let mut transaction = get_transaction(); - transaction.lock_time = 0; + transaction.lock_time = LockTime::ZERO; let raw_tx = serialize(&transaction); manager.enqueue_transaction(&raw_tx); manager.advertise_txids(&mut channel); @@ -441,7 +444,7 @@ mod test { .process_bitcoin_network_message( &mut channel, address1, - &NetworkMessage::GetData(vec![Inventory::Transaction(transaction.txid())]), + &NetworkMessage::GetData(vec![Inventory::Transaction(transaction.compute_txid())]), ) .unwrap(); channel.pop_front().unwrap(); @@ -461,7 +464,9 @@ mod test { channel.pop_front().unwrap(), Command { address: Some(address2), - message: NetworkMessage::Inv(vec![Inventory::Transaction(transaction.txid())]) + message: NetworkMessage::Inv(vec![Inventory::Transaction( + transaction.compute_txid() + )]) } ); } @@ -479,7 +484,7 @@ mod test { let mut manager = make_transaction_manager(); let transaction = get_transaction(); let raw_tx = serialize(&transaction); - let txid = transaction.txid(); + let txid = transaction.compute_txid(); manager.enqueue_transaction(&raw_tx); assert_eq!(manager.transactions.len(), 1); manager @@ -491,7 +496,7 @@ mod test { .ok(); assert_eq!(channel.command_count(), 1); let command = channel.pop_front().unwrap(); - assert!(matches!(command.message, NetworkMessage::Tx(t) if t.txid() == txid)); + assert!(matches!(command.message, NetworkMessage::Tx(t) if t.compute_txid() == txid)); } /// This function tests the `TransactionStore::process_bitcoin_network_message(...)` method. @@ -510,8 +515,8 @@ mod test { // First regtest genesis transaction. let mut transaction = get_transaction(); // Alter transaction such that we get a different `txid` - transaction.lock_time = i.try_into().unwrap(); - let txid = transaction.txid(); + transaction.lock_time = LockTime::from_height(i.try_into().unwrap()).unwrap(); + let txid = transaction.compute_txid(); inventory.push(Inventory::Transaction(txid)); } manager @@ -536,7 +541,7 @@ mod test { let mut manager = make_transaction_manager(); let transaction = get_transaction(); let raw_tx = serialize(&transaction); - let txid = transaction.txid(); + let txid = transaction.compute_txid(); manager.enqueue_transaction(&raw_tx); manager.advertise_txids(&mut channel); manager @@ -561,12 +566,12 @@ mod test { ); let command = channel.pop_front().unwrap(); - assert!(matches!(command.message, NetworkMessage::Tx(t) if t.txid() == txid)); + assert!(matches!(command.message, NetworkMessage::Tx(t) if t.compute_txid() == txid)); manager.enqueue_transaction(&raw_tx); let info = manager .transactions - .get_mut(&transaction.txid()) + .get_mut(&transaction.compute_txid()) .expect("transaction should be in the map"); info.ttl = SystemTime::now() - Duration::from_secs(TX_CACHE_TIMEOUT_PERIOD_SECS); manager.advertise_txids(&mut channel); diff --git a/rs/bitcoin/adapter/test_utils/src/bitcoind.rs b/rs/bitcoin/adapter/test_utils/src/bitcoind.rs index da9c0c83365..b3ef7a9b94c 100644 --- a/rs/bitcoin/adapter/test_utils/src/bitcoind.rs +++ b/rs/bitcoin/adapter/test_utils/src/bitcoind.rs @@ -5,16 +5,21 @@ use std::{ sync::Arc, }; +use bitcoin::p2p::{Magic, ServiceFlags}; + use bitcoin::{ + block::Header as BlockHeader, consensus::{deserialize_partial, encode, serialize}, - network::{ - constants::ServiceFlags, + p2p::{ message::{NetworkMessage, RawNetworkMessage}, message_blockdata::{GetHeadersMessage, Inventory}, message_network::VersionMessage, }, - Block, BlockHash, BlockHeader, + Block, BlockHash, }; + +use bitcoin::io as bitcoin_io; + use tokio::{ io::{AsyncReadExt, AsyncWriteExt}, net::{TcpListener, TcpStream}, @@ -24,10 +29,10 @@ const MINIMUM_PROTOCOL_VERSION: u32 = 70001; async fn write_network_message( socket: &mut TcpStream, - magic: u32, + magic: Magic, payload: NetworkMessage, ) -> io::Result<()> { - let res = RawNetworkMessage { magic, payload }; + let res = RawNetworkMessage::new(magic, payload); let serialized = serialize(&res); socket.write_all(&serialized).await?; socket.flush().await?; @@ -36,8 +41,8 @@ async fn write_network_message( async fn handle_getdata( socket: &mut TcpStream, - msg: Vec, - magic: u32, + msg: &[Inventory], + magic: Magic, blocks: Arc>, ) -> io::Result<()> { for inv in msg.iter() { @@ -57,11 +62,15 @@ async fn handle_getdata( Ok(()) } -async fn handle_ping(socket: &mut TcpStream, val: u64, magic: u32) -> io::Result<()> { +async fn handle_ping(socket: &mut TcpStream, val: u64, magic: Magic) -> io::Result<()> { write_network_message(socket, magic, NetworkMessage::Pong(val)).await } -async fn handle_version(socket: &mut TcpStream, v: VersionMessage, magic: u32) -> io::Result<()> { +async fn handle_version( + socket: &mut TcpStream, + v: &VersionMessage, + magic: Magic, +) -> io::Result<()> { if v.version < MINIMUM_PROTOCOL_VERSION { let err = io::Error::new(ErrorKind::Other, "Protocol version too low"); return Err(err); @@ -73,14 +82,14 @@ async fn handle_version(socket: &mut TcpStream, v: VersionMessage, magic: u32) - Ok(()) } -async fn handle_getaddr(socket: &mut TcpStream, magic: u32) -> io::Result<()> { +async fn handle_getaddr(socket: &mut TcpStream, magic: Magic) -> io::Result<()> { write_network_message(socket, magic, NetworkMessage::Addr(vec![])).await } async fn handle_getheaders( socket: &mut TcpStream, - msg: GetHeadersMessage, - magic: u32, + msg: &GetHeadersMessage, + magic: Magic, cached_headers: Arc>, children: Arc>>, ) -> io::Result<()> { @@ -89,18 +98,18 @@ async fn handle_getheaders( let locator = { let mut found = None; - for locator in msg.locator_hashes { - if cached_headers.contains_key(&locator) { - found = Some(locator); + for locator in &msg.locator_hashes { + if cached_headers.contains_key(locator) { + found = Some(*locator); break; } } - found.unwrap_or( + found.unwrap_or_else(|| { // If no locators are found, use the genesis hash. "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f" .parse() - .unwrap(), - ) + .unwrap() + }) }; let mut queue = VecDeque::new(); @@ -183,22 +192,22 @@ impl FakeBitcoind { match deserialize_partial::(&unparsed) { Ok((raw, cnt)) => { let handler_result = - match raw.payload { + match raw.payload() { NetworkMessage::Version(v) => { - handle_version(&mut socket, v, raw.magic).await + handle_version(&mut socket, v, *raw.magic()).await } NetworkMessage::Verack => Ok(()), NetworkMessage::GetAddr => { - handle_getaddr(&mut socket, raw.magic).await + handle_getaddr(&mut socket, *raw.magic()).await } NetworkMessage::GetHeaders(msg) => { - handle_getheaders(&mut socket, msg, raw.magic, cached_headers.clone(), children.clone()).await + handle_getheaders(&mut socket, msg, *raw.magic(), cached_headers.clone(), children.clone()).await } NetworkMessage::GetData(msg) => { - handle_getdata(&mut socket, msg, raw.magic, blocks.clone()).await + handle_getdata(&mut socket, msg, *raw.magic(), blocks.clone()).await } NetworkMessage::Ping(val) => { - handle_ping(&mut socket, val, raw.magic).await + handle_ping(&mut socket, *val, *raw.magic()).await } smth => panic!("Unexpected NetworkMessage: {:?}", smth), }; @@ -208,7 +217,7 @@ impl FakeBitcoind { unparsed.drain(..cnt); } Err(encode::Error::Io(ref err)) // Received incomplete message - if err.kind() == std::io::ErrorKind::UnexpectedEof => + if err.kind() == bitcoin_io::ErrorKind::UnexpectedEof => { break } diff --git a/rs/bitcoin/adapter/test_utils/src/lib.rs b/rs/bitcoin/adapter/test_utils/src/lib.rs index 17c4c81ed9f..e8a52da88c6 100644 --- a/rs/bitcoin/adapter/test_utils/src/lib.rs +++ b/rs/bitcoin/adapter/test_utils/src/lib.rs @@ -1,8 +1,10 @@ use std::collections::HashSet; use bitcoin::{ - consensus::deserialize, util::uint::Uint256, Block, BlockHash, BlockHeader, Transaction, - TxMerkleNode, + block::{Header as BlockHeader, Version}, + consensus::deserialize, + hashes::Hash, + Block, BlockHash, Target, Transaction, TxMerkleNode, }; use hex::FromHex; use rand::{prelude::StdRng, Rng, SeedableRng}; @@ -21,12 +23,7 @@ pub const BLOCK_2_ENCODED: &str = "010000004860eb18bf1b1620e37e9490fc8a427514416 /// an overflow occurs if bits is set to 0. /// /// https://github.com/bitcoin/bitcoin/blame/master/src/chainparams.cpp#L402 -const TARGET: Uint256 = Uint256([ - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0x7fffffffffffffff, -]); +const TARGET: Target = Target::MAX_ATTAINABLE_REGTEST; fn decode_block(hex_str: &str) -> Block { let encoded_block_1 = Vec::from_hex(hex_str).expect("failed to covert hex to vec"); @@ -49,11 +46,11 @@ pub fn headers_to_hashes(headers: &[BlockHeader]) -> Vec { fn large_block(prev_blockhash: &BlockHash, prev_time: u32, tx: Transaction) -> Block { let mut block = Block { header: BlockHeader { - version: 1, + version: Version::ONE, prev_blockhash: *prev_blockhash, - merkle_root: TxMerkleNode::default(), + merkle_root: TxMerkleNode::all_zeros(), time: prev_time + gen_time_delta(), - bits: BlockHeader::compact_target_from_u256(&TARGET), + bits: TARGET.to_compact_lossy(), nonce: 0, }, txdata: vec![], @@ -64,7 +61,9 @@ fn large_block(prev_blockhash: &BlockHash, prev_time: u32, tx: Transaction) -> B block.txdata.push(tx.clone()); } - block.header.merkle_root = block.compute_merkle_root().unwrap_or_default(); + block.header.merkle_root = block + .compute_merkle_root() + .unwrap_or(TxMerkleNode::all_zeros()); solve_proof_of_work(&mut block.header); block } @@ -124,11 +123,11 @@ pub fn generate_headers( /// This helper generates a single header with a given previous blockhash. pub fn generate_header(prev_blockhash: BlockHash, prev_time: u32, nonce: u32) -> BlockHeader { let mut header = BlockHeader { - version: 1, + version: Version::ONE, prev_blockhash, - merkle_root: TxMerkleNode::default(), + merkle_root: TxMerkleNode::all_zeros(), time: prev_time + gen_time_delta(), - bits: BlockHeader::compact_target_from_u256(&TARGET), + bits: TARGET.to_compact_lossy(), nonce, }; @@ -145,7 +144,7 @@ fn gen_time_delta() -> u32 { /// This method is used to solve a header's proof of work puzzle. fn solve_proof_of_work(header: &mut BlockHeader) { let target = header.target(); - while header.validate_pow(&target).is_err() { + while header.validate_pow(target).is_err() { header.nonce += 1; } } diff --git a/rs/bitcoin/adapter/tests/adapter_test.rs b/rs/bitcoin/adapter/tests/adapter_test.rs index 0192a8d99e3..693f3229c3a 100644 --- a/rs/bitcoin/adapter/tests/adapter_test.rs +++ b/rs/bitcoin/adapter/tests/adapter_test.rs @@ -1,10 +1,7 @@ use bitcoin::{consensus::encode::deserialize, Address, Amount, Block, BlockHash}; use bitcoincore_rpc::{bitcoincore_rpc_json::CreateRawTransactionInput, Auth, Client, RpcApi}; use bitcoind::{BitcoinD, Conf, P2P}; -use ic_btc_adapter::{ - config::{Config, IncomingSource}, - start_server, -}; +use ic_btc_adapter::{start_server, Config, IncomingSource}; use ic_btc_adapter_client::setup_bitcoin_adapter_clients; use ic_btc_interface::Network; use ic_btc_replica_types::{ @@ -192,7 +189,7 @@ fn start_adapter_and_client( .unwrap(); if let AdapterState::Active = adapter_state { // We send this request to make sure the adapter is not idle. - let _ = make_get_successors_request(&res.0, anchor.to_vec(), vec![]); + let _ = make_get_successors_request(&res.0, anchor[..].to_vec(), vec![]); } res @@ -379,7 +376,9 @@ fn sync_blocks_at_once( } fn get_blackhole_address() -> Address { - Address::from_str("mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn").unwrap() + Address::from_str("mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn") + .unwrap() + .assume_checked() } fn create_alice_and_bob_wallets(bitcoind: &BitcoinD) -> (Client, Client, Address, Address) { @@ -401,8 +400,14 @@ fn create_alice_and_bob_wallets(bitcoind: &BitcoinD) -> (Client, Client, Address .create_wallet("bob", None, None, None, None) .unwrap(); - let alice_address = alice_client.get_new_address(None, None).unwrap(); - let bob_address = bob_client.get_new_address(None, None).unwrap(); + let alice_address = alice_client + .get_new_address(None, None) + .unwrap() + .assume_checked(); + let bob_address = bob_client + .get_new_address(None, None) + .unwrap() + .assume_checked(); (alice_client, bob_client, alice_address, bob_address) } @@ -411,7 +416,7 @@ fn fund_with_btc(to_fund_client: &Client, to_fund_address: &Address) { let initial_amount = to_fund_client .get_received_by_address(to_fund_address, Some(0)) .unwrap() - .as_btc(); + .to_btc(); to_fund_client .generate_to_address(1, to_fund_address) @@ -526,7 +531,7 @@ fn test_receives_blocks() { assert_eq!(0, client.get_blockchain_info().unwrap().blocks); - let address = client.get_new_address(None, None).unwrap(); + let address = client.get_new_address(None, None).unwrap().assume_checked(); client.generate_to_address(150, &address).unwrap(); @@ -709,7 +714,7 @@ fn test_receives_new_3rd_party_txs() { let blocks = sync_until_end_block(&adapter_client, &alice_client, 101, &mut vec![], 15); assert_eq!(blocks.len(), 1); - assert!(blocks[0].txdata.iter().any(|tx| tx.txid() == txid)); + assert!(blocks[0].txdata.iter().any(|tx| tx.compute_txid() == txid)); } /// Ensures the client (replica) can send a transaction (1 BTC from Alice to Bob) using the gRPC service. @@ -821,13 +826,19 @@ fn test_receives_blocks_from_forks() { wait_for_connection(&client1, 2); wait_for_connection(&client2, 2); - let address1 = client1.get_new_address(None, None).unwrap(); + let address1 = client1 + .get_new_address(None, None) + .unwrap() + .assume_checked(); client1.generate_to_address(25, &address1).unwrap(); wait_for_blocks(&client1, 25); wait_for_blocks(&client2, 25); - let address2 = client2.get_new_address(None, None).unwrap(); + let address2 = client2 + .get_new_address(None, None) + .unwrap() + .assume_checked(); client2.generate_to_address(25, &address2).unwrap(); wait_for_blocks(&client1, 50); @@ -889,7 +900,10 @@ fn test_bfs_order() { wait_for_connection(&client1, 2); wait_for_connection(&client2, 2); - let address1 = client1.get_new_address(None, None).unwrap(); + let address1 = client1 + .get_new_address(None, None) + .unwrap() + .assume_checked(); let shared_blocks = client1.generate_to_address(5, &address1).unwrap(); wait_for_blocks(&client1, 5); @@ -905,7 +919,10 @@ fn test_bfs_order() { let fork1 = client1.generate_to_address(15, &address1).unwrap(); - let address2 = client2.get_new_address(None, None).unwrap(); + let address2 = client2 + .get_new_address(None, None) + .unwrap() + .assume_checked(); let fork2 = client2.generate_to_address(15, &address2).unwrap(); wait_for_blocks(&client1, 20); diff --git a/rs/bitcoin/checker/BUILD.bazel b/rs/bitcoin/checker/BUILD.bazel index 2944598a22e..e881302c0d4 100644 --- a/rs/bitcoin/checker/BUILD.bazel +++ b/rs/bitcoin/checker/BUILD.bazel @@ -17,7 +17,7 @@ rust_library( crate_name = "ic_btc_checker", deps = [ # Keep sorted. - "@crate_index//:bitcoin_0_32", + "@crate_index//:bitcoin", "@crate_index//:candid", "@crate_index//:serde", ], @@ -62,7 +62,7 @@ rust_canister( "//rs/rust_canisters/http_types", "@crate_index//:askama", "@crate_index//:base64", - "@crate_index//:bitcoin_0_32", + "@crate_index//:bitcoin", "@crate_index//:candid", "@crate_index//:candid_parser", "@crate_index//:ciborium", @@ -73,7 +73,6 @@ rust_canister( "@crate_index//:ic-cdk", "@crate_index//:ic-metrics-encoder", "@crate_index//:ic-stable-structures", - "@crate_index//:num-traits", "@crate_index//:serde", "@crate_index//:serde_json", "@crate_index//:time", @@ -98,6 +97,7 @@ rust_ic_test( # Keep sorted. ":btc_checker_lib", "//:pocket-ic-server", + "//packages/ic-metrics-assert:ic-metrics-assert_pocket_ic", "//packages/pocket-ic", "//rs/rust_canisters/http_types", "//rs/test_utilities/load_wasm", @@ -107,5 +107,6 @@ rust_ic_test( "@crate_index//:candid", "@crate_index//:ic-btc-interface", "@crate_index//:ic-cdk", + "@crate_index//:regex", ], ) diff --git a/rs/bitcoin/checker/Cargo.toml b/rs/bitcoin/checker/Cargo.toml index 972c1058d08..59d73720b1c 100644 --- a/rs/bitcoin/checker/Cargo.toml +++ b/rs/bitcoin/checker/Cargo.toml @@ -13,7 +13,7 @@ path = "src/main.rs" [dependencies] askama = { workspace = true } base64 = { workspace = true } -bitcoin = { version = "0.32.2", default-features = false } +bitcoin = { workspace = true } candid = { workspace = true } ciborium = { workspace = true } futures = { workspace = true } @@ -24,15 +24,15 @@ ic-canisters-http-types = { path = "../../rust_canisters/http_types" } ic-cdk = { workspace = true } ic-metrics-encoder = "1.1" ic-stable-structures = { workspace = true } -num-traits = { workspace = true } serde = { workspace = true } -serde_json = {workspace = true } +serde_json = { workspace = true } time = { workspace = true } url = { workspace = true } [dev-dependencies] candid_parser = { workspace = true } ic-base-types = { path = "../../types/base_types" } +ic-metrics-assert = { path = "../../../packages/ic-metrics-assert", features = ["pocket_ic"] } ic-types = { path = "../../types/types" } ic-test-utilities-load-wasm = { path = "../../test_utilities/load_wasm" } ic-universal-canister = { path = "../../universal_canister/lib" } diff --git a/rs/bitcoin/checker/src/main.rs b/rs/bitcoin/checker/src/main.rs index 134b9491c47..e81f92263fc 100644 --- a/rs/bitcoin/checker/src/main.rs +++ b/rs/bitcoin/checker/src/main.rs @@ -1,19 +1,20 @@ use bitcoin::{consensus::Decodable, Address, Transaction}; +use candid::Nat; use ic_btc_checker::{ blocklist::is_blocked, get_tx_cycle_cost, BtcNetwork, CheckAddressArgs, CheckAddressResponse, CheckArg, CheckMode, CheckTransactionArgs, CheckTransactionIrrecoverableError, CheckTransactionResponse, CheckTransactionRetriable, CheckTransactionStatus, CheckTransactionStrArgs, CHECK_TRANSACTION_CYCLES_REQUIRED, - CHECK_TRANSACTION_CYCLES_SERVICE_FEE, + CHECK_TRANSACTION_CYCLES_SERVICE_FEE, RETRY_MAX_RESPONSE_BYTES, }; use ic_btc_interface::Txid; use ic_canister_log::{export as export_logs, log}; use ic_canisters_http_types as http; use ic_cdk::api::call::RejectionCode; use ic_cdk::api::management_canister::http_request::{HttpResponse, TransformArgs}; -use num_traits::cast::ToPrimitive; use std::cell::RefCell; use std::collections::BTreeMap; +use std::fmt; use std::str::FromStr; mod dashboard; @@ -26,12 +27,28 @@ use fetch::{FetchEnv, FetchResult, TryFetchResult}; use logs::{Log, LogEntry, Priority, DEBUG, WARN}; use state::{get_config, set_config, Config, FetchGuardError, HttpGetTxError}; +#[derive(PartialOrd, Ord, PartialEq, Eq)] +enum HttpsOutcallStatus { + ResponseTooLarge, + IcError(RejectionCode), + HttpStatusCode(Nat), +} + +impl fmt::Display for HttpsOutcallStatus { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::ResponseTooLarge => write!(f, "ResponseTooLarge"), + Self::IcError(rejection_code) => write!(f, "IcError({})", *rejection_code as i32), + Self::HttpStatusCode(status_code) => write!(f, "HttpStatusCode({})", status_code), + } + } +} + #[derive(Default)] struct Stats { - https_outcall_status: BTreeMap<(String, u16), u64>, + https_outcall_status: BTreeMap<(String, HttpsOutcallStatus), u64>, http_response_size: BTreeMap, check_transaction_count: u64, - check_address_count: u64, } thread_local! { @@ -57,7 +74,6 @@ fn check_address(args: CheckAddressArgs) -> CheckAddressResponse { ic_cdk::trap(&format!("Not a Bitcoin {} address: {}", btc_network, err)) }); - STATS.with(|s| s.borrow_mut().check_transaction_count += 1); match config.check_mode { CheckMode::AcceptAll => CheckAddressResponse::Passed, CheckMode::RejectAll => CheckAddressResponse::Failed, @@ -232,7 +248,7 @@ fn http_request(req: http::HttpRequest) -> http::HttpResponse { .value( &[ ("provider", provider.as_str()), - ("status", status.to_string().as_str()), + ("status", status.to_string().as_ref()), ], *count as f64, ) @@ -259,11 +275,6 @@ fn http_request(req: http::HttpRequest) -> http::HttpResponse { &[("type", "check_transaction")], stats.check_transaction_count as f64, ) - .unwrap() - .value( - &[("type", "check_address")], - stats.check_address_count as f64, - ) .unwrap(); }); @@ -373,7 +384,10 @@ impl FetchEnv for BtcCheckerCanisterEnv { let mut stat = s.borrow_mut(); *stat .https_outcall_status - .entry((provider.name(), response.status.0.to_u16().unwrap())) + .entry(( + provider.name(), + HttpsOutcallStatus::HttpStatusCode(response.status.clone()), + )) .or_default() += 1; // Calculate size bucket as a series of power of 2s. // Note that the max is bounded by `max_response_bytes`, which fits `u32`. @@ -421,8 +435,26 @@ impl FetchEnv for BtcCheckerCanisterEnv { } Ok(tx) } - Err((r, m)) if is_response_too_large(&r, &m) => Err(HttpGetTxError::ResponseTooLarge), + Err((r, m)) if is_response_too_large(&r, &m) => { + if max_response_bytes >= RETRY_MAX_RESPONSE_BYTES { + STATS.with(|s| { + let mut stat = s.borrow_mut(); + *stat + .https_outcall_status + .entry((provider.name(), HttpsOutcallStatus::ResponseTooLarge)) + .or_default() += 1; + }); + } + Err(HttpGetTxError::ResponseTooLarge) + } Err((r, m)) => { + STATS.with(|s| { + let mut stat = s.borrow_mut(); + *stat + .https_outcall_status + .entry((provider.name(), HttpsOutcallStatus::IcError(r))) + .or_default() += 1; + }); log!( DEBUG, "The http_request resulted into error. RejectionCode: {r:?}, Error: {m}, Request: {request:?}" diff --git a/rs/bitcoin/checker/tests/tests.rs b/rs/bitcoin/checker/tests/tests.rs index 50be26eb384..426816e3e82 100644 --- a/rs/bitcoin/checker/tests/tests.rs +++ b/rs/bitcoin/checker/tests/tests.rs @@ -1,4 +1,4 @@ -use candid::{decode_one, CandidType, Deserialize, Encode, Principal}; +use candid::{decode_one, Encode, Principal}; use ic_base_types::PrincipalId; use ic_btc_checker::{ blocklist, get_tx_cycle_cost, BtcNetwork, CheckAddressArgs, CheckAddressResponse, CheckArg, @@ -8,15 +8,19 @@ use ic_btc_checker::{ INITIAL_MAX_RESPONSE_BYTES, }; use ic_btc_interface::Txid; +use ic_canisters_http_types::{HttpRequest, HttpResponse}; +use ic_cdk::api::call::RejectionCode; +use ic_metrics_assert::{MetricsAssert, PocketIcHttpQuery}; use ic_test_utilities_load_wasm::load_wasm; use ic_types::Cycles; use ic_universal_canister::{call_args, wasm, UNIVERSAL_CANISTER_WASM}; +use pocket_ic::management_canister::CanisterId; use pocket_ic::{ common::rest::{ - CanisterHttpHeader, CanisterHttpReply, CanisterHttpRequest, CanisterHttpResponse, - MockCanisterHttpResponse, RawMessageId, + CanisterHttpHeader, CanisterHttpReject, CanisterHttpReply, CanisterHttpRequest, + CanisterHttpResponse, MockCanisterHttpResponse, RawMessageId, }, - query_candid, PocketIc, PocketIcBuilder, UserError, WasmResult, + query_candid, PocketIc, PocketIcBuilder, RejectResponse, }; use std::str::FromStr; @@ -98,7 +102,7 @@ impl Setup { method: &str, args: Vec, cycles: u128, - ) -> Result { + ) -> Result { let payload = wasm() .call_with_cycles( PrincipalId(self.btc_checker_canister), @@ -114,13 +118,6 @@ impl Setup { } } -fn decode<'a, T: CandidType + Deserialize<'a>>(result: &'a WasmResult) -> T { - match result { - WasmResult::Reply(bytes) => decode_one(bytes).unwrap(), - WasmResult::Reject(msg) => panic!("unexpected reject: {}", msg), - } -} - #[test] fn test_get_tx_cycle_cost() { assert_eq!( @@ -364,7 +361,7 @@ fn test_check_transaction_passed() { .expect("the fetch request didn't finish"); assert!(matches!( - decode::(&result), + decode_one(&result).unwrap(), CheckTransactionResponse::Passed )); @@ -374,6 +371,9 @@ fn test_check_transaction_passed() { let actual_cost = cycles_before - cycles_after; assert!(actual_cost > expected_cost); assert!(actual_cost - expected_cost < UNIVERSAL_CANISTER_CYCLE_MARGIN); + MetricsAssert::from_http_query(&setup).assert_contains_metric_matching( + r#"btc_check_requests_total\{type=\"check_transaction\"\} 1 \d+"#, + ); }; // With default installation @@ -408,7 +408,7 @@ fn test_check_transaction_passed() { .expect("the fetch request didn't finish"); assert!(matches!( - decode::(&result), + decode_one(&result).unwrap(), CheckTransactionResponse::Failed(addresses) if addresses.is_empty() ),); let cycles_after = env.cycle_balance(setup.caller); @@ -416,6 +416,9 @@ fn test_check_transaction_passed() { let actual_cost = cycles_before - cycles_after; assert!(actual_cost > expected_cost); assert!(actual_cost - expected_cost < UNIVERSAL_CANISTER_CYCLE_MARGIN); + MetricsAssert::from_http_query(&setup).assert_contains_metric_matching( + r#"btc_check_requests_total\{type=\"check_transaction\"\} 1 \d+"#, + ); // Test CheckMode::AcceptAll env.tick(); @@ -446,7 +449,7 @@ fn test_check_transaction_passed() { .expect("the fetch request didn't finish"); assert!(matches!( - decode::(&result), + decode_one(&result).unwrap(), CheckTransactionResponse::Passed ),); let cycles_after = env.cycle_balance(setup.caller); @@ -457,6 +460,9 @@ fn test_check_transaction_passed() { actual_cost - expected_cost < UNIVERSAL_CANISTER_CYCLE_MARGIN, "actual_cost: {actual_cost}, expected_cost: {expected_cost}" ); + MetricsAssert::from_http_query(&setup).assert_contains_metric_matching( + r#"btc_check_requests_total\{type=\"check_transaction\"\} 1 \d+"#, + ); // Test CheckMode::Normal env.tick(); @@ -497,7 +503,7 @@ fn test_check_transaction_error() { .await_call(call_id) .expect("the fetch request didn't finish"); assert!(matches!( - decode::(&result), + decode_one(&result).unwrap(), CheckTransactionResponse::Unknown(CheckTransactionStatus::NotEnoughCycles), )); @@ -515,7 +521,7 @@ fn test_check_transaction_error() { .await_call(call_id) .expect("the fetch request didn't finish"); assert!(matches!( - decode::(&result), + decode_one(&result).unwrap(), CheckTransactionResponse::Unknown(CheckTransactionStatus::NotEnoughCycles), )); @@ -553,7 +559,7 @@ fn test_check_transaction_error() { .expect("the fetch request didn't finish"); // 500 error is retriable assert!(matches!( - decode::(&result), + decode_one(&result).unwrap(), CheckTransactionResponse::Unknown(CheckTransactionStatus::Retriable( CheckTransactionRetriable::TransientInternalError(msg) )) if msg.contains("received code 500") @@ -593,7 +599,7 @@ fn test_check_transaction_error() { .expect("the fetch request didn't finish"); // 404 error is retriable too assert!(matches!( - decode::(&result), + decode_one(&result).unwrap(), CheckTransactionResponse::Unknown(CheckTransactionStatus::Retriable( CheckTransactionRetriable::TransientInternalError(msg) )) if msg.contains("received code 404") @@ -605,6 +611,45 @@ fn test_check_transaction_error() { assert!(actual_cost > expected_cost); assert!(actual_cost - expected_cost < UNIVERSAL_CANISTER_CYCLE_MARGIN); + // Test for CanisterHttpReject error + let cycles_before = setup.env.cycle_balance(setup.caller); + let call_id = setup + .submit_btc_checker_call( + "check_transaction", + Encode!(&CheckTransactionArgs { txid: txid.clone() }).unwrap(), + CHECK_TRANSACTION_CYCLES_REQUIRED, + ) + .expect("submit_call failed to return call id"); + let canister_http_requests = tick_until_next_request(&setup.env); + setup + .env + .mock_canister_http_response(MockCanisterHttpResponse { + subnet_id: canister_http_requests[0].subnet_id, + request_id: canister_http_requests[0].request_id, + response: CanisterHttpResponse::CanisterHttpReject(CanisterHttpReject { + reject_code: RejectionCode::SysTransient as u64, + message: "Failed to directly connect".to_string(), + }), + additional_responses: vec![], + }); + let result = setup + .env + .await_call(call_id) + .expect("the fetch request didn't finish"); + // Reject error is retriable too + assert!(matches!( + decode_one(&result).unwrap(), + CheckTransactionResponse::Unknown(CheckTransactionStatus::Retriable( + CheckTransactionRetriable::TransientInternalError(msg) + )) if msg.contains("Failed to directly connect") + )); + let cycles_after = setup.env.cycle_balance(setup.caller); + let expected_cost = CHECK_TRANSACTION_CYCLES_SERVICE_FEE + + get_tx_cycle_cost(INITIAL_MAX_RESPONSE_BYTES, TEST_SUBNET_NODES); + let actual_cost = cycles_before - cycles_after; + assert!(actual_cost > expected_cost); + assert!(actual_cost - expected_cost < UNIVERSAL_CANISTER_CYCLE_MARGIN); + // Test for malformatted transaction data let cycles_before = setup.env.cycle_balance(setup.caller); let call_id = setup @@ -633,7 +678,7 @@ fn test_check_transaction_error() { .expect("the fetch request didn't finish"); // malformated tx error is retriable assert!(matches!( - decode::(&result), + decode_one(&result).unwrap(), CheckTransactionResponse::Unknown(CheckTransactionStatus::Retriable( CheckTransactionRetriable::TransientInternalError(msg) )) if msg.contains("TxEncoding") @@ -660,7 +705,7 @@ fn test_check_transaction_error() { .await_call(call_id) .expect("the fetch request didn't finish"); assert!(matches!( - decode::(&result), + decode_one(&result).unwrap(), CheckTransactionResponse::Unknown(CheckTransactionStatus::Error( CheckTransactionIrrecoverableError::InvalidTransactionId(_) )) @@ -691,7 +736,7 @@ fn test_check_transaction_error() { .await_call(call_id) .expect("the fetch request didn't finish"); assert!(matches!( - decode::(&result), + decode_one(&result).unwrap(), CheckTransactionResponse::Unknown(CheckTransactionStatus::Error( CheckTransactionIrrecoverableError::InvalidTransactionId(_) )) @@ -702,6 +747,23 @@ fn test_check_transaction_error() { let actual_cost = cycles_before - cycles_after; assert!(actual_cost > expected_cost); assert!(actual_cost - expected_cost < UNIVERSAL_CANISTER_CYCLE_MARGIN); + + MetricsAssert::from_http_query(&setup) + .assert_contains_metric_matching( + r#"btc_check_requests_total\{type=\"check_transaction\"\} 5 \d+"#, + ) + .assert_contains_metric_matching( + r#"btc_checker_http_calls_total\{provider=\"[a-z.]*\",status=\"HttpStatusCode\(500\)\"\} 1 \d+"#, + ) + .assert_contains_metric_matching( + r#"btc_checker_http_calls_total\{provider=\"[a-z.]*\",status=\"HttpStatusCode\(200\)\"\} 1 \d+"#, + ) + .assert_contains_metric_matching( + r#"btc_checker_http_calls_total\{provider=\"[a-z.]*\",status=\"HttpStatusCode\(404\)\"\} 1 \d+"#, + ) + .assert_contains_metric_matching( + r#"btc_checker_http_calls_total\{provider=\"[a-z.]*\",status=\"IcError\(2\)\"\} 1 \d+"#, + ); } fn tick_until_next_request(env: &PocketIc) -> Vec { @@ -723,45 +785,44 @@ fn tick_until_next_request(env: &PocketIc) -> Vec { #[test] fn should_query_logs_and_metrics() { + let setup = Setup::new(BtcNetwork::Mainnet); + make_http_query(&setup, "/metrics"); + make_http_query(&setup, "/logs"); +} + +fn make_http_query>(setup: &Setup, url: U) -> Vec { use candid::Decode; + let request = HttpRequest { + method: "GET".to_string(), + url: url.into(), + headers: Default::default(), + body: Default::default(), + }; - let setup = Setup::new(BtcNetwork::Mainnet); - test_http_query(&setup, "/metrics"); - test_http_query(&setup, "/logs"); - - fn test_http_query>(setup: &Setup, url: U) { - let request = ic_canisters_http_types::HttpRequest { - method: "GET".to_string(), - url: url.into(), - headers: Default::default(), - body: Default::default(), - }; + let response = Decode!( + &setup + .env + .query_call( + setup.btc_checker_canister, + Principal::anonymous(), + "http_request", + Encode!(&request).expect("failed to encode HTTP request"), + ) + .expect("failed to query get_transactions on the ledger"), + HttpResponse + ) + .unwrap(); - let response = Decode!( - &assert_reply( - setup - .env - .query_call( - setup.btc_checker_canister, - Principal::anonymous(), - "http_request", - Encode!(&request).expect("failed to encode HTTP request"), - ) - .expect("failed to query get_transactions on the ledger") - ), - ic_canisters_http_types::HttpResponse - ) - .unwrap(); + assert_eq!(response.status_code, 200_u16); + response.body.into_vec() +} - assert_eq!(response.status_code, 200_u16); +impl PocketIcHttpQuery for &Setup { + fn get_pocket_ic(&self) -> &PocketIc { + &self.env } -} -fn assert_reply(result: WasmResult) -> Vec { - match result { - WasmResult::Reply(bytes) => bytes, - WasmResult::Reject(reject) => { - panic!("Expected a successful reply, got a reject: {}", reject) - } + fn get_canister_id(&self) -> CanisterId { + self.btc_checker_canister } } diff --git a/rs/bitcoin/ckbtc/mainnet/README.md b/rs/bitcoin/ckbtc/mainnet/README.md index 8c746d4c706..b412d6e094d 100644 --- a/rs/bitcoin/ckbtc/mainnet/README.md +++ b/rs/bitcoin/ckbtc/mainnet/README.md @@ -3,7 +3,6 @@ Root canister id: `r7inp-6aaaa-aaaaa-aaabq-cai`. Subnet: `pzp6e-ekpqk-3c5x7-2h6so-njoeq-mt45d-h3h6c-q3mxf-vpeq5-fk5o7-yae` - ## Installing the minter ([`mqygn-kiaaa-aaaar-qaadq-cai`](https://dashboard.internetcomputer.org/canister/mqygn-kiaaa-aaaar-qaadq-cai)) Notes on init args: @@ -81,7 +80,7 @@ Notes on init args: * The transfer fee is 10 ckBTC Satoshis. * There are no initial balances: the minter is responsible for minting all ckBTC. * Archive max memory size is 3 GiB, or 3_221_225_472 bytes. We can afford to use that much memory because archives store transactions in stable memory. -* The `max_memo_length` was last udpated to 80 in [NNS proposal 123422](https://dashboard.internetcomputer.org/proposal/123422). +* The `max_memo_length` was last updated to 80 in [NNS proposal 123422](https://dashboard.internetcomputer.org/proposal/123422). The metadata contains the official ckBTC logo. @@ -138,7 +137,7 @@ bazel build //rs/registry/admin:ic-admin Encoding the init args: ```shell -didc encode -d ../../../ledger_suite/icrc1/index/index.did -t '(InitArgs)' '(record { ledger_id = principal "mxzaz-hqaaa-aaaar-qaada-cai" })' | xxd -r -p > index_arg.bin +didc encode -d ../../../ledger_suite/icrc1/index-ng/index-ng.did -t '(opt IndexArg)' '(opt variant { Init = record { ledger_id = principal "mxzaz-hqaaa-aaaar-qaada-cai" } })' | xxd -r -p > index_arg.bin ``` Submitting the install proposal: @@ -156,7 +155,7 @@ bazel build //rs/registry/admin:ic-admin --proposer $NEURON_ID \ --canister-id n5wcd-faaaa-aaaar-qaaea-cai \ --mode install \ - --wasm-module-path ./ic-icrc1-index.wasm.gz \ + --wasm-module-path ./ic-icrc1-index-ng.wasm.gz \ --wasm-module-sha256 $WASM_SHA256 \ --arg index_arg.bin \ --summary-file ./index_proposal.md @@ -177,7 +176,7 @@ bazel build //rs/registry/admin:ic-admin --proposer $NEURON_ID \ --canister-id n5wcd-faaaa-aaaar-qaaea-cai \ --mode upgrade \ - --wasm-module-path ./ic-icrc1-index.wasm.gz \ + --wasm-module-path ./ic-icrc1-index-ng.wasm.gz \ --wasm-module-sha256 $WASM_SHA256 \ --summary-file ./index_upgrade.md ``` diff --git a/rs/bitcoin/ckbtc/mainnet/ckbtc_archive_upgrade_2025_01_17.md b/rs/bitcoin/ckbtc/mainnet/ckbtc_archive_upgrade_2025_01_17.md new file mode 100644 index 00000000000..5443f70ffa2 --- /dev/null +++ b/rs/bitcoin/ckbtc/mainnet/ckbtc_archive_upgrade_2025_01_17.md @@ -0,0 +1,47 @@ +# Proposal to upgrade the ckBTC archive canister + +Repository: `https://github.com/dfinity/ic.git` + +Git hash: `c741e349451edf0c9792149ad439bb32a0161371` + +New compressed Wasm hash: `2b0970a84976bc2eb9591b68d44501566937994fa5594972f8aac9c8b058672f` + +Upgrade args hash: `0fee102bd16b053022b69f2c65fd5e2f41d150ce9c214ac8731cfaf496ebda4e` + +Target canister: `nbsys-saaaa-aaaar-qaaga-cai` + +Previous ckBTC archive proposal: https://dashboard.internetcomputer.org/proposal/134451 + +--- + +## Motivation + +Upgrade the ckBTC archive canister to the same version ([ledger-suite-icrc-2025-01-07](https://github.com/dfinity/ic/releases/tag/ledger-suite-icrc-2025-01-07)) as the ckBTC ledger canister to maintain a consistent versioning across the ckBTC ledger suite. + +## Upgrade args + +``` +git fetch +git checkout c741e349451edf0c9792149ad439bb32a0161371 +cd rs/ledger_suite/icrc1/archive +didc encode '()' | xxd -r -p | sha256sum +``` + +## Release Notes + +No changes since last version (`2190613d3b5bcd9b74c382b22d151580b8ac271a`). + +``` +git log --format='%C(auto) %h %s' 2190613d3b5bcd9b74c382b22d151580b8ac271a..c741e349451edf0c9792149ad439bb32a0161371 -- rs/ledger_suite/icrc1/archive + ``` + +## Wasm Verification + +Verify that the hash of the gzipped WASM matches the proposed hash. + +``` +git fetch +git checkout c741e349451edf0c9792149ad439bb32a0161371 +"./ci/container/build-ic.sh" "--canisters" +sha256sum ./artifacts/canisters/ic-icrc1-archive.wasm.gz +``` diff --git a/rs/bitcoin/ckbtc/mainnet/ckbtc_index_upgrade_2025_01_17.md b/rs/bitcoin/ckbtc/mainnet/ckbtc_index_upgrade_2025_01_17.md new file mode 100644 index 00000000000..0c1786fc1bd --- /dev/null +++ b/rs/bitcoin/ckbtc/mainnet/ckbtc_index_upgrade_2025_01_17.md @@ -0,0 +1,49 @@ +# Proposal to upgrade the ckBTC index canister + +Repository: `https://github.com/dfinity/ic.git` + +Git hash: `c741e349451edf0c9792149ad439bb32a0161371` + +New compressed Wasm hash: `e155db9d06b6147ece4f9defe599844f132a7db21693265671aa6ac60912935f` + +Upgrade args hash: `0fee102bd16b053022b69f2c65fd5e2f41d150ce9c214ac8731cfaf496ebda4e` + +Target canister: `n5wcd-faaaa-aaaar-qaaea-cai` + +Previous ckBTC index proposal: https://dashboard.internetcomputer.org/proposal/134449 + +--- + +## Motivation + +Upgrade the ckBTC index canister to the same version ([ledger-suite-icrc-2025-01-07](https://github.com/dfinity/ic/releases/tag/ledger-suite-icrc-2025-01-07)) as the ckBTC ledger canister to maintain a consistent versioning across the ckBTC ledger suite. + +## Upgrade args + +``` +git fetch +git checkout c741e349451edf0c9792149ad439bb32a0161371 +cd rs/ledger_suite/icrc1/index-ng +didc encode '()' | xxd -r -p | sha256sum +``` + +## Release Notes + +``` +git log --format='%C(auto) %h %s' 2190613d3b5bcd9b74c382b22d151580b8ac271a..c741e349451edf0c9792149ad439bb32a0161371 -- rs/ledger_suite/icrc1/index-ng +c741e34945 feat: ICRC-ledger: FI-1439: Implement V4 for ICRC ledger - migrate balances to stable structures (#2901) +575ca531a7 chore(ICRC_Index): FI-1468: Remove old ICRC index canister (#3286) +8d4fcddc6e test(ICRC_Index): FI-1617: Optimize retrieve_blocks_from_ledger_interval tests (#3236) +e369646b76 fix: Use default rust edition instead of specifying it in the BUILD rules (#3047) + ``` + +## Wasm Verification + +Verify that the hash of the gzipped WASM matches the proposed hash. + +``` +git fetch +git checkout c741e349451edf0c9792149ad439bb32a0161371 +"./ci/container/build-ic.sh" "--canisters" +sha256sum ./artifacts/canisters/ic-icrc1-index-ng.wasm.gz +``` diff --git a/rs/bitcoin/ckbtc/mainnet/ckbtc_ledger_upgrade_2025_01_17.md b/rs/bitcoin/ckbtc/mainnet/ckbtc_ledger_upgrade_2025_01_17.md new file mode 100644 index 00000000000..bcef187ba8b --- /dev/null +++ b/rs/bitcoin/ckbtc/mainnet/ckbtc_ledger_upgrade_2025_01_17.md @@ -0,0 +1,49 @@ +# Proposal to upgrade the ckBTC ledger canister + +Repository: `https://github.com/dfinity/ic.git` + +Git hash: `c741e349451edf0c9792149ad439bb32a0161371` + +New compressed Wasm hash: `3b03d1bb1145edbcd11101ab2788517bc0f427c3bd7b342b9e3e7f42e29d5822` + +Upgrade args hash: `0fee102bd16b053022b69f2c65fd5e2f41d150ce9c214ac8731cfaf496ebda4e` + +Target canister: `mxzaz-hqaaa-aaaar-qaada-cai` + +Previous ckBTC ledger proposal: https://dashboard.internetcomputer.org/proposal/134450 + +--- + +## Motivation + +Upgrade the ckBTC ledger canister to the latest version ([ledger-suite-icrc-2025-01-07](https://github.com/dfinity/ic/releases/tag/ledger-suite-icrc-2025-01-07)) to continue the migration towards stable memory. + +## Upgrade args + +``` +git fetch +git checkout c741e349451edf0c9792149ad439bb32a0161371 +cd rs/ledger_suite/icrc1/ledger +didc encode '()' | xxd -r -p | sha256sum +``` + +## Release Notes + +``` +git log --format='%C(auto) %h %s' 2190613d3b5bcd9b74c382b22d151580b8ac271a..c741e349451edf0c9792149ad439bb32a0161371 -- rs/ledger_suite/icrc1/ledger +c741e34945 feat: ICRC-ledger: FI-1439: Implement V4 for ICRC ledger - migrate balances to stable structures (#2901) +ddadaafd51 test(ICP_Ledger): FI-1616: Fix ICP ledger upgrade tests (#3213) +dfc3810851 fix(ICRC-Ledger): changed certificate version (#2848) +b006ae9934 feat(ICP-ledger): FI-1438: Implement V3 for ICP ledger - migrate allowances to stable structures (#2818) + ``` + +## Wasm Verification + +Verify that the hash of the gzipped WASM matches the proposed hash. + +``` +git fetch +git checkout c741e349451edf0c9792149ad439bb32a0161371 +"./ci/container/build-ic.sh" "--canisters" +sha256sum ./artifacts/canisters/ic-icrc1-ledger.wasm.gz +``` diff --git a/rs/bitcoin/ckbtc/mainnet/dfx.json b/rs/bitcoin/ckbtc/mainnet/dfx.json index 48d51478d74..ca0f34feb16 100644 --- a/rs/bitcoin/ckbtc/mainnet/dfx.json +++ b/rs/bitcoin/ckbtc/mainnet/dfx.json @@ -32,10 +32,10 @@ "index": { "type": "custom", "build": [ - "bazel build //rs/ledger_suite/icrc1/index:index_canister" + "bazel build //rs/ledger_suite/icrc1/index-ng:index_ng_canister" ], - "wasm": "../../../../bazel-bin/rs/ledger_suite/icrc1/index/index_canister.wasm", - "candid": "../../../ledger_suite/icrc1/index/index.did", + "wasm": "../../../../bazel-bin/rs/ledger_suite/icrc1/index-ng/index_ng_canister.wasm", + "candid": "../../../ledger_suite/icrc1/index-ng/index-ng.did", "shrink": false }, "btc_checker": { diff --git a/rs/bitcoin/ckbtc/mainnet/minter_upgrade_2025_01_24.md b/rs/bitcoin/ckbtc/mainnet/minter_upgrade_2025_01_24.md new file mode 100644 index 00000000000..1121f53b961 --- /dev/null +++ b/rs/bitcoin/ckbtc/mainnet/minter_upgrade_2025_01_24.md @@ -0,0 +1,54 @@ +# Proposal to upgrade the ckBTC minter canister + +Repository: `https://github.com/dfinity/ic.git` + +Git hash: `744f4683df2ca79f5f537b3db48a1c03d4ff084e` + +New compressed Wasm hash: `f8d82d7bad3e7d6b8d36c04936d3aa6a67190250bf456710c9fcd7da39926d6c` + +Upgrade args hash: `0fee102bd16b053022b69f2c65fd5e2f41d150ce9c214ac8731cfaf496ebda4e` + +Target canister: `mqygn-kiaaa-aaaar-qaadq-cai` + +Previous ckBTC minter proposal: https://dashboard.internetcomputer.org/proposal/134414 + +--- + +## Motivation + +Update the ckBTC minter to include the latest code changes, notably: +* Add metrics for the `update_latency` method. +* Add timestamps to the minter events. + +## Upgrade args + +``` +git fetch +git checkout 744f4683df2ca79f5f537b3db48a1c03d4ff084e +cd rs/bitcoin/ckbtc/minter +didc encode '()' | xxd -r -p | sha256sum +``` + +## Release Notes + +``` +git log --format='%C(auto) %h %s' 9849a2f03af855d09ac42f5949393c86df3d9c47..744f4683df2ca79f5f537b3db48a1c03d4ff084e -- rs/bitcoin/ckbtc/minter +841793d547 chore: add MetricsAssert test utility (#3375) +cfd1859fd8 chore(ckbtc): remove distribute_kyt_fee and reimburse_failed_kyt (#3325) +72a1f85c9f chore(ckbtc): property tests for event serialization and deserialization (#3277) +9afadc4a78 chore(ckbtc): add optional timestamp to minter events (#3157) +7136d8b228 fix(ckbtc): Add num_subnet_nodes to Bitcoin Checker's InitArg (#3075) +db8c33d181 chore(ckbtc): add metrics for the latency of `update_balance` in the minter (#3003) +f901615f3d fix(ckbtc): fix bitcoin checker cycle cost calculation (#3056) + ``` + +## Wasm Verification + +Verify that the hash of the gzipped WASM matches the proposed hash. + +``` +git fetch +git checkout 744f4683df2ca79f5f537b3db48a1c03d4ff084e +"./ci/container/build-ic.sh" "--canisters" +sha256sum ./artifacts/canisters/ic-ckbtc-minter.wasm.gz +``` diff --git a/rs/bitcoin/ckbtc/minter/BUILD.bazel b/rs/bitcoin/ckbtc/minter/BUILD.bazel index f1a304a7c34..8e942337897 100644 --- a/rs/bitcoin/ckbtc/minter/BUILD.bazel +++ b/rs/bitcoin/ckbtc/minter/BUILD.bazel @@ -109,7 +109,7 @@ rust_test( crate = ":ckbtc_minter_lib", deps = [ # Keep sorted. - "@crate_index//:bitcoin", + "@crate_index//:bitcoin_0_28", "@crate_index//:maplit", "@crate_index//:mockall", "@crate_index//:proptest", @@ -141,10 +141,11 @@ rust_test( deps = [ # Keep sorted. ":ckbtc_minter_lib", - "@crate_index//:bitcoin", + "@crate_index//:bitcoin_0_28", "@crate_index//:candid", "@crate_index//:flate2", "@crate_index//:ic-agent", + "@crate_index//:ic-stable-structures", "@crate_index//:serde", "@crate_index//:tokio", ], @@ -170,6 +171,7 @@ rust_ic_test( deps = [ # Keep sorted. ":ckbtc_minter_lib", + "//packages/ic-metrics-assert", "//packages/icrc-ledger-types:icrc_ledger_types", "//rs/bitcoin/checker:btc_checker_lib", "//rs/bitcoin/mock", @@ -183,7 +185,7 @@ rust_ic_test( "//rs/types/base_types", "//rs/types/types", "@crate_index//:assert_matches", - "@crate_index//:bitcoin", + "@crate_index//:bitcoin_0_28", "@crate_index//:candid", "@crate_index//:ic-btc-interface", "@crate_index//:minicbor", diff --git a/rs/bitcoin/ckbtc/minter/Cargo.toml b/rs/bitcoin/ckbtc/minter/Cargo.toml index 22f238d721a..c15f8898b5f 100644 --- a/rs/bitcoin/ckbtc/minter/Cargo.toml +++ b/rs/bitcoin/ckbtc/minter/Cargo.toml @@ -48,7 +48,7 @@ serde_json = { workspace = true } [dev-dependencies] assert_matches = { workspace = true } -bitcoin = { workspace = true } +bitcoin = { version = "0.28.2" } candid_parser = { workspace = true } canister-test = { path = "../../../rust_canisters/canister_test" } flate2 = { workspace = true } @@ -56,13 +56,13 @@ ic-agent = { workspace = true } ic-bitcoin-canister-mock = { path = "../../mock" } ic-config = { path = "../../../config" } ic-icrc1-ledger = { path = "../../../ledger_suite/icrc1/ledger" } +ic-metrics-assert = { path = "../../../../packages/ic-metrics-assert" } ic-state-machine-tests = { path = "../../../state_machine_tests" } ic-test-utilities-load-wasm = { path = "../../../test_utilities/load_wasm" } ic-types = { path = "../../../types/types" } maplit = "1.0.2" mockall = { workspace = true } proptest = { workspace = true } -regex = "1.11.0" simple_asn1 = { workspace = true } tokio = { workspace = true } diff --git a/rs/bitcoin/ckbtc/minter/src/guard.rs b/rs/bitcoin/ckbtc/minter/src/guard.rs index 35639580086..954bc2abc1e 100644 --- a/rs/bitcoin/ckbtc/minter/src/guard.rs +++ b/rs/bitcoin/ckbtc/minter/src/guard.rs @@ -89,29 +89,6 @@ impl Drop for TimerLogicGuard { } } -#[must_use] -pub struct DistributeKytFeeGuard(()); - -impl DistributeKytFeeGuard { - pub fn new() -> Option { - mutate_state(|s| { - if s.is_distributing_fee { - return None; - } - s.is_distributing_fee = true; - Some(DistributeKytFeeGuard(())) - }) - } -} - -impl Drop for DistributeKytFeeGuard { - fn drop(&mut self) { - mutate_state(|s| { - s.is_distributing_fee = false; - }); - } -} - pub fn balance_update_guard(p: Principal) -> Result, GuardError> { Guard::new(p) } diff --git a/rs/bitcoin/ckbtc/minter/src/lib.rs b/rs/bitcoin/ckbtc/minter/src/lib.rs index 17606e5bc19..3d156535210 100644 --- a/rs/bitcoin/ckbtc/minter/src/lib.rs +++ b/rs/bitcoin/ckbtc/minter/src/lib.rs @@ -1,9 +1,7 @@ use crate::address::BitcoinAddress; use crate::logs::{P0, P1}; use crate::management::CallError; -use crate::memo::Status; use crate::queries::WithdrawalFee; -use crate::state::ReimbursementReason; use crate::updates::update_balance::UpdateBalanceError; use async_trait::async_trait; use candid::{CandidType, Deserialize, Principal}; @@ -14,8 +12,7 @@ use ic_btc_interface::{ use ic_canister_log::log; use ic_management_canister_types::DerivationPath; use icrc_ledger_types::icrc1::account::Account; -use icrc_ledger_types::icrc1::transfer::{Memo, TransferError}; -use num_traits::ToPrimitive; +use icrc_ledger_types::icrc1::transfer::Memo; use scopeguard::{guard, ScopeGuard}; use serde::Serialize; use serde_bytes::ByteBuf; @@ -464,40 +461,6 @@ fn finalized_txids(candidates: &[state::SubmittedBtcTransaction], new_utxos: &[U .collect() } -async fn reimburse_failed_kyt() { - let try_to_reimburse = state::read_state(|s| s.pending_reimbursements.clone()); - for (burn_block_index, entry) in try_to_reimburse { - let (memo_status, kyt_fee) = match entry.reason { - ReimbursementReason::TaintedDestination { kyt_fee, .. } => (Status::Rejected, kyt_fee), - ReimbursementReason::CallFailed => (Status::CallFailed, 0), - }; - let reimburse_memo = crate::memo::MintMemo::KytFail { - kyt_fee: Some(kyt_fee), - status: Some(memo_status), - associated_burn_index: Some(burn_block_index), - }; - if let Ok(block_index) = crate::updates::update_balance::mint( - entry - .amount - .checked_sub(kyt_fee) - .expect("reimburse underflow"), - entry.account, - crate::memo::encode(&reimburse_memo).into(), - ) - .await - { - state::mutate_state(|s| { - state::audit::reimbursed_failed_deposit( - s, - burn_block_index, - block_index, - &IC_CANISTER_RUNTIME, - ) - }); - } - } -} - async fn finalize_requests() { if state::read_state(|s| s.submitted_transactions.is_empty()) { return; @@ -1119,96 +1082,6 @@ fn distribute(amount: u64, n: u64) -> Vec { shares } -pub async fn distribute_kyt_fees() { - use icrc_ledger_client_cdk::CdkRuntime; - use icrc_ledger_client_cdk::ICRC1Client; - use icrc_ledger_types::icrc1::transfer::TransferArg; - - enum MintError { - TransferError(TransferError), - CallError(i32, String), - } - - impl std::fmt::Debug for MintError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - MintError::TransferError(e) => write!(f, "TransferError({:?})", e), - MintError::CallError(code, msg) => write!(f, "CallError({}, {:?})", code, msg), - } - } - } - - async fn mint(amount: u64, to: candid::Principal, memo: Memo) -> Result { - debug_assert!(memo.0.len() <= CKBTC_LEDGER_MEMO_SIZE as usize); - - let client = ICRC1Client { - runtime: CdkRuntime, - ledger_canister_id: state::read_state(|s| s.ledger_id.get().into()), - }; - client - .transfer(TransferArg { - from_subaccount: None, - to: Account { - owner: to, - subaccount: None, - }, - fee: None, - created_at_time: None, - memo: Some(memo), - amount: candid::Nat::from(amount), - }) - .await - .map_err(|(code, msg)| MintError::CallError(code, msg))? - .map_err(MintError::TransferError) - .map(|n| n.0.to_u64().expect("nat does not fit into u64")) - } - - let fees_to_distribute = state::read_state(|s| s.owed_kyt_amount.clone()); - for (provider, amount) in fees_to_distribute { - let memo = crate::memo::MintMemo::Kyt; - match mint(amount, provider, crate::memo::encode(&memo).into()).await { - Ok(block_index) => { - state::mutate_state(|s| { - if let Err(state::Overdraft(overdraft)) = state::audit::distributed_kyt_fee( - s, - provider, - amount, - block_index, - &IC_CANISTER_RUNTIME, - ) { - // This should never happen because: - // 1. The fee distribution task is guarded (at most one copy is active). - // 2. Fee distribution is the only way to decrease the balance. - log!( - P0, - "BUG[distribute_kyt_fees]: distributed {} to {} but the balance is only {}", - tx::DisplayAmount(amount), - provider, - tx::DisplayAmount(amount - overdraft), - ); - } else { - log!( - P0, - "[distribute_kyt_fees]: minted {} to {}", - tx::DisplayAmount(amount), - provider, - ); - } - }); - } - Err(error) => { - log!( - P0, - "[distribute_kyt_fees]: failed to mint {} to {} with error: {:?}", - tx::DisplayAmount(amount), - provider, - error - ); - } - } - } -} - pub fn timer(runtime: R) { use tasks::{pop_if_ready, run_task}; diff --git a/rs/bitcoin/ckbtc/minter/src/lifecycle/upgrade.rs b/rs/bitcoin/ckbtc/minter/src/lifecycle/upgrade.rs index 3bc4013f329..e265b76ee40 100644 --- a/rs/bitcoin/ckbtc/minter/src/lifecycle/upgrade.rs +++ b/rs/bitcoin/ckbtc/minter/src/lifecycle/upgrade.rs @@ -2,7 +2,7 @@ use crate::logs::P0; use crate::state::eventlog::{replay, EventType}; use crate::state::invariants::CheckInvariantsImpl; use crate::state::{replace_state, Mode}; -use crate::storage::{count_events, events, record_event}; +use crate::storage::{count_events, events, migrate_old_events_if_not_empty, record_event}; use crate::IC_CANISTER_RUNTIME; use candid::{CandidType, Deserialize}; use ic_base_types::CanisterId; @@ -58,6 +58,9 @@ pub fn post_upgrade(upgrade_args: Option) { let start = ic_cdk::api::instruction_counter(); + if let Some(removed) = migrate_old_events_if_not_empty() { + log!(P0, "[upgrade]: {} empty events removed", removed) + } log!(P0, "[upgrade]: replaying {} events", count_events()); let state = replay::(events()).unwrap_or_else(|e| { diff --git a/rs/bitcoin/ckbtc/minter/src/main.rs b/rs/bitcoin/ckbtc/minter/src/main.rs index e12d791e89e..5456da881c9 100644 --- a/rs/bitcoin/ckbtc/minter/src/main.rs +++ b/rs/bitcoin/ckbtc/minter/src/main.rs @@ -50,7 +50,6 @@ fn init(args: MinterArg) { fn setup_tasks() { schedule_now(TaskType::ProcessLogic, &IC_CANISTER_RUNTIME); schedule_now(TaskType::RefreshFeePercentiles, &IC_CANISTER_RUNTIME); - schedule_now(TaskType::DistributeKytFee, &IC_CANISTER_RUNTIME); } #[cfg(feature = "self_check")] @@ -84,16 +83,6 @@ fn check_invariants() -> Result<(), String> { }) } -#[cfg(feature = "self_check")] -#[update] -async fn distribute_kyt_fee() { - let _guard = match ic_ckbtc_minter::guard::DistributeKytFeeGuard::new() { - Some(guard) => guard, - None => return, - }; - ic_ckbtc_minter::distribute_kyt_fees().await; -} - #[cfg(feature = "self_check")] #[update] async fn refresh_fee_percentiles() { diff --git a/rs/bitcoin/ckbtc/minter/src/memo.rs b/rs/bitcoin/ckbtc/minter/src/memo.rs index 7aeed4531d0..554ec72f274 100644 --- a/rs/bitcoin/ckbtc/minter/src/memo.rs +++ b/rs/bitcoin/ckbtc/minter/src/memo.rs @@ -1,3 +1,4 @@ +#![allow(deprecated)] use minicbor::Encoder; use minicbor::{Decode, Encode}; @@ -37,9 +38,11 @@ pub enum MintMemo<'a> { kyt_fee: Option, }, #[n(1)] + #[deprecated] /// The minter minted accumulated check fees to the KYT provider. Kyt, #[n(2)] + #[deprecated] /// The minter failed to check retrieve btc destination address /// or the destination address is tainted. KytFail { diff --git a/rs/bitcoin/ckbtc/minter/src/state/audit.rs b/rs/bitcoin/ckbtc/minter/src/state/audit.rs index 6b7d0aa939b..993f128bf68 100644 --- a/rs/bitcoin/ckbtc/minter/src/state/audit.rs +++ b/rs/bitcoin/ckbtc/minter/src/state/audit.rs @@ -5,9 +5,8 @@ use super::{ RetrieveBtcRequest, SubmittedBtcTransaction, SuspendedReason, }; use crate::state::invariants::CheckInvariantsImpl; -use crate::state::{ReimburseDepositTask, ReimbursedDeposit}; use crate::storage::record_event; -use crate::{CanisterRuntime, ReimbursementReason, Timestamp}; +use crate::{CanisterRuntime, Timestamp}; use candid::Principal; use ic_btc_interface::{Txid, Utxo}; use icrc_ledger_types::icrc1::account::Account; @@ -213,58 +212,3 @@ pub fn distributed_kyt_fee( ); state.distribute_kyt_fee(kyt_provider, amount) } - -pub fn schedule_deposit_reimbursement( - state: &mut CkBtcMinterState, - account: Account, - amount: u64, - reason: ReimbursementReason, - burn_block_index: u64, - runtime: &R, -) { - record_event( - EventType::ScheduleDepositReimbursement { - account, - amount, - reason, - burn_block_index, - }, - runtime, - ); - state.schedule_deposit_reimbursement( - burn_block_index, - ReimburseDepositTask { - account, - amount, - reason, - }, - ); -} - -pub fn reimbursed_failed_deposit( - state: &mut CkBtcMinterState, - burn_block_index: u64, - mint_block_index: u64, - runtime: &R, -) { - record_event( - EventType::ReimbursedFailedDeposit { - burn_block_index, - mint_block_index, - }, - runtime, - ); - let reimbursed_tx = state - .pending_reimbursements - .remove(&burn_block_index) - .expect("bug: reimbursement task should be present"); - state.reimbursed_transactions.insert( - burn_block_index, - ReimbursedDeposit { - account: reimbursed_tx.account, - amount: reimbursed_tx.amount, - reason: reimbursed_tx.reason, - mint_block_index, - }, - ); -} diff --git a/rs/bitcoin/ckbtc/minter/src/storage.rs b/rs/bitcoin/ckbtc/minter/src/storage.rs index b907fcf645e..27e285cec13 100644 --- a/rs/bitcoin/ckbtc/minter/src/storage.rs +++ b/rs/bitcoin/ckbtc/minter/src/storage.rs @@ -11,8 +11,11 @@ use ic_stable_structures::{ use serde::Deserialize; use std::cell::RefCell; -const LOG_INDEX_MEMORY_ID: MemoryId = MemoryId::new(0); -const LOG_DATA_MEMORY_ID: MemoryId = MemoryId::new(1); +const V0_LOG_INDEX_MEMORY_ID: MemoryId = MemoryId::new(0); +const V0_LOG_DATA_MEMORY_ID: MemoryId = MemoryId::new(1); + +const V1_LOG_INDEX_MEMORY_ID: MemoryId = MemoryId::new(2); +const V1_LOG_DATA_MEMORY_ID: MemoryId = MemoryId::new(3); type VMem = VirtualMemory; type EventLog = StableLog, VMem, VMem>; @@ -22,13 +25,24 @@ thread_local! { MemoryManager::init(DefaultMemoryImpl::default()) ); - /// The log of the ckBTC state modifications. - static EVENTS: RefCell = MEMORY_MANAGER + /// The v0 log of the ckBTC state modifications that should be migrated to v1 and then set to empty. + static V0_EVENTS: RefCell = MEMORY_MANAGER + .with(|m| + RefCell::new( + StableLog::init( + m.borrow().get(V0_LOG_INDEX_MEMORY_ID), + m.borrow().get(V0_LOG_DATA_MEMORY_ID) + ).expect("failed to initialize stable log") + ) + ); + + /// The latest log of the ckBTC state modifications. + static V1_EVENTS: RefCell = MEMORY_MANAGER .with(|m| RefCell::new( StableLog::init( - m.borrow().get(LOG_INDEX_MEMORY_ID), - m.borrow().get(LOG_DATA_MEMORY_ID) + m.borrow().get(V1_LOG_INDEX_MEMORY_ID), + m.borrow().get(V1_LOG_DATA_MEMORY_ID) ).expect("failed to initialize stable log") ) ); @@ -43,7 +57,7 @@ impl Iterator for EventIterator { type Item = Event; fn next(&mut self) -> Option { - EVENTS.with(|events| { + V1_EVENTS.with(|events| { let events = events.borrow(); match events.read_entry(self.pos, &mut self.buf) { @@ -63,7 +77,7 @@ impl Iterator for EventIterator { } /// Encodes an event into a byte array. -fn encode_event(event: &Event) -> Vec { +pub fn encode_event(event: &Event) -> Vec { let mut buf = Vec::new(); ciborium::ser::into_writer(event, &mut buf).expect("failed to encode a minter event"); buf @@ -72,7 +86,7 @@ fn encode_event(event: &Event) -> Vec { /// # Panics /// /// This function panics if the event decoding fails. -fn decode_event(buf: &[u8]) -> Event { +pub fn decode_event(buf: &[u8]) -> Event { // For backwards compatibility, we have to handle two cases: // 1. Legacy events: raw instances of the event type enum // 2. New events: a struct containing a timestamp and an event type @@ -101,9 +115,49 @@ pub fn events() -> impl Iterator { } } +pub fn migrate_old_events_if_not_empty() -> Option { + let mut num_events_removed = None; + V0_EVENTS.with(|old_events| { + let mut old = old_events.borrow_mut(); + if old.len() > 0 { + V1_EVENTS.with(|new| { + num_events_removed = Some(migrate_events(&old, &new.borrow())); + }); + *old = MEMORY_MANAGER.with(|m| { + StableLog::new( + m.borrow().get(V0_LOG_INDEX_MEMORY_ID), + m.borrow().get(V0_LOG_DATA_MEMORY_ID), + ) + }); + } + }); + assert_eq!( + V0_EVENTS.with(|events| events.borrow().len()), + 0, + "Old events is not emptied after data migration" + ); + num_events_removed +} + +pub fn migrate_events(old_events: &EventLog, new_events: &EventLog) -> u64 { + let mut removed = 0; + for bytes in old_events.iter() { + let event = decode_event(&bytes); + match event.payload { + EventType::ReceivedUtxos { utxos, .. } if utxos.is_empty() => removed += 1, + _ => { + new_events + .append(&bytes) + .expect("failed to append an entry to the new event log"); + } + } + } + removed +} + /// Returns the current number of events in the log. pub fn count_events() -> u64 { - EVENTS.with(|events| events.borrow().len()) + V1_EVENTS.with(|events| events.borrow().len()) } /// Records a new minter event. @@ -112,7 +166,7 @@ pub fn record_event(payload: EventType, runtime: &R) { timestamp: Some(runtime.time()), payload, }); - EVENTS.with(|events| { + V1_EVENTS.with(|events| { events .borrow() .append(&bytes) diff --git a/rs/bitcoin/ckbtc/minter/src/storage/tests.rs b/rs/bitcoin/ckbtc/minter/src/storage/tests.rs index 906a1e76f3e..453f44c5cb6 100644 --- a/rs/bitcoin/ckbtc/minter/src/storage/tests.rs +++ b/rs/bitcoin/ckbtc/minter/src/storage/tests.rs @@ -1,48 +1,38 @@ -use crate::state::eventlog::{Event, EventType}; -use crate::test_fixtures::{ignored_utxo, ledger_account}; - -#[test] -fn should_decode_encoded_event() { - let event = Event { - timestamp: Some(123), - payload: event_type(), - }; - - let encoded = super::encode_event(&event); - let decoded = super::decode_event(&encoded); - - assert_eq!(event, decoded); -} - -#[test] -fn should_decode_encoded_legacy_event() { - /// Legacy events simply consisted of an event type instance. The - /// encoding logic is the exact same as for new events, only the type - /// being encoded differs. - fn encode_legacy_event(event: &EventType) -> Vec { - let mut buf = Vec::new(); - ciborium::ser::into_writer(event, &mut buf).expect("failed to encode a minter event"); - buf +use super::{decode_event, encode_event}; +use crate::{ + state::eventlog::{Event, EventType}, + test_fixtures::arbitrary, +}; +use proptest::proptest; + +proptest! { + #[test] + fn should_decode_encoded_event(event in arbitrary::event()) { + let encoded = encode_event(&event); + let decoded = decode_event(&encoded); + + assert_eq!(event, decoded); } - let legacy_event = event_type(); - - let encoded = encode_legacy_event(&legacy_event); - let decoded = super::decode_event(&encoded); - - assert_eq!( - decoded, - Event { - timestamp: None, - payload: legacy_event, + #[test] + fn should_decode_encoded_legacy_event(legacy_event in arbitrary::event_type()) { + /// Legacy events just consist of an event type instance. The encoding logic + /// is the exact same as for new events. Only the type being encoded differs. + fn encode_legacy_event(event: &EventType) -> Vec { + let mut buf = Vec::new(); + ciborium::ser::into_writer(event, &mut buf).expect("failed to encode a minter event"); + buf } - ); -} -fn event_type() -> EventType { - EventType::ReceivedUtxos { - mint_txid: Some(1), - to_account: ledger_account(), - utxos: vec![ignored_utxo()], + let encoded = encode_legacy_event(&legacy_event); + let decoded = decode_event(&encoded); + + assert_eq!( + decoded, + Event { + timestamp: None, + payload: legacy_event, + } + ); } } diff --git a/rs/bitcoin/ckbtc/minter/src/tasks.rs b/rs/bitcoin/ckbtc/minter/src/tasks.rs index 4dcfb77c14f..d454c13ab0f 100644 --- a/rs/bitcoin/ckbtc/minter/src/tasks.rs +++ b/rs/bitcoin/ckbtc/minter/src/tasks.rs @@ -1,10 +1,6 @@ #[cfg(test)] mod tests; -use crate::{ - distribute_kyt_fees, estimate_fee_per_vbyte, finalize_requests, reimburse_failed_kyt, - submit_pending_requests, CanisterRuntime, -}; -use ic_btc_interface::Network; +use crate::{estimate_fee_per_vbyte, finalize_requests, submit_pending_requests, CanisterRuntime}; use scopeguard::guard; use std::cell::{Cell, RefCell}; use std::collections::{BTreeMap, BTreeSet}; @@ -19,7 +15,6 @@ thread_local! { pub enum TaskType { ProcessLogic, RefreshFeePercentiles, - DistributeKytFee, } #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] @@ -147,7 +142,6 @@ pub(crate) async fn run_task(task: Task, runtime: R) { submit_pending_requests().await; finalize_requests().await; - reimburse_failed_kyt().await; } TaskType::RefreshFeePercentiles => { const FEE_ESTIMATE_DELAY: Duration = Duration::from_secs(60 * 60); @@ -165,29 +159,5 @@ pub(crate) async fn run_task(task: Task, runtime: R) { }; let _ = estimate_fee_per_vbyte().await; } - TaskType::DistributeKytFee => { - const MAINNET_KYT_FEE_DISTRIBUTION_PERIOD: Duration = Duration::from_secs(24 * 60 * 60); - - let _enqueue_followup_guard = guard((), |_| { - schedule_after( - MAINNET_KYT_FEE_DISTRIBUTION_PERIOD, - TaskType::DistributeKytFee, - &runtime, - ); - }); - let _guard = match crate::guard::DistributeKytFeeGuard::new() { - Some(guard) => guard, - None => return, - }; - - match crate::state::read_state(|s| s.btc_network) { - Network::Mainnet | Network::Testnet => { - distribute_kyt_fees().await; - } - // We use a debug canister build exposing an endpoint - // triggering the fee distribution in tests. - Network::Regtest => {} - } - } } } diff --git a/rs/bitcoin/ckbtc/minter/src/tasks/tests.rs b/rs/bitcoin/ckbtc/minter/src/tasks/tests.rs index 78a8d4beec6..e571855b441 100644 --- a/rs/bitcoin/ckbtc/minter/src/tasks/tests.rs +++ b/rs/bitcoin/ckbtc/minter/src/tasks/tests.rs @@ -48,16 +48,6 @@ async fn should_reschedule_refresh_fees() { .await; } -#[tokio::test] -async fn should_reschedule_distribute_kyt_fee() { - test_reschedule( - TaskType::DistributeKytFee, - || crate::guard::DistributeKytFeeGuard::new().unwrap(), - Duration::from_secs(24 * 60 * 60), - ) - .await; -} - async fn test_reschedule T>( task_type: TaskType, guard: G, diff --git a/rs/bitcoin/ckbtc/minter/src/test_fixtures.rs b/rs/bitcoin/ckbtc/minter/src/test_fixtures.rs index cee6585f3a8..7861fc0c5c3 100644 --- a/rs/bitcoin/ckbtc/minter/src/test_fixtures.rs +++ b/rs/bitcoin/ckbtc/minter/src/test_fixtures.rs @@ -142,3 +142,306 @@ pub mod mock { } } } + +pub mod arbitrary { + use crate::{ + address::BitcoinAddress, + signature::EncodedSignature, + state::{ + eventlog::{Event, EventType}, + ChangeOutput, Mode, ReimbursementReason, RetrieveBtcRequest, SuspendedReason, + }, + tx, + tx::{SignedInput, TxOut, UnsignedInput}, + }; + use candid::Principal; + pub use event::event_type; + use ic_base_types::CanisterId; + use ic_btc_interface::{OutPoint, Satoshi, Txid, Utxo}; + use icrc_ledger_types::icrc1::account::Account; + use proptest::{ + array::uniform20, + array::uniform32, + collection::{vec as pvec, SizeRange}, + option, + prelude::{any, Just, Strategy}, + prop_oneof, + }; + use serde_bytes::ByteBuf; + + // Macro to simplify writing strategies that generate structs. + macro_rules! prop_struct { + ($struct_path:path { $($field_name:ident: $strategy:expr),* $(,)? }) => { + #[allow(unused_parens)] + ($($strategy),*).prop_map(|($($field_name),*)| { + $struct_path { + $($field_name),* + } + }) + }; + } + + fn amount() -> impl Strategy { + 1..10_000_000_000u64 + } + + fn txid() -> impl Strategy { + uniform32(any::()).prop_map(Txid::from) + } + + fn outpoint() -> impl Strategy { + prop_struct!(OutPoint { + txid: txid(), + vout: any::(), + }) + } + + fn canister_id() -> impl Strategy { + any::().prop_map(CanisterId::from_u64) + } + + pub fn retrieve_btc_requests( + amount: impl Strategy, + num: impl Into, + ) -> impl Strategy> { + pvec(retrieve_btc_request(amount), num).prop_map(|mut reqs| { + reqs.sort_by_key(|req| req.received_at); + for (i, req) in reqs.iter_mut().enumerate() { + req.block_index = i as u64; + } + reqs + }) + } + + fn principal() -> impl Strategy { + pvec(any::(), 1..=Principal::MAX_LENGTH_IN_BYTES) + .prop_map(|bytes| Principal::from_slice(bytes.as_slice())) + } + + fn retrieve_btc_request( + amount: impl Strategy, + ) -> impl Strategy { + prop_struct!(RetrieveBtcRequest { + amount: amount, + address: address(), + block_index: any::(), + received_at: 1569975147000..2069975147000u64, + kyt_provider: option::of(principal()), + reimbursement_account: option::of(account()), + }) + } + + fn reimbursement_reason() -> impl Strategy { + prop_oneof![ + (principal(), any::()).prop_map(|(kyt_provider, kyt_fee)| { + ReimbursementReason::TaintedDestination { + kyt_provider, + kyt_fee, + } + }), + Just(ReimbursementReason::CallFailed), + ] + } + + fn suspended_reason() -> impl Strategy { + prop_oneof![ + Just(SuspendedReason::ValueTooSmall), + Just(SuspendedReason::Quarantined), + ] + } + + fn change_output() -> impl Strategy { + (any::(), any::()).prop_map(|(vout, value)| ChangeOutput { vout, value }) + } + + fn mode() -> impl Strategy { + prop_oneof![ + Just(Mode::ReadOnly), + pvec(principal(), 0..10_000).prop_map(Mode::RestrictedTo), + pvec(principal(), 0..10_000).prop_map(Mode::DepositsRestrictedTo), + Just(Mode::GeneralAvailability), + ] + } + + fn encoded_signature() -> impl Strategy { + pvec(1u8..0xff, 64).prop_map(|bytes| EncodedSignature::from_sec1(bytes.as_slice())) + } + + pub fn unsigned_input( + value: impl Strategy, + ) -> impl Strategy { + prop_struct!(UnsignedInput { + previous_output: outpoint(), + value: value, + sequence: any::(), + }) + } + + pub fn signed_input() -> impl Strategy { + prop_struct!(SignedInput { + previous_output: outpoint(), + sequence: any::(), + signature: encoded_signature(), + pubkey: pvec(any::(), tx::PUBKEY_LEN).prop_map(ByteBuf::from), + }) + } + + pub fn address() -> impl Strategy { + prop_oneof![ + uniform20(any::()).prop_map(BitcoinAddress::P2wpkhV0), + uniform32(any::()).prop_map(BitcoinAddress::P2wshV0), + uniform32(any::()).prop_map(BitcoinAddress::P2trV1), + uniform20(any::()).prop_map(BitcoinAddress::P2pkh), + uniform20(any::()).prop_map(BitcoinAddress::P2sh), + ] + } + + pub fn tx_out() -> impl Strategy { + prop_struct!(TxOut { + value: amount(), + address: address(), + }) + } + + pub fn utxo(amount: impl Strategy) -> impl Strategy { + prop_struct!(Utxo { + outpoint: outpoint(), + value: amount, + height: any::(), + }) + } + + pub fn account() -> impl Strategy { + prop_struct!(Account { + owner: principal(), + subaccount: option::of(uniform32(any::())), + }) + } + + pub fn event() -> impl Strategy { + (any::>(), event_type()) + .prop_map(|(timestamp, payload)| Event { timestamp, payload }) + } + + // Some event types are deprecated, however we still want to use them in prop tests as we want + // to make sure they can still be deserialized. + // For convenience, the module is not visible to the outside. + #[allow(deprecated)] + mod event { + use super::*; + use crate::lifecycle::{ + init::{BtcNetwork, InitArgs}, + upgrade::UpgradeArgs, + }; + + fn btc_network() -> impl Strategy { + prop_oneof![ + Just(BtcNetwork::Mainnet), + Just(BtcNetwork::Testnet), + Just(BtcNetwork::Regtest), + ] + } + + fn init_args() -> impl Strategy { + prop_struct!(InitArgs { + btc_network: btc_network(), + ecdsa_key_name: ".*", + retrieve_btc_min_amount: any::(), + ledger_id: canister_id(), + max_time_in_queue_nanos: any::(), + min_confirmations: option::of(any::()), + mode: mode(), + check_fee: option::of(any::()), + kyt_fee: option::of(any::()), + btc_checker_principal: option::of(canister_id()), + kyt_principal: option::of(canister_id()), + }) + } + + fn upgrade_args() -> impl Strategy { + prop_struct!(UpgradeArgs { + retrieve_btc_min_amount: option::of(any::()), + min_confirmations: option::of(any::()), + max_time_in_queue_nanos: option::of(any::()), + mode: option::of(mode()), + check_fee: option::of(any::()), + kyt_fee: option::of(any::()), + btc_checker_principal: option::of(canister_id()), + kyt_principal: option::of(canister_id()), + }) + } + + pub fn event_type() -> impl Strategy { + prop_oneof![ + init_args().prop_map(EventType::Init), + upgrade_args().prop_map(EventType::Upgrade), + retrieve_btc_request(amount()).prop_map(EventType::AcceptedRetrieveBtcRequest), + prop_struct!(EventType::ReceivedUtxos { + mint_txid: option::of(any::()), + to_account: account(), + utxos: pvec(utxo(amount()), 0..10_000), + }), + prop_struct!(EventType::RemovedRetrieveBtcRequest { + block_index: any::() + }), + prop_struct!(EventType::SentBtcTransaction { + request_block_indices: pvec(any::(), 0..10_000), + txid: txid(), + utxos: pvec(utxo(amount()), 0..10_000), + change_output: option::of(change_output()), + submitted_at: any::(), + fee_per_vbyte: option::of(any::()), + }), + prop_struct!(EventType::ReplacedBtcTransaction { + old_txid: txid(), + new_txid: txid(), + change_output: change_output(), + submitted_at: any::(), + fee_per_vbyte: any::(), + }), + prop_struct!(EventType::ConfirmedBtcTransaction { txid: txid() }), + prop_struct!(EventType::CheckedUtxo { + utxo: utxo(amount()), + uuid: any::(), + clean: any::(), + kyt_provider: option::of(principal()) + }), + prop_struct!(EventType::CheckedUtxoV2 { + utxo: utxo(amount()), + account: account(), + }), + prop_struct!(EventType::IgnoredUtxo { + utxo: utxo(amount()) + }), + prop_struct!(EventType::SuspendedUtxo { + utxo: utxo(amount()), + account: account(), + reason: suspended_reason(), + }), + prop_struct!(EventType::DistributedKytFee { + kyt_provider: principal(), + amount: any::(), + block_index: any::(), + }), + prop_struct!(EventType::RetrieveBtcKytFailed { + owner: principal(), + address: ".*", + amount: any::(), + uuid: ".*", + kyt_provider: principal(), + block_index: any::(), + }), + prop_struct!(EventType::ScheduleDepositReimbursement { + account: account(), + amount: any::(), + reason: reimbursement_reason(), + burn_block_index: any::(), + }), + prop_struct!(EventType::ReimbursedFailedDeposit { + burn_block_index: any::(), + mint_block_index: any::(), + }), + ] + } + } +} diff --git a/rs/bitcoin/ckbtc/minter/src/tests.rs b/rs/bitcoin/ckbtc/minter/src/tests.rs index e492584e597..b772c95e58b 100644 --- a/rs/bitcoin/ckbtc/minter/src/tests.rs +++ b/rs/bitcoin/ckbtc/minter/src/tests.rs @@ -1,33 +1,29 @@ -use crate::state::invariants::CheckInvariantsImpl; -use crate::{ - address::BitcoinAddress, build_unsigned_transaction, estimate_retrieve_btc_fee, fake_sign, - greedy, signature::EncodedSignature, tx, BuildTxError, -}; -use crate::{evaluate_minter_fee, MINTER_ADDRESS_DUST_LIMIT}; use crate::{ + address::BitcoinAddress, + build_unsigned_transaction, estimate_retrieve_btc_fee, evaluate_minter_fee, fake_sign, greedy, lifecycle::init::InitArgs, + state::invariants::CheckInvariantsImpl, state::{ ChangeOutput, CkBtcMinterState, Mode, RetrieveBtcRequest, RetrieveBtcStatus, SubmittedBtcTransaction, }, + test_fixtures::arbitrary, + tx, BuildTxError, MINTER_ADDRESS_DUST_LIMIT, }; use bitcoin::network::constants::Network as BtcNetwork; use bitcoin::util::psbt::serialize::{Deserialize, Serialize}; use candid::Principal; -use ic_base_types::{CanisterId, PrincipalId}; -use ic_btc_interface::{Network, OutPoint, Satoshi, Txid, Utxo}; +use ic_base_types::CanisterId; +use ic_btc_interface::{Network, OutPoint, Utxo}; use icrc_ledger_types::icrc1::account::Account; use maplit::btreeset; -use proptest::proptest; use proptest::{ array::uniform20, - array::uniform32, - collection::{btree_set, vec as pvec, SizeRange}, + collection::{btree_set, vec as pvec}, option, - prelude::{any, Strategy}, + prelude::any, + prop_assert, prop_assert_eq, prop_assume, proptest, }; -use proptest::{prop_assert, prop_assert_eq, prop_assume, prop_oneof}; -use serde_bytes::ByteBuf; use std::collections::{BTreeMap, BTreeSet, HashMap}; use std::str::FromStr; @@ -444,121 +440,6 @@ fn test_no_dust_in_change_output() { } } -fn arb_amount() -> impl Strategy { - 1..10_000_000_000u64 -} - -fn vec_to_txid(vec: Vec) -> Txid { - let bytes: [u8; 32] = vec.try_into().expect("Can't convert to [u8; 32]"); - bytes.into() -} - -fn arb_out_point() -> impl Strategy { - (pvec(any::(), 32), any::()).prop_map(|(txid, vout)| tx::OutPoint { - txid: vec_to_txid(txid), - vout, - }) -} - -fn arb_unsigned_input( - value: impl Strategy, -) -> impl Strategy { - (arb_out_point(), value, any::()).prop_map(|(previous_output, value, sequence)| { - tx::UnsignedInput { - previous_output, - value, - sequence, - } - }) -} - -fn arb_signed_input() -> impl Strategy { - ( - arb_out_point(), - any::(), - pvec(1u8..0xff, 64), - pvec(any::(), 32), - ) - .prop_map( - |(previous_output, sequence, sec1, pubkey)| tx::SignedInput { - previous_output, - sequence, - signature: EncodedSignature::from_sec1(&sec1), - pubkey: ByteBuf::from(pubkey), - }, - ) -} - -fn arb_address() -> impl Strategy { - prop_oneof![ - uniform20(any::()).prop_map(BitcoinAddress::P2wpkhV0), - uniform32(any::()).prop_map(BitcoinAddress::P2wshV0), - uniform32(any::()).prop_map(BitcoinAddress::P2trV1), - uniform20(any::()).prop_map(BitcoinAddress::P2pkh), - uniform20(any::()).prop_map(BitcoinAddress::P2sh), - ] -} - -fn arb_tx_out() -> impl Strategy { - (arb_amount(), arb_address()).prop_map(|(value, address)| tx::TxOut { value, address }) -} - -fn arb_utxo(amount: impl Strategy) -> impl Strategy { - (amount, pvec(any::(), 32), 0..5u32).prop_map(|(value, txid, vout)| Utxo { - outpoint: OutPoint { - txid: vec_to_txid(txid), - vout, - }, - value, - height: 0, - }) -} - -fn arb_account() -> impl Strategy { - (pvec(any::(), 32), option::of(uniform32(any::()))).prop_map(|(pk, subaccount)| { - Account { - owner: PrincipalId::new_self_authenticating(&pk).0, - subaccount, - } - }) -} - -fn arb_retrieve_btc_requests( - amount: impl Strategy, - num: impl Into, -) -> impl Strategy> { - let request_strategy = ( - amount, - arb_address(), - any::(), - 1569975147000..2069975147000u64, - option::of(any::()), - option::of(arb_account()), - ) - .prop_map( - |(amount, address, block_index, received_at, provider, reimbursement_account)| { - RetrieveBtcRequest { - amount, - address, - block_index, - received_at, - kyt_provider: provider - .map(|id| Principal::from(CanisterId::from_u64(id).get())), - reimbursement_account, - } - }, - ); - pvec(request_strategy, num).prop_map(|mut reqs| { - reqs.sort_by_key(|req| req.received_at); - - for (i, req) in reqs.iter_mut().enumerate() { - req.block_index = i as u64; - } - - reqs - }) -} - proptest! { #[test] fn greedy_solution_properties( @@ -621,8 +502,8 @@ proptest! { #[test] fn unsigned_tx_encoding_model( - inputs in pvec(arb_unsigned_input(5_000u64..1_000_000_000), 1..20), - outputs in pvec(arb_tx_out(), 1..20), + inputs in pvec(arbitrary::unsigned_input(5_000u64..1_000_000_000), 1..20), + outputs in pvec(arbitrary::tx_out(), 1..20), lock_time in any::(), ) { let arb_tx = tx::UnsignedTransaction { inputs, outputs, lock_time }; @@ -643,13 +524,13 @@ proptest! { fn unsigned_tx_sighash_model( inputs_data in pvec( ( - arb_utxo(5_000u64..1_000_000_000), + arbitrary::utxo(5_000u64..1_000_000_000), any::(), pvec(any::(), tx::PUBKEY_LEN) ), 1..20 ), - outputs in pvec(arb_tx_out(), 1..20), + outputs in pvec(arbitrary::tx_out(), 1..20), lock_time in any::(), ) { let inputs: Vec = inputs_data @@ -686,8 +567,8 @@ proptest! { #[test] fn signed_tx_encoding_model( - inputs in pvec(arb_signed_input(), 1..20), - outputs in pvec(arb_tx_out(), 1..20), + inputs in pvec(arbitrary::signed_input(), 1..20), + outputs in pvec(arbitrary::tx_out(), 1..20), lock_time in any::(), ) { let arb_tx = tx::SignedTransaction { inputs, outputs, lock_time }; @@ -707,7 +588,7 @@ proptest! { #[test] fn build_tx_splits_utxos( - mut utxos in btree_set(arb_utxo(5_000u64..1_000_000_000), 1..20), + mut utxos in btree_set(arbitrary::utxo(5_000u64..1_000_000_000), 1..20), dst_pkhash in uniform20(any::()), main_pkhash in uniform20(any::()), fee_per_vbyte in 1000..2000u64, @@ -754,7 +635,7 @@ proptest! { #[test] fn check_output_order( - mut utxos in btree_set(arb_utxo(1_000_000u64..1_000_000_000), 1..20), + mut utxos in btree_set(arbitrary::utxo(1_000_000u64..1_000_000_000), 1..20), dst_pkhash in uniform20(any::()), main_pkhash in uniform20(any::()), target in 50000..100000u64, @@ -776,7 +657,7 @@ proptest! { #[test] fn build_tx_handles_change_from_inputs( - mut utxos in btree_set(arb_utxo(1_000_000u64..1_000_000_000), 1..20), + mut utxos in btree_set(arbitrary::utxo(1_000_000u64..1_000_000_000), 1..20), dst_pkhash in uniform20(any::()), main_pkhash in uniform20(any::()), target in 50000..100000u64, @@ -824,7 +705,7 @@ proptest! { #[test] fn build_tx_does_not_modify_utxos_on_error( - mut utxos in btree_set(arb_utxo(5_000u64..1_000_000_000), 1..20), + mut utxos in btree_set(arbitrary::utxo(5_000u64..1_000_000_000), 1..20), dst_pkhash in uniform20(any::()), main_pkhash in uniform20(any::()), fee_per_vbyte in 1000..2000u64, @@ -858,8 +739,8 @@ proptest! { #[test] fn add_utxos_maintains_invariants( - utxos_acc_idx in pvec((arb_utxo(5_000u64..1_000_000_000), 0..5usize), 10..20), - accounts in pvec(arb_account(), 5), + utxos_acc_idx in pvec((arbitrary::utxo(5_000u64..1_000_000_000), 0..5usize), 10..20), + accounts in pvec(arbitrary::account(), 5), ) { let mut state = CkBtcMinterState::from(InitArgs { retrieve_btc_min_amount: 1000, @@ -873,9 +754,9 @@ proptest! { #[test] fn batching_preserves_invariants( - utxos_acc_idx in pvec((arb_utxo(5_000u64..1_000_000_000), 0..5usize), 10..20), - accounts in pvec(arb_account(), 5), - requests in arb_retrieve_btc_requests(5_000u64..1_000_000_000, 1..25), + utxos_acc_idx in pvec((arbitrary::utxo(5_000u64..1_000_000_000), 0..5usize), 10..20), + accounts in pvec(arbitrary::account(), 5), + requests in arbitrary::retrieve_btc_requests(5_000u64..1_000_000_000, 1..25), limit in 1..25usize, ) { let mut state = CkBtcMinterState::from(InitArgs { @@ -907,9 +788,9 @@ proptest! { #[test] fn tx_replacement_preserves_invariants( - accounts in pvec(arb_account(), 5), - utxos_acc_idx in pvec((arb_utxo(5_000_000u64..1_000_000_000), 0..5usize), 10..=10), - requests in arb_retrieve_btc_requests(5_000_000u64..10_000_000, 1..5), + accounts in pvec(arbitrary::account(), 5), + utxos_acc_idx in pvec((arbitrary::utxo(5_000_000u64..1_000_000_000), 0..5usize), 10..=10), + requests in arbitrary::retrieve_btc_requests(5_000_000u64..10_000_000, 1..5), main_pkhash in uniform20(any::()), resubmission_chain_length in 1..=5, ) { @@ -1029,7 +910,7 @@ proptest! { } #[test] - fn btc_address_display_model(address in arb_address()) { + fn btc_address_display_model(address in arbitrary::address()) { for network in [Network::Mainnet, Network::Testnet].iter() { let addr_str = address.display(*network); let btc_addr = address_to_btc_address(&address, *network); @@ -1038,7 +919,7 @@ proptest! { } #[test] - fn address_roundtrip(address in arb_address()) { + fn address_roundtrip(address in arbitrary::address()) { for network in [Network::Mainnet, Network::Testnet, Network::Regtest].iter() { let addr_str = address.display(*network); prop_assert_eq!(BitcoinAddress::parse(&addr_str, *network), Ok(address.clone())); @@ -1110,7 +991,7 @@ proptest! { #[test] fn test_fee_range( - utxos in btree_set(arb_utxo(5_000u64..1_000_000_000), 0..20), + utxos in btree_set(arbitrary::utxo(5_000u64..1_000_000_000), 0..20), amount in option::of(any::()), fee_per_vbyte in 2000..10000u64, ) { diff --git a/rs/bitcoin/ckbtc/minter/tests/replay_events.rs b/rs/bitcoin/ckbtc/minter/tests/replay_events.rs index c9bfadfbc8d..238e635218c 100644 --- a/rs/bitcoin/ckbtc/minter/tests/replay_events.rs +++ b/rs/bitcoin/ckbtc/minter/tests/replay_events.rs @@ -5,6 +5,66 @@ use ic_ckbtc_minter::state::invariants::{CheckInvariants, CheckInvariantsImpl}; use ic_ckbtc_minter::state::{CkBtcMinterState, Network}; use std::path::PathBuf; +fn assert_useless_events_is_empty(events: impl Iterator) { + let mut count = 0; + for event in events { + match &event.payload { + EventType::ReceivedUtxos { utxos, .. } if utxos.is_empty() => { + count += 1; + } + _ => {} + } + } + assert_eq!(count, 0); +} + +async fn should_migrate_events_for(file: GetEventsFile) -> CkBtcMinterState { + use ic_ckbtc_minter::storage::{decode_event, encode_event, migrate_events}; + use ic_stable_structures::{ + log::Log as StableLog, + memory_manager::{MemoryId, MemoryManager}, + DefaultMemoryImpl, + }; + + file.retrieve_and_store_events_if_env().await; + + let mgr = MemoryManager::init(DefaultMemoryImpl::default()); + let old_events = StableLog::new(mgr.get(MemoryId::new(0)), mgr.get(MemoryId::new(1))); + let new_events = StableLog::new(mgr.get(MemoryId::new(2)), mgr.get(MemoryId::new(3))); + let events = file.deserialize().events; + events.iter().for_each(|event| { + old_events.append(&encode_event(event)).unwrap(); + }); + let removed = migrate_events(&old_events, &new_events); + assert!(removed > 0); + assert!(!new_events.is_empty()); + assert_eq!(new_events.len() + removed, old_events.len()); + assert_useless_events_is_empty(new_events.iter().map(|bytes| decode_event(&bytes))); + + let state = + replay::(new_events.iter().map(|bytes| decode_event(&bytes))) + .expect("Failed to replay events"); + state + .check_invariants() + .expect("Failed to check invariants"); + + state +} + +#[tokio::test] +async fn should_migrate_events_for_mainnet() { + let state = should_migrate_events_for(GetEventsFile::Mainnet).await; + assert_eq!(state.btc_network, Network::Mainnet); + assert_eq!(state.get_total_btc_managed(), 21_723_786_340); +} + +#[tokio::test] +async fn should_migrate_events_for_testnet() { + let state = should_migrate_events_for(GetEventsFile::Testnet).await; + assert_eq!(state.btc_network, Network::Testnet); + assert_eq!(state.get_total_btc_managed(), 16578205978); +} + #[tokio::test] async fn should_replay_events_for_mainnet() { GetEventsFile::Mainnet diff --git a/rs/bitcoin/ckbtc/minter/tests/tests.rs b/rs/bitcoin/ckbtc/minter/tests/tests.rs index 6f2190eb8ab..d6bb399554d 100644 --- a/rs/bitcoin/ckbtc/minter/tests/tests.rs +++ b/rs/bitcoin/ckbtc/minter/tests/tests.rs @@ -26,14 +26,14 @@ use ic_ckbtc_minter::{ Log, MinterInfo, CKBTC_LEDGER_MEMO_SIZE, MIN_RELAY_FEE_PER_VBYTE, MIN_RESUBMISSION_DELAY, }; use ic_icrc1_ledger::{InitArgsBuilder as LedgerInitArgsBuilder, LedgerArgument}; -use ic_state_machine_tests::{StateMachine, StateMachineBuilder, WasmResult}; +use ic_metrics_assert::{CanisterHttpQuery, MetricsAssert}; +use ic_state_machine_tests::{StateMachine, StateMachineBuilder, UserError, WasmResult}; use ic_test_utilities_load_wasm::load_wasm; use ic_types::Cycles; use icrc_ledger_types::icrc1::account::Account; use icrc_ledger_types::icrc1::transfer::{TransferArg, TransferError}; use icrc_ledger_types::icrc2::approve::{ApproveArgs, ApproveError}; use icrc_ledger_types::icrc3::transactions::{GetTransactionsRequest, GetTransactionsResponse}; -use regex::Regex; use std::collections::BTreeMap; use std::path::PathBuf; use std::str::FromStr; @@ -1232,8 +1232,16 @@ impl CkBtcSetup { .expect("minter self-check failed") } - pub fn check_minter_metrics(self) -> MetricsAssert { - MetricsAssert::from_querying_metrics(self.env, self.minter_id) + pub fn check_minter_metrics(self) -> MetricsAssert { + MetricsAssert::from_http_query(self) + } +} + +impl CanisterHttpQuery for CkBtcSetup { + fn http_query(&self, request: Vec) -> Result, UserError> { + self.env + .query(self.minter_id, "http_request", request) + .map(assert_reply) } } @@ -2110,69 +2118,3 @@ fn test_retrieve_btc_with_approval_fail() { vec![] ); } - -pub struct MetricsAssert { - metrics: Vec, -} - -impl MetricsAssert { - pub fn from_querying_metrics(state_machine: StateMachine, canister_id: CanisterId) -> Self { - use ic_canisters_http_types::{HttpRequest, HttpResponse}; - let request = HttpRequest { - method: "GET".to_string(), - url: "/metrics".to_string(), - headers: Default::default(), - body: Default::default(), - }; - let response = Decode!( - &assert_reply( - state_machine - .query( - canister_id, - "http_request", - Encode!(&request).expect("failed to encode HTTP request"), - ) - .expect("failed to get metrics") - ), - HttpResponse - ) - .unwrap(); - assert_eq!(response.status_code, 200_u16); - let metrics = String::from_utf8_lossy(response.body.as_slice()) - .trim() - .split('\n') - .map(|line| line.to_string()) - .collect::>(); - Self { metrics } - } - - pub fn assert_contains_metric_matching(self, pattern: &str) -> Self { - assert!( - !self.find_metrics_matching(pattern).is_empty(), - "Expected to find metric matching '{}', but none matched in:\n{:?}", - pattern, - self.metrics - ); - self - } - - pub fn assert_does_not_contain_metric_matching(self, pattern: &str) -> Self { - let matches = self.find_metrics_matching(pattern); - assert!( - matches.is_empty(), - "Expected not to find any metric matching '{}', but found the following matches:\n{:?}", - pattern, - matches - ); - self - } - - fn find_metrics_matching(&self, pattern: &str) -> Vec { - let regex = Regex::new(pattern).unwrap_or_else(|_| panic!("Invalid regex: {}", pattern)); - self.metrics - .iter() - .filter(|line| regex.is_match(line)) - .cloned() - .collect() - } -} diff --git a/rs/bitcoin/ckbtc/staging/dfx.json b/rs/bitcoin/ckbtc/staging/dfx.json index 2089106eb7f..633a51aa2ef 100644 --- a/rs/bitcoin/ckbtc/staging/dfx.json +++ b/rs/bitcoin/ckbtc/staging/dfx.json @@ -1,63 +1,63 @@ { - "version": 1, - "dfx": "0.12.1", - "canisters": { - "minter": { - "type": "custom", - "build": [ - "bazel build //rs/bitcoin/ckbtc/minter:ckbtc_minter" - ], - "wasm": "../../../../bazel-bin/rs/bitcoin/ckbtc/minter/ckbtc_minter.wasm", - "candid": "../minter/ckbtc_minter.did", - "shrink": false - }, - "ledger": { - "type": "custom", - "build": [ - "bazel build //rs/ledger_suite/icrc1/ledger:ledger_canister" - ], - "wasm": "../../../../bazel-bin/rs/ledger_suite/icrc1/ledger/ledger_canister.wasm", - "candid": "../../../ledger_suite/icrc1/ledger/ledger.did", - "shrink": false - }, - "archive": { - "type": "custom", - "build": [ - "bazel build //rs/ledger_suite/icrc1/archive:archive_canister" - ], - "wasm": "../../../../bazel-bin/rs/ledger_suite/icrc1/archive/archive_canister.wasm", - "candid": "../../../ledger_suite/icrc1/archive/archive.did", - "shrink": false - }, - "index": { - "type": "custom", - "build": [ - "bazel build //rs/ledger_suite/icrc1/index:index_canister" - ], - "wasm": "../../../../bazel-bin/rs/ledger_suite/icrc1/index/index_canister.wasm", - "candid": "../../../ledger_suite/icrc1/index/index.did", - "shrink": false - }, - "kyt": { - "type": "custom", - "build": [ - "bazel build //rs/bitcoin/ckbtc/kyt:kyt_canister" - ], - "wasm": "../../../../bazel-bin/rs/bitcoin/ckbtc/kyt/kyt_canister.wasm", - "candid": "../kyt/kyt.did", - "shrink": false - } + "version": 1, + "dfx": "0.12.1", + "canisters": { + "minter": { + "type": "custom", + "build": [ + "bazel build //rs/bitcoin/ckbtc/minter:ckbtc_minter" + ], + "wasm": "../../../../bazel-bin/rs/bitcoin/ckbtc/minter/ckbtc_minter.wasm", + "candid": "../minter/ckbtc_minter.did", + "shrink": false }, - "defaults": { - "build": { - "packtool": "", - "args": "" - } + "ledger": { + "type": "custom", + "build": [ + "bazel build //rs/ledger_suite/icrc1/ledger:ledger_canister" + ], + "wasm": "../../../../bazel-bin/rs/ledger_suite/icrc1/ledger/ledger_canister.wasm", + "candid": "../../../ledger_suite/icrc1/ledger/ledger.did", + "shrink": false }, - "networks": { - "local": { - "bind": "127.0.0.1:8000", - "type": "ephemeral" - } + "archive": { + "type": "custom", + "build": [ + "bazel build //rs/ledger_suite/icrc1/archive:archive_canister" + ], + "wasm": "../../../../bazel-bin/rs/ledger_suite/icrc1/archive/archive_canister.wasm", + "candid": "../../../ledger_suite/icrc1/archive/archive.did", + "shrink": false + }, + "index": { + "type": "custom", + "build": [ + "bazel build //rs/ledger_suite/icrc1/index-ng:index_ng_canister" + ], + "wasm": "../../../../bazel-bin/rs/ledger_suite/icrc1/index-ng/index_ng_canister.wasm", + "candid": "../../../ledger_suite/icrc1/index-ng/index-ng.did", + "shrink": false + }, + "kyt": { + "type": "custom", + "build": [ + "bazel build //rs/bitcoin/ckbtc/kyt:kyt_canister" + ], + "wasm": "../../../../bazel-bin/rs/bitcoin/ckbtc/kyt/kyt_canister.wasm", + "candid": "../kyt/kyt.did", + "shrink": false + } + }, + "defaults": { + "build": { + "packtool": "", + "args": "" + } + }, + "networks": { + "local": { + "bind": "127.0.0.1:8000", + "type": "ephemeral" } -} \ No newline at end of file + } +} diff --git a/rs/bitcoin/consensus/src/payload_builder/parse.rs b/rs/bitcoin/consensus/src/payload_builder/parse.rs index 520de3d848d..8a5281e116a 100644 --- a/rs/bitcoin/consensus/src/payload_builder/parse.rs +++ b/rs/bitcoin/consensus/src/payload_builder/parse.rs @@ -1,11 +1,13 @@ use ic_btc_replica_types::BitcoinAdapterResponse; -use ic_interfaces::batch_payload::{iterator_to_bytes, slice_to_messages, PastPayload}; +use ic_interfaces::batch_payload::PastPayload; use ic_logger::{error, warn, ReplicaLogger}; use ic_protobuf::{ bitcoin::v1::BitcoinAdapterResponse as PbBitcoinAdapterResponse, proxy::ProxyDecodeError, }; use ic_types::{ - batch::{SelfValidatingPayload, MAX_BITCOIN_PAYLOAD_IN_BYTES}, + batch::{ + iterator_to_bytes, slice_to_messages, SelfValidatingPayload, MAX_BITCOIN_PAYLOAD_IN_BYTES, + }, NumBytes, }; use prost::Message; diff --git a/rs/bitcoin/mock/tests/tests.rs b/rs/bitcoin/mock/tests/tests.rs index a2b5b4ef574..0d0b71cd890 100644 --- a/rs/bitcoin/mock/tests/tests.rs +++ b/rs/bitcoin/mock/tests/tests.rs @@ -152,7 +152,7 @@ fn test_install_bitcoin_mock_canister() { .expect("failed to decode get_mempool response"); assert_eq!(mempool.len(), 1); - let tx = deserialize::(&mempool[0]).expect("failed to parse transaction"); + let tx: Transaction = deserialize(&mempool[0]).expect("failed to parse transaction"); assert_eq!(tx.input.len(), 1); assert_eq!(tx.output.len(), 2); diff --git a/rs/bitcoin/replica_types/src/lib.rs b/rs/bitcoin/replica_types/src/lib.rs index 5213797bafe..269d01b2eee 100644 --- a/rs/bitcoin/replica_types/src/lib.rs +++ b/rs/bitcoin/replica_types/src/lib.rs @@ -414,6 +414,7 @@ impl From for SendTransactionResponse { impl SendTransactionResponse { /// Returns the size of this `SendTransactionResponse` in bytes. pub fn count_bytes(&self) -> usize { + let SendTransactionResponse {} = &self; 0 } } @@ -429,7 +430,11 @@ pub struct BitcoinReject { impl BitcoinReject { /// Returns the size of this `RejectResponse` in bytes. pub fn count_bytes(&self) -> usize { - size_of_val(&self.reject_code) + self.message.len() + let BitcoinReject { + reject_code, + message, + } = &self; + size_of_val(reject_code) + message.len() } } @@ -598,7 +603,11 @@ impl TryFrom for BitcoinAdapterResponse { impl BitcoinAdapterResponse { /// Returns the size of this `BitcoinAdapterResponse` in bytes. pub fn count_bytes(&self) -> usize { - self.response.count_bytes() + std::mem::size_of::() + let BitcoinAdapterResponse { + response, + callback_id, + } = &self; + response.count_bytes() + size_of_val(callback_id) } } diff --git a/rs/bitcoin/validation/Cargo.toml b/rs/bitcoin/validation/Cargo.toml index 9198b12fbbe..752eea92d36 100644 --- a/rs/bitcoin/validation/Cargo.toml +++ b/rs/bitcoin/validation/Cargo.toml @@ -10,6 +10,8 @@ documentation.workspace = true [dependencies] bitcoin = { workspace = true } +hex = { workspace = true } [dev-dependencies] csv = "1.1" +proptest = "0.9.4" diff --git a/rs/bitcoin/validation/src/constants.rs b/rs/bitcoin/validation/src/constants.rs index 432e752b4d7..5a632b27808 100644 --- a/rs/bitcoin/validation/src/constants.rs +++ b/rs/bitcoin/validation/src/constants.rs @@ -1,6 +1,6 @@ -use std::collections::HashMap; +use std::{collections::HashMap, str::FromStr}; -use bitcoin::{hashes::hex::FromHex, util::uint::Uint256, BlockHash, Network}; +use bitcoin::{BlockHash, CompactTarget, Network, Target}; use crate::BlockHeight; @@ -57,45 +57,14 @@ const TESTNET: &[(BlockHeight, &str)] = &[ (546, "000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70") ]; -/// Bitcoin mainnet maximum target value -const BITCOIN_MAX_TARGET: Uint256 = Uint256([ - 0x0000000000000000, - 0x0000000000000000, - 0x0000000000000000, - 0x00000000ffff0000, -]); - -/// Bitcoin testnet maximum target value -const TESTNET_MAX_TARGET: Uint256 = Uint256([ - 0x0000000000000000, - 0x0000000000000000, - 0x0000000000000000, - 0x00000000ffff0000, -]); - -/// Bitcoin regtest maximum target value -const REGTEST_MAX_TARGET: Uint256 = Uint256([ - 0x0000000000000000, - 0x0000000000000000, - 0x0000000000000000, - 0x7fffff0000000000, -]); - -/// Bitcoin signet maximum target value -const SIGNET_MAX_TARGET: Uint256 = Uint256([ - 0x0000000000000000u64, - 0x0000000000000000u64, - 0x0000000000000000u64, - 0x00000377ae000000u64, -]); - /// Returns the maximum difficulty target depending on the network -pub fn max_target(network: &Network) -> Uint256 { +pub fn max_target(network: &Network) -> Target { match network { - Network::Bitcoin => BITCOIN_MAX_TARGET, - Network::Testnet => TESTNET_MAX_TARGET, - Network::Regtest => REGTEST_MAX_TARGET, - Network::Signet => SIGNET_MAX_TARGET, + Network::Bitcoin => Target::MAX_ATTAINABLE_MAINNET, + Network::Testnet => Target::MAX_ATTAINABLE_TESTNET, + Network::Regtest => Target::MAX_ATTAINABLE_REGTEST, + Network::Signet => Target::MAX_ATTAINABLE_SIGNET, + &other => unreachable!("Unsupported network: {:?}", other), } } @@ -105,17 +74,19 @@ pub fn no_pow_retargeting(network: &Network) -> bool { match network { Network::Bitcoin | Network::Testnet | Network::Signet => false, Network::Regtest => true, + &other => unreachable!("Unsupported network: {:?}", other), } } /// Returns the PoW limit bits of the bitcoin network -pub fn pow_limit_bits(network: &Network) -> u32 { - match network { +pub fn pow_limit_bits(network: &Network) -> CompactTarget { + CompactTarget::from_consensus(match network { Network::Bitcoin => 0x1d00ffff, Network::Testnet => 0x1d00ffff, Network::Regtest => 0x207fffff, Network::Signet => 0x1e0377ae, - } + &other => unreachable!("Unsupported network: {:?}", other), + }) } /// Checkpoints used to validate blocks at certain heights. @@ -123,14 +94,17 @@ pub fn checkpoints(network: &Network) -> HashMap { let points = match network { Network::Bitcoin => BITCOIN, Network::Testnet => TESTNET, + Network::Testnet4 => &[], Network::Signet => &[], Network::Regtest => &[], + _ => &[], }; points .iter() .cloned() .map(|(height, hash)| { - let hash = BlockHash::from_hex(hash).expect("Programmer error: invalid hash"); + //TODO: handle this unwrap without crashing + let hash = BlockHash::from_str(hash).expect("Programmer error: invalid hash"); (height, hash) }) .collect() @@ -140,8 +114,10 @@ pub fn latest_checkpoint_height(network: &Network, current_height: BlockHeight) let points = match network { Network::Bitcoin => BITCOIN, Network::Testnet => TESTNET, + Network::Testnet4 => &[], Network::Signet => &[], Network::Regtest => &[], + _ => &[], }; points @@ -151,29 +127,8 @@ pub fn latest_checkpoint_height(network: &Network, current_height: BlockHeight) .map_or(0, |(height, _)| *height) } -pub fn last_checkpoint(network: &Network) -> Option { - let points = match network { - Network::Bitcoin => BITCOIN, - Network::Testnet => TESTNET, - Network::Signet => &[], - Network::Regtest => &[], - }; - - points.last().map(|(height, _)| *height) -} - #[cfg(test)] pub mod test { - - use super::*; - - /// Mainnet 00000000bcb3c8ff4e3e243ad47832d75bb81e922efdc05be63f2696c5dfad09 - pub const MAINNET_HEADER_11109: &str = "0100000027e37046713f768e57bd9c613f70657048320cab3e016c6ad437dadd00000000a12e0863a26054892799db694b8ccd9f44ad062b4d6ef09d2be12e994d50649b9ca2e649ffff001d2823bacb"; - /// Mainnet 00000000deaa3a36d8531844fd1cb11faff6a1171d5228d42131d1b302c56271 - pub const MAINNET_HEADER_11110: &str = "0100000009addfc596263fe65bc0fd2e921eb85bd73278d43a243e4effc8b3bc0000000006413a83ca2d3fbf6b1ac332976043152e0093f17e29fef68c3eb736d379d7365aa3e649ffff001da44e7f02"; - /// Mainnet 0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d - pub const MAINNET_HEADER_11111: &str = "010000007162c502b3d13121d428521d17a1f6af1fb11cfd441853d8363aaade000000007adf7b53a9dc840a210766054aa8a1b2076fd30dadcc3f757c23318a8c8be55213a4e649ffff001d05d9428b"; - /// Mainnet 000000000000000000063108ecc1f03f7fd1481eb20f97307d532a612bc97f04 pub const MAINNET_HEADER_586656: &str ="00008020cff0e07ab39db0f31d4ded81ba2339173155b9c57839110000000000000000007a2d75dce5981ec421a54df706d3d407f66dc9170f1e0d6e48ed1e8a1cad7724e9ed365d083a1f17bc43b10a"; /// Mainnet 0000000000000000000d37dfef7fe1c7bd22c893dbe4a94272c8cf556e40be99 @@ -187,22 +142,4 @@ pub mod test { pub const TESTNET_HEADER_2132555: &str = "004000200e1ff99438666c67c649def743fb82117537c2017bcc6ad617000000000000007fa40cf82bf224909e3174281a57af2eb3a4a2a961d33f50ec0772c1221c9e61ddfdc061ffff001a64526636"; /// Testnet 00000000383cd7fff4692410ccd9bd6201790043bb41b93bacb21e9b85620767 pub const TESTNET_HEADER_2132556: &str = "00000020974f55e77dff100bc252a01aa7b00d16736c6e04a091b03be200000000000000c44f2d69fc200c4a2211885000b6b67512f42c1bec550f3754e103b6c4046e05a202c161ffff001d09ec1bc4"; - - #[test] - fn test_latest_checkpoint_height() { - let height = latest_checkpoint_height(&Network::Bitcoin, 1_000_000); - assert_eq!(height, 704_256); - - let height = latest_checkpoint_height(&Network::Bitcoin, 40_000); - assert_eq!(height, 33_333); - - let height = latest_checkpoint_height(&Network::Testnet, 1_000_000); - assert_eq!(height, 546); - } - - #[test] - fn test_last_checkpoint() { - assert_eq!(last_checkpoint(&Network::Bitcoin), Some(704_256)); - assert_eq!(last_checkpoint(&Network::Regtest), None); - } } diff --git a/rs/bitcoin/validation/src/header.rs b/rs/bitcoin/validation/src/header.rs index b17d0ba47d7..ef7769ebaf4 100644 --- a/rs/bitcoin/validation/src/header.rs +++ b/rs/bitcoin/validation/src/header.rs @@ -1,21 +1,29 @@ -use bitcoin::{util::uint::Uint256, BlockHash, BlockHeader, Network}; +use bitcoin::{ + block::Header as BlockHeader, block::ValidationError, BlockHash, CompactTarget, Network, Target, +}; use crate::{ constants::{ - checkpoints, last_checkpoint, latest_checkpoint_height, max_target, no_pow_retargeting, - pow_limit_bits, BLOCKS_IN_ONE_YEAR, DIFFICULTY_ADJUSTMENT_INTERVAL, TEN_MINUTES, + checkpoints, latest_checkpoint_height, max_target, no_pow_retargeting, pow_limit_bits, + BLOCKS_IN_ONE_YEAR, DIFFICULTY_ADJUSTMENT_INTERVAL, TEN_MINUTES, }, BlockHeight, }; /// An error thrown when trying to validate a header. -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum ValidateHeaderError { /// Used when the timestamp in the header is lower than /// the median of timestamps of past 11 headers. HeaderIsOld, /// Used when the header doesn't match with a checkpoint. DoesNotMatchCheckpoint, + /// Used when the timestamp in the header is more than 2 hours + /// from the current time. + HeaderIsTooFarInFuture { + block_time: u64, + max_allowed_time: u64, + }, /// Used when the PoW in the header is invalid as per the target mentioned /// in the header. InvalidPoWForHeaderTarget, @@ -33,12 +41,13 @@ pub enum ValidateHeaderError { } pub trait HeaderStore { - /// Retrieves the header from the store. + /// Returns the header with the given block hash. fn get_header(&self, hash: &BlockHash) -> Option<(BlockHeader, BlockHeight)>; - /// Retrieves the current height of the block chain. - fn get_height(&self) -> BlockHeight; - /// Retrieves the initial hash the store starts from. + + /// Returns the initial hash the store starts from. fn get_initial_hash(&self) -> BlockHash; + + fn get_height(&self) -> BlockHeight; } /// Validates a header. If a failure occurs, a @@ -73,15 +82,16 @@ pub fn validate_header( return Err(ValidateHeaderError::TargetDifficultyAboveMax); } - if header.validate_pow(&header_target).is_err() { + if header.validate_pow(header_target).is_err() { return Err(ValidateHeaderError::InvalidPoWForHeaderTarget); } - let target = get_next_target(network, store, &prev_header, prev_height, header); - if let Err(err) = header.validate_pow(&target) { + let compact_target = + get_next_compact_target(network, store, &prev_header, prev_height, header.time); + if let Err(err) = header.validate_pow(Target::from_compact(compact_target)) { match err { - bitcoin::Error::BlockBadProofOfWork => println!("bad proof of work"), - bitcoin::Error::BlockBadTarget => println!("bad target"), + ValidationError::BadProofOfWork => println!("bad proof of work"), + ValidationError::BadTarget => println!("bad target"), _ => {} }; return Err(ValidateHeaderError::InvalidPoWForComputedTarget); @@ -90,16 +100,6 @@ pub fn validate_header( Ok(()) } -/// Checks if block height is higher than the last checkpoint height. -/// By beeing beyond the last checkpoint we are sure that we store the correct chain up to the height -/// of the last checkpoint. -pub fn is_beyond_last_checkpoint(network: &Network, height: BlockHeight) -> bool { - match last_checkpoint(network) { - Some(last) => last <= height, - None => true, - } -} - /// This validates the header against the network's checkpoints. /// 1. If the next header is at a checkpoint height, the checkpoint is compared to the next header's block hash. /// 2. If the header is not the same height, the function then compares the height to the latest checkpoint. @@ -154,19 +154,15 @@ fn is_timestamp_valid(store: &impl HeaderStore, header: &BlockHeader) -> bool { header.time > median } -/// Gets the next target by doing the following: -/// * If the network allows blocks to have the max target (testnet & regtest), -/// the next difficulty is searched for unless the header's timestamp is -/// greater than 20 minutes from the previous header's timestamp. -/// * If the network does not allow blocks with the max target, the next -/// difficulty is computed and then cast into the next target. -fn get_next_target( +// Returns the next required target at the given timestamp. +// The target is the number that a block hash must be below for it to be accepted. +fn get_next_compact_target( network: &Network, store: &impl HeaderStore, prev_header: &BlockHeader, prev_height: BlockHeight, - header: &BlockHeader, -) -> Uint256 { + timestamp: u32, +) -> CompactTarget { match network { Network::Testnet | Network::Regtest => { if (prev_height + 1) % DIFFICULTY_ADJUSTMENT_INTERVAL != 0 { @@ -175,32 +171,23 @@ fn get_next_target( // "If no block has been found in 20 minutes, the difficulty automatically // resets back to the minimum for a single block, after which it // returns to its previous value." - if header.time > prev_header.time + TEN_MINUTES * 2 { + if timestamp > prev_header.time + TEN_MINUTES * 2 { //If no block has been found in 20 minutes, then use the maximum difficulty // target - max_target(network) + max_target(network).to_compact_lossy() } else { //If the block has been found within 20 minutes, then use the previous // difficulty target that is not equal to the maximum difficulty target - BlockHeader::u256_from_compact_target(find_next_difficulty_in_chain( - network, - store, - prev_header, - prev_height, - )) + find_next_difficulty_in_chain(network, store, prev_header, prev_height) } } else { - BlockHeader::u256_from_compact_target(compute_next_difficulty( - network, - store, - prev_header, - prev_height, - )) + compute_next_difficulty(network, store, prev_header, prev_height) } } - Network::Bitcoin | Network::Signet => BlockHeader::u256_from_compact_target( - compute_next_difficulty(network, store, prev_header, prev_height), - ), + Network::Bitcoin | Network::Signet => { + compute_next_difficulty(network, store, prev_header, prev_height) + } + &other => unreachable!("Unsupported network: {:?}", other), } } @@ -216,36 +203,44 @@ fn find_next_difficulty_in_chain( store: &impl HeaderStore, prev_header: &BlockHeader, prev_height: BlockHeight, -) -> u32 { +) -> CompactTarget { // This is the maximum difficulty target for the network let pow_limit_bits = pow_limit_bits(network); match network { Network::Testnet | Network::Regtest => { let mut current_header = *prev_header; let mut current_height = prev_height; - let mut current_hash = prev_header.block_hash(); + let mut current_hash = current_header.block_hash(); let initial_header_hash = store.get_initial_hash(); // Keep traversing the blockchain backwards from the recent block to initial // header hash. - while current_hash != initial_header_hash { + loop { + // Check if non-limit PoW found or it's time to adjust difficulty. if current_header.bits != pow_limit_bits || current_height % DIFFICULTY_ADJUSTMENT_INTERVAL == 0 { return current_header.bits; } - // Traverse to the previous header - let header_info = store - .get_header(¤t_header.prev_blockhash) + // Stop if we reach the initial header. + if current_hash == initial_header_hash { + break; + } + + // Traverse to the previous header. + let prev_blockhash = current_header.prev_blockhash; + (current_header, _) = store + .get_header(&prev_blockhash) .expect("previous header should be in the header store"); - current_header = header_info.0; - current_height = header_info.1; - current_hash = current_header.prev_blockhash; + // Update the current height and hash. + current_height -= 1; + current_hash = prev_blockhash; } pow_limit_bits } Network::Bitcoin | Network::Signet => pow_limit_bits, + &other => unreachable!("Unsupported network: {:?}", other), } } @@ -256,13 +251,14 @@ fn compute_next_difficulty( store: &impl HeaderStore, prev_header: &BlockHeader, prev_height: BlockHeight, -) -> u32 { +) -> CompactTarget { // Difficulty is adjusted only once in every interval of 2 weeks (2016 blocks) // If an interval boundary is not reached, then previous difficulty target is // returned Regtest network doesn't adjust PoW difficult levels. For // regtest, simply return the previous difficulty target - if (prev_height + 1) % DIFFICULTY_ADJUSTMENT_INTERVAL != 0 || no_pow_retargeting(network) { + let height = prev_height + 1; + if height % DIFFICULTY_ADJUSTMENT_INTERVAL != 0 || no_pow_retargeting(network) { return prev_header.bits; } @@ -283,32 +279,10 @@ fn compute_next_difficulty( // actual_interval will deviate slightly from 2 weeks. Our goal is to // readjust the difficulty target so that the expected time taken for the next // 2016 blocks is again 2 weeks. - let actual_interval = (prev_header.time as i64) - (last_adjustment_time as i64); - - // The target_adjustment_interval_time is 2 weeks of time expressed in seconds - let target_adjustment_interval_time: i64 = - (DIFFICULTY_ADJUSTMENT_INTERVAL * TEN_MINUTES) as i64; - - // Adjusting the actual_interval to [0.5 week, 8 week] range in case the - // actual_interval deviates too much from the expected 2 weeks. - let adjusted_interval = actual_interval.clamp( - target_adjustment_interval_time / 4, - target_adjustment_interval_time * 4, - ) as u32; - - // Computing new difficulty target. - // new difficulty target = old difficult target * (adjusted_interval / - // 2_weeks); - let mut target = prev_header.target(); - target = target.mul_u32(adjusted_interval); - target = target / Uint256::from_u64(target_adjustment_interval_time as u64).unwrap(); - - // Adjusting the newly computed difficulty target so that it doesn't exceed the - // max_difficulty_target limit - target = Uint256::min(target, max_target(network)); - - // Converting the target (Uint256) into a 32 bit representation used by Bitcoin - BlockHeader::compact_target_from_u256(&target) + let actual_interval = + std::cmp::max((prev_header.time as i64) - (last_adjustment_time as i64), 0) as u64; + + CompactTarget::from_next_work_required(prev_header.bits, actual_interval, *network) } #[cfg(test)] @@ -316,13 +290,14 @@ mod test { use std::{collections::HashMap, path::PathBuf, str::FromStr}; - use bitcoin::{consensus::deserialize, hashes::hex::FromHex, TxMerkleNode}; + use bitcoin::{ + block::Version, consensus::deserialize, hashes::hex::FromHex, hashes::Hash, TxMerkleNode, + }; use csv::Reader; use super::*; use crate::constants::test::{ - MAINNET_HEADER_11109, MAINNET_HEADER_11110, MAINNET_HEADER_11111, MAINNET_HEADER_586656, - MAINNET_HEADER_705600, MAINNET_HEADER_705601, MAINNET_HEADER_705602, + MAINNET_HEADER_586656, MAINNET_HEADER_705600, MAINNET_HEADER_705601, MAINNET_HEADER_705602, TESTNET_HEADER_2132555, TESTNET_HEADER_2132556, }; @@ -335,12 +310,14 @@ mod test { struct SimpleHeaderStore { headers: HashMap, height: BlockHeight, + tip_hash: BlockHash, initial_hash: BlockHash, } impl SimpleHeaderStore { fn new(initial_header: BlockHeader, height: BlockHeight) -> Self { let initial_hash = initial_header.block_hash(); + let tip_hash = initial_header.block_hash(); let mut headers = HashMap::new(); headers.insert( initial_hash, @@ -353,6 +330,7 @@ mod test { Self { headers, height, + tip_hash, initial_hash, } } @@ -369,6 +347,7 @@ mod test { self.height = stored_header.height; self.headers.insert(header.block_hash(), stored_header); + self.tip_hash = header.block_hash(); } } @@ -379,13 +358,13 @@ mod test { .map(|stored| (stored.header, stored.height)) } - fn get_height(&self) -> BlockHeight { - self.height - } - fn get_initial_hash(&self) -> BlockHash { self.initial_hash } + + fn get_height(&self) -> BlockHeight { + self.height + } } fn deserialize_header(encoded_bytes: &str) -> BlockHeader { @@ -395,8 +374,6 @@ mod test { /// This function reads `num_headers` headers from `tests/data/headers.csv` /// and returns them. - /// This function reads `num_headers` headers from `blockchain_headers.csv` - /// and returns them. fn get_bitcoin_headers() -> Vec { let rdr = Reader::from_path( PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()) @@ -408,11 +385,15 @@ mod test { for result in rdr.records() { let record = result.unwrap(); let header = BlockHeader { - version: i32::from_str_radix(record.get(0).unwrap(), 16).unwrap(), + version: Version::from_consensus( + i32::from_str_radix(record.get(0).unwrap(), 16).unwrap(), + ), prev_blockhash: BlockHash::from_str(record.get(1).unwrap()).unwrap(), merkle_root: TxMerkleNode::from_str(record.get(2).unwrap()).unwrap(), time: u32::from_str_radix(record.get(3).unwrap(), 16).unwrap(), - bits: u32::from_str_radix(record.get(4).unwrap(), 16).unwrap(), + bits: CompactTarget::from_consensus( + u32::from_str_radix(record.get(4).unwrap(), 16).unwrap(), + ), nonce: u32::from_str_radix(record.get(5).unwrap(), 16).unwrap(), }; headers.push(header); @@ -420,17 +401,6 @@ mod test { headers } - fn genesis_header(bits: u32) -> BlockHeader { - BlockHeader { - version: 1, - prev_blockhash: Default::default(), - merkle_root: Default::default(), - time: 1296688602, - bits, - nonce: 0, - } - } - #[test] fn test_simple_mainnet() { let header_705600 = deserialize_header(MAINNET_HEADER_705600); @@ -466,39 +436,6 @@ mod test { } } - #[test] - fn test_is_timestamp_valid() { - let header_705600 = deserialize_header(MAINNET_HEADER_705600); - let header_705601 = deserialize_header(MAINNET_HEADER_705601); - let header_705602 = deserialize_header(MAINNET_HEADER_705602); - let mut store = SimpleHeaderStore::new(header_705600, 705_600); - store.add(header_705601); - store.add(header_705602); - - let mut header = BlockHeader { - version: 0x20800004, - prev_blockhash: BlockHash::from_hex( - "00000000000000000001eea12c0de75000c2546da22f7bf42d805c1d2769b6ef", - ) - .unwrap(), - merkle_root: TxMerkleNode::from_hex( - "c120ff2ae1363593a0b92e0d281ec341a0cc989b4ee836dc3405c9f4215242a6", - ) - .unwrap(), - time: 1634590600, - bits: 0x170e0408, - nonce: 0xb48e8b0a, - }; - assert!(is_timestamp_valid(&store, &header)); - - // Monday, October 18, 2021 20:26:40 - header.time = 1634588800; - assert!(!is_timestamp_valid(&store, &header)); - - let result = validate_header(&Network::Bitcoin, &store, &header); - assert!(matches!(result, Err(ValidateHeaderError::HeaderIsOld))); - } - #[test] fn test_is_header_valid_missing_prev_header() { let header_705600 = deserialize_header(MAINNET_HEADER_705600); @@ -511,82 +448,6 @@ mod test { )); } - #[test] - fn test_is_header_valid_checkpoint_valid_at_height() { - let network = Network::Bitcoin; - let header_11110 = deserialize_header(MAINNET_HEADER_11110); - let mut header_11111 = deserialize_header(MAINNET_HEADER_11111); - let store = SimpleHeaderStore::new(header_11110, 11110); - let (_, prev_height) = store.get_header(&header_11111.prev_blockhash).unwrap(); - - assert!(is_checkpoint_valid( - &network, - prev_height, - &header_11111, - store.get_height() - )); - - // Change time to slightly modify the block hash to make it invalid for the - // checkpoint. - header_11111.time -= 1; - - let result = validate_header(&network, &store, &header_11111); - assert!(matches!( - result, - Err(ValidateHeaderError::DoesNotMatchCheckpoint) - )); - } - - #[test] - fn test_is_header_valid_checkpoint_valid_detect_fork_around_11111() { - let network = Network::Bitcoin; - let header_11109 = deserialize_header(MAINNET_HEADER_11109); - let header_11110 = deserialize_header(MAINNET_HEADER_11110); - let header_11111 = deserialize_header(MAINNET_HEADER_11111); - // Make a header for height 11110 that would cause a fork. - let mut bad_header_11110 = header_11110; - bad_header_11110.time -= 1; - - let mut store = SimpleHeaderStore::new(header_11109, 11109); - store.add(header_11110); - - let (_, prev_height) = store.get_header(&header_11111.prev_blockhash).unwrap(); - - assert!(is_checkpoint_valid( - &network, - prev_height, - &header_11111, - store.get_height() - )); - - store.add(header_11111); - - // This should return false as bad_header_11110 is a fork. - let (_, prev_height) = store.get_header(&header_11111.prev_blockhash).unwrap(); - assert!(!is_checkpoint_valid( - &network, - prev_height, - &bad_header_11110, - store.get_height() - )); - } - - #[test] - fn test_is_header_valid_checkpoint_valid_detect_fork_around_705600() { - let network = Network::Bitcoin; - let header_705600 = deserialize_header(MAINNET_HEADER_705600); - let header_705601 = deserialize_header(MAINNET_HEADER_705601); - let store = SimpleHeaderStore::new(header_705600, 705_600); - let (_, prev_height) = store.get_header(&header_705601.prev_blockhash).unwrap(); - - assert!(is_checkpoint_valid( - &network, - prev_height, - &header_705601, - store.get_height() - )); - } - #[test] fn test_is_header_valid_invalid_header_target() { let header_705600 = deserialize_header(MAINNET_HEADER_705600); @@ -602,10 +463,16 @@ mod test { #[test] fn test_is_header_valid_invalid_computed_target() { - let header_705600 = deserialize_header(MAINNET_HEADER_705600); - let header = deserialize_header(MAINNET_HEADER_705601); - let store = SimpleHeaderStore::new(header_705600, 705_600); - let result = validate_header(&Network::Regtest, &store, &header); + let pow_bitcoin = pow_limit_bits(&Network::Bitcoin); + let pow_regtest = pow_limit_bits(&Network::Regtest); + let h0 = genesis_header(pow_bitcoin); + let h1 = next_block_header(h0, pow_regtest); + let h2 = next_block_header(h1, pow_regtest); + let h3 = next_block_header(h2, pow_regtest); + let mut store = SimpleHeaderStore::new(h0, 0); + store.add(h1); + store.add(h2); + let result = validate_header(&Network::Regtest, &store, &h3); assert!(matches!( result, Err(ValidateHeaderError::InvalidPoWForComputedTarget) @@ -625,42 +492,70 @@ mod test { )); } - #[test] - fn test_is_header_within_one_year_of_tip_next_height_is_above_the_minimum() { - assert!( - is_header_within_one_year_of_tip(700_000, 650_000), - "next height is above the one year minimum" - ); - assert!( - is_header_within_one_year_of_tip(700_000, 750_000), - "next height is within the one year range" - ); - assert!( - !is_header_within_one_year_of_tip(700_000, 800_000), - "next height is below the one year minimum" - ); + fn genesis_header(bits: CompactTarget) -> BlockHeader { + BlockHeader { + version: Version::ONE, + prev_blockhash: Hash::all_zeros(), + merkle_root: Hash::all_zeros(), + time: 1296688602, + bits, + nonce: 0, + } } - #[test] - #[should_panic(expected = "next height causes an overflow")] - fn test_is_header_within_one_year_of_tip_should_panic_as_next_height_is_too_high() { - is_header_within_one_year_of_tip(BlockHeight::MAX, 0); + fn next_block_header(prev: BlockHeader, bits: CompactTarget) -> BlockHeader { + BlockHeader { + prev_blockhash: prev.block_hash(), + time: prev.time + TEN_MINUTES, + bits, + ..prev + } + } + + /// Creates a chain of headers with the given length and + /// proof of work for the first header. + fn create_chain( + network: &Network, + initial_pow: CompactTarget, + chain_length: u32, + ) -> (SimpleHeaderStore, BlockHeader) { + let pow_limit = pow_limit_bits(network); + let h0 = genesis_header(initial_pow); + let mut store = SimpleHeaderStore::new(h0, 0); + let mut last_header = h0; + + for _ in 1..chain_length { + let new_header = next_block_header(last_header, pow_limit); + store.add(new_header); + last_header = new_header; + } + + (store, last_header) } #[test] - fn test_is_header_within_one_year_of_tip_chain_height_is_less_than_one_year() { - assert!( - is_header_within_one_year_of_tip(1, 0), - "chain height is less than one year" - ); - assert!( - is_header_within_one_year_of_tip(1, BLOCKS_IN_ONE_YEAR + 2), - "chain height difference is exactly one year" - ); - assert!( - !is_header_within_one_year_of_tip(1, BLOCKS_IN_ONE_YEAR + 3), - "chain height difference is one year + 1 block" - ); + fn test_next_target_regtest() { + // This test checks the chain of headers of different lengths + // with non-limit PoW in the first block header and PoW limit + // in all the other headers. + // Expect difficulty to be equal to the non-limit PoW. + + // Arrange. + let network = Network::Regtest; + let expected_pow = CompactTarget::from_consensus(7); // Some non-limit PoW, the actual value is not important. + for chain_length in 1..10 { + let (store, last_header) = create_chain(&network, expected_pow, chain_length); + // Act. + let compact_target = get_next_compact_target( + &network, + &store, + &last_header, + chain_length - 1, + last_header.time + TEN_MINUTES, + ); + // Assert. + assert_eq!(compact_target, expected_pow); + } } #[test] @@ -668,7 +563,7 @@ mod test { // Arrange: Set up the test network and parameters let network = Network::Testnet; let chain_length = DIFFICULTY_ADJUSTMENT_INTERVAL - 1; // To trigger the difficulty adjustment. - let genesis_difficulty = 486604799; + let genesis_difficulty = CompactTarget::from_consensus(486604799); // Create the genesis header and initialize the header store let genesis_header = genesis_header(genesis_difficulty); @@ -688,6 +583,6 @@ mod test { let difficulty = compute_next_difficulty(&network, &store, &last_header, chain_length); // Assert. - assert_eq!(difficulty, 473956288); + assert_eq!(difficulty, CompactTarget::from_consensus(473956288)); } } diff --git a/rs/bitcoin/validation/src/lib.rs b/rs/bitcoin/validation/src/lib.rs index 99843cea939..dbfaee7bfec 100644 --- a/rs/bitcoin/validation/src/lib.rs +++ b/rs/bitcoin/validation/src/lib.rs @@ -1,8 +1,7 @@ mod constants; mod header; -pub use crate::header::{ - is_beyond_last_checkpoint, validate_header, HeaderStore, ValidateHeaderError, -}; +pub use crate::constants::max_target; +pub use crate::header::{validate_header, HeaderStore, ValidateHeaderError}; type BlockHeight = u32; diff --git a/rs/boundary_node/canary_proxy/src/support/auto_server.rs b/rs/boundary_node/canary_proxy/src/support/auto_server.rs index 0dd0d4079a7..cd5ce9926fe 100644 --- a/rs/boundary_node/canary_proxy/src/support/auto_server.rs +++ b/rs/boundary_node/canary_proxy/src/support/auto_server.rs @@ -123,7 +123,7 @@ enum Version { H1, H2, } -async fn read_version<'a, A>(mut reader: A) -> IoResult<(Version, Rewind)> +async fn read_version(mut reader: A) -> IoResult<(Version, Rewind)> where A: AsyncRead + Unpin, { @@ -171,7 +171,7 @@ where if this.buf.filled() == H2_PREFACE { *this.version = Version::H2; } - return Poll::Ready(Ok((*this.version, this.buf.filled().to_vec()))); + Poll::Ready(Ok((*this.version, this.buf.filled().to_vec()))) } } diff --git a/rs/boundary_node/certificate_issuance/certificate_issuer/src/check.rs b/rs/boundary_node/certificate_issuance/certificate_issuer/src/check.rs index f2f3abc0869..f5321deaa71 100644 --- a/rs/boundary_node/certificate_issuance/certificate_issuer/src/check.rs +++ b/rs/boundary_node/certificate_issuance/certificate_issuer/src/check.rs @@ -171,15 +171,10 @@ impl Check for Checker { })?; // Phase 4 - Ensure canister mentions known domain. - let request = HttpRequest { - method: String::from("GET"), - url: String::from("/.well-known/ic-domains"), - headers: vec![], - body: vec![], - }; + let request = HttpRequest::get("/.well-known/ic-domains").build(); let (response,) = HttpRequestCanister::create(&self.agent, canister_id) - .http_request(&request.method, &request.url, vec![], vec![], None) + .http_request(&request.method(), &request.url(), vec![], vec![], None) .call() .await .map_err(|_| CheckError::KnownDomainsUnavailable { @@ -197,16 +192,18 @@ impl Check for Checker { }?; // Check response certification - let response_for_verification = HttpResponse { - status_code: response.status_code, - headers: response + let response_for_verification = HttpResponse::ok( + // body + response.body.clone(), + // headers + response .headers .iter() .map(|field| (field.0.to_string(), field.1.to_string())) .collect::>(), - body: response.body.clone(), - upgrade: response.upgrade, - }; + ) + .with_upgrade(response.upgrade.unwrap_or_default()) + .build(); let max_cert_time_offset_ns = 300_000_000_000; let current_time_ns = SystemTime::now() .duration_since(UNIX_EPOCH) diff --git a/rs/boundary_node/certificate_issuance/certificate_issuer/src/main.rs b/rs/boundary_node/certificate_issuance/certificate_issuer/src/main.rs index ab2dca4f12c..f21af9ca8c2 100644 --- a/rs/boundary_node/certificate_issuance/certificate_issuer/src/main.rs +++ b/rs/boundary_node/certificate_issuance/certificate_issuer/src/main.rs @@ -23,9 +23,7 @@ use axum::{ use candid::{DecoderConfig, Principal}; use chacha20poly1305::{KeyInit, XChaCha20Poly1305}; use clap::Parser; -use ic_agent::{ - agent::http_transport::reqwest_transport::ReqwestTransport, identity::Secp256k1Identity, Agent, -}; +use ic_agent::{identity::Secp256k1Identity, Agent}; use instant_acme::{Account, AccountCredentials, NewAccount}; use opentelemetry::{ metrics::{Counter, Histogram, MeterProvider as _}, @@ -198,15 +196,13 @@ async fn main() -> Result<(), Error> { static USER_AGENT: &str = "Ic-Certificate-Issuer"; let client = reqwest::Client::builder().user_agent(USER_AGENT).build()?; - let transport = - ReqwestTransport::create_with_client(cli.orchestrator_uri.to_string(), client)?; - let f = File::open(cli.identity_path).context("failed to open identity file")?; let identity = Secp256k1Identity::from_pem(f).context("failed to create basic identity")?; let agent = Agent::builder() .with_identity(identity) - .with_transport(transport) + .with_url(cli.orchestrator_uri.to_string()) + .with_http_client(client) .build()?; let root_key = cli diff --git a/rs/boundary_node/ic_boundary/BUILD.bazel b/rs/boundary_node/ic_boundary/BUILD.bazel index 411bb4e75fd..2a95d624a98 100644 --- a/rs/boundary_node/ic_boundary/BUILD.bazel +++ b/rs/boundary_node/ic_boundary/BUILD.bazel @@ -51,6 +51,7 @@ DEPENDENCIES = [ "@crate_index//:humantime", "@crate_index//:ic-agent", "@crate_index//:ic-bn-lib", + "@crate_index//:ipnet", "@crate_index//:lazy_static", "@crate_index//:little-loadshedder", "@crate_index//:maxminddb", diff --git a/rs/boundary_node/ic_boundary/Cargo.toml b/rs/boundary_node/ic_boundary/Cargo.toml index 1ffd3653e91..dfc8567888a 100644 --- a/rs/boundary_node/ic_boundary/Cargo.toml +++ b/rs/boundary_node/ic_boundary/Cargo.toml @@ -61,6 +61,7 @@ ic-registry-replicator = { path = "../../orchestrator/registry_replicator" } ic-registry-routing-table = { path = "../../registry/routing_table" } ic-registry-subnet-type = { path = "../../registry/subnet_type" } ic-types = { path = "../../types/types" } +ipnet = { workspace = true } lazy_static = { workspace = true } maxminddb = "0.24" mockall = { workspace = true } diff --git a/rs/boundary_node/ic_boundary/src/bouncer.rs b/rs/boundary_node/ic_boundary/src/bouncer.rs index 216dff101c5..c0e7ddd5b07 100644 --- a/rs/boundary_node/ic_boundary/src/bouncer.rs +++ b/rs/boundary_node/ic_boundary/src/bouncer.rs @@ -13,16 +13,15 @@ use axum::{body::Body, extract::State, middleware::Next, response::IntoResponse} use dashmap::DashMap; use http::Request; use ic_bn_lib::http::ConnInfo; -use moka::sync::{Cache, CacheBuilder}; use prometheus::{ register_histogram_with_registry, register_int_gauge_vec_with_registry, Histogram, IntGaugeVec, Registry, }; -use ratelimit::Ratelimiter; use tracing::{debug, error, info, warn}; use crate::{ cli, + rate_limiting::sharded::ShardedRatelimiter, routes::{ErrorCause, RateLimitCause}, }; @@ -44,10 +43,6 @@ pub struct Decision { pub length: Duration, } -struct Bucket { - limiter: Ratelimiter, -} - struct Metrics { decisions: IntGaugeVec, fw_latency: Histogram, @@ -55,11 +50,9 @@ struct Metrics { pub struct Bouncer { firewall: Arc, - buckets: Cache>, + shards: ShardedRatelimiter, decisions: DashMap, ban_time: Duration, - burst_size: u64, - refill_interval: Duration, // Generations are used to track changes to `decisions` and to apply firewall only when needed gen_current: AtomicU64, gen_applied: AtomicU64, @@ -69,10 +62,10 @@ pub struct Bouncer { impl Bouncer { fn new( rate_per_second: u32, - burst_size: u64, + burst_size: u32, ban_time: Duration, - max_buckets: u64, - bucket_expiry: Duration, + max_shards: u64, + shard_tti: Duration, firewall: Arc, registry: &Registry, ) -> Result { @@ -80,15 +73,10 @@ impl Bouncer { return Err(anyhow!("rate_per_second should be > 0")); } - if burst_size < rate_per_second as u64 { + if burst_size < rate_per_second { return Err(anyhow!("burst_size should be >= rate_per_second")); } - let buckets = CacheBuilder::new(max_buckets) - // Expire buckets when they're not queried for some time, this bounds memory usage - .time_to_idle(bucket_expiry) - .build(); - let metrics = Metrics { decisions: register_int_gauge_vec_with_registry!( "bouncer_decisions", @@ -105,28 +93,22 @@ impl Bouncer { }; Ok(Self { - burst_size, firewall, - buckets, + shards: ShardedRatelimiter::new( + rate_per_second, + burst_size, + Duration::from_secs(1), + shard_tti, + max_shards, + ), decisions: DashMap::new(), ban_time, - refill_interval: Duration::from_secs(1).checked_div(rate_per_second).unwrap(), gen_current: AtomicU64::new(0), gen_applied: AtomicU64::new(0), metrics, }) } - fn new_bucket(&self) -> Arc { - Arc::new(Bucket { - limiter: Ratelimiter::builder(1, self.refill_interval) - .max_tokens(self.burst_size) - .initial_available(self.burst_size) - .build() - .unwrap(), - }) - } - // Increment the generation to indicate that we need to apply fn mark_update(&self) { self.gen_current.fetch_add(1, Ordering::SeqCst); @@ -139,12 +121,7 @@ impl Bouncer { return false; } - // Get bucket or create a new one - // Moka guarantees that concurrent requests for the same key would lead to only a single one value created - let bucket = self.buckets.get_with(ip, || self.new_bucket()); - - // Try to acquire a token - if bucket.limiter.try_wait().is_ok() { + if self.shards.acquire(ip) { return true; } diff --git a/rs/boundary_node/ic_boundary/src/cache.rs b/rs/boundary_node/ic_boundary/src/cache.rs index 11f046cf4cc..5920874df4d 100644 --- a/rs/boundary_node/ic_boundary/src/cache.rs +++ b/rs/boundary_node/ic_boundary/src/cache.rs @@ -100,7 +100,7 @@ fn weigh_entry(k: &Arc, v: &CacheItem) -> u32 { + 58; // 2 x Principal for (k, v) in v.headers.iter() { - cost += k.as_str().as_bytes().len(); + cost += k.as_str().len(); cost += v.as_bytes().len(); } diff --git a/rs/boundary_node/ic_boundary/src/check/test.rs b/rs/boundary_node/ic_boundary/src/check/test.rs index e65b17284fa..2bec3d0fe5d 100644 --- a/rs/boundary_node/ic_boundary/src/check/test.rs +++ b/rs/boundary_node/ic_boundary/src/check/test.rs @@ -83,6 +83,7 @@ pub fn generate_custom_registry_snapshot( nns_public_key: vec![], subnets, nodes: nodes_hash, + api_bns: vec![], } } diff --git a/rs/boundary_node/ic_boundary/src/cli.rs b/rs/boundary_node/ic_boundary/src/cli.rs index ddb15177c7a..8ca8c993d02 100644 --- a/rs/boundary_node/ic_boundary/src/cli.rs +++ b/rs/boundary_node/ic_boundary/src/cli.rs @@ -279,6 +279,22 @@ pub struct RateLimiting { /// How frequently to poll for rules (from file or canister) #[clap(env, long, default_value = "30s", value_parser = parse_duration)] pub rate_limit_generic_poll_interval: Duration, + + /// Time-to-idle for rules that have the `ip_group_prefix`. + /// If no requests are coming for the given shard - it will be removed. + #[clap(env, long, default_value = "1h", value_parser = parse_duration)] + pub rate_limit_generic_tti: Duration, + + /// Maximum number of shards that we store (per rule) + #[clap(env, long, default_value = "30000")] + pub rate_limit_generic_max_shards: u64, + + /// Whether to use the number of API BNs from the registry to scale the rate limit rules. + /// E.g. if a ratelimit action is set to "500/1h" and the number of API BNs is 5 then the + /// rule would be adjusted to "100/1h" so that the total ratelimit of all API BNs would be "500/1h". + /// Important: if after the divison the numerator would be less than 1 then it would be rounded to 1. + #[clap(env, long)] + pub rate_limit_generic_autoscale: bool, } #[derive(Args)] @@ -340,8 +356,8 @@ pub struct Bouncer { pub bouncer_ratelimit: u32, /// Number of requests in a burst allowed, must be higher than --bouncer-ratelimit - #[clap(env, long, default_value = "600", value_parser = clap::value_parser!(u64).range(1..))] - pub bouncer_burst_size: u64, + #[clap(env, long, default_value = "600", value_parser = clap::value_parser!(u32).range(1..))] + pub bouncer_burst_size: u32, /// For how long to ban the IPs #[clap(env, long, default_value = "10m", value_parser = parse_duration)] diff --git a/rs/boundary_node/ic_boundary/src/core.rs b/rs/boundary_node/ic_boundary/src/core.rs index a32185db95b..8c06873aa21 100644 --- a/rs/boundary_node/ic_boundary/src/core.rs +++ b/rs/boundary_node/ic_boundary/src/core.rs @@ -2,7 +2,7 @@ use std::{ error::Error as StdError, net::{Ipv4Addr, Ipv6Addr, SocketAddr}, - sync::Arc, + sync::{Arc, RwLock}, time::{Duration, Instant}, }; @@ -50,7 +50,7 @@ use ic_types::{crypto::threshold_sig::ThresholdSigPublicKey, messages::MessageId use nix::unistd::{getpgid, setpgid, Pid}; use prometheus::Registry; use rand::rngs::OsRng; -use tokio::sync::RwLock; +use tokio::sync::watch; use tokio_util::sync::CancellationToken; use tower::{limit::ConcurrencyLimitLayer, util::MapResponseLayer, ServiceBuilder}; use tower_http::{compression::CompressionLayer, request_id::MakeRequestUuid, ServiceBuilderExt}; @@ -201,6 +201,9 @@ pub async fn main(cli: Cli) -> Result<(), Error> { // Setup registry-related stuff let persister = Persister::new(Arc::clone(&routing_table)); + // Snapshot update notification channels + let (channel_snapshot_send, channel_snapshot_recv) = tokio::sync::watch::channel(None); + // Registry Client let (registry_client, registry_replicator, nns_pub_key) = if let Some(v) = &cli.registry.registry_local_store_path { @@ -222,6 +225,8 @@ pub async fn main(cli: Cli) -> Result<(), Error> { WithMetricsPersist(persister, MetricParamsPersist::new(&metrics_registry)), http_client_check, &metrics_registry, + channel_snapshot_send, + channel_snapshot_recv.clone(), &mut runners, )?; @@ -283,16 +288,30 @@ pub async fn main(cli: Cli) -> Result<(), Error> { }; // Generic Ratelimiter + let generic_limiter_opts = generic::Options { + tti: cli.rate_limiting.rate_limit_generic_tti, + max_shards: cli.rate_limiting.rate_limit_generic_max_shards, + poll_interval: cli.rate_limiting.rate_limit_generic_poll_interval, + autoscale: cli.rate_limiting.rate_limit_generic_autoscale, + }; let generic_limiter = if let Some(v) = &cli.rate_limiting.rate_limit_generic_file { - Some(Arc::new(generic::Limiter::new_from_file(v.clone()))) + Some(Arc::new(generic::GenericLimiter::new_from_file( + v.clone(), + generic_limiter_opts, + channel_snapshot_recv, + &metrics_registry, + ))) + } else if let Some(v) = cli.rate_limiting.rate_limit_generic_canister_id { + Some(Arc::new(generic::GenericLimiter::new_from_canister( + v, + agent.clone().unwrap(), + generic_limiter_opts, + cli.misc.crypto_config.is_some(), + channel_snapshot_recv, + &metrics_registry, + ))) } else { - cli.rate_limiting.rate_limit_generic_canister_id.map(|x| { - Arc::new(if cli.misc.crypto_config.is_some() { - generic::Limiter::new_from_canister_update(x, agent.clone().unwrap()) - } else { - generic::Limiter::new_from_canister_query(x, agent.clone().unwrap()) - }) - }) + None }; // HTTP Logs Anonymization @@ -424,11 +443,7 @@ pub async fn main(cli: Cli) -> Result<(), Error> { runners.push(Box::new(metrics_runner)); if let Some(v) = generic_limiter { - let runner = Box::new(WithThrottle( - v, - ThrottleParams::new(cli.rate_limiting.rate_limit_generic_poll_interval), - )); - runners.push(runner); + runners.push(Box::new(v)); } // HTTP Logs Anonymization @@ -594,10 +609,11 @@ fn setup_registry( persister: WithMetricsPersist, http_client_check: Arc, metrics_registry: &Registry, + channel_snapshot_send: watch::Sender>>, + channel_snapshot_recv: watch::Receiver>>, runners: &mut Vec>, ) -> Result<(Option, Option), Error> { // Snapshots - let (channel_snapshot_send, channel_snapshot_recv) = tokio::sync::watch::channel(None); let snapshot_runner = WithMetricsSnapshot( { let mut snapshotter = Snapshotter::new( @@ -818,7 +834,7 @@ pub fn setup_router( routing_table: Arc>, http_client: Arc, bouncer: Option>, - generic_limiter: Option>, + generic_limiter: Option>, cli: &Cli, metrics_registry: &Registry, cache: Option>, diff --git a/rs/boundary_node/ic_boundary/src/metrics.rs b/rs/boundary_node/ic_boundary/src/metrics.rs index 38ff6ad18ff..6b8c1701295 100644 --- a/rs/boundary_node/ic_boundary/src/metrics.rs +++ b/rs/boundary_node/ic_boundary/src/metrics.rs @@ -2,7 +2,7 @@ use std::{ hash::{DefaultHasher, Hash, Hasher}, - sync::Arc, + sync::{Arc, RwLock}, time::Instant, }; @@ -25,7 +25,6 @@ use prometheus::{ IntGauge, IntGaugeVec, Registry, TextEncoder, }; use tikv_jemalloc_ctl::{epoch, stats}; -use tokio::sync::RwLock; use tower_http::request_id::RequestId; use tracing::info; @@ -220,7 +219,7 @@ impl Run for MetricsRunner { } // Take a write lock, truncate the vector and encode the metrics into it - let mut metrics_cache = self.metrics_cache.write().await; + let mut metrics_cache = self.metrics_cache.write().unwrap(); metrics_cache.buffer.clear(); self.encoder .encode(&metric_families, &mut metrics_cache.buffer)?; @@ -501,8 +500,17 @@ pub async fn metrics_middleware( let ip_family = request .extensions() .get::>() - .map(|x| x.remote_addr.family()) - .unwrap_or("0"); + .map(|x| { + let f = x.remote_addr.family(); + if f == "v4" { + 4 + } else if f == "v6" { + 6 + } else { + 0 + } + }) + .unwrap_or(0); let remote_addr = request .extensions() @@ -707,7 +715,7 @@ pub async fn metrics_handler( // Get a read lock and clone the buffer contents ( [(CONTENT_TYPE, PROMETHEUS_CONTENT_TYPE)], - cache.read().await.buffer.clone(), + cache.read().unwrap().buffer.clone(), ) } diff --git a/rs/boundary_node/ic_boundary/src/rate_limiting.rs b/rs/boundary_node/ic_boundary/src/rate_limiting.rs index 4a02db0a352..a7c9f613be8 100644 --- a/rs/boundary_node/ic_boundary/src/rate_limiting.rs +++ b/rs/boundary_node/ic_boundary/src/rate_limiting.rs @@ -106,6 +106,7 @@ impl RateLimit { pub mod fetcher; pub mod generic; +pub mod sharded; #[cfg(test)] pub mod test; diff --git a/rs/boundary_node/ic_boundary/src/rate_limiting/fetcher.rs b/rs/boundary_node/ic_boundary/src/rate_limiting/fetcher.rs index 58f11e07154..32635d8bf9d 100644 --- a/rs/boundary_node/ic_boundary/src/rate_limiting/fetcher.rs +++ b/rs/boundary_node/ic_boundary/src/rate_limiting/fetcher.rs @@ -123,13 +123,13 @@ impl FetchesRules for CanisterFetcher { let Some(raw) = x.rule_raw else { return Err(anyhow!( "rule with id {} ({:?}) is None", - x.id, + x.rule_id, x.description )); }; let rule = RateLimitRule::from_bytes_yaml(&raw) - .context(format!("unable to decode raw rule with id {}", x.id))?; + .context(format!("unable to decode raw rule with id {}", x.rule_id))?; Ok(rule) }) @@ -164,7 +164,7 @@ mod test { is_redacted: false, rules: vec![ OutputRule { - id: "foobar".into(), + rule_id: "foobar".into(), incident_id: "barfoo".into(), rule_raw: Some(indoc! {" canister_id: aaaaa-aa @@ -175,7 +175,7 @@ mod test { description: None }, OutputRule { - id: "foobaz".into(), + rule_id: "foobaz".into(), incident_id: "barfoo".into(), rule_raw: Some(indoc! {" canister_id: 5s2ji-faaaa-aaaaa-qaaaq-cai @@ -186,7 +186,7 @@ mod test { description: None }, OutputRule { - id: "deadbeef".into(), + rule_id: "deadbeef".into(), incident_id: "barfoo".into(), rule_raw: Some(indoc! {" canister_id: aaaaa-aa @@ -234,7 +234,7 @@ mod test { schema_version: SCHEMA_VERSION, is_redacted: false, rules: vec![OutputRule { - id: "foobar".into(), + rule_id: "foobar".into(), incident_id: "barfoo".into(), rule_raw: None, description: None, @@ -270,6 +270,8 @@ mod test { )), methods_regex: Some(Regex::new("^foo|bar$").unwrap()), request_types: None, + ip_prefix_group: None, + ip: None, limit: v1::Action::Block, }, RateLimitRule { @@ -279,6 +281,8 @@ mod test { )), methods_regex: Some(Regex::new("^baz|bax$").unwrap()), request_types: None, + ip_prefix_group: None, + ip: None, limit: v1::Action::Limit(1, Duration::from_secs(10)), }, RateLimitRule { @@ -286,6 +290,8 @@ mod test { subnet_id: None, methods_regex: Some(Regex::new("^foo|bax$").unwrap()), request_types: None, + ip_prefix_group: None, + ip: None, limit: v1::Action::Limit(10, Duration::from_secs(60)), } ] diff --git a/rs/boundary_node/ic_boundary/src/rate_limiting/generic.rs b/rs/boundary_node/ic_boundary/src/rate_limiting/generic.rs index a28dce41f82..8d6414aeffe 100644 --- a/rs/boundary_node/ic_boundary/src/rate_limiting/generic.rs +++ b/rs/boundary_node/ic_boundary/src/rate_limiting/generic.rs @@ -1,6 +1,14 @@ -use std::{path::PathBuf, sync::Arc, time::Duration}; +use std::{ + net::IpAddr, + path::PathBuf, + sync::{ + atomic::{AtomicU32, Ordering}, + Arc, + }, + time::{Duration, Instant}, +}; -use anyhow::{Context, Error}; +use anyhow::{Context as _, Error}; use arc_swap::ArcSwap; use async_trait::async_trait; use axum::{ @@ -11,21 +19,34 @@ use axum::{ response::IntoResponse, }; use candid::Principal; +use ic_bn_lib::http::ConnInfo; use ic_canister_client::Agent; use ic_types::CanisterId; -use rate_limits_api::v1::{Action, RateLimitRule, RequestType as RequestTypeRule}; +use ipnet::IpNet; +use prometheus::{ + register_int_counter_vec_with_registry, register_int_gauge_with_registry, IntCounterVec, + IntGauge, Registry, +}; +use rate_limits_api::v1::{Action, IpPrefixes, RateLimitRule, RequestType as RequestTypeRule}; use ratelimit::Ratelimiter; +use strum::{Display, IntoStaticStr}; +#[allow(clippy::disallowed_types)] +use tokio::sync::{watch, Mutex}; use tracing::warn; -use super::fetcher::{ - CanisterConfigFetcherQuery, CanisterConfigFetcherUpdate, CanisterFetcher, FetchesConfig, - FetchesRules, FileFetcher, +use super::{ + fetcher::{ + CanisterConfigFetcherQuery, CanisterConfigFetcherUpdate, CanisterFetcher, FetchesConfig, + FetchesRules, FileFetcher, + }, + sharded::{create_ratelimiter, ShardedRatelimiter}, }; use crate::{ core::Run, persist::RouteSubnet, routes::{ErrorCause, RateLimitCause, RequestContext, RequestType}, + snapshot::RegistrySnapshot, }; // Converts between different request types @@ -41,9 +62,31 @@ fn convert_request_type(rt: RequestType) -> RequestTypeRule { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Display, IntoStaticStr)] +enum Decision { + Pass, + Block, + Limit, +} + +pub struct Context<'a> { + subnet_id: Principal, + canister_id: Option, + method: Option<&'a str>, + request_type: RequestType, + ip: IpAddr, +} + +#[derive(Clone)] +enum Limiter { + Single(Arc), + Sharded(Arc>, IpPrefixes), +} + +#[derive(Clone)] struct Bucket { rule: RateLimitRule, - limiter: Option, + limiter: Option, } impl PartialEq for Bucket { @@ -53,54 +96,246 @@ impl PartialEq for Bucket { } impl Eq for Bucket {} -pub struct Limiter { +impl Bucket { + fn evaluate(&self, ctx: &Context) -> Option { + if let Some(v) = self.rule.subnet_id { + if ctx.subnet_id != v { + return None; + } + } + + if let Some(v) = self.rule.canister_id { + if let Some(x) = ctx.canister_id { + if x != v { + return None; + } + } + } + + if let Some(v) = &self.rule.request_types { + if !v.contains(&convert_request_type(ctx.request_type)) { + return None; + } + } + + if let Some(rgx) = &self.rule.methods_regex { + if let Some(v) = ctx.method { + if !rgx.is_match(v) { + return None; + } + } else { + return None; + } + } + + if let Some(v) = self.rule.ip { + if !v.contains(&ctx.ip) { + return None; + } + } + + if self.rule.limit == Action::Pass { + return Some(Decision::Pass); + } else if self.rule.limit == Action::Block { + return Some(Decision::Block); + } + + if let Some(limiter) = &self.limiter { + let allowed = match limiter { + Limiter::Single(v) => v.try_wait().is_ok(), + Limiter::Sharded(v, prefix) => { + let prefix = match ctx.ip { + IpAddr::V4(_) => prefix.v4, + IpAddr::V6(_) => prefix.v6, + }; + + // We assume that the prefix is correct, assert is safe + let net = IpNet::new_assert(ctx.ip, prefix); + v.acquire(net) + } + }; + + return Some(if allowed { + Decision::Pass + } else { + Decision::Limit + }); + } + + // Should never get here + unreachable!(); + } +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct Options { + pub tti: Duration, + pub max_shards: u64, + pub poll_interval: Duration, + pub autoscale: bool, +} + +struct Metrics { + scale: IntGauge, + last_successful_fetch: IntGauge, + active_rules: IntGauge, + fetches: IntCounterVec, + decisions: IntCounterVec, + shards_count: IntGauge, +} + +impl Metrics { + fn new(registry: &Registry) -> Self { + Self { + scale: register_int_gauge_with_registry!( + format!("generic_limiter_scale"), + format!("Current scale that's applied to the rules"), + registry, + ) + .unwrap(), + + last_successful_fetch: register_int_gauge_with_registry!( + format!("generic_limiter_last_successful_fetch"), + format!("How many seconds ago the last successful fetch happened"), + registry + ) + .unwrap(), + + active_rules: register_int_gauge_with_registry!( + format!("generic_limiter_rules"), + format!("Number of rules currently installed"), + registry + ) + .unwrap(), + + fetches: register_int_counter_vec_with_registry!( + format!("generic_limiter_fetches"), + format!("Count of rule fetches and their outcome"), + &["result"], + registry + ) + .unwrap(), + + decisions: register_int_counter_vec_with_registry!( + format!("generic_limiter_decisions"), + format!("Count of decisions made by the ratelimiter"), + &["decision"], + registry + ) + .unwrap(), + + shards_count: register_int_gauge_with_registry!( + format!("generic_limiter_shards_count"), + format!("Number of dynamic shards if the corresponding rules are used"), + registry, + ) + .unwrap(), + } + } +} + +pub struct GenericLimiter { fetcher: Arc, buckets: ArcSwap>, + active_rules: ArcSwap>, + scale: AtomicU32, + #[allow(clippy::disallowed_types)] + channel_snapshot: Mutex>>>, + #[allow(clippy::disallowed_types)] + last_refresh: Mutex, + opts: Options, + metrics: Metrics, } -impl Limiter { - pub fn new_from_file(path: PathBuf) -> Self { +impl GenericLimiter { + pub fn new_from_file( + path: PathBuf, + opts: Options, + channel_snapshot: watch::Receiver>>, + registry: &Registry, + ) -> Self { let fetcher = Arc::new(FileFetcher(path)); - Self::new_with_fetcher(fetcher) - } - - pub fn new_from_canister_query(canister_id: CanisterId, agent: Agent) -> Self { - let config_fetcher = CanisterConfigFetcherQuery(agent, canister_id); - Self::new_with_config_fetcher(Arc::new(config_fetcher)) + Self::new_with_fetcher(fetcher, opts, channel_snapshot, registry) } - pub fn new_from_canister_update(canister_id: CanisterId, agent: Agent) -> Self { - let config_fetcher = CanisterConfigFetcherUpdate(agent, canister_id); - Self::new_with_config_fetcher(Arc::new(config_fetcher)) - } + pub fn new_from_canister( + canister_id: CanisterId, + agent: Agent, + opts: Options, + use_update_call: bool, + channel_snapshot: watch::Receiver>>, + registry: &Registry, + ) -> Self { + let config_fetcher: Arc = if use_update_call { + Arc::new(CanisterConfigFetcherUpdate(agent, canister_id)) + } else { + Arc::new(CanisterConfigFetcherQuery(agent, canister_id)) + }; - fn new_with_config_fetcher(config_fetcher: Arc) -> Self { let fetcher = Arc::new(CanisterFetcher(config_fetcher)); - Self::new_with_fetcher(fetcher) + Self::new_with_fetcher(fetcher, opts, channel_snapshot, registry) } - fn new_with_fetcher(fetcher: Arc) -> Self { + fn new_with_fetcher( + fetcher: Arc, + opts: Options, + channel_snapshot: watch::Receiver>>, + registry: &Registry, + ) -> Self { Self { fetcher, buckets: ArcSwap::new(Arc::new(vec![])), + active_rules: ArcSwap::new(Arc::new(vec![])), + opts, + #[allow(clippy::disallowed_types)] + last_refresh: Mutex::new(Instant::now()), + scale: AtomicU32::new(1), + #[allow(clippy::disallowed_types)] + channel_snapshot: Mutex::new(channel_snapshot), + metrics: Metrics::new(registry), } } - fn process_rules(rules: Vec) -> Vec { + fn process_rules( + &self, + rules: Vec, + old: &Arc>, + scale: u32, + ) -> Vec { rules .into_iter() - .map(|rule| { - let limiter = if let Action::Limit(limit, duration) = rule.limit { - Some( - Ratelimiter::builder( - 1, - duration.checked_div(limit).unwrap_or(Duration::ZERO), + .enumerate() + .map(|(idx, mut rule)| { + // Scale the rule limit accordingly + if let Action::Limit(n, d) = rule.limit { + // Make sure the limit doesn't go below 1 + let limit = (n / scale).max(1); + rule.limit = Action::Limit(limit, d); + } + + // Check if the same rule exists in the same position. + // If yes, then copy over the old limiter to avoid resetting it. + if let Some(v) = old.get(idx) { + if v.rule == rule { + return v.clone(); + } + } + + let limiter = if let Action::Limit(limit, duration) = &rule.limit { + Some(if let Some(v) = &rule.ip_prefix_group { + Limiter::Sharded( + Arc::new(ShardedRatelimiter::new( + *limit, + *limit, + *duration, + self.opts.tti, + self.opts.max_shards, + )), + *v, ) - .max_tokens(limit as u64) - .initial_available(limit as u64) - .build() - .unwrap(), - ) + } else { + Limiter::Single(Arc::new(create_ratelimiter(*limit, *limit, *duration))) + }) } else { None }; @@ -110,18 +345,18 @@ impl Limiter { .collect() } - fn apply_rules(&self, rules: Vec) -> bool { - let new = Arc::new(Self::process_rules(rules)); + fn apply_rules(&self, rules: Vec, scale: u32) -> bool { let old = self.buckets.load_full(); + let new = Arc::new(self.process_rules(rules, &old, scale)); if old != new { - warn!("GenericLimiter: ruleset updated: {} rules", new.len()); + warn!( + "GenericLimiter: ruleset updated: {} rules (scale {scale})", + new.len() + ); for b in new.as_ref() { - warn!( - "GenericLimiter: subnet: {:?}, canister: {:?}, methods: {:?}, action: {:?}", - b.rule.subnet_id, b.rule.canister_id, b.rule.methods_regex, b.rule.limit, - ); + warn!("GenericLimiter: {}", b.rule); } self.buckets.store(new); @@ -138,105 +373,183 @@ impl Limiter { .await .context("unable to fetch rules")?; - self.apply_rules(rules); - Ok(()) - } + self.metrics.active_rules.set(rules.len() as i64); - fn acquire_token( - &self, - subnet_id: Principal, - canister_id: Option, - method: Option<&str>, - request_type: RequestType, - ) -> bool { - for b in self.buckets.load_full().as_ref() { - if let Some(v) = b.rule.subnet_id { - if subnet_id != v { - continue; - } - } + self.apply_rules(rules.clone(), self.scale.load(Ordering::SeqCst)); - if let Some(v) = b.rule.canister_id { - if let Some(x) = canister_id { - if x != v { - continue; - } - } - } + // Store the new copy of the rules as a golden copy for future recalculation + self.active_rules.store(Arc::new(rules)); + *self.last_refresh.lock().await = Instant::now(); - if let Some(v) = &b.rule.request_types { - if !v.contains(&convert_request_type(request_type)) { - continue; - } - } + Ok(()) + } - if let Some(rgx) = &b.rule.methods_regex { - if let Some(v) = method { - if !rgx.is_match(v) { - continue; - } - } else { - continue; - } - } + fn evaluate(&self, ctx: Context) -> Decision { + // Always allow access from localhost. + // This makes sure that ic-boundary & colocated services will always be able to query anything. + if ctx.ip.is_loopback() { + return Decision::Pass; + } - if let Some(r) = &b.limiter { - return r.try_wait().is_ok(); + for b in self.buckets.load_full().as_ref() { + if let Some(v) = b.evaluate(&ctx) { + return v; } - - // Always block - return false; } // No rules / no match -> pass - true + Decision::Pass + } + + /// Count the number of shards in sharded limiters (if there are any) + fn shards_count(&self) -> u64 { + self.buckets + .load_full() + .iter() + .filter_map(|x| { + if let Some(Limiter::Sharded(v, _)) = &x.limiter { + Some(v.shards_count()) + } else { + None + } + }) + .sum() } } #[async_trait] -impl Run for Arc { +impl Run for Arc { async fn run(&mut self) -> Result<(), Error> { - if let Err(e) = self.refresh().await { - warn!("Ratelimiter: unable to refresh: {e:#}"); + let mut interval = tokio::time::interval(self.opts.poll_interval); + interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip); + + let mut channel = self.channel_snapshot.lock().await; + + loop { + tokio::select! { + biased; + + Ok(()) = channel.changed(), if self.opts.autoscale => { + let snapshot = channel.borrow_and_update().clone(); + + if let Some(v) = snapshot { + // Store the count of API BNs as a scale and make sure it's >= 1 + let scale = v.api_bns.len().max(1) as u32; + self.scale.store(scale, Ordering::SeqCst); + self.metrics.scale.set(scale as i64); + warn!("GenericLimiter: got a new registry snapshot, recalculating with scale {scale}"); + + // Recalculate the rules based on the potentially new scale + self.apply_rules(self.active_rules.load().as_ref().clone(), scale); + } + } + + _ = interval.tick() => { + let r = self.refresh().await; + self.metrics.fetches.with_label_values(&[if r.is_ok() { "success" } else {"failure"}]).inc(); + if let Err(e) = r { + warn!("GenericLimiter: unable to refresh: {e:#}"); + } + + // Update the metrics + self.metrics.last_successful_fetch.set(self.last_refresh.lock().await.elapsed().as_secs_f64() as i64); + self.metrics.shards_count.set(self.shards_count() as i64); + } + } } - Ok(()) } } pub async fn middleware( - State(state): State>, + State(state): State>, Extension(ctx): Extension>, Extension(subnet): Extension>, + Extension(conn_info): Extension>, canister_id: Option>, request: Request, next: Next, ) -> Result { - if !state.acquire_token( - subnet.id, - canister_id.map(|x| x.0.get().into()), - ctx.method_name.as_deref(), - ctx.request_type, - ) { - return Err(ErrorCause::RateLimited(RateLimitCause::Generic)); - } + let ctx = Context { + subnet_id: subnet.id, + canister_id: canister_id.map(|x| x.0.get().into()), + method: ctx.method_name.as_deref(), + request_type: ctx.request_type, + ip: conn_info.remote_addr.ip(), + }; - Ok(next.run(request).await) + let decision = state.evaluate(ctx); + + let decision_str: &'static str = decision.into(); + state + .metrics + .decisions + .with_label_values(&[decision_str]) + .inc(); + + match decision { + Decision::Pass => Ok(next.run(request).await), + Decision::Block => Err(ErrorCause::Forbidden), + Decision::Limit => Err(ErrorCause::RateLimited(RateLimitCause::Generic)), + } } #[cfg(test)] mod test { use super::*; use indoc::indoc; + use std::str::FromStr; + + use crate::{ + principal, + snapshot::{generate_stub_snapshot, ApiBoundaryNode}, + }; + + struct BrokenFetcher; + + #[async_trait] + impl FetchesRules for BrokenFetcher { + async fn fetch_rules(&self) -> Result, Error> { + Err(anyhow::anyhow!("boo")) + } + } + + struct TestFetcher(Vec); + + #[async_trait] + impl FetchesRules for TestFetcher { + async fn fetch_rules(&self) -> Result, Error> { + Ok(self.0.clone()) + } + } + + #[tokio::test] + async fn test_ratelimit() { + let ip1 = IpAddr::from_str("10.0.0.1").unwrap(); + let ip2 = IpAddr::from_str("192.168.0.1").unwrap(); + let ip_local4 = IpAddr::from_str("127.0.0.1").unwrap(); + let ip_local6 = IpAddr::from_str("::1").unwrap(); + + let id0 = principal!("pawub-syaaa-aaaam-qb7zq-cai"); + let id1 = principal!("aaaaa-aa"); + let id2 = principal!("5s2ji-faaaa-aaaaa-qaaaq-cai"); + let id3 = principal!("qoctq-giaaa-aaaaa-aaaea-cai"); + + let subnet_id = + principal!("3hhby-wmtmw-umt4t-7ieyg-bbiig-xiylg-sblrt-voxgt-bqckd-a75bf-rqe"); + let subnet_id2 = + principal!("6pbhf-qzpdk-kuqbr-pklfa-5ehhf-jfjps-zsj6q-57nrl-kzhpd-mu7hc-vae"); - #[test] - fn test_ratelimit() { let rules = indoc! {" + - canister_id: pawub-syaaa-aaaam-qb7zq-cai + limit: block + - subnet_id: 3hhby-wmtmw-umt4t-7ieyg-bbiig-xiylg-sblrt-voxgt-bqckd-a75bf-rqe canister_id: aaaaa-aa methods_regex: ^.*$ limit: 10/1h - canister_id: 5s2ji-faaaa-aaaaa-qaaaq-cai + ip: 10.0.0.0/24 methods_regex: ^(foo|bar)$ limit: 20/1h @@ -245,78 +558,491 @@ mod test { limit: block - canister_id: qoctq-giaaa-aaaaa-aaaea-cai + ip: 10.0.0.0/8 request_types: [call] limit: 10/1h + - canister_id: qoctq-giaaa-aaaaa-aaaea-cai + request_types: [read_state] + ip_prefix_group: + v4: 24 + v6: 64 + limit: 10/1h + - canister_id: qoctq-giaaa-aaaaa-aaaea-cai limit: 20/1h "}; + let rules: Vec = serde_yaml::from_str(rules).unwrap(); + let opts = Options { + tti: Duration::from_secs(10), + max_shards: 10000, + poll_interval: Duration::from_secs(30), + autoscale: true, + }; - let limiter = Limiter::new_from_file("/tmp/foo".into()); - limiter.apply_rules(rules); + // Check that fetching works + let fetcher = TestFetcher(rules.clone()); + let (_, rx) = watch::channel(None); + let limiter = Arc::new(GenericLimiter::new_with_fetcher( + Arc::new(fetcher), + opts.clone(), + rx, + &Registry::new(), + )); + assert!(limiter.refresh().await.is_ok()); + assert_eq!(limiter.active_rules.load().len(), 7); - let id1 = Principal::from_text("aaaaa-aa").unwrap(); - let id2 = Principal::from_text("5s2ji-faaaa-aaaaa-qaaaq-cai").unwrap(); - let id3 = Principal::from_text("qoctq-giaaa-aaaaa-aaaea-cai").unwrap(); - let subnet_id = - Principal::from_text("3hhby-wmtmw-umt4t-7ieyg-bbiig-xiylg-sblrt-voxgt-bqckd-a75bf-rqe") - .unwrap(); - let subnet_id2 = - Principal::from_text("6pbhf-qzpdk-kuqbr-pklfa-5ehhf-jfjps-zsj6q-57nrl-kzhpd-mu7hc-vae") - .unwrap(); + // Check id1 limiting with any method + // 10 pass + for _ in 0..10 { + assert_eq!( + limiter.evaluate(Context { + subnet_id, + canister_id: Some(id1), + method: Some("foo"), + request_type: RequestType::Query, + ip: ip1, + }), + Decision::Pass + ); + } + + // then all blocked + for _ in 0..100 { + assert_eq!( + limiter.evaluate(Context { + subnet_id, + canister_id: Some(id1), + method: Some("bar"), + request_type: RequestType::Query, + ip: ip1, + }), + Decision::Limit + ); + } + + // Check different rules + let (tx, rx) = watch::channel(None); + let limiter = Arc::new(GenericLimiter::new_with_fetcher( + Arc::new(BrokenFetcher), + opts, + rx, + &Registry::new(), + )); - // Check id1 blocking with any method + let mut runner = limiter.clone(); + tokio::spawn(async move { + let _ = runner.run().await; + }); + + limiter.apply_rules(rules.clone(), 1); + + let mut snapshot = generate_stub_snapshot(vec![]); + snapshot.api_bns = vec![ + ApiBoundaryNode { + id: principal!("3hhby-wmtmw-umt4t-7ieyg-bbiig-xiylg-sblrt-voxgt-bqckd-a75bf-rqe"), + addr: ip1, + port: 31337, + }, + ApiBoundaryNode { + id: principal!("3hhby-wmtmw-umt4t-7ieyg-bbiig-xiylg-sblrt-voxgt-bqckd-a75bf-rqe"), + addr: ip2, + port: 31337, + }, + ]; + + // Check that blocked canister always works from localhost even if there's a block rule present + for _ in 0..100 { + assert_eq!( + limiter.evaluate(Context { + subnet_id, + canister_id: Some(id0), + method: None, + request_type: RequestType::Query, + ip: ip_local4, + }), + Decision::Pass + ); + } + for _ in 0..100 { + assert_eq!( + limiter.evaluate(Context { + subnet_id, + canister_id: Some(id0), + method: None, + request_type: RequestType::Query, + ip: ip_local6, + }), + Decision::Pass + ); + } + + // Check id1 limiting with any method // 10 pass for _ in 0..10 { - assert!(limiter.acquire_token(subnet_id, Some(id1), Some("foo"), RequestType::Query)); + assert_eq!( + limiter.evaluate(Context { + subnet_id, + canister_id: Some(id1), + method: Some("foo"), + request_type: RequestType::Query, + ip: ip1, + }), + Decision::Pass + ); } + // then all blocked for _ in 0..100 { - assert!(!limiter.acquire_token(subnet_id, Some(id1), Some("bar"), RequestType::Query)); + assert_eq!( + limiter.evaluate(Context { + subnet_id, + canister_id: Some(id1), + method: Some("bar"), + request_type: RequestType::Query, + ip: ip1, + }), + Decision::Limit + ); } - // Check id2 blocking with two methods + // Check id2 limiting with two methods // 20 pass // Another subnet_id which shouldn't have any difference for _ in 0..20 { - assert!(limiter.acquire_token(subnet_id2, Some(id2), Some("foo"), RequestType::Query)); + assert_eq!( + limiter.evaluate(Context { + subnet_id: subnet_id2, + canister_id: Some(id2), + method: Some("foo"), + request_type: RequestType::Query, + ip: ip1, + }), + Decision::Pass + ); } - // Then all blocked + + // Then all limit for _ in 0..100 { - assert!(!limiter.acquire_token(subnet_id2, Some(id2), Some("bar"), RequestType::Query)); + assert_eq!( + limiter.evaluate(Context { + subnet_id: subnet_id2, + canister_id: Some(id2), + method: Some("bar"), + request_type: RequestType::Query, + ip: ip1, + }), + Decision::Limit + ); } - // Other methods should not block ever + // Other methods should not limit ever for _ in 0..100 { - assert!(limiter.acquire_token(subnet_id2, Some(id2), Some("lol"), RequestType::Query)); + assert_eq!( + limiter.evaluate(Context { + subnet_id: subnet_id2, + canister_id: Some(id2), + method: Some("lol"), + request_type: RequestType::Query, + ip: ip1, + }), + Decision::Pass + ); } for _ in 0..100 { - assert!(limiter.acquire_token(subnet_id2, Some(id2), Some("rofl"), RequestType::Query)); + assert_eq!( + limiter.evaluate(Context { + subnet_id: subnet_id2, + canister_id: Some(id2), + method: Some("rofl"), + request_type: RequestType::Query, + ip: ip1, + }), + Decision::Pass + ); } // This method should be blocked always for _ in 0..100 { - assert!(!limiter.acquire_token(subnet_id, Some(id2), Some("baz"), RequestType::Query)); + assert_eq!( + limiter.evaluate(Context { + subnet_id, + canister_id: Some(id2), + method: Some("baz"), + request_type: RequestType::Query, + ip: ip1, + }), + Decision::Block + ); } - // Check id3 blocking with any method and request type call + // Check id3 limiting with any method and request type call // 10 pass for _ in 0..10 { - assert!(limiter.acquire_token(subnet_id, Some(id3), Some("foo"), RequestType::Call)); + assert_eq!( + limiter.evaluate(Context { + subnet_id, + canister_id: Some(id3), + method: Some("rofl"), + request_type: RequestType::Call, + ip: ip1, + }), + Decision::Pass + ); } - // then all blocked + // then all limited for _ in 0..100 { - assert!(!limiter.acquire_token(subnet_id, Some(id3), Some("bar"), RequestType::Call)); + assert_eq!( + limiter.evaluate(Context { + subnet_id, + canister_id: Some(id3), + method: Some("bar"), + request_type: RequestType::Call, + ip: ip1, + }), + Decision::Limit + ); } - // Then check id3 blocking with any method and request type query + // Then check id3 limiting with any method and request type query // 20 pass for _ in 0..20 { - assert!(limiter.acquire_token(subnet_id, Some(id3), Some("baz"), RequestType::Query)); + assert_eq!( + limiter.evaluate(Context { + subnet_id, + canister_id: Some(id3), + method: Some("baz"), + request_type: RequestType::Query, + ip: ip1, + }), + Decision::Pass + ); } + // then all limited + for _ in 0..100 { + assert_eq!( + limiter.evaluate(Context { + subnet_id, + canister_id: Some(id3), + method: Some("zob"), + request_type: RequestType::Query, + ip: ip1, + }), + Decision::Limit + ); + } + + // Check per-ip-subnet blocking + // IP1 + // 10 pass + for _ in 0..10 { + assert_eq!( + limiter.evaluate(Context { + subnet_id, + canister_id: Some(id3), + method: None, + request_type: RequestType::ReadState, + ip: ip1, + }), + Decision::Pass + ); + } + // Then all limited + for _ in 0..10 { + assert_eq!( + limiter.evaluate(Context { + subnet_id, + canister_id: Some(id3), + method: None, + request_type: RequestType::ReadState, + ip: ip1, + }), + Decision::Limit + ); + } + // IP2 + // 10 pass + for _ in 0..10 { + assert_eq!( + limiter.evaluate(Context { + subnet_id, + canister_id: Some(id3), + method: None, + request_type: RequestType::ReadState, + ip: ip2, + }), + Decision::Pass + ); + } + // Then all limited + for _ in 0..10 { + assert_eq!( + limiter.evaluate(Context { + subnet_id, + canister_id: Some(id3), + method: None, + request_type: RequestType::ReadState, + ip: ip2, + }), + Decision::Limit + ); + } + + // Check that scaling works, the rules should fire 2x earlier now + limiter.apply_rules(rules.clone(), 2); + + // 5 pass (instead of configured 10) + for _ in 0..5 { + assert_eq!( + limiter.evaluate(Context { + subnet_id, + canister_id: Some(id1), + method: Some("foo"), + request_type: RequestType::Query, + ip: ip1, + }), + Decision::Pass + ); + } + + // then all blocked + for _ in 0..100 { + assert_eq!( + limiter.evaluate(Context { + subnet_id, + canister_id: Some(id1), + method: Some("bar"), + request_type: RequestType::Query, + ip: ip1, + }), + Decision::Limit + ); + } + + // Make sure that resetting scale back to 1 works + limiter.apply_rules(rules.clone(), 1); + + // 10 pass + for _ in 0..10 { + assert_eq!( + limiter.evaluate(Context { + subnet_id, + canister_id: Some(id1), + method: Some("foo"), + request_type: RequestType::Query, + ip: ip1, + }), + Decision::Pass + ); + } + + // then all blocked + for _ in 0..100 { + assert_eq!( + limiter.evaluate(Context { + subnet_id, + canister_id: Some(id1), + method: Some("bar"), + request_type: RequestType::Query, + ip: ip1, + }), + Decision::Limit + ); + } + + // Check that limit after scaling doesn't go under 1 + limiter.apply_rules(rules.clone(), 100); + + // 1 pass (instead of configured 10) + assert_eq!( + limiter.evaluate(Context { + subnet_id, + canister_id: Some(id1), + method: Some("foo"), + request_type: RequestType::Query, + ip: ip1, + }), + Decision::Pass + ); + + // then all blocked + for _ in 0..100 { + assert_eq!( + limiter.evaluate(Context { + subnet_id, + canister_id: Some(id1), + method: Some("bar"), + request_type: RequestType::Query, + ip: ip1, + }), + Decision::Limit + ); + } + + // Check that autoscaling works by sending a snapshot + limiter.active_rules.store(Arc::new(rules.clone())); + tx.send(Some(Arc::new(snapshot.clone()))).unwrap(); + tokio::time::sleep(Duration::from_millis(100)).await; + + // 5 pass (instead of configured 10) + for _ in 0..5 { + assert_eq!( + limiter.evaluate(Context { + subnet_id, + canister_id: Some(id1), + method: Some("foo"), + request_type: RequestType::Query, + ip: ip1, + }), + Decision::Pass + ); + } + + // then all blocked + for _ in 0..100 { + assert_eq!( + limiter.evaluate(Context { + subnet_id, + canister_id: Some(id1), + method: Some("bar"), + request_type: RequestType::Query, + ip: ip1, + }), + Decision::Limit + ); + } + + // Check that autoscaling resets to 1 if there are no API BNs + snapshot.api_bns = vec![]; + tx.send(Some(Arc::new(snapshot))).unwrap(); + tokio::time::sleep(Duration::from_millis(100)).await; + + // 10 pass + for _ in 0..10 { + assert_eq!( + limiter.evaluate(Context { + subnet_id, + canister_id: Some(id1), + method: Some("foo"), + request_type: RequestType::Query, + ip: ip1, + }), + Decision::Pass + ); + } + // then all blocked for _ in 0..100 { - assert!(!limiter.acquire_token(subnet_id, Some(id3), Some("zob"), RequestType::Query)); + assert_eq!( + limiter.evaluate(Context { + subnet_id, + canister_id: Some(id1), + method: Some("bar"), + request_type: RequestType::Query, + ip: ip1, + }), + Decision::Limit + ); } } } diff --git a/rs/boundary_node/ic_boundary/src/rate_limiting/sharded.rs b/rs/boundary_node/ic_boundary/src/rate_limiting/sharded.rs new file mode 100644 index 00000000000..9943dd3370f --- /dev/null +++ b/rs/boundary_node/ic_boundary/src/rate_limiting/sharded.rs @@ -0,0 +1,82 @@ +use std::{hash::Hash, sync::Arc, time::Duration}; + +use moka::sync::Cache; +use ratelimit::Ratelimiter; + +pub fn create_ratelimiter(limit: u32, burst: u32, duration: Duration) -> Ratelimiter { + Ratelimiter::builder(1, duration.checked_div(limit).unwrap_or(Duration::ZERO)) + .max_tokens(burst as u64) + .initial_available(burst as u64) + .build() + .unwrap() +} + +#[derive(Clone)] +struct Shard { + limiter: Arc, +} + +// Ratelimiter that creates sub-limiters for each key +pub struct ShardedRatelimiter { + shards: Cache, + limit: u32, + burst: u32, + dur: Duration, +} + +impl ShardedRatelimiter { + pub fn new(limit: u32, burst: u32, dur: Duration, tti: Duration, max_shards: u64) -> Self { + let shards = Cache::builder() + .time_to_idle(tti) + .max_capacity(max_shards) + .build(); + + Self { + shards, + limit, + burst, + dur, + } + } + + pub fn acquire(&self, key: K) -> bool { + let shard = self.shards.get_with(key, || Shard { + limiter: Arc::new(create_ratelimiter(self.limit, self.burst, self.dur)), + }); + + shard.limiter.try_wait().is_ok() + } + + pub fn shards_count(&self) -> u64 { + self.shards.run_pending_tasks(); + self.shards.entry_count() + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_sharded() { + let s: ShardedRatelimiter = + ShardedRatelimiter::new(10, 10, Duration::from_secs(1), Duration::from_secs(5), 1000); + + // Check 1st shard works and then blocked + for _ in 0..10 { + assert!(s.acquire("foo".into())); + } + assert!(!s.acquire("foo".into())); + + // Check 2nd shard works and then blocked + for _ in 0..10 { + assert!(s.acquire("bar".into())); + } + assert!(!s.acquire("bar".into())); + + // Check 1st still blocked + for _ in 0..10 { + assert!(!s.acquire("foo".into())); + } + } +} diff --git a/rs/boundary_node/ic_boundary/src/routes.rs b/rs/boundary_node/ic_boundary/src/routes.rs index 026e93f5eb5..178c1f22fe3 100644 --- a/rs/boundary_node/ic_boundary/src/routes.rs +++ b/rs/boundary_node/ic_boundary/src/routes.rs @@ -43,8 +43,6 @@ use crate::{ snapshot::{Node, RegistrySnapshot}, }; -// TODO which one to use? -const IC_API_VERSION: &str = "0.18.0"; pub const ANONYMOUS_PRINCIPAL: Principal = Principal::anonymous(); const METHOD_HTTP: &str = "http_request"; @@ -73,8 +71,7 @@ pub enum RateLimitCause { Generic, } -// Categorized possible causes for request processing failures -// Not using Error as inner type since it's not cloneable +/// Categorized possible causes for request processing failures #[derive(Clone, Debug, Display)] #[strum(serialize_all = "snake_case")] pub enum ErrorCause { @@ -84,6 +81,7 @@ pub enum ErrorCause { UnableToParseCBOR(String), UnableToParseHTTPArg(String), LoadShed, + Forbidden, MalformedRequest(String), NoRoutingTable, SubnetNotFound, @@ -143,6 +141,7 @@ impl ErrorCause { Self::ReplicaTLSErrorOther(_) => ErrorClientFacing::ReplicaError, Self::ReplicaTLSErrorCert(_) => ErrorClientFacing::ReplicaError, Self::ReplicaErrorOther(_) => ErrorClientFacing::ReplicaError, + Self::Forbidden => ErrorClientFacing::Forbidden, Self::RateLimited(_) => ErrorClientFacing::RateLimited, } } @@ -169,6 +168,7 @@ pub enum ErrorClientFacing { #[strum(serialize = "internal_server_error")] Other, PayloadTooLarge(usize), + Forbidden, RateLimited, ReplicaError, ServiceUnavailable, @@ -187,6 +187,7 @@ impl ErrorClientFacing { Self::NoHealthyNodes => StatusCode::SERVICE_UNAVAILABLE, Self::Other => StatusCode::INTERNAL_SERVER_ERROR, Self::PayloadTooLarge(_) => StatusCode::PAYLOAD_TOO_LARGE, + Self::Forbidden => StatusCode::FORBIDDEN, Self::RateLimited => StatusCode::TOO_MANY_REQUESTS, Self::ReplicaError => StatusCode::SERVICE_UNAVAILABLE, Self::ServiceUnavailable => StatusCode::SERVICE_UNAVAILABLE, @@ -205,6 +206,7 @@ impl ErrorClientFacing { Self::NoHealthyNodes => "There are currently no healthy replica nodes available to handle the request. This may be due to an ongoing upgrade of the replica software in the subnet. Please try again later.".to_string(), Self::Other => "Internal Server Error".to_string(), Self::PayloadTooLarge(x) => format!("Payload is too large: maximum body size is {x} bytes."), + Self::Forbidden => "Request is forbidden according to currently active policy, it might work later.".to_string(), Self::RateLimited => "Rate limit exceeded. Please slow down requests and try again later.".to_string(), Self::ReplicaError => "An unexpected error occurred while communicating with the upstream replica node. Please try again later.".to_string(), Self::ServiceUnavailable => "The API boundary node is temporarily unable to process the request. Please try again later.".to_string(), @@ -783,7 +785,6 @@ pub async fn status( let health = h.health().await; let status = HttpStatusResponse { - ic_api_version: IC_API_VERSION.to_string(), root_key: rk.root_key().await.map(|x| x.into()), impl_version: None, impl_hash: None, diff --git a/rs/boundary_node/ic_boundary/src/snapshot.rs b/rs/boundary_node/ic_boundary/src/snapshot.rs index 7866c4a0e78..2de9a70131d 100644 --- a/rs/boundary_node/ic_boundary/src/snapshot.rs +++ b/rs/boundary_node/ic_boundary/src/snapshot.rs @@ -13,6 +13,7 @@ use async_trait::async_trait; use candid::Principal; use ic_registry_client::client::RegistryClient; use ic_registry_client_helpers::{ + api_boundary_node::ApiBoundaryNodeRegistry, crypto::CryptoRegistry, node::NodeRegistry, routing_table::RoutingTableRegistry, @@ -86,6 +87,14 @@ impl Node { } } +#[derive(Clone, Debug)] +#[allow(dead_code)] +pub struct ApiBoundaryNode { + pub id: Principal, + pub addr: IpAddr, + pub port: u16, +} + #[derive(Clone, Debug)] pub struct CanisterRange { pub start: Principal, @@ -138,6 +147,7 @@ pub struct RegistrySnapshot { pub nns_public_key: Vec, pub subnets: Vec, pub nodes: HashMap>, + pub api_bns: Vec, } pub struct Snapshotter { @@ -192,6 +202,38 @@ impl Snapshotter { self.persister = Some(persister); } + fn get_api_boundary_nodes( + &self, + version: RegistryVersion, + ) -> Result, Error> { + let node_ids = self + .registry_client + .get_api_boundary_node_ids(version) + .context("unable to get API BN node ids")?; + + let nodes = node_ids + .into_iter() + .map(|x| -> Result<_, Error> { + let node = self + .registry_client + .get_node_record(x, version) + .context("unable to get node record")? + .context("node not available")?; + + let http_endpoint = node.http.context("http endpoint not available")?; + + Ok(ApiBoundaryNode { + id: x.get().0, + addr: IpAddr::from_str(http_endpoint.ip_addr.as_str()) + .context("unable to parse IP address")?, + port: http_endpoint.port as u16, + }) + }) + .collect::, _>>()?; + + Ok(nodes) + } + // Creates a snapshot of the registry for given version fn get_snapshot(&self, version: RegistryVersion) -> Result { // Get routing table with canister ranges @@ -243,6 +285,11 @@ impl Snapshotter { .context("failed to get subnet ids")? // Result .context("subnet ids not available")?; // Option + // Fetch a list of API BNs + let api_bns = self + .get_api_boundary_nodes(version) + .context("unable to get API BNs")?; + let subnets = subnet_ids .into_iter() .map(|subnet_id| { @@ -337,6 +384,7 @@ impl Snapshotter { nns_public_key: nns_key_with_prefix, subnets, nodes: nodes_map, + api_bns, }) } } @@ -463,6 +511,7 @@ pub fn generate_stub_snapshot(subnets: Vec) -> RegistrySnapshot { nns_public_key: vec![], subnets, nodes, + api_bns: vec![], } } diff --git a/rs/boundary_node/rate_limits/BUILD.bazel b/rs/boundary_node/rate_limits/BUILD.bazel index 51c9bcadcb1..a9bb1930784 100644 --- a/rs/boundary_node/rate_limits/BUILD.bazel +++ b/rs/boundary_node/rate_limits/BUILD.bazel @@ -7,14 +7,13 @@ DEPENDENCIES = [ # Keep sorted. "//rs/boundary_node/rate_limits/api:rate_limits_api", "//rs/nns/constants", + "//rs/rust_canisters/canister_log", "//rs/rust_canisters/http_types", "@crate_index//:anyhow", "@crate_index//:candid", "@crate_index//:getrandom", - "@crate_index//:hex", "@crate_index//:ic-cdk", "@crate_index//:ic-cdk-timers", - "@crate_index//:ic-metrics-encoder", "@crate_index//:ic-stable-structures", "@crate_index//:mockall", "@crate_index//:prometheus", @@ -47,6 +46,7 @@ rust_test( srcs = glob(["canister/**/*.rs"]), crate_name = "rate_limit_canister", crate_root = "canister/lib.rs", + data = ["canister/interface.did"], proc_macro_deps = MACRO_DEPENDENCIES, - deps = DEPENDENCIES, + deps = DEPENDENCIES + ["@crate_index//:candid_parser"], ) diff --git a/rs/boundary_node/rate_limits/Cargo.toml b/rs/boundary_node/rate_limits/Cargo.toml index b88fca8cf01..247157ec494 100644 --- a/rs/boundary_node/rate_limits/Cargo.toml +++ b/rs/boundary_node/rate_limits/Cargo.toml @@ -10,8 +10,8 @@ documentation.workspace = true anyhow = { workspace = true } candid = { workspace = true } getrandom = { version = "0.2", features = ["custom"] } -hex = { workspace = true } ic-canisters-http-types = { path = "../../rust_canisters/http_types" } +ic-canister-log = { path = "../../rust_canisters/canister_log" } ic-cdk = { workspace = true } ic-cdk-macros = { workspace = true } ic-cdk-timers = { workspace = true } @@ -29,6 +29,7 @@ thiserror = { workspace = true } uuid = { workspace = true, features = ['serde', 'v4'] } [dev-dependencies] +candid_parser = { workspace = true } rate-limit-canister-integration-tests = { path = "./integration_tests" } [lib] diff --git a/rs/boundary_node/rate_limits/api/BUILD.bazel b/rs/boundary_node/rate_limits/api/BUILD.bazel index 7d52ed485c3..54ac3b10f81 100644 --- a/rs/boundary_node/rate_limits/api/BUILD.bazel +++ b/rs/boundary_node/rate_limits/api/BUILD.bazel @@ -6,6 +6,7 @@ DEPENDENCIES = [ # Keep sorted. "@crate_index//:candid", "@crate_index//:humantime", + "@crate_index//:ipnet", "@crate_index//:regex", "@crate_index//:serde", "@crate_index//:serde_json", diff --git a/rs/boundary_node/rate_limits/api/Cargo.toml b/rs/boundary_node/rate_limits/api/Cargo.toml index 45f57bfa32c..3a6c639fc86 100644 --- a/rs/boundary_node/rate_limits/api/Cargo.toml +++ b/rs/boundary_node/rate_limits/api/Cargo.toml @@ -10,6 +10,7 @@ documentation.workspace = true candid = { workspace = true } humantime = "2.1" ic-bn-lib = { workspace = true } +ipnet = { workspace = true } regex = { workspace = true } serde = { workspace = true } serde_bytes = { workspace = true } diff --git a/rs/boundary_node/rate_limits/api/src/lib.rs b/rs/boundary_node/rate_limits/api/src/lib.rs index 0a9dc300efd..9f79729f01f 100644 --- a/rs/boundary_node/rate_limits/api/src/lib.rs +++ b/rs/boundary_node/rate_limits/api/src/lib.rs @@ -108,7 +108,7 @@ pub struct InputRule { #[derive(CandidType, Deserialize, Debug, PartialEq)] pub struct OutputRule { - pub id: RuleId, + pub rule_id: RuleId, pub incident_id: IncidentId, pub rule_raw: Option>, pub description: Option, @@ -116,7 +116,7 @@ pub struct OutputRule { #[derive(CandidType, Deserialize, Debug, PartialEq)] pub struct OutputRuleMetadata { - pub id: RuleId, + pub rule_id: RuleId, pub incident_id: IncidentId, pub rule_raw: Option>, pub description: Option, @@ -158,7 +158,7 @@ impl std::fmt::Display for OutputConfig { writeln!(f, "{INDENT}Is redacted: {}", self.is_redacted)?; for (i, rule) in self.rules.iter().enumerate() { writeln!(f, "{DOUBLE_INDENT}Rule {}:", i + 1)?; - writeln!(f, "{DOUBLE_INDENT}ID: {}", rule.id)?; + writeln!(f, "{DOUBLE_INDENT}ID: {}", rule.rule_id)?; writeln!(f, "{DOUBLE_INDENT}Incident ID: {}", rule.incident_id)?; if let Some(ref description) = rule.description { writeln!(f, "{DOUBLE_INDENT}Description: {description}")?; @@ -175,7 +175,7 @@ impl std::fmt::Display for OutputConfig { impl std::fmt::Display for OutputRuleMetadata { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { writeln!(f, "\nOutputRuleMetadata")?; - writeln!(f, "{INDENT}ID: {}", self.id)?; + writeln!(f, "{INDENT}ID: {}", self.rule_id)?; writeln!( f, "{INDENT}Disclosed at: {}", diff --git a/rs/boundary_node/rate_limits/api/src/schema_versions/v1.rs b/rs/boundary_node/rate_limits/api/src/schema_versions/v1.rs index 00d79c91c73..42644593858 100644 --- a/rs/boundary_node/rate_limits/api/src/schema_versions/v1.rs +++ b/rs/boundary_node/rate_limits/api/src/schema_versions/v1.rs @@ -1,18 +1,20 @@ -use std::{fmt, time::Duration}; +use std::{ + fmt::{self, Display}, + time::Duration, +}; use candid::Principal; use humantime::{format_duration, parse_duration}; +use ipnet::IpNet; use regex::Regex; use serde::{ - de::{self, Deserializer}, + de::{self, Deserializer, Error}, ser::Serializer, Deserialize, Serialize, }; pub const SCHEMA_VERSION: u64 = 1; -const DOUBLE_INDENT: &str = " "; - #[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum RequestType { @@ -26,7 +28,7 @@ pub enum RequestType { /// Implement serde parser for Action struct ActionVisitor; -impl<'de> de::Visitor<'de> for ActionVisitor { +impl de::Visitor<'_> for ActionVisitor { type Value = Action; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { @@ -42,6 +44,8 @@ impl<'de> de::Visitor<'de> for ActionVisitor { { if s == "block" { return Ok(Action::Block); + } else if s == "pass" { + return Ok(Action::Pass); } let (count, interval) = s @@ -59,8 +63,10 @@ impl<'de> de::Visitor<'de> for ActionVisitor { } } -#[derive(Clone, Eq, PartialEq, Debug)] +#[derive(Clone, Eq, PartialEq, Debug, Default)] pub enum Action { + #[default] + Pass, Block, Limit(u32, Duration), } @@ -74,32 +80,78 @@ impl<'de> Deserialize<'de> for Action { } } +impl Serialize for Action { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + impl fmt::Display for Action { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { + Self::Pass => write!(f, "pass"), Self::Block => write!(f, "block"), Self::Limit(l, d) => write!(f, "{l}/{}", format_duration(*d)), } } } -impl Serialize for Action { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&self.to_string()) +// Checks that u8 is <= 32 +fn de_le_32<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let v = u8::deserialize(deserializer)?; + if v > 32 { + return Err(D::Error::custom("v4 prefix must be <=32")); } + + Ok(v) } -// Defines the rate-limit rule to be stored in the canister -#[derive(Serialize, Deserialize, Debug)] +// Checks that u8 is <= 128 +fn de_le_128<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let v = u8::deserialize(deserializer)?; + if v > 128 { + return Err(D::Error::custom("v6 prefix must be <=128")); + } + + Ok(v) +} + +/// IP prefix lengths for v4 and v6 +/// v4 must be <= 32, v6 <= 128 +#[derive(Clone, Copy, Serialize, Deserialize, Debug, PartialEq, Eq)] +pub struct IpPrefixes { + #[serde(deserialize_with = "de_le_32")] + pub v4: u8, + #[serde(deserialize_with = "de_le_128")] + pub v6: u8, +} + +impl std::fmt::Display for IpPrefixes { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "v4: {}, v6: {}", self.v4, self.v6) + } +} + +/// Defines the rate-limit rule to be stored in the canister +#[derive(Clone, Deserialize, Serialize, Debug, Default)] +#[serde(remote = "Self")] pub struct RateLimitRule { pub canister_id: Option, pub subnet_id: Option, #[serde(default, with = "serde_regex")] pub methods_regex: Option, + pub ip: Option, pub request_types: Option>, + pub ip_prefix_group: Option, pub limit: Action, } @@ -111,42 +163,68 @@ impl PartialEq for RateLimitRule { && self.request_types == other.request_types && self.canister_id == other.canister_id && self.subnet_id == other.subnet_id + && self.ip == other.ip + && self.ip_prefix_group == other.ip_prefix_group && self.limit == other.limit } } impl Eq for RateLimitRule {} -impl std::fmt::Display for RateLimitRule { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - writeln!( - f, - "{DOUBLE_INDENT}Canister ID: {}", - format_principal_option(&self.canister_id) - )?; +impl<'de> Deserialize<'de> for RateLimitRule { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let this = Self::deserialize(deserializer)?; - writeln!( - f, - "{DOUBLE_INDENT}Subnet ID: {}", - format_principal_option(&self.subnet_id) - )?; + if this.ip_prefix_group.is_some() && !matches!(this.limit, Action::Limit(_, _)) { + return Err(D::Error::custom( + "ip_prefix_group only makes sense with 'limit' set to an actual ratelimit", + )); + } + + if this.canister_id.is_none() + && this.subnet_id.is_none() + && this.methods_regex.is_none() + && this.request_types.is_none() + && this.ip.is_none() + { + return Err(D::Error::custom( + "at least one filtering condition must be specified", + )); + } + + Ok(this) + } +} + +impl Serialize for RateLimitRule { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + Self::serialize(self, serializer) + } +} - writeln!( +impl std::fmt::Display for RateLimitRule { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( f, - "{DOUBLE_INDENT}Methods: {}", - &self - .methods_regex - .as_ref() - .map(|x| x.to_string()) - .unwrap_or("None".to_string()) - )?; - - write!(f, "{DOUBLE_INDENT}Limit: {}", &self.limit)?; - Ok(()) + "CanisterID: {}, SubnetID: {}, Request Types: {:?}, Methods: {}, IP: {}, IP Prefix: {}, Limit: {}", + format_option(&self.canister_id), + format_option(&self.subnet_id), + self.request_types, + format_option(&self.methods_regex), + format_option(&self.ip), + format_option(&self.ip_prefix_group), + self.limit, + ) } } -fn format_principal_option(principal: &Option) -> String { - match principal { +fn format_option(v: &Option) -> String { + match v { Some(p) => p.to_string(), None => "None".to_string(), } @@ -172,6 +250,11 @@ impl RateLimitRule { #[cfg(test)] mod test { + use std::{ + net::{IpAddr, Ipv4Addr, Ipv6Addr}, + str::FromStr, + }; + use super::*; use indoc::indoc; @@ -198,6 +281,10 @@ mod test { let rule_raw = indoc! {" canister_id: aaaaa-aa methods_regex: ^.*$ + ip: 10.1.1.0/24 + ip_prefix_group: + v4: 24 + v6: 64 limit: 100/1s "}; @@ -209,13 +296,79 @@ mod test { subnet_id: None, methods_regex: Some(Regex::new("^.*$").unwrap()), request_types: None, + ip: Some(IpNet::new_assert( + IpAddr::V4(Ipv4Addr::new(10, 1, 1, 0)), + 24 + )), + ip_prefix_group: Some(IpPrefixes { v4: 24, v6: 64 }), limit: Action::Limit(100, Duration::from_secs(1)), } ); + // Bad prefix lengths + let rule_raw = indoc! {" + canister_id: aaaaa-aa + methods_regex: ^.*$ + ip: 10.1.1.0/24 + ip_prefix_group: + v4: 33 + v6: 64 + limit: 100/1s + "}; + + assert!(RateLimitRule::from_bytes_yaml(rule_raw.as_bytes()) + .unwrap_err() + .to_string() + .contains("v4 prefix must be")); + + let rule_raw = indoc! {" + canister_id: aaaaa-aa + methods_regex: ^.*$ + ip: 10.1.1.0/24 + ip_prefix_group: + v4: 24 + v6: 129 + limit: 100/1s + "}; + + assert!(RateLimitRule::from_bytes_yaml(rule_raw.as_bytes()) + .unwrap_err() + .to_string() + .contains("v6 prefix must be")); + + // limit: block with ip prefixes + let rule_raw = indoc! {" + canister_id: aaaaa-aa + methods_regex: ^.*$ + ip: 10.1.1.0/24 + ip_prefix_group: + v4: 24 + v6: 64 + limit: block + "}; + + assert!(RateLimitRule::from_bytes_yaml(rule_raw.as_bytes()) + .unwrap_err() + .to_string() + .contains("ip_prefix_group only makes sense with")); + + // No conditions + let rule_raw = indoc! {" + ip_prefix_group: + v4: 24 + v6: 64 + limit: 100/1s + "}; + + assert!(RateLimitRule::from_bytes_yaml(rule_raw.as_bytes()) + .unwrap_err() + .to_string() + .contains("at least one filtering condition must be")); + let rules = indoc! {" - canister_id: aaaaa-aa methods_regex: ^.*$ + ip: 2001:db8::/32 limit: 100/1s - canister_id: 5s2ji-faaaa-aaaaa-qaaaq-cai @@ -241,7 +394,13 @@ mod test { - call - sync_call limit: block - "}; + + - canister_id: 5s2ji-faaaa-aaaaa-qaaaq-cai + request_types: + - call + - sync_call + limit: pass + "}; let rules: Vec = serde_yaml::from_str(rules).unwrap(); @@ -253,6 +412,11 @@ mod test { canister_id: Some(Principal::from_text("aaaaa-aa").unwrap()), request_types: None, methods_regex: Some(Regex::new("^.*$").unwrap()), + ip: Some(IpNet::new_assert( + IpAddr::V6(Ipv6Addr::from_str("2001:db8::").unwrap()), + 32 + )), + ip_prefix_group: None, limit: Action::Limit(100, Duration::from_secs(1)), }, RateLimitRule { @@ -260,6 +424,8 @@ mod test { canister_id: Some(Principal::from_text("5s2ji-faaaa-aaaaa-qaaaq-cai").unwrap()), request_types: None, methods_regex: Some(Regex::new("^(foo|bar)$").unwrap()), + ip: None, + ip_prefix_group: None, limit: Action::Limit(60, Duration::from_secs(60)), }, RateLimitRule { @@ -272,6 +438,8 @@ mod test { canister_id: Some(Principal::from_text("5s2ji-faaaa-aaaaa-qaaaq-cai").unwrap()), request_types: None, methods_regex: None, + ip: None, + ip_prefix_group: None, limit: Action::Limit(90, Duration::from_secs(60)), }, RateLimitRule { @@ -279,6 +447,8 @@ mod test { canister_id: Some(Principal::from_text("5s2ji-faaaa-aaaaa-qaaaq-cai").unwrap()), request_types: None, methods_regex: Some(Regex::new("^(foo|bar)$").unwrap()), + ip: None, + ip_prefix_group: None, limit: Action::Block, }, RateLimitRule { @@ -286,6 +456,8 @@ mod test { canister_id: Some(Principal::from_text("5s2ji-faaaa-aaaaa-qaaaq-cai").unwrap()), request_types: Some(vec![RequestType::Query]), methods_regex: Some(Regex::new("^(foo|bar)$").unwrap()), + ip: None, + ip_prefix_group: None, limit: Action::Block, }, RateLimitRule { @@ -293,8 +465,19 @@ mod test { canister_id: Some(Principal::from_text("5s2ji-faaaa-aaaaa-qaaaq-cai").unwrap()), request_types: Some(vec![RequestType::Call, RequestType::SyncCall]), methods_regex: None, + ip: None, + ip_prefix_group: None, limit: Action::Block, }, + RateLimitRule { + subnet_id: None, + canister_id: Some(Principal::from_text("5s2ji-faaaa-aaaaa-qaaaq-cai").unwrap()), + request_types: Some(vec![RequestType::Call, RequestType::SyncCall]), + methods_regex: None, + ip: None, + ip_prefix_group: None, + limit: Action::Pass, + }, ], ); @@ -304,7 +487,7 @@ mod test { limit: 100/1s "}; let rules = serde_yaml::from_str::>(rules); - assert!(rules.is_err()); + assert!(rules.unwrap_err().to_string().contains("canister_id")); // Bad regex let rules = indoc! {" @@ -313,7 +496,7 @@ mod test { limit: 100/1s "}; let rules = serde_yaml::from_str::>(rules); - assert!(rules.is_err()); + assert!(rules.unwrap_err().to_string().contains("regex")); // Bad limits let rules = indoc! {" @@ -321,42 +504,42 @@ mod test { limit: 100/ "}; let rules = serde_yaml::from_str::>(rules); - assert!(rules.is_err()); + assert!(rules.unwrap_err().to_string().contains("limit")); let rules = indoc! {" - canister_id: aaaaa-aa limit: /100s "}; let rules = serde_yaml::from_str::>(rules); - assert!(rules.is_err()); + assert!(rules.unwrap_err().to_string().contains("limit")); let rules = indoc! {" - canister_id: aaaaa-aa limit: / "}; let rules = serde_yaml::from_str::>(rules); - assert!(rules.is_err()); + assert!(rules.unwrap_err().to_string().contains("limit")); let rules = indoc! {" - canister_id: aaaaa-aa limit: 0/1s "}; let rules = serde_yaml::from_str::>(rules); - assert!(rules.is_err()); + assert!(rules.unwrap_err().to_string().contains("limit")); let rules = indoc! {" - canister_id: aaaaa-aa limit: 1/0s "}; let rules = serde_yaml::from_str::>(rules); - assert!(rules.is_err()); + assert!(rules.unwrap_err().to_string().contains("limit")); let rules = indoc! {" - canister_id: aaaaa-aa limit: 1/1 "}; let rules = serde_yaml::from_str::>(rules); - assert!(rules.is_err()); + assert!(rules.unwrap_err().to_string().contains("limit")); // Bad request type let rules = indoc! {" @@ -365,6 +548,6 @@ mod test { limit: 10/1s "}; let rules = serde_yaml::from_str::>(rules); - assert!(rules.is_err()); + assert!(rules.unwrap_err().to_string().contains("request_type")); } } diff --git a/rs/boundary_node/rate_limits/canister/add_config.rs b/rs/boundary_node/rate_limits/canister/add_config.rs index 6450b9f0767..35e201c68f9 100644 --- a/rs/boundary_node/rate_limits/canister/add_config.rs +++ b/rs/boundary_node/rate_limits/canister/add_config.rs @@ -90,7 +90,11 @@ impl AddsConfig for ConfigAdder { AddConfigError::Internal(anyhow!("No config for version={current_version} found")) })?; - let next_version = current_version + 1; + let next_version = current_version.checked_add(1).ok_or_else(|| { + AddConfigError::Internal(anyhow!( + "Overflow occurred while incrementing the current version {current_version}" + )) + })?; // Ordered IDs of all rules in the submitted config let mut rule_ids = Vec::::new(); diff --git a/rs/boundary_node/rate_limits/canister/canister.rs b/rs/boundary_node/rate_limits/canister/canister.rs index c8648f09c16..3c53e1b89ac 100644 --- a/rs/boundary_node/rate_limits/canister/canister.rs +++ b/rs/boundary_node/rate_limits/canister/canister.rs @@ -5,12 +5,13 @@ use crate::confidentiality_formatting::{ }; use crate::disclose::{DisclosesRules, RulesDiscloser}; use crate::getter::{ConfigGetter, EntityGetter, IncidentGetter, RuleGetter}; +use crate::logs::{self, Log, LogEntry, Priority, P0}; use crate::metrics::{ - export_metrics_as_http_response, with_metrics_registry, WithMetrics, LAST_CANISTER_CHANGE_TIME, - LAST_SUCCESSFUL_REGISTRY_POLL_TIME, REGISTRY_POLL_CALLS_COUNTER, + export_metrics_as_http_response, with_metrics_registry, WithMetrics, METRICS, }; use crate::state::{init_version_and_config, with_canister_state, CanisterApi}; use candid::Principal; +use ic_canister_log::{export as export_logs, log}; use ic_canisters_http_types::{HttpRequest, HttpResponse, HttpResponseBuilder}; use ic_cdk::api::call::call; use ic_cdk_macros::{init, inspect_message, post_upgrade, query, update}; @@ -20,7 +21,7 @@ use rate_limits_api::{ GetApiBoundaryNodeIdsRequest, GetConfigResponse, GetRuleByIdResponse, GetRulesByIncidentIdResponse, IncidentId, InitArg, InputConfig, RuleId, Version, }; -use std::{sync::Arc, time::Duration}; +use std::{borrow::BorrowMut, str::FromStr, sync::Arc, time::Duration}; const REGISTRY_CANISTER_METHOD: &str = "get_api_boundary_node_ids"; const UPDATE_METHODS: [&str; 2] = ["add_config", "disclose_rules"]; @@ -84,8 +85,11 @@ fn init(init_arg: InitArg) { ); }); // Update metric. - LAST_CANISTER_CHANGE_TIME.with(|cell| { - cell.borrow_mut().set(current_time as i64); + METRICS.with(|cell| { + let mut cell = cell.borrow_mut(); + cell.last_canister_change_time + .borrow_mut() + .set(current_time as i64); }); } @@ -146,14 +150,14 @@ fn get_rules_by_incident_id(incident_id: IncidentId) -> GetRulesByIncidentIdResp fn add_config(config: InputConfig) -> AddConfigResponse { let caller_id = ic_cdk::api::caller(); let current_time = ic_cdk::api::time(); - let result = with_canister_state(|state| { + with_canister_state(|state| { let access_resolver = AccessLevelResolver::new(caller_id, state.clone()); let adder = ConfigAdder::new(state); let adder = WithAuthorization::new(adder, access_resolver); let adder = WithMetrics::new(adder); adder.add_config(config, current_time) })?; - Ok(result) + Ok(()) } /// Makes specified rules publicly accessible for viewing @@ -164,14 +168,14 @@ fn add_config(config: InputConfig) -> AddConfigResponse { fn disclose_rules(args: DiscloseRulesArg) -> DiscloseRulesResponse { let caller_id = ic_cdk::api::caller(); let disclose_time = ic_cdk::api::time(); - let result = with_canister_state(|state| { + with_canister_state(|state| { let access_resolver = AccessLevelResolver::new(caller_id, state.clone()); let discloser = RulesDiscloser::new(state); let discloser = WithAuthorization::new(discloser, access_resolver); let discloser = WithMetrics::new(discloser); discloser.disclose_rules(args, disclose_time) })?; - Ok(result) + Ok(()) } #[query(decoding_quota = 10000)] @@ -180,6 +184,50 @@ fn http_request(request: HttpRequest) -> HttpResponse { "/metrics" => with_canister_state(|state| { with_metrics_registry(|registry| export_metrics_as_http_response(registry, state)) }), + "/logs" => { + use serde_json; + + let max_skip_timestamp = match request.raw_query_param("time") { + Some(arg) => match u64::from_str(arg) { + Ok(value) => value, + Err(_) => { + return HttpResponseBuilder::bad_request() + .with_body_and_content_length("failed to parse the 'time' parameter") + .build() + } + }, + None => 0, + }; + + let mut entries: Log = Default::default(); + for entry in export_logs(&logs::P0) { + entries.entries.push(LogEntry { + timestamp: entry.timestamp, + counter: entry.counter, + priority: Priority::P0, + file: entry.file.to_string(), + line: entry.line, + message: entry.message, + }); + } + for entry in export_logs(&logs::P1) { + entries.entries.push(LogEntry { + timestamp: entry.timestamp, + counter: entry.counter, + priority: Priority::P1, + file: entry.file.to_string(), + line: entry.line, + message: entry.message, + }); + } + entries + .entries + .retain(|entry| entry.timestamp >= max_skip_timestamp); + HttpResponseBuilder::ok() + .header("Content-Type", "application/json; charset=utf-8") + .with_body_and_content_length(serde_json::to_string(&entries).unwrap_or_default()) + .build() + } _ => HttpResponseBuilder::not_found().build(), } } @@ -205,22 +253,98 @@ fn periodically_poll_api_boundary_nodes(interval: u64, canister_api: Arc ("failure", "calling_canister_method_failed"), - Err(_) => ("failure", "canister_call_rejected"), + Ok((Err(err),)) => { + log!( + P0, + "[poll_api_boundary_nodes]: failed to fetch nodes from registry {err:?}", + ); + ("failure", "calling_canister_method_failed") + } + Err(err) => { + log!( + P0, + "[poll_api_boundary_nodes]: failed to fetch nodes from registry {err:?}", + ); + ("failure", "canister_call_rejected") + } }; // Update metric. - REGISTRY_POLL_CALLS_COUNTER.with(|cell| { - let metric = cell.borrow_mut(); - metric.with_label_values(&[call_status, message]).inc(); + METRICS.with(|cell| { + let mut cell = cell.borrow_mut(); + cell.registry_poll_calls + .borrow_mut() + .with_label_values(&[call_status, message]) + .inc(); }); }); }); } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn check_candid_interface_compatibility() { + use candid_parser::utils::{service_equal, CandidSource}; + + fn source_to_str(source: &CandidSource) -> String { + match source { + CandidSource::File(f) => { + std::fs::read_to_string(f).unwrap_or_else(|_| "".to_string()) + } + CandidSource::Text(t) => t.to_string(), + } + } + + fn check_service_equal( + new_name: &str, + new: CandidSource, + old_name: &str, + old: CandidSource, + ) { + let new_str = source_to_str(&new); + let old_str = source_to_str(&old); + match service_equal(new, old) { + Ok(_) => {} + Err(e) => { + eprintln!( + "{} is not compatible with {}!\n\n\ + {}:\n\ + {}\n\n\ + {}:\n\ + {}\n", + new_name, old_name, new_name, new_str, old_name, old_str + ); + panic!("{:?}", e); + } + } + } + + candid::export_service!(); + + let new_interface = __export_service(); + + // check the public interface against the actual one + let old_interface = std::path::PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()) + .join("canister/interface.did"); + + check_service_equal( + "actual rate-limit candid interface", + candid_parser::utils::CandidSource::Text(&new_interface), + "declared candid interface in interface.did file", + candid_parser::utils::CandidSource::File(old_interface.as_path()), + ); + } +} diff --git a/rs/boundary_node/rate_limits/canister/getter.rs b/rs/boundary_node/rate_limits/canister/getter.rs index afe365e7953..038d24d6656 100644 --- a/rs/boundary_node/rate_limits/canister/getter.rs +++ b/rs/boundary_node/rate_limits/canister/getter.rs @@ -339,13 +339,13 @@ mod tests { is_redacted: false, rules: vec![ api::OutputRule { - id: rule_id_1.0.to_string(), + rule_id: rule_id_1.0.to_string(), incident_id: incident_id.0.to_string(), rule_raw: Some(b"{\"a\": 1}".to_vec()), description: Some("verbose description 1".to_string()), }, api::OutputRule { - id: rule_id_2.0.to_string(), + rule_id: rule_id_2.0.to_string(), incident_id: incident_id.0.to_string(), rule_raw: Some(b"{\"b\": 2}".to_vec()), description: Some("verbose description 2".to_string()), @@ -367,13 +367,13 @@ mod tests { is_redacted: true, rules: vec![ api::OutputRule { - id: rule_id_1.0.to_string(), + rule_id: rule_id_1.0.to_string(), incident_id: incident_id.0.to_string(), rule_raw: None, description: None, }, api::OutputRule { - id: rule_id_2.0.to_string(), + rule_id: rule_id_2.0.to_string(), incident_id: incident_id.0.to_string(), rule_raw: Some(b"{\"b\": 2}".to_vec()), description: Some("verbose description 2".to_string()), @@ -421,7 +421,7 @@ mod tests { assert_eq!( response, api::OutputRuleMetadata { - id: rule_id.0.to_string(), + rule_id: rule_id.0.to_string(), incident_id: incident_id.0.to_string(), rule_raw: Some(b"{\"a\": 1}".to_vec()), description: Some("verbose description".to_string()), @@ -435,7 +435,7 @@ mod tests { assert_eq!( response, api::OutputRuleMetadata { - id: rule_id.0.to_string(), + rule_id: rule_id.0.to_string(), incident_id: incident_id.0.to_string(), rule_raw: None, description: None, @@ -498,7 +498,7 @@ mod tests { let response = getter_unauthorized.get(&incident_id.0.to_string()).unwrap(); let rule_1 = api::OutputRuleMetadata { - id: rule_id_1.0.to_string(), + rule_id: rule_id_1.0.to_string(), incident_id: incident_id.0.to_string(), rule_raw: None, description: None, @@ -507,7 +507,7 @@ mod tests { removed_in_version: Some(3), }; let rule_2 = api::OutputRuleMetadata { - id: rule_id_2.0.to_string(), + rule_id: rule_id_2.0.to_string(), incident_id: incident_id.0.to_string(), rule_raw: Some(b"{\"b\": 2}".to_vec()), description: Some("verbose description 2".to_string()), diff --git a/rs/boundary_node/rate_limits/canister/interface.did b/rs/boundary_node/rate_limits/canister/interface.did index d1f60823a0b..e87ad544923 100644 --- a/rs/boundary_node/rate_limits/canister/interface.did +++ b/rs/boundary_node/rate_limits/canister/interface.did @@ -29,7 +29,7 @@ type OutputConfig = record { }; // Response structure for returning the requested configuration and associated metadata -type OutputConfigResponse = record { +type ConfigResponse = record { version: Version; // Version of the configuration active_since: Timestamp; // Time when this configuration was added (became active) config: OutputConfig; // Contains the list of rules @@ -94,14 +94,14 @@ type GetConfigError = variant { }; type GetRuleByIdError = variant { - NotFound; // Indicates that a rule with the specified ID does not exist - InvalidUuidFormat: text; // Indicates that the provided ID is not a valid UUID + NotFound; // Indicates that a rule with the specified ID does not exist + InvalidUuidFormat; // Indicates that the provided ID is not a valid UUID Internal: text; // Captures all unexpected internal errors }; type GetRulesByIncidentIdError = variant { - NotFound; // Indicates that an incident with the specified ID does not exist - InvalidUuidFormat: text; // Indicates that the provided ID is not a valid UUID + NotFound; // Indicates that an incident with the specified ID does not exist + InvalidUuidFormat; // Indicates that the provided ID is not a valid UUID Internal: text; // Captures all unexpected internal errors }; @@ -117,7 +117,7 @@ type DiscloseRulesResponse = variant { }; type GetConfigResponse = variant { - Ok: OutputConfigResponse; + Ok: ConfigResponse; Err: GetConfigError; }; diff --git a/rs/boundary_node/rate_limits/canister/lib.rs b/rs/boundary_node/rate_limits/canister/lib.rs index 7e6ee975e84..83adf10f42b 100644 --- a/rs/boundary_node/rate_limits/canister/lib.rs +++ b/rs/boundary_node/rate_limits/canister/lib.rs @@ -2,7 +2,7 @@ mod access_control; #[allow(dead_code)] mod add_config; -#[cfg(target_family = "wasm")] +#[cfg(any(target_family = "wasm", test))] mod canister; #[allow(dead_code)] mod confidentiality_formatting; @@ -10,6 +10,7 @@ mod confidentiality_formatting; mod disclose; #[allow(dead_code)] mod getter; +mod logs; #[allow(dead_code)] mod metrics; mod random; diff --git a/rs/boundary_node/rate_limits/canister/logs.rs b/rs/boundary_node/rate_limits/canister/logs.rs new file mode 100644 index 00000000000..a470a72c57c --- /dev/null +++ b/rs/boundary_node/rate_limits/canister/logs.rs @@ -0,0 +1,29 @@ +use candid::Deserialize; +use ic_canister_log::declare_log_buffer; + +// High-priority messages. +declare_log_buffer!(name = P0, capacity = 1000); + +// Low-priority info messages. +declare_log_buffer!(name = P1, capacity = 1000); + +#[derive(Clone, Debug, Default, Deserialize, serde::Serialize)] +pub struct Log { + pub entries: Vec, +} + +#[derive(Clone, Debug, Deserialize, serde::Serialize)] +pub struct LogEntry { + pub timestamp: u64, + pub priority: Priority, + pub file: String, + pub line: u32, + pub message: String, + pub counter: u64, +} + +#[derive(Clone, Debug, Deserialize, serde::Serialize)] +pub enum Priority { + P0, + P1, +} diff --git a/rs/boundary_node/rate_limits/canister/metrics.rs b/rs/boundary_node/rate_limits/canister/metrics.rs index a2c9ec0d865..2f95b9b285a 100644 --- a/rs/boundary_node/rate_limits/canister/metrics.rs +++ b/rs/boundary_node/rate_limits/canister/metrics.rs @@ -6,136 +6,114 @@ use crate::{ }; use ic_canisters_http_types::{HttpResponse, HttpResponseBuilder}; use ic_cdk::api::stable::WASM_PAGE_SIZE_IN_BYTES; -use prometheus::{CounterVec, Encoder, Gauge, IntGauge, Opts, Registry, TextEncoder}; -use std::cell::RefCell; +use prometheus::{ + CounterVec, Encoder, Gauge, IntGauge, Opts, Registry, Result as PrometheusResult, TextEncoder, +}; +use std::{borrow::BorrowMut, cell::RefCell}; thread_local! { - static STABLE_MEMORY_SIZE: RefCell = RefCell::new( - Gauge::new( + pub static METRICS: RefCell = RefCell::new(CanisterMetrics::new().expect("failed to create Prometheus metrics")); +} + +/// Represents all metrics collected in the canister +pub struct CanisterMetrics { + pub registry: Registry, // Prometheus registry + pub active_rate_limit_rules_count: IntGauge, + pub active_version: IntGauge, + pub api_boundary_nodes_count: IntGauge, + pub canister_api_calls: CounterVec, + pub configs_count: IntGauge, + pub incidents_count: IntGauge, + pub last_canister_change_time: IntGauge, + pub last_successful_registry_poll_time: IntGauge, + pub registry_poll_calls: CounterVec, + pub stable_memory_size: Gauge, +} + +impl CanisterMetrics { + pub fn new() -> PrometheusResult { + let registry = Registry::new(); + + let stable_memory_size = Gauge::new( "stable_memory_bytes", "Size of the stable memory allocated by this canister in bytes.", - ) - .unwrap(), - ); + )?; - static CANISTER_API_CALLS_COUNTER: RefCell = RefCell::new(CounterVec::new(Opts::new( - "canister_api_calls", - "Number of calls to the canister methods with the status and message (in case of error)", - ), &["method", "status", "message"]).unwrap()); + let canister_api_calls = CounterVec::new( + Opts::new( + "canister_api_calls", + "Number of calls to the canister methods with the status and message (in case of error)", + ), + &["method", "status", "message"], + )?; - static API_BOUNDARY_NODES_COUNT: RefCell = RefCell::new( - IntGauge::new( + let api_boundary_nodes_count = IntGauge::new( "api_boundary_nodes_count", "Number of API boundary nodes with full read access permission to rate-limit config.", - ) - .unwrap(), - ); + )?; - static ACTIVE_VERSION: RefCell = RefCell::new( - IntGauge::new( + let active_version = IntGauge::new( "active_config_version", "Version of the currently active configuration", - ) - .unwrap(), - ); + )?; - static ACTIVE_RATE_LIMIT_RULES_COUNT: RefCell = RefCell::new( - IntGauge::new( + let active_rate_limit_rules_count = IntGauge::new( "active_rules_count", "Number of rate-limit rules in the active configuration", - ) - .unwrap(), - ); + )?; - static INCIDENTS_COUNT: RefCell = - RefCell::new(IntGauge::new("stored_incidents_count", "Number of stored incidents").unwrap()); + let incidents_count = + IntGauge::new("stored_incidents_count", "Number of stored incidents")?; - static CONFIGS_COUNT: RefCell = RefCell::new( - IntGauge::new( + let configs_count = IntGauge::new( "stored_configs_count", "Number of stored rate-limit configurations", - ) - .unwrap(), - ); + )?; - pub static LAST_SUCCESSFUL_REGISTRY_POLL_TIME: RefCell = RefCell::new(IntGauge::new( - "last_successful_registry_poll", - "The Unix timestamp of the last successful poll of the API boundary nodes from registry canister").unwrap()); + let last_successful_registry_poll_time = IntGauge::new( + "last_successful_registry_poll", + "The Unix timestamp of the last successful poll of the API boundary nodes from registry canister", + )?; - pub static REGISTRY_POLL_CALLS_COUNTER: RefCell = RefCell::new( - CounterVec::new( + let registry_poll_calls = CounterVec::new( Opts::new( "registry_poll_calls", "Number of registry polling calls with the status and message (in case of error)", ), &["status", "message"], - ) - .unwrap(), - ); + )?; - pub static LAST_CANISTER_CHANGE_TIME: RefCell = RefCell::new( - IntGauge::new( + let last_canister_change_time = IntGauge::new( "last_successful_canister_upgrade", "The Unix timestamp of the last successful canister upgrade", - ) - .unwrap(), - ); - - static METRICS_REGISTRY: RefCell = RefCell::new({ - let registry = Registry::new(); - - STABLE_MEMORY_SIZE.with(|cell| { - let cell = Box::new(cell.borrow().clone()); - registry.register(cell).unwrap(); - }); - - CANISTER_API_CALLS_COUNTER.with(|cell| { - let cell = Box::new(cell.borrow().clone()); - registry.register(cell).unwrap(); - }); - - API_BOUNDARY_NODES_COUNT.with(|cell| { - let cell = Box::new(cell.borrow().clone()); - registry.register(cell).unwrap(); - }); - - ACTIVE_VERSION.with(|cell| { - let cell = Box::new(cell.borrow().clone()); - registry.register(cell).unwrap(); - }); - - ACTIVE_RATE_LIMIT_RULES_COUNT.with(|cell| { - let cell = Box::new(cell.borrow().clone()); - registry.register(cell).unwrap(); - }); - - INCIDENTS_COUNT.with(|cell| { - let cell = Box::new(cell.borrow().clone()); - registry.register(cell).unwrap(); - }); - - CONFIGS_COUNT.with(|cell| { - let cell = Box::new(cell.borrow().clone()); - registry.register(cell).unwrap(); - }); - - LAST_SUCCESSFUL_REGISTRY_POLL_TIME.with(|cell| { - let cell = Box::new(cell.borrow().clone()); - registry.register(cell).unwrap(); - }); - - REGISTRY_POLL_CALLS_COUNTER.with(|cell| { - let cell = Box::new(cell.borrow().clone()); - registry.register(cell).unwrap(); - }); - - LAST_CANISTER_CHANGE_TIME.with(|cell| { - let cell = Box::new(cell.borrow().clone()); - registry.register(cell).unwrap(); - }); - - registry - }); + )?; + + // Register all metrics in the registry + registry.register(Box::new(stable_memory_size.clone()))?; + registry.register(Box::new(canister_api_calls.clone()))?; + registry.register(Box::new(api_boundary_nodes_count.clone()))?; + registry.register(Box::new(active_version.clone()))?; + registry.register(Box::new(active_rate_limit_rules_count.clone()))?; + registry.register(Box::new(incidents_count.clone()))?; + registry.register(Box::new(configs_count.clone()))?; + registry.register(Box::new(last_successful_registry_poll_time.clone()))?; + registry.register(Box::new(registry_poll_calls.clone()))?; + registry.register(Box::new(last_canister_change_time.clone()))?; + + Ok(Self { + registry, + active_rate_limit_rules_count, + active_version, + api_boundary_nodes_count, + canister_api_calls, + configs_count, + incidents_count, + last_canister_change_time, + last_successful_registry_poll_time, + registry_poll_calls, + stable_memory_size, + }) + } } pub fn export_metrics_as_http_response( @@ -163,31 +141,20 @@ pub fn export_metrics_as_http_response( } pub fn recompute_metrics(canister_api: impl CanisterApi) { - STABLE_MEMORY_SIZE.with(|cell| { + METRICS.with(|cell| { + let mut cell = cell.borrow_mut(); + let memory = (ic_cdk::api::stable::stable_size() * WASM_PAGE_SIZE_IN_BYTES) as f64; - cell.borrow_mut().set(memory); - }); - API_BOUNDARY_NODES_COUNT.with(|cell| { - cell.borrow_mut() + cell.stable_memory_size.borrow_mut().set(memory); + cell.api_boundary_nodes_count .set(canister_api.api_boundary_nodes_count() as i64); - }); - - ACTIVE_VERSION.with(|cell| { - cell.borrow_mut() + cell.active_version .set(canister_api.get_version().unwrap_or(0) as i64); - }); - - CONFIGS_COUNT.with(|cell| { - cell.borrow_mut().set(canister_api.configs_count() as i64); - }); - - INCIDENTS_COUNT.with(|cell| { - cell.borrow_mut().set(canister_api.incidents_count() as i64); - }); - - ACTIVE_RATE_LIMIT_RULES_COUNT.with(|cell| { - cell.borrow_mut() + cell.configs_count.set(canister_api.configs_count() as i64); + cell.incidents_count + .set(canister_api.incidents_count() as i64); + cell.active_rate_limit_rules_count .set(canister_api.active_rules_count() as i64); }); } @@ -239,8 +206,8 @@ impl DisclosesRules for WithMetrics { } fn update_canister_call_metrics(method_name: &str, status: &str, message: &str) { - CANISTER_API_CALLS_COUNTER.with(|cell| { - let metric = cell.borrow_mut(); + METRICS.with(|cell| { + let metric = &cell.borrow_mut().canister_api_calls; metric .with_label_values(&[method_name, status, message]) .inc(); @@ -251,8 +218,8 @@ pub fn with_metrics_registry(f: F) -> T where F: FnOnce(&Registry) -> T, { - METRICS_REGISTRY.with(|cell| { - let registry = cell.borrow().clone(); + METRICS.with(|cell| { + let registry = cell.borrow().registry.clone(); f(®istry) }) } diff --git a/rs/boundary_node/rate_limits/canister/types.rs b/rs/boundary_node/rate_limits/canister/types.rs index 672a8b0e954..0b579194632 100644 --- a/rs/boundary_node/rate_limits/canister/types.rs +++ b/rs/boundary_node/rate_limits/canister/types.rs @@ -79,7 +79,7 @@ impl From for api::OutputRule { fn from(value: OutputRule) -> Self { api::OutputRule { description: value.description, - id: value.id.to_string(), + rule_id: value.id.to_string(), incident_id: value.incident_id.to_string(), rule_raw: value.rule_raw, } @@ -299,7 +299,7 @@ pub struct OutputRuleMetadata { impl From for api::OutputRuleMetadata { fn from(value: OutputRuleMetadata) -> Self { api::OutputRuleMetadata { - id: value.id.0.to_string(), + rule_id: value.id.0.to_string(), incident_id: value.incident_id.0.to_string(), rule_raw: value.rule_raw, description: value.description, diff --git a/rs/boundary_node/rate_limits/canister_client/src/lib.rs b/rs/boundary_node/rate_limits/canister_client/src/lib.rs index 2502251b3ef..d91193ef5fb 100644 --- a/rs/boundary_node/rate_limits/canister_client/src/lib.rs +++ b/rs/boundary_node/rate_limits/canister_client/src/lib.rs @@ -8,21 +8,13 @@ use rate_limits_api::{ use serde::Deserialize; use std::{fs, path::PathBuf, str}; use tracing::{debug, info}; -use tracing_subscriber::EnvFilter; use uuid::Uuid; pub async fn submit_config( config_file: PathBuf, canister_id: Principal, agent: Agent, - debug: bool, ) -> Result<(), Error> { - // initialize tracing subscriber with corresponding log level - let log_level = if debug { "debug" } else { "info" }; - tracing_subscriber::fmt() - .with_env_filter(EnvFilter::new(log_level)) - .init(); - // read the rules let rules = read_yaml_file(&config_file)?; @@ -83,6 +75,11 @@ pub async fn submit_config( Ok(()) } +pub fn check_config(config_file: PathBuf) -> Result<(), Error> { + let _ = read_yaml_file(&config_file)?; + Ok(()) +} + #[derive(Debug, Deserialize)] struct YamlRule { #[serde(flatten)] @@ -95,8 +92,7 @@ fn read_yaml_file(file_path: &PathBuf) -> Result, Error> { let yaml_str = fs::read_to_string(file_path).context("Unable to read file")?; // Deserialize directly into `YamlRule` and transform `InputRule` - let yaml_rules: Vec = - serde_yaml::from_str(&yaml_str).context("Failed to parse YAML")?; + let yaml_rules: Vec = serde_yaml::from_str(&yaml_str)?; let input_rules = yaml_rules .into_iter() .map(|entry| InputRule { diff --git a/rs/boundary_node/rate_limits/canister_client/src/main.rs b/rs/boundary_node/rate_limits/canister_client/src/main.rs index 3e8d8df7d53..04be8b669ae 100644 --- a/rs/boundary_node/rate_limits/canister_client/src/main.rs +++ b/rs/boundary_node/rate_limits/canister_client/src/main.rs @@ -1,9 +1,12 @@ +use anyhow::{bail, Context, Error}; use candid::Principal; use clap::Parser; use ic_agent::{identity::Secp256k1Identity, Agent}; use k256::elliptic_curve::SecretKey; -use rate_limiting_canister_client::submit_config; +use rate_limiting_canister_client::{check_config, submit_config}; use std::{path::PathBuf, str}; +use tracing::info; +use tracing_subscriber::EnvFilter; const SERVICE_NAME: &str = "rate-limiting-canister-client"; @@ -12,7 +15,7 @@ const SERVICE_NAME: &str = "rate-limiting-canister-client"; struct Cli { /// ID of the rate-limiting canister #[arg(long)] - canister_id: Principal, + canister_id: Option, /// Path to the file containing all the rules #[arg(long)] @@ -20,33 +23,55 @@ struct Cli { /// Identity key #[arg(long)] - identity_key: String, + identity_key: Option, /// IC domain URL #[arg(long, default_value = "https://icp-api.io")] ic_domain: String, + /// Only check the config file + #[arg(long)] + check: bool, + /// Enable debug logging #[arg(long)] debug: bool, } #[tokio::main] -async fn main() { +async fn main() -> Result<(), Error> { // parse command-line arguments let cli = Cli::parse(); - // create the agent - let identity = Secp256k1Identity::from_private_key( - SecretKey::from_sec1_pem(&cli.identity_key).expect("failed to parse the identity key"), - ); - let agent = Agent::builder() - .with_url(cli.ic_domain) - .with_identity(identity) - .build() - .expect("failed to build the agent"); - - submit_config(cli.config_file, cli.canister_id, agent, cli.debug) - .await - .expect("failed to submit the config to the canister"); + // initialize tracing subscriber with corresponding log level + let log_level = if cli.debug { "debug" } else { "info" }; + tracing_subscriber::fmt() + .with_env_filter(EnvFilter::new(log_level)) + .init(); + + if cli.check { + check_config(cli.config_file).context("Failed to parse the config")?; + info!("Config file is correctly formatted"); + } else { + if cli.identity_key.is_none() || cli.canister_id.is_none() { + bail!("Canister ID and identity key are required to submit the configuration to the canister!"); + } + let identity_key = cli.identity_key.unwrap(); + let canister_id = cli.canister_id.unwrap(); + + // create the agent + let identity = Secp256k1Identity::from_private_key( + SecretKey::from_sec1_pem(&identity_key).context("failed to parse the identity key")?, + ); + let agent = Agent::builder() + .with_url(cli.ic_domain) + .with_identity(identity) + .build() + .context("failed to build the agent")?; + + submit_config(cli.config_file, canister_id, agent) + .await + .context("failed to submit the config to the canister")?; + } + Ok(()) } diff --git a/rs/boundary_node/rate_limits/integration_tests/src/pocket_ic_helpers.rs b/rs/boundary_node/rate_limits/integration_tests/src/pocket_ic_helpers.rs index cfbce8ae134..02c2ea31af4 100644 --- a/rs/boundary_node/rate_limits/integration_tests/src/pocket_ic_helpers.rs +++ b/rs/boundary_node/rate_limits/integration_tests/src/pocket_ic_helpers.rs @@ -7,9 +7,7 @@ use ic_nns_test_utils::common::{ build_mainnet_registry_wasm, build_registry_wasm, NnsInitPayloadsBuilder, }; use ic_registry_transport::pb::v1::RegistryAtomicMutateRequest; -use pocket_ic::{ - management_canister::CanisterSettings, nonblocking::PocketIc, PocketIcBuilder, WasmResult, -}; +use pocket_ic::{management_canister::CanisterSettings, nonblocking::PocketIc, PocketIcBuilder}; use rate_limits_api::InitArg; use serde::de::DeserializeOwned; @@ -115,11 +113,6 @@ pub async fn canister_call( _ => panic!("{method_type} is not allowed"), }; - let result = match result { - WasmResult::Reply(result) => result, - WasmResult::Reject(s) => panic!("Call to {method} failed: {:#?}", s), - }; - let decoded: R = Decode!(&result, R).unwrap(); Ok(decoded) diff --git a/rs/boundary_node/rate_limits/proposals/install_10-01-2025_134775.md b/rs/boundary_node/rate_limits/proposals/install_10-01-2025_134775.md new file mode 100644 index 00000000000..28a5ace58a3 --- /dev/null +++ b/rs/boundary_node/rate_limits/proposals/install_10-01-2025_134775.md @@ -0,0 +1,72 @@ +# Install the Rate-Limit Canister from Commit ab29295 + +__Proposer__: DFINITY Foundation + +__Source code__: [ab29295b39258e753aafaaad72c740d938d61e35][new-commit] + +[new-commit]: https://github.com/dfinity/ic/tree/ab29295b39258e753aafaaad72c740d938d61e35 + +## Summary + +Following the adoption of the motion proposal addressing [incident handling](https://dashboard.internetcomputer.org/proposal/134031) within the framework of the new decentralized boundary node architecture, we propose the deployment of the new rate-limit canister. + +This canister will enable API boundary nodes to enforce rate-limiting rules issued by an authorized DFINITY principal, hence protecting the ICP during incidents. Canister is designed as an append-only storage model, ensuring transparency and auditability of rate-limit rules after incidents disclosure. + +The authorized principal responsible for pushing new rate-limit configurations and disclosing them is specified through the upgrade arguments in this proposal. + +## Verifying the installation + +First, make sure your Git repo has the right information. + +``` +# Option A. Get a fresh copy of the code. +git clone git@github.com:dfinity/ic.git && cd ic +# Option B. If you already have a copy of the ICP repo. +git fetch +``` + +Second, checkout the right version of the code. + +``` +git checkout ab29295b39258e753aafaaad72c740d938d61e35 +``` + +### Argument Verification + +The [didc][latest_didc] tool is required. + +[latest_didc]: https://github.com/dfinity/candid/releases/latest + +Fingerprint the canister argument: + +``` +didc encode \ + -d rs/boundary_node/rate_limits/canister/interface.did \ + -t '(InitArg)' \ + '(record { + authorized_principal = opt principal "2igsz-4cjfz-unvfj-s4d3u-ftcdb-6ibug-em6tf-nzm2h-6igks-spdus-rqe"; + registry_polling_period_secs = 60; + })' | xxd -r -p | sha256sum +``` + +This should match `arg_hash` field of this proposal. + +### WASM Verification + +See ["Building the code"][prereqs] for prerequisites. + +[prereqs]: https://github.com/dfinity/ic/tree/ab29295b39258e753aafaaad72c740d938d61e35/README.adoc#building-the-code + +Build the release version of canisters: + +``` +./ci/container/build-ic.sh -c +``` + +Fingerprint the canister module: + +``` +sha256sum ./artifacts/canisters/rate-limit-canister.wasm.gz +``` + +This should match `wasm_module_hash` field of this proposal. diff --git a/rs/boundary_node/salt_sharing/canister/salt_sharing_canister.did b/rs/boundary_node/salt_sharing/canister/salt_sharing_canister.did new file mode 100644 index 00000000000..74530ee8d8c --- /dev/null +++ b/rs/boundary_node/salt_sharing/canister/salt_sharing_canister.did @@ -0,0 +1,68 @@ +// Represents timestamp in milliseconds since the epoch (1970-01-01) +type Timestamp = nat64; +// Structured HTTP header representation +type HeaderField = record { text; text; }; + +// Detailed salt retrieval response +type GetSaltResponse = variant { + Ok: SaltResponse; + Err: GetSaltError; +}; + +// Comprehensive error for salt retrieval +type GetSaltError = variant { + // Indicates an unauthorized attempt to get the salt + Unauthorized; + // Captures all unexpected internal errors during process + Internal: text; +}; + +// Salt response containing salt itself and additional metadata +type SaltResponse = record { + salt: blob; + salt_id: Timestamp; +}; + +type HttpRequest = record { + method: text; + url: text; + headers: vec HeaderField; + body: blob; +}; + +type HttpResponse = record { + status_code: nat16; + headers: vec HeaderField; + body: blob; +}; + +// Salt generation strategies +type SaltGenerationStrategy = variant { + // Generates a new salt at 00:00:00 UTC on the first day of the next calendar month + // Handles calendar edge cases including: transitions between months (December-January), leap years + StartOfMonth; + // Generates a new salt at fixed intervals from an unspecified reference point + FixedIntervalSecs: nat64; +}; + +// Initialization arguments used when installing/upgrading/reinstalling the canister +type InitArgs = record { + // Strategy defining salt generation + // If specified: + // - salt is regenerated immediately + // - subsequent strategy is based on the variant + // If not specified: + // - preserve previously configured strategy + // - no immediate salt regeneration + salt_generation_strategy: opt SaltGenerationStrategy; + // Interval (in seconds) for polling API boundary node IDs from the registry + // The first polling operation occurs immediately + registry_polling_interval_secs: nat64; +}; + +service : (InitArgs) -> { + // Fetches the current salt (randomly generated value to be added to data before hashing) + get_salt: () -> (GetSaltResponse) query; + // Canister metrics (Http Interface) + http_request: (HttpRequest) -> (HttpResponse) query; +} \ No newline at end of file diff --git a/rs/canister_sandbox/BUILD.bazel b/rs/canister_sandbox/BUILD.bazel index 70e77c64477..fe1a2757697 100644 --- a/rs/canister_sandbox/BUILD.bazel +++ b/rs/canister_sandbox/BUILD.bazel @@ -96,7 +96,6 @@ rust_binary( # Keep sorted. ":backend_lib", ":build_script", - "@crate_index//:libc", ], ) diff --git a/rs/canister_sandbox/bin/canister_sandbox.rs b/rs/canister_sandbox/bin/canister_sandbox.rs index 4f76bde94bd..f1a6645a649 100644 --- a/rs/canister_sandbox/bin/canister_sandbox.rs +++ b/rs/canister_sandbox/bin/canister_sandbox.rs @@ -1,4 +1,4 @@ -#[cfg(target_os = "linux")] +#[cfg(all(target_os = "linux", target_arch = "x86_64"))] extern "C" { fn install_backtrace_handler(); } diff --git a/rs/canister_sandbox/src/protocol/ctlsvc.rs b/rs/canister_sandbox/src/protocol/ctlsvc.rs index 93656fbf868..a23ba95830b 100644 --- a/rs/canister_sandbox/src/protocol/ctlsvc.rs +++ b/rs/canister_sandbox/src/protocol/ctlsvc.rs @@ -67,14 +67,16 @@ mod tests { InstanceStats, SystemApiCallCounters, WasmExecutionOutput, }; use ic_replicated_state::{Global, NumWasmPages, PageMap}; - use ic_system_api::sandbox_safe_system_state::SystemStateChanges; + use ic_system_api::sandbox_safe_system_state::SystemStateModifications; use ic_types::{ingress::WasmResult, CanisterLog, NumBytes, NumInstructions}; use crate::protocol::{ ctlsvc::{ExecutionFinishedReply, ExecutionPausedReply, ExecutionPausedRequest, Reply}, id::ExecId, logging::{LogLevel, LogRequest}, - structs::{MemoryModifications, SandboxExecOutput, StateModifications}, + structs::{ + ExecutionStateModifications, MemoryModifications, SandboxExecOutput, StateModifications, + }, }; use super::{ExecutionFinishedRequest, Request}; @@ -105,24 +107,26 @@ mod tests { system_api_call_counters: SystemApiCallCounters::default(), canister_log: CanisterLog::default(), }, - state: Some(StateModifications { - globals: vec![ - Global::I32(10), - Global::I64(32), - Global::F32(10.5), - Global::F64(1.1), - Global::V128(123), - ], - wasm_memory: MemoryModifications { - page_delta: PageMap::new_for_testing().serialize_delta(&[]), - size: NumWasmPages::new(10), - }, - stable_memory: MemoryModifications { - page_delta: PageMap::new_for_testing().serialize_delta(&[]), - size: NumWasmPages::new(42), - }, - system_state_changes: SystemStateChanges::default(), - }), + state: StateModifications { + execution_state_modifications: Some(ExecutionStateModifications { + globals: vec![ + Global::I32(10), + Global::I64(32), + Global::F32(10.5), + Global::F64(1.1), + Global::V128(123), + ], + wasm_memory: MemoryModifications { + page_delta: PageMap::new_for_testing().serialize_delta(&[]), + size: NumWasmPages::new(10), + }, + stable_memory: MemoryModifications { + page_delta: PageMap::new_for_testing().serialize_delta(&[]), + size: NumWasmPages::new(42), + }, + }), + system_state_modifications: SystemStateModifications::default(), + }, execute_total_duration: Duration::from_secs(10), execute_run_duration: Duration::from_secs(1), }; diff --git a/rs/canister_sandbox/src/protocol/structs.rs b/rs/canister_sandbox/src/protocol/structs.rs index 3cc2d049424..243c1687cdc 100644 --- a/rs/canister_sandbox/src/protocol/structs.rs +++ b/rs/canister_sandbox/src/protocol/structs.rs @@ -4,7 +4,7 @@ use ic_replicated_state::{ page_map::PageDeltaSerialization, Global, Memory, NumWasmPages, PageIndex, }; use ic_system_api::{ - sandbox_safe_system_state::{SandboxSafeSystemState, SystemStateChanges}, + sandbox_safe_system_state::{SandboxSafeSystemState, SystemStateModifications}, ApiType, ExecutionParameters, }; use ic_types::{methods::FuncRef, NumBytes}; @@ -36,11 +36,17 @@ pub struct SandboxExecInput { pub struct SandboxExecOutput { pub slice: SliceExecutionOutput, pub wasm: WasmExecutionOutput, - pub state: Option, + pub state: StateModifications, pub execute_total_duration: std::time::Duration, pub execute_run_duration: std::time::Duration, } +impl SandboxExecOutput { + pub fn take_state_modifications(&mut self) -> StateModifications { + std::mem::take(&mut self.state) + } +} + /// Describes the memory changes performed by execution. #[derive(Clone, PartialEq, Debug, Deserialize, Serialize)] pub struct MemoryModifications { @@ -48,8 +54,24 @@ pub struct MemoryModifications { pub size: NumWasmPages, } -#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)] +#[derive(Serialize, Default, Debug, Deserialize, Clone, PartialEq)] pub struct StateModifications { + /// Modifications in the execution state of the canister. + /// + /// This field is optional because the state changes might or might not + /// be applied depending on the method executed. + pub execution_state_modifications: Option, + + /// Modifications in the system state of the canister. + /// + /// The system state changes contain parts that are always applied + /// and parts that are only applied depending on the method executed + /// (similarly to `execution_state_modifications`). + pub system_state_modifications: SystemStateModifications, +} + +#[derive(Serialize, Debug, Deserialize, Clone, PartialEq)] +pub struct ExecutionStateModifications { /// The state of the global variables after execution. pub globals: Vec, @@ -58,19 +80,15 @@ pub struct StateModifications { /// Modifications in the stable memory. pub stable_memory: MemoryModifications, - - /// Modifications in the system state. - pub system_state_changes: SystemStateChanges, } -impl StateModifications { +impl ExecutionStateModifications { pub fn new( globals: Vec, wasm_memory: &Memory, stable_memory: &Memory, wasm_memory_delta: &[PageIndex], stable_memory_delta: &[PageIndex], - system_state_changes: SystemStateChanges, ) -> Self { let wasm_memory = MemoryModifications { page_delta: wasm_memory.page_map.serialize_delta(wasm_memory_delta), @@ -82,11 +100,10 @@ impl StateModifications { size: stable_memory.size, }; - StateModifications { + ExecutionStateModifications { globals, wasm_memory, stable_memory, - system_state_changes, } } } diff --git a/rs/canister_sandbox/src/replica_controller/sandboxed_execution_controller.rs b/rs/canister_sandbox/src/replica_controller/sandboxed_execution_controller.rs index 15bfa97a703..127281459d5 100644 --- a/rs/canister_sandbox/src/replica_controller/sandboxed_execution_controller.rs +++ b/rs/canister_sandbox/src/replica_controller/sandboxed_execution_controller.rs @@ -3,14 +3,14 @@ use crate::controller_launcher_service::ControllerLauncherService; use crate::launcher_service::LauncherService; use crate::protocol::id::{ExecId, MemoryId, WasmId}; use crate::protocol::sbxsvc::MemorySerialization; -use crate::protocol::structs::{SandboxExecInput, SandboxExecOutput}; +use crate::protocol::structs::{SandboxExecInput, SandboxExecOutput, StateModifications}; use crate::sandbox_service::SandboxService; use crate::{protocol, rpc}; use ic_config::embedders::Config as EmbeddersConfig; use ic_config::flag_status::FlagStatus; use ic_embedders::wasm_executor::{ - get_wasm_reserved_pages, wasm_execution_error, CanisterStateChanges, PausedWasmExecution, - SliceExecutionOutput, WasmExecutionResult, WasmExecutor, + get_wasm_reserved_pages, wasm_execution_error, CanisterStateChanges, ExecutionStateChanges, + PausedWasmExecution, SliceExecutionOutput, WasmExecutionResult, WasmExecutor, }; use ic_embedders::{ wasm_utils::WasmImportsDetails, CompilationCache, CompilationResult, StoredCompilation, @@ -24,7 +24,7 @@ use ic_logger::{error, info, ReplicaLogger}; use ic_metrics::buckets::decimal_buckets_with_zero; use ic_metrics::MetricsRegistry; use ic_replicated_state::canister_state::execution_state::{ - SandboxMemory, SandboxMemoryHandle, SandboxMemoryOwner, WasmBinary, + SandboxMemory, SandboxMemoryHandle, SandboxMemoryOwner, WasmBinary, WasmExecutionMode, }; use ic_replicated_state::{ EmbedderCache, ExecutionState, ExportedFunctions, Memory, PageMap, ReplicatedState, @@ -1089,7 +1089,7 @@ impl WasmExecutor for SandboxedExecutionController { stable_memory_page_map, ic_replicated_state::NumWasmPages::from(0), ); - let is_wasm64 = serialized_module.is_wasm64(); + let (exports, metadata) = serialized_module.exports_and_metadata(); let execution_state = ExecutionState { canister_root, @@ -1101,7 +1101,7 @@ impl WasmExecutor for SandboxedExecutionController { metadata, last_executed_round: ExecutionRound::from(0), next_scheduled_method: NextScheduledMethod::default(), - is_wasm64, + wasm_execution_mode: WasmExecutionMode::from_is_wasm64(serialized_module.is_wasm64()), }; Ok(( @@ -1238,6 +1238,8 @@ impl SandboxedExecutionController { let sandbox_processes = get_sandbox_process_stats(&backends); #[allow(unused_mut)] // for MacOS let mut sandbox_processes_rss = Vec::with_capacity(sandbox_processes.len()); + let mut active_last_used = Vec::with_capacity(sandbox_processes.len()); + let mut evicted_last_used = Vec::with_capacity(sandbox_processes.len()); #[cfg(target_os = "linux")] { @@ -1278,14 +1280,10 @@ impl SandboxedExecutionController { .unwrap_or_else(|| std::time::Duration::from_secs(0)); match status { SandboxProcessStatus::Active => { - metrics - .sandboxed_execution_subprocess_active_last_used - .observe(time_since_last_usage.as_secs_f64()); + active_last_used.push(time_since_last_usage.as_secs_f64()); } SandboxProcessStatus::Evicted => { - metrics - .sandboxed_execution_subprocess_evicted_last_used - .observe(time_since_last_usage.as_secs_f64()); + evicted_last_used.push(time_since_last_usage.as_secs_f64()); } } } @@ -1313,18 +1311,24 @@ impl SandboxedExecutionController { .unwrap_or_else(|| std::time::Duration::from_secs(0)); match status { SandboxProcessStatus::Active => { - metrics - .sandboxed_execution_subprocess_active_last_used - .observe(time_since_last_usage.as_secs_f64()); + active_last_used.push(time_since_last_usage.as_secs_f64()); } SandboxProcessStatus::Evicted => { - metrics - .sandboxed_execution_subprocess_evicted_last_used - .observe(time_since_last_usage.as_secs_f64()); + evicted_last_used.push(time_since_last_usage.as_secs_f64()); } } } } + for o in active_last_used { + metrics + .sandboxed_execution_subprocess_active_last_used + .observe(o); + } + for o in evicted_last_used { + metrics + .sandboxed_execution_subprocess_evicted_last_used + .observe(o); + } { let mut guard = backends.lock().unwrap(); @@ -1575,22 +1579,31 @@ impl SandboxedExecutionController { next_stable_memory_id: MemoryId, canister_id: CanisterId, sandbox_process: Arc, - ) -> Option { + ) -> CanisterStateChanges { // If the execution has failed, then we don't apply any changes. if exec_output.wasm.wasm_result.is_err() { - return None; + return CanisterStateChanges::default(); } - match exec_output.state.take() { - None => None, - Some(state_modifications) => { + + let StateModifications { + execution_state_modifications, + system_state_modifications, + } = exec_output.take_state_modifications(); + + match execution_state_modifications { + None => CanisterStateChanges { + execution_state_changes: None, + system_state_modifications, + }, + Some(execution_state_modifications) => { // TODO: If a canister has broken out of wasm then it might have allocated more // wasm or stable memory then allowed. We should add an additional check here // that thet canister is still within it's allowed memory usage. let mut wasm_memory = execution_state.wasm_memory.clone(); wasm_memory .page_map - .deserialize_delta(state_modifications.wasm_memory.page_delta); - wasm_memory.size = state_modifications.wasm_memory.size; + .deserialize_delta(execution_state_modifications.wasm_memory.page_delta); + wasm_memory.size = execution_state_modifications.wasm_memory.size; wasm_memory.sandbox_memory = SandboxMemory::synced(wrap_remote_memory( &sandbox_process, next_wasm_memory_id, @@ -1610,8 +1623,8 @@ impl SandboxedExecutionController { let mut stable_memory = execution_state.stable_memory.clone(); stable_memory .page_map - .deserialize_delta(state_modifications.stable_memory.page_delta); - stable_memory.size = state_modifications.stable_memory.size; + .deserialize_delta(execution_state_modifications.stable_memory.page_delta); + stable_memory.size = execution_state_modifications.stable_memory.size; stable_memory.sandbox_memory = SandboxMemory::synced(wrap_remote_memory( &sandbox_process, next_stable_memory_id, @@ -1628,12 +1641,14 @@ impl SandboxedExecutionController { .sandboxed_execution_critical_error_invalid_memory_size .inc(); } - Some(CanisterStateChanges { - globals: state_modifications.globals, - wasm_memory, - stable_memory, - system_state_changes: state_modifications.system_state_changes, - }) + CanisterStateChanges { + execution_state_changes: Some(ExecutionStateChanges { + globals: execution_state_modifications.globals, + wasm_memory, + stable_memory, + }), + system_state_modifications, + } } } } diff --git a/rs/canister_sandbox/src/rpc.rs b/rs/canister_sandbox/src/rpc.rs index 8cdef287c94..4dc06be5d51 100644 --- a/rs/canister_sandbox/src/rpc.rs +++ b/rs/canister_sandbox/src/rpc.rs @@ -5,7 +5,6 @@ use std::task::{Context, Poll}; /// Pieces for a very simple bidirectional RPC using an underlying /// duplex stream channel. - /// Describe RPC error -- can be either related to transport (i.e. /// failure to transport or parse a message) or to server (i.e. server /// responded, but gave us a message indicating an error). @@ -276,7 +275,6 @@ impl MessageSink for ReplyManager { /// An RPC result that is immediately "ready" (i.e. pass a value to /// a caller such that it does not need to wait). - pub struct ReadyResult { value: Mutex>, } diff --git a/rs/canister_sandbox/src/sandbox_manager.rs b/rs/canister_sandbox/src/sandbox_manager.rs index 2266ecaee2e..38c32c93f38 100644 --- a/rs/canister_sandbox/src/sandbox_manager.rs +++ b/rs/canister_sandbox/src/sandbox_manager.rs @@ -21,7 +21,8 @@ use std::time::{Duration, Instant}; use crate::protocol::id::{ExecId, MemoryId, WasmId}; use crate::protocol::sbxsvc::{CreateExecutionStateSerializedSuccessReply, OpenMemoryRequest}; use crate::protocol::structs::{ - MemoryModifications, SandboxExecInput, SandboxExecOutput, StateModifications, + ExecutionStateModifications, MemoryModifications, SandboxExecInput, SandboxExecOutput, + StateModifications, }; use crate::{controller_service::ControllerService, protocol}; use ic_config::embedders::Config as EmbeddersConfig; @@ -158,37 +159,44 @@ impl Execution { match wasm_result { Ok(_) => { - let state_modifications = deltas.map( - |WasmStateChanges { - dirty_page_indices, - globals, - }| { - let system_state_changes = match instance_or_system_api { - // Here we use `store_data_mut` instead of - // `into_store_data` because the later will drop the - // wasmtime Instance which can be an expensive - // operation. Mutating the store instead allows us - // to delay the drop until after the execution - // completed message is sent back to the main - // process. - Ok(mut instance) => instance - .store_data_mut() - .system_api_mut() - .expect("System api not present in the wasmtime instance") - .take_system_state_changes(), - Err(system_api) => system_api.into_system_state_changes(), - }; - StateModifications::new( - globals, - &wasm_memory, - &stable_memory, - &dirty_page_indices.wasm_memory_delta, - &dirty_page_indices.stable_memory_delta, - system_state_changes, - ) - }, - ); - if state_modifications.is_some() { + let state_modifications = { + let system_state_modifications = match instance_or_system_api { + // Here we use `store_data_mut` instead of + // `into_store_data` because the later will drop the + // wasmtime Instance which can be an expensive + // operation. Mutating the store instead allows us + // to delay the drop until after the execution + // completed message is sent back to the main + // process. + Ok(mut instance) => instance + .store_data_mut() + .system_api_mut() + .expect("System api not present in the wasmtime instance") + .take_system_state_modifications(), + Err(mut system_api) => system_api.take_system_state_modifications(), + }; + + let execution_state_modifications = deltas.map( + |WasmStateChanges { + dirty_page_indices, + globals, + }| { + ExecutionStateModifications::new( + globals, + &wasm_memory, + &stable_memory, + &dirty_page_indices.wasm_memory_delta, + &dirty_page_indices.stable_memory_delta, + ) + }, + ); + + StateModifications { + execution_state_modifications, + system_state_modifications, + } + }; + if state_modifications.execution_state_modifications.is_some() { self.sandbox_manager .add_memory(exec_input.next_wasm_memory_id, wasm_memory); self.sandbox_manager @@ -237,7 +245,7 @@ impl Execution { exec_output: SandboxExecOutput { slice, wasm: wasm_output, - state: None, + state: StateModifications::default(), execute_total_duration: total_timer.elapsed(), execute_run_duration: run_timer.elapsed(), }, diff --git a/rs/canister_sandbox/src/sandbox_server.rs b/rs/canister_sandbox/src/sandbox_server.rs index 8df92b51f9e..9ac46749ccc 100644 --- a/rs/canister_sandbox/src/sandbox_server.rs +++ b/rs/canister_sandbox/src/sandbox_server.rs @@ -294,7 +294,7 @@ mod tests { } } - fn exec_input_for_query( + fn exec_input_for_replicated_query( method_name: &str, incoming_payload: &[u8], globals: Vec, @@ -303,6 +303,7 @@ mod tests { Time::from_nanos_since_unix_epoch(0), incoming_payload.to_vec(), PrincipalId::try_from([0].as_ref()).unwrap(), + CallContextId::from(0), ); let caller = api_type.caller(); let call_context_id = api_type.call_context_id(); @@ -650,7 +651,12 @@ mod tests { < NumInstructions::from(INSTRUCTION_LIMIT) ); let wasm_result = result.exec_output.wasm.wasm_result.unwrap().unwrap(); - let globals = result.exec_output.state.unwrap().globals; + let globals = result + .exec_output + .state + .execution_state_modifications + .unwrap() + .globals; assert_eq!(WasmResult::Reply([1, 0, 0, 0].to_vec()), wasm_result); // Second time around, issue a query to read the counter. We @@ -662,7 +668,7 @@ mod tests { wasm_id, wasm_memory_id, stable_memory_id, - exec_input: exec_input_for_query("read", &[], globals), + exec_input: exec_input_for_replicated_query("read", &[], globals), }) .sync() .unwrap(); @@ -728,10 +734,14 @@ mod tests { let result = exec_finished_sync.get(); let wasm_result = result.exec_output.wasm.wasm_result.unwrap().unwrap(); - let state_modifications = result.exec_output.state.unwrap(); + let execution_state_modifications = result + .exec_output + .state + .execution_state_modifications + .unwrap(); assert_eq!(WasmResult::Reply([].to_vec()), wasm_result); - wasm_memory.deserialize_delta(state_modifications.wasm_memory.page_delta); + wasm_memory.deserialize_delta(execution_state_modifications.wasm_memory.page_delta); assert_eq!( vec![1, 2, 3, 4], wasm_memory.get_page(PageIndex::new(0))[16..20].to_vec() @@ -799,7 +809,7 @@ mod tests { wasm_id, wasm_memory_id: next_wasm_memory_id, stable_memory_id: next_stable_memory_id, - exec_input: exec_input_for_query( + exec_input: exec_input_for_replicated_query( "read", &[16, 0, 0, 0, 4, 0, 0, 0], vec![Global::I64(0)], @@ -883,7 +893,12 @@ mod tests { < NumInstructions::from(INSTRUCTION_LIMIT) ); let wasm_result = result.exec_output.wasm.wasm_result.unwrap().unwrap(); - let globals = result.exec_output.state.unwrap().globals; + let globals = result + .exec_output + .state + .execution_state_modifications + .unwrap() + .globals; assert_eq!(WasmResult::Reply([1, 0, 0, 0].to_vec()), wasm_result); assert_eq!(Global::I32(1), globals[0]); @@ -923,7 +938,12 @@ mod tests { < NumInstructions::from(INSTRUCTION_LIMIT) ); let wasm_result = result.exec_output.wasm.wasm_result.unwrap().unwrap(); - let globals = result.exec_output.state.unwrap().globals; + let globals = result + .exec_output + .state + .execution_state_modifications + .unwrap() + .globals; assert_eq!(WasmResult::Reply([2, 0, 0, 0].to_vec()), wasm_result); // Second time around, issue a query to read the counter. We @@ -935,7 +955,7 @@ mod tests { wasm_id, wasm_memory_id, stable_memory_id, - exec_input: exec_input_for_query("read", &[], globals), + exec_input: exec_input_for_replicated_query("read", &[], globals), }) .sync() .unwrap(); @@ -1000,10 +1020,14 @@ mod tests { let result = exec_finished_sync.get(); let wasm_result = result.exec_output.wasm.wasm_result.unwrap().unwrap(); - let state_modifications = result.exec_output.state.unwrap(); + let execution_state_modifications = result + .exec_output + .state + .execution_state_modifications + .unwrap(); assert_eq!(WasmResult::Reply([].to_vec()), wasm_result); - stable_memory.deserialize_delta(state_modifications.stable_memory.page_delta); + stable_memory.deserialize_delta(execution_state_modifications.stable_memory.page_delta); assert_eq!( vec![1, 2, 3, 4], stable_memory.get_page(PageIndex::new(0))[16..20].to_vec() @@ -1071,7 +1095,7 @@ mod tests { wasm_id, wasm_memory_id: next_wasm_memory_id, stable_memory_id: next_stable_memory_id, - exec_input: exec_input_for_query( + exec_input: exec_input_for_replicated_query( "read_stable", &[16, 0, 0, 0, 4, 0, 0, 0], vec![Global::I64(0)], @@ -1138,10 +1162,14 @@ mod tests { let result = exec_finished_sync.get(); let wasm_result = result.exec_output.wasm.wasm_result.unwrap().unwrap(); - let state_modifications = result.exec_output.state.unwrap(); + let execution_state_modifications = result + .exec_output + .state + .execution_state_modifications + .unwrap(); assert_eq!(WasmResult::Reply([].to_vec()), wasm_result); - wasm_memory.deserialize_delta(state_modifications.wasm_memory.page_delta); + wasm_memory.deserialize_delta(execution_state_modifications.wasm_memory.page_delta); assert_eq!( vec![1, 2, 3, 4], wasm_memory.get_page(PageIndex::new(0))[16..20].to_vec() @@ -1169,10 +1197,14 @@ mod tests { let result = exec_finished_sync.get(); let wasm_result = result.exec_output.wasm.wasm_result.unwrap().unwrap(); - let state_modifications = result.exec_output.state.unwrap(); + let execution_state_modifications = result + .exec_output + .state + .execution_state_modifications + .unwrap(); assert_eq!(WasmResult::Reply([].to_vec()), wasm_result); - wasm_memory.deserialize_delta(state_modifications.wasm_memory.page_delta); + wasm_memory.deserialize_delta(execution_state_modifications.wasm_memory.page_delta); assert_eq!( vec![5, 6, 7, 8], wasm_memory.get_page(PageIndex::new(0))[32..36].to_vec() @@ -1240,10 +1272,14 @@ mod tests { let result = exec_finished_sync.get(); let wasm_result = result.exec_output.wasm.wasm_result.unwrap().unwrap(); - let state_modifications = result.exec_output.state.unwrap(); + let execution_state_modifications = result + .exec_output + .state + .execution_state_modifications + .unwrap(); assert_eq!(WasmResult::Reply([].to_vec()), wasm_result); - stable_memory.deserialize_delta(state_modifications.stable_memory.page_delta); + stable_memory.deserialize_delta(execution_state_modifications.stable_memory.page_delta); assert_eq!( vec![1, 2, 3, 4], stable_memory.get_page(PageIndex::new(0))[16..20].to_vec() @@ -1271,10 +1307,14 @@ mod tests { let result = exec_finished_sync.get(); let wasm_result = result.exec_output.wasm.wasm_result.unwrap().unwrap(); - let state_modifications = result.exec_output.state.unwrap(); + let execution_state_modifications = result + .exec_output + .state + .execution_state_modifications + .unwrap(); assert_eq!(WasmResult::Reply([].to_vec()), wasm_result); - stable_memory.deserialize_delta(state_modifications.stable_memory.page_delta); + stable_memory.deserialize_delta(execution_state_modifications.stable_memory.page_delta); assert_eq!( vec![5, 6, 7, 8], stable_memory.get_page(PageIndex::new(0))[32..36].to_vec() diff --git a/rs/canister_sandbox/src/transport.rs b/rs/canister_sandbox/src/transport.rs index 57975178cf6..9e6f9a4e9ba 100644 --- a/rs/canister_sandbox/src/transport.rs +++ b/rs/canister_sandbox/src/transport.rs @@ -471,7 +471,7 @@ pub fn socket_read_messages< // updating the socket timeout. loop { if let Some(bytes) = reader.receive_message(&mut buf, &mut fds, 0, None) { - break (bytes); + break bytes; } } } diff --git a/rs/canonical_state/src/encoding/tests/compatibility.rs b/rs/canonical_state/src/encoding/tests/compatibility.rs index dd46b757209..c451d085f61 100644 --- a/rs/canonical_state/src/encoding/tests/compatibility.rs +++ b/rs/canonical_state/src/encoding/tests/compatibility.rs @@ -333,7 +333,6 @@ fn canonical_encoding_stream_header_v19_plus() { /// 00 # unsigned(0) /// 03 # field_index(SubnetMetrics::update_transactions_total) /// 19 1068 # unsigned(4200) - /// ``` /// Used http://cbor.me/ for printing the human friendly output. #[test] diff --git a/rs/canonical_state/src/subtree_visitor.rs b/rs/canonical_state/src/subtree_visitor.rs index a9ebbd7a574..8c9d92c8312 100644 --- a/rs/canonical_state/src/subtree_visitor.rs +++ b/rs/canonical_state/src/subtree_visitor.rs @@ -91,7 +91,7 @@ impl<'a, V> SubtreeVisitor<'a, V> { } } -impl<'a, V> Visitor for SubtreeVisitor<'a, V> +impl Visitor for SubtreeVisitor<'_, V> where V: Visitor, { diff --git a/rs/canonical_state/tree_hash/src/lazy_tree.rs b/rs/canonical_state/tree_hash/src/lazy_tree.rs index 64f6c8d8a4f..fd76d0a3cdc 100644 --- a/rs/canonical_state/tree_hash/src/lazy_tree.rs +++ b/rs/canonical_state/tree_hash/src/lazy_tree.rs @@ -25,7 +25,7 @@ pub enum Lazy<'a, T> { Func(ArcFn<'a, T>), } -impl<'a, T: Clone> Lazy<'a, T> { +impl Lazy<'_, T> { pub fn force(&self) -> T { match self { Self::Value(v) => v.clone(), diff --git a/rs/config/src/config_sample.rs b/rs/config/src/config_sample.rs index 46069274b59..147248c05c6 100644 --- a/rs/config/src/config_sample.rs +++ b/rs/config/src/config_sample.rs @@ -60,7 +60,6 @@ /// # EXAMPLE: y: "bad" /// y: "good" /// ``` - pub const SAMPLE_CONFIG: &str = r#" { // ============================================ diff --git a/rs/config/src/embedders.rs b/rs/config/src/embedders.rs index 9c6ef4db9d7..837f62f5cbb 100644 --- a/rs/config/src/embedders.rs +++ b/rs/config/src/embedders.rs @@ -3,7 +3,10 @@ use std::time::Duration; use ic_base_types::NumBytes; use ic_registry_subnet_type::SubnetType; use ic_sys::PAGE_SIZE; -use ic_types::{NumInstructions, NumOsPages, MAX_STABLE_MEMORY_IN_BYTES, MAX_WASM_MEMORY_IN_BYTES}; +use ic_types::{ + NumInstructions, NumOsPages, MAX_STABLE_MEMORY_IN_BYTES, MAX_WASM64_MEMORY_IN_BYTES, + MAX_WASM_MEMORY_IN_BYTES, +}; use serde::{Deserialize, Serialize}; use crate::flag_status::FlagStatus; @@ -245,6 +248,9 @@ pub struct Config { /// The maximum size of the wasm heap memory. pub max_wasm_memory_size: NumBytes, + /// The maximum size of the wasm heap memory for Wasm64 canisters. + pub max_wasm64_memory_size: NumBytes, + /// The maximum size of the stable memory. pub max_stable_memory_size: NumBytes, } @@ -284,6 +290,7 @@ impl Config { dirty_page_copy_overhead: DIRTY_PAGE_COPY_OVERHEAD, wasm_max_size: WASM_MAX_SIZE, max_wasm_memory_size: NumBytes::new(MAX_WASM_MEMORY_IN_BYTES), + max_wasm64_memory_size: NumBytes::new(MAX_WASM64_MEMORY_IN_BYTES), max_stable_memory_size: NumBytes::new(MAX_STABLE_MEMORY_IN_BYTES), wasm64_dirty_page_overhead_multiplier: WASM64_DIRTY_PAGE_OVERHEAD_MULTIPLIER, } diff --git a/rs/config/src/execution_environment.rs b/rs/config/src/execution_environment.rs index d6e47d83aa6..84636d07e8f 100644 --- a/rs/config/src/execution_environment.rs +++ b/rs/config/src/execution_environment.rs @@ -2,7 +2,8 @@ use crate::embedders::Config as EmbeddersConfig; use crate::flag_status::FlagStatus; use ic_base_types::{CanisterId, NumSeconds}; use ic_types::{ - Cycles, NumBytes, NumInstructions, MAX_STABLE_MEMORY_IN_BYTES, MAX_WASM_MEMORY_IN_BYTES, + Cycles, NumBytes, NumInstructions, MAX_STABLE_MEMORY_IN_BYTES, MAX_WASM64_MEMORY_IN_BYTES, + MAX_WASM_MEMORY_IN_BYTES, }; use serde::{Deserialize, Serialize}; use std::{str::FromStr, time::Duration}; @@ -204,7 +205,12 @@ pub struct Config { pub subnet_memory_reservation: NumBytes, /// The maximum amount of memory that can be utilized by a single canister. - pub max_canister_memory_size: NumBytes, + /// running in Wasm32 mode. + pub max_canister_memory_size_wasm32: NumBytes, + + /// The maximum amount of memory that can be utilized by a single canister. + /// running in Wasm64 mode. + pub max_canister_memory_size_wasm64: NumBytes, /// The soft limit on the subnet-wide number of callbacks. Beyond this limit, /// canisters are only allowed to make downstream calls up to their individual @@ -342,9 +348,12 @@ impl Default for Config { subnet_wasm_custom_sections_memory_capacity: SUBNET_WASM_CUSTOM_SECTIONS_MEMORY_CAPACITY, subnet_memory_reservation: SUBNET_MEMORY_RESERVATION, - max_canister_memory_size: NumBytes::new( + max_canister_memory_size_wasm32: NumBytes::new( MAX_STABLE_MEMORY_IN_BYTES + MAX_WASM_MEMORY_IN_BYTES, ), + max_canister_memory_size_wasm64: NumBytes::new( + MAX_STABLE_MEMORY_IN_BYTES + MAX_WASM64_MEMORY_IN_BYTES, + ), subnet_callback_soft_limit: SUBNET_CALLBACK_SOFT_LIMIT, canister_guaranteed_callback_quota: CANISTER_GUARANTEED_CALLBACK_QUOTA, default_provisional_cycles_balance: Cycles::new(100_000_000_000_000), diff --git a/rs/config/src/http_handler.rs b/rs/config/src/http_handler.rs index d2c6b851725..fd4e717589b 100644 --- a/rs/config/src/http_handler.rs +++ b/rs/config/src/http_handler.rs @@ -1,10 +1,9 @@ use crate::execution_environment::QUERY_EXECUTION_THREADS_TOTAL; use serde::{Deserialize, Serialize}; -use std::net::SocketAddr; +use std::net::{IpAddr, Ipv6Addr, SocketAddr}; use std::path::PathBuf; -const DEFAULT_IP_ADDR: &str = "0.0.0.0"; - +const DEFAULT_IP_V6_ADDR: Ipv6Addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0); const DEFAULT_PORT: u16 = 8080u16; /// The internal configuration -- any historical warts from the external @@ -70,10 +69,7 @@ pub struct Config { impl Default for Config { fn default() -> Self { Self { - listen_addr: SocketAddr::new( - DEFAULT_IP_ADDR.parse().expect("can't fail"), - DEFAULT_PORT, - ), + listen_addr: SocketAddr::new(IpAddr::V6(DEFAULT_IP_V6_ADDR), DEFAULT_PORT), port_file_path: None, connection_read_timeout_seconds: 1_200, // 20 min request_timeout_seconds: 300, // 5 min diff --git a/rs/config/src/registration.rs b/rs/config/src/registration.rs index f95d57147b9..11fe5ecb4e7 100644 --- a/rs/config/src/registration.rs +++ b/rs/config/src/registration.rs @@ -30,6 +30,10 @@ pub struct Config { /// If this Sec256k1 PEM is available, use it instead of the HSM. pub node_operator_pem: Option, + + /// Specifies the type of node rewards that the node operator expects to receive. + /// Examples include "type3.1" or "type1", corresponding to entries in the node rewards table in the NNS. + pub node_reward_type: Option, } // We allow for the operator to only specify some of the fields while the others @@ -57,6 +61,7 @@ impl Default for Config { nns_url: None, nns_pub_key_pem: None, node_operator_pem: None, + node_reward_type: None, } } } diff --git a/rs/config/src/subnet_config.rs b/rs/config/src/subnet_config.rs index 3724a39fa5d..0bbefe812c5 100644 --- a/rs/config/src/subnet_config.rs +++ b/rs/config/src/subnet_config.rs @@ -140,7 +140,6 @@ const DEFAULT_REFERENCE_SUBNET_SIZE: usize = 13; /// Costs for each newly created dirty page in stable memory. const DEFAULT_DIRTY_PAGE_OVERHEAD: NumInstructions = NumInstructions::new(1_000); -const SYSTEM_SUBNET_DIRTY_PAGE_OVERHEAD: NumInstructions = NumInstructions::new(0); /// Accumulated priority reset interval, rounds. /// @@ -341,7 +340,7 @@ impl SchedulerConfig { // This limit should be high enough (1000T) to effectively disable // rate-limiting for the system subnets. install_code_rate_limit: NumInstructions::from(1_000_000_000_000_000), - dirty_page_overhead: SYSTEM_SUBNET_DIRTY_PAGE_OVERHEAD, + dirty_page_overhead: DEFAULT_DIRTY_PAGE_OVERHEAD, accumulated_priority_reset_interval: ACCUMULATED_PRIORITY_RESET_INTERVAL, upload_wasm_chunk_instructions: NumInstructions::from(0), canister_snapshot_baseline_instructions: NumInstructions::from(0), diff --git a/rs/consensus/BUILD.bazel b/rs/consensus/BUILD.bazel index 55d0c52f6e1..512d45e0049 100644 --- a/rs/consensus/BUILD.bazel +++ b/rs/consensus/BUILD.bazel @@ -6,6 +6,7 @@ package(default_visibility = ["//visibility:public"]) DEPENDENCIES = [ # Keep sorted. "//rs/config", + "//rs/consensus/dkg", "//rs/consensus/utils", "//rs/crypto", "//rs/crypto/prng", @@ -30,7 +31,6 @@ DEPENDENCIES = [ "@crate_index//:prometheus", "@crate_index//:rand", "@crate_index//:rand_chacha", - "@crate_index//:rayon", "@crate_index//:slog", "@crate_index//:tokio", ] diff --git a/rs/consensus/Cargo.toml b/rs/consensus/Cargo.toml index 910b2aeae5a..38bee84bf8a 100644 --- a/rs/consensus/Cargo.toml +++ b/rs/consensus/Cargo.toml @@ -8,6 +8,7 @@ documentation.workspace = true [dependencies] ic-config = { path = "../config" } +ic-consensus-dkg = { path = "./dkg" } ic-consensus-utils = { path = "./utils" } ic-crypto = { path = "../crypto" } ic-crypto-prng = { path = "../crypto/prng" } @@ -33,7 +34,6 @@ phantom_newtype = { path = "../phantom_newtype" } prometheus = { workspace = true } rand = { workspace = true } rand_chacha = { workspace = true } -rayon = { workspace = true } slog = { workspace = true } strum_macros = { workspace = true } tokio = { workspace = true } diff --git a/rs/consensus/benches/validate_payload.rs b/rs/consensus/benches/validate_payload.rs index 00d463268f0..db2b14d1507 100644 --- a/rs/consensus/benches/validate_payload.rs +++ b/rs/consensus/benches/validate_payload.rs @@ -10,7 +10,7 @@ //! in the past payloads, and the user signature is checked eventually, and //! the message validates successfully -use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion}; use dkg::DkgDataPayload; use ic_artifact_pool::{consensus_pool::ConsensusPoolImpl, ingress_pool::IngressPoolImpl}; use ic_config::state_manager::Config as StateManagerConfig; @@ -80,7 +80,7 @@ const PAST_PAYLOAD_HEIGHT: u64 = 4; /// Ingress history size: 5 min worth of messages at 1000/sec = 300K. const INGRESS_HISTORY_SIZE: usize = 300_000; -fn run_test(_test_name: &str, test_fn: T) +fn run_test(test_fn: T) where T: FnOnce(Time, &mut ConsensusPoolImpl, &dyn PayloadBuilder), { @@ -233,13 +233,18 @@ fn setup_ingress_state(now: Time, state_manager: &mut StateManagerImpl) { /// Prepares the ingress payload which has 1K x specified number of /// SignedIngress messages. The payload is filled with the specified 'seed' /// bytes -fn prepare_ingress_payload(now: Time, message_count: usize, seed: u8) -> IngressPayload { +fn prepare_ingress_payload( + now: Time, + message_count: usize, + message_size: usize, + seed: u8, +) -> IngressPayload { let mut ingress_msgs = Vec::new(); let expiry = std::time::Duration::from_secs(MAX_INGRESS_TTL.as_secs() - 1); for i in 0..message_count { let ingress = SignedIngressBuilder::new() .method_name("provisional_create_canister_with_cycles") - .method_payload(vec![seed; INGRESS_MESSAGE_SIZE]) + .method_payload(vec![seed; message_size]) .nonce(i as u64) .expiry_time(now + expiry) .canister_id(IC_00) @@ -269,7 +274,7 @@ fn add_past_blocks( for i in 1..=to_add { let mut block = Block::from_parent(&parent); block.rank = Rank(i); - let ingress = prepare_ingress_payload(now, message_count, i as u8); + let ingress = prepare_ingress_payload(now, message_count, INGRESS_MESSAGE_SIZE, i as u8); block.payload = Payload::new( ic_types::crypto::crypto_hash, BlockPayload::Data(DataPayload { @@ -336,7 +341,6 @@ fn validate_payload_benchmark(criterion: &mut Criterion) { for message_count in (50..=850).step_by(50) { run_test( - "validate_payload_benchmark", |now: Time, consensus_pool: &mut ConsensusPoolImpl, payload_builder: &dyn PayloadBuilder| { @@ -344,7 +348,8 @@ fn validate_payload_benchmark(criterion: &mut Criterion) { let pool_reader = PoolReader::new(consensus_pool); let seed = CERTIFIED_HEIGHT + PAST_PAYLOAD_HEIGHT + 10; - let ingress = prepare_ingress_payload(now, message_count, seed as u8); + let ingress = + prepare_ingress_payload(now, message_count, INGRESS_MESSAGE_SIZE, seed as u8); let payload = Payload::new( ic_types::crypto::crypto_hash, BlockPayload::Data(DataPayload { @@ -359,8 +364,7 @@ fn validate_payload_benchmark(criterion: &mut Criterion) { }), ); - let name = format!("validate_payload_{}", message_count); - group.bench_function(&name, |bench| { + group.bench_function(format!("validate_payload_{}", message_count), |bench| { bench.iter(|| { validate_payload(now, &payload, &pool_reader, &tip, payload_builder) .expect("Invalid payload") @@ -372,31 +376,39 @@ fn validate_payload_benchmark(criterion: &mut Criterion) { } fn serialization_benchmark(criterion: &mut Criterion) { - let mut group = criterion.benchmark_group("serialization"); - group.sample_size(30); + let mut group = criterion.benchmark_group("ingress_payload_serialization_deserialization"); + group.sample_size(50); group.measurement_time(std::time::Duration::from_secs(10)); - for message_count in (2000..=8000).step_by(2000) { + for (message_count, message_size_kb, tag) in [ + (1_000, 4_000, "1000x4KB"), + (2_000, 4_000, "2000x4KB"), + (1, 4_000_000, "1x4MB"), + (1, 8_000_000, "1x8MB"), + ] { run_test( - "serialization_benchmark", |now: Time, _: &mut ConsensusPoolImpl, _: &dyn PayloadBuilder| { let seed = CERTIFIED_HEIGHT + PAST_PAYLOAD_HEIGHT + 10; - let ingress = prepare_ingress_payload(now, message_count, seed as u8); - let name = format!("serialization_{}_kb_payload", message_count); - group.bench_function(&name, |bench| { + let ingress = + prepare_ingress_payload(now, message_count, message_size_kb, seed as u8); + + group.bench_function(format!("serialization_{tag}"), |bench| { bench.iter(|| { let proto: pb::IngressPayload = (&ingress).into(); black_box(proto); }) }); - let name = format!("deserialization_{}_kb_payload", message_count); - group.bench_function(&name, |bench| { + + group.bench_function(format!("deserialization_{tag}"), |bench| { let p: pb::IngressPayload = (&ingress).into(); - bench.iter(|| { - let proto = p.clone(); - let deser: IngressPayload = proto.try_into().unwrap(); - black_box(deser); - }) + bench.iter_batched( + || p.clone(), + |proto| { + let deser: IngressPayload = proto.try_into().unwrap(); + black_box(deser); + }, + BatchSize::LargeInput, + ) }); }, ) diff --git a/rs/consensus/dkg/BUILD.bazel b/rs/consensus/dkg/BUILD.bazel new file mode 100644 index 00000000000..0f5a9ae42c0 --- /dev/null +++ b/rs/consensus/dkg/BUILD.bazel @@ -0,0 +1,60 @@ +load("@rules_rust//rust:defs.bzl", "rust_doc", "rust_library", "rust_test") + +package(default_visibility = ["//visibility:public"]) + +DEPENDENCIES = [ + # Keep sorted. + "//rs/consensus/utils", + "//rs/interfaces", + "//rs/interfaces/registry", + "//rs/interfaces/state_manager", + "//rs/monitoring/logger", + "//rs/monitoring/metrics", + "//rs/protobuf", + "//rs/registry/helpers", + "//rs/replicated_state", + "//rs/types/types", + "@crate_index//:prometheus", + "@crate_index//:rayon", + "@crate_index//:slog", +] + +DEV_DEPENDENCIES = [ + # Keep sorted. + "//rs/artifact_pool", + "//rs/consensus/mocks", + "//rs/crypto/test_utils/ni-dkg", + "//rs/test_utilities", + "//rs/test_utilities/artifact_pool", + "//rs/test_utilities/consensus", + "//rs/test_utilities/logger", + "//rs/test_utilities/registry", + "//rs/test_utilities/state", + "//rs/test_utilities/types", +] + +rust_library( + name = "dkg", + srcs = glob(["src/**/*.rs"]), + crate_features = select({ + "//conditions:default": [], + }), + crate_name = "ic_consensus_dkg", + proc_macro_deps = [ + # Keep sorted. + "@crate_index//:strum_macros", + ], + version = "0.9.0", + deps = DEPENDENCIES, +) + +rust_doc( + name = "consensus_dkg_doc", + crate = ":dkg", +) + +rust_test( + name = "consensus_dkg_test", + crate = ":dkg", + deps = DEPENDENCIES + DEV_DEPENDENCIES, +) diff --git a/rs/consensus/dkg/Cargo.toml b/rs/consensus/dkg/Cargo.toml new file mode 100644 index 00000000000..6d21a754f2a --- /dev/null +++ b/rs/consensus/dkg/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "ic-consensus-dkg" +version.workspace = true +authors.workspace = true +edition.workspace = true +description.workspace = true +documentation.workspace = true + +[dependencies] +ic-consensus-utils = { path = "../utils" } +ic-interfaces = { path = "../../interfaces" } +ic-interfaces-registry = { path = "../../interfaces/registry" } +ic-interfaces-state-manager = { path = "../../interfaces/state_manager" } +ic-logger = { path = "../../monitoring/logger" } +ic-metrics = { path = "../../monitoring/metrics" } +ic-protobuf = { path = "../../protobuf" } +ic-registry-client-helpers = { path = "../../registry/helpers" } +ic-replicated-state = { path = "../../replicated_state" } +ic-types = { path = "../../types/types" } +prometheus = { workspace = true } +slog = { workspace = true } +rayon = { workspace = true } + +[dev-dependencies] +ic-artifact-pool = { path = "../../artifact_pool" } +ic-consensus-mocks = { path = "../mocks" } +ic-crypto-test-utils-ni-dkg = { path = "../../crypto/test_utils/ni-dkg" } +ic-test-artifact-pool = { path = "../../test_utilities/artifact_pool" } +ic-test-utilities = { path = "../../test_utilities" } +ic-test-utilities-consensus = { path = "../../test_utilities/consensus" } +ic-test-utilities-logger = { path = "../../test_utilities/logger" } +ic-test-utilities-registry = { path = "../../test_utilities/registry" } +ic-test-utilities-state = { path = "../../test_utilities/state" } +ic-test-utilities-types = { path = "../../test_utilities/types" } diff --git a/rs/consensus/src/consensus/dkg_key_manager.rs b/rs/consensus/dkg/src/dkg_key_manager.rs similarity index 99% rename from rs/consensus/src/consensus/dkg_key_manager.rs rename to rs/consensus/dkg/src/dkg_key_manager.rs index 4d95ead7b60..38b516876bf 100644 --- a/rs/consensus/src/consensus/dkg_key_manager.rs +++ b/rs/consensus/dkg/src/dkg_key_manager.rs @@ -484,7 +484,7 @@ impl DkgKeyManager { .set(summary.registry_version.get() as i64); for tag in [NiDkgTag::LowThreshold, NiDkgTag::HighThreshold].iter() { - let current_transcript = summary.current_transcript(tag); + let current_transcript = summary.current_transcript(tag).unwrap(); let metric_label = &format!("{:?}", tag); self.metrics diff --git a/rs/consensus/src/dkg.rs b/rs/consensus/dkg/src/lib.rs similarity index 86% rename from rs/consensus/src/dkg.rs rename to rs/consensus/dkg/src/lib.rs index 67dc521c357..523a567fbb4 100644 --- a/rs/consensus/src/dkg.rs +++ b/rs/consensus/dkg/src/lib.rs @@ -2,47 +2,26 @@ //! component into the consensus algorithm that is implemented within this //! crate. -use crate::{ - bouncer_metrics::BouncerMetrics, - consensus::{check_protocol_version, dkg_key_manager::DkgKeyManager}, - idkg::{ - make_bootstrap_summary, - payload_builder::make_bootstrap_summary_with_initial_dealings, - utils::{get_idkg_chain_key_config_if_enabled, inspect_idkg_chain_key_initializations}, - }, -}; -use ic_consensus_utils::crypto::ConsensusCrypto; +use ic_consensus_utils::{bouncer_metrics::BouncerMetrics, crypto::ConsensusCrypto}; use ic_interfaces::{ consensus_pool::ConsensusPoolCache, crypto::ErrorReproducibility, dkg::{ChangeAction, DkgPool, Mutations}, p2p::consensus::{Bouncer, BouncerFactory, BouncerValue, PoolMutationsProducer}, }; -use ic_interfaces_registry::RegistryClient; -use ic_logger::{error, info, warn, ReplicaLogger}; +use ic_logger::{error, info, ReplicaLogger}; use ic_metrics::{ buckets::{decimal_buckets, linear_buckets}, MetricsRegistry, }; -use ic_protobuf::registry::subnet::v1::CatchUpPackageContents; -use ic_registry_client_helpers::subnet::SubnetRegistry; use ic_types::{ - batch::ValidationContext, - consensus::{ - dkg::{DealingContent, DkgMessageId, Message}, - idkg, Block, BlockPayload, CatchUpContent, CatchUpPackage, HashedBlock, HashedRandomBeacon, - Payload, RandomBeaconContent, Rank, SummaryPayload, - }, + consensus::dkg::{DealingContent, DkgMessageId, Message}, crypto::{ - crypto_hash, threshold_sig::ni_dkg::{config::NiDkgConfig, NiDkgId, NiDkgTag, NiDkgTargetSubnet}, - CombinedThresholdSig, CombinedThresholdSigOf, CryptoHash, Signed, + Signed, }, - signature::ThresholdSignature, - Height, NodeId, RegistryVersion, SubnetId, Time, + Height, NodeId, ReplicaVersion, }; -pub(crate) use payload_validator::{DkgPayloadValidationFailure, InvalidDkgPayloadReason}; -use phantom_newtype::Id; use prometheus::Histogram; use rayon::prelude::*; use std::{ @@ -50,13 +29,20 @@ use std::{ sync::{Arc, Mutex}, }; -pub(crate) mod payload_builder; -pub(crate) mod payload_validator; +pub mod dkg_key_manager; +pub mod payload_builder; +pub mod payload_validator; + +pub use crate::payload_validator::{DkgPayloadValidationFailure, InvalidDkgPayloadReason}; + #[cfg(test)] mod test_utils; mod utils; -pub use payload_builder::{create_payload, make_genesis_summary, PayloadCreationError}; +pub use { + dkg_key_manager::DkgKeyManager, + payload_builder::{create_payload, make_genesis_summary, PayloadCreationError}, +}; // The maximal number of DKGs for other subnets we want to run in one interval. const MAX_REMOTE_DKGS_PER_INTERVAL: usize = 1; @@ -212,7 +198,7 @@ impl DkgImpl { return Mutations::new(); }; - if check_protocol_version(&message.content.version).is_err() { + if message.content.version != ReplicaVersion::default() { return Mutations::from(ChangeAction::RemoveFromUnvalidated((*message).clone())); } @@ -421,195 +407,6 @@ impl BouncerFactory for DkgBouncer { } } -/// Constructs a genesis/recovery CUP from the CUP contents associated with the -/// given subnet -pub fn make_registry_cup( - registry: &dyn RegistryClient, - subnet_id: SubnetId, - logger: &ReplicaLogger, -) -> Option { - let versioned_record = match registry.get_cup_contents(subnet_id, registry.get_latest_version()) - { - Ok(versioned_record) => versioned_record, - Err(e) => { - warn!( - logger, - "Failed to retrieve versioned record from the registry {:?}", e, - ); - return None; - } - }; - - let cup_contents = versioned_record.value.expect("Missing CUP contents"); - make_registry_cup_from_cup_contents( - registry, - subnet_id, - cup_contents, - versioned_record.version, - logger, - ) -} - -/// Constructs a genesis/recovery CUP from the CUP contents associated with the -/// given subnet from the provided CUP contents -pub fn make_registry_cup_from_cup_contents( - registry: &dyn RegistryClient, - subnet_id: SubnetId, - cup_contents: CatchUpPackageContents, - registry_version: RegistryVersion, - logger: &ReplicaLogger, -) -> Option { - let replica_version = match registry.get_replica_version(subnet_id, registry_version) { - Ok(Some(replica_version)) => replica_version, - err => { - warn!( - logger, - "Failed to retrieve subnet replica version at registry version {:?}: {:?}", - registry_version, - err - ); - return None; - } - }; - let dkg_summary = payload_builder::get_dkg_summary_from_cup_contents( - cup_contents.clone(), - subnet_id, - registry, - registry_version, - ); - let cup_height = Height::new(cup_contents.height); - - let idkg_summary = match bootstrap_idkg_summary( - &cup_contents, - subnet_id, - registry_version, - registry, - logger, - ) { - Ok(summary) => summary, - Err(err) => { - warn!( - logger, - "Failed constructing IDKG summary block from CUP contents: {}.", err - ); - - return None; - } - }; - - let low_dkg_id = dkg_summary - .current_transcript(&NiDkgTag::LowThreshold) - .dkg_id - .clone(); - let high_dkg_id = dkg_summary - .current_transcript(&NiDkgTag::HighThreshold) - .dkg_id - .clone(); - - // In a NNS subnet recovery case the block validation context needs to reference a registry - // version of the NNS to be recovered. Otherwise the validation context points to a registry - // version without the NNS subnet record. - let block_registry_version = cup_contents - .registry_store_uri - .as_ref() - .map(|v| RegistryVersion::from(v.registry_version)) - .unwrap_or(registry_version); - let block = Block { - version: replica_version.clone(), - parent: Id::from(CryptoHash(Vec::new())), - payload: Payload::new( - crypto_hash, - BlockPayload::Summary(SummaryPayload { - dkg: dkg_summary, - idkg: idkg_summary, - }), - ), - height: cup_height, - rank: Rank(0), - context: ValidationContext { - certified_height: cup_height, - registry_version: block_registry_version, - time: Time::from_nanos_since_unix_epoch(cup_contents.time), - }, - }; - let random_beacon = Signed { - content: RandomBeaconContent { - version: replica_version, - height: cup_height, - parent: Id::from(CryptoHash(Vec::new())), - }, - signature: ThresholdSignature { - signer: low_dkg_id, - signature: CombinedThresholdSigOf::new(CombinedThresholdSig(vec![])), - }, - }; - - Some(CatchUpPackage { - content: CatchUpContent::new( - HashedBlock::new(crypto_hash, block), - HashedRandomBeacon::new(crypto_hash, random_beacon), - Id::from(CryptoHash(cup_contents.state_hash)), - /* oldest_registry_version_in_use_by_replicated_state */ None, - ), - signature: ThresholdSignature { - signer: high_dkg_id, - signature: CombinedThresholdSigOf::new(CombinedThresholdSig(vec![])), - }, - }) -} - -fn bootstrap_idkg_summary_from_cup_contents( - cup_contents: &CatchUpPackageContents, - subnet_id: SubnetId, - logger: &ReplicaLogger, -) -> Result { - let initial_dealings = inspect_idkg_chain_key_initializations( - &cup_contents.ecdsa_initializations, - &cup_contents.chain_key_initializations, - )?; - if initial_dealings.is_empty() { - return Ok(None); - }; - - make_bootstrap_summary_with_initial_dealings( - subnet_id, - Height::new(cup_contents.height), - initial_dealings, - logger, - ) - .map_err(|err| format!("Failed to create IDKG summary block: {:?}", err)) -} - -fn bootstrap_idkg_summary( - cup_contents: &CatchUpPackageContents, - subnet_id: SubnetId, - registry_version: RegistryVersion, - registry_client: &dyn RegistryClient, - logger: &ReplicaLogger, -) -> Result { - if let Some(summary) = - bootstrap_idkg_summary_from_cup_contents(cup_contents, subnet_id, logger)? - { - return Ok(Some(summary)); - } - - match get_idkg_chain_key_config_if_enabled(subnet_id, registry_version, registry_client) - .map_err(|err| format!("Failed getting the chain key config: {:?}", err))? - { - Some(chain_key_config) => Ok(make_bootstrap_summary( - subnet_id, - chain_key_config - .key_configs - .iter() - .map(|key_config| key_config.key_id.clone()) - .filter_map(|key_id| key_id.try_into().ok()) - .collect(), - Height::new(cup_contents.height), - )), - None => Ok(None), - } -} - #[cfg(test)] mod tests { use super::{test_utils::complement_state_manager_with_remote_dkg_requests, *}; @@ -619,28 +416,22 @@ mod tests { dependencies_with_subnet_records_with_raw_state_manager, Dependencies, }; use ic_consensus_utils::pool_reader::PoolReader; - use ic_crypto_test_utils_ni_dkg::dummy_initial_dkg_transcript; use ic_interfaces::{ consensus_pool::ConsensusPool, p2p::consensus::{MutablePool, UnvalidatedArtifact}, }; - use ic_interfaces_registry::RegistryVersionedRecord; - use ic_logger::replica_logger::no_op_logger; + use ic_interfaces_registry::RegistryClient; use ic_metrics::MetricsRegistry; - use ic_protobuf::registry::subnet::v1::{CatchUpPackageContents, SubnetRecord}; use ic_test_artifact_pool::consensus_pool::TestConsensusPool; use ic_test_utilities::crypto::CryptoReturningOk; use ic_test_utilities_logger::with_test_replica_logger; use ic_test_utilities_registry::{add_subnet_record, SubnetRecordBuilder}; use ic_test_utilities_types::ids::{node_test_id, subnet_test_id}; use ic_types::{ - consensus::HasVersion, - crypto::threshold_sig::ni_dkg::{ - NiDkgDealing, NiDkgId, NiDkgTag, NiDkgTargetId, NiDkgTargetSubnet, - }, - registry::RegistryClientError, + consensus::{Block, BlockPayload}, + crypto::threshold_sig::ni_dkg::{NiDkgDealing, NiDkgId, NiDkgTargetId, NiDkgTargetSubnet}, time::UNIX_EPOCH, - PrincipalId, RegistryVersion, ReplicaVersion, + RegistryVersion, ReplicaVersion, }; use std::{collections::BTreeSet, convert::TryFrom}; @@ -1855,7 +1646,7 @@ mod tests { RegistryVersion::from(5) ); for tag in TAGS.iter() { - let current_transcript = dkg_summary.current_transcript(tag); + let current_transcript = dkg_summary.current_transcript(tag).unwrap(); assert_eq!( current_transcript.dkg_id.start_block_height, Height::from(0) @@ -1909,7 +1700,7 @@ mod tests { ); for tag in TAGS.iter() { // We reused the transcript. - let current_transcript = dkg_summary.current_transcript(tag); + let current_transcript = dkg_summary.current_transcript(tag).unwrap(); assert_eq!( current_transcript.dkg_id.start_block_height, Height::from(0) @@ -1972,7 +1763,7 @@ mod tests { conf.receivers().get(), &committee3.clone().into_iter().collect::>() ); - let current_transcript = dkg_summary.current_transcript(tag); + let current_transcript = dkg_summary.current_transcript(tag).unwrap(); assert_eq!( current_transcript.dkg_id.start_block_height, Height::from(0) @@ -2012,7 +1803,7 @@ mod tests { conf.receivers().get(), &committee3.clone().into_iter().collect::>() ); - let current_transcript = dkg_summary.current_transcript(tag); + let current_transcript = dkg_summary.current_transcript(tag).unwrap(); assert_eq!( current_transcript.dkg_id.start_block_height, Height::from(5) @@ -2050,7 +1841,7 @@ mod tests { conf.receivers().get(), &committee3.clone().into_iter().collect::>() ); - let current_transcript = dkg_summary.current_transcript(tag); + let current_transcript = dkg_summary.current_transcript(tag).unwrap(); assert_eq!( current_transcript.dkg_id.start_block_height, Height::from(10) @@ -2061,131 +1852,6 @@ mod tests { }); } - /// `RegistryClient` implementation that allows to provide a custom function - /// to provide a `get_versioned_value`. - struct MockRegistryClient - where - F: Fn(&str, RegistryVersion) -> Option>, - { - latest_registry_version: RegistryVersion, - get_versioned_value_fun: F, - } - - impl MockRegistryClient - where - F: Fn(&str, RegistryVersion) -> Option>, - { - fn new(latest_registry_version: RegistryVersion, get_versioned_value_fun: F) -> Self { - Self { - latest_registry_version, - get_versioned_value_fun, - } - } - } - - impl RegistryClient for MockRegistryClient - where - F: Fn(&str, RegistryVersion) -> Option> + Send + Sync, - { - fn get_versioned_value( - &self, - key: &str, - version: RegistryVersion, - ) -> ic_interfaces_registry::RegistryClientVersionedResult> { - let value = (self.get_versioned_value_fun)(key, version); - Ok(RegistryVersionedRecord { - key: key.to_string(), - version, - value, - }) - } - - // Not needed for this test - fn get_key_family( - &self, - _: &str, - _: RegistryVersion, - ) -> Result, RegistryClientError> { - Ok(vec![]) - } - - fn get_latest_version(&self) -> RegistryVersion { - self.latest_registry_version - } - - // Not needed for this test - fn get_version_timestamp(&self, _: RegistryVersion) -> Option = Sender<(SlotUpdate, NodeId, ConnId)>; +type InboundSlotUpdatesSender = Sender<(SlotUpdate, NodeId, ConnId)>; pub fn build_axum_router( log: ReplicaLogger, @@ -78,7 +76,7 @@ impl IntoResponse for UpdateHandlerError { } async fn update_handler( - State((log, sender)): State<(ReplicaLogger, ReceivedAdvertSender)>, + State((log, sender)): State<(ReplicaLogger, InboundSlotUpdatesSender)>, Extension(peer): Extension, Extension(conn_id): Extension, payload: Bytes, @@ -115,10 +113,7 @@ async fn update_handler( }; if sender.send((update, peer, conn_id)).await.is_err() { - error!( - log, - "Failed to send advert update from handler to event loop" - ) + error!(log, "Failed to send slot update from handler to event loop") } Ok(()) @@ -175,17 +170,17 @@ impl PeerCounter { pub(crate) struct ConsensusManagerReceiver< Artifact: IdentifiableArtifact, - WireArtifact: IdentifiableArtifact, + WireArtifact: IdentifiableArtifact + PbArtifact, Assembler, - ReceivedAdvert, > { log: ReplicaLogger, metrics: ConsensusManagerMetrics, rt_handle: Handle, // Receive side: - adverts_received: Receiver, - sender: UnboundedSender>, + slot_updates_rx: Receiver<(SlotUpdate, NodeId, ConnId)>, + sender: Sender>, + artifact_assembler: Assembler, slot_table: HashMap>>, @@ -199,13 +194,7 @@ pub(crate) struct ConsensusManagerReceiver< slot_limit: usize, } -impl - ConsensusManagerReceiver< - Artifact, - WireArtifact, - Assembler, - (SlotUpdate, NodeId, ConnId), - > +impl ConsensusManagerReceiver where Artifact: IdentifiableArtifact, WireArtifact: PbArtifact, @@ -215,9 +204,9 @@ where log: ReplicaLogger, metrics: ConsensusManagerMetrics, rt_handle: Handle, - adverts_received: Receiver<(SlotUpdate, NodeId, ConnId)>, + slot_updates_rx: Receiver<(SlotUpdate, NodeId, ConnId)>, artifact_assembler: Assembler, - sender: UnboundedSender>, + sender: Sender>, topology_watcher: watch::Receiver, slot_limit: usize, ) -> Shutdown { @@ -225,7 +214,7 @@ where log, metrics, rt_handle: rt_handle.clone(), - adverts_received, + slot_updates_rx, artifact_assembler, sender, active_assembles: HashMap::new(), @@ -241,7 +230,7 @@ where ) } - /// Event loop that processes advert updates and artifact assembles. + /// Event loop that processes slot updates and artifact assembles. /// The event loop preserves the invariants checked with `debug_assert`. async fn start_event_loop(mut self, cancellation_token: CancellationToken) { loop { @@ -249,13 +238,13 @@ where _ = cancellation_token.cancelled() => { error!( self.log, - "Sender event loop for the P2P client `{:?}` terminated. No more adverts will be sent for this client.", + "Sender event loop for the P2P client `{:?}` terminated. No more slot updates will be sent for this client.", uri_prefix::() ); break; } - Some((advert_update, peer_id, conn_id)) = self.adverts_received.recv() => { - self.handle_advert_receive(advert_update, peer_id, conn_id, cancellation_token.clone()); + Some((slot_update, peer_id, conn_id)) = self.slot_updates_rx.recv() => { + self.handle_slot_update_receive(slot_update, peer_id, conn_id, cancellation_token.clone()); } Some(result) = self.artifact_processor_tasks.join_next() => { match result { @@ -310,7 +299,7 @@ where self.metrics.assemble_task_restart_after_join_total.inc(); self.metrics.assemble_task_started_total.inc(); self.artifact_processor_tasks.spawn_on( - Self::process_advert( + Self::process_slot_update( self.log.clone(), id, None, @@ -335,9 +324,9 @@ where } #[instrument(skip_all)] - pub(crate) fn handle_advert_receive( + pub(crate) fn handle_slot_update_receive( &mut self, - advert_update: SlotUpdate, + slot_update: SlotUpdate, peer_id: NodeId, connection_id: ConnId, cancellation_token: CancellationToken, @@ -347,7 +336,7 @@ where slot_number, commit_id, update, - } = advert_update; + } = slot_update; let (id, artifact) = match update { Update::Artifact(artifact) => (artifact.id(), Some(artifact)), @@ -413,7 +402,7 @@ where self.active_assembles.insert(id.clone(), tx); self.artifact_processor_tasks.spawn_on( - Self::process_advert( + Self::process_slot_update( self.log.clone(), id.clone(), artifact.map(|a| (a, peer_id)), @@ -453,13 +442,13 @@ where /// This future waits for all peers that advertise the artifact to delete it. /// The artifact is deleted from the unvalidated pool upon completion. #[instrument(skip_all)] - async fn process_advert( + async fn process_slot_update( log: ReplicaLogger, id: WireArtifact::Id, // Only first peer for specific artifact ID is considered for push artifact: Option<(WireArtifact, NodeId)>, mut peer_rx: watch::Receiver, - sender: UnboundedSender>, + sender: Sender>, artifact_assembler: Assembler, metrics: ConsensusManagerMetrics, cancellation_token: CancellationToken, @@ -488,10 +477,13 @@ where select! { assemble_result = assemble_artifact => { match assemble_result { - Ok((artifact, peer_id)) => { - let id = artifact.id(); - // Send artifact to pool - if sender.send(UnvalidatedArtifactMutation::Insert((artifact, peer_id))).is_err() { + AssembleResult::Done { message, peer_id } => { + let id = message.id(); + // Sends artifact to the pool. In theory this channel can get full if there is a bug in consensus and each round takes very long time. + // However, the duration of this await is not IO-bound so for the time being it is fine that sending over the channel is not done as + // part of a select. + if sender.send(UnvalidatedArtifactMutation::Insert((message, peer_id))).await.is_err() { + error!(log, "The receiving side of the channel, owned by the consensus thread, was closed. This should be infallible situation since a cancellation token should be received. If this happens then most likely there is very subnet synchonization bug."); } @@ -499,8 +491,10 @@ where // TODO: NET-1774 let _ = peer_rx.wait_for(|p| p.is_empty()).await; - // Purge from the unvalidated pool - if sender.send(UnvalidatedArtifactMutation::Remove(id)).is_err() { + // Purge artifact from the unvalidated pool. In theory this channel can get full if there is a bug in consensus and each round takes very long time. + // However, the duration of this await is not IO-bound so for the time being it is fine that sending over the channel is not done as + // part of a select. + if sender.send(UnvalidatedArtifactMutation::Remove(id)).await.is_err() { error!(log, "The receiving side of the channel, owned by the consensus thread, was closed. This should be infallible situation since a cancellation token should be received. If this happens then most likely there is very subnet synchonization bug."); } metrics @@ -508,7 +502,7 @@ where .with_label_values(&[ASSEMBLE_TASK_RESULT_COMPLETED]) .inc(); } - Err(Aborted) => { + AssembleResult::Unwanted => { // wait for deletion from peers // TODO: NET-1774 let _ = peer_rx.wait_for(|p| p.is_empty()).await; @@ -553,12 +547,8 @@ where }); for peers_sender in self.active_assembles.values() { - peers_sender.send_if_modified(|set| { - nodes_leaving_topology - .iter() - .map(|n| set.remove(*n)) - .any(|r| r) - }); + peers_sender + .send_if_modified(|set| nodes_leaving_topology.iter().any(|n| set.remove(*n))); } debug_assert!( self.slot_table.len() <= self.topology_watcher.borrow().iter().count(), @@ -610,7 +600,7 @@ mod tests { use ic_test_utilities_logger::with_test_replica_logger; use ic_types::{artifact::IdentifiableArtifact, RegistryVersion}; use ic_types_test_utils::ids::{NODE_1, NODE_2}; - use tokio::{sync::mpsc::UnboundedReceiver, time::timeout}; + use tokio::time::timeout; use tower::util::ServiceExt; use super::*; @@ -618,9 +608,9 @@ mod tests { const PROCESS_ARTIFACT_TIMEOUT: Duration = Duration::from_millis(1000); struct ReceiverManagerBuilder { - // Adverts received from peers - adverts_received: Receiver<(SlotUpdate, NodeId, ConnId)>, - sender: UnboundedSender>, + // Slot updates received from peers + slot_updates_rx: Receiver<(SlotUpdate, NodeId, ConnId)>, + sender: Sender>, artifact_assembler: MockArtifactAssembler, topology_watcher: watch::Receiver, slot_limit: usize, @@ -628,15 +618,11 @@ mod tests { channels: Channels, } - type ConsensusManagerReceiverForTest = ConsensusManagerReceiver< - U64Artifact, - U64Artifact, - MockArtifactAssembler, - (SlotUpdate, NodeId, ConnId), - >; + type ConsensusManagerReceiverForTest = + ConsensusManagerReceiver; struct Channels { - unvalidated_artifact_receiver: UnboundedReceiver>, + unvalidated_artifact_receiver: Receiver>, } impl ReceiverManagerBuilder { @@ -651,14 +637,14 @@ mod tests { } fn new() -> Self { - let (_, adverts_received) = tokio::sync::mpsc::channel(100); - let (sender, unvalidated_artifact_receiver) = tokio::sync::mpsc::unbounded_channel(); + let (_, slot_updates_rx) = tokio::sync::mpsc::channel(100); + let (sender, unvalidated_artifact_receiver) = tokio::sync::mpsc::channel(1000); let (_, topology_watcher) = watch::channel(SubnetTopology::default()); let artifact_assembler = Self::make_mock_artifact_assembler_with_clone(MockArtifactAssembler::default); Self { - adverts_received, + slot_updates_rx, sender, topology_watcher, artifact_assembler, @@ -698,7 +684,7 @@ mod tests { &MetricsRegistry::default(), ), rt_handle: Handle::current(), - adverts_received: self.adverts_received, + slot_updates_rx: self.slot_updates_rx, sender: self.sender, artifact_assembler: self.artifact_assembler, topology_watcher: self.topology_watcher, @@ -712,13 +698,13 @@ mod tests { } } - /// Check that all variants of stale adverts to not get added to the slot table. + /// Check that all variants of stale slot updates to not get added to the slot table. #[tokio::test] - async fn receiving_stale_advert_updates() { + async fn receiving_stale_slot_updates() { let (mut mgr, _channels) = ReceiverManagerBuilder::new().build(); let cancellation = CancellationToken::new(); - mgr.handle_advert_receive( + mgr.handle_slot_update_receive( SlotUpdate { slot_number: SlotNumber::from(1), commit_id: CommitId::from(1), @@ -744,8 +730,8 @@ mod tests { assert_eq!(mgr.slot_table.get(&NODE_1).unwrap().len(), 1); assert_eq!(mgr.active_assembles.len(), 1); assert_eq!(mgr.artifact_processor_tasks.len(), 1); - // Send stale advert with lower commit id. - mgr.handle_advert_receive( + // Send stale slot update with lower commit id. + mgr.handle_slot_update_receive( SlotUpdate { slot_number: SlotNumber::from(1), commit_id: CommitId::from(0), @@ -768,8 +754,8 @@ mod tests { id: 0, } ); - // Send stale advert with lower conn id - mgr.handle_advert_receive( + // Send stale slot update with lower conn id + mgr.handle_slot_update_receive( SlotUpdate { slot_number: SlotNumber::from(1), commit_id: CommitId::from(0), @@ -792,8 +778,8 @@ mod tests { id: 0, } ); - // Send stale advert with lower conn id but higher commit id - mgr.handle_advert_receive( + // Send stale slot update with lower conn id but higher commit id + mgr.handle_slot_update_receive( SlotUpdate { slot_number: SlotNumber::from(1), commit_id: CommitId::from(10), @@ -816,8 +802,8 @@ mod tests { id: 0, } ); - // Send stale advert with lower conn id and lower commit id - mgr.handle_advert_receive( + // Send stale slot update with lower conn id and lower commit id + mgr.handle_slot_update_receive( SlotUpdate { slot_number: SlotNumber::from(1), commit_id: CommitId::from(0), @@ -851,7 +837,7 @@ mod tests { let (mut mgr, _channels) = ReceiverManagerBuilder::new().with_slot_limit(2).build(); let cancellation = CancellationToken::new(); - mgr.handle_advert_receive( + mgr.handle_slot_update_receive( SlotUpdate { slot_number: SlotNumber::from(1), commit_id: CommitId::from(1), @@ -861,7 +847,7 @@ mod tests { ConnId::from(1), cancellation.clone(), ); - mgr.handle_advert_receive( + mgr.handle_slot_update_receive( SlotUpdate { slot_number: SlotNumber::from(2), commit_id: CommitId::from(2), @@ -875,7 +861,7 @@ mod tests { assert_eq!(mgr.slot_table.get(&NODE_1).unwrap().len(), 2); assert_eq!(mgr.active_assembles.len(), 2); // Send slot update that exceeds limit - mgr.handle_advert_receive( + mgr.handle_slot_update_receive( SlotUpdate { slot_number: SlotNumber::from(3), commit_id: CommitId::from(3), @@ -889,7 +875,7 @@ mod tests { assert_eq!(mgr.active_assembles.len(), 2); } - /// Check that adverts updates with higher connection ids take precedence. + /// Check that slot updates with higher connection ids take precedence. #[tokio::test] async fn overwrite_slot1() { fn make_artifact_assembler() -> MockArtifactAssembler { @@ -897,7 +883,12 @@ mod tests { artifact_assembler .expect_assemble_message() .returning(|id, _, _: PeerWatcher| { - Box::pin(async move { Ok((U64Artifact::id_to_msg(id, 100), NODE_1)) }) + Box::pin(async move { + AssembleResult::Done { + message: U64Artifact::id_to_msg(id, 100), + peer_id: NODE_1, + } + }) }); artifact_assembler } @@ -905,7 +896,7 @@ mod tests { .with_artifact_assembler_maker(make_artifact_assembler) .build(); let cancellation = CancellationToken::new(); - mgr.handle_advert_receive( + mgr.handle_slot_update_receive( SlotUpdate { slot_number: SlotNumber::from(1), commit_id: CommitId::from(1), @@ -915,7 +906,7 @@ mod tests { ConnId::from(1), cancellation.clone(), ); - // Verify that advert is correctly inserted into slot table. + // Verify that slot update is correctly inserted into slot table. assert_eq!( mgr.slot_table .get(&NODE_1) @@ -932,8 +923,8 @@ mod tests { assert_eq!(mgr.slot_table.get(&NODE_1).unwrap().len(), 1); assert_eq!(mgr.active_assembles.len(), 1); assert_eq!(mgr.artifact_processor_tasks.len(), 1); - // Send advert with higher conn id. - mgr.handle_advert_receive( + // Send slot update with higher conn id. + mgr.handle_slot_update_receive( SlotUpdate { slot_number: SlotNumber::from(1), commit_id: CommitId::from(0), @@ -965,11 +956,11 @@ mod tests { .expect("Joining artifact processor task failed") .expect("Artifact processor task panicked"); - // Check that assemble task for first advert closes. + // Check that assemble task for first slot closes. assert_eq!(result.1, 0); } - /// Check that adverts updates with higher connection ids take precedence. + /// Check that slot updates with higher connection ids take precedence. #[tokio::test] async fn overwrite_slot_send_remove() { fn make_artifact_assembler() -> MockArtifactAssembler { @@ -977,7 +968,12 @@ mod tests { artifact_assembler .expect_assemble_message() .returning(|id, _, _: PeerWatcher| { - Box::pin(async move { Ok((U64Artifact::id_to_msg(id, 100), NODE_1)) }) + Box::pin(async move { + AssembleResult::Done { + message: U64Artifact::id_to_msg(id, 100), + peer_id: NODE_1, + } + }) }); artifact_assembler } @@ -985,7 +981,7 @@ mod tests { .with_artifact_assembler_maker(make_artifact_assembler) .build(); let cancellation = CancellationToken::new(); - mgr.handle_advert_receive( + mgr.handle_slot_update_receive( SlotUpdate { slot_number: SlotNumber::from(1), commit_id: CommitId::from(1), @@ -995,7 +991,7 @@ mod tests { ConnId::from(1), cancellation.clone(), ); - // Verify that advert is correctly inserted into slot table. + // Verify that slot updates is correctly inserted into slot table. assert_eq!( mgr.slot_table .get(&NODE_1) @@ -1017,8 +1013,8 @@ mod tests { UnvalidatedArtifactMutation::Insert((U64Artifact::id_to_msg(0, 100), NODE_1)) ); - // Send advert with higher conn id. - mgr.handle_advert_receive( + // Send slot update with higher conn id. + mgr.handle_slot_update_receive( SlotUpdate { slot_number: SlotNumber::from(1), commit_id: CommitId::from(0), @@ -1062,16 +1058,16 @@ mod tests { && receiver_unvalidated_1 == UnvalidatedArtifactMutation::Remove(0)) ); - // Check that assemble task for first advert closes. + // Check that assemble task for first slot closes. assert_eq!(result.1, 0); } - /// Verify that if two peers advertise the same advert it will get added to the same assemble task. + /// Verify that if two peers send the same slot content it will get added to the same assemble task. #[tokio::test] - async fn two_peers_advertise_same_advert() { + async fn two_peers_send_same_slot_content() { let (mut mgr, _channels) = ReceiverManagerBuilder::new().build(); let cancellation = CancellationToken::new(); - mgr.handle_advert_receive( + mgr.handle_slot_update_receive( SlotUpdate { slot_number: SlotNumber::from(1), commit_id: CommitId::from(1), @@ -1081,8 +1077,8 @@ mod tests { ConnId::from(1), cancellation.clone(), ); - // Second advert for advert 0. - mgr.handle_advert_receive( + // Second slot update for slot 1. + mgr.handle_slot_update_receive( SlotUpdate { slot_number: SlotNumber::from(1), commit_id: CommitId::from(1), @@ -1101,13 +1097,18 @@ mod tests { /// Verify that a new assemble task is started if we receive a new update for an already finished assemble. #[tokio::test] - async fn new_advert_while_assemble_finished() { + async fn new_slot_update_while_assemble_finished() { fn make_artifact_assembler() -> MockArtifactAssembler { let mut artifact_assembler = MockArtifactAssembler::default(); artifact_assembler .expect_assemble_message() .returning(|id, _, _: PeerWatcher| { - Box::pin(async move { Ok((U64Artifact::id_to_msg(id, 100), NODE_1)) }) + Box::pin(async move { + AssembleResult::Done { + message: U64Artifact::id_to_msg(id, 100), + peer_id: NODE_1, + } + }) }); artifact_assembler } @@ -1115,7 +1116,7 @@ mod tests { .with_artifact_assembler_maker(make_artifact_assembler) .build(); let cancellation = CancellationToken::new(); - mgr.handle_advert_receive( + mgr.handle_slot_update_receive( SlotUpdate { slot_number: SlotNumber::from(1), commit_id: CommitId::from(1), @@ -1125,8 +1126,8 @@ mod tests { ConnId::from(1), cancellation.clone(), ); - // Overwrite advert to close the assemble task. - mgr.handle_advert_receive( + // Overwrite slot content to close the assemble task. + mgr.handle_slot_update_receive( SlotUpdate { slot_number: SlotNumber::from(1), commit_id: CommitId::from(2), @@ -1143,8 +1144,8 @@ mod tests { .await .unwrap() .unwrap(); - // Simulate that a new peer was added for this advert while closing. - mgr.handle_advert_receive( + // Simulate that a new peer was added for this slot while closing. + mgr.handle_slot_update_receive( SlotUpdate { slot_number: SlotNumber::from(1), commit_id: CommitId::from(3), @@ -1155,7 +1156,7 @@ mod tests { cancellation.clone(), ); assert_eq!(mgr.active_assembles.len(), 2); - // Verify that we reopened the assemble task for advert 0. + // Verify that we reopened the assemble task for artifact ID 0. mgr.handle_artifact_processor_joined(peer_rx, id, cancellation.clone()); assert_eq!(mgr.active_assembles.len(), 2); } @@ -1168,7 +1169,7 @@ mod tests { .with_topology_watcher(pfn_rx) .build(); let cancellation = CancellationToken::new(); - mgr.handle_advert_receive( + mgr.handle_slot_update_receive( SlotUpdate { slot_number: SlotNumber::from(1), commit_id: CommitId::from(1), @@ -1178,7 +1179,7 @@ mod tests { ConnId::from(1), cancellation.clone(), ); - mgr.handle_advert_receive( + mgr.handle_slot_update_receive( SlotUpdate { slot_number: SlotNumber::from(1), commit_id: CommitId::from(1), @@ -1237,7 +1238,12 @@ mod tests { artifact_assembler .expect_assemble_message() .returning(|id, _, _: PeerWatcher| { - Box::pin(async move { Ok((U64Artifact::id_to_msg(id, 100), NODE_1)) }) + Box::pin(async move { + AssembleResult::Done { + message: U64Artifact::id_to_msg(id, 100), + peer_id: NODE_1, + } + }) }); artifact_assembler } @@ -1246,7 +1252,7 @@ mod tests { .with_topology_watcher(pfn_rx) .build(); let cancellation = CancellationToken::new(); - mgr.handle_advert_receive( + mgr.handle_slot_update_receive( SlotUpdate { slot_number: SlotNumber::from(1), commit_id: CommitId::from(1), @@ -1280,13 +1286,18 @@ mod tests { #[tokio::test] /// Advertise same id on different slots and overwrite both slots with new ids. - async fn duplicate_advert_on_different_slots() { + async fn duplicate_ids_on_different_slots() { fn make_artifact_assembler() -> MockArtifactAssembler { let mut artifact_assembler = MockArtifactAssembler::default(); artifact_assembler .expect_assemble_message() .returning(|id, _, _: PeerWatcher| { - Box::pin(async move { Ok((U64Artifact::id_to_msg(id, 100), NODE_1)) }) + Box::pin(async move { + AssembleResult::Done { + message: U64Artifact::id_to_msg(id, 100), + peer_id: NODE_1, + } + }) }); artifact_assembler } @@ -1295,7 +1306,7 @@ mod tests { .build(); let cancellation: CancellationToken = CancellationToken::new(); // Add id 0 on slot 1. - mgr.handle_advert_receive( + mgr.handle_slot_update_receive( SlotUpdate { slot_number: SlotNumber::from(1), commit_id: CommitId::from(1), @@ -1306,7 +1317,7 @@ mod tests { cancellation.clone(), ); // Add id 0 on slot 2. - mgr.handle_advert_receive( + mgr.handle_slot_update_receive( SlotUpdate { slot_number: SlotNumber::from(2), commit_id: CommitId::from(2), @@ -1317,7 +1328,7 @@ mod tests { cancellation.clone(), ); // Overwrite id 0 on slot 1. - mgr.handle_advert_receive( + mgr.handle_slot_update_receive( SlotUpdate { slot_number: SlotNumber::from(1), commit_id: CommitId::from(3), @@ -1338,7 +1349,7 @@ mod tests { assert_eq!(mgr.artifact_processor_tasks.len(), 2); // Overwrite remaining id 0 at slot 2. - mgr.handle_advert_receive( + mgr.handle_slot_update_receive( SlotUpdate { slot_number: SlotNumber::from(2), commit_id: CommitId::from(4), @@ -1371,7 +1382,12 @@ mod tests { artifact_assembler .expect_assemble_message() .returning(|id, _, _: PeerWatcher| { - Box::pin(async move { Ok((U64Artifact::id_to_msg(id, 100), NODE_1)) }) + Box::pin(async move { + AssembleResult::Done { + message: U64Artifact::id_to_msg(id, 100), + peer_id: NODE_1, + } + }) }); artifact_assembler } @@ -1379,7 +1395,7 @@ mod tests { .with_artifact_assembler_maker(make_artifact_assembler) .build(); let cancellation: CancellationToken = CancellationToken::new(); - mgr.handle_advert_receive( + mgr.handle_slot_update_receive( SlotUpdate { slot_number: SlotNumber::from(1), commit_id: CommitId::from(1), @@ -1389,7 +1405,7 @@ mod tests { ConnId::from(1), cancellation.clone(), ); - mgr.handle_advert_receive( + mgr.handle_slot_update_receive( SlotUpdate { slot_number: SlotNumber::from(2), commit_id: CommitId::from(2), @@ -1410,7 +1426,7 @@ mod tests { .unwrap_err(); // Overwrite id 1 with id 0. - mgr.handle_advert_receive( + mgr.handle_slot_update_receive( SlotUpdate { slot_number: SlotNumber::from(2), commit_id: CommitId::from(3), @@ -1440,7 +1456,12 @@ mod tests { artifact_assembler .expect_assemble_message() .returning(|id, _, _: PeerWatcher| { - Box::pin(async move { Ok((U64Artifact::id_to_msg(id, 100), NODE_1)) }) + Box::pin(async move { + AssembleResult::Done { + message: U64Artifact::id_to_msg(id, 100), + peer_id: NODE_1, + } + }) }); artifact_assembler } @@ -1448,7 +1469,7 @@ mod tests { .with_artifact_assembler_maker(make_artifact_assembler) .build(); let cancellation: CancellationToken = CancellationToken::new(); - mgr.handle_advert_receive( + mgr.handle_slot_update_receive( SlotUpdate { slot_number: SlotNumber::from(1), commit_id: CommitId::from(1), @@ -1471,7 +1492,7 @@ mod tests { } ); // Advertise id 0 again on same slot. - mgr.handle_advert_receive( + mgr.handle_slot_update_receive( SlotUpdate { slot_number: SlotNumber::from(1), commit_id: CommitId::from(2), @@ -1510,7 +1531,7 @@ mod tests { assert_eq!(mgr.artifact_processor_tasks.len(), 1); // Overwrite id 0. - mgr.handle_advert_receive( + mgr.handle_slot_update_receive( SlotUpdate { slot_number: SlotNumber::from(1), commit_id: CommitId::from(3), diff --git a/rs/p2p/consensus_manager/src/sender.rs b/rs/p2p/consensus_manager/src/sender.rs index bf6513fb811..fb6b417fbe7 100644 --- a/rs/p2p/consensus_manager/src/sender.rs +++ b/rs/p2p/consensus_manager/src/sender.rs @@ -109,14 +109,14 @@ impl< _ = cancellation_token.cancelled() => { error!( self.log, - "Sender event loop for the P2P client `{:?}` terminated. No more adverts will be sent for this client.", + "Sender event loop for the P2P client `{:?}` terminated. No more transmits will be sent for this client.", uri_prefix::() ); break; } - Some(advert) = self.outbound_transmits.recv() => { - match advert { - ArtifactTransmit::Deliver(new_artifact) => self.handle_deliver_transmit(new_artifact, cancellation_token.clone()), + Some(outbound_transmit) = self.outbound_transmits.recv() => { + match outbound_transmit { + ArtifactTransmit::Deliver(artifact) => self.handle_deliver_transmit(artifact, cancellation_token.clone()), ArtifactTransmit::Abort(id) => self.handle_abort_transmit(&id), } @@ -407,6 +407,7 @@ mod available_slot_set { mod tests { use anyhow::anyhow; use axum::http::Response; + use ic_interfaces::p2p::consensus::{AssembleResult, Peers}; use ic_logger::replica_logger::no_op_logger; use ic_metrics::MetricsRegistry; use ic_p2p_test_utils::{consensus::U64Artifact, mocks::MockTransport}; @@ -415,8 +416,6 @@ mod tests { use mockall::Sequence; use tokio::{runtime::Handle, time::timeout}; - use ic_interfaces::p2p::consensus::{Aborted, Peers}; - use super::*; #[derive(Clone)] @@ -431,7 +430,7 @@ mod tests { _id: ::Id, _artifact: Option<(U64Artifact, NodeId)>, _peers: P, - ) -> Result<(U64Artifact, NodeId), Aborted> { + ) -> AssembleResult { todo!() } } diff --git a/rs/p2p/consensus_manager/tests/test.rs b/rs/p2p/consensus_manager/tests/test.rs index 056d9d43ec3..6b4639abb61 100644 --- a/rs/p2p/consensus_manager/tests/test.rs +++ b/rs/p2p/consensus_manager/tests/test.rs @@ -345,17 +345,18 @@ fn load_test( for i in 0..num_peers { let node = node_test_id(i); let processor = TestConsensus::new(log.clone(), node, 256 * (i as usize + 1), i % 2 == 0); - let (jh, mut cm) = + let (jh, cm) = start_consensus_manager(no_op_logger(), rt.handle().clone(), processor.clone()); jhs.push(jh); - nodes.push((node, cm.router())); + let r = cm.router(); + nodes.push((node, r)); cms.push((node, cm)); node_advert_map.insert(node, processor); } let (nodes, topology_watcher) = fully_connected_localhost_subnet(rt.handle(), log, id, nodes); for ((node1, transport), (node2, cm)) in nodes.into_iter().zip(cms.into_iter()) { assert!(node1 == node2); - cm.run(transport, topology_watcher.clone()); + cm.start(transport, topology_watcher.clone()); } rt.block_on(async move { diff --git a/rs/p2p/state_sync_manager/src/lib.rs b/rs/p2p/state_sync_manager/src/lib.rs index cca66a54dfe..94dd80319d5 100644 --- a/rs/p2p/state_sync_manager/src/lib.rs +++ b/rs/p2p/state_sync_manager/src/lib.rs @@ -47,25 +47,23 @@ const ADVERT_BROADCAST_INTERVAL: Duration = Duration::from_secs(5); const ADVERT_BROADCAST_TIMEOUT: Duration = ADVERT_BROADCAST_INTERVAL.saturating_sub(Duration::from_secs(2)); -pub fn build_axum_router( - state_sync: Arc>, - log: ReplicaLogger, +pub fn build_state_sync_manager( + log: &ReplicaLogger, metrics_registry: &MetricsRegistry, -) -> ( - Router, - tokio::sync::mpsc::Receiver<(StateSyncArtifactId, NodeId)>, -) { + rt_handle: &tokio::runtime::Handle, + state_sync: Arc>, +) -> (Router, StateSyncManager) { let metrics = StateSyncManagerHandlerMetrics::new(metrics_registry); let shared_chunk_state = Arc::new(StateSyncChunkHandler::new( log.clone(), - state_sync, + state_sync.clone(), metrics.clone(), )); - let (tx, rx) = tokio::sync::mpsc::channel(20); - let advert_handler_state = Arc::new(StateSyncAdvertHandler::new(log, tx)); + let (advert_sender, advert_receiver) = tokio::sync::mpsc::channel(20); + let advert_handler_state = Arc::new(StateSyncAdvertHandler::new(log.clone(), advert_sender)); - let app = Router::new() + let router = Router::new() .route(STATE_SYNC_CHUNK_PATH, any(state_sync_chunk_handler)) .with_state(shared_chunk_state) .route( @@ -74,45 +72,37 @@ pub fn build_axum_router( ) .with_state(advert_handler_state); - (app, rx) -} - -pub fn start_state_sync_manager( - log: &ReplicaLogger, - metrics: &MetricsRegistry, - rt: &Handle, - transport: Arc, - state_sync: Arc>, - advert_receiver: tokio::sync::mpsc::Receiver<(StateSyncArtifactId, NodeId)>, -) -> Shutdown { - let state_sync_manager_metrics = StateSyncManagerMetrics::new(metrics); + let state_sync_manager_metrics = StateSyncManagerMetrics::new(metrics_registry); let manager = StateSyncManager { log: log.clone(), - rt: rt.clone(), + rt: rt_handle.clone(), metrics: state_sync_manager_metrics, - transport, state_sync, advert_receiver, ongoing_state_sync: None, }; - Shutdown::spawn_on_with_cancellation( - |cancellation: CancellationToken| manager.run(cancellation), - rt, - ) + (router, manager) } -struct StateSyncManager { +pub struct StateSyncManager { log: ReplicaLogger, rt: Handle, metrics: StateSyncManagerMetrics, - transport: Arc, state_sync: Arc>, advert_receiver: tokio::sync::mpsc::Receiver<(StateSyncArtifactId, NodeId)>, ongoing_state_sync: Option, } impl StateSyncManager { - async fn run(mut self, cancellation: CancellationToken) { + pub fn start(self, transport: Arc) -> Shutdown { + let rt_handle = self.rt.clone(); + Shutdown::spawn_on_with_cancellation( + |cancellation: CancellationToken| self.run(cancellation, transport), + &rt_handle, + ) + } + + async fn run(mut self, cancellation: CancellationToken, transport: Arc) { let mut interval = tokio::time::interval(ADVERT_BROADCAST_INTERVAL); interval.set_missed_tick_behavior(MissedTickBehavior::Skip); let mut advertise_task = JoinSet::new(); @@ -127,7 +117,7 @@ impl StateSyncManager { Self::send_state_adverts( self.rt.clone(), self.state_sync.clone(), - self.transport.clone(), + transport.clone(), self.metrics.clone(), cancellation.clone(), ), @@ -135,7 +125,7 @@ impl StateSyncManager { ); }, Some((advert, peer_id)) = self.advert_receiver.recv() =>{ - self.handle_advert(advert, peer_id).await; + self.handle_advert(advert, peer_id, transport.clone()).await; } Some(_) = advertise_task.join_next() => {} } @@ -146,7 +136,12 @@ impl StateSyncManager { } } - async fn handle_advert(&mut self, artifact_id: StateSyncArtifactId, peer_id: NodeId) { + async fn handle_advert( + &mut self, + artifact_id: StateSyncArtifactId, + peer_id: NodeId, + transport: Arc, + ) { self.metrics.adverts_received_total.inc(); // Remove ongoing state sync if finished or try to add peer if ongoing. if let Some(ongoing) = &mut self.ongoing_state_sync { @@ -184,7 +179,7 @@ impl StateSyncManager { self.metrics.ongoing_state_sync_metrics.clone(), Arc::new(Mutex::new(chunkable)), artifact_id.clone(), - self.transport.clone(), + transport, ); // Add peer that initiated this state sync to ongoing state sync. ongoing @@ -330,14 +325,17 @@ mod tests { }; let (handler_tx, handler_rx) = tokio::sync::mpsc::channel(100); - start_state_sync_manager( - &log, - &MetricsRegistry::default(), - rt.handle(), - Arc::new(t) as Arc<_>, - Arc::new(s) as Arc<_>, - handler_rx, - ); + let metrics = StateSyncManagerMetrics::new(&MetricsRegistry::default()); + + let manager = StateSyncManager { + log: log.clone(), + advert_receiver: handler_rx, + ongoing_state_sync: None, + metrics, + state_sync: Arc::new(s) as Arc<_>, + rt: rt.handle().clone(), + }; + let _ = manager.start(Arc::new(t) as Arc<_>); rt.block_on(async move { handler_tx.send((id, NODE_1)).await.unwrap(); handler_tx.send((old_id, NODE_2)).await.unwrap(); diff --git a/rs/p2p/state_sync_manager/tests/common.rs b/rs/p2p/state_sync_manager/tests/common.rs index 3f6fe8f9f00..4d211ef8241 100644 --- a/rs/p2p/state_sync_manager/tests/common.rs +++ b/rs/p2p/state_sync_manager/tests/common.rs @@ -418,10 +418,11 @@ pub fn create_node( disconnected: Arc::new(AtomicBool::new(false)), }); - let (router, rx) = ic_state_sync_manager::build_axum_router( - state_sync.clone(), - log.clone(), + let (router, manager) = ic_state_sync_manager::build_state_sync_manager( + &log, &MetricsRegistry::default(), + rt, + state_sync.clone(), ); let transport = transport_router.add_peer( NodeId::from(PrincipalId::new_node_test_id(node_num)), @@ -429,14 +430,6 @@ pub fn create_node( link.0, link.1, ); - let shutdown = ic_state_sync_manager::start_state_sync_manager( - &log, - &MetricsRegistry::default(), - rt, - Arc::new(transport), - state_sync.clone(), - rx, - ); - + let shutdown = manager.start(Arc::new(transport)); (state_sync, shutdown) } diff --git a/rs/p2p/test_utils/BUILD.bazel b/rs/p2p/test_utils/BUILD.bazel index 4263af5951b..68abf6b3f56 100644 --- a/rs/p2p/test_utils/BUILD.bazel +++ b/rs/p2p/test_utils/BUILD.bazel @@ -45,6 +45,7 @@ DEPENDENCIES = [ "@crate_index//:slog", "@crate_index//:tempfile", "@crate_index//:tokio", + "@crate_index//:tokio-stream", "@crate_index//:turmoil", ] diff --git a/rs/p2p/test_utils/Cargo.toml b/rs/p2p/test_utils/Cargo.toml index 529c36d4b9b..8b3c295e8f0 100644 --- a/rs/p2p/test_utils/Cargo.toml +++ b/rs/p2p/test_utils/Cargo.toml @@ -47,4 +47,5 @@ serde = { workspace = true } slog = { workspace = true } tempfile = { workspace = true } tokio = { workspace = true } +tokio-stream = { workspace = true } turmoil = { workspace = true } diff --git a/rs/p2p/test_utils/src/lib.rs b/rs/p2p/test_utils/src/lib.rs index 7d749c41c3d..2e41b6a6394 100644 --- a/rs/p2p/test_utils/src/lib.rs +++ b/rs/p2p/test_utils/src/lib.rs @@ -7,6 +7,7 @@ use futures::{ }; use ic_artifact_downloader::FetchArtifact; use ic_base_types::{NodeId, PrincipalId, RegistryVersion, SubnetId}; +use ic_consensus_manager::AbortableBroadcastChannel; use ic_crypto_temp_crypto::{NodeKeysToGenerate, TempCryptoComponent}; use ic_crypto_tls_interfaces::TlsConfig; use ic_interfaces::p2p::artifact_manager::JoinGuard; @@ -449,30 +450,34 @@ pub fn start_consensus_manager( processor: TestConsensus, ) -> ( Box, - ic_consensus_manager::ConsensusManagerBuilder, + ic_consensus_manager::AbortableBroadcastChannelBuilder, ) { let _enter = rt_handle.enter(); let pool = Arc::new(RwLock::new(processor)); - let (artifact_processor_jh, artifact_manager_event_rx, artifact_sender) = - start_test_processor(pool.clone(), pool.clone().read().unwrap().clone()); let bouncer_factory = Arc::new(pool.clone().read().unwrap().clone()); - let mut cm1 = ic_consensus_manager::ConsensusManagerBuilder::new( + let downloader = FetchArtifact::new( log.clone(), rt_handle.clone(), + pool.clone(), + bouncer_factory, MetricsRegistry::default(), ); - let downloader = FetchArtifact::new( - log, - rt_handle, - pool, - bouncer_factory, + + let mut cm1 = ic_consensus_manager::AbortableBroadcastChannelBuilder::new( + log.clone(), + rt_handle.clone(), MetricsRegistry::default(), ); - cm1.add_client( - artifact_manager_event_rx, - artifact_sender, - downloader, - usize::MAX, + let AbortableBroadcastChannel { + outbound_tx, + inbound_rx, + } = cm1.abortable_broadcast_channel(downloader, usize::MAX); + + let artifact_processor_jh = start_test_processor( + outbound_tx, + inbound_rx, + pool.clone(), + pool.clone().read().unwrap().clone(), ); (artifact_processor_jh, cm1) } diff --git a/rs/p2p/test_utils/src/mocks.rs b/rs/p2p/test_utils/src/mocks.rs index 42eff3856d6..6cc924e8243 100644 --- a/rs/p2p/test_utils/src/mocks.rs +++ b/rs/p2p/test_utils/src/mocks.rs @@ -2,7 +2,9 @@ use async_trait::async_trait; use axum::http::{Request, Response}; use bytes::Bytes; use ic_interfaces::p2p::{ - consensus::{Aborted, ArtifactAssembler, Bouncer, BouncerFactory, Peers, ValidatedPoolReader}, + consensus::{ + ArtifactAssembler, AssembleResult, Bouncer, BouncerFactory, Peers, ValidatedPoolReader, + }, state_sync::{AddChunkError, Chunk, ChunkId, Chunkable, StateSyncArtifactId, StateSyncClient}, }; use ic_quic_transport::{ConnId, Transport}; @@ -101,6 +103,6 @@ mock! { id: u64, artifact: Option<(U64Artifact, NodeId)>, peers: P, - ) -> impl std::future::Future> + Send; + ) -> impl std::future::Future> + Send; } } diff --git a/rs/p2p/test_utils/src/turmoil.rs b/rs/p2p/test_utils/src/turmoil.rs index 3096d4453ab..0621ec2e483 100644 --- a/rs/p2p/test_utils/src/turmoil.rs +++ b/rs/p2p/test_utils/src/turmoil.rs @@ -19,6 +19,7 @@ use bytes::BytesMut; use futures::{future::BoxFuture, FutureExt}; use ic_artifact_downloader::FetchArtifact; use ic_artifact_manager::run_artifact_processor; +use ic_consensus_manager::AbortableBroadcastChannel; use ic_crypto_tls_interfaces::TlsConfig; use ic_interfaces::{ p2p::artifact_manager::JoinGuard, p2p::consensus::ArtifactTransmit, @@ -35,6 +36,7 @@ use tokio::{ select, sync::{mpsc, oneshot, watch, Notify}, }; +use tokio_stream::wrappers::ReceiverStream; use turmoil::Sim; pub struct CustomUdp { @@ -346,7 +348,7 @@ pub fn add_transport_to_sim( async move { let metrics_registry = MetricsRegistry::default(); - let mut consensus_builder = ic_consensus_manager::ConsensusManagerBuilder::new( + let mut consensus_builder = ic_consensus_manager::AbortableBroadcastChannelBuilder::new( log.clone(), tokio::runtime::Handle::current(), metrics_registry, @@ -357,42 +359,45 @@ pub fn add_transport_to_sim( let this_ip = turmoil::lookup(peer.to_string()); let custom_udp = CustomUdp::new(this_ip, udp_listener); - let state_sync_rx = if let Some(ref state_sync) = state_sync_client_clone { - let (state_sync_router, state_sync_rx) = ic_state_sync_manager::build_axum_router( - state_sync.clone(), - log.clone(), - &MetricsRegistry::default(), - ); + let state_sync_manager = if let Some(ref state_sync) = state_sync_client_clone { + let (state_sync_router, state_sync_manager) = + ic_state_sync_manager::build_state_sync_manager( + &log, + &MetricsRegistry::default(), + &tokio::runtime::Handle::current(), + state_sync.clone(), + ); router = Some(router.unwrap_or_default().merge(state_sync_router)); - Some(state_sync_rx) + Some(state_sync_manager) } else { None }; - let _artifact_processor_jh = if let Some(consensus) = consensus_manager_clone { - let (artifact_processor_jh, artifact_manager_event_rx, artifact_sender) = - start_test_processor( - consensus.clone(), - consensus.clone().read().unwrap().clone(), - ); + let con = if let Some(consensus) = consensus_manager_clone { let bouncer_factory = Arc::new(consensus.clone().read().unwrap().clone()); - let downloader = FetchArtifact::new( log.clone(), tokio::runtime::Handle::current(), - consensus, + consensus.clone(), bouncer_factory, MetricsRegistry::default(), ); - consensus_builder.add_client( - artifact_manager_event_rx, - artifact_sender, - downloader, - usize::MAX, + let AbortableBroadcastChannel { + outbound_tx, + inbound_rx, + } = consensus_builder.abortable_broadcast_channel(downloader, usize::MAX); + + let artifact_processor_jh = start_test_processor( + outbound_tx, + inbound_rx, + consensus.clone(), + consensus.clone().read().unwrap().clone(), ); - router = Some(router.unwrap_or_default().merge(consensus_builder.router())); - Some(artifact_processor_jh) + let consensus_router = consensus_builder.router(); + router = Some(router.unwrap_or_default().merge(consensus_router)); + + Some((artifact_processor_jh, consensus_builder)) } else { None }; @@ -409,17 +414,12 @@ pub fn add_transport_to_sim( router.unwrap_or_default(), )); - consensus_builder.run(transport.clone(), topology_watcher_clone.clone()); + if let Some((_, con_manager)) = con { + con_manager.start(transport.clone(), topology_watcher_clone.clone()); + } - if let Some(state_sync_rx) = state_sync_rx { - ic_state_sync_manager::start_state_sync_manager( - &log, - &MetricsRegistry::default(), - &tokio::runtime::Handle::current(), - transport.clone(), - state_sync_client_clone.unwrap().clone(), - state_sync_rx, - ); + if let Some(state_sync_manager) = state_sync_manager { + state_sync_manager.start(transport.clone()); } post_setup_future_clone(peer, transport).await; @@ -442,22 +442,19 @@ pub fn waiter_fut( #[allow(clippy::type_complexity)] pub fn start_test_processor( + outbound_tx: mpsc::Sender>, + inbound_rx: mpsc::Receiver>, pool: Arc>>, change_set_producer: TestConsensus, -) -> ( - Box, - mpsc::Receiver>, - mpsc::UnboundedSender>, -) { - let (tx, rx) = tokio::sync::mpsc::channel(1000); +) -> Box { let time_source = Arc::new(SysTimeSource::new()); let client = ic_artifact_manager::Processor::new(pool, change_set_producer); - let (jh, sender) = run_artifact_processor( + run_artifact_processor::>>( time_source, MetricsRegistry::default(), Box::new(client), - tx, + outbound_tx, + inbound_rx.into(), vec![], - ); - (jh, rx, sender) + ) } diff --git a/rs/phantom_newtype/src/displayer.rs b/rs/phantom_newtype/src/displayer.rs index 1c8ed820e4b..aca27709e9e 100644 --- a/rs/phantom_newtype/src/displayer.rs +++ b/rs/phantom_newtype/src/displayer.rs @@ -34,7 +34,7 @@ where } } -impl<'a, T, Displayer> fmt::Display for DisplayProxy<'a, T, Displayer> +impl fmt::Display for DisplayProxy<'_, T, Displayer> where Displayer: DisplayerOf, { diff --git a/rs/pocket_ic_server/BUILD.bazel b/rs/pocket_ic_server/BUILD.bazel index d0f74e8c21d..112ffb34b72 100644 --- a/rs/pocket_ic_server/BUILD.bazel +++ b/rs/pocket_ic_server/BUILD.bazel @@ -141,7 +141,10 @@ rust_binary( proc_macro_deps = MACRO_DEPENDENCIES, # TODO: restrict the visibility visibility = ["//visibility:public"], - deps = LIB_DEPENDENCIES + [":pocket-ic-server-lib"], + deps = LIB_DEPENDENCIES + [ + ":pocket-ic-server-lib", + "//rs/canister_sandbox:build_script", + ], ) rust_library( diff --git a/rs/pocket_ic_server/CHANGELOG.md b/rs/pocket_ic_server/CHANGELOG.md index a6826641d97..1bc8140d114 100644 --- a/rs/pocket_ic_server/CHANGELOG.md +++ b/rs/pocket_ic_server/CHANGELOG.md @@ -13,6 +13,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - New endpoint `/instances//read/ingress_status` to fetch the status of an update call submitted through an ingress message. + If an optional caller is provided, the status of the update call is known, but the update call was submitted by a different caller, then an error is returned. +- New endpoint `/instances//update/set_certified_time` to set the current certified time on all subnets of the PocketIC instance. + +### Fixed +- Canisters created via `provisional_create_canister_with_cycles` with the management canister ID as the effective canister ID + are created on an arbitrary subnet. + +### Changed +- The response type `RawSubmitIngressResult` is replaced by `Result`. +- The response types `RawWasmResult` and `UserError` in `RawCanisterResult` are replaced by `Vec` and `RejectResponse`. + +### Removed +- The endpoint `/instances//update/execute_ingress_message`: + use the two endpoints `/instances//update/submit_ingress_message` and `/instances//update/await_ingress_message` + to submit and then await an ingress message instead. diff --git a/rs/pocket_ic_server/Cargo.toml b/rs/pocket_ic_server/Cargo.toml index 33adf717663..8ed2df90b47 100644 --- a/rs/pocket_ic_server/Cargo.toml +++ b/rs/pocket_ic_server/Cargo.toml @@ -12,7 +12,7 @@ axum-extra = { version = "^0.9", features = ["typed-header"] } axum-server = { version = "0.6.0", features = ["tls-rustls"] } backoff = { workspace = true } base64 = { workspace = true } -bitcoin = { version = "0.28.1", features = ["default", "use-serde", "rand"] } +bitcoin = { workspace = true } bytes = { workspace = true } candid = { workspace = true } clap = { workspace = true } @@ -38,7 +38,7 @@ ic-crypto-iccsa = { path = "../crypto/iccsa" } ic-crypto-sha2 = { path = "../crypto/sha2" } ic-crypto-utils-threshold-sig-der = { path = "../crypto/utils/threshold_sig_der" } ic-error-types = { path = "../types/error_types" } -ic-http-gateway = { git = "https://github.com/dfinity/http-gateway", tag = "0.1.0-b0" } +ic-http-gateway = { workspace = true } ic-http-endpoints-public = { path = "../http_endpoints/public" } ic-https-outcalls-adapter = { path = "../https_outcalls/adapter" } ic-https-outcalls-adapter-client = { path = "../https_outcalls/client" } @@ -84,7 +84,7 @@ tracing-subscriber = { workspace = true } wat = { workspace = true } [dev-dependencies] -bitcoincore-rpc = "0.15.0" +bitcoincore-rpc = { workspace = true } ic-btc-interface = { workspace = true } ic-config = { path = "../config" } ic-registry-transport = { path = "../registry/transport" } diff --git a/rs/pocket_ic_server/src/main.rs b/rs/pocket_ic_server/src/main.rs index 9911af56018..adbc07586c3 100644 --- a/rs/pocket_ic_server/src/main.rs +++ b/rs/pocket_ic_server/src/main.rs @@ -78,6 +78,11 @@ fn current_binary_path() -> Option { std::env::args().next().map(PathBuf::from) } +#[cfg(all(target_os = "linux", target_arch = "x86_64"))] +extern "C" { + fn install_backtrace_handler(); +} + fn main() { let current_binary_path = current_binary_path().unwrap(); let current_binary_name = current_binary_path.file_name().unwrap().to_str().unwrap(); @@ -89,6 +94,10 @@ fn main() { // before the arguments are parsed because the parent process does not pass // all the normally required arguments of `pocket-ic-server`. if std::env::args().any(|arg| arg == RUN_AS_CANISTER_SANDBOX_FLAG) { + #[cfg(all(target_os = "linux", target_arch = "x86_64"))] + unsafe { + install_backtrace_handler(); + } canister_sandbox_main(); } else if std::env::args().any(|arg| arg == RUN_AS_SANDBOX_LAUNCHER_FLAG) { sandbox_launcher_main(); diff --git a/rs/pocket_ic_server/src/pocket_ic.rs b/rs/pocket_ic_server/src/pocket_ic.rs index 19f1d5b5f27..699479acbed 100644 --- a/rs/pocket_ic_server/src/pocket_ic.rs +++ b/rs/pocket_ic_server/src/pocket_ic.rs @@ -22,7 +22,6 @@ use ic_config::{ subnet_config::SubnetConfig, }; use ic_crypto_sha2::Sha256; -use ic_error_types::RejectCode; use ic_http_endpoints_public::{ call_v2, call_v3, metrics::HttpHandlerMetrics, CanisterReadStateServiceBuilder, IngressValidatorBuilder, QueryServiceBuilder, SubnetReadStateServiceBuilder, @@ -80,6 +79,7 @@ use pocket_ic::common::rest::{ RawCanisterCall, RawCanisterId, RawEffectivePrincipal, RawMessageId, RawSetStableMemory, SubnetInstructionConfig, SubnetKind, SubnetSpec, Topology, }; +use pocket_ic::{ErrorCode, RejectCode, RejectResponse}; use serde::{Deserialize, Serialize}; use slog::Level; use std::hash::Hash; @@ -122,6 +122,33 @@ pub(crate) type ApiResponse = BoxFuture<'static, (u16, BTreeMap> /// Used for generating canister ID ranges that do not appear on mainnet. pub const MAXIMUM_NUMBER_OF_SUBNETS_ON_MAINNET: u64 = 1024; +fn wasm_result_to_canister_result( + res: ic_state_machine_tests::WasmResult, + certified: bool, +) -> Result, RejectResponse> { + match res { + ic_state_machine_tests::WasmResult::Reply(data) => Ok(data), + ic_state_machine_tests::WasmResult::Reject(reject_message) => Err(RejectResponse { + reject_code: RejectCode::CanisterReject, + reject_message, + error_code: ErrorCode::CanisterRejectedMessage, + certified, + }), + } +} + +fn user_error_to_reject_response( + err: ic_error_types::UserError, + certified: bool, +) -> RejectResponse { + RejectResponse { + reject_code: RejectCode::try_from(err.reject_code() as u64).unwrap(), + reject_message: err.description().to_string(), + error_code: ErrorCode::try_from(err.code() as u64).unwrap(), + certified, + } +} + async fn into_api_response(resp: AxumResponse) -> (u16, BTreeMap>, Vec) { ( resp.status().into(), @@ -384,6 +411,9 @@ impl SubnetsImpl { pub(crate) fn get_all(&self) -> Vec> { self.subnets.read().unwrap().values().cloned().collect() } + fn clear(&self) { + self.subnets.write().unwrap().clear(); + } } impl Subnets for SubnetsImpl { @@ -421,8 +451,8 @@ pub struct PocketIc { impl Drop for PocketIc { fn drop(&mut self) { - let subnets = self.subnets.get_all(); if let Some(ref state_dir) = self.state_dir { + let subnets = self.subnets.get_all(); for subnet in &subnets { subnet.state_machine.checkpointed_tick(); } @@ -452,9 +482,36 @@ impl Drop for PocketIc { let topology_json = serde_json::to_string(&raw_topology).unwrap(); topology_file.write_all(topology_json.as_bytes()).unwrap(); } - for subnet in subnets { + for subnet in self.subnets.get_all() { subnet.state_machine.drop_payload_builder(); } + let state_machines: Vec<_> = self + .subnets + .get_all() + .into_iter() + .map(|subnet| subnet.state_machine.clone()) + .collect(); + self.subnets.clear(); + // for every StateMachine, wait until nobody else has an Arc to that StateMachine + // and then drop that StateMachine + let start = std::time::Instant::now(); + for state_machine in state_machines { + let mut state_machine = Some(state_machine); + while state_machine.is_some() { + match Arc::try_unwrap(state_machine.take().unwrap()) { + Ok(sm) => { + sm.drop(); + break; + } + Err(sm) => { + state_machine = Some(sm); + } + } + if start.elapsed() > std::time::Duration::from_secs(5 * 60) { + panic!("Timed out while dropping PocketIC."); + } + } + } } } @@ -1109,32 +1166,55 @@ pub struct SetTime { pub time: Time, } -impl Operation for SetTime { - fn compute(&self, pic: &mut PocketIc) -> OpOut { - // Time is kept in sync across subnets, so one can take any subnet. - let current_time: SystemTime = pic.any_subnet().time(); - let set_time: SystemTime = self.time.into(); - match current_time.cmp(&set_time) { - std::cmp::Ordering::Greater => OpOut::Error(PocketIcError::SettingTimeIntoPast(( - systemtime_to_unix_epoch_nanos(current_time), - systemtime_to_unix_epoch_nanos(set_time), - ))), - std::cmp::Ordering::Equal => OpOut::NoOutput, - std::cmp::Ordering::Less => { - // Sets the time on all subnets. - for subnet in pic.subnets.get_all() { +fn set_time(pic: &PocketIc, time: Time, certified: bool) -> OpOut { + // Time is kept in sync across subnets, so one can take any subnet. + let current_time: SystemTime = pic.any_subnet().time(); + let set_time: SystemTime = time.into(); + match current_time.cmp(&set_time) { + std::cmp::Ordering::Greater => OpOut::Error(PocketIcError::SettingTimeIntoPast(( + systemtime_to_unix_epoch_nanos(current_time), + systemtime_to_unix_epoch_nanos(set_time), + ))), + std::cmp::Ordering::Equal => OpOut::NoOutput, + std::cmp::Ordering::Less => { + // Sets the time on all subnets. + for subnet in pic.subnets.get_all() { + if certified { + subnet.state_machine.set_certified_time(set_time); + } else { subnet.state_machine.set_time(set_time); } - OpOut::NoOutput } + OpOut::NoOutput } } +} + +impl Operation for SetTime { + fn compute(&self, pic: &mut PocketIc) -> OpOut { + set_time(pic, self.time, false) + } fn id(&self) -> OpId { OpId(format!("set_time_{}", self.time)) } } +#[derive(Clone, Debug)] +pub struct SetCertifiedTime { + pub time: Time, +} + +impl Operation for SetCertifiedTime { + fn compute(&self, pic: &mut PocketIc) -> OpOut { + set_time(pic, self.time, true) + } + + fn id(&self) -> OpId { + OpId(format!("set_certified_time_{}", self.time)) + } +} + #[derive(Copy, Clone, Debug)] pub struct GetTopology; @@ -1248,6 +1328,7 @@ impl Operation for ProcessCanisterHttpInternal { timeout: context.time + Duration::from_secs(5 * 60), id, context, + socks_proxy_addrs: vec![], }) { canister_http.pending.insert(id); } @@ -1358,7 +1439,7 @@ fn process_mock_canister_https_response( reject_codes.push(reject_code) } for reject_code in reject_codes { - if RejectCode::try_from(reject_code).is_err() { + if ic_error_types::RejectCode::try_from(reject_code).is_err() { return OpOut::Error(PocketIcError::InvalidRejectCode(reject_code)); } } @@ -1415,6 +1496,7 @@ fn process_mock_canister_https_response( timeout, id: canister_http_request_id, context: context.clone(), + socks_proxy_addrs: vec![], }) .unwrap(); let response = loop { @@ -1429,7 +1511,7 @@ fn process_mock_canister_https_response( } CanisterHttpResponse::CanisterHttpReject(reject) => { CanisterHttpResponseContent::Reject(CanisterHttpReject { - reject_code: RejectCode::try_from(reject.reject_code).unwrap(), + reject_code: ic_error_types::RejectCode::try_from(reject.reject_code).unwrap(), message: reject.message.clone(), }) } @@ -1551,7 +1633,7 @@ impl Operation for SubmitIngressMessage { } Err(SubmitIngressError::UserError(e)) => { eprintln!("Failed to submit ingress message: {:?}", e); - Err::(e).into() + OpOut::CanisterResult(Err(user_error_to_reject_response(e, false))) } Ok(msg_id) => OpOut::MessageId(( EffectivePrincipal::SubnetId(subnet.get_subnet_id()), @@ -1614,16 +1696,18 @@ impl Operation for AwaitIngressMessage { IngressStatus::Known { state: IngressState::Completed(result), .. - } => return Ok(result).into(), + } => { + return OpOut::CanisterResult(wasm_result_to_canister_result( + result, true, + )); + } IngressStatus::Known { state: IngressState::Failed(error), .. } => { - return Err::< - ic_state_machine_tests::WasmResult, - ic_state_machine_tests::UserError, - >(error) - .into() + return OpOut::CanisterResult(Err(user_error_to_reject_response( + error, true, + ))); } _ => {} } @@ -1646,94 +1730,49 @@ impl Operation for AwaitIngressMessage { } #[derive(Clone, Debug)] -pub struct ExecuteIngressMessage(pub CanisterCall); +pub struct IngressMessageStatus { + pub message_id: MessageId, + pub caller: Option, +} -impl Operation for ExecuteIngressMessage { +impl Operation for IngressMessageStatus { fn compute(&self, pic: &mut PocketIc) -> OpOut { - let canister_call = self.0.clone(); - let subnet = route_call(pic, canister_call); + let subnet = route(pic, self.message_id.effective_principal.clone(), false); match subnet { Ok(subnet) => { - match subnet.submit_ingress_as( - self.0.sender, - self.0.canister_id, - self.0.method.clone(), - self.0.payload.clone(), - ) { - Err(SubmitIngressError::HttpError(e)) => { - eprintln!("Failed to submit ingress message: {}", e); - OpOut::Error(PocketIcError::BadIngressMessage(e)) - } - Err(SubmitIngressError::UserError(e)) => { - eprintln!("Failed to submit ingress message: {:?}", e); - Err::(e).into() - } - Ok(msg_id) => { - // Now, we execute on all subnets until we have the result - let max_rounds = 100; - for _i in 0..max_rounds { - for subnet_ in pic.subnets.get_all() { - subnet_.state_machine.execute_round(); - } - match subnet.ingress_status(&msg_id) { - IngressStatus::Known { - state: IngressState::Completed(result), - .. - } => return Ok(result).into(), - IngressStatus::Known { - state: IngressState::Failed(error), - .. - } => { - return Err::< - ic_state_machine_tests::WasmResult, - ic_state_machine_tests::UserError, - >(error) - .into() - } - _ => {} - } + if let Some(caller) = self.caller { + if let Some(actual_caller) = subnet.ingress_caller(&self.message_id.msg_id) { + if caller != actual_caller.get().0 { + return OpOut::Error(PocketIcError::Forbidden( + "The user tries to access Request ID not signed by the caller." + .to_string(), + )); } - OpOut::Error(PocketIcError::BadIngressMessage(format!( - "Failed to answer to ingress {} after {} rounds.", - msg_id, max_rounds - ))) } } + match subnet.ingress_status(&self.message_id.msg_id) { + IngressStatus::Known { + state: IngressState::Completed(result), + .. + } => OpOut::CanisterResult(wasm_result_to_canister_result(result, true)), + IngressStatus::Known { + state: IngressState::Failed(error), + .. + } => OpOut::CanisterResult(Err(user_error_to_reject_response(error, true))), + _ => OpOut::NoOutput, + } } Err(e) => OpOut::Error(PocketIcError::BadIngressMessage(e)), } } fn id(&self) -> OpId { - let call_id = self.0.id(); - OpId(format!("canister_update_{}", call_id.0)) - } -} - -#[derive(Clone, Debug)] -pub struct IngressMessageStatus(pub MessageId); - -impl Operation for IngressMessageStatus { - fn compute(&self, pic: &mut PocketIc) -> OpOut { - let subnet = route(pic, self.0.effective_principal.clone(), false); - match subnet { - Ok(subnet) => match subnet.ingress_status(&self.0.msg_id) { - IngressStatus::Known { - state: IngressState::Completed(result), - .. - } => Ok(result).into(), - IngressStatus::Known { - state: IngressState::Failed(error), - .. - } => Err(error).into(), - _ => OpOut::NoOutput, - }, - Err(e) => OpOut::Error(PocketIcError::BadIngressMessage(e)), - } - } - - fn id(&self) -> OpId { - OpId(format!("ingress_status_{}", self.0.msg_id)) + OpId(format!( + "ingress_status({},{:?},{:?})", + self.message_id.msg_id, + self.message_id.effective_principal, + self.caller.map(|caller| caller.to_string()) + )) } } @@ -1746,15 +1785,20 @@ impl Operation for Query { match subnet { Ok(subnet) => { let delegation = pic.get_nns_delegation_for_subnet(subnet.get_subnet_id()); - subnet - .query_as_with_delegation( - self.0.sender, - self.0.canister_id, - self.0.method.clone(), - self.0.payload.clone(), - delegation, - ) - .into() + match subnet.query_as_with_delegation( + self.0.sender, + self.0.canister_id, + self.0.method.clone(), + self.0.payload.clone(), + delegation, + ) { + Ok(result) => { + OpOut::CanisterResult(wasm_result_to_canister_result(result, false)) + } + Err(user_error) => { + OpOut::CanisterResult(Err(user_error_to_reject_response(user_error, false))) + } + } } Err(e) => OpOut::Error(PocketIcError::BadIngressMessage(e)), } @@ -2563,7 +2607,18 @@ fn route( EffectivePrincipal::CanisterId(canister_id) => match pic.try_route_canister(canister_id) { Some(subnet) => Ok(subnet), None => { - if is_provisional_create_canister { + // Canisters created via `provisional_create_canister_with_cycles` + // with the management canister ID as the effective canister ID + // are created on the subnet with the default effective canister ID. + if is_provisional_create_canister && canister_id == CanisterId::ic_00() { + Ok(pic + .try_route_canister( + PrincipalId(pic.topology.default_effective_canister_id) + .try_into() + .unwrap(), + ) + .unwrap()) + } else if is_provisional_create_canister { // We retrieve the PocketIC instace time (consistent across all subnets) from one subnet. let time = pic.subnets.get_all().first().unwrap().state_machine.time(); // We create a new subnet with the IC mainnet configuration containing the effective canister ID. diff --git a/rs/pocket_ic_server/src/state_api/routes.rs b/rs/pocket_ic_server/src/state_api/routes.rs index 5b04216cdc2..777b57589ba 100644 --- a/rs/pocket_ic_server/src/state_api/routes.rs +++ b/rs/pocket_ic_server/src/state_api/routes.rs @@ -8,9 +8,9 @@ use super::state::{ApiState, OpOut, PocketIcError, StateLabel, UpdateReply}; use crate::pocket_ic::{ AddCycles, AwaitIngressMessage, CallRequest, CallRequestVersion, CanisterReadStateRequest, - DashboardRequest, ExecuteIngressMessage, GetCanisterHttp, GetControllers, GetCyclesBalance, - GetStableMemory, GetSubnet, GetTime, GetTopology, IngressMessageStatus, MockCanisterHttp, - PubKey, Query, QueryRequest, SetStableMemory, SetTime, StatusRequest, SubmitIngressMessage, + DashboardRequest, GetCanisterHttp, GetControllers, GetCyclesBalance, GetStableMemory, + GetSubnet, GetTime, GetTopology, IngressMessageStatus, MockCanisterHttp, PubKey, Query, + QueryRequest, SetCertifiedTime, SetStableMemory, SetTime, StatusRequest, SubmitIngressMessage, SubnetReadStateRequest, Tick, }; use crate::{async_trait, pocket_ic::PocketIc, BlobStore, InstanceId, OpId, Operation}; @@ -38,11 +38,11 @@ use ic_types::{CanisterId, SubnetId}; use pocket_ic::common::rest::{ self, ApiResponse, AutoProgressConfig, ExtendedSubnetConfigSet, HttpGatewayConfig, HttpGatewayDetails, InstanceConfig, MockCanisterHttpResponse, RawAddCycles, RawCanisterCall, - RawCanisterHttpRequest, RawCanisterId, RawCanisterResult, RawCycles, RawMessageId, - RawMockCanisterHttpResponse, RawPrincipalId, RawSetStableMemory, RawStableMemory, - RawSubmitIngressResult, RawSubnetId, RawTime, RawWasmResult, Topology, + RawCanisterHttpRequest, RawCanisterId, RawCanisterResult, RawCycles, RawIngressStatusArgs, + RawMessageId, RawMockCanisterHttpResponse, RawPrincipalId, RawSetStableMemory, RawStableMemory, + RawSubnetId, RawTime, Topology, }; -use pocket_ic::WasmResult; +use pocket_ic::RejectResponse; use serde::Serialize; use slog::Level; use std::str::FromStr; @@ -98,11 +98,8 @@ where "/await_ingress_message", post(handler_await_ingress_message), ) - .directory_route( - "/execute_ingress_message", - post(handler_execute_ingress_message), - ) .directory_route("/set_time", post(handler_set_time)) + .directory_route("/set_certified_time", post(handler_set_certified_time)) .directory_route("/add_cycles", post(handler_add_cycles)) .directory_route("/set_stable_memory", post(handler_set_stable_memory)) .directory_route("/tick", post(handler_tick)) @@ -312,6 +309,12 @@ impl> FromOpOut for T { async fn from(value: OpOut) -> (StatusCode, ApiResponse) { // match errors explicitly to make sure they have a 4xx status code match value { + OpOut::Error(PocketIcError::Forbidden(msg)) => ( + StatusCode::FORBIDDEN, + ApiResponse::Error { + message: msg.to_string(), + }, + ), OpOut::Error(e) => ( StatusCode::BAD_REQUEST, ApiResponse::Error { @@ -405,18 +408,7 @@ impl TryFrom for RawCanisterResult { type Error = OpConversionError; fn try_from(value: OpOut) -> Result { match value { - OpOut::CanisterResult(wasm_result) => { - let inner = match wasm_result { - Ok(WasmResult::Reply(wasm_result)) => { - RawCanisterResult::Ok(RawWasmResult::Reply(wasm_result)) - } - Ok(WasmResult::Reject(error_message)) => { - RawCanisterResult::Ok(RawWasmResult::Reject(error_message)) - } - Err(user_error) => RawCanisterResult::Err(user_error), - }; - Ok(inner) - } + OpOut::CanisterResult(result) => Ok(result.into()), _ => Err(OpConversionError), } } @@ -426,18 +418,7 @@ impl TryFrom for Option { type Error = OpConversionError; fn try_from(value: OpOut) -> Result { match value { - OpOut::CanisterResult(wasm_result) => { - let inner = match wasm_result { - Ok(WasmResult::Reply(wasm_result)) => { - Some(RawCanisterResult::Ok(RawWasmResult::Reply(wasm_result))) - } - Ok(WasmResult::Reject(error_message)) => { - Some(RawCanisterResult::Ok(RawWasmResult::Reject(error_message))) - } - Err(user_error) => Some(RawCanisterResult::Err(user_error)), - }; - Ok(inner) - } + OpOut::CanisterResult(result) => Ok(Some(result.into())), OpOut::NoOutput => Ok(None), _ => Err(OpConversionError), } @@ -490,17 +471,15 @@ impl TryFrom for Vec { } } -impl TryFrom for RawSubmitIngressResult { +impl TryFrom for Result { type Error = OpConversionError; fn try_from(value: OpOut) -> Result { match value { - OpOut::MessageId((effective_principal, message_id)) => { - Ok(RawSubmitIngressResult::Ok(RawMessageId { - effective_principal: effective_principal.into(), - message_id, - })) - } - OpOut::CanisterResult(Err(user_error)) => Ok(RawSubmitIngressResult::Err(user_error)), + OpOut::MessageId((effective_principal, message_id)) => Ok(Ok(RawMessageId { + effective_principal: effective_principal.into(), + message_id, + })), + OpOut::CanisterResult(Err(reject_response)) => Ok(Err(reject_response)), _ => Err(OpConversionError), } } @@ -832,7 +811,7 @@ async fn op_out_to_response(op_out: OpOut) -> Response { opout @ OpOut::MessageId(_) => ( StatusCode::OK, Json(ApiResponse::Success( - RawSubmitIngressResult::try_from(opout).unwrap(), + Result::::try_from(opout).unwrap(), )), ) .into_response(), @@ -894,6 +873,13 @@ async fn op_out_to_response(op_out: OpOut) -> Response { )), ) .into_response(), + OpOut::Error(PocketIcError::Forbidden(msg)) => ( + StatusCode::FORBIDDEN, + Json(ApiResponse::<()>::Error { + message: msg.to_string(), + }), + ) + .into_response(), opout @ OpOut::Error(_) => ( StatusCode::BAD_REQUEST, Json(ApiResponse::<()>::Error { @@ -967,7 +953,10 @@ pub async fn handler_submit_ingress_message( Path(instance_id): Path, headers: HeaderMap, extract::Json(raw_canister_call): extract::Json, -) -> (StatusCode, Json>) { +) -> ( + StatusCode, + Json>>, +) { let timeout = timeout_or_default(headers); match crate::pocket_ic::CanisterCall::try_from(raw_canister_call) { Ok(canister_call) => { @@ -1006,16 +995,21 @@ pub async fn handler_await_ingress_message( } } -pub async fn handler_execute_ingress_message( +pub async fn handler_ingress_status( State(AppState { api_state, .. }): State, Path(instance_id): Path, headers: HeaderMap, - extract::Json(raw_canister_call): extract::Json, -) -> (StatusCode, Json>) { + extract::Json(raw_ingress_status_args): extract::Json, +) -> (StatusCode, Json>>) { let timeout = timeout_or_default(headers); - match crate::pocket_ic::CanisterCall::try_from(raw_canister_call) { - Ok(canister_call) => { - let ingress_op = ExecuteIngressMessage(canister_call); + match crate::pocket_ic::MessageId::try_from(raw_ingress_status_args.raw_message_id) { + Ok(message_id) => { + let ingress_op = IngressMessageStatus { + message_id, + caller: raw_ingress_status_args + .raw_caller + .map(|caller| caller.into()), + }; let (code, response) = run_operation(api_state, instance_id, timeout, ingress_op).await; (code, Json(response)) } @@ -1028,36 +1022,28 @@ pub async fn handler_execute_ingress_message( } } -pub async fn handler_ingress_status( +pub async fn handler_set_time( State(AppState { api_state, .. }): State, Path(instance_id): Path, headers: HeaderMap, - extract::Json(raw_message_id): extract::Json, -) -> (StatusCode, Json>>) { + axum::extract::Json(time): axum::extract::Json, +) -> (StatusCode, Json>) { let timeout = timeout_or_default(headers); - match crate::pocket_ic::MessageId::try_from(raw_message_id) { - Ok(message_id) => { - let ingress_op = IngressMessageStatus(message_id); - let (code, response) = run_operation(api_state, instance_id, timeout, ingress_op).await; - (code, Json(response)) - } - Err(e) => ( - StatusCode::BAD_REQUEST, - Json(ApiResponse::Error { - message: format!("{:?}", e), - }), - ), - } + let op = SetTime { + time: ic_types::Time::from_nanos_since_unix_epoch(time.nanos_since_epoch), + }; + let (code, response) = run_operation(api_state, instance_id, timeout, op).await; + (code, Json(response)) } -pub async fn handler_set_time( +pub async fn handler_set_certified_time( State(AppState { api_state, .. }): State, Path(instance_id): Path, headers: HeaderMap, axum::extract::Json(time): axum::extract::Json, ) -> (StatusCode, Json>) { let timeout = timeout_or_default(headers); - let op = SetTime { + let op = SetCertifiedTime { time: ic_types::Time::from_nanos_since_unix_epoch(time.nanos_since_epoch), }; let (code, response) = run_operation(api_state, instance_id, timeout, op).await; diff --git a/rs/pocket_ic_server/src/state_api/state.rs b/rs/pocket_ic_server/src/state_api/state.rs index f3d7a2311be..e13ed094bf0 100644 --- a/rs/pocket_ic_server/src/state_api/state.rs +++ b/rs/pocket_ic_server/src/state_api/state.rs @@ -38,7 +38,7 @@ use pocket_ic::common::rest::{ CanisterHttpRequest, HttpGatewayBackend, HttpGatewayConfig, HttpGatewayDetails, HttpGatewayInfo, Topology, }; -use pocket_ic::{ErrorCode, UserError, WasmResult}; +use pocket_ic::RejectResponse; use reqwest::Url; use serde::{Deserialize, Serialize}; use std::{ @@ -230,7 +230,7 @@ impl PocketIcApiStateBuilder { pub enum OpOut { NoOutput, Time(u64), - CanisterResult(Result), + CanisterResult(Result, RejectResponse>), CanisterId(CanisterId), Controllers(Vec), Cycles(u128), @@ -255,24 +255,7 @@ pub enum PocketIcError { InvalidMockCanisterHttpResponses((usize, usize)), InvalidRejectCode(u64), SettingTimeIntoPast((u64, u64)), -} - -impl From> for OpOut { - fn from( - r: Result, - ) -> Self { - let res = { - match r { - Ok(ic_state_machine_tests::WasmResult::Reply(wasm)) => Ok(WasmResult::Reply(wasm)), - Ok(ic_state_machine_tests::WasmResult::Reject(s)) => Ok(WasmResult::Reject(s)), - Err(user_err) => Err(UserError { - code: ErrorCode::try_from(user_err.code() as u64).unwrap(), - description: user_err.description().to_string(), - }), - } - }; - OpOut::CanisterResult(res) - } + Forbidden(String), } impl std::fmt::Debug for OpOut { @@ -328,6 +311,9 @@ impl std::fmt::Debug for OpOut { OpOut::Error(PocketIcError::SettingTimeIntoPast((current, set))) => { write!(f, "SettingTimeIntoPast(current={},set={})", current, set) } + OpOut::Error(PocketIcError::Forbidden(msg)) => { + write!(f, "Forbidden({})", msg) + } OpOut::Bytes(bytes) => write!(f, "Bytes({})", base64::encode(bytes)), OpOut::StableMemBytes(bytes) => write!(f, "StableMemory({})", base64::encode(bytes)), OpOut::MaybeSubnetId(Some(subnet_id)) => write!(f, "SubnetId({})", subnet_id), @@ -608,8 +594,7 @@ async fn handler( |_| ErrorCause::RequestTooLarge, ) })? - .to_bytes() - .to_vec(); + .to_bytes(); let args = HttpGatewayRequestArgs { canister_request: CanisterRequest::from_parts(parts, body), diff --git a/rs/pocket_ic_server/tests/bitcoin_integration_tests.rs b/rs/pocket_ic_server/tests/bitcoin_integration_tests.rs index 59a97ab7dba..7b29841928c 100644 --- a/rs/pocket_ic_server/tests/bitcoin_integration_tests.rs +++ b/rs/pocket_ic_server/tests/bitcoin_integration_tests.rs @@ -94,7 +94,7 @@ rpcauth=ic-btc-integration:cdf2741387f3a12438f69092f0fdad8e$62081498c98bee09a0dc let data_dir_path = tmp_dir.path().join("data"); create_dir(data_dir_path.clone()).unwrap(); - Command::new(bitcoind_path) + let mut bitcoin_d_process = Command::new(bitcoind_path) .arg(format!("-conf={}", conf_path.display())) .arg(format!("-datadir={}", data_dir_path.display())) .spawn() @@ -140,9 +140,25 @@ rpcauth=ic-btc-integration:cdf2741387f3a12438f69092f0fdad8e$62081498c98bee09a0dc // `n` must be more than 100 (Coinbase maturity rule) so that the reward for the first block can be sent out let mut n = 101; - btc_rpc - .generate_to_address(n, &Address::from_str(&bitcoin_address).unwrap()) - .unwrap(); + // retry generating blocks until the bitcoind is up and running + let start = std::time::Instant::now(); + loop { + match btc_rpc.generate_to_address( + n, + &Address::from_str(&bitcoin_address) + .unwrap() + .assume_checked(), + ) { + Ok(_) => break, + Err(bitcoincore_rpc::Error::JsonRpc(err)) => { + if start.elapsed() > std::time::Duration::from_secs(30) { + panic!("Timed out when waiting for bitcoind; last error: {}", err); + } + std::thread::sleep(std::time::Duration::from_millis(100)); + } + Err(err) => panic!("Unexpected error when talking to bitcoind: {}", err), + } + } let reward = 50 * 100_000_000; // 50 BTC @@ -175,9 +191,18 @@ rpcauth=ic-btc-integration:cdf2741387f3a12438f69092f0fdad8e$62081498c98bee09a0dc break; } else { btc_rpc - .generate_to_address(1, &Address::from_str(&bitcoin_address).unwrap()) + .generate_to_address( + 1, + &Address::from_str(&bitcoin_address) + .unwrap() + .assume_checked(), + ) .unwrap(); n += 1; } } + + // Kill the task to avoid zombie process. + bitcoin_d_process.kill().unwrap(); + bitcoin_d_process.wait().unwrap(); } diff --git a/rs/pocket_ic_server/tests/spec_test.rs b/rs/pocket_ic_server/tests/spec_test.rs index 037013ecb23..567c79cf324 100644 --- a/rs/pocket_ic_server/tests/spec_test.rs +++ b/rs/pocket_ic_server/tests/spec_test.rs @@ -90,7 +90,8 @@ fn setup_and_run_ic_ref_test( cmd.arg("--key-file").arg(key_path); } - cmd.stdout(Stdio::inherit()) + let mut process = cmd + .stdout(Stdio::inherit()) .stderr(Stdio::inherit()) .spawn() .expect("httpbin binary crashed"); @@ -196,6 +197,9 @@ fn setup_and_run_ic_ref_test( included_tests, 32, ); + + process.kill().unwrap(); + process.wait().unwrap(); } #[test] diff --git a/rs/pocket_ic_server/tests/test.rs b/rs/pocket_ic_server/tests/test.rs index b8ecb76821a..68d9ad3eb06 100644 --- a/rs/pocket_ic_server/tests/test.rs +++ b/rs/pocket_ic_server/tests/test.rs @@ -1,5 +1,5 @@ use candid::{Encode, Principal}; -use ic_agent::agent::{http_transport::ReqwestTransport, CallResponse}; +use ic_agent::agent::CallResponse; use ic_cdk::api::management_canister::main::CanisterIdRecord; use ic_cdk::api::management_canister::provisional::ProvisionalCreateCanisterWithCyclesArgument; use ic_interfaces_registry::{ @@ -16,7 +16,7 @@ use pocket_ic::common::rest::{ CreateHttpGatewayResponse, HttpGatewayBackend, HttpGatewayConfig, HttpGatewayDetails, HttpsConfig, InstanceConfig, SubnetConfigSet, SubnetKind, Topology, }; -use pocket_ic::{update_candid, PocketIc, PocketIcBuilder, WasmResult}; +use pocket_ic::{update_candid, PocketIc, PocketIcBuilder}; use rcgen::{CertificateParams, KeyPair}; use registry_canister::init::RegistryCanisterInitPayload; use reqwest::blocking::Client; @@ -313,14 +313,10 @@ async fn test_gateway(server_url: Url, https: bool) { } let client = builder.build().unwrap(); - // create agent with custom transport - let transport = ReqwestTransport::create_with_client( - format!("{}://{}:{}", proto, localhost, port), - client.clone(), - ) - .unwrap(); + // create agent let agent = ic_agent::Agent::builder() - .with_transport(transport) + .with_url(format!("{}://{}:{}", proto, localhost, port)) + .with_http_client(client.clone()) .build() .unwrap(); agent.fetch_root_key().await.unwrap(); @@ -632,12 +628,7 @@ fn check_counter(pic: &PocketIc, canister_id: Principal, expected_ctr: u32) { let res = pic .query_call(canister_id, Principal::anonymous(), "read", vec![]) .unwrap(); - match res { - WasmResult::Reply(data) => { - assert_eq!(u32::from_le_bytes(data.try_into().unwrap()), expected_ctr); - } - _ => panic!("Unexpected update call response"), - }; + assert_eq!(u32::from_le_bytes(res.try_into().unwrap()), expected_ctr); } /// Tests that the PocketIC topology and canister states @@ -897,12 +888,9 @@ fn test_specified_id_call_v3() { .build() .unwrap(); rt.block_on(async { - let transport = ReqwestTransport::create(endpoint.clone()) - .unwrap() - .with_use_call_v3_endpoint(); - let agent = ic_agent::Agent::builder() - .with_transport(transport) + .with_url(endpoint) + .with_http_client(reqwest::Client::new()) .build() .unwrap(); agent.fetch_root_key().await.unwrap(); @@ -1308,9 +1296,6 @@ fn registry_canister() { } #[test] -#[should_panic( - expected = "The binary representation of effective canister ID aaaaa-aa should consist of 10 bytes." -)] fn provisional_create_canister_with_cycles() { let pic = PocketIcBuilder::new() .with_nns_subnet() diff --git a/rs/prep/src/bin/prep.rs b/rs/prep/src/bin/prep.rs index 7d200ed245a..1079d7c5632 100644 --- a/rs/prep/src/bin/prep.rs +++ b/rs/prep/src/bin/prep.rs @@ -509,6 +509,7 @@ mod test_flag_node_parser { node_operator_principal_id: None, secret_key_store: None, domain: None, + node_reward_type: None, }, }; diff --git a/rs/prep/src/node.rs b/rs/prep/src/node.rs index fa9bfaf53b8..3a92674cfa6 100644 --- a/rs/prep/src/node.rs +++ b/rs/prep/src/node.rs @@ -21,7 +21,9 @@ use ic_crypto_node_key_validation::ValidNodePublicKeys; use ic_interfaces_state_manager::{CertificationScope, StateHashError, StateManager}; use ic_protobuf::registry::{ crypto::v1::{PublicKey, X509PublicKeyCert}, - node::v1::{ConnectionEndpoint as pbConnectionEndpoint, NodeRecord as pbNodeRecord}, + node::v1::{ + ConnectionEndpoint as pbConnectionEndpoint, NodeRecord as pbNodeRecord, NodeRewardType, + }, }; use ic_registry_keys::{make_crypto_node_key, make_crypto_tls_cert_key, make_node_record_key}; use ic_registry_proto_data_provider::ProtoRegistryDataProvider; @@ -306,6 +308,10 @@ pub struct NodeConfiguration { /// The domain name of the node #[serde(skip_serializing, skip_deserializing)] pub domain: Option, + + /// The type of rewards that the node operator wants to receive for the node. + /// E.g. "type3.1" or "type1" or similar from the node reward table in the NNS. + pub node_reward_type: Option, } impl From for pbNodeRecord { @@ -324,6 +330,10 @@ impl From for pbNodeRecord { .map(|id| id.to_vec()) .unwrap_or_default(), domain: node_configuration.domain, + node_reward_type: node_configuration + .node_reward_type + .map(NodeRewardType::from) + .map(|t| t as i32), ..Default::default() } } @@ -440,6 +450,7 @@ mod node_configuration { node_operator_principal_id: None, secret_key_store: None, domain: None, + node_reward_type: None, }; let got = pbNodeRecord::from(node_configuration); diff --git a/rs/prep/src/prep_state_directory.rs b/rs/prep/src/prep_state_directory.rs index a316b5d6c1c..ca86b81a83b 100755 --- a/rs/prep/src/prep_state_directory.rs +++ b/rs/prep/src/prep_state_directory.rs @@ -129,6 +129,7 @@ mod tests { node_operator_principal_id: None, secret_key_store: None, domain: None, + node_reward_type: None, }, ); diff --git a/rs/prep/src/subnet_configuration.rs b/rs/prep/src/subnet_configuration.rs index c7ef379feab..6e518b7c4c9 100644 --- a/rs/prep/src/subnet_configuration.rs +++ b/rs/prep/src/subnet_configuration.rs @@ -169,7 +169,6 @@ pub fn duration_to_millis(unit_delay: Duration) -> u64 { /// The configuration for app subnets is used for new app subnets with at most /// 13 nodes. App subnets with more than 13 nodes will be deployed with the NNS /// subnet configs. - pub fn get_default_config_params(subnet_type: SubnetType, nodes_num: usize) -> SubnetConfigParams { let use_app_config = subnet_type == SubnetType::Application && nodes_num <= ic_limits::SMALL_APP_SUBNET_MAX_SIZE; diff --git a/rs/protobuf/def/log/crypto_log_entry/v1/crypto_log_entry.proto b/rs/protobuf/def/log/crypto_log_entry/v1/crypto_log_entry.proto index e71c9f521bc..f9f2738ae0a 100644 --- a/rs/protobuf/def/log/crypto_log_entry/v1/crypto_log_entry.proto +++ b/rs/protobuf/def/log/crypto_log_entry/v1/crypto_log_entry.proto @@ -34,4 +34,8 @@ message CryptoLogEntry { google.protobuf.StringValue signature_shares = 26; google.protobuf.StringValue signature_inputs = 27; google.protobuf.UInt64Value log_id = 28; + google.protobuf.StringValue vetkd_args = 29; + google.protobuf.StringValue vetkd_key_share = 30; + google.protobuf.StringValue vetkd_key_shares = 31; + google.protobuf.StringValue vetkd_key = 32; } diff --git a/rs/protobuf/def/registry/node_operator/v1/node_operator.proto b/rs/protobuf/def/registry/node_operator/v1/node_operator.proto index 91ae44a2ead..2b13634ba43 100644 --- a/rs/protobuf/def/registry/node_operator/v1/node_operator.proto +++ b/rs/protobuf/def/registry/node_operator/v1/node_operator.proto @@ -29,8 +29,3 @@ message NodeOperatorRecord { optional string ipv6 = 6; } - -// The payload of a request to remove Node Operator records from the Registry -message RemoveNodeOperatorsPayload { - repeated bytes node_operators_to_remove = 1; -} diff --git a/rs/protobuf/def/types/v1/consensus.proto b/rs/protobuf/def/types/v1/consensus.proto index b788a2e1026..33bd3b36180 100644 --- a/rs/protobuf/def/types/v1/consensus.proto +++ b/rs/protobuf/def/types/v1/consensus.proto @@ -198,21 +198,42 @@ message CanisterQueryStats { uint64 egress_payload_size = 5; } +enum VetKdErrorCode { + VET_KD_ERROR_CODE_UNSPECIFIED = 0; + VET_KD_ERROR_CODE_TIMED_OUT = 1; + VET_KD_ERROR_CODE_INVALID_KEY = 2; +} + +message VetKdAgreement { + uint64 callback_id = 1; + oneof agreement { + bytes data = 2; + VetKdErrorCode reject = 3; + } +} + message IngressIdOffset { uint64 expiry = 1; bytes message_id = 2; uint64 offset = 3; } +message IngressMessage { + bytes message_id = 1; + uint64 expiry = 2; + bytes signed_request_bytes = 3; +} + message IngressPayload { - repeated IngressIdOffset id_and_pos = 1; - bytes buffer = 2; + reserved 1, 2; + repeated IngressMessage ingress_messages = 3; } // Stripped consensus artifacts messages below message GetIngressMessageInBlockRequest { IngressMessageId ingress_message_id = 1; ConsensusMessageId block_proposal_id = 2; + bytes ingress_bytes_hash = 3; } message GetIngressMessageInBlockResponse { @@ -227,6 +248,7 @@ message StrippedBlockProposal { message StrippedIngressMessage { IngressMessageId stripped = 1; + bytes ingress_bytes_hash = 2; } message StrippedConsensusMessage { diff --git a/rs/protobuf/src/gen/log/log.crypto_log_entry.v1.rs b/rs/protobuf/src/gen/log/log.crypto_log_entry.v1.rs index d43066d9c89..556c14cb8db 100644 --- a/rs/protobuf/src/gen/log/log.crypto_log_entry.v1.rs +++ b/rs/protobuf/src/gen/log/log.crypto_log_entry.v1.rs @@ -85,4 +85,12 @@ pub struct CryptoLogEntry { #[prost(message, optional, tag = "28")] #[serde(skip_serializing_if = "Option::is_none")] pub log_id: ::core::option::Option, + #[prost(message, optional, tag = "29")] + pub vetkd_args: ::core::option::Option<::prost::alloc::string::String>, + #[prost(message, optional, tag = "30")] + pub vetkd_key_share: ::core::option::Option<::prost::alloc::string::String>, + #[prost(message, optional, tag = "31")] + pub vetkd_key_shares: ::core::option::Option<::prost::alloc::string::String>, + #[prost(message, optional, tag = "32")] + pub vetkd_key: ::core::option::Option<::prost::alloc::string::String>, } diff --git a/rs/protobuf/src/gen/registry/registry.node_operator.v1.rs b/rs/protobuf/src/gen/registry/registry.node_operator.v1.rs index 7016b9114ef..cd42ae996d2 100644 --- a/rs/protobuf/src/gen/registry/registry.node_operator.v1.rs +++ b/rs/protobuf/src/gen/registry/registry.node_operator.v1.rs @@ -39,18 +39,3 @@ pub struct NodeOperatorRecord { #[prost(string, optional, tag = "6")] pub ipv6: ::core::option::Option<::prost::alloc::string::String>, } -/// The payload of a request to remove Node Operator records from the Registry -#[derive( - candid::CandidType, - serde::Serialize, - candid::Deserialize, - Eq, - Hash, - Clone, - PartialEq, - ::prost::Message, -)] -pub struct RemoveNodeOperatorsPayload { - #[prost(bytes = "vec", repeated, tag = "1")] - pub node_operators_to_remove: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, -} diff --git a/rs/protobuf/src/gen/types/types.v1.rs b/rs/protobuf/src/gen/types/types.v1.rs index 892c97432d9..cd5e4689df7 100644 --- a/rs/protobuf/src/gen/types/types.v1.rs +++ b/rs/protobuf/src/gen/types/types.v1.rs @@ -1519,6 +1519,23 @@ pub struct CanisterQueryStats { pub egress_payload_size: u64, } #[derive(Clone, PartialEq, ::prost::Message)] +pub struct VetKdAgreement { + #[prost(uint64, tag = "1")] + pub callback_id: u64, + #[prost(oneof = "vet_kd_agreement::Agreement", tags = "2, 3")] + pub agreement: ::core::option::Option, +} +/// Nested message and enum types in `VetKdAgreement`. +pub mod vet_kd_agreement { + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Agreement { + #[prost(bytes, tag = "2")] + Data(::prost::alloc::vec::Vec), + #[prost(enumeration = "super::VetKdErrorCode", tag = "3")] + Reject(i32), + } +} +#[derive(Clone, PartialEq, ::prost::Message)] pub struct IngressIdOffset { #[prost(uint64, tag = "1")] pub expiry: u64, @@ -1528,11 +1545,18 @@ pub struct IngressIdOffset { pub offset: u64, } #[derive(Clone, PartialEq, ::prost::Message)] +pub struct IngressMessage { + #[prost(bytes = "vec", tag = "1")] + pub message_id: ::prost::alloc::vec::Vec, + #[prost(uint64, tag = "2")] + pub expiry: u64, + #[prost(bytes = "vec", tag = "3")] + pub signed_request_bytes: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] pub struct IngressPayload { - #[prost(message, repeated, tag = "1")] - pub id_and_pos: ::prost::alloc::vec::Vec, - #[prost(bytes = "vec", tag = "2")] - pub buffer: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "3")] + pub ingress_messages: ::prost::alloc::vec::Vec, } /// Stripped consensus artifacts messages below #[derive(Clone, PartialEq, ::prost::Message)] @@ -1541,6 +1565,8 @@ pub struct GetIngressMessageInBlockRequest { pub ingress_message_id: ::core::option::Option, #[prost(message, optional, tag = "2")] pub block_proposal_id: ::core::option::Option, + #[prost(bytes = "vec", tag = "3")] + pub ingress_bytes_hash: ::prost::alloc::vec::Vec, } #[derive(Clone, PartialEq, ::prost::Message)] pub struct GetIngressMessageInBlockResponse { @@ -1560,6 +1586,8 @@ pub struct StrippedBlockProposal { pub struct StrippedIngressMessage { #[prost(message, optional, tag = "1")] pub stripped: ::core::option::Option, + #[prost(bytes = "vec", tag = "2")] + pub ingress_bytes_hash: ::prost::alloc::vec::Vec, } #[allow(clippy::large_enum_variant)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -1583,3 +1611,32 @@ pub struct StrippedConsensusMessageId { #[prost(message, optional, tag = "1")] pub unstripped_id: ::core::option::Option, } +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum VetKdErrorCode { + Unspecified = 0, + TimedOut = 1, + InvalidKey = 2, +} +impl VetKdErrorCode { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "VET_KD_ERROR_CODE_UNSPECIFIED", + Self::TimedOut => "VET_KD_ERROR_CODE_TIMED_OUT", + Self::InvalidKey => "VET_KD_ERROR_CODE_INVALID_KEY", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "VET_KD_ERROR_CODE_UNSPECIFIED" => Some(Self::Unspecified), + "VET_KD_ERROR_CODE_TIMED_OUT" => Some(Self::TimedOut), + "VET_KD_ERROR_CODE_INVALID_KEY" => Some(Self::InvalidKey), + _ => None, + } + } +} diff --git a/rs/protobuf/src/proxy.rs b/rs/protobuf/src/proxy.rs index 51bf54ba2d2..ef4942c068a 100644 --- a/rs/protobuf/src/proxy.rs +++ b/rs/protobuf/src/proxy.rs @@ -132,7 +132,7 @@ impl ProxyDecodeError { T: Error + Eq + 'static, { self.source() - .map_or(false, |err| err.downcast_ref() == Some(&other_err)) + .is_some_and(|err| err.downcast_ref() == Some(&other_err)) } } diff --git a/rs/protobuf/src/registry/node.rs b/rs/protobuf/src/registry/node.rs index 96fb4072f7a..c850826d193 100644 --- a/rs/protobuf/src/registry/node.rs +++ b/rs/protobuf/src/registry/node.rs @@ -12,10 +12,30 @@ impl From for String { } NodeRewardType::Type0 => "type0".to_string(), NodeRewardType::Type1 => "type1".to_string(), + NodeRewardType::Type1dot1 => "type1.1".to_string(), NodeRewardType::Type2 => "type2".to_string(), NodeRewardType::Type3 => "type3".to_string(), NodeRewardType::Type3dot1 => "type3.1".to_string(), - NodeRewardType::Type1dot1 => "type1.1".to_string(), + } + } +} + +impl std::fmt::Display for NodeRewardType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", String::from(*self)) + } +} + +impl From for NodeRewardType { + fn from(value: String) -> Self { + match value.as_str() { + "type0" => NodeRewardType::Type0, + "type1" => NodeRewardType::Type1, + "type1.1" => NodeRewardType::Type1dot1, + "type2" => NodeRewardType::Type2, + "type3" => NodeRewardType::Type3, + "type3.1" => NodeRewardType::Type3dot1, + _ => NodeRewardType::Unspecified, } } } diff --git a/rs/query_stats/src/payload_builder.rs b/rs/query_stats/src/payload_builder.rs index 44bfac928b8..a4e741234d5 100644 --- a/rs/query_stats/src/payload_builder.rs +++ b/rs/query_stats/src/payload_builder.rs @@ -163,7 +163,7 @@ impl QueryStatsPayloadBuilderImpl { warn!( every_n_seconds => 30, self.log, - "Current stats are uninitalized. This warning should go away after some minutes if the replica is processing query calls." + "Current stats are uninitialized. This warning should go away after some minutes if the replica is processing query calls." ); vec![] } diff --git a/rs/recovery/BUILD.bazel b/rs/recovery/BUILD.bazel index 1364ceed55c..e135f4024a3 100644 --- a/rs/recovery/BUILD.bazel +++ b/rs/recovery/BUILD.bazel @@ -60,8 +60,8 @@ rust_library( name = "recovery", srcs = glob(["src/**/*.rs"]), aliases = ALIASES, + compile_data = ["ic_public_key.pem"], crate_name = "ic_recovery", - data = ["ic_public_key.pem"], proc_macro_deps = MACRO_DEPENDENCIES, version = "0.1.0", visibility = [ @@ -88,7 +88,7 @@ rust_binary( rust_test( name = "recovery_test", + compile_data = ["ic_public_key.pem"], crate = ":recovery", - data = ["ic_public_key.pem"], deps = DEPENDENCIES + DEV_DEPENDENCIES, ) diff --git a/rs/recovery/src/admin_helper.rs b/rs/recovery/src/admin_helper.rs index 0e1c4aa718c..7035d114ac9 100644 --- a/rs/recovery/src/admin_helper.rs +++ b/rs/recovery/src/admin_helper.rs @@ -122,9 +122,7 @@ impl AdminHelper { let mut ic_admin = self.get_ic_admin_cmd_base(); ic_admin - // TODO: Switch to the new command name: - // .add_positional_argument("propose-to-revise-elected-guestos-versions") - .add_positional_argument("propose-to-update-elected-replica-versions") + .add_positional_argument("propose-to-revise-elected-guestos-versions") .add_argument("replica-version-to-elect", quote(upgrade_version)) .add_argument("release-package-urls", quote(upgrade_url)) .add_argument("release-package-sha256-hex", quote(sha256)) @@ -149,9 +147,7 @@ impl AdminHelper { let mut ic_admin = self.get_ic_admin_cmd_base(); ic_admin - // TODO: Switch to the new command name: - // .add_positional_argument("propose-to-deploy-guestos-to-all-subnet-nodes") - .add_positional_argument("propose-to-update-subnet-replica-version") + .add_positional_argument("propose-to-deploy-guestos-to-all-subnet-nodes") .add_positional_argument(subnet_id) .add_positional_argument(upgrade_version) .add_argument( @@ -416,7 +412,7 @@ mod tests { result, "/fake/ic/admin/dir/ic-admin \ --nns-url \"https://fake_nns_url.com:8080/\" \ - propose-to-update-elected-replica-versions \ + propose-to-revise-elected-guestos-versions \ --replica-version-to-elect \"fake_replica_version\" \ --release-package-urls \"https://fake_upgrade_url.com/\" \ --release-package-sha256-hex \"fake_sha_256\" \ @@ -529,7 +525,7 @@ mod tests { assert_eq!(result, "/fake/ic/admin/dir/ic-admin \ --nns-url \"https://fake_nns_url.com:8080/\" \ - propose-to-update-subnet-replica-version \ + propose-to-deploy-guestos-to-all-subnet-nodes \ gpvux-2ejnk-3hgmh-cegwf-iekfc-b7rzs-hrvep-5euo2-3ywz3-k3hcb-cqe \ fake_replica_version \ --summary \"Upgrade replica version of subnet gpvux-2ejnk-3hgmh-cegwf-iekfc-b7rzs-hrvep-5euo2-3ywz3-k3hcb-cqe.\" \ diff --git a/rs/recovery/src/app_subnet_recovery.rs b/rs/recovery/src/app_subnet_recovery.rs index b216b03b2d4..7923983735a 100644 --- a/rs/recovery/src/app_subnet_recovery.rs +++ b/rs/recovery/src/app_subnet_recovery.rs @@ -306,10 +306,11 @@ impl RecoveryIterator for AppSubnetRecovery { StepType::DownloadCertifications => { if self.params.pub_key.is_some() { - Ok(Box::new( - self.recovery - .get_download_certs_step(self.params.subnet_id, false), - )) + Ok(Box::new(self.recovery.get_download_certs_step( + self.params.subnet_id, + false, + !self.interactive(), + ))) } else { Err(RecoveryError::StepSkipped) } diff --git a/rs/recovery/src/args_merger.rs b/rs/recovery/src/args_merger.rs index e648cf35876..52718495a7e 100644 --- a/rs/recovery/src/args_merger.rs +++ b/rs/recovery/src/args_merger.rs @@ -66,6 +66,7 @@ mod tests { key_file: Some(PathBuf::from("/dir1/key_file")), test_mode: true, skip_prompts: true, + use_local_binaries: false, }; let args2 = RecoveryArgs { dir: PathBuf::from("/dir2/"), @@ -74,6 +75,7 @@ mod tests { key_file: None, test_mode: false, skip_prompts: true, + use_local_binaries: false, }; let expected = RecoveryArgs { @@ -83,6 +85,7 @@ mod tests { key_file: args1.key_file.clone(), test_mode: args2.test_mode, skip_prompts: true, + use_local_binaries: false, }; assert_eq!(expected, merge(&logger, "test", &args1, &args2).unwrap()); diff --git a/rs/recovery/src/cmd.rs b/rs/recovery/src/cmd.rs index 43fabcb13fb..929d53a06f2 100644 --- a/rs/recovery/src/cmd.rs +++ b/rs/recovery/src/cmd.rs @@ -54,6 +54,12 @@ pub struct RecoveryToolArgs { #[clap(long)] pub skip_prompts: bool, + /// Flag to indicate we're running recovery directly on a node, and should use + /// the locally available binaries. If this option is not set, missing binaries + /// will be downloaded. + #[clap(long)] + pub use_local_binaries: bool, + #[clap(subcommand)] pub subcmd: Option, } diff --git a/rs/recovery/src/error.rs b/rs/recovery/src/error.rs index 805a5825422..71a0b085961 100644 --- a/rs/recovery/src/error.rs +++ b/rs/recovery/src/error.rs @@ -26,6 +26,7 @@ pub enum RecoveryError { RegistryError(String), ValidationFailed(String), AgentError(String), + RsyncFailed, StepSkipped, } @@ -101,6 +102,7 @@ impl fmt::Display for RecoveryError { write!(f, "Validation failed: {}", msg) } RecoveryError::AgentError(msg) => write!(f, "ic-agent error: {}", msg), + RecoveryError::RsyncFailed => write!(f, "Rsync command failed"), } } } diff --git a/rs/recovery/src/file_sync_helper.rs b/rs/recovery/src/file_sync_helper.rs index a29de1834d2..ec3b221a866 100644 --- a/rs/recovery/src/file_sync_helper.rs +++ b/rs/recovery/src/file_sync_helper.rs @@ -4,8 +4,8 @@ use crate::{ error::{RecoveryError, RecoveryResult}, ssh_helper, }; -use core::time; use ic_http_utils::file_downloader::FileDownloader; +use ic_replay::consent_given; use ic_types::ReplicaVersion; use slog::{info, warn, Logger}; use std::{ @@ -13,7 +13,6 @@ use std::{ io::Write, path::{Path, PathBuf}, process::Command, - thread, time::Duration, }; @@ -67,6 +66,7 @@ pub async fn download_binary( Ok(file) } +/// If auto-retry is set to false, the user will be prompted for retries on rsync failures. pub fn rsync_with_retries( logger: &Logger, excludes: Vec<&str>, @@ -74,9 +74,10 @@ pub fn rsync_with_retries( target: &str, require_confirmation: bool, key_file: Option<&PathBuf>, - retries: usize, + auto_retry: bool, + max_retries: usize, ) -> RecoveryResult> { - for _ in 0..retries { + for _ in 0..max_retries { match rsync( logger, excludes.clone(), @@ -85,12 +86,21 @@ pub fn rsync_with_retries( require_confirmation, key_file, ) { - Err(e) => warn!(logger, "Rsync failed: {:?}, retrying...", e), + Err(e) => { + warn!(logger, "Rsync failed: {:?}", e); + if auto_retry { + // In non-interactive cases, we wait a short while + // before re-trying rsync. + info!(logger, "Retrying in 10 seconds..."); + std::thread::sleep(Duration::from_secs(10)); + } else if !consent_given("Do you want to retry the download for this node?") { + return Err(RecoveryError::RsyncFailed); + } + } success => return success, } - thread::sleep(time::Duration::from_secs(10)); } - Err(RecoveryError::UnexpectedError("All retries failed".into())) + Err(RecoveryError::RsyncFailed) } /// Copy the files from src to target using [rsync](https://linux.die.net/man/1/rsync) and options `--delete`, `-acP`. diff --git a/rs/recovery/src/lib.rs b/rs/recovery/src/lib.rs index 23b3cd9c052..d2db2aa29f3 100644 --- a/rs/recovery/src/lib.rs +++ b/rs/recovery/src/lib.rs @@ -107,6 +107,7 @@ pub struct RecoveryArgs { pub key_file: Option, pub test_mode: bool, pub skip_prompts: bool, + pub use_local_binaries: bool, } /// The recovery struct comprises working directories for the recovery of a @@ -145,13 +146,22 @@ impl Recovery { ) -> RecoveryResult { let ssh_confirmation = !args.test_mode; let recovery_dir = args.dir.join(RECOVERY_DIRECTORY_NAME); - let binary_dir = recovery_dir.join("binaries"); + let binary_dir = if args.use_local_binaries { + PathBuf::from_str("/opt/ic/bin/").expect("bad file path string") + } else { + recovery_dir.join("binaries") + }; let data_dir = recovery_dir.join("original_data"); let work_dir = recovery_dir.join("working_dir"); let local_store_path = work_dir.join("data").join(IC_REGISTRY_LOCAL_STORE); let nns_pem = recovery_dir.join("nns.pem"); - match Recovery::create_dirs(&[&binary_dir, &data_dir, &work_dir, &local_store_path]) { + let mut to_create: Vec<&Path> = vec![&data_dir, &work_dir, &local_store_path]; + if !args.use_local_binaries { + to_create.push(&binary_dir); + } + + match Recovery::create_dirs(&to_create) { Err(RecoveryError::IoError(s, err)) => match err.kind() { ErrorKind::PermissionDenied => Err(RecoveryError::IoError( format!( @@ -180,7 +190,7 @@ impl Recovery { wait_for_confirmation(&logger); } - if !binary_dir.join("ic-admin").exists() { + if !args.use_local_binaries && !binary_dir.join("ic-admin").exists() { if let Some(version) = args.replica_version { block_on(download_binary( &logger, @@ -304,7 +314,14 @@ impl Recovery { /// Return a [DownloadCertificationsStep] downloading the certification pools of all reachable /// nodes in the given subnet to the recovery data directory using the readonly account. - pub fn get_download_certs_step(&self, subnet_id: SubnetId, admin: bool) -> impl Step { + /// If auto-retry is false, the user will be prompted on what to do (skip or continue). In + /// non-interactive recoveries, auto-retry should be set to true. + pub fn get_download_certs_step( + &self, + subnet_id: SubnetId, + admin: bool, + auto_retry: bool, + ) -> impl Step { DownloadCertificationsStep { logger: self.logger.clone(), subnet_id, @@ -312,6 +329,7 @@ impl Recovery { work_dir: self.work_dir.clone(), require_confirmation: self.ssh_confirmation, key_file: self.key_file.clone(), + auto_retry, admin, } } diff --git a/rs/recovery/src/main.rs b/rs/recovery/src/main.rs index 80dc35eb73a..f66ecda26e9 100644 --- a/rs/recovery/src/main.rs +++ b/rs/recovery/src/main.rs @@ -34,6 +34,7 @@ fn main() { key_file: args.key_file, test_mode: args.test, skip_prompts: args.skip_prompts, + use_local_binaries: args.use_local_binaries, }; let recovery_state = cli::read_and_maybe_update_state(&logger, recovery_args, args.subcmd); diff --git a/rs/recovery/src/nns_recovery_failover_nodes.rs b/rs/recovery/src/nns_recovery_failover_nodes.rs index fc7fcae4b60..0a1d7908356 100644 --- a/rs/recovery/src/nns_recovery_failover_nodes.rs +++ b/rs/recovery/src/nns_recovery_failover_nodes.rs @@ -257,10 +257,13 @@ impl RecoveryIterator for NNSRecoveryFailoverNodes { } } - StepType::DownloadCertifications => Ok(Box::new( - self.recovery - .get_download_certs_step(self.params.subnet_id, true), - )), + StepType::DownloadCertifications => { + Ok(Box::new(self.recovery.get_download_certs_step( + self.params.subnet_id, + true, + !self.interactive(), + ))) + } StepType::MergeCertificationPools => { Ok(Box::new(self.recovery.get_merge_certification_pools_step())) diff --git a/rs/recovery/src/nns_recovery_same_nodes.rs b/rs/recovery/src/nns_recovery_same_nodes.rs index ae470547509..60ae170a3a9 100644 --- a/rs/recovery/src/nns_recovery_same_nodes.rs +++ b/rs/recovery/src/nns_recovery_same_nodes.rs @@ -183,10 +183,13 @@ impl RecoveryIterator for NNSRecoverySameNodes { } } - StepType::DownloadCertifications => Ok(Box::new( - self.recovery - .get_download_certs_step(self.params.subnet_id, true), - )), + StepType::DownloadCertifications => { + Ok(Box::new(self.recovery.get_download_certs_step( + self.params.subnet_id, + true, + !self.interactive(), + ))) + } StepType::MergeCertificationPools => { Ok(Box::new(self.recovery.get_merge_certification_pools_step())) diff --git a/rs/recovery/src/recovery_state.rs b/rs/recovery/src/recovery_state.rs index 24cc57df1a2..73f28a7444b 100644 --- a/rs/recovery/src/recovery_state.rs +++ b/rs/recovery/src/recovery_state.rs @@ -177,6 +177,7 @@ mod tests { key_file: Some(PathBuf::from(dir)), test_mode: true, skip_prompts: true, + use_local_binaries: false, }, subcommand_args: SubCommand::AppSubnetRecovery(AppSubnetRecoveryArgs { subnet_id: fake_subnet_id(), diff --git a/rs/recovery/src/steps.rs b/rs/recovery/src/steps.rs index fae2469dfa0..16aeaf57a77 100644 --- a/rs/recovery/src/steps.rs +++ b/rs/recovery/src/steps.rs @@ -66,6 +66,7 @@ pub struct DownloadCertificationsStep { pub registry_helper: RegistryHelper, pub work_dir: PathBuf, pub require_confirmation: bool, + pub auto_retry: bool, pub key_file: Option, pub admin: bool, } @@ -98,9 +99,10 @@ impl Step for DownloadCertificationsStep { &target.display().to_string(), self.require_confirmation, self.key_file.as_ref(), + self.auto_retry, 5, ) - .map_err(|e| warn!(self.logger, "Failed to download certifications: {:?}", e)); + .map_err(|e| warn!(self.logger, "Skipping download: {:?}", e)); success || res.is_ok() }); diff --git a/rs/recovery/subnet_splitting/BUILD.bazel b/rs/recovery/subnet_splitting/BUILD.bazel index 7250b6d7f43..60a21945820 100644 --- a/rs/recovery/subnet_splitting/BUILD.bazel +++ b/rs/recovery/subnet_splitting/BUILD.bazel @@ -64,7 +64,7 @@ rust_binary( rust_test( name = "subnet_splitting_tool_test", + compile_data = ["test_data/fake_expected_manifests.data"], crate = "subnet_splitting", - data = ["test_data/fake_expected_manifests.data"], deps = DEPENDENCIES + DEV_DEPENDENCIES, ) diff --git a/rs/recovery/subnet_splitting/src/main.rs b/rs/recovery/subnet_splitting/src/main.rs index 84777025b6e..48e291a4d0b 100644 --- a/rs/recovery/subnet_splitting/src/main.rs +++ b/rs/recovery/subnet_splitting/src/main.rs @@ -47,6 +47,12 @@ struct SplitArgs { #[clap(long)] pub skip_prompts: bool, + /// Flag to indicate we're running recovery directly on a node, and should use + /// the locally available binaries. If this option is not set, missing binaries + /// will be downloaded. + #[clap(long)] + pub use_local_binaries: bool, + #[clap(flatten)] subnet_splitting_args: SubnetSplittingArgs, } @@ -145,6 +151,7 @@ fn do_split(args: SplitArgs, logger: Logger) -> RecoveryResult<()> { key_file: args.key_file, test_mode: args.test, skip_prompts: args.skip_prompts, + use_local_binaries: args.use_local_binaries, }; let subnet_splitting_state = diff --git a/rs/registry/admin-derive/src/lib.rs b/rs/registry/admin-derive/src/lib.rs index 431729f4b6a..aa58d1766f2 100644 --- a/rs/registry/admin-derive/src/lib.rs +++ b/rs/registry/admin-derive/src/lib.rs @@ -23,7 +23,7 @@ fn impl_proposal_metadata(ast: &syn::DeriveInput) -> TokenStream { } fn url(&self) -> String { - parse_proposal_url(self.proposal_url.clone()) + parse_proposal_url(&self.proposal_url) } fn proposer_and_sender(&self, sender: Sender) -> (NeuronId, Sender) { diff --git a/rs/registry/admin/src/helpers.rs b/rs/registry/admin/src/helpers.rs index 0c594a66e6e..180fd7ef130 100644 --- a/rs/registry/admin/src/helpers.rs +++ b/rs/registry/admin/src/helpers.rs @@ -62,7 +62,7 @@ pub(crate) fn summary_from_string_or_file( } /// Parses the URL of a proposal. -pub(crate) fn parse_proposal_url(url: Option) -> String { +pub(crate) fn parse_proposal_url(url: &Option) -> String { match url { Some(url) => { if url.scheme() != "https" { diff --git a/rs/registry/admin/src/main.rs b/rs/registry/admin/src/main.rs index f254b3d677b..825deaa928e 100644 --- a/rs/registry/admin/src/main.rs +++ b/rs/registry/admin/src/main.rs @@ -76,8 +76,8 @@ use ic_protobuf::registry::{ crypto::v1::{PublicKey, X509PublicKeyCert}, dc::v1::{AddOrRemoveDataCentersProposalPayload, DataCenterRecord}, firewall::v1::{FirewallConfig, FirewallRule, FirewallRuleSet}, - node::v1::NodeRecord, - node_operator::v1::{NodeOperatorRecord, RemoveNodeOperatorsPayload}, + node::v1::{NodeRecord, NodeRewardType}, + node_operator::v1::NodeOperatorRecord, node_rewards::v2::{NodeRewardRate, UpdateNodeRewardsTableProposalPayload}, provisional_whitelist::v1::ProvisionalWhitelist as ProvisionalWhitelistProto, replica_version::v1::{BlessedReplicaVersions, ReplicaVersionRecord}, @@ -99,8 +99,8 @@ use ic_registry_keys::{ make_node_operator_record_key, make_node_record_key, make_provisional_whitelist_record_key, make_replica_version_key, make_routing_table_record_key, make_subnet_list_record_key, make_subnet_record_key, make_unassigned_nodes_config_record_key, FirewallRulesScope, - API_BOUNDARY_NODE_RECORD_KEY_PREFIX, NODE_OPERATOR_RECORD_KEY_PREFIX, NODE_REWARDS_TABLE_KEY, - ROOT_SUBNET_ID_KEY, + API_BOUNDARY_NODE_RECORD_KEY_PREFIX, NODE_OPERATOR_RECORD_KEY_PREFIX, NODE_RECORD_KEY_PREFIX, + NODE_REWARDS_TABLE_KEY, ROOT_SUBNET_ID_KEY, }; use ic_registry_local_store::{ Changelog, ChangelogEntry, KeyMutation, LocalStoreImpl, LocalStoreWriter, @@ -134,6 +134,7 @@ use registry_canister::mutations::{ do_deploy_guestos_to_all_subnet_nodes::DeployGuestosToAllSubnetNodesPayload, do_deploy_guestos_to_all_unassigned_nodes::DeployGuestosToAllUnassignedNodesPayload, do_remove_api_boundary_nodes::RemoveApiBoundaryNodesPayload, + do_remove_node_operators::RemoveNodeOperatorsPayload, do_revise_elected_replica_versions::ReviseElectedGuestosVersionsPayload, do_set_firewall_config::SetFirewallConfigPayload, do_update_api_boundary_nodes_version::DeployGuestosToSomeApiBoundaryNodes, @@ -193,7 +194,7 @@ const IC_DOMAINS: &[&str; 3] = &["ic0.app", "icp0.io", "icp-api.io"]; #[derive(Parser)] #[clap(version = "1.0")] struct Opts { - #[clap(short = 'r', long, aliases = &["registry-url", "nns-url"], value_delimiter = ',', global = true)] + #[clap(short = 'r', long, aliases = &["registry-url", "nns-url"], value_delimiter = ',', global = true, default_value = "https://ic0.app")] /// The URL of an NNS entry point. That is, the URL of any replica on the /// NNS subnet. nns_urls: Vec, @@ -207,14 +208,15 @@ struct Opts { subcmd: SubCommand, /// Use an HSM to sign calls. - #[clap(long, global = true)] + #[clap(long, global = true, requires_all = &["hsm_slot", "hsm_key_id", "hsm_pin"])] use_hsm: bool, /// The slot related to the HSM key that shall be used. #[clap( long = "slot", - help = "Only required if use-hsm is set. Ignored otherwise.", - global = true + help = "Required if use-hsm is set.", + global = true, + requires = "use_hsm" )] hsm_slot: Option, @@ -222,17 +224,21 @@ struct Opts { #[clap( long = "key-id", help = "Only required if use-hsm is set. Ignored otherwise.", - global = true + global = true, + requires = "use_hsm", + visible_alias = "key-id" )] - key_id: Option, + hsm_key_id: Option, /// The PIN used to unlock the HSM. #[clap( long = "pin", help = "Only required if use-hsm is set. Ignored otherwise.", - global = true + global = true, + requires = "use_hsm", + visible_alias = "pin" )] - pin: Option, + hsm_pin: Option, /// Verify NNS responses against NNS public key. #[clap( @@ -258,205 +264,267 @@ struct Opts { silence_notices: bool, } +//////////////////////////////////////////////////////////////////////////////// +// NOTE: Please keep the sub-commands in alphabetical order. +//////////////////////////////////////////////////////////////////////////////// /// List of sub-commands accepted by `ic-admin`. #[allow(clippy::large_enum_variant)] #[derive(clap::Subcommand)] enum SubCommand { - /// Get the last version of a node's public key from the registry. - GetPublicKey(GetPublicKeyCmd), - /// Get the last version of a node's TLS certificate key from the registry. - GetTlsCertificate(GetTlsCertificateCmd), - /// Submits a proposal to change node membership in a subnet. - /// Consider using instead the DRE tool to submit this type of proposals. - /// https://github.com/dfinity/dre - ProposeToChangeSubnetMembership(ProposeToChangeSubnetMembershipCmd), + /// Convert the integer node ID into Principal Id + ConvertNumericNodeIdToPrincipalId(ConvertNumericNodeIdtoPrincipalIdCmd), + + /// Sub-command to fetch an API Boundary Node record from the registry. + /// Retrieve an API Boundary Node record + GetApiBoundaryNode(GetApiBoundaryNodeCmd), + + /// Retrieve all API Boundary Node Ids + GetApiBoundaryNodes, + + /// Get the latest canister migrations. + GetCanisterMigrations, + + /// Get the Master public key ids and their signing subnets + GetChainKeySigningSubnets, + + /// Get a DataCenterRecord + GetDataCenter(GetDataCenterCmd), + + /// Get the ECDSA key ids and their signing subnets + GetEcdsaSigningSubnets, + + /// Get the current list of elected GuestOS/Replica versions. + #[clap(visible_alias = "get-blessed-replica-versions")] + GetElectedGuestosVersions, + + /// Get the current list of elected HostOS versions + GetElectedHostosVersions, + + /// Get the current firewall config + GetFirewallConfig, + + /// Get the existing firewall rules for a given scope + GetFirewallRules(GetFirewallRulesCmd), + + /// Get the existing firewall rules that apply to a given node + GetFirewallRulesForNode(GetFirewallRulesForNodeCmd), + + /// Compute the SHA-256 hash of a given list of firewall rules + GetFirewallRulesetHash(GetFirewallRulesetHashCmd), + + /// Get the monthly Node Provider rewards + GetMonthlyNodeProviderRewards, + /// Get the last version of a node from the registry. GetNode(GetNodeCmd), + /// Get the nodes added since a given version (exclusive). GetNodeListSince(GetNodeListSinceCmd), - /// Get the topology of the system as described in the registry, in JSON - /// format. - GetTopology, - /// Get the last version of a subnet from the registry. - GetSubnet(GetSubnetCmd), - /// Get the last version of the subnet list from the registry. - GetSubnetList, + + /// Get a node operator's record + GetNodeOperator(GetNodeOperatorCmd), + + /// Get the list of all node operators + GetNodeOperatorList, + + /// Get the node rewards table + GetNodeRewardsTable, + + // Get the pending proposals to upgrade the governance canister. + GetPendingRootProposalsToUpgradeGovernanceCanister, + + /// Get whitelist of principals that can access the provisional_* APIs in + /// the management canister. + GetProvisionalWhitelist, + + /// Get the last version of a node's public key from the registry. + GetPublicKey(GetPublicKeyCmd), + + // Get latest registry version number + GetRegistryVersion, + /// Get info about a Replica version GetReplicaVersion(GetReplicaVersionCmd), - /// Deprecated. Please use `ProposeToDeployGuestosToAllSubnetNodes` instead. - ProposeToUpdateSubnetReplicaVersion(ProposeToDeployGuestosToAllSubnetNodesCmd), - /// Propose to deploy a priorly elected GuestOS version to all subnet nodes. - ProposeToDeployGuestosToAllSubnetNodes(ProposeToDeployGuestosToAllSubnetNodesCmd), - /// Get the list of blessed Replica versions. - GetBlessedReplicaVersions, + /// Get the latest routing table. GetRoutingTable, - /// Deprecated. Please use `ProposeToReviseElectedGuestosVersions` instead. - ProposeToUpdateElectedReplicaVersions(ProposeToReviseElectedGuestssVersionsCmd), - /// Submits a proposal to change the set of currently elected GuestOS versions, by electing - /// a new version and/or unelecting multiple priorly elected versions. - ProposeToReviseElectedGuestosVersions(ProposeToReviseElectedGuestssVersionsCmd), - /// Submits a proposal to create a new subnet. - ProposeToCreateSubnet(ProposeToCreateSubnetCmd), - /// Submits a proposal to create a new service nervous system (usually referred to as SNS). - ProposeToCreateServiceNervousSystem(ProposeToCreateServiceNervousSystemCmd), - /// Submits a proposal to update a subnet's recovery CUP - ProposeToUpdateRecoveryCup(ProposeToUpdateRecoveryCupCmd), - /// Submits a proposal to update an existing subnet's configuration. - ProposeToUpdateSubnet(ProposeToUpdateSubnetCmd), + + /// Get the last version of a subnet from the registry. + GetSubnet(GetSubnetCmd), + + /// Get the last version of the subnet list from the registry. + GetSubnetList, + + /// Get the public of the subnet. + GetSubnetPublicKey(SubnetPublicKeyCmd), + + /// Get the last version of a node's TLS certificate key from the registry. + GetTlsCertificate(GetTlsCertificateCmd), + + /// Get the topology of the system as described in the registry, in JSON + /// format. + GetTopology, + + /// Get the SSH key access lists for unassigned nodes + GetUnassignedNodes, + + /// Propose to add an API Boundary Node + ProposeToAddApiBoundaryNodes(ProposeToAddApiBoundaryNodesCmd), + + /// Propose to add firewall rules + ProposeToAddFirewallRules(ProposeToAddFirewallRulesCmd), + + /// Submits a proposal to add a new canister on NNS. + ProposeToAddNnsCanister(ProposeToAddNnsCanisterCmd), + + /// Propose to add a new node operator to the registry. + ProposeToAddNodeOperator(ProposeToAddNodeOperatorCmd), + + /// Submit a proposal to add data centers and/or remove data centers from the Registry + ProposeToAddOrRemoveDataCenters(ProposeToAddOrRemoveDataCentersCmd), + + /// Propose to add or remove a node provider from the governance canister + ProposeToAddOrRemoveNodeProvider(ProposeToAddOrRemoveNodeProviderCmd), + + /// Submits a proposal to add an SNS wasm (e.g. Governance, Ledger, etc) to the SNS-WASM NNS + /// canister. + ProposeToAddWasmToSnsWasm(ProposeToAddWasmToSnsWasmCmd), + /// Submits a proposal to change an existing canister on NNS. ProposeToChangeNnsCanister(ProposeToChangeNnsCanisterCmd), - /// Submits a proposal to uninstall and install root to a particular version - ProposeToHardResetNnsRootToVersion(ProposeToHardResetNnsRootToVersionCmd), - /// Submits a proposal to uninstall code of a canister. - ProposeToUninstallCode(ProposeToUninstallCodeCmd), - /// Submits a proposal to set authorized subnetworks that the cycles minting - /// canister can use. - ProposeToSetAuthorizedSubnetworks(ProposeToSetAuthorizedSubnetworksCmd), - /// Submits a proposal to update the subnet types that are available in the - /// cycles minting canister. - ProposeToUpdateSubnetType(ProposeToUpdateSubnetTypeCmd), + + /// Submits a proposal to change node membership in a subnet. + /// Consider using instead the DRE tool to submit this type of proposals. + /// https://github.com/dfinity/dre + ProposeToChangeSubnetMembership(ProposeToChangeSubnetMembershipCmd), + /// Submits a proposal to add or remove subnets from a subnet type in the /// cycles minting canister. ProposeToChangeSubnetTypeAssignment(ProposeToChangeSubnetTypeAssignmentCmd), - /// Submits a proposal to add a new canister on NNS. - ProposeToAddNnsCanister(ProposeToAddNnsCanisterCmd), - /// Convert the integer node ID into Principal Id - ConvertNumericNodeIdToPrincipalId(ConvertNumericNodeIdtoPrincipalIdCmd), - /// Get whitelist of principals that can access the provisional_* APIs in - /// the management canister. - GetProvisionalWhitelist, - /// Get the public of the subnet. - GetSubnetPublicKey(SubnetPublicKeyCmd), - /// Propose to add a new node operator to the registry. - ProposeToAddNodeOperator(ProposeToAddNodeOperatorCmd), - /// Get a node operator's record - GetNodeOperator(GetNodeOperatorCmd), - /// Get the list of all node operators - GetNodeOperatorList, - /// Update local registry store by pulling from remote URL - UpdateRegistryLocalStore(UpdateRegistryLocalStoreCmd), + /// Update the whitelist of principals that can access the provisional_* /// APIs in the management canister. ProposeToClearProvisionalWhitelist(ProposeToClearProvisionalWhitelistCmd), - /// Update the Node Operator's specified parameters - ProposeToUpdateNodeOperatorConfig(ProposeToUpdateNodeOperatorConfigCmd), - /// Get the current firewall config - GetFirewallConfig, - /// Propose to set the firewall config - ProposeToSetFirewallConfig(ProposeToSetFirewallConfigCmd), - /// Propose to add firewall rules - ProposeToAddFirewallRules(ProposeToAddFirewallRulesCmd), - /// Propose to remove firewall rules - ProposeToRemoveFirewallRules(ProposeToRemoveFirewallRulesCmd), - /// Propose to update firewall rules - ProposeToUpdateFirewallRules(ProposeToUpdateFirewallRulesCmd), - /// Get the existing firewall rules for a given scope - GetFirewallRules(GetFirewallRulesCmd), - /// Get the existing firewall rules that apply to a given node - GetFirewallRulesForNode(GetFirewallRulesForNodeCmd), - /// Compute the SHA-256 hash of a given list of firewall rules - GetFirewallRulesetHash(GetFirewallRulesetHashCmd), - /// Propose to remove a node from the registry via proposal. - ProposeToRemoveNodes(ProposeToRemoveNodesCmd), - /// Propose to add or remove a node provider from the governance canister - ProposeToAddOrRemoveNodeProvider(ProposeToAddOrRemoveNodeProviderCmd), - // Get latest registry version number - GetRegistryVersion, - // Submit a root proposal to the root canister to upgrade the governance canister. - SubmitRootProposalToUpgradeGovernanceCanister(SubmitRootProposalToUpgradeGovernanceCanisterCmd), - // Get the pending proposals to upgrade the governance canister. - GetPendingRootProposalsToUpgradeGovernanceCanister, - // Vote on a pending root proposal to upgrade the governance canister. - VoteOnRootProposalToUpgradeGovernanceCanister(VoteOnRootProposalToUpgradeGovernanceCanisterCmd), - /// Get a DataCenterRecord - GetDataCenter(GetDataCenterCmd), - /// Submit a proposal to add data centers and/or remove data centers from - /// the Registry - ProposeToAddOrRemoveDataCenters(ProposeToAddOrRemoveDataCentersCmd), - /// Get the node rewards table - GetNodeRewardsTable, - /// Submit a proposal to update the node rewards table - ProposeToUpdateNodeRewardsTable(ProposeToUpdateNodeRewardsTableCmd), - /// Submit a proposal to update the unassigned nodes. This subcommand is obsolete; please use - /// `ProposeToDeployGuestosToAllUnassignedNodes` or `ProposeToUpdateSshReadonlyAccessForAllUnassignedNodes` instead. - ProposeToUpdateUnassignedNodesConfig(ProposeToUpdateUnassignedNodesConfigCmd), + + /// Propose to remove entries from `canister_migrations`. Step 3 of canister migration. + ProposeToCompleteCanisterMigration(ProposeToCompleteCanisterMigrationCmd), + + /// Submits a proposal to create a new service nervous system (usually referred to as SNS). + ProposeToCreateServiceNervousSystem(ProposeToCreateServiceNervousSystemCmd), + + /// Submits a proposal to create a new subnet. + ProposeToCreateSubnet(ProposeToCreateSubnetCmd), + + /// Propose to deploy a priorly elected GuestOS version to all subnet nodes. + ProposeToDeployGuestosToAllSubnetNodes(ProposeToDeployGuestosToAllSubnetNodesCmd), + /// Propose to deploy the GuestOS version to all unassigned nodes. ProposeToDeployGuestosToAllUnassignedNodes(ProposeToDeployGuestosToAllUnassignedNodesCmd), - /// Propose to update the SSH keys that have read-only access to all unassigned nodes. - ProposeToUpdateSshReadonlyAccessForAllUnassignedNodes( - ProposeToUpdateSshReadonlyAccessForAllUnassignedNodesCmd, - ), - /// Get the SSH key access lists for unassigned nodes - GetUnassignedNodes, - /// Get the monthly Node Provider rewards - GetMonthlyNodeProviderRewards, - /// Propose Xdr/Icp conversion rate. - ProposeXdrIcpConversionRate(ProposeXdrIcpConversionRateCmd), - /// Propose to start a canister managed by the governance. - ProposeToStartCanister(StartCanisterCmd), - /// Propose to stop a canister managed by the governance. - ProposeToStopCanister(StopCanisterCmd), + + /// Propose to upgrade the GuestOS version of a set of API Boundary Nodes. + ProposeToDeployGuestosToSomeApiBoundaryNodes(ProposeToDeployGuestosToSomeApiBoundaryNodesCmd), + + /// Propose to deploy a HostOS version to some nodes. + ProposeToDeployHostosToSomeNodes(ProposeToDeployHostosToSomeNodesCmd), + + /// Submits a proposal to uninstall and install root to a particular version + ProposeToHardResetNnsRootToVersion(ProposeToHardResetNnsRootToVersionCmd), + + // Submits a proposal to add custom upgrade path entries + ProposeToInsertSnsWasmUpgradePathEntries(ProposeToInsertSnsWasmUpgradePathEntriesCmd), + + /// Propose additions or updates to `canister_migrations`. Step 1 of canister migration. + ProposeToPrepareCanisterMigration(ProposeToPrepareCanisterMigrationCmd), + + /// Propose to remove a set of API Boundary Nodes + ProposeToRemoveApiBoundaryNodes(ProposeToRemoveApiBoundaryNodesCmd), + + /// Propose to remove firewall rules + ProposeToRemoveFirewallRules(ProposeToRemoveFirewallRulesCmd), + /// Propose to remove a list of node operators from the Registry ProposeToRemoveNodeOperators(ProposeToRemoveNodeOperatorsCmd), + + /// Propose to remove a node from the registry via proposal. + ProposeToRemoveNodes(ProposeToRemoveNodesCmd), + + /// Submits a proposal to express the interest in renting a subnet. + ProposeToRentSubnet(ProposeToRentSubnetCmd), + /// Propose to modify the routing table. Step 2 of canister migration. ProposeToRerouteCanisterRanges(ProposeToRerouteCanisterRangesCmd), - /// Propose additions or updates to `canister_migrations`. Step 1 of canister migration. - ProposeToPrepareCanisterMigration(ProposeToPrepareCanisterMigrationCmd), - /// Propose to remove entries from `canister_migrations`. Step 3 of canister migration. - ProposeToCompleteCanisterMigration(ProposeToCompleteCanisterMigrationCmd), - /// Get the latest canister migrations. - GetCanisterMigrations, - /// Submits a proposal to add an SNS wasm (e.g. Governance, Ledger, etc) to the SNS-WASM NNS - /// canister. - ProposeToAddWasmToSnsWasm(ProposeToAddWasmToSnsWasmCmd), - // Submits a proposal to add custom upgrade path entries - ProposeToInsertSnsWasmUpgradePathEntries(ProposeToInsertSnsWasmUpgradePathEntriesCmd), - /// Get the ECDSA key ids and their signing subnets - GetEcdsaSigningSubnets, - /// Get the Master public key ids and their signing subnets - GetChainKeySigningSubnets, - /// Propose to update the list of SNS Subnet IDs that SNS-WASM deploys SNS instances to - ProposeToUpdateSnsSubnetIdsInSnsWasm(ProposeToUpdateSnsSubnetIdsInSnsWasmCmd), - /// Propose to update the list of Principals that are allowed to deploy SNS instances - ProposeToUpdateSnsDeployWhitelist(ProposeToUpdateSnsDeployWhitelistCmd), - /// Propose to start a decentralization swap. This subcommand is obsolete; please use - /// `ProposeToCreateServiceNervousSystem` instead. - ProposeToOpenSnsTokenSwap(ProposeToOpenSnsTokenSwap), - /// Propose to set the Bitcoin configuration - ProposeToSetBitcoinConfig(ProposeToSetBitcoinConfig), - /// Submits a proposal to change the set of currently elected HostOS versions, by electing - /// a new version and/or unelecting multiple versions. This subcommand is obsolete; please use - /// `ProposeToReviseElectedHostosVersions` instead. - ProposeToUpdateElectedHostosVersions(ProposeToUpdateElectedHostosVersionsCmd), + + /// Submits a proposal to change the set of currently elected GuestOS versions, by electing + /// a new version and/or unelecting multiple priorly elected versions. + ProposeToReviseElectedGuestosVersions(ProposeToReviseElectedGuestsOsVersionsCmd), + /// Submits a proposal to change the set of currently elected HostOS versions, by electing /// a new version and/or unelecting multiple versions. ProposeToReviseElectedHostosVersions(ProposeToReviseElectedHostosVersionsCmd), - /// Set or remove a HostOS version on Nodes. This subcommand is obsolete; please use - /// `ProposeToDeployHostosToSomeNodes` instead. - ProposeToUpdateNodesHostosVersion(ProposeToUpdateNodesHostosVersionCmd), - /// Propose to deploy a HostOS version to some nodes. - ProposeToDeployHostosToSomeNodes(ProposeToDeployHostosToSomeNodesCmd), - /// Get current list of elected HostOS versions - GetElectedHostosVersions, - /// Propose to add an API Boundary Node - ProposeToAddApiBoundaryNodes(ProposeToAddApiBoundaryNodesCmd), - /// Propose to remove a set of API Boundary Nodes - ProposeToRemoveApiBoundaryNodes(ProposeToRemoveApiBoundaryNodesCmd), - /// Propose to update the version of a set of API Boundary Nodes. This subcommand is obsolete; please use - /// `ProposeToDeployGuestosToSomeApiBoundaryNodes` instead. - ProposeToUpdateApiBoundaryNodesVersion(ProposeToUpdateApiBoundaryNodesVersionCmd), - /// Propose to upgrade the GuestOS version of a set of API Boundary Nodes. - ProposeToDeployGuestosToSomeApiBoundaryNodes(ProposeToDeployGuestosToSomeApiBoundaryNodesCmd), - /// Sub-command to fetch an API Boundary Node record from the registry. - /// Retrieve an API Boundary Node record - GetApiBoundaryNode(GetApiBoundaryNodeCmd), - /// Retrieve all API Boundary Node Ids - GetApiBoundaryNodes, - /// Submits a proposal to express the interest in renting a subnet. - ProposeToRentSubnet(ProposeToRentSubnetCmd), + + /// Submits a proposal to set authorized subnetworks that the cycles minting + /// canister can use. + ProposeToSetAuthorizedSubnetworks(ProposeToSetAuthorizedSubnetworksCmd), + + /// Propose to set the Bitcoin configuration + ProposeToSetBitcoinConfig(ProposeToSetBitcoinConfig), + + /// Propose to set the firewall config + ProposeToSetFirewallConfig(ProposeToSetFirewallConfigCmd), + + /// Propose to start a canister managed by the governance. + ProposeToStartCanister(StartCanisterCmd), + + /// Propose to stop a canister managed by the governance. + ProposeToStopCanister(StopCanisterCmd), + + /// Submits a proposal to uninstall code of a canister. + ProposeToUninstallCode(ProposeToUninstallCodeCmd), + /// Propose to update the settings of a canister. ProposeToUpdateCanisterSettings(ProposeToUpdateCanisterSettingsCmd), + + /// Propose to update firewall rules + ProposeToUpdateFirewallRules(ProposeToUpdateFirewallRulesCmd), + + /// Update the Node Operator's specified parameters + ProposeToUpdateNodeOperatorConfig(ProposeToUpdateNodeOperatorConfigCmd), + + /// Submit a proposal to update the node rewards table + ProposeToUpdateNodeRewardsTable(ProposeToUpdateNodeRewardsTableCmd), + + /// Submits a proposal to update a subnet's recovery CUP + ProposeToUpdateRecoveryCup(ProposeToUpdateRecoveryCupCmd), + + /// Propose to update the list of Principals that are allowed to deploy SNS instances + ProposeToUpdateSnsDeployWhitelist(ProposeToUpdateSnsDeployWhitelistCmd), + + /// Propose to update the list of SNS Subnet IDs that SNS-WASM deploys SNS instances to + ProposeToUpdateSnsSubnetIdsInSnsWasm(ProposeToUpdateSnsSubnetIdsInSnsWasmCmd), + + /// Propose to update the SSH keys that have read-only access to all unassigned nodes. + ProposeToUpdateSshReadonlyAccessForAllUnassignedNodes( + ProposeToUpdateSshReadonlyAccessForAllUnassignedNodesCmd, + ), + + /// Submits a proposal to update an existing subnet's configuration. + ProposeToUpdateSubnet(ProposeToUpdateSubnetCmd), + + /// Submits a proposal to update the subnet types that are available in the + /// cycles minting canister. + ProposeToUpdateSubnetType(ProposeToUpdateSubnetTypeCmd), + + /// Propose Xdr/Icp conversion rate. + ProposeToUpdateXdrIcpConversionRate(ProposeXdrIcpConversionRateCmd), + + // Submit a root proposal to the root canister to upgrade the governance canister. + SubmitRootProposalToUpgradeGovernanceCanister(SubmitRootProposalToUpgradeGovernanceCanisterCmd), + + /// Update local registry store by pulling from remote URL + UpdateRegistryLocalStore(UpdateRegistryLocalStoreCmd), + + // Vote on a pending root proposal to upgrade the governance canister. + VoteOnRootProposalToUpgradeGovernanceCanister(VoteOnRootProposalToUpgradeGovernanceCanisterCmd), } /// Indicates whether a value should be added or removed. @@ -634,14 +702,7 @@ impl ProposalTitle for ProposeToRemoveNodeOperatorsCmd { #[async_trait] impl ProposalPayload for ProposeToRemoveNodeOperatorsCmd { async fn payload(&self, _: &Agent) -> RemoveNodeOperatorsPayload { - RemoveNodeOperatorsPayload { - node_operators_to_remove: self - .node_operators_to_remove - .clone() - .iter() - .map(|x| x.to_vec()) - .collect(), - } + RemoveNodeOperatorsPayload::new(self.node_operators_to_remove.clone()) } } @@ -672,7 +733,7 @@ impl ProposalPayload } } -/// Obsolete; please use `ProposeToDeployGuestosToAllUnassignedNodes` or +/// Deprecated; please use `ProposeToDeployGuestosToAllUnassignedNodes` or /// `ProposeToUpdateSshReadonlyAccessForAllUnassignedNodes` instead. #[derive_common_proposal_fields] #[derive(Clone, Parser, ProposalMetadata)] @@ -866,7 +927,7 @@ impl ProposalAction for StopCanisterCmd { /// Sub-command to submit a proposal to update elected replica versions. #[derive_common_proposal_fields] #[derive(Parser, ProposalMetadata)] -struct ProposeToReviseElectedGuestssVersionsCmd { +struct ProposeToReviseElectedGuestsOsVersionsCmd { #[clap(long)] /// The replica version ID to elect. pub replica_version_to_elect: Option, @@ -886,7 +947,7 @@ struct ProposeToReviseElectedGuestssVersionsCmd { pub replica_versions_to_unelect: Vec, } -impl ProposalTitle for ProposeToReviseElectedGuestssVersionsCmd { +impl ProposalTitle for ProposeToReviseElectedGuestsOsVersionsCmd { fn title(&self) -> String { match &self.proposal_title { Some(title) => title.clone(), @@ -900,7 +961,7 @@ impl ProposalTitle for ProposeToReviseElectedGuestssVersionsCmd { #[async_trait] impl ProposalPayload - for ProposeToReviseElectedGuestssVersionsCmd + for ProposeToReviseElectedGuestsOsVersionsCmd { async fn payload(&self, _: &Agent) -> ReviseElectedGuestosVersionsPayload { let payload = ReviseElectedGuestosVersionsPayload { @@ -1022,6 +1083,7 @@ impl ProposalPayload for ProposeToChangeNnsCanisterCmd { arg, compute_allocation: self.compute_allocation.map(candid::Nat::from), memory_allocation: self.memory_allocation.map(candid::Nat::from), + chunked_canister_wasm: None, } } } @@ -1629,7 +1691,7 @@ struct ProposeToUpdateSnsDeployWhitelistCmd { pub removed_principals: Vec, } -/// Obsolete; please use `CreateServiceNervousSystem` instead. +/// Deprecated; please use `CreateServiceNervousSystem` instead. #[derive_common_proposal_fields] #[derive(Clone, Parser, ProposalMetadata)] struct ProposeToOpenSnsTokenSwap {} @@ -3184,7 +3246,7 @@ async fn propose_to_create_service_nervous_system( )); let title = cmd.title(); let summary = cmd.summary.clone().unwrap(); - let url = parse_proposal_url(cmd.proposal_url.clone()); + let url = parse_proposal_url(&cmd.proposal_url); let proposal = MakeProposalRequest { title: Some(title.clone()), summary, @@ -3272,11 +3334,6 @@ impl ProposalPayload } } -/// Obsolete; please use `ProposeToDeployHostosToSomeNodes` instead. -#[derive_common_proposal_fields] -#[derive(Parser, ProposalMetadata)] -struct ProposeToUpdateNodesHostosVersionCmd {} - /// Sub-command to deploy a HostOS version to a set of nodes. #[derive_common_proposal_fields] #[derive(Parser, ProposalMetadata)] @@ -3421,11 +3478,6 @@ impl ProposalPayload for ProposeToRemoveApiBounda } } -/// Obsolete; please use `ProposeToDeployGuestosToSomeApiBoundaryNodes` instead. -#[derive_common_proposal_fields] -#[derive(Clone, Parser, ProposalMetadata)] -struct ProposeToUpdateApiBoundaryNodesVersionCmd {} - #[derive_common_proposal_fields] #[derive(Parser, ProposalMetadata)] struct ProposeToDeployGuestosToSomeApiBoundaryNodesCmd { @@ -3609,77 +3661,54 @@ async fn main() { // // TODO(NNS1-486): Remove ic-admin command whitelist for sender match opts.subcmd { - SubCommand::ProposeToDeployGuestosToAllSubnetNodes(_) => (), - SubCommand::ProposeToUpdateSubnetReplicaVersion(_) => (), - SubCommand::ProposeToCreateSubnet(_) => (), - SubCommand::ProposeToRemoveNodes(_) => (), - SubCommand::ProposeToChangeSubnetMembership(_) => (), - SubCommand::ProposeToChangeNnsCanister(_) => (), - SubCommand::ProposeToHardResetNnsRootToVersion(_) => (), - SubCommand::ProposeToUninstallCode(_) => (), - SubCommand::ProposeToAddNnsCanister(_) => (), - SubCommand::ProposeToReviseElectedGuestosVersions(_) => (), - SubCommand::ProposeToUpdateElectedReplicaVersions(_) => (), - SubCommand::ProposeToUpdateSubnet(_) => (), - SubCommand::ProposeToClearProvisionalWhitelist(_) => (), - SubCommand::ProposeToUpdateRecoveryCup(_) => (), - SubCommand::ProposeToUpdateNodeOperatorConfig(_) => (), - SubCommand::ProposeToSetFirewallConfig(_) => (), + SubCommand::ProposeToAddApiBoundaryNodes(_) => (), SubCommand::ProposeToAddFirewallRules(_) => (), - SubCommand::ProposeToRemoveFirewallRules(_) => (), - SubCommand::ProposeToUpdateFirewallRules(_) => (), - SubCommand::ProposeToSetAuthorizedSubnetworks(_) => (), - SubCommand::ProposeToUpdateSubnetType(_) => (), - SubCommand::ProposeToChangeSubnetTypeAssignment(_) => (), - SubCommand::ProposeToAddOrRemoveNodeProvider(_) => (), - SubCommand::SubmitRootProposalToUpgradeGovernanceCanister(_) => (), - SubCommand::VoteOnRootProposalToUpgradeGovernanceCanister(_) => (), - SubCommand::ProposeToAddOrRemoveDataCenters(_) => (), - SubCommand::ProposeToUpdateNodeRewardsTable(_) => (), - SubCommand::ProposeToUpdateUnassignedNodesConfig(_) => panic!( - "Subcommand ProposeToUpdateUnassignedNodesConfig is obsolete; please use \ - ProposeToDeployGuestosToAllUnassignedNodesCmd or \ - ProposeToUpdateSshReadonlyAccessForAllUnassignedNodes instead" - ), - SubCommand::ProposeToDeployGuestosToAllUnassignedNodes(_) => (), - SubCommand::ProposeToUpdateSshReadonlyAccessForAllUnassignedNodes(_) => (), + SubCommand::ProposeToAddNnsCanister(_) => (), SubCommand::ProposeToAddNodeOperator(_) => (), - SubCommand::ProposeToRemoveNodeOperators(_) => (), + SubCommand::ProposeToAddOrRemoveDataCenters(_) => (), + SubCommand::ProposeToAddOrRemoveNodeProvider(_) => (), SubCommand::ProposeToAddWasmToSnsWasm(_) => (), - SubCommand::ProposeToPrepareCanisterMigration(_) => (), + SubCommand::ProposeToChangeNnsCanister(_) => (), + SubCommand::ProposeToChangeSubnetMembership(_) => (), + SubCommand::ProposeToChangeSubnetTypeAssignment(_) => (), + SubCommand::ProposeToClearProvisionalWhitelist(_) => (), SubCommand::ProposeToCompleteCanisterMigration(_) => (), - SubCommand::ProposeToStopCanister(_) => (), - SubCommand::ProposeToStartCanister(_) => (), - SubCommand::ProposeToRerouteCanisterRanges(_) => (), - SubCommand::ProposeXdrIcpConversionRate(_) => (), - SubCommand::ProposeToUpdateSnsSubnetIdsInSnsWasm(_) => (), - SubCommand::ProposeToUpdateSnsDeployWhitelist(_) => (), - SubCommand::ProposeToInsertSnsWasmUpgradePathEntries(_) => (), - SubCommand::ProposeToUpdateElectedHostosVersions(_) => panic!( - "Subcommand ProposeToUpdateElectedHostosVersions is obsolete; please use \ - ProposeToReviseElectedHostosVersions instead" - ), - SubCommand::ProposeToReviseElectedHostosVersions(_) => (), - SubCommand::ProposeToUpdateNodesHostosVersion(_) => panic!( - "Subcommand ProposeToUpdateNodesHostosVersion is obsolete; please use \ - ProposeToDeployHostosToSomeNodes instead" - ), - SubCommand::ProposeToDeployHostosToSomeNodes(_) => (), SubCommand::ProposeToCreateServiceNervousSystem(_) => (), - SubCommand::ProposeToSetBitcoinConfig(_) => (), - SubCommand::ProposeToAddApiBoundaryNodes(_) => (), - SubCommand::ProposeToRemoveApiBoundaryNodes(_) => (), - SubCommand::ProposeToUpdateApiBoundaryNodesVersion(_) => panic!( - "Subcommand ProposeToUpdateApiBoundaryNodesVersion is obsolete; please use \ - ProposeToDeployGuestosToSomeApiBoundaryNodes instead" - ), + SubCommand::ProposeToCreateSubnet(_) => (), + SubCommand::ProposeToDeployGuestosToAllSubnetNodes(_) => (), + SubCommand::ProposeToDeployGuestosToAllUnassignedNodes(_) => (), SubCommand::ProposeToDeployGuestosToSomeApiBoundaryNodes(_) => (), - SubCommand::ProposeToOpenSnsTokenSwap(_) => panic!( - "Subcommand OpenSnsTokenSwap is obsolete; please use \ - ProposeToCreateServiceNervousSystem instead" - ), + SubCommand::ProposeToDeployHostosToSomeNodes(_) => (), + SubCommand::ProposeToHardResetNnsRootToVersion(_) => (), + SubCommand::ProposeToInsertSnsWasmUpgradePathEntries(_) => (), + SubCommand::ProposeToPrepareCanisterMigration(_) => (), + SubCommand::ProposeToRemoveApiBoundaryNodes(_) => (), + SubCommand::ProposeToRemoveFirewallRules(_) => (), + SubCommand::ProposeToRemoveNodeOperators(_) => (), + SubCommand::ProposeToRemoveNodes(_) => (), SubCommand::ProposeToRentSubnet(_) => (), + SubCommand::ProposeToRerouteCanisterRanges(_) => (), + SubCommand::ProposeToReviseElectedGuestosVersions(_) => (), + SubCommand::ProposeToReviseElectedHostosVersions(_) => (), + SubCommand::ProposeToSetAuthorizedSubnetworks(_) => (), + SubCommand::ProposeToSetBitcoinConfig(_) => (), + SubCommand::ProposeToSetFirewallConfig(_) => (), + SubCommand::ProposeToStartCanister(_) => (), + SubCommand::ProposeToStopCanister(_) => (), + SubCommand::ProposeToUninstallCode(_) => (), SubCommand::ProposeToUpdateCanisterSettings(_) => (), + SubCommand::ProposeToUpdateFirewallRules(_) => (), + SubCommand::ProposeToUpdateNodeOperatorConfig(_) => (), + SubCommand::ProposeToUpdateNodeRewardsTable(_) => (), + SubCommand::ProposeToUpdateRecoveryCup(_) => (), + SubCommand::ProposeToUpdateSnsDeployWhitelist(_) => (), + SubCommand::ProposeToUpdateSnsSubnetIdsInSnsWasm(_) => (), + SubCommand::ProposeToUpdateSshReadonlyAccessForAllUnassignedNodes(_) => (), + SubCommand::ProposeToUpdateSubnet(_) => (), + SubCommand::ProposeToUpdateSubnetType(_) => (), + SubCommand::ProposeToUpdateXdrIcpConversionRate(_) => (), + SubCommand::SubmitRootProposalToUpgradeGovernanceCanister(_) => (), + SubCommand::VoteOnRootProposalToUpgradeGovernanceCanister(_) => (), _ => panic!( "Specifying a secret key or HSM is only supported for \ methods that interact with NNS handlers." @@ -3693,9 +3722,15 @@ async fn main() { Sender::SigKeys(sig_keys) } else if opts.use_hsm { make_hsm_sender( - &opts.hsm_slot.unwrap(), - &opts.key_id.unwrap(), - &opts.pin.unwrap(), + &opts.hsm_slot.expect( + "HSM slot must also be provided for --use-hsm; use --hsm-slot or see --help.", + ), + &opts.hsm_key_id.expect( + "HSM key ID must also be provided for --use-hsm; use --key-id or see --help.", + ), + &opts.hsm_pin.expect( + "HSM pin must also be provided for --use-hsm; use --pin or see --help.", + ), ) } else { Sender::Anonymous @@ -3896,8 +3931,7 @@ async fn main() { exit(1); } } - SubCommand::ProposeToUpdateSubnetReplicaVersion(cmd) - | SubCommand::ProposeToDeployGuestosToAllSubnetNodes(cmd) => { + SubCommand::ProposeToDeployGuestosToAllSubnetNodes(cmd) => { let (proposer, sender) = cmd.proposer_and_sender(sender); propose_external_proposal_from_command( cmd, @@ -3912,7 +3946,7 @@ async fn main() { ) .await; } - SubCommand::GetBlessedReplicaVersions => { + SubCommand::GetElectedGuestosVersions => { print_and_get_last_value::( make_blessed_replica_versions_key().as_bytes().to_vec(), ®istry_canister, @@ -3968,8 +4002,7 @@ async fn main() { println!("KeyId {:?}: {:?}", key_id, subnets); } } - SubCommand::ProposeToUpdateElectedReplicaVersions(cmd) - | SubCommand::ProposeToReviseElectedGuestosVersions(cmd) => { + SubCommand::ProposeToReviseElectedGuestosVersions(cmd) => { let (proposer, sender) = cmd.proposer_and_sender(sender); propose_external_proposal_from_command( cmd, @@ -4144,7 +4177,7 @@ async fn main() { ) .await; } - SubCommand::ProposeXdrIcpConversionRate(cmd) => { + SubCommand::ProposeToUpdateXdrIcpConversionRate(cmd) => { let (proposer, sender) = cmd.proposer_and_sender(sender); propose_external_proposal_from_command( cmd, @@ -4918,9 +4951,6 @@ async fn main() { ); propose_action_from_command(cmd, canister_client, proposer).await; } - // Since we're matching on the `SubCommand` type the second time, this match doesn't have - // to be exhaustive, e.g., we've already verified that the subcommand is not obsolete. - _ => unreachable!(), } } @@ -5090,6 +5120,48 @@ async fn print_and_get_last_value( record, as_json, ); + } else if key.starts_with(NODE_RECORD_KEY_PREFIX.as_bytes()) { + #[derive(Debug, Serialize)] + pub struct Node { + pub xnet: Option, + pub http: Option, + pub node_operator_id: PrincipalId, + pub chip_id: Option, + pub hostos_version_id: Option, + pub public_ipv4_config: Option, + pub domain: Option, + pub node_reward_type: Option, + } + let record = + NodeRecord::decode(&bytes[..]).expect("Error decoding value from registry."); + let record = Node { + xnet: record.xnet.map(|v| format!("[{}]:{}", v.ip_addr, v.port)), + http: record.http.map(|v| format!("[{}]:{}", v.ip_addr, v.port)), + node_operator_id: PrincipalId::try_from(record.node_operator_id) + .expect("Error decoding principal"), + chip_id: record.chip_id.map(hex::encode), + hostos_version_id: record.hostos_version_id, + public_ipv4_config: record.public_ipv4_config.map(|v| { + format!( + "ip_addr {} gw {:#?} prefix_length {}", + v.ip_addr, v.gateway_ip_addr, v.prefix_length + ) + }), + domain: record.domain, + node_reward_type: record.node_reward_type.map(|t: i32| { + NodeRewardType::try_from(t) + .expect("Invalid node_reward_type value") + .to_string() + }), + }; + print_value( + &std::str::from_utf8(&key) + .expect("key is not a str") + .to_string(), + version, + record, + as_json, + ); } else { let value = T::decode(&bytes[..]).expect("Error decoding value from registry."); print_value( @@ -5471,8 +5543,7 @@ async fn get_node_list_since( *node_map.entry(node_id).or_default() = record; } None => { - #[allow(deprecated)] - node_map.remove(&node_id); + node_map.shift_remove(&node_id); } }; } else if is_node_operator_record_key(&versioned_record.key) { @@ -5484,8 +5555,7 @@ async fn get_node_list_since( *node_operator_map.entry(node_operator_id).or_default() = record; } None => { - #[allow(deprecated)] - node_operator_map.remove(&node_operator_id); + node_operator_map.shift_remove(&node_operator_id); } }; } @@ -5697,7 +5767,7 @@ async fn propose_to_add_or_remove_node_provider( let response = canister_client .submit_add_or_remove_node_provider_proposal( payload, - parse_proposal_url(cmd.proposal_url), + parse_proposal_url(&cmd.proposal_url), title, summary, ) @@ -5913,7 +5983,7 @@ struct GovernanceCanisterClient(NnsCanisterClient); struct RootCanisterClient(NnsCanisterClient); fn is_mainnet(url: &Url) -> bool { - url.domain().map_or(false, |domain| { + url.domain().is_some_and(|domain| { IC_DOMAINS .iter() .any(|&ic_domain| domain.contains(ic_domain)) @@ -6284,12 +6354,14 @@ fn print_proposal { title: String, summary: String, + url: String, payload: T, } let serialized = serde_json::to_string_pretty(&Proposal { title: cmd.title(), summary: cmd.summary(), + url: cmd.url(), payload, }) .expect("Serialization for the cmd to JSON failed."); @@ -6297,6 +6369,7 @@ fn print_proposal) -> SubnetRec .into() } -/// Prepare a mutate request to add the desired of nodes, and returned the IDs +/// Prepare a mutate request to add the desired of number of nodes, and returned the IDs /// of the nodes to be added, together with their NI-DKG dealing encryption public keys. pub fn prepare_registry_with_nodes( start_mutation_id: u8, nodes: u64, +) -> (RegistryAtomicMutateRequest, BTreeMap) { + prepare_registry_with_nodes_and_node_operator_id( + start_mutation_id, + nodes, + PrincipalId::new_user_test_id(999), + ) +} + +/// Same as above, just with the possibility to provide a node operator principal. +pub fn prepare_registry_with_nodes_and_node_operator_id( + start_mutation_id: u8, + nodes: u64, + node_operator_id: PrincipalId, ) -> (RegistryAtomicMutateRequest, BTreeMap) { // Prepare a transaction to add the nodes to the registry let mut mutations = Vec::::default(); @@ -115,7 +131,7 @@ pub fn prepare_registry_with_nodes( ip_addr: format!("128.0.{effective_id}.1"), ..Default::default() }), - node_operator_id: PrincipalId::new_user_test_id(999).into_vec(), + node_operator_id: node_operator_id.into_vec(), // Preset this field to Some(), in order to allow seamless creation of ApiBoundaryNodeRecord if needed. domain: Some(format!("node{effective_id}.example.com")), ..Default::default() @@ -135,3 +151,58 @@ pub fn prepare_registry_with_nodes( }; (mutate_request, node_ids_and_dkg_pks) } + +pub fn registry_create_subnet_with_nodes( + registry: &mut Registry, + node_ids_and_dkg_pks: &BTreeMap, + node_offsets: &[usize], +) -> ic_types::SubnetId { + let node_ids: Vec = node_ids_and_dkg_pks.keys().cloned().collect(); + + // Create a subnet with the specified nodes + let subnet_id = subnet_test_id(1000); + let mut subnet_list_record = registry.get_subnet_list_record(); + let subnet_record: SubnetRecord = + get_invariant_compliant_subnet_record(node_offsets.iter().map(|&i| node_ids[i]).collect()); + let subnet_nodes = node_offsets + .iter() + .map(|&i| (node_ids[i], node_ids_and_dkg_pks[&node_ids[i]].clone())) + .collect(); + registry.maybe_apply_mutation_internal(add_fake_subnet( + subnet_id, + &mut subnet_list_record, + subnet_record, + &subnet_nodes, + )); + + subnet_id +} + +pub fn registry_add_node_operator_for_node( + registry: &mut Registry, + node_id: NodeId, + node_allowance: u64, +) -> PrincipalId { + let node_operator_id = + PrincipalId::try_from(registry.get_node_or_panic(node_id).node_operator_id).unwrap(); + let node_operator_record_key = make_node_operator_record_key(node_operator_id); + + if registry + .get( + node_operator_record_key.as_bytes(), + registry.latest_version(), + ) + .is_none() + { + let node_operator_record = NodeOperatorRecord { + node_allowance, + ..Default::default() + }; + + registry.maybe_apply_mutation_internal(vec![insert( + node_operator_record_key, + node_operator_record.encode_to_vec(), + )]); + }; + node_operator_id +} diff --git a/rs/registry/canister/src/get_node_providers_monthly_xdr_rewards.rs b/rs/registry/canister/src/get_node_providers_monthly_xdr_rewards.rs index 7c099c70621..cfdfd126b2c 100644 --- a/rs/registry/canister/src/get_node_providers_monthly_xdr_rewards.rs +++ b/rs/registry/canister/src/get_node_providers_monthly_xdr_rewards.rs @@ -107,11 +107,13 @@ mod tests { // Check invariants before applying mutations registry.maybe_apply_mutation_internal(mutations); - assert!(registry - .get_node_providers_monthly_xdr_rewards() - .unwrap() - .rewards - .is_empty()); + assert_eq!( + registry + .get_node_providers_monthly_xdr_rewards() + .unwrap() + .rewards, + std::collections::HashMap::new() + ); } fn registry_init_empty() -> Registry { diff --git a/rs/registry/canister/src/lib.rs b/rs/registry/canister/src/lib.rs index 4cb626eddaf..eaf612de5bb 100644 --- a/rs/registry/canister/src/lib.rs +++ b/rs/registry/canister/src/lib.rs @@ -5,6 +5,7 @@ pub mod get_node_operators_and_dcs_of_node_provider; pub mod get_node_providers_monthly_xdr_rewards; pub mod init; mod invariants; +mod missing_node_types_map; pub mod mutations; pub mod pb; pub mod proto_on_wire; diff --git a/rs/registry/canister/src/missing_node_types_map.rs b/rs/registry/canister/src/missing_node_types_map.rs new file mode 100644 index 00000000000..95aa92601fc --- /dev/null +++ b/rs/registry/canister/src/missing_node_types_map.rs @@ -0,0 +1,1429 @@ +use lazy_static::lazy_static; +use maplit::btreemap; +use std::collections::BTreeMap; + +lazy_static! { + pub static ref MISSING_NODE_TYPES_MAP: BTreeMap = btreemap! { + "5w4eg-mb4ih-bwfmd-332ta-krtlf-y5igk-r3lzd-l3bze-34553-3opqh-mae" => "type1", + "656go-lv4fr-us6n5-uiyjx-ca6i2-pqidc-qowpr-z6npc-gihub-r6of4-tqe" => "type1", + "6inbw-5r6vc-63gh7-imy3n-ghurg-ritzv-re77i-qb2vu-wt77g-7drvt-lqe" => "type1", + "77fe5-a4oq4-o5pk6-glxt7-ejfpv-tdkrr-24mgs-yuvvz-2tqx6-mowdr-eae" => "type1", + "7ip7l-crn4t-bmbgn-yqmaa-3igqp-a444k-ozelm-dd7cp-p7rmm-3zwrx-wqe" => "type1", + "7rkml-6hpmp-t2e4r-v6rab-iqugz-surqq-vcqxh-h7fld-rnts2-osixh-yqe" => "type1", + "blo7w-oao2l-iurb2-4sauz-qeoli-b6np7-nemuq-vs6qx-y2h54-esxtu-tae" => "type1", + "bufsd-4t7vs-rgepm-vpwdz-vtxwg-cndj5-rke55-ztkep-glhax-v2tcm-sqe" => "type1", + "egsow-rg26x-j3sml-kkjym-kswtc-sbrdy-zgbrj-uxuhg-3pw5k-oknue-xqe" => "type1", + "fneyt-nqoj2-ehhfp-vat74-ookwk-othuy-dj2nz-giiio-tuzqi-dkbsw-mqe" => "type1", + "gdj2z-rvm74-ayvhv-prqgg-cfzfp-3xbco-hyqu5-qabee-p5xko-7d2zw-mae" => "type1", + "gdvjc-myo24-i3wa2-jish6-qlkpj-4jgw5-3gs7y-pss6b-lpngt-uuj4f-2qe" => "type1", + "i6baw-ywtnm-q36ab-zi7o2-f5bva-xji6q-puhpm-pb73u-qlc3e-3nemh-kae" => "type1", + "jq33i-hlo5d-hyou6-wsgu4-vi7o6-upgg3-pzawk-les4l-gn3fg-eplfx-eae" => "type1", + "kby7l-tpmyp-ntea5-g4kn2-gkwrc-fbjbo-6pjgz-gaoz7-owtct-welil-pae" => "type1", + "lt3wc-g6uvu-v3jcj-qaxlz-nuoen-oof5g-neahy-44h6g-4uqxy-mlri6-iae" => "type1", + "mehhn-b5swd-urm2r-cltwk-c5tns-2s2bf-skz74-hv63o-3qdad-ubbfn-iae" => "type1", + "mihvd-umv3j-cjsl2-bfsdu-td7aw-2y6if-aw4fn-cghkm-v2oxd-kj75q-cae" => "type1", + "mjcgv-4nin5-hkr2z-jm4ao-u5h4c-odopf-is72s-cvll3-7ocz3-jscx2-nqe" => "type1", + "mxan5-53r72-hkqtz-u6rcw-p7ptu-tx5bk-z5a3z-phhfg-q44od-cbf4s-zqe" => "type1", + "myrfu-jkojj-uazua-a7qws-mg5vd-ye4tp-ycg6n-c2wwj-ixkwp-obihw-xae" => "type1", + "plofy-xgqcd-j5rdn-uby5b-pxymt-w5yzc-jumzd-rhosg-33xha-krwao-sae" => "type1", + "rbr7v-biuga-7kvyt-bmptt-qzqaf-mizsl-5be3p-tfszx-akpur-j62co-cqe" => "type1", + "rg3ny-2qkvz-btitk-sxuiu-fsl2h-nkmij-lagjs-y4bnq-cb63u-vh2rd-aqe" => "type1", + "tlv7f-uljzk-bihml-mjq5v-qqoup-cdapz-ecuyd-b4bez-k45ib-ljtay-rqe" => "type1", + "xgvwp-qbx33-ahd7y-rhpph-7ihcy-3sesx-cmu7z-c45sc-nbk5i-5tnrh-xae" => "type1", + "xu2zg-nns7x-z67l6-foa5w-yc4ek-addku-g2hqg-c3jdg-wabdu-5ouaw-yae" => "type1", + "zxu3p-na2jg-zam2n-crt3x-iqr3f-4u54l-snivu-abwbs-zvtu4-qhvjm-dqe" => "type1", + "5resh-f6n7z-xxkbq-7lpod-spptk-nyt7n-tcvf6-f5ecb-ixxwc-6k5ai-rqe" => "type3.1", + "pzuk7-f54ar-77de6-nkxs2-ybva6-dpn63-blie4-rg2no-vtyl7-pvviz-tqe" => "type3.1", + "v27at-hedf7-4a2my-tboq6-escdm-77krt-2qfuq-zjptf-z2sbk-vd7zs-xae" => "type3.1", + "yahq2-6rnmm-n7ubm-q76zd-256dl-5f7k6-jxx5l-njyo2-hl7tk-sqcet-6ae" => "type3.1", + "35tkk-2zqw2-r5bjh-brbpj-5kns4-rmqf6-bwnq2-ylykq-czesn-zkewj-6qe" => "type0", + "3pfzk-4qioe-5p24u-dqz3m-dm4vi-2n3rs-fwk7z-uwxff-4xcq2-w6rbm-lae" => "type0", + "4dcpq-6fc3c-cdc4i-jgjlt-vvm2v-42uor-zd36s-gdte2-isw7e-7ffse-hae" => "type0", + "6z4k6-xj4pl-byhgo-7g4wa-ucfy5-327wq-uwolw-pjo4d-mb52n-cwtg3-5qe" => "type0", + "aegne-ykwsq-zne7r-uye56-zr6lr-am4vf-26huv-lttjl-74k4y-wryc4-5qe" => "type0", + "bsfx4-dwsm3-oals3-rj2bn-oo67t-vtvkm-zejvb-z244r-2yiti-e2ume-bqe" => "type0", + "dufju-f3t53-ozvn4-l65s5-jc5uu-h7z2y-4odvo-3oixg-nixr3-yfrua-7ae" => "type0", + "fczqt-l6jks-tagrx-7tan2-v4ila-2a3gc-2dbxy-ppegd-bikqf-ddyno-3ae" => "type0", + "gfr4z-q3ccx-hbojo-sh7wp-vrcgo-oqckk-zdflv-duzqd-6eop3-xwepk-qae" => "type0", + "hh6zo-i3dy7-m3uzl-5aekv-jpoih-pi74a-jsnj6-w4t6h-tzr2o-ja2r3-dqe" => "type0", + "j5frv-bigyd-2a6p5-ikqhp-e3afj-7q2tr-447rn-gwypp-ebjwm-7vrmh-eae" => "type0", + "jdqc3-ykmxt-io342-3iup2-nbi3s-rajh4-ltxjj-zeifw-b7lfr-wugxm-dae" => "type0", + "kysfq-adwei-hpc4a-qscmv-onh4l-xmgba-uuo2p-53ntr-b2hnt-x5njs-zae" => "type0", + "lzveb-mlka7-ectcs-cqg44-him3y-vh6yf-q4vdo-7z45l-3o46m-64awc-5ae" => "type0", + "nkldn-vjbjp-5qngd-46dba-3bloh-dival-fldrp-a5cl4-u77n2-plsek-gae" => "type0", + "oa3ki-jx37b-5kqbi-fmmbm-g3gna-j6yum-gmof2-acjh6-wxkqd-doawm-vae" => "type0", + "p3g3j-vvhxl-luedm-woh3j-sllez-jkbzz-274bf-hyuoc-kyo5t-kwziw-eae" => "type0", + "q4znb-i6cfm-4rsfj-wbomc-3tqoi-rtvzq-nkws4-frnd2-ayisx-qzmcf-jae" => "type0", + "qjcai-zojq5-ec46h-5jj3m-wtih3-ytunz-2crlp-a23c7-hgrky-ymmx5-aqe" => "type0", + "qqzht-uatxo-sshte-mx2by-vyke6-4hquw-lo4pm-cayzw-25cu2-h3gtb-pqe" => "type0", + "r4ub2-cb4cw-s2b4o-xawyq-7dbtr-zdryi-iylv5-zf4gr-ww76a-or6cx-tae" => "type0", + "s2ifh-yyi2f-3bht5-jtpb2-q5bim-tuwfv-yqio6-zrgbd-w7yck-peeeq-yae" => "type0", + "spjcy-g5o34-rdpg5-szn2t-yvxkl-42ohl-s477h-vgurz-xxkln-vdeuv-oae" => "type0", + "unugt-oqfqb-id7qj-pbtuc-3jole-etsfa-54og2-7qyci-o3fkc-zppgt-cae" => "type0", + "v6caw-kf4b4-j22f5-pqsvj-hg7dj-wx4xx-y7urh-eixmh-hnvz7-hoe2v-jae" => "type0", + "xkqjw-4ewff-yy6gb-ywj7c-5g3fn-nheqs-czrvl-dnpor-kry46-jhdtc-5qe" => "type0", + "zcfzo-aqmwr-lvpdo-ghmlu-mep5f-ygfeo-7kymf-5x4gh-bugiy-ieheh-uqe" => "type0", + "ziskd-2eycv-scycv-jdyn6-5lwsh-waapa-fbckr-zee2j-7njoa-wwflg-vae" => "type0", + "2n6eu-vv67p-bvuwq-eqfwb-r6jdi-wp3fo-jcodd-a7jtx-frju7-3sh3j-zqe" => "type1", + "3655m-si4pt-xysac-y2fwl-htr7g-yhynz-zaey6-xzglx-yqnso-vutgb-lqe" => "type1", + "5i7he-lt457-b3ov6-fsi2c-pufn7-4i2mp-zkacf-4yysn-7fhr6-zfxg2-4qe" => "type1", + "7zf3w-emnhk-7yhl4-sxvmx-wxgle-2hatv-hagfv-ybpxo-jglsx-fgadx-hae" => "type1", + "auqy7-dtbmh-udhws-kto3f-7z2bp-be3xa-2qsmo-yqiq2-3wm3o-scdu6-3qe" => "type1", + "bbw4d-zsigq-xpyvt-zgbjm-ryrm5-z75t6-4dvz7-tt365-sdzgs-gffzu-pae" => "type1", + "c3clp-pxh3b-ut2t3-dejla-zsfqz-k2cqd-2aygq-zk7pf-cv3is-oqr5k-oqe" => "type1", + "cjiry-m7wev-s6adg-3oqev-2g74r-wmtlj-ttdbj-abwts-rtwlj-6lxts-dqe" => "type1", + "cvic3-6mmjj-2zszr-mr727-im4l5-skwod-gzrg2-avd4i-bahw3-xyx3l-2ae" => "type1", + "dkadh-uqddg-66vac-zppnt-zh4n2-hlof2-4flz5-mtcll-q6cd5-7xchm-7ae" => "type1", + "dvtqe-tiij5-cthxl-ccixf-7fymg-2iuh3-dbdup-zfnmc-5myo5-vjdm3-nqe" => "type1", + "e4vra-l37h6-mc2n6-pmlwx-5xseo-ycge7-mwii6-5u2yq-bboun-c27ki-6qe" => "type1", + "edmjn-npvdy-gn3rw-jix5y-da2ae-qb5dh-th3p3-2lkrb-c6prf-tnx7r-zae" => "type1", + "f6o5k-3o3cr-be7ps-mpld6-s4ujf-ggri2-ayd7o-ub2yr-b4ku3-tt2kh-vqe" => "type1", + "gbfq4-ydyau-75yqj-tg2ez-z6q2m-33lkf-5ao7g-ull47-fei56-ynayw-uqe" => "type1", + "hz6bd-sbzif-bm6fa-aithr-quiez-j4lqu-3ijih-sjpwr-ginmi-omshg-3ae" => "type1", + "icbco-xw6zg-wlhyp-juqf4-h5gbs-jjein-nmc6n-itoxd-o52q7-q7fjz-gae" => "type1", + "jd4tu-jujfx-r4jhd-64yek-3s34j-3qw73-443cu-um6ng-6me6p-r5jwl-3ae" => "type1", + "lvoro-iipem-adk5b-qwiiv-2pgbb-dlxwo-qgtd6-d2fvu-z7jxj-telie-jae" => "type1", + "m22uy-ch26x-ymd7q-n3stg-kyoel-t3zdn-4uwqf-zu6sb-pezk4-swax5-yqe" => "type1", + "n3szk-wj6o2-r4pyr-26er4-6uw5b-2aq7m-73gnn-afu3q-dpjew-vusrt-4ae" => "type1", + "qowce-hpatx-sz3de-t6iwa-6keak-jdxmo-5gmmw-iohme-ngbmx-wegof-jqe" => "type1", + "rkzu3-pcime-656cn-67oho-rkmjy-ko335-tjxqq-cd3cc-zmv4f-3kgug-pae" => "type1", + "uxbqo-4ugdi-djpsm-h6lcd-sdtcm-g7vve-4ajsi-lnv3n-ujnbi-2tgos-7qe" => "type1", + "vhw72-z4xlu-g2ee2-7owc7-6qk6u-j6jhm-cfzk4-2jtyb-e6hkg-bkimw-2ae" => "type1", + "vqucp-jq7oa-t3iwb-hdfmp-n7zer-futce-i7rmw-23e6i-ketsi-m6gnb-iqe" => "type1", + "wo25n-k5g37-sc6et-n5vpz-xpsrn-t23xp-rrs5j-d5fy2-ibfny-5vrd7-mqe" => "type1", + "3md72-bk7ui-3gh4x-c5v3k-qum57-birz4-o3sul-xxkab-jbv5g-brfbx-fqe" => "type1", + "4akse-wjzws-t4sph-2me3c-554vx-raus2-tf5s2-g52pu-wfltp-pk3f6-hqe" => "type1", + "55qds-ljsb5-6xzvg-x7wfe-otezc-xcmho-g3czx-pif5r-zv5zl-fikmq-tqe" => "type1", + "56zpl-ndbhq-d547m-hgcw3-7vysh-7hf3k-z5laf-hdyut-e7dal-cy7nx-6ae" => "type1", + "5kk5o-wsnpr-qr37o-zex2a-hxrbz-nw3dz-urxjv-cgtmk-3fwxr-b6eez-iae" => "type1", + "7z4rm-qncnt-olg6f-7ncqm-k4zpq-qdcn3-6g3rh-6y5km-2px22-bg2sr-xqe" => "type1", + "c5jxt-jeco5-6xmqb-rmmzv-m53fa-scouz-dutbt-inrqf-de7e2-2wiqx-xae" => "type1", + "cjdpl-qi6xo-viniu-qi5su-djhlv-3ko3j-zwila-davsb-xkiou-vz4km-rqe" => "type1", + "cttab-pom2f-5a3ni-6rxwe-yt2nc-4kn4f-3jhd3-wo6hd-576cr-y6nge-2ae" => "type1", + "eu2gw-3uj2c-vhwzo-kx3tz-cmnte-3gymk-tza4c-z66f6-uuksx-x3g2m-xqe" => "type1", + "g4eis-jmdlu-iiivf-75n73-uto6l-miezg-kluob-ogj6z-2llqc-pomjl-jae" => "type1", + "gc52d-oqi6l-auiz3-wg3pr-qrhhh-obbb2-mq6x2-xsugp-pubxd-lpjbd-lqe" => "type1", + "geihd-pwhjg-q3tno-beo64-ormih-pkjah-rrdlu-quwdk-nas5k-lmzoo-pae" => "type1", + "ijqzn-wklsd-asfzv-ixif4-u7lrb-dblfi-mcq2d-nrwuk-d6cys-jgnar-7qe" => "type1", + "jjrbu-lwq3g-6z32f-54avq-c6jcp-sxp3o-kn4sf-hiiy4-jkuiy-252or-4ae" => "type1", + "ku5nu-lvxbu-7fxgd-54wv7-mlev3-zcdnj-ut6nb-vcukr-oxrdl-3dbjk-xqe" => "type1", + "lmsfp-rhlpi-pgvyv-hhuos-k2cun-rtcuq-ztly6-6qczo-5leeg-vl72u-dqe" => "type1", + "on5bp-3jfja-rgaxh-iuxzi-7d5x6-fnuze-3tpbn-zdnkm-p2dpz-spn6k-bae" => "type1", + "quee3-x7mo5-o22u7-ml55j-xatgs-btnqh-hxsgx-2yai2-octo4-icka5-dae" => "type1", + "r7sia-tsoz3-f443d-66eke-rxk7n-zxv2s-5mieb-4jmi7-ja3mc-iugll-dqe" => "type1", + "va53e-afslx-vabwe-whjns-r66hd-coh34-aeb7w-omrcv-3so2u-esow7-aae" => "type1", + "w5nh3-v5yix-fnwcd-6iema-sj7yc-hprcf-5xju7-nj7iu-o5p4y-aq5pc-6ae" => "type1", + "wbqzc-cvi3c-gnuak-ztcqs-j4ga3-6m64f-w2fmy-qjqve-lzvja-rfir7-kae" => "type1", + "ww77d-34jmu-slkvr-kvwod-qqkef-rf73s-us6xc-ni24m-lyk5b-elzf4-mae" => "type1", + "xfozx-vhnrh-27riw-k6amn-pj4vh-t7qx3-bydxt-p6lv4-7ppyd-432fg-wae" => "type1", + "ygkiw-fwhds-ofhzk-3uon5-bhgcp-brjn6-viudd-nwfuo-pt6sq-chcuj-iqe" => "type1", + "yujwb-ujbfv-apyte-q7roz-xophj-77vdu-3p7uu-kb3lu-wlyiw-srv6b-aae" => "type1", + "z3xsm-cbdou-eesh6-vctpe-2l7hn-qwtsa-rawbn-vapgu-wljp5-vfgso-fae" => "type1", + "74qsa-j6jno-nmxmb-7jpf5-u6nig-g7hgo-thvjm-6f6zu-c2ugp-krple-qae" => "type3.1", + "7exbb-k4tu4-mw24y-3zylh-bopa5-fwdsc-f4a3z-45hh4-twpin-4zv4a-yae" => "type3.1", + "g765q-rteh7-xio4e-vtyea-ww46p-beylc-3e3s3-yhvuy-in6jd-hj5qo-3qe" => "type3.1", + "2eqbk-www2z-qcopx-wujey-4frhx-cpstt-4ecyv-7wh64-qozmi-wwkpe-yqe" => "type1", + "5dpkp-lfhr2-j7mfz-gavpn-puej5-wdfzg-fw42o-zupnu-izvk3-ubzzi-6ae" => "type1", + "7effw-j4nle-7smlm-xzebk-ttmdp-lndrw-jk5a6-nsr4b-fikx3-zpuee-6ae" => "type1", + "7ucvx-t6mxf-viej7-ils7i-k3aco-tqzsg-6cy3o-cakgs-zifaj-kiduk-cae" => "type1", + "bydvi-usuoj-bhuak-xyt2l-gw5hc-j5o5y-plelm-p3uim-5myex-wmrrq-uqe" => "type1", + "clfor-3la2h-lohby-tfriy-xfjkf-fnqpw-kkhjw-ctb2i-tscfx-ddwu7-bqe" => "type1", + "ddbl6-37efl-b75e4-jpfsb-zioa6-ilvzo-tldwy-fnbhm-nbuoy-66cza-uqe" => "type1", + "ehorg-uqvqd-ddlfa-nep3y-kq34u-j7zpx-ukwak-j47hn-x4az2-zzsoo-hqe" => "type1", + "h3iv6-ezskc-3ij4j-zd3le-hld6d-hkuxx-dj6zq-w7rrd-gziap-a23ob-dqe" => "type1", + "jmuoq-7kbey-cbtmm-zlz6t-chwt6-dk7ly-d3jim-5nuu4-y5tou-l5egg-7qe" => "type1", + "m6pbx-6q64u-443wl-43j3t-tysy5-bb3f2-dtotm-hd6zq-qbemu-dizp5-zae" => "type1", + "o7jhq-4dniw-6nzel-kye3v-qpsq4-c24ek-nkyl5-grlrs-qo3qw-qfmjj-qqe" => "type1", + "wxpaf-p35qf-lgbep-ea4cu-dfxwf-65xgh-5hoyq-lplcs-ztt5m-nbweu-zae" => "type1", + "xsa4m-ko3b7-2zvcf-56q55-ahyqi-2qkho-obuu3-l4otg-4z6fn-n2r6i-cae" => "type1", + "7pvxh-37ula-relu7-uim72-jvx46-dcnvm-52zsf-vkqzm-xsjoy-4xf4l-hqe" => "type3.1", + "aajth-ndp7x-ro5ok-yikyd-4i7xn-5k5ki-e3d37-hh4gn-s4opz-bnzxf-4qe" => "type3.1", + "ihttm-45oz5-an5mg-i2jtb-fayst-s47j6-vmuwr-fqotf-mp2il-n5s5x-cae" => "type3.1", + "tws6x-33lmm-tptgb-v2tte-opnm4-fychj-thpve-x4ofu-cn4vo-6yuhl-qqe" => "type3.1", + "3yok3-yvswm-l4ior-bjh62-fsi73-vfqp3-77wji-coglq-l2bjw-rxrph-uqe" => "type3.1", + "afu64-x2met-w2vxb-4lzus-z4fhf-divej-q46gf-wvpek-dj67h-fptsd-aae" => "type3.1", + "b44r4-u77ay-myhhv-d6d75-jusik-b7ry2-g6ms5-6okxm-tumyx-rjm4g-4ae" => "type3.1", + "izmdg-yz3l4-jp5vr-rva2j-5btpc-bzgt5-7snt4-wwy2g-jaq7v-rzpsv-oae" => "type3.1", + "kwict-ujiu6-u4ss3-nlwqq-dchsd-2h5lh-nzkke-24ij3-3otqq-rswre-vqe" => "type3.1", + "ljfwz-ol5ig-7fsu7-4vznl-52eyn-xw76h-uls7q-35qlu-t3hfh-5kyu5-oae" => "type3.1", + "mae7q-ggnbj-xb52f-hvest-dgqoc-whvsq-yui5d-5cys2-zeold-l5ydx-oae" => "type3.1", + "zgtrt-4vlgr-pbytl-t2yqq-qf4nk-wyoos-vrfpu-hxqcw-tcnfu-73kjb-pae" => "type3.1", + "4jtgm-ywxcc-xh3o3-x2omx-tgmdm-gobca-agb3a-alvw4-dhmyn-khis6-xae" => "type1", + "4wpdz-2h276-lbrzr-nro2a-c7cjl-ivkxz-psqjf-grrrc-7hqem-f66qd-zqe" => "type1", + "5djxc-oflqt-exy2d-ka2dg-nndl7-pzw2n-sgquq-75cal-v23ht-uxzkt-tqe" => "type1", + "664pj-z6lri-encbc-qpm5p-gjv7i-qvf3s-uyst7-xpfal-xtkas-dbpgp-kqe" => "type1", + "67i4t-repqq-g4e5c-prcmq-nh3zp-xwdwz-e3pwf-w6sfw-gqr4i-znc4c-yae" => "type1", + "af7ti-auyik-jfsne-tljmz-6purg-2msmy-jw34z-b4ie3-abk5f-h23xt-zae" => "type1", + "bd5ij-46qqy-2kvsc-dlrxx-ep6kx-thotd-3jyxy-xekbw-kkdta-jwi4t-dae" => "type1", + "br34g-3vg7a-a5enq-jysmz-gm7nt-zz7np-6qiz6-nopmh-mfrwc-vspa3-tae" => "type1", + "d3e4o-lzo7o-edq5a-aiebe-kudf7-j2dqh-fowbh-du4xw-luzlh-wxhd3-gqe" => "type1", + "dhuj2-ftdaf-zp53t-76w3c-xe4q4-x5cvo-uraiv-672js-h3bl4-hrhqz-2qe" => "type1", + "fd5e4-a2xzl-lxu7m-kjvn6-2arnt-jghro-rdrgx-zvvkd-j2hza-pbwl4-5qe" => "type1", + "g3jye-pe4af-ugyxv-z7ag5-3m5ms-s3ibo-utma3-xqa5d-7742b-xb6tr-kae" => "type1", + "id2le-fhh6d-bqc5m-uza7x-tih4p-ghysr-otoju-dkuxz-y32qk-ygynt-7ae" => "type1", + "iev4c-24zfk-fk3t5-i24fh-e6yhu-xx26q-rzsdq-aukdo-xd2ex-thlpl-cqe" => "type1", + "ik7kx-ejjyb-duuem-davt4-whf4q-t33ai-izjat-3yiko-kjjgw-3iago-qae" => "type1", + "nwjwl-gfnnf-zpo2o-ciacp-hvdgm-53rbb-5uysh-lleob-47rbt-rsehe-uae" => "type1", + "oa4zo-rk7lp-iwpe3-raybn-xique-c7lbx-qu5qn-qpzit-zzr73-asq36-zae" => "type1", + "ojohy-bikhs-4rhct-vawxt-zejuj-as3nh-zg7x4-kd4vj-6snox-jg6vd-pae" => "type1", + "q2ucv-x7dv5-hheao-ocsye-jbg4z-enm75-ss62d-ehqhj-zwwm3-cap5q-tqe" => "type1", + "rfik5-ueo7z-7onkv-gnw5z-abjr5-64i4i-prly4-edkr4-3myl3-zkovh-cae" => "type1", + "tk7c2-vuwkh-cllbs-6wqbw-bilx5-g27gf-ytrm2-24llv-fl47r-tma5c-uae" => "type1", + "tkjd7-67pky-t52ju-l2nae-poag7-pmlkf-vvsrd-byrl7-tgh3z-ca7f2-rqe" => "type1", + "v2tn3-ioqn4-rkthu-o5urm-qk2zn-kwcyh-q7h5e-vncxr-refgi-wsgk5-6ae" => "type1", + "w5v5z-fcppt-a43do-cb2aq-uvl3a-kog7c-ijff2-3scow-pk7uf-6cr6q-jqe" => "type1", + "wrlf2-3vbmx-cchct-vvdya-d53vz-tma44-zjv5t-xcbpv-qgaio-2333m-fae" => "type1", + "ye7hj-6vkr5-lfzi5-kmlhk-ghdxd-nk4tu-tjjmh-hu5as-mpqs3-a27mc-xqe" => "type1", + "yqw3m-oz2zj-iumts-2b36x-2kgcr-iaeph-qsa4l-icno4-rqn7g-dqgcb-rae" => "type1", + "zwj7g-vr2fu-mei6y-mg6rb-lluav-v3rjp-lwez7-74d62-ltspf-2mxh3-rqe" => "type1", + "2ew2x-bmzxs-o6sw6-xbxv6-efhzc-47y5k-vy5ce-luaqo-lecdi-33z4i-gqe" => "type1", + "3s6fj-wulzc-ml7vo-vpvhj-4loap-25asb-m6efr-f52qf-ehpxb-7mh3k-vae" => "type1", + "3smci-63tqe-6q5xc-wpdmj-pnhan-own4t-6km2r-77mfh-c2h7d-xzunk-cqe" => "type1", + "4i64c-7p2wc-yotet-r64av-qwt2b-hp3hm-v3wad-bp5md-aeotm-gsofp-jqe" => "type1", + "4txlq-o5ukt-if4pj-adxtx-jwp42-jch73-z5nfy-77gi5-4lll7-eou2p-rqe" => "type1", + "agoki-54kg7-2fdfq-lmnvz-bj5lc-xrkvs-dqjnr-s5as6-7jj7i-2xh4h-vae" => "type1", + "akpwl-fouuj-clhyn-kjbq5-h2xix-hiirq-6tjxs-7w5ps-fhtdn-roxcy-2ae" => "type1", + "ctwsk-mtlet-rcsk6-p44am-6yabc-psi27-den2q-gaeg4-lk5zi-hr23q-6ae" => "type1", + "dbdiq-igutu-o3uss-y7ms7-xh7du-cta2k-ar223-ms5cr-mjej5-vbxvc-qae" => "type1", + "eexw3-cwz3z-6antd-yoyyc-radeu-jmt66-7p2r7-kjbkn-cij4b-kwqrb-tqe" => "type1", + "ek3yy-o3qk4-xivlj-qik4h-tgbwt-kmyyv-po3lm-hhwll-o7p3w-qrc4s-hqe" => "type1", + "fabpy-po455-j7im4-iisoo-hrsvg-4nk6z-srkn2-yzxba-vmeoh-wmg42-kqe" => "type1", + "fxdqu-epq5z-kmnok-ch46f-jxujv-of7ws-3ec4b-gszap-gtklj-sxfiv-wae" => "type1", + "gzwoq-ajoox-o6vj3-rncja-nekig-dhmqo-pxuwj-yu6a3-w4ygh-wof6w-6ae" => "type1", + "iirmo-i6vu4-jmbkl-pvgwx-zjcft-c2bq3-xg27t-de7bm-icdh3-wfsys-yae" => "type1", + "jsnfc-hogin-macy3-lfird-pfijz-d5jcg-woonn-sbtgx-gzh5h-jjvst-yae" => "type1", + "kehk3-dpd75-txcgc-plazi-gsgws-h6krw-lnflf-dr6zt-ly7c7-sohhp-6qe" => "type1", + "mvsqq-rkiq2-fuint-an5oz-eqvhk-qu3vj-hesjn-uhn52-z7f37-wxucs-eqe" => "type1", + "qgbme-g6fmi-gks7d-gorcj-xtytv-cq5c7-yaahr-z2m3x-v76qv-fdw6l-kae" => "type1", + "rphlf-rfstt-sg5fi-ytcie-gnuwt-osv5d-fg32b-stlgf-5ajgi-tiw2i-eae" => "type1", + "swzzz-nct4o-kgh2s-fezeh-luamk-chexm-jsk4r-ks2qa-au63y-jtc7r-pae" => "type1", + "tlrvk-uwtes-ghpse-oekbm-jsrow-lf5kk-gqff7-c27d4-ozpou-xugiv-nqe" => "type1", + "ubipk-gibrt-gr23n-u4mrg-iwgaa-4jz42-y6gt6-3htxw-3mq2m-dwhv2-pqe" => "type1", + "vdvh4-bdy7m-gyxbj-7pt5m-ykhey-mtobh-ul4kq-mwozd-t6enu-3pcau-jqe" => "type1", + "vy6tj-7mw6l-hp4pv-ly5ez-dy23b-aqicj-5u7et-rlmyq-n2rin-7fck3-tqe" => "type1", + "x65cx-a7sdm-q3sli-wg2gi-ooqns-nrfui-cfhrq-hl4vg-i2qb4-4mluf-hae" => "type1", + "xnraq-n62so-zdfy3-63wd4-dbf55-m2h66-xcpbu-laatr-52ytd-5gdv3-wae" => "type1", + "yh3a6-fzjir-23ow3-cnsz4-w56dj-veho7-qvu6c-psj3h-uscb5-4ikej-pae" => "type1", + "53p5r-qqup5-e2z7n-eeg65-kzklr-ekrsa-6g3hc-vrmqu-wtows-6q2t7-6ae" => "type1", + "6fm36-gj33w-34vuw-rb6se-62vzz-he655-2vasp-jnu6w-ixzel-e6azy-eae" => "type1", + "bfbpg-wbgy6-vqu5a-bqnyp-bcb43-mp3yz-2t3sj-nkrnp-ac7ux-g2jj3-yae" => "type1", + "jxgxw-ublpi-lmhwt-jhiwe-fzsnm-md7ni-i4jfx-vitgi-ojusx-vcwxx-lqe" => "type1", + "kdldi-idcju-r7agw-nm3au-un5cc-2zxtu-mtnlv-tzvmt-c5alf-yjqld-cqe" => "type1", + "najnj-to3ju-pl6pl-5yjlc-6vdqi-f7tht-qd7tt-fenyr-f3tjc-tfj37-uae" => "type1", + "qlvmn-xucv5-o6qp6-nmhaa-mfm2g-x42bb-6sisq-wrytb-wpxkm-fser2-dqe" => "type1", + "rhy7d-pjsmu-5ljz7-vuaio-ihiaq-iysyk-np226-tirem-kuw7n-v2i2w-iqe" => "type1", + "rl4ec-tk6gm-5pc5u-2vtnm-vim5f-xgjik-iolpy-lhjof-yvnu2-mwixm-vae" => "type1", + "ulhxy-qlokv-5iual-yizlo-dtlmi-aavz5-bi7ez-kjwqz-la64z-cxjuk-fae" => "type1", + "wgxbt-4e5xm-lsyc6-kazar-7kvzj-7biff-vzt52-2hddx-h3ddg-pfd3n-iae" => "type1", + "xzpf4-zeihc-upncx-zz65w-rep5j-xh2sy-yw6bg-qtgwu-5z3ez-er2gz-sae" => "type1", + "zk4lm-5c2p7-zhlqv-3pi2i-hxxlf-xy6t7-ttvyc-hxenc-ixtcf-66ezn-cqe" => "type1", + "zqwwk-hrvpl-isozp-ls5ab-wsej3-bvfry-gkebu-6542b-grakp-yxffy-4ae" => "type1", + "24fcm-3onfs-cykax-al4jb-gvdd3-u76zl-ht6ef-hcsbc-xwf54-xbrym-xae" => "type3.1", + "ax6zb-r4jnm-5aswz-ahoqx-ujs2a-mcvds-6gmdt-4ggxg-tkwdr-3323l-vqe" => "type3.1", + "c2ewt-wic55-asi27-6eoeu-rn7bq-xjzmz-dzxg2-pxrfw-qeinw-yrj52-7ae" => "type3.1", + "cvx4p-7vyi4-bfhkf-avoox-3pek3-yzlip-u4sxf-d3udp-nzzsu-equkg-oqe" => "type3.1", + "lsew2-tmjjw-ytiyx-st6sx-qo2wi-cxu3e-sgidd-i34jd-okjsn-rtli5-cqe" => "type3.1", + "u5ycx-4avna-dp4ca-svus4-iycai-cmcrg-do5ll-qmosp-segtt-5jmu7-dqe" => "type3.1", + "zi64g-qzbnp-3eu7w-mcz3p-2jb7a-he5lk-aru4t-goyi6-p2gtf-i4yqh-2ae" => "type3.1", + "zk7wk-ueuvm-5quvh-lm6kq-etjru-i63xr-u2xmc-srfx6-uzqq4-jx5j2-nae" => "type3.1", + "5fpvj-plxyb-bsyji-b4pp5-j47gg-vlkor-q6bhy-5bhmp-kjtu6-fqkjh-iqe" => "type1", + "7agd5-pfc4g-rdozn-zajij-aax72-7jft2-rzw36-y3flc-xwt77-pjg5w-7qe" => "type1", + "7h3aw-y3ygk-37mdb-cbuj7-ric2q-b7pgf-xwspg-bxzq5-cxid3-lqqwi-nae" => "type1", + "b3knf-xg5xg-p3oyq-dgpdw-xjz2l-n35mj-pdoyl-4dgas-u2t5l-bndrp-hqe" => "type1", + "bjhao-hlctl-g24ce-7hfcg-mqxbw-yxhyq-q23mj-smxsk-4o2s4-u353p-zqe" => "type1", + "d7dyc-slisa-nrkkz-hrpee-2xbpi-xjido-shkjk-vrtob-cmfxd-6sevt-5qe" => "type1", + "foa3b-mjkuu-3qlgu-6o64i-2omzt-x6suc-5gzcq-6j2gz-bftru-j5idg-gae" => "type1", + "fzclq-uw6cm-gw5nv-j2iyy-zeiu5-qqw4i-5x4cs-csrw5-pquah-xspkw-uae" => "type1", + "g4avo-ecrmg-ki3ol-dxah7-zksar-suefa-26fco-fudva-ajioq-xvhmq-4ae" => "type1", + "ihb24-7542u-a5hwt-le3wd-ion4f-wbduk-yawte-kaxrd-bv5kf-7jyld-bqe" => "type1", + "j6uir-h4bk3-chdqr-hxnkr-3jp7o-yb3el-skdsd-7oejj-eubk5-5onvy-zae" => "type1", + "jyh32-azcnn-mwosu-urewl-m3pyx-6pmlq-lu5fu-vqjyl-gwx3j-zqrjr-gae" => "type1", + "kmez2-lipof-3kjdn-nt35r-ai7oy-gvxxb-5hkv4-lk5ci-vtjya-hpd6w-wqe" => "type1", + "kpvfz-zg5cs-5ofal-k5zng-uop7b-gjm4v-z4ug3-sggef-4zu7x-ihvcw-yqe" => "type1", + "kxmey-hzsm7-7lfch-yfrbq-ereis-e2t7b-ztii4-7ib5t-fljrs-kjkey-kqe" => "type1", + "m34r6-l2rj7-deohw-yrkjp-sretm-6tadf-xnwqy-3flhm-nqj4r-wwcoh-eae" => "type1", + "o42ny-ab3zt-iknyx-eo4ji-utgbe-xmae4-ybmwp-wdkzt-efffe-oq6nh-oae" => "type1", + "ozzf3-fgu6i-zazyu-53tna-6zxsi-6vh22-fvvuq-omtpr-hjrqn-n3ufz-cae" => "type1", + "pym4f-y6zlv-feua6-7y7ao-cbvlp-hkwfn-hqzjd-5sdiu-rsv33-y7245-nqe" => "type1", + "q6hk7-hplmv-xsedz-5bv7n-vfkws-rjyel-ijhge-hx2zg-nk7wb-tzisp-xqe" => "type1", + "r7few-pljgn-iynmr-iprtj-p66dg-qpc5m-2tx4m-245oc-6dzgk-pu2wy-dae" => "type1", + "tkoxk-7juao-nynai-s6dt7-mpsfm-alwl7-yg5md-2zsbl-twivd-xbdae-4qe" => "type1", + "vsuqg-6hald-hxxxi-bxr2s-e5af5-p5lsr-2sutj-pip7r-co24u-2he35-hqe" => "type1", + "vuizy-nfm5v-rapnc-rijer-hijfx-bvjrz-ccxdd-kte3v-awbnq-bdm6m-6qe" => "type1", + "wihnn-o5yix-kkneh-33pvm-ybil3-4rdf3-vu3w2-6lmg4-76yu3-feyyd-cae" => "type1", + "ys5ct-b73ij-z33xq-y4tej-vby6a-pwzjk-t7ob5-y5nub-oe4vo-alrjn-oae" => "type1", + "34j6h-6tcf5-3d4cv-oiqq4-hr5if-llmpd-p6arh-ab2jz-xege6-to7ix-pae" => "type1", + "4pmj2-nvzxw-vuhjr-kx4tt-mux3j-u4iez-x6q53-ldahn-dfz6n-s4yre-yae" => "type1", + "5h452-eaa2z-ftaoe-ogjhu-cvxhn-rcojo-slfuc-emuzp-gce3g-m6fgp-yqe" => "type1", + "7wsw7-duv4i-kab3u-6ni7n-liqbo-vnvja-7pxgb-gi3vx-6pjyx-xbnb6-sae" => "type1", + "aae7j-zvepi-ypdnp-4a6fg-durhx-37r2u-d4e5u-z67va-dsen2-rnusw-pqe" => "type1", + "c6opb-poeyo-5fim4-kjq7j-oy5iw-7ilmt-i5ghy-pkg2p-ba2dg-jkrcb-xae" => "type1", + "cuzkp-ldqbp-izmg3-k7de3-c3wap-tqd3r-ponxu-ivvex-q3kct-abfmo-vqe" => "type1", + "e7625-vbvfx-4bykm-vra2c-tp36j-nfomw-ufzm7-b4fiq-inhjx-jiklv-xae" => "type1", + "eyotd-gytcw-elyeb-7zpko-zqugd-xhv2z-xhiig-yn7wt-opowo-hqkjt-7qe" => "type1", + "fklji-ssgwy-zhe65-ibmdx-rvn66-ffgrp-shnqe-ngihs-6uqcu-2cyxr-tqe" => "type1", + "gcuhs-tkcbz-clrlc-5igua-nlyq6-sy4o3-5cm7g-4h6fp-cvwff-gulmc-5ae" => "type1", + "ig7nu-g4dod-mbdlj-vxfxd-ypg5t-lmf4z-de5sg-fbod7-hxtgm-rowon-iqe" => "type1", + "j4iqd-awpgy-rryy3-l757v-6sbbb-6kfoa-yf3uv-3dyzt-gyogi-hfnom-6ae" => "type1", + "kr7kb-gysmk-v4sym-effou-xcfto-y5ztu-ntc3l-6vmf6-s2xvk-kluxb-zae" => "type1", + "ld4l7-atf5p-m23yt-na5oz-actjn-2klyw-xod2j-6yi54-iuqf7-ln4as-7ae" => "type1", + "m3s5o-tq2bv-fr2tl-cfddz-kzpdh-zwgei-33ptw-u33lm-5sf6x-zbqlb-4qe" => "type1", + "mru5t-4okss-sqbty-binpv-kjh6e-mspcc-3c6in-3mcrf-tht36-kqvq2-lqe" => "type1", + "mucpu-fdszg-52bjn-7j5dy-2pm3x-zatqh-rkxyi-zrplo-bmjrh-big45-gae" => "type1", + "oylzx-ztr7p-d62l7-vnmqf-hspfi-uoywu-zi3wt-tunk3-rzleg-zdrha-7qe" => "type1", + "pgim3-5bk4s-y4auc-ocnuj-gdsqb-iv2yk-qi4t7-75bpp-kbz2r-bdtuq-4qe" => "type1", + "qyk3t-cmxxq-2mapb-v7txs-zasvv-u4rmz-5seft-llldo-5b4zl-frmmk-lqe" => "type1", + "vnjyo-e3xna-nf7pm-depum-2ak7i-sc43a-db3ei-43zhm-kg5fr-za6y3-rqe" => "type1", + "yncop-s3nfr-crv5w-r5ouv-n3gpb-mdwy2-wbco3-hwhpr-uj2bi-3aewl-gae" => "type1", + "zxt3p-naibu-wlon6-w2h5f-ur74w-wxde7-x4zxc-ghfgs-dgbpr-65lnr-wqe" => "type1", + "2tk2h-7snvn-clgy3-g33vp-fc57w-ptljs-xujfq-fu47q-ludhi-g7zzf-xqe" => "type1", + "3iiom-54bdc-ldhpk-4cvoq-ir6lw-ltmzi-mpbjc-vp4tc-7ymrw-dflgy-xae" => "type1", + "6453v-332ee-yfusz-cqhxq-egv6l-4k7l5-twzcg-mxjm4-g23ex-e3usm-wqe" => "type1", + "bfg6z-r35jm-aozxh-b3gle-dw6f2-bikru-qbrfk-wjli2-k6yny-bfwzw-aqe" => "type1", + "cilsw-jxcbi-qvp5o-7cylv-up5nj-2yykt-jtzha-s2uao-ee7uy-nprfm-vae" => "type1", + "cluon-6c5dz-l5eua-y7wv7-xjail-6yoyr-mrula-6jso5-t7li5-la3bq-nqe" => "type1", + "dafyf-uvzhi-ypecj-c6ujq-e7aae-m6y2m-5f4pp-v64pz-lfebd-awmre-cqe" => "type1", + "dh45w-jfabo-oxzpd-apcyy-552kp-qexws-ztnij-ckqsh-zfga2-f7sow-yqe" => "type1", + "draav-qjefv-lyojv-vrjkn-p34vf-cfx7b-pla4f-3rzot-e7w7g-hntuv-bqe" => "type1", + "etuqz-zkhan-chvtt-la6gf-eykfh-4khqj-5nv46-mbtk3-c6nip-itv3u-bqe" => "type1", + "gtfa3-saq3t-ymlel-lsf6d-ans7b-cr45x-xg5np-xbxyt-nxfrt-iynyy-5qe" => "type1", + "hhmhu-hgxo6-vpbtl-etbxm-fewvk-q4jtb-ljalp-tuaku-agi3w-irqjh-aae" => "type1", + "hs7hm-k72ny-ku5s7-ienrr-lpwyf-2n6wa-5un72-aqp7e-no3ji-326ux-4ae" => "type1", + "jenqi-v5dfm-ewebs-gvnsu-l6gni-ujqqv-rw7aq-bnplx-gfg2i-2il3x-dae" => "type1", + "lkajo-nadnr-4qwaf-gqbzn-hzw7q-t2b5k-o6rmn-uh7ik-s4ojc-rfzan-lqe" => "type1", + "mio4i-vzoqd-2plux-foird-ilqwv-oii7n-cnmsj-2p75v-uqv2p-iskvk-dqe" => "type1", + "n22ur-d5chg-oia3o-cteyn-hm5jd-vhfxg-tqvij-aabom-o6uny-qcid5-uqe" => "type1", + "nj7ux-d4ymw-w3aue-6hu2d-2iocj-bjs6h-6ywln-fpo5q-a5bu2-mdkgg-fqe" => "type1", + "psz2h-gwldw-4xyex-ca33n-lojke-aiert-jk7b5-mcnzp-4ohor-jrrw3-aae" => "type1", + "sua4d-2yomd-7lnkd-6kumk-tzsqm-mv5dm-prpte-zjtqr-x3d63-bapf4-6ae" => "type1", + "tav5h-wv4rc-ty5vk-jgfh2-vm4uv-rtcm6-vasc2-acr5w-vop75-sjvut-gqe" => "type1", + "wyqv7-vzpcs-e2dqn-464l7-bzmqr-7u5gw-cp6eu-73fiw-6vrq4-cmzvp-uqe" => "type1", + "yvu2u-pciqk-fz2qg-27u3n-wz332-qa63c-euswo-hrj5t-ryt25-6zlaa-hae" => "type1", + "bv2x3-p2b3h-b23rg-aoq3s-kdzo2-pipmf-5y2me-jfa4w-tb7vr-qistz-2ae" => "type3.1", + "fhg3q-muslh-pp7ur-hcivl-q3kof-mju7a-kjyyf-hjifg-nsa35-nqjv3-7qe" => "type3.1", + "lxgqb-dlqzi-schn4-i2mha-qh6re-eq3c2-3k6cn-oqumg-jkmgk-d4gex-3qe" => "type3.1", + "qpt6h-7m7xq-4crv7-pml2g-2jsxx-vxgv4-eka6d-hq4xj-73u5v-bsaki-zae" => "type3.1", + "shtwt-kuy74-afhyd-sivzp-nj3vl-vh2zl-hdsvv-yunlj-k3asp-zsigu-cae" => "type3.1", + "suty3-goyd2-t6ngb-dsgzv-vkvt6-w2x4t-lioxl-2xsaa-ztaly-y2ov2-hae" => "type3.1", + "63rak-vvm6t-c4cfc-bgak4-mkcsc-oy7jy-p4ow4-kdebr-otqwx-ok23w-fqe" => "type3.1", + "7muaz-6c5bc-6wjw2-f65xy-i7poz-jwhob-ty26j-ol5dh-hf3oo-dkgb3-6qe" => "type3.1", + "aqbno-vejst-3kuee-ksrrr-bo5p3-6nhht-z2coe-dg6he-ptywi-pzubm-bqe" => "type3.1", + "fvy7i-ux7is-cuvfm-2n2zh-5lpb4-oe2vz-bfnhz-oi5s5-jkzhk-phlj2-gqe" => "type3.1", + "ltzjy-54bkf-bwynr-rcqst-ncpe6-qlg4x-tvhmp-73bm5-75e7u-t7c7s-eae" => "type3.1", + "nbela-otrcc-dakmr-4ljx7-igd2t-3ge3b-xskv7-cnj65-co4r7-cjxyd-vqe" => "type3.1", + "os2wv-dchv6-fjwdg-nichf-3cra6-wccb3-57glq-xqgen-zwobp-aonve-lqe" => "type3.1", + "qobom-nt626-4n6jf-nrepv-xpdez-m4s66-fch4n-tdwdg-mibua-pq5xc-rae" => "type3.1", + "g5zt4-f3nhd-aquto-6vecp-tnrxe-gexie-kk7fk-znuxf-45cav-zcnlz-zqe" => "type3.1", + "if4w2-pekba-btwrw-6rlcx-erlgo-fdvuq-qlaa7-en4bx-iedqt-nrdo7-pae" => "type3.1", + "kzqmb-at6mu-giydk-zo7dx-pbcpg-bd2h6-v5w3u-p7jnu-ihalm-feqx6-4ae" => "type3.1", + "nxeqo-y6jk3-5zpcu-waryv-lz3ha-c2iuu-jljee-yzew7-jo5dk-6dgr7-3qe" => "type3.1", + "plbgg-2silg-344nm-n7zqv-lpy4j-uktsq-darh7-shrma-srui4-xrcgp-dae" => "type3.1", + "qepub-grdmh-34oin-6eus4-r47jt-t7enf-g4yyz-fbvpt-isn4q-vdg7f-dae" => "type3.1", + "4c63m-yfsxg-sy4dc-qhhvt-nwkgn-ib5fj-3qewz-svezz-2og3i-tlxll-eae" => "type3.1", + "7fiyj-xjxkj-fjvbl-tl4rp-6xvg3-moow7-dwtyi-nsxeg-unw7n-ivshf-pqe" => "type3.1", + "jk3km-afmc3-lk6bk-ej7aw-4e2md-56ywu-uvolh-trr2p-vs3wf-f6fo4-vae" => "type3.1", + "kgo2t-vidyw-yw2g5-pqwrt-nr227-rbq2o-pog27-zarc2-dfrlw-vvjge-4qe" => "type3.1", + "noos2-mfe4u-r4gp3-jrjow-ancs2-ncakm-zi72z-zzcbg-uam7p-d4s6s-6qe" => "type3.1", + "ntk4m-3jwml-24enm-akwhr-gps2m-xe36d-qlkd2-scgcu-qmjik-nrk3b-wae" => "type3.1", + "alo6p-xfu2p-hz75j-z2has-64evw-zhsjt-4lhp5-ne56v-xifpc-cpq6h-wqe" => "type1.1", + "3o5rr-5acje-r4kun-h2deq-fbv6e-gyujh-222t2-b4ob6-rukpb-kmjn7-hae" => "type1.1", + "4cldf-jb7ta-kvxlu-e6otc-fotnw-cgjot-vmo27-pt63w-btc7d-yo6dn-eae" => "type1.1", + "5qw6d-gs4jl-hfhco-hetj6-avx4k-q6kee-s3pq6-io33z-iyjx6-dkx5l-5qe" => "type1.1", + "bq47i-nl3wp-zbsv3-juxt5-ptcqw-wfm6e-eqv5e-m3y4e-w6xgk-z7rmw-eqe" => "type1.1", + "77vl2-fnnm5-k5m3d-lkgnn-pspvq-ld7iy-xp7s2-3hk2s-ufzbi-tcclc-gqe" => "type1.1", + "7pxqq-e4kmr-saxrs-jziwc-sfdft-bl2wh-k7vhl-6a35o-2stq5-v6uso-3qe" => "type1.1", + "i7zdo-qrk2t-phjla-2unbd-p76z5-eg2m3-yko4n-5dclc-rewwh-t33gv-wae" => "type1.1", + "jztlw-rpjdi-5nruc-vm56o-n3ezj-q7kjk-ggenx-jyqow-3safl-3gyda-gae" => "type1.1", + "kc5j4-yilq3-6v4m7-5ongj-rzlki-t7srs-m7pjb-fa77e-icyep-ptouc-uqe" => "type1.1", + "lxebw-azsxd-nejr3-ye7ii-vdyjv-kppip-trprn-pdn7o-daluo-5sqtw-eae" => "type1.1", + "n4yvn-ac55a-ixjvd-7h7xc-dan5u-ksjzx-zsi74-emfwt-dsfbo-ortnj-hqe" => "type1.1", + "oimru-7xylm-5ndhq-ggeb4-okdx4-4s47u-cndgm-vln2l-5vfwr-qulvi-3qe" => "type1.1", + "ttjma-2i6x2-e7nan-fmldh-po4oj-nfksl-czde6-gddrj-22ace-3oq36-uqe" => "type1.1", + "ucznq-t6max-wjxsb-yryjd-qgrbu-5nn4z-qfwqk-chaap-lymtj-cktlx-jae" => "type1.1", + "vguej-xvzwk-r5a4a-fkmbl-pxlxt-y6ser-ldurz-dbt7o-7k4fh-biuuf-5ae" => "type1.1", + "vw36t-lgksm-5cgtg-ait3y-sy25v-gdtsq-en6oe-md35y-a7cji-uiryt-3ae" => "type1.1", + "z6jp6-245uu-gh3cs-sblcy-f3jmj-s4ngl-v3z4u-lafz2-qudjr-6mbqx-vqe" => "type1", + "235hh-hmjhq-dejel-3q5oi-pdz66-dygbp-yi2sy-zmuiq-rj7r7-65hue-wae" => "type3.1", + "ct3c3-aaohu-xben4-mbsjf-evtcb-bvf4i-qax6r-i4lm2-tcn6j-ybqpz-wae" => "type3.1", + "m3ju2-x3nqm-fcrha-zbyll-tl7no-7iwen-ph3y4-5xxwa-cniii-yu2ti-zae" => "type3.1", + "pekym-jis5l-i3ucx-gujvl-ncj7j-7xk7k-uwwkn-qshzy-z765w-rjwdn-aqe" => "type3.1", + "pqcqa-fs3ly-y7v77-4faa5-jzuay-5i737-t3olr-w5o4y-7gkog-zqan3-7qe" => "type3.1", + "rq2bj-ek3yy-mjj6s-zewtq-zzke4-u4zix-tz2xd-xigpz-s5hih-4wpmt-iqe" => "type3.1", + "uao44-6xaz7-xmjyv-wxzqn-fawuf-hpz34-d7ea4-2ft3d-znzye-k246e-cqe" => "type3.1", + "3dnev-emean-nwuoo-djz3d-nw6u3-zoyh4-scoon-u5xlc-mlxsm-m3ys6-hae" => "type0", + "44iwn-x22dg-wvi3r-gt7xn-udrbj-uwrrj-qwpvt-nqldr-d35ia-bdx27-6qe" => "type0", + "6f66d-yqupc-trqgh-bdzkl-5h7mq-5rd55-4yfk7-iq4ex-gfodw-b4dxx-tqe" => "type0", + "6gl2o-2v73n-q65pc-nf6tv-avcbc-hzjxh-urwbq-vurvj-wj5og-3xmnk-tqe" => "type0", + "75run-c7fya-7uoor-jzgtx-pwjc4-lfhyp-62p2w-2pnyv-amqh6-tdhji-rae" => "type0", + "afeaf-65btd-vc6me-h6imn-qnrqi-j23tx-gf52h-uv6ca-qv5sw-dbvzy-7qe" => "type0", + "agxrl-l4wev-coutf-zvpjd-sqkbj-wasxg-rok7t-mrwjp-362te-5ngce-iqe" => "type0", + "axo3p-axths-mmqxt-2gawc-zakoe-mgbww-zv7hv-ppnp6-twqz7-umjz4-kqe" => "type0", + "bftn6-hrt7w-3wd6b-i57zj-bpcqp-mrn3c-jagsn-ced4d-4e77g-nwhfn-zqe" => "type0", + "bmlxd-vohad-ymfvi-hm7id-7g3vp-236w4-n3cqd-tkwgf-wlrww-lqbcr-gqe" => "type0", + "c7xvz-smsfh-zia7w-jeobz-hd52h-ynhm2-dilea-25vi5-5noty-gzuls-rae" => "type0", + "clgez-cnier-zieyq-77d3d-njbsf-p34cx-pbzm4-stdmo-wy62k-d5ng3-4ae" => "type3.1", + "eqkwl-dlbnc-qvlun-4pyek-55nin-3piem-ljccu-52hda-oaxnx-ts6db-tqe" => "type0", + "fdunq-3nknr-3nex5-eravm-bauyc-ry3fs-mncyc-nzp4b-4prvk-4grhz-yae" => "type3.1", + "gojj3-3h3jg-rm33m-ces4z-bzops-c42hx-kwpjy-vm2ev-bfpzk-ednuh-gqe" => "type3.1", + "hq3ax-gfadv-rotxa-reac4-brdqf-vefij-h5fdt-4fucu-j27gx-n6cm2-gqe" => "type0", + "kqf7j-27dmb-6mtlc-kqnsm-ujlcw-3mw2p-sia27-gvkzk-qaqf6-6eekr-eqe" => "type0", + "llmi2-s5gnw-f4dqt-oflqn-gpfgy-4ah62-in5cl-otwgn-w4wv4-mbrkf-tae" => "type0", + "m2yt5-mn7lw-76bqj-ijlgn-55n3k-xbjzl-43olv-6xi4p-2ecp4-eipxu-xae" => "type0", + "ml6lc-ukm4v-rxmjz-dci63-wsytp-poqta-j7qfc-pk7f6-klj4y-fuqkw-kae" => "type0", + "oogpo-zejrk-3kogj-gekxk-i26xw-kjpyx-nqqfh-rh2qd-aagvh-rxjgw-4qe" => "type0", + "oui6x-f7txs-jlv4b-qvqai-yab67-m2eu6-ka4xt-sgc4w-olu3k-pt42f-jqe" => "type0", + "p6pnd-h7v4x-43jo2-6wei4-p5ek6-dq5ey-awcoi-bs64n-32n6k-4t7ku-3ae" => "type0", + "rurko-bjadb-7qnhu-yvszf-o7wrr-sxiq6-wj3xe-wdgeb-egac5-dsd7v-eqe" => "type0", + "wovgc-wcby2-ojzzr-a2ksn-rodxg-wmzob-dhy3r-nxho2-iznvm-wsi5z-uae" => "type0", + "z4jw5-v4ee6-aa7gr-5axkc-4ocjy-v5vv5-inwc6-ma4hw-mb7jv-3skxy-eqe" => "type0", + "zogei-smf6m-6rsx5-zcjzq-yfmlz-ddr5k-oolqy-vmtks-unmhc-wqmyu-vae" => "type0", + "4rpiz-7w7bf-smfm5-u7j3c-we6q7-qehxb-5lqnq-tjqsh-f7xu4-ihpao-zqe" => "type1", + "5eixt-ipfle-nr2qo-odmfu-rymbn-5ommv-n5rw5-yaili-g7tam-kzsac-rqe" => "type1", + "a6t2w-sxcps-qmgvc-vlitk-kvrsv-pqpl7-hylkb-urlhs-gove6-ehq7x-iae" => "type1", + "bhrbv-chf2l-tjjcj-mqryf-7z7ww-z6xi3-su7hl-idbfo-nnfpu-u6aju-nae" => "type1", + "biwrd-kahzx-y7tjm-m43ho-zxc3y-vfikq-3o7ux-ykhjc-qotsq-bvzai-yqe" => "type1", + "bwicx-4gcmo-yswte-eb3ec-mc4cf-3wdjb-ueiuh-2xdwn-qnpn7-luand-6qe" => "type1", + "ceqbc-sqdzs-bquge-3mmjz-7jc7z-ahpx4-mpwng-biiwp-xn64d-caque-pqe" => "type1", + "d2hzh-j3wzi-mkmcx-ufb6u-5gft4-ta7yt-xq4qo-dncvt-y6dov-xbujm-hae" => "type1", + "dbpb7-eze6y-jdemh-2zemb-nuwhl-75ia7-xpwbf-26quj-2q4ok-u4ado-tqe" => "type1", + "dd3ye-qncdq-jvghc-iymve-55lbp-hf6mq-6a3pa-ms5yj-espcn-n32jm-3qe" => "type1", + "dnxq6-e2xox-dtmag-4ofwe-xfyko-iqr4i-zewbt-raveo-dipyk-zbjqq-rae" => "type1", + "dzol4-3eqco-5b5ag-ritui-tyl7u-i3iwz-ex2qo-cbfjx-5fmxw-yfgwo-tqe" => "type1", + "ebfjs-4yyde-6v3qo-zhvwl-2fvp2-4tarv-autre-tparv-kwi7f-t4wml-fae" => "type1", + "elons-ke2ex-4ykad-warsf-bi7zm-oskxc-un44b-phznq-sovqe-ko6u3-nqe" => "type1", + "gyty5-impo7-5uugu-2ocbi-ekcg2-eloni-cj67g-vasbq-i4z74-2bhpw-7qe" => "type1", + "i57fv-35hgv-3ix6f-dwxwi-n6oug-5wlc2-nphaa-3jqfg-wf56r-3ndbw-3qe" => "type1", + "kvj4i-xw67n-f673e-7unem-npgcc-ag7ya-f46sk-g7qq6-ep6l4-brrkt-tqe" => "type1", + "lwxeh-xayoz-wu5eb-hwraj-a3pew-yeioj-cnwat-5uow4-ugxkc-kx44o-4ae" => "type1", + "n4d4s-hgx3a-nwox5-tmfqw-q2gu6-mlzhy-deyzq-zu6r7-wryv2-napyl-qae" => "type1", + "nu5cn-l6rgt-kqeyj-daqss-hfdzc-hduqr-vqio6-j2mwl-4hhto-gw3po-mqe" => "type1", + "pbken-vny4r-j4a2m-zlvt2-kjj4i-pwehn-ftsa4-ne4xu-yz72s-fk6nz-eqe" => "type1", + "q6bkk-dqhlg-orvwk-4lfjk-3fbpu-y3s4e-v4ivs-5ys2a-v6ok7-yqolr-iqe" => "type1", + "rgwtg-tqevh-uhktu-n7qg4-dpqdh-rtt4m-k42lw-zfsrz-5dwmk-nbb34-iae" => "type1", + "spr2z-4kaf5-2dxlg-hatfo-obp75-b6vwh-ts2z5-eurny-rbun2-z6m4h-kae" => "type1", + "tvote-xqphk-gfqph-ra3wj-u3sd3-karqx-6fimy-altcr-27fmf-uc6vi-aae" => "type1", + "wy2t5-owrk5-lfq3s-h6swl-iwhqj-4bdba-2v3nt-hkja2-jadjq-cul6d-qqe" => "type1", + "x3rso-73u7b-3pyeo-3fmcu-g635n-ri47m-jv3kg-zjfub-aqxsb-tnxbq-nqe" => "type1", + "ysjs7-fe67q-k7pxe-g4puk-d5z7p-xrtg2-xqcmy-pmpoa-7bcfu-pmlqy-rae" => "type1", + "6t3hc-abwek-snx4i-s57g2-w7u2g-beaec-jsjgx-g47s7-zjeb2-cb5pu-yqe" => "type1", + "7mwzk-chbqh-ydgzb-j3bvh-3l7fh-xc7xm-aud2g-hxyx2-bqcop-st67c-dae" => "type1", + "7up6g-ihrwe-e2at5-bi6e2-6rjeu-yk5mv-wabsc-qsu7x-ve6kj-2kyq4-vae" => "type1", + "ag3bm-tdrfp-kki7m-yfwop-aoc4g-f4sjz-grd33-7zy3x-xq3tr-hpbuh-zqe" => "type1", + "b74lr-4ly45-pyqyx-jo4yd-eptv5-7fvps-zjeca-eugl3-ih77p-yjyu5-7ae" => "type1", + "bayvy-p36kq-krz3d-o5lay-rxx3w-h35pc-235tv-mmhfe-b674r-xwfnq-mae" => "type1", + "c2dnc-3ozfd-ew52a-yvmrc-mjyss-edoch-k7cki-f7vzk-zqapg-aw44i-mqe" => "type1", + "c3xxv-clwfo-3pfn3-srt75-3wxn7-twfzm-7o5g6-pfvhd-rvsuj-47d3c-2qe" => "type1", + "d52j3-6vwb2-ij2ul-sqxhg-wmck4-bh4ce-eabt4-3eqqx-djb6m-n4sd5-xae" => "type1", + "dndht-2fce7-aze7e-coa4j-yvyuw-ut4hw-gn5dj-xvxox-whdhg-txikb-yae" => "type1", + "ehshg-qtj5n-d5cdz-vaaec-665ol-7akv5-atlvm-jmdkw-5lo4t-rqiac-qae" => "type1", + "ew5us-dwxat-fg6sp-opp2j-fjjg7-rcmyj-d3saj-vyych-f3keg-5kor5-yqe" => "type1", + "foasq-jql3g-5njm3-75iv5-5zlx3-lbods-dnsbj-k7srs-3isgp-k4wlo-tqe" => "type1", + "g2yrq-l6heq-ztxzm-fd6aa-piqxh-wno3t-q3bhj-5qvd5-vukzm-z4bjx-kae" => "type1", + "gtjga-mlpu7-5vz4f-knn5w-i2fea-cy3r4-vq245-ip62r-fosvm-7w3ck-2qe" => "type1", + "h5hs5-veobj-ft6ms-fr24y-efxzg-fcidc-s2dbn-pcnke-siz2k-bejcm-uqe" => "type1", + "jlb2x-vto3e-r2yf4-adrqu-3gult-4fpun-besaz-4nynm-gcmht-nziru-lae" => "type1", + "kte5g-iwzok-3epfk-lovgf-cylc5-57lmu-olhwg-w4jsn-dquju-5xun6-uae" => "type1", + "nf5ha-6wgiw-jhlaa-rs7hh-3k66r-mou6j-wudlt-pckzu-7goiz-yyfcc-gqe" => "type1", + "t7u3k-tw44d-sewhz-oyy7d-jvial-tci2j-avcj3-3rayz-7jydr-33scr-yqe" => "type1", + "tybza-gyple-63wq2-qsgwo-w6fqw-6trwu-awukb-skekh-67bqu-qsoeo-aae" => "type1", + "xypz2-ls2al-37boh-5a22g-5z3qj-rh53t-63qjo-khc65-izr5n-hth7g-uae" => "type1", + "yi5cv-jplzh-hnw2e-cmfyt-qpfvw-pes2g-7jc4j-qfrmd-o66m6-iz4f3-iae" => "type1", + "3b2lt-4in5i-knoux-f6nkg-tgfpa-ifejj-xkw5s-qtqam-3sqxi-bhpqc-6ae" => "type1", + "4fssn-4vi43-2qufr-hlrfz-hfohd-jgrwc-7l7ok-uatwb-ukau7-lwmoz-tae" => "type1", + "4lqcl-tzzs3-jztya-ro7ak-ew5ue-l3tlc-5cxk3-4jt7m-z66st-ad5ld-aae" => "type1", + "4mm7j-dmeng-7ib4h-yvt3c-g5ed6-i5yar-rrc6w-rp7tk-ej3by-gilvv-kae" => "type1", + "5aaq4-rdwmq-au7kc-lziuc-ii4hi-ygyx7-qalig-2ytfu-rsxtm-h6pt5-aae" => "type1", + "c5w2g-uilsl-icq36-vonoi-4k42z-hiutx-4wdrr-3u5mq-pvb42-j5om6-zae" => "type1", + "ega5w-ekfo7-lejfc-fve6j-vnml3-2nmxj-flw2p-564o4-rhier-bz73x-aae" => "type1", + "flc7j-p6oyd-c5hbs-f5h3s-2lgxj-ryled-d534j-wd4em-jujlp-xpno2-jae" => "type1", + "fqxyo-ua4oa-73v6o-gwpwj-pd4vt-eb276-bjjab-4vfhi-7izzl-yhqod-2qe" => "type1", + "hgvcj-xdftg-62bcj-s66dr-rsshe-rmj6x-mzlvg-65ytj-3wapx-ig6js-mqe" => "type1", + "l57yq-ottrc-3clfg-uxx42-wvw6s-znzbi-swx2r-sbhhb-ge5gw-rvy3u-3ae" => "type1", + "m4e6a-3t7oi-ooshc-2b2vq-56xpu-pyo7q-qafn2-3sem2-cvtck-6xfqy-vae" => "type1", + "nrz3y-3qhvi-mhz6v-zlvpg-wvwn3-fsmom-wtkla-p67ly-fgsl2-sf5g4-bae" => "type1", + "oijdr-tw52e-rxqz3-jqz3p-svaqt-xkl36-uwesu-rlfgq-b6ztg-7r7no-kqe" => "type1", + "oo4np-rrvnz-5vram-kglex-enhkp-uew6q-vdf6z-whj4x-v44jd-tebaw-nqe" => "type1", + "rg2yy-3or4h-pllvs-aczz3-dgf2k-3m3d5-oresl-easzv-2xa6h-7eoda-zqe" => "type1", + "tetdc-mvyld-p3miw-jdreb-5apjz-w73pk-3y3jo-3w6qj-2lrsc-6d2bq-nae" => "type1", + "tn2ne-tskw6-dfk3n-urdmd-krtq6-tcebq-dx2xr-kokq7-eood7-fadkg-5qe" => "type1", + "tsbrx-uiuyw-pf3h4-bx5i3-daxya-ygait-mnedj-kj574-vsw3u-fwuuz-5qe" => "type1", + "uvyny-tt2f2-rb743-fk6ia-qn6ut-c3qyb-ob3aw-6qcvm-qbktr-pco7n-2ae" => "type1", + "w2zci-bq5jo-vzrng-42mks-hvytn-she3z-52pej-vmigw-tic67-4prc6-qqe" => "type1", + "wonxb-wwggg-nxsfi-mkmmd-b5g65-2h3ct-glu5s-o6jb5-5ifbv-w3zdv-vqe" => "type1", + "wwwxf-wrvqf-3l3mr-iryoj-gvsjj-yex74-mhput-ag4mv-yftkg-xny2a-gqe" => "type1", + "6syks-la2v3-ua4aj-iyt6f-oivnq-6yosi-cqxwo-ahzno-ieuvx-bl7pf-vqe" => "type3.1", + "bbagu-dnle7-gc3zz-7dqkj-aflcj-c5nvr-w36w6-zbk6x-mutkh-sj55s-yqe" => "type3.1", + "efnid-smcmg-3orkr-3csy7-yo6jp-4vejo-zo2xa-zobrf-qkez7-5y3ui-3ae" => "type3.1", + "f4xs2-aejji-yt4ks-7sjyb-g7zxl-ayuc3-lw6tv-khrii-nlh7j-ttbqy-zae" => "type3.1", + "ltav6-3lsov-3cp5a-lswi6-dwyes-nyery-l3in3-tqcj2-6o4cc-pnqit-jqe" => "type3.1", + "ofemt-rbrvy-o6hne-hqs4p-xbhmf-go5aj-3gbs4-gj3it-akve3-2kklg-jae" => "type3.1", + "pojjf-vh65n-rephu-6rm4t-3zdx3-5pnon-csrf2-wlm32-z3tak-qyefb-fae" => "type3.1", + "snwzs-jy7bs-y6t3i-kegus-3s5xm-ob7yz-bcje6-qjb2m-jgndt-5dvbl-oae" => "type3.1", + "vte5d-zw5lg-axpbi-yrc6s-nm3mj-knu44-piwp3-ukrtj-xb4s5-mleiq-lae" => "type3.1", + "wdg4t-afemy-nttou-sdh2u-b6lik-2cuxv-frfoy-wjige-z4qy5-wheg6-kae" => "type3.1", + "2a7fq-jivc5-upcob-l5e6k-v3l6a-xo4fc-5rfga-7t5ng-cnv72-t6z7d-wqe" => "type3.1", + "2nnkv-2c4j4-m6kml-3ftsa-4rah3-2bu74-lkcxf-cm776-2bclz-ge6s2-sqe" => "type3", + "2w5be-ukk5g-kmecu-jxtan-w2gsb-htdfz-z5czd-6yacq-cemqu-qhjpm-iqe" => "type3.1", + "53yhi-vczlg-v3kl3-4ngxw-bexgv-pe5u2-e57vf-sqfs7-dutii-ebq75-2qe" => "type3", + "5az5s-5xxrd-ip5m2-6ps3s-uptc2-mz5gh-5ntqe-bmu7p-bhdyu-x4hu3-dae" => "type3", + "6zj3x-tsbc3-jfzjr-lrnay-maejy-wwrrr-byxwk-kdzqu-nrkmz-mdcrx-aae" => "type3", + "bqplp-iejs2-2awcz-oiy6u-62jzx-rzp66-z4eba-m6nan-dtljh-bb4pn-rae" => "type3.1", + "c3syk-l56ky-owcvg-y4ogf-3qfti-elv4e-2bqp7-k5v32-ibzm6-o4k6s-eqe" => "type3", + "ctgsx-urhsi-pslxe-6p2vy-5dlpy-mbo7b-zdpht-zzbqt-j7hga-qsptm-4ae" => "type3", + "daawl-5c7hb-iltb3-rkphn-d272x-kkpu4-vb5nr-4qxzp-azreg-47okd-gqe" => "type3.1", + "fpjx3-tfebc-csio2-hxi4u-7jomj-p4c4q-b66xe-ngut6-yi63x-keuhl-6ae" => "type3.1", + "g2cpu-evuez-tk76a-n54kf-nxeik-c5b5g-hpdxo-sqlq5-st2ov-4xgzi-hqe" => "type3", + "gsvxv-tou7p-drlxw-rnldp-m7z3n-e42j4-nrsp4-dzsfc-7wuqr-2qwpf-pae" => "type3.1", + "hcjwo-3h7be-ds4ff-dasid-xbp6i-4uxxq-s4dmi-kwf5g-yzvyy-wakil-hqe" => "type3", + "jemyk-uhint-w6ftv-s3xpy-ne4hr-xbcpu-aqskm-6a4cy-orm5q-aqqwd-gqe" => "type3.1", + "ked4e-syml2-ao2ub-xgtxi-awdfd-2vkf6-lbol5-prycv-2vpb5-ws5jp-oae" => "type3.1", + "oe52f-su776-yflut-ihohm-rm7gp-47fvy-alweq-ndg3i-g5uci-qzcvo-jqe" => "type3.1", + "q6i2a-r2jyo-qvchx-fx2eg-5vz74-tb674-au7zd-cy24h-wvywl-zbyro-7ae" => "type3.1", + "kydvk-tokwg-vqxwp-xszgh-5reys-aprlz-jpyjd-uwhlz-rnii4-pijvj-yae" => "type3.1", + "lyhuu-ljnh7-ba3zz-g5ftu-xepo5-a3bqy-sggtx-6wgu2-5sw77-cxnfk-iae" => "type3.1", + "mvb2f-rjwwu-pek7l-bsbr4-5dj7p-mypt4-4ia5d-iptvq-qi6sz-4hzaw-jqe" => "type3.1", + "mwrqx-e25kz-tchcz-quxpv-jhwdn-rwxeg-au4ci-xv5ko-s44pv-dmu6g-kqe" => "type3.1", + "po5od-oz2a3-3mjm5-sv4ss-exwfn-mlbr2-5vf6v-2f52u-uoqh7-yicbb-mae" => "type3.1", + "r4642-fjh73-ltnlt-qhdcr-kfujl-v3up4-5pnci-o7zqi-set7a-pvi3p-5qe" => "type3.1", + "sbjj4-mggba-56ihm-lf4hg-mjzad-sg6up-lv4os-56vvp-vagid-ieawy-iae" => "type3.1", + "talby-llnjk-kdnl7-f4ahz-qlxqg-p4n6n-bqcpm-3d2lg-jv7wk-er476-7ae" => "type3.1", + "uj5bp-c66bz-meslf-u4uts-q3njs-tgfnr-bueq6-372lq-yz7so-4fu27-3qe" => "type3.1", + "w4ri3-ytfnq-jg3z3-qseka-se4xe-b2fl2-km766-ruzwd-riw72-6bifs-4ae" => "type3.1", + "5ei6o-6mx3z-zag6b-ubdsz-odxr3-ytixv-rrnv3-khmkc-4tuni-ber6p-mae" => "type3.1", + "hlc73-cklhu-zo4mt-waxb6-ew5tx-gm4or-coy2j-xnwmy-xsycn-j6f4e-iae" => "type3.1", + "rcxhc-af5ln-myck4-qrbsj-aelzk-l4hy4-o4fws-7kx57-ajldl-v6pmo-fqe" => "type3.1", + "za7cm-bcsh6-dkayq-wbbbi-5pw7l-5dsqk-7isoh-osqdi-rmcot-pswxd-aae" => "type3.1", + "zzuq4-xiygt-ypkox-2tgdr-yvpzp-optye-xazeq-vnpw5-pata3-4jlle-wqe" => "type3.1", + "32yxi-hqan7-bhpqw-ypgjn-zq64m-6uqxs-htba2-sldaq-dkm7c-af3o5-vae" => "type3.1", + "3hibk-2fnod-r7pab-hb3im-blyqv-lyvdx-v273s-psxjn-yfsue-iakax-yae" => "type3.1", + "5iihd-fkroy-ow5zp-hlvwz-bsgbl-mecta-kxubm-6adxr-ckcu6-prsus-fqe" => "type3.1", + "6ricl-vwbej-yk73y-6ttjb-oefyv-cpjta-7ttqk-e4ucl-5rxsq-v2bag-zae" => "type3.1", + "amy5r-rdfjn-cmkrz-pjjlb-dqwo3-lv3tm-4x23k-tu4yk-lkruv-hxtu2-2qe" => "type3.1", + "clpy5-qdkib-dop3e-vts5k-btmkp-qncpk-kn4jj-zfiiu-a6b4m-nscgs-fqe" => "type3.1", + "cvryq-gpest-bfwqd-m6sce-7atse-publw-vcki4-yyny5-wb2su-yr44m-gae" => "type3.1", + "dtf67-kbgf3-apiap-nxgwu-wtt3w-53scu-ep2yk-rfqm2-ccwod-jkdsh-tae" => "type3.1", + "ieou2-4zpdp-4nkfc-nloch-f76bm-e6e2g-cfwv4-mbsdh-twzth-piqyp-cqe" => "type3.1", + "jux3z-ivwyz-ury64-jth4b-rrbfi-sx5af-ci72l-j4ot3-nl5jk-z4ilu-6qe" => "type3.1", + "lf2qh-kizev-gkppo-tsz4a-tvev3-y7be3-gnyjz-5tmzr-ds56j-ylpwe-3ae" => "type3.1", + "ozt53-z3dnp-udkif-5iqi7-flb57-vr2ev-vxe4o-nq7fz-d6oro-go7mq-7ae" => "type3.1", + "s5waw-pnvds-kpwqg-n6bly-r45vy-ax64x-sltbe-bbzl3-74npl-u4sos-mae" => "type3.1", + "tkdjq-rhpgw-7ubus-5w3nx-jrsdi-w4q65-e457d-yt2pt-hsz22-yehmk-uae" => "type3.1", + "5qmio-5ls3e-yujlz-eqt7k-qk5ys-bscfd-7w2rn-2q3fq-6smjq-n7asc-xae" => "type0", + "6pt6z-rdv25-glenj-xvuq3-zdeeu-4c74r-smqeh-tgt34-exgd6-2wxvv-eqe" => "type0", + "7r64j-zqmz7-krqtz-zetpm-x56et-wkege-el55a-nbgwx-dffso-lbtob-4qe" => "type0", + "akbo3-imbii-tzc4p-swdwz-hh2tg-i2ffj-tnzjy-zycuo-3xjcq-zxbth-aqe" => "type0", + "ayugg-n2ex3-azu4v-3sddp-mtbj5-k3ygp-tcmmy-pp2f3-3faes-hw77i-aqe" => "type0", + "ghmoa-vaks4-tubtk-z3wml-wglax-lrfzt-6lh7y-uwgzg-ly4iv-zubjh-iqe" => "type0", + "ham46-r7u4j-yoyrk-mrnxu-utzq3-c37he-vcns2-gc5yg-jxyv5-mvlhc-gae" => "type0", + "m2g5i-bmomq-dyjao-k642i-fsfxg-trxki-6i42q-hm6ec-xu5cd-n5jhq-sqe" => "type0", + "wh2dv-njrdv-4xdwd-dflb2-mjoel-l6pkz-e37ap-pkcjx-jyasn-hawsc-wqe" => "type0", + "wix7d-hb2a6-2pcsi-xwm54-gmlxk-gfh3d-dtfog-h7o35-vzm56-w7h34-6qe" => "type0", + "wqqsg-ww46o-muchq-pty53-amebx-mucdz-ugjkt-fvjqr-5ncmg-knqg4-aqe" => "type0", + "wxhcm-5armm-j6fvs-lts6q-waitz-k6t4q-ncfbh-2t6ee-nv2tn-keod2-uae" => "type0", + "xcfmt-vumwz-773ew-pvwnt-lgwlu-prfzm-lvitu-bx7ru-ncgqg-srhut-mae" => "type0", + "yqbqe-whgvn-teyic-zvtln-rcolf-yztin-ecal6-smlwy-6imph-6isdn-qqe" => "type0", + "2yjqo-sw2lo-4tqkt-6znj3-42w5f-iixil-q5b6b-shnbo-fnmvr-gvgwd-dqe" => "type3", + "hsctm-i3ioa-wqjxe-kiofl-x2knb-za5os-w5bi7-frpay-wjf5q-yh72k-mae" => "type3", + "iqnlc-oy677-m524v-tisrn-3v44n-2eylu-bclq3-fqjtg-4ab7z-ycrie-mqe" => "type3", + "tcln7-paoz5-dzngx-cv5ih-rqi35-6dhhd-5j26y-mnu43-xbguo-vgp7h-yqe" => "type3", + "trupz-4o3zm-6itr3-6fjf3-ipbci-r2scy-pvycg-xlh4x-vtokf-avpr6-oqe" => "type3", + "u3bgl-kw7h5-djuod-zigyy-xp3ux-l6y3z-e223a-wmaki-i3kbh-fmoib-sae" => "type3", + "23ykg-dluh4-32ndj-zw7au-wa3id-ptbpg-mtqk3-7xpck-3ys3p-klsqm-xqe" => "type3", + "5v4on-bsceg-rdgxe-zcqqf-l5wnq-fpxw7-x3ktj-3x4fs-o2cny-uzhor-vqe" => "type3.1", + "7jxpa-y2d6g-zsy7g-vnjp6-sblcx-32lke-hulup-hj3y4-i3svi-pi2c5-yae" => "type3.1", + "e6334-h54qb-rfmfa-kktez-s5tjs-q2ihe-fazyk-ybuo5-leln6-v3cfi-dae" => "type3.1", + "hdkxy-jonbm-voico-5u7px-6vet6-6i7zx-xm65d-gdswt-2uvss-5o73g-aae" => "type3.1", + "i22yb-niksa-7n5xn-7i6ha-6duwj-kneqt-gikln-auy4j-tjk2b-lms5t-hae" => "type3.1", + "j4et3-lkqhr-32t4q-3o57n-nm7u3-hvgsd-7v574-3z7mj-eyks5-twtxo-zae" => "type3.1", + "lfque-cnxjq-mq7nk-tcmti-tm2bn-sgwin-uclrl-kdkmt-y6epr-ltweh-zqe" => "type3", + "mcmqr-z4yrw-7wnwf-au7wv-5f7az-fco3a-vuxx3-7lol5-sigpc-jtnqq-aqe" => "type3", + "nuass-yihti-wszdr-r7qze-32dgz-osovf-pracd-hg5tt-mwlhf-rvuyq-4ae" => "type3.1", + "ocvcv-56eg5-gxibh-dcgpo-unhwq-4bwmo-x7q3h-stwf2-ycfex-gdr2a-kae" => "type3.1", + "rv4uk-b3egt-xwgor-q26dh-7yv4s-er4s5-kn5a2-nlrht-zsatl-47q3w-zqe" => "type3.1", + "u4wm2-yrwzl-4774n-y4fkc-dgijp-woboh-5v4mi-7grjs-6z7th-zwrbm-fqe" => "type3", + "ulcre-xbhsb-in5ny-xqhwy-nolvb-xzw22-fcp65-jutld-c2pjz-y7ydr-gqe" => "type3", + "wazkf-2xehg-y26ek-2uq64-pkrbh-6frmq-4q7lf-5gaya-44q4p-v5r5z-cqe" => "type3.1", + "wwpob-ttemi-zg5fc-us2jz-3q3gm-mhzmm-higzj-2bqrr-7tmoz-ajy7l-wqe" => "type3.1", + "xav3a-kdo3a-2rgbg-o6vnk-clat5-bcc7w-vmnej-z55rx-mfx26-7xugo-tqe" => "type3", + "yszpk-lt4rh-gktee-rctwj-hrajy-vbg62-jy24t-47ua3-mu55b-yxonx-zae" => "type3.1", + "5osj4-6yxs6-czubw-7e547-kbpd5-j2na3-37ciw-xhu7l-pqefv-orvkk-bae" => "type3.1", + "jlifv-mddo3-zfwdc-x2wmu-xklbj-dai3i-3pvjy-j4hqm-sarqr-fhkq6-zae" => "type3.1", + "jys4w-lodfn-h5325-xters-z3hf2-v7imc-ow7dh-py22m-epjvt-aldwt-oqe" => "type3.1", + "kwryq-ezysk-c4ono-aet7a-hh6h5-4o3bb-a33et-ef4g5-42tot-zaek6-fae" => "type3.1", + "q4ksz-lpvfy-ikkem-wyuaa-abopq-nmu5k-ovh6h-xfoqk-hx3x3-g2bk6-4qe" => "type3.1", + "qicnz-k4irc-lorep-ymp33-kx5l4-hnh6u-scxtx-rlwdu-remff-ubqb3-gae" => "type3.1", + "2kuci-tagmj-s2qnd-63jiy-s4xjc-ryvbs-gadtz-ywc6o-mcr4l-im6q5-wqe" => "type1", + "44hxg-ypmby-4grlh-grmge-hz6gp-vzir4-odo3n-6zvgn-xwcqs-46nne-gqe" => "type1", + "57dku-guaw2-l6pn2-upfg5-qnhnx-6vysx-cxpzh-fwtcw-56yxy-dfbmy-mqe" => "type1", + "5s6r2-75ps5-mkxnk-litjj-szhkg-wanvq-jwuly-drl6j-ldie7-2ols7-yae" => "type1", + "7ak5q-ev3ur-nmmup-rocyw-z2kxo-bqppk-vkj22-gicfw-6h37b-cpvmc-uae" => "type1", + "7w3cq-fboqk-xsflg-76qek-nguog-nofpj-ssxoh-q7ine-5w3ie-34egh-jae" => "type1", + "ahekl-ihkqy-u4jqo-5kjvc-ygeuv-ejmin-25kld-7vyop-d6unr-uhlrv-wqe" => "type1", + "em4bq-rtixw-mwh3u-jcmqa-svuus-leyix-3cpa2-vwj7f-byybe-gdwxo-2qe" => "type1", + "gb5dv-wc7gh-6yj6n-fhbdt-csu4k-h4f64-jnk6z-apq3o-pxmbn-vuvf2-xae" => "type1", + "hf6fg-j4n43-ufjh4-qzwo5-vtk6g-b6pvt-dyxs4-6toq3-fnmle-y67at-rqe" => "type1", + "jpduk-so45u-mskaa-bbahb-xyyst-el3zw-54o5z-r5wfq-zzydt-acr6y-uqe" => "type1", + "k67we-rbzgg-dzz2e-y3rki-ourfo-tfxdk-boewb-zyayq-l5m7f-su4px-gae" => "type1", + "kv4yb-otkiz-ilqle-wk3b3-upqjh-hvkhv-wmbqq-ca4qq-5va4r-matmt-oqe" => "type1", + "ltybm-636u3-akcv7-xgnfz-32elo-mskzs-e4zmv-rq4sd-xok5a-2zguf-uae" => "type1", + "lukcs-ackeg-qmzut-q7lsk-piijp-7bukk-ymtrw-sziz7-vbwde-sgkvq-zae" => "type1", + "ojfqu-qooyf-umyhe-bryn4-x6dcf-qv7ax-vnr5r-22ioq-jycjr-7le7t-uae" => "type1", + "rb35i-iw6yi-h4gle-oyhqk-ce3o2-bqrnx-34ddb-i3npb-2wdrv-o2sib-vqe" => "type1", + "rfxdk-pa5yk-3mljd-qldfw-245t2-gfkkh-7s3zu-jygwl-5lur5-4lvkq-4ae" => "type1", + "rvsz5-jddu5-sfq3o-4ntft-ymbkg-fyik5-lmjes-dj2aa-l66xg-7ogcn-6ae" => "type1", + "t4zmr-lcw2a-fa42e-rmr34-lvcxe-7exme-xyv3x-bzei5-uihvp-helst-oqe" => "type1", + "u7niu-lhdw7-x2lje-t4vif-jan6i-wzanp-twrqw-sv3so-ft3x5-iqmnv-kqe" => "type1", + "uizdf-gjwfo-lm2np-7de6s-53nph-a62a5-jy54p-xvbp7-nowgp-fscr3-kae" => "type1", + "x72tl-5x3u2-jyb6f-l5une-yx32n-3mcx5-rsza6-7x4z6-4jx6x-rbqfy-hae" => "type1", + "zh4od-vkuoo-tkh7e-lbtxa-jkoze-2irxd-xg4f5-4ldmg-6j2bk-r32xj-3qe" => "type1", + "5alj6-ij6fd-4efxw-chag5-7a327-7uvyj-lhait-ojze2-gxxtr-mgfkf-rae" => "type3.1", + "ap34d-la2pe-wiwkw-jpe5h-vn4nz-h3z46-vm2al-gdcoc-h4f45-xfnn5-6qe" => "type3.1", + "d46vh-m3zki-yvo54-bbb2b-rejve-nhgiy-6cb5m-amog7-25mpn-dnua4-7ae" => "type3.1", + "f3t2w-pzmmm-65xb6-u2afi-y2ewh-gwvto-f7qtr-2xhv4-awen7-m3cdg-lqe" => "type3.1", + "fqczw-cfksr-xl2d4-5bubi-2am2s-znzzk-k6tma-5o6dq-hmvnc-rjhfs-wqe" => "type3.1", + "ijend-azski-eod2m-d2lqp-a3y3u-x44xl-7mapp-bjr6r-qw4v5-okx55-cae" => "type3.1", + "isegs-i4akf-kb7st-hnqjn-4col7-qxpph-pncbs-25hiy-5pwsu-nyw3x-xae" => "type3.1", + "mhfef-zbxsl-rocpc-ovqeh-jg3ct-7nvmi-gzpnm-xjmm4-uekgj-rquls-uqe" => "type3.1", + "n2h7o-alfdb-7upvh-ulvn4-m4khz-yx5gq-3za3b-c4xiy-wxu34-3suzk-bqe" => "type3.1", + "qtcl6-k4m2v-zr4fp-2jvwf-2h46j-l3mg5-lwlxy-3wsh7-hghcp-afdsa-6qe" => "type3.1", + "shg6y-mwjc2-rqfxr-7pi76-x3jed-yfn4p-v6gue-jbai7-uraz4-i35vp-eqe" => "type3.1", + "td2et-sj5tw-njhtp-uw2ly-zwmwn-g4frd-off2d-5adoy-ybkoa-lj2ns-5ae" => "type3.1", + "ttjeo-6vece-x2hpb-lzmdg-b2fwl-7osa4-r5vb6-n4dyc-ocg5r-rztx2-eqe" => "type3.1", + "u72ns-336f6-myqpk-aetio-fnywm-inebz-lg6ws-5mvdw-mjw6f-ezq2i-qqe" => "type3.1", + "uf3xb-i4fzz-jtgv6-a5pga-i5cs7-ex4ck-d46lp-khsjr-3yzdy-xnjrc-xqe" => "type3.1", + "vn5yd-eiwsz-jdwpi-ox2xg-wlwob-bbq4i-s4l2f-sz7ek-nmjtk-vhoxb-hae" => "type3.1", + "wgigf-asogz-mx7ne-27hly-zynet-2ly45-q5pq4-wtmbb-yidjg-ctidi-7ae" => "type3.1", + "xbsjm-f7kbx-wrerv-ponbs-e3kjp-pvt6d-lgjfv-kolpw-c4j3y-to5pe-7qe" => "type3.1", + "34vxs-3ebd4-x454e-jacqp-5rxv3-lfna4-xtoua-vzulm-lwh77-zu5fq-lqe" => "type3.1", + "5hvab-67ek5-c7j2t-se7xt-zd3df-xjp6a-bnz76-kjx6e-bbx7e-rnwhz-nqe" => "type3.1", + "c6kyc-37zic-3xhsz-smhba-moq5m-k6xqk-4355l-xtabo-jkeyf-aenq7-hqe" => "type3.1", + "coqzx-lgyi2-hizw4-nax6t-mshs6-y4ml6-xon24-ioon4-rer4r-qtuan-cae" => "type3.1", + "fni34-xs5zz-z6t63-2gkxo-2fzxa-zery5-guujj-fbixf-lnqsd-ixwjs-bqe" => "type3.1", + "ftgel-apwfx-aqphw-6itlw-as5il-53hfx-mtpy5-djn6y-7a6fa-3nqrs-5ae" => "type3.1", + "gn36d-h6lnl-aojm5-5bztb-pm7nm-sha3f-a7lui-epylf-dpvsw-hz6mc-qqe" => "type3.1", + "lpkij-6jgir-sukcx-6kplx-qddla-ufncr-e3m2p-kikly-bcwzd-mtslj-4ae" => "type3.1", + "vysyd-tlwq5-f5vfi-kw2jc-ziylz-di2s5-sf4p3-iflm7-vyvjv-hz3b3-pae" => "type3.1", + "wjwzb-q3ogf-fi3po-kf6y6-wzuuj-3ac3m-kjvab-fufsm-z2skq-kthkx-xae" => "type3.1", + "3jol6-6a6dv-ysedb-5qlxc-baclw-ub23p-d324m-7sfus-bkbnk-jlh6b-jqe" => "type3.1", + "ncr4b-rasb7-tueb3-n4uos-5nxou-3wbxv-xmyt3-wfdsd-vu4b6-5x3cp-aqe" => "type3.1", + "b3ml3-v62hu-gdyb5-2raax-b33g2-nav6y-i5d3n-carmz-tosee-rbhk3-lqe" => "type3.1", + "j7mu5-sfjx5-rflso-aqhd5-ymijf-glw4a-ovi6i-f3blz-o54sg-a2rfk-bae" => "type3.1", + "u3ahx-ft3kq-pzlkr-imzus-aeqxm-cirqv-ymehh-xzctz-os4ud-3a74m-vae" => "type3.1", + "6hqi5-ctyoo-qijwz-dtvwo-g5puo-yaftz-dmaid-rambc-li5dx-q46y2-sae" => "type3.1", + "2lhun-vmepu-zc7ic-6lrjj-o4yda-nlmwn-m4i2i-h4oby-hwv4y-mpv7c-fae" => "type1", + "3wc5p-yqjw2-ul5u4-oitcf-6g67a-czta2-oimin-cr6au-vxxqz-bag3v-eqe" => "type1", + "4ql5c-ky7on-eceuo-hqkl7-lzxvo-mrjco-znpn5-hfs4b-kuel6-35hmg-qqe" => "type1", + "5j5bj-nof4w-umwfb-jqemy-uhcqy-bzioi-23ct7-xsb4m-ojzy6-4jxz5-rqe" => "type1", + "5jxvk-4hn3t-7bzwt-enxfa-4u7rh-n3fno-t33ly-74lic-z2uep-zqlvp-nqe" => "type1", + "5oph7-csqih-dxfky-syp3w-fvcpv-2vfmy-gh3i6-mcpko-j2cmp-radto-wae" => "type1", + "6adxp-p7u63-xsdtk-lo6oc-vpqmi-44hgt-yv652-cbm5p-mssge-wsrz6-oqe" => "type1", + "a3biv-gee7t-qsjnz-4hsb5-zmvds-l3tjb-bubn5-gevin-2ehtn-thooo-hqe" => "type1", + "bkult-ic75e-xfviw-iyidh-tivyz-r7fvw-7r4xt-aawip-cp7vm-4iu2v-rae" => "type1", + "blcw7-64reu-4kwke-es7l7-v5uel-hi4ll-a4oki-uw2n7-3dbdy-vdzgh-oqe" => "type1", + "c6on3-qhvg5-k5v5v-wcson-2wek7-blvc7-xnr54-gyakg-6etp7-khw33-qqe" => "type1", + "f3zwq-g3ap2-42acw-twrar-6wire-5uhtr-qyu2e-v7vl7-43e3b-kdz7g-yae" => "type1", + "fl2ks-flv23-luwoe-t3tza-xmurj-aa3ts-bcuda-gpz53-gnnfn-xzm7x-4qe" => "type1", + "hhixb-o5xrj-s5pty-esitz-m6s46-wgsez-c7thd-6h27w-7hikq-ae5n7-pae" => "type1", + "hieqj-frwxa-nqiem-krmuw-yvstt-wpmrn-wdeys-6mafh-o77rv-gxc7o-dae" => "type1", + "hr3lj-x426p-5hbqs-7yezn-5xyz2-bovpo-zc4rd-4rkhd-urqct-ksck3-4ae" => "type1", + "i5kts-s424n-xg4np-i2wnx-lbb24-d7imi-veczd-l5dwq-4bvi6-ltdpg-7qe" => "type1", + "jx6y5-u55cq-nwk3i-niahw-ogiah-vy46i-vq4oa-b7azf-27wgi-v7hhr-sqe" => "type1", + "nnrw2-jsoru-y6nao-advtq-jb3xe-rwml6-tplfi-2nlrj-ofb2x-6ione-nqe" => "type1", + "np4ih-n24tu-rdhck-pe4wk-b55zi-6teik-a327g-kjh7q-plk7k-zkhnh-iqe" => "type1", + "nsphz-ldqlm-3miwx-7dbuf-xj74o-44zvd-xhnzf-lpfsu-edhof-gscrd-4ae" => "type1", + "p3wi6-xvpmj-6zjye-564xs-zwglx-5ixeq-faeou-pbfna-64ezf-5znrj-aae" => "type1", + "pk6jo-5oauw-yubar-gifh2-zhhkd-erh4g-2wagj-5zh7c-saq7w-pb73t-dqe" => "type1", + "popmv-gccw7-zincv-gtcgo-jxfby-ar7zq-odvna-4jcs2-cnovy-qiha6-yae" => "type1", + "uzy2p-nvzre-vqaov-qj7vj-bznwn-mllrr-t25mo-lbody-dgyf4-o5rj7-sqe" => "type1", + "vavxh-wzhxn-jxxv5-cu57r-amggd-3e2mt-xeddr-iikdn-tyvdz-pxg7d-bqe" => "type1", + "wq5v7-ngito-7ztqs-zlf2v-ibk6f-e54em-t3hou-x24kz-v5j77-6vo72-kqe" => "type1", + "x3t5j-kmxkj-vmc6d-jxrqf-qd2v7-bszg3-mh4j3-a6yn5-2bgkv-wtmu7-iae" => "type1", + "5zqhj-66qn7-he3p5-76gnj-hlsvl-ajlt5-k356i-fgm4m-5it4c-6m5ag-7qe" => "type3", + "c37f7-3shrz-2mk6g-e2zvp-3z6xc-uuk6r-6yegs-53uzt-c3vtf-dacsv-tae" => "type3", + "dnorv-obqlt-6fl2s-ylxsf-o6pwv-fssdj-63kdj-jjw5d-luhdg-xe2zn-2ae" => "type3", + "fr3jr-74qz3-jzmga-mpt7c-eipd7-numir-hpczg-egnrw-lrrrh-2f7ws-qqe" => "type3", + "g3ug2-wz6kb-unyk6-6mj4f-halk3-kkfvi-k33iz-fftob-qipom-t4kzr-qqe" => "type3", + "gj6gc-mbslq-bt63y-72b7h-xs2xw-6vyuj-frvp3-tx375-dsqx5-wem6o-vqe" => "type3", + "ocony-vhzun-3ygcw-3mck2-2knqd-d47bv-hi7jq-kbfc5-xf6ui-ou5c4-bqe" => "type3", + "pm6hc-2ghis-xwera-jdcuf-xo6mv-ura5k-aurmb-xzoip-lx6nn-2rtew-aae" => "type3", + "qkcyr-pk3wb-n4w54-hofnn-mf6g6-uvhhj-lwzir-rw737-3ji4w-hqynu-vae" => "type3", + "u6e7b-mgtes-r5nay-mmrcn-p3rns-hj2fn-74dn7-cp2mu-kk24k-ljfnm-2ae" => "type3", + "vs5lv-6h44x-qfalu-aesc6-uehxl-zlwiu-5z7ik-c3kjy-6vtxl-3yz7x-qae" => "type3", + "y6xdi-6nbil-4w6ju-4vqux-wdl5m-uofuq-2hbv4-dels3-knu42-xievh-hae" => "type3", + "cboiv-rhphn-7rklc-clz34-hy3ie-r2bcn-dqnx5-y32dd-2aezq-ahkdr-wqe" => "type1", + "2htd7-tusvn-5az76-uabki-trilh-egfi4-5t2tv-z4j7e-bcgbj-dkxru-sae" => "type1.1", + "6qf6k-fjcct-wl47g-3onza-lzdjg-5z2mp-u6n7c-hbirw-ovirx-izl67-iae" => "type1.1", + "h4f2h-szkhd-6bdx3-r7z3i-dtcd4-nked4-6a7v4-43uxy-mffxx-lyunt-gae" => "type1.1", + "hes6w-pmcrp-mml4m-mgtqo-25yxp-x6nwd-oygpv-i23gm-l3clq-vsm4r-rae" => "type1.1", + "kwylh-u7itt-vwqn3-vskq4-ftpcm-kgyqr-23ibw-tgbw2-jfg64-fqwi6-oae" => "type1.1", + "l74v6-bcojh-ldmdj-xdotf-mvwlb-fsmor-u7wno-sp3ys-aum5z-xnug6-6qe" => "type1.1", + "nto5n-yff7l-mwmhx-ltx7u-oo5td-zizuo-vga6s-cyly2-vksce-77bdp-nae" => "type1.1", + "oh5wh-qqgpq-brv22-hsr24-jsgng-iiuew-gs34v-tgavl-e5wgu-vzany-fqe" => "type1.1", + "qyyhd-spoy7-gblwf-ar326-iawpe-gpgsf-talb4-e2fig-pnnhj-jty3o-lae" => "type1", + "jj7si-b7rs7-nq7sv-npame-aec3y-2anip-ol5s6-gxbh7-kjbg2-a5u6j-rae" => "type3.1", + "pbva7-4kbrk-oyagu-q2y77-izb74-bqzmb-xrpil-nfjxu-qzzpk-zgzid-bqe" => "type3.1", + "yi6r6-u4kax-jphcr-jcqr5-t3zpm-gmp3b-2hiew-iinpf-sgjos-eabha-aqe" => "type3.1", + "2mmpk-6kvr3-3syvq-twao3-g3fxc-gz3n2-o3tgf-34irb-idruv-cogfq-sqe" => "type3.1", + "oiso5-hxkkc-elqlo-iyoqg-7oux4-5mscl-zkvoh-b2432-ohrir-b5fcm-gae" => "type3.1", + "5w5mf-4b2jl-l5vv7-igktn-en2ra-jkwdn-skhc3-6etg7-qmei3-ijb2c-5ae" => "type1", + "65a3w-nbftx-ikuvf-6kgta-fxrza-tye5d-qggwc-4orau-6mt2e-xsszw-4ae" => "type1", + "6ssdj-55z6j-7q72p-vn255-utr5r-r2lgq-iitti-wnchn-rpwos-hjgrt-lqe" => "type1", + "7tayv-2pmqc-gaiwu-3kd3h-fh4qh-3moqd-aawsb-4rq6o-ui7fp-dofcu-iqe" => "type1", + "bija4-ybnku-fbggc-n3bqj-hardz-4mr5r-prymh-fripb-msk7h-ilh2n-3ae" => "type1", + "djil5-54fkt-55svu-26a7h-ttflx-dqn6u-3w3j6-zyuwg-cfwuo-7oi46-uae" => "type1", + "efdju-ef2ce-a5jdn-obybl-x6ema-h5lwv-nc2sy-v4hvc-7nltm-aldtv-6ae" => "type1", + "efwaf-xbg25-6hqec-pjsqp-fon6d-hdljb-5kxeh-qjzku-yxaiy-6jwim-sae" => "type1", + "g2jlm-xvj2n-x7gzj-66e5m-h6gwn-4pgy6-otp4b-5ssj5-ozl4h-bitxk-4qe" => "type1", + "jamsd-hvypj-w56fb-4cwvt-tqbmo-6652c-eaw2u-3njxg-5nfe7-ae6ht-bae" => "type1", + "jfryc-owgdd-a7pp4-lao2c-anza2-nryvi-gqkmu-m2moj-4hzai-zfdiy-4qe" => "type1", + "jthcr-uruul-ytxuj-qbvv2-byyz2-tyyo4-yy4ku-2fu4d-aufm7-d5k6l-7ae" => "type1", + "mp3w3-ut6qr-ef4ax-cwxtg-kkdly-q2xpl-4bisk-vjncp-qcyfy-5t4uz-vqe" => "type1", + "n74se-c345d-2jd4b-leuu6-jyvim-vcllk-aahj2-vjpvn-pbpks-xtgy2-4qe" => "type1", + "njsmq-lvuj4-fo325-pt3od-f5zqr-j64dp-u5l6n-kdffj-3gvvr-3rbfa-wqe" => "type1", + "nlghu-pupyy-n5oci-hcqvn-5fvdt-h23ta-gueoi-bkpbn-atazo-sfdlp-nae" => "type1", + "ou5js-fk2zz-pi2ra-jjavx-2ld2m-3l3rh-asgas-tof7u-gxveo-jf3bz-vqe" => "type1", + "oxa4d-pajkk-n7fto-dyfxr-z34x3-7zebw-zdqbq-qxzxp-qg7vk-nhcnx-fae" => "type1", + "sa67b-rto5p-rvyts-ehk7m-dpsnk-2c3w2-pmcx7-mzvgr-4p6co-ampd6-pqe" => "type1", + "scjri-zcrz3-27aoo-a2ggm-alu4o-fcu4t-r5jy4-7hdeh-prukj-4cols-qae" => "type1", + "tm3pc-2bjsx-hhv3v-fsrt7-wotdj-nbu3t-ewloq-uporp-tacou-lupdn-oae" => "type1", + "vcl5k-pctnh-iqitr-mg7h4-a3bb3-j5cpy-w6yvz-ak5tb-q4dgq-z5lgw-4ae" => "type1", + "wjidr-63mpn-3nnhd-knwl2-hiq65-to2lf-4zlii-o4tab-big3p-cbfpz-jae" => "type1", + "xzec6-ujfic-zvupf-3cgro-tqgtp-xi5ss-vtxfz-ycecq-n6jsb-t37nk-vqe" => "type1", + "yttmc-lxazf-6ctyr-aqchl-g5est-gut4i-qltuy-gds2b-7vhfu-43xey-2qe" => "type1", + "z23fe-7tw5j-f3lfl-4vprq-f6pdm-22uqa-73lqa-pokor-bvb2z-gsu5f-xqe" => "type1", + "zatyv-4l26n-3lecc-xpeme-ul34o-yiye2-3d447-aiiz4-5pxtm-v44nh-cqe" => "type1", + "zxxo4-abmdb-uj3ok-cbj4k-is36g-b5o2d-c6akl-vmgyu-d2wie-hgr3y-uae" => "type1", + "2d7n2-eizp5-ahi6r-puqn4-4ay4r-iuw3f-upywy-23g43-lwmky-uthpg-rae" => "type1", + "3ubw5-5mps4-pchvp-dvmug-6slw7-b2xxy-mbucp-55dvj-cj3fa-wejo4-yae" => "type1", + "6f7g4-miwmh-hbks4-ecx4e-hgxyr-ylhdh-slh2y-wivah-ydt24-uvflm-2ae" => "type1", + "6zzbh-w7fgz-tdyah-gtump-ef7i5-6kkpc-272kt-kf2ls-h2dor-waqzn-eae" => "type1", + "ahssx-gbqb7-tzctl-ncw46-zqzt5-kzd6q-bhgpg-wnxay-2av36-bak3k-4qe" => "type1", + "bbbva-usqzj-vwlpc-n2kbh-ylcxk-33ht5-p5jwl-twrtp-kghqh-z55ji-dae" => "type1", + "jtlem-ihl56-w5ila-rbypa-egv2e-a327m-uh5n6-gj4aw-qinez-dkknw-vae" => "type1", + "kywkz-eopg4-nn6md-cjb24-5ri6y-aq6au-vt57i-kg7gk-ch5pw-7er3w-7qe" => "type1", + "kzjcu-omavw-h4vok-bmlax-rg5vk-frd2n-isfht-x2iie-4fdgm-inngx-dae" => "type1", + "lj5xq-w2wqp-c4i47-phewb-xw2y6-atnl4-awraj-um3hm-2vkmo-jneb4-3ae" => "type1", + "oe5ta-7u4bx-fcugr-r2xtk-gxdxl-sh5ij-se45r-qb26g-3uopr-7fdsl-sqe" => "type1", + "q6bis-oxwxg-eh76l-5i47b-nmcm7-wibd3-q5alp-j6hxy-puzh2-qgequ-bae" => "type1", + "ze4ou-bfvbt-c5onv-3sxls-vqa4d-gwmt2-fr3zy-svzdq-ge2yd-oehb3-wqe" => "type1", + "zzhpj-v2jd7-sfsts-suhfq-k4otv-fy5n3-tsogo-6jrk2-adsxv-c3jwu-xqe" => "type1", + "23jbm-6z6mi-ki2ut-fkz5x-yy4uy-6llls-yyodh-xv537-2qonk-2iod3-jae" => "type3.1", + "4itjx-vyshn-cw55d-zywto-yston-7b5i7-upxxx-b2hhp-eajpv-lvygs-dae" => "type3.1", + "6bt7p-6zrhd-xi3rj-jsugy-c6qzy-nvip5-7thap-gk6po-ygo5h-mh4jk-tae" => "type3.1", + "d6thv-qf4lw-o3n7c-rfg5q-zupjh-25a2d-kaxkp-y5wr6-nzjly-w2bcy-pqe" => "type3.1", + "hrhn3-gzwom-ni6br-ywxaj-55bk3-gkh2d-ckb5s-4max7-xpnep-7kafw-eae" => "type3.1", + "jbgo7-cqnq6-mkd2d-fnqf2-6neke-h5zep-jtcmr-ahpnz-5vxox-z7saa-rae" => "type3.1", + "lus5w-zk4cf-asgto-epkw2-ec2l4-djm6h-zywzq-qeg34-fxhf7-go6fb-jae" => "type3.1", + "njrl2-xk3tb-n662s-ckjbn-a6nrw-efzr2-o2352-su2lq-csj4k-pjlgs-rqe" => "type3.1", + "o2q5i-kszma-radgr-fdzu5-iqfps-w6xfc-aka7m-fftq4-2lpar-wj6xc-mqe" => "type3.1", + "onv2n-rm4ad-mgpnn-iiheu-6a76r-crgmo-wxpwt-dqf6y-n7gcf-5po4q-2qe" => "type3.1", + "tu7cd-aljf3-56j73-xdusu-fyjps-sgkoi-5w3fx-p3vvd-tusca-w2vyl-bqe" => "type3.1", + "un26u-2rdxx-incuh-txlau-breig-k2ipi-wargx-73by5-prrcl-nnbdo-bqe" => "type3.1", + "vhw7k-ajobo-4e3gl-dninw-4tovw-th6o5-z4fx6-7favv-m4qaw-xovu3-6qe" => "type3.1", + "xhha5-oe4lp-ij2xv-gqhfe-pjavc-lwazu-y2wcm-rsvqw-hpvso-ksn7c-fqe" => "type3.1", + "xs5iy-nxwwp-pf2br-3544d-el6uf-xu2kf-qxqzm-sizvd-vgwfm-iqifv-jae" => "type3.1", + "yjbaw-7mgap-u2qrc-ol6db-ncf2z-wgxuy-4337g-2wjw2-6jjcj-wzdgc-5ae" => "type3.1", + "zjcl6-i6eie-fllmg-27ohu-lm4cy-4dax2-7ppcd-4mgig-rtfpz-typl6-yqe" => "type3.1", + "ztrgw-a7sec-ynzfd-utn5y-lnjjs-eb3w5-3yc6s-rygch-tizry-xvjwr-uae" => "type3.1", + "2qfdv-bjorr-dghqy-4x2zq-43lso-2ivzq-wrf5j-76l5q-rjcxw-vngvo-tqe" => "type1", + "d645c-5ux6v-agbpe-7lhvv-7u7si-r5z7w-tgrl2-pzujy-t4v4s-tylqk-4ae" => "type1", + "esagr-i4eyp-zc2re-yz3me-57nuc-4bd2u-vqhzd-sd3ox-ze52k-rx2cv-qqe" => "type1", + "gimp7-na466-yv267-zlz2q-k6tyl-4otwo-esa57-7eqvy-fj6nc-ptny5-cqe" => "type1", + "gn3jh-ppjoz-hdh3g-3dckg-zdgk2-qspnh-5i7p6-dfcq3-ke6vc-n5l7d-sqe" => "type1", + "hswqs-bwpdc-e4x2p-n2xcg-e6nic-4nc2h-nikqz-r6szj-n5lej-ndevz-mae" => "type1", + "jbv47-onhzv-26wyo-b535n-qmitt-chde3-4csxd-b6y73-a7slt-2qz7z-bae" => "type1", + "jcqkt-qbr7t-ka65z-tffmw-4sjkq-pgp3j-a3bxh-lf5sq-2jxgg-qa7av-dqe" => "type1", + "jt5zg-gisxf-5qwnr-klcxa-aepze-e47ng-ov2ur-y3swu-yickt-j624e-gqe" => "type1", + "lrc24-ewvts-5ecwr-oitgj-2mrc3-elne2-otvv2-2b2kq-lj24z-jpvqq-eqe" => "type1", + "o5zje-aptvc-nlegp-5nmxn-lm2as-m4tto-blbrj-mutsl-46tx5-vxffy-xae" => "type1", + "pe2iu-4l3d6-vu4tr-s4e6k-2fgnh-a6xup-ewdph-c2a62-l2scj-rsmou-uae" => "type1", + "pljjv-uhjhi-6e76q-um3ee-j3qfb-q4l7e-fpslu-pq7cu-ydm4y-j74ok-fae" => "type1", + "rhomq-7xfte-67w4d-rvxfe-4h7hm-eyzat-xc3zh-vlkqd-6e2v7-io6xe-wae" => "type1", + "rsp26-d2hko-kvacs-6mdca-dumka-qxiyw-4yzkp-cuwgr-lj7je-v7e6z-4qe" => "type1", + "uztgy-sickk-3uibr-yzg5x-ekzfo-ebh4g-3vvwb-vix3g-bfdyz-ohv4p-lae" => "type1", + "vlbpb-szwgu-jqul3-kpfje-ozgkp-jdwz4-f2hhq-6w6tf-e2lrl-4bmad-xae" => "type1", + "wzr5d-7qa2x-tduot-6mkkq-ou4sc-5ktje-4hxqa-a75d5-gunkc-3xhyt-6qe" => "type1", + "x42yb-62cli-sssge-gtewf-dklhs-nrqic-p2vx6-m57wb-m3nkh-ghm6x-vqe" => "type1", + "yfjtc-dovv2-lmpi5-3cp3u-xe6tg-xvswd-ahzlj-wumnr-6bruz-ynxk2-sae" => "type1", + "ysoax-3hlow-oy3hd-idxfs-3k2dj-vfmto-igbdf-qfgwu-onkk2-rtq3k-gae" => "type1", + "yyzqe-bsxfk-g2yg3-j7q7x-rs3l7-g5lfi-cs5ta-rwcdq-mfekv-saqjf-bae" => "type1", + "anudy-m6vfi-lphxb-sazpy-la4ti-2hriw-c67ny-butfn-axcel-otvuh-oqe" => "type3.1", + "f7hyn-ga437-hpalr-zqueh-4v4fr-zvt7s-galuf-fspe5-fnd6w-rao5b-gae" => "type3.1", + "yim23-u5r3y-yffo3-3hrab-vtvok-nw2qw-uzzbf-mvepc-fvnso-hjar4-6ae" => "type3", + "4p3lu-7sy3a-ph47t-wlvb6-naehi-oki2f-pkokv-hovam-s5iul-sxvwk-3ae" => "type3.1", + "5fpzb-vvlbo-rtbdy-4kcev-qy4k2-okbxr-3wtbp-nmjra-bqknh-zmquz-kqe" => "type3.1", + "5wzcz-mfner-c5wor-3liei-7mpao-vkult-zdds6-6n3rg-42ksv-6zzqt-vae" => "type3.1", + "67t6p-i4h3c-msv6p-kmbmm-rr6gj-z3nix-d6lo2-mq3q4-3h6rb-lwkbc-lae" => "type3.1", + "6bp3h-ds5qd-6o7ae-6nmnu-brwpj-2c5iw-yjfnk-4drxh-vxa2o-qcchx-6qe" => "type3.1", + "axmok-d4arr-sjcjs-cd56j-biooj-lo4fh-vfuoo-fextc-5iw2d-6zhq5-4ae" => "type3.1", + "b37zb-apiex-wwlpd-ccbxn-yes6n-q44fy-jtz3j-mjxrs-ecw7v-lzmut-oae" => "type3.1", + "cekdc-hmzri-3u3or-ei7ip-su7ck-3xt6e-zsse2-tgakq-rolmv-6crkh-hqe" => "type3.1", + "cxuqe-ou4rp-y6vot-zzuar-x3qwp-3jj5s-abhs4-yxacr-nxl4p-2cgb5-jae" => "type3.1", + "l3m3j-bzfir-r3ld2-kri7q-obwcx-lix5j-7oxey-pbb6q-5tije-krocc-2qe" => "type3.1", + "mlbpy-pvy7z-gfwcb-5bakp-cuubm-prmi3-dppq6-rlepq-nkyvb-u7acf-fqe" => "type3.1", + "nj7re-dvkal-yq25a-opp2g-5y3qy-3ena4-opd5j-22uir-eaakx-ljk7k-6ae" => "type3.1", + "wnuri-kqarn-qyjem-ae34x-dhkub-ocuug-3xjnj-xydiu-pmpkx-56lnn-3ae" => "type3.1", + "x6ufc-46e3d-cvse3-vbjz6-gkrnq-4lpvu-6lloe-l24kd-y75gq-3pmt4-tqe" => "type3.1", + "25a7h-yfxtk-5wcoz-p56pu-bzpsa-xkr4h-7mlkz-slh2e-iwrtk-kqeav-cae" => "type3", + "3y7fy-fd2oe-33qxg-wq7y6-zzplk-33jhm-n4glx-2c7ij-k24nn-jbsst-xae" => "type3", + "6hvx2-ymemx-tmykh-vxyic-bgfhv-2lcmt-3oy4x-rysuf-rnerz-vqokw-sqe" => "type3", + "awmdh-qwewl-6fgbr-dled5-2elki-xwucv-sslhm-awghc-dbxby-phz5e-wae" => "type3", + "dnt7y-wwa7a-6kdko-zjtn6-nkahd-cfcn4-yw5ai-pc6jg-6g46r-qq6jr-uae" => "type3", + "ecxbl-3dp33-mpskv-yvs6f-674ct-tpr4d-dhzdg-jfgf4-gzhny-n6zzx-lqe" => "type3", + "h3gd6-r6oc7-om7g2-2rlvu-dxqc5-moweq-tu63n-ffdcp-5kobp-mvdin-xae" => "type3", + "hhau4-uativ-guor2-rm5ip-omduw-lunoa-lfbnf-kwxm4-4h4lc-edmhy-4ae" => "type3", + "kqtlh-qojsl-k4xyd-baow4-ihgyb-y47y7-nvrhv-k35k3-432ti-l4ci6-tae" => "type3", + "lj3ve-ztk3f-wbkmj-garra-gm3kk-f2kas-zq6cw-6url4-ekmls-5vlm5-hae" => "type3", + "pdo46-iehoo-x2gfu-t5qu5-y3e64-cdymo-eioop-h6f4a-zebwa-fenb4-xae" => "type3", + "snaam-n24at-yjwgu-o62mf-y2cry-lsxfm-nilrt-mgyat-kshvj-rlhlm-fae" => "type3", + "j3pcf-ielts-wwr24-73hhq-flvsq-d7yh3-ly5li-r6csx-5tczk-wkpc3-5ae" => "type3.1", + "w7v5s-nethv-crfsc-zzq5e-gibjz-dmmyp-mrl6e-vkvid-g56eh-iilcm-eqe" => "type3.1", + "4valy-j3u2g-t6w5n-ijf7d-oxhrt-nxiv7-rvsku-r4hwu-dwdaa-tqij4-fqe" => "type1", + "b22uz-mtwbz-zx3hl-l5wrg-d7g2g-ywui6-bda6g-raycm-7wsr3-g5a2s-pae" => "type1", + "b4ncs-a2td6-7dnqn-pqt3t-wo5ti-fhggv-dfmwq-kycqw-dnm6l-v7vsm-6ae" => "type1", + "imzyn-2nlyg-dqnsl-xx3oj-kekcf-f5upx-ux2b6-ic6uq-tpzg7-rmyld-nqe" => "type1", + "ky2x5-zdh5q-ua542-tlua6-w7y4e-sjjyi-dlsy3-bbztl-rb7fh-urdnp-vqe" => "type1", + "lmzmv-wgqvf-7wyla-xwztl-sp3ot-goprx-zvrgp-c6zlm-3moh2-422de-mae" => "type1", + "mh3hm-bj3v2-hmzz6-hvoln-7xozs-7dser-rcb6n-l7kgd-6xqh2-qwblu-4ae" => "type1", + "rhbeg-gtwzm-5houe-dzejb-pel44-6kojq-pwdun-clvqv-47wpj-lxyt2-nqe" => "type1", + "rzmf2-bann2-xwd4z-7nyz4-jniay-7lqod-nom4t-yg2lx-tjros-wzzti-7ae" => "type1", + "taw6q-m7mc7-y344x-rs5re-opesl-44f5k-g7tk5-k6cho-vqksw-msm6d-yqe" => "type1", + "ux7z2-x4re3-c4b2o-g6htx-h7ads-mri2k-iae5p-jaave-nt7ac-emqry-cae" => "type1", + "vramb-bmuze-lyagk-iwt4x-d6sua-jlqhn-hedt3-cubog-bloos-z2no7-uqe" => "type1", + "ydhcr-xjjwd-xriaf-h5wmf-wenp5-ldpj2-oqu6h-oam5a-za6ad-4opha-7ae" => "type1", + "z6klu-fk2xm-geyc4-7qqtc-a7sbk-4xthk-3xqb5-d6qx2-dvdfn-25n74-6qe" => "type1", + "2ojd2-6gt7o-v5dyl-fd2re-pd5yu-b4gih-gebku-sekge-rpnwr-eifss-kae" => "type1", + "3t2lk-azfse-g3r3q-hliod-sxmhg-qdnt7-ilx5f-vlciw-mc3cl-2zssn-fae" => "type1", + "46qvk-mdby5-dbzf3-mpwtq-axasp-rkf37-wawoc-7edbb-drt46-oq3xy-vae" => "type1", + "4khgm-tyjsl-ic4up-p76l3-wmbbq-esylr-bljy5-f6vwj-5hgeu-wem6o-5ae" => "type1", + "5wsbi-bbit6-zsfuq-3sqk6-e6sni-ziokq-rfhgv-ovwzt-tosz5-nyq2n-aqe" => "type1", + "ad6tc-euwqd-mflr6-rwpc5-pfhvn-vqrq2-oyula-2vdkb-c7bsn-gdrkk-uae" => "type1", + "aiqux-sozuk-rzknl-zso62-hlr7c-gn6bw-gzgwj-cp3uk-wrmnf-hq7jj-rqe" => "type1", + "atw6w-w2ns6-k3brc-5ysgn-dxngj-j4zsc-rvepx-c7fh5-xgafc-trvvl-dqe" => "type1", + "dkwph-vw5pi-ewbn7-b7h5v-4bksq-m4zf3-2muqu-qamcy-togz7-ylndo-zqe" => "type1", + "dkx5h-i5d56-xeb37-uldi5-dlgmp-atrom-gyfn7-edeih-i5ph6-2cquy-dqe" => "type1", + "gchmg-6e4tl-q5d7k-lb7x5-anmtb-rnujp-wg2mj-shhjs-bo5nf-b5iz3-aqe" => "type1", + "hyaha-pwwhs-p7oe3-vbfkf-atj4t-qjm4b-34noy-ksjdc-cznbs-ppkdh-sae" => "type1", + "hzfgy-lpd4o-lye4m-gvqcy-ah24f-fv5hx-iu3mm-avxp6-s3xuh-cxbxi-2ae" => "type1", + "i7u4k-qo5lu-w2ynn-m7iec-lit7d-prf44-3fy7c-hvwqw-wwhdw-f6tdr-uae" => "type1", + "j3wos-4uumz-er7pq-m7fc3-nqm4c-lx64b-h4jp4-gipdn-5osxc-wtbox-sqe" => "type1", + "j7yar-ld6kg-f2sza-yczf2-5zpoy-mxs2b-shdva-fw5xf-4luwq-na4xa-sqe" => "type1", + "mvoup-lc62d-qskas-hm2zf-fahj7-jxr62-q7abb-evmok-p7rq5-yqr3l-sqe" => "type1", + "nnxzf-kyrl5-d5cnt-tjtcq-hefnq-yc3hu-525a7-lhoez-4xt2m-kvk5r-hqe" => "type1", + "owedb-225ie-3nagr-r7p27-jbwtd-745nr-rl5nh-qka5h-adlww-uu33p-oqe" => "type1", + "r3svb-ns2l6-e3aas-hcbar-zky3s-c74dm-empjy-kp3hi-mna2k-ipuos-hae" => "type1", + "rcta4-ak4lb-esqpi-uo3c6-qhp27-4f24e-iqdou-xytal-edyi4-izdgl-kae" => "type1", + "tok3b-qpyeu-7mcxo-7uu4f-fgyv6-nr6r7-v2evo-52ko3-rrhqi-4qwll-lae" => "type1", + "ufciq-eduab-vwpdh-pfizy-jr6cj-xykuv-i3c64-b2odl-3nii3-b7r5f-jae" => "type1", + "xcicf-xt3ws-fm3fj-gcuve-euk5a-alhzk-lgexm-zh4df-apchk-67xqv-zae" => "type1", + "xelcd-pogkb-lrtry-hfe4m-maxod-fwebb-3fvh7-6sb5b-vs2n2-fkaxz-yqe" => "type1", + "yxsbv-gyoyu-stbn7-qj27p-y47zs-w2tyd-u47gp-7zqei-oazsy-us3ad-vqe" => "type1", + "z5a4h-43szy-vvp4j-xorii-l6yma-4iyzt-7o3ry-frvqe-azkit-5iag2-rqe" => "type1", + "mjte3-dopva-7tttu-5hnnu-q572b-p3wwy-b56of-4q6h7-hbtf4-f3m32-tae" => "type3.1", + "5irn3-rlxhd-lp3cf-caeka-xu2ph-54ceg-afnwr-atwlj-a33ip-odxsy-iae" => "type3.1", + "cl43a-f4cfk-5bhhf-ro5s7-q7w6d-2vqow-3apn6-psfrl-i53ay-pykvk-mqe" => "type3.1", + "2xph2-xvn4v-pc5xz-3upfj-etpio-qaf2k-d2s3v-jnceu-vcher-qhqav-rqe" => "type3.1", + "bxczc-ao2x4-yzmq4-o2ezo-7mjct-ypeqs-6rcyl-ozl5d-j64m6-blata-iae" => "type3.1", + "euo2x-vl67u-3vtja-yrovf-oah6r-pq43e-pn522-j3zq2-pnly3-ad7wd-lqe" => "type3.1", + "y7bml-csbq7-euzyf-njmvm-qfftp-iy7lc-wisaq-jlmul-sdo7p-7lkx4-3ae" => "type3.1", + "2qclu-jxgmm-cjrng-h2wwe-i6ku5-dmj6p-e5qzy-ktplm-yu6kw-zkao2-xae" => "type1", + "4whgz-3ksfr-4rroh-dluex-fptua-mrxjo-uzmgg-lvuws-oe72b-aynhj-fae" => "type1", + "5kc7d-5fv6m-3ps2z-n4gf2-vm63s-a3nyz-7g4gv-rkgg2-nkccw-arwq3-wae" => "type1", + "7qvtt-6lr4l-gx337-d6xvn-55ka2-6kcx2-c7iby-w36qm-u3ojf-ildyc-iqe" => "type1", + "7vhhj-2qrcv-n5spd-aiff6-pjrke-jetz4-trolr-icnee-yirwt-4pgtp-5ae" => "type1", + "agtd6-f2qxq-ciadf-bterj-vpcos-7fapt-co3cv-ypvhs-zy6mk-ijrto-uae" => "type1", + "cfnj6-4cdzn-k26lk-ognvg-caesz-jogjw-5bvps-vunrw-gop5u-ibhs3-xae" => "type1", + "dgfn4-5fo26-vlsjo-culqq-3gres-zrmat-xstpb-ejd5b-dqcmy-oidwl-mae" => "type1", + "fe7vt-yjpwr-js7dn-62rht-w46qq-kbxls-ujd3f-bgotr-hjdzy-wu5kq-vae" => "type1", + "fqsoy-ydsec-w36cf-sc727-7ll46-pbdbk-dn4d6-lypyf-p74lq-6wzlz-lae" => "type1", + "hizzw-uvqcw-rvn3v-hculj-rrsc7-fvq2c-nzsjr-jvymi-5ilat-bvvci-kqe" => "type1", + "jtxt7-4zrbu-naeyw-kor5w-seylc-jdrgg-xftz5-eo4fa-cfmhm-gs2hy-dqe" => "type1", + "km5kr-aafyx-5dlbb-cl5wq-a5w6a-smv3u-dybbo-i4hv4-w5iru-ey5jf-qqe" => "type1", + "njxw5-wwjkv-fe7pn-573wu-qdvk6-n7nug-supvd-txyla-pefzj-erqsu-oqe" => "type1", + "qccxj-nhnre-k2hq2-zxdqw-3kjpf-rbnn6-6ngt6-meoqb-c5ypx-jltjs-mqe" => "type1", + "sbae7-ta24c-4nofs-6bn35-iqzta-tmvbk-wtdef-f44ky-2j3bl-grgog-tae" => "type1", + "tyv4e-thhn5-svu3d-jvx3v-2hajh-st6i3-ytdnx-waozw-hwxut-jxajr-mae" => "type1", + "ub4s4-wfatc-aqvyr-coedg-rddvk-ksrb3-f22x5-5rjei-qx2zx-qsawc-lqe" => "type1", + "ulsfy-bqajl-ktzwn-pgr2u-s4uyu-jipgr-etcjd-smsyr-ulind-u3nr5-hqe" => "type1", + "wbazg-vj5ii-5h3d6-jopmi-3zlpk-kitkf-nbf7f-fvfbg-emg2h-sz763-oae" => "type1", + "xiagf-4pdgj-efen3-ywuo5-jv2wz-fwc5y-bdzwj-epe6u-vxpa5-xc4nx-6qe" => "type1", + "2zulq-xy5xs-lcuqd-hap3b-scpd2-oqa3p-oxdmj-jrwlk-vsrhj-uvcvl-kqe" => "type1", + "5hpo7-g35qx-gomzb-ev6u4-uuhmm-mx3vz-dtx6z-ey7jc-bgzgw-hhv2i-sae" => "type1", + "637ii-3bf4g-arp6y-a64q2-memci-bszoe-vqykw-4smuy-pew4q-bkg4j-oae" => "type1", + "7pfb3-xfk5a-rkqk7-pa2up-xnthp-vsdo7-iyouf-hsh3e-zijpc-kk3f5-uqe" => "type1", + "ab6fw-gx5u6-hq7u2-cryei-hw6ht-mxdlj-xb23n-l2vyw-3pb3o-oihhr-eqe" => "type1", + "d3ixl-33uds-x3g73-fsu3v-r6p34-m35ux-syruc-ikz7t-3ufuq-dklkh-iae" => "type1", + "duxj6-cso4c-tsuyw-oajla-ufqtp-t4zwc-ue2bp-zvu32-jodhq-rx6qn-pae" => "type1", + "fcfcb-mfjxb-72qbc-ekzgu-3kd2y-lbbtn-eu5wu-wi47e-ns4k5-wwgyt-eae" => "type1", + "jsaui-j3qrn-3i3m3-xdn2s-bad3f-5kwlu-36gej-i75ik-5fjrg-z3jge-bqe" => "type1", + "q2gfr-yfiod-ohoo3-czlr4-ghfbs-bp7pr-zefjc-gbpyy-lfg4y-etedq-lqe" => "type1", + "qkhl7-fonek-gpter-opkuq-eoypr-tsvtj-xw4r5-oe7hn-cuaev-6ggik-qae" => "type1", + "ryexs-xz6li-6s3a2-zrune-elpjl-mih6u-qrijd-wbexf-qyvrd-oy2ck-3ae" => "type1", + "v5dl6-se7kt-adpcu-v32fm-m5n6q-2ghfa-6x2js-ifsyz-kmlie-nma2o-6qe" => "type1", + "v7s66-ti4cx-35ml2-q5lba-vxerb-akxdl-ncztm-l5375-3jygk-w2uvo-bae" => "type1", + "v7xli-jnnol-am5py-5stvp-ucmul-5y4kn-yy4tu-34xee-mgfnu-y3hfg-xae" => "type1", + "w3x4r-ks2fs-llbjl-3cz6h-xhtyt-tk42r-gys2j-fwcq4-lsj3f-aknin-4qe" => "type1", + "x5ibr-x6kwd-zguci-kvzpx-jcux7-3c3e5-bnjlk-mtt2p-qqr3r-qyhg6-hqe" => "type1", + "xgysg-f6alc-ft2ai-gseic-u3ohh-cvvkf-5pvjh-spcbk-gvkla-bwswo-jae" => "type1", + "2dzst-xscbm-fudmf-67coh-wopas-z46qm-2awcg-d2xjh-xxlnz-ru3pg-rqe" => "type3.1", + "2katp-edgoa-qnmux-ynruc-wwgub-y5von-zavsw-nvzfs-dwj4i-t7jil-fqe" => "type3.1", + "3pjo5-pzanu-lcywl-mybke-dymuw-qfx63-i5tss-4lotj-nfx4j-dniyi-7qe" => "type3.1", + "5jqmb-vz4s4-tp7g3-v22ai-lqxgz-fonyb-6mmlk-xsmfs-md3u4-ovqek-zqe" => "type3.1", + "bfj6y-cmhcf-6fxs7-ku2u4-tucww-b2eej-2dmap-snurk-3yaks-ss7xe-rae" => "type3.1", + "covnt-jinzc-tezyn-anumv-iboot-y5ksq-63usw-mstsa-vaasd-rpb7v-xqe" => "type3.1", + "dyycg-wc45f-jwks2-abddo-m3n5r-o5kxy-g5xhm-7ve4u-3tlk7-i7xec-oae" => "type3.1", + "fh6lp-xtfi2-7aa22-dsx64-hs42f-hc2a2-wfqsu-5rvnk-7wcf7-yu6cm-kae" => "type3.1", + "k4ecc-uo6mt-3jzeo-axsrf-fm4s4-2v2fo-f2hai-nhbm7-ktblp-aixvz-iae" => "type3.1", + "kwlys-46pnx-4t2m3-wqrly-q5qg5-lzr6p-75c77-celqe-rbtan-oclkg-5ae" => "type3.1", + "mtti3-fw4qf-27www-bah3o-ctlkw-lebmb-3m6e6-eesv4-l7onf-4db2y-qae" => "type3.1", + "nr6q7-lhbao-gj6jo-5nfem-lttds-nammh-hfc3u-zjv2a-g4tno-tslx5-sqe" => "type3.1", + "srgrm-5pwg2-225hn-fxwdd-zphpu-wwxgu-uxako-avmlx-epw7e-zwkhf-sqe" => "type3.1", + "tmevu-x7kon-mdgqs-552gs-e3gwf-wdeqd-egoxr-wn3av-v3vy7-ul7ca-tae" => "type3.1", + "w2sev-mtuls-aa7m5-cdjgi-5vipg-2jn7i-4awvf-o6suu-73ebs-sw5db-kqe" => "type3.1", + "atjbz-kcjz7-y4mgn-t5wqp-3emfk-6mtlx-ln5i7-4pixf-ocjgh-hfu77-bqe" => "type3.1", + "if2wa-juqxh-ii2v2-kzwry-3sgof-lqya5-j7o3s-ukvl6-ad4lz-ihrls-7qe" => "type3.1", + "oobdg-4ugoj-vv6lz-ytrmu-ckcnh-3d654-rlds3-jdabb-pr2e2-z4bfx-zqe" => "type3.1", + "tgbmf-2jayg-jhoty-cgdle-jezqv-vxaly-pom4f-vnhzr-jvxdd-3yoyo-zqe" => "type3.1", + "eqe4v-xnyd5-sqimm-7qxav-sxmuf-c5jjj-bpqq3-ccpso-g6jqf-2a4yo-bqe" => "type3.1", + "lmfy6-g2wta-3pp67-cjv5u-qws2j-kf3ci-tkze3-iqucl-mscsw-3u7bx-jae" => "type3.1", + "lmn5j-j7gfv-ktncs-h7ws3-i57yi-7jn5a-p25mh-chqq2-kksgd-wmcbw-jae" => "type3.1", + "poyg5-cbmcm-a372x-j72lt-zm4rz-4sf5v-ajtle-hyqkt-rdgaz-eqzmi-5qe" => "type3.1", + "qhfut-l6rgw-kkbjn-iglum-tgn5d-pb36h-rlus5-f6uhu-fgv5g-5nqyc-nqe" => "type3.1", + "qlk52-xi45d-fpbsj-epcof-uoghd-xkmb2-woudx-aci7c-tpglx-rq5mj-vqe" => "type3.1", + "vivoi-pgaro-d7j64-broi5-wf4ig-3n2jq-sp366-rgh56-yx6do-gcx3w-mqe" => "type3.1", + "yf2ct-a2auf-nofdz-acf6v-ugwyb-k5f6j-5sr3y-7h3xi-svcdg-mhpgg-aae" => "type3.1", + "3beeq-bvtv4-mxeff-2ypcm-jnw74-envji-bflpz-sipbb-e6gnt-kfptx-5qe" => "type3.1", + "k2ovz-ptdmz-hzmkh-ntfqn-evhux-ktxz4-26jg6-qjjsa-rpgal-wkdjo-kqe" => "type3.1", + "3ojqg-v6jhs-frq5l-a3l7k-7xvhc-kolvz-2e4to-pdf4l-xiuem-h72b3-bqe" => "type3.1", + "4jf5x-7jffe-qbtoz-czwrh-6ko6m-coxky-jdegu-dlhaj-jkv5x-zytf6-vqe" => "type3.1", + "4y5k6-auywv-zpovi-teeu2-bqfdx-spyxc-b76oo-ug55w-o4vrq-6z2jt-tae" => "type3.1", + "56ovz-lrvyd-gggsl-qtenl-uuokx-p7t3t-rg6mc-6lc5l-usfqb-fygiv-aqe" => "type3.1", + "6wnv3-oxkmg-rtcgq-slqsn-5xlwu-7a27t-524pa-st7b3-epsck-tixsa-uqe" => "type3.1", + "7f5c7-l7cuq-7jnfc-yexo3-bytd3-yf6xa-ctk76-wywln-jsy2n-ds2lp-tqe" => "type3.1", + "i4zve-soi3o-vqbql-gklv5-dbklt-g43ha-k3ju6-vdlrf-5t6lu-pyl3j-xqe" => "type3.1", + "jdqro-ad2ju-bncnk-igcfw-u4z73-bckrs-kg5s7-6gxey-ijnzx-vxoir-lae" => "type3.1", + "kq3sv-bziyq-im66o-5jrzn-6uexa-gyj3b-n7z2x-nvdqh-ffxhn-dfrh6-wqe" => "type3.1", + "m345s-atzkk-etodt-4bssh-cxcit-aw5ou-kozs4-5bipl-i46aj-uwnnh-bqe" => "type3.1", + "mtr6i-a7lue-qnbje-xizsv-z3v55-atzbw-5xzbk-yto2x-v5ynd-lsmqr-mqe" => "type3.1", + "q3w37-sdo2u-z72qf-hpesy-rgqes-lzflk-aescx-c5ivv-qdbty-s6pgc-jae" => "type3.1", + "r4dbq-jplty-hxb2n-yx4pt-dm2vf-m73ro-sqgq7-onnzn-zenvp-dleaz-mae" => "type3.1", + "u4zo2-i6nsc-mowwj-lf6az-s6rdo-caskt-a6yx6-fke5w-qyezs-lyqvn-tqe" => "type3.1", + "uajv3-ocqku-n5d2z-kbw3g-mqsir-er7gs-mhsdg-27f6d-xbfjl-uugdr-cae" => "type3.1", + "zjiki-pzvnv-m4rnn-fodt3-poon4-uldx7-d3wkq-gptsu-g2mjs-boi35-3qe" => "type3.1", + "2yzcy-lywoi-fpfpg-ghagw-3emh7-2lj3p-ritfh-6e542-poww3-rafkw-wae" => "type1", + "3fgii-zd73c-ftxy2-ypsbx-hnqf2-nzhc6-veurh-bru27-mz6bg-j62qd-xqe" => "type1", + "3ppfv-vqufk-yrg2j-nos4h-6fxos-iucst-jsf7h-be4fn-im63d-fnbir-jae" => "type1", + "6df7i-epxjg-xjgjb-reyqa-3d5a5-cvgxz-zscfa-ycszz-32cuu-ruhx5-lae" => "type1", + "b5pgw-6wtjo-dzwgu-zkfz6-3l5f3-cw6ka-y4crr-dikku-fkjbe-lfzki-qae" => "type1", + "cupz3-ehtqa-su3yl-2shoq-7yxw5-25uhz-g5kew-73dpi-w4gpu-hsmoi-vqe" => "type1", + "diz6c-cfgpz-4itzw-ufwi2-zdc5d-uwcvx-gfhcd-7pjnm-526lr-lyuh5-6ae" => "type1", + "f63hs-lnxxy-icsqv-j2wkr-aly5f-piig2-ptkk5-c6x4y-px2tp-gx67m-jqe" => "type1", + "gsrhr-kwp6h-y6ibc-yftud-fzi66-vg5df-subl3-wiren-uzza6-rb5fc-dae" => "type1", + "ihoip-fmbnl-mm4fa-pu7hu-2sbx2-mzl2f-tgzje-unnjg-bgcac-3apmo-gqe" => "type1", + "incp3-oydir-onuos-fifuz-6n4c4-4aawc-gxmxz-v4juj-xzupz-h22aj-aae" => "type1", + "jvqnq-6jnty-tr3ml-izvma-e5w6e-s3rfx-w4wir-mwcsf-x5yok-nc4uz-6ae" => "type1", + "k6zrw-n3gr2-3wb3v-ejowh-5tw2u-kqbr6-ucttf-hayoz-ebxvi-3wiib-bqe" => "type1", + "kjzcx-5a22v-gcxvx-ilfil-mp54w-hlcsh-r4k6n-t36ia-vzoy4-pvedu-cae" => "type1", + "klluj-vrtrp-k27jr-cb2fm-oyw2o-hrksz-lbojt-xv3ts-off62-cdcbz-iae" => "type1", + "l4mrq-cmo2o-ydidi-v2zit-pemyc-itm4j-qw2u3-kwzso-yz5dv-geium-pqe" => "type1", + "mbsyf-dtlsd-ccjq4-63rmh-u2lv7-dwxuq-ym6bk-63i2l-5uyki-zccek-mqe" => "type1", + "mh6ne-3zycb-z4p45-lcfrf-lh6of-zelj3-7nuii-mzg7b-zi2ic-s67kx-6qe" => "type1", + "nhkbm-fqj7r-jdjii-spvcr-to3iu-ccrkm-pils3-6io5l-ahtry-qhlly-4ae" => "type1", + "o2ejh-i3l5b-tawnx-6vane-ek7hi-apzj2-d2vid-j5dro-xityu-sszcb-gqe" => "type1", + "pud2o-rtmqx-ym5vr-3isia-wojnu-xwqmn-qcsxu-ktsva-udz72-m3pkg-zae" => "type1", + "q36yu-tbutb-rdwfe-oyda7-gregz-3speg-zemom-hhjpt-ow74s-p5tyw-tae" => "type1", + "t3d5h-mcdqm-lt7jk-b7nk2-nkxfi-dl4ew-pn776-olxjd-eyott-3hpjb-iae" => "type1", + "tjg3r-ffr6q-hu7rb-4wevn-e4tlr-brdq3-zvobk-t4aof-5zix7-qfi2w-vqe" => "type1", + "tnjj3-xy6c4-ftk6z-kuhrs-zijli-xlbfg-j5gqt-qa77l-ehjkq-yxlua-2ae" => "type1", + "wz42c-ah6mx-lt7c6-vnqcm-kjnd7-52x65-2yfqw-ibpxs-th23v-7z3hl-qae" => "type1", + "xu6mr-rd7cf-pq6uo-xf4bm-ivd5c-hpnp7-dpjhc-kkwnu-xeaqu-eue5r-sae" => "type1", + "y6mus-re3u7-vpqsx-xmhkh-jksck-rp2ko-xvjlb-7tdhu-jjm63-47alq-yae" => "type1", + "apkff-eadlc-x6g5g-it6ky-aevto-kjvy2-ai2xe-eppyi-kyihn-nth2v-cae" => "type1", + "cpywp-n4j5f-ja44p-oykxm-umz7h-fk6v2-rowix-bkwc4-ly4fw-tvu6c-mae" => "type1", + "dapyz-46k2p-hgf2n-zxdjl-mq4ek-jgbvj-6xlfz-srm6z-dmfvj-wldgk-aqe" => "type1", + "gxree-vqwe5-izmyu-7o77a-mof7f-7opvv-xcpql-nsw33-rqkk7-st4t6-3ae" => "type1", + "hober-6po5w-o6flx-nxc4v-pr5j7-g423z-i72ls-rxwtl-ghabg-p75vf-iqe" => "type1", + "i5xgw-uzqkn-xrfkd-7nqdz-3yvas-lujhr-mvs5w-sxv5u-m7zrn-hrd2k-pqe" => "type1", + "ii5t4-bvjp2-sypuy-pqz6v-v44yf-lzox2-ja3tp-m7gtj-7pszo-fk66d-cqe" => "type1", + "pxxcj-h2sa5-q2yql-4j7od-jwtvq-uisnw-w2ox6-xtfqi-noxva-nlgxx-wqe" => "type1", + "qp3lh-25yxy-dlk4t-ay73d-frr4t-3kmi5-35kqg-3vvbq-26qhh-6xrdr-oqe" => "type1", + "uttow-iiov3-lbqfs-eiiga-7wxgi-2jdak-5h6rw-pvouf-ulmlm-zphgp-wqe" => "type1", + "ux7wu-iidyv-r5cth-tz6n5-4xryn-av37c-24lrk-ozfaq-7sjax-ohkd2-6ae" => "type1", + "vtbf4-olrta-q4eso-47b2z-ncgrs-lksia-xohkj-slwil-m5xoi-uxovi-wae" => "type1", + "xp737-mf6s2-27fc3-w22ge-g67ck-levhr-yf4a3-f2luo-4xd3y-3veag-pae" => "type1", + "xpit6-cyps4-ru26l-kw5rs-hbtll-yr7aw-ywa4o-2ibcf-zaich-qrzdq-3qe" => "type1", + "2ajl7-n6xum-2j7km-24scw-5tirj-whjlx-756pq-jbcua-utl3q-mts5r-hae" => "type1", + "63wdw-l6myo-hzrps-cfj7d-p7fqi-snpzk-richg-7dvag-qn5el-r4li2-tae" => "type1", + "67xbd-qjnxf-frrt3-hm4oq-kcslb-npcyi-r36lw-ftwv5-tfnfc-4djur-5ae" => "type1", + "6rib4-6jg6x-oryru-yunc7-jwcva-bis2k-rzqey-amqxe-nmrje-qsafr-qqe" => "type1", + "7l5iy-sbp36-y53hp-7n2ts-hujyq-ajbfn-ont26-sdr4w-j22rw-rzsz2-pae" => "type1", + "amjrq-m7xgs-bacs7-g54xa-t72h6-dszrv-7mj3i-6vcbx-3zzb2-6eean-xqe" => "type1", + "aq6el-xkxqp-xmtas-kvdol-x76er-bgwyq-4hgln-szuow-ezwxw-7fi3c-mqe" => "type1", + "bz73b-igxp6-g5dbn-f3ran-p2t5o-b4q42-guoec-imv4z-mvo25-7p4sp-2ae" => "type1", + "caza6-vttyg-zpys6-lnxfo-nuxdd-gx2m6-n77y3-44m3g-p3ysz-6cgyt-5qe" => "type1", + "e7uss-ohzuw-wizjh-lyl4e-m5vfx-ldvyl-q3555-mcr4a-trmic-e2bvo-4qe" => "type1", + "eukqc-dmuix-aquwg-q6bzs-cqjg3-uh6xu-x7tyt-myggw-qxuec-sgwcl-gqe" => "type1", + "fwqkl-dmqnk-rrbxh-of2b3-lfqzw-ecvst-vh6tl-o2nzi-fjscu-3yzlh-uae" => "type1", + "ltnwl-wml6h-hbv6s-edgfj-q3g4i-wxuph-67pga-vwawx-f5az6-tfoix-hae" => "type1", + "mf3d3-z5tww-ii4jn-zhy4f-ah3un-wpre7-edsb4-ktrb5-h7f3f-uz7rv-bqe" => "type1", + "mhjyn-urbgj-u7g37-fq7b7-zlu6o-iiqc7-q2enw-3svx5-4cgeq-choel-tqe" => "type1", + "pmqd7-arpst-bhzvn-jcxe2-sdy7z-w22so-md27z-dk4z5-khrwu-5babs-oae" => "type1", + "q726w-b4o76-dgi2a-imscu-26tiw-rdtfq-pvp4r-gze3l-gutf2-tohwm-rae" => "type1", + "rgacz-r4m53-3mwgy-63dqd-txx4i-o6aw6-5wwdt-eizs3-6lh2z-hxzlv-3ae" => "type1", + "rptzo-ymdmg-3zp32-xdvov-efmon-lgmet-pnbxl-4qjgy-nba6j-pcw3k-yqe" => "type1", + "skrp7-gim75-d5mmc-j4rce-5zruj-b26u2-uqfav-o4gnp-nk5pk-aa3jp-aae" => "type1", + "spxvj-lc4pg-zgdik-qpl32-npgvd-tsycq-rgxat-ublzw-clkmq-gom2n-uqe" => "type1", + "t6ads-xnmvp-nwrkg-vytby-wrqj7-5ddqe-h5a7h-jjmpa-snv7j-3pqqz-iae" => "type1", + "tgkyo-qr2rx-6u7tu-nfj5x-amxiq-zkcpl-w3zji-dk65k-x6sgj-rmce4-vqe" => "type1", + "tlu2b-eaodu-j6dxv-xj7cw-7evtx-x43k5-iobcz-5sspz-3f4rq-c5etq-6qe" => "type1", + "vqrj7-n4xxl-qbgb2-uudxw-mvmkt-d4irw-7ln7t-2m6w7-4ryfz-6pfaq-3qe" => "type1", + "wj4ul-2uxc6-4zyg7-ubs4e-meno4-2pjfz-3rl2y-ksarb-vlbjx-zrnpy-6qe" => "type1", + "xrhqb-wn2tc-5pz5b-6uo77-4suat-uzryd-kew6a-lcgjv-oes2b-4xqel-bae" => "type1", + "4xhpj-gsmak-akpfh-z7rro-63yfp-5ewvu-i4o6g-yyrix-yd7xj-cus4y-hqe" => "type1", + "5flj4-x56ts-jud7h-rzygl-a7uas-2degg-kyz5e-xojpt-pbuph-34zby-bae" => "type1", + "5gcqu-upr5b-ol3fj-vz273-3ov5j-s2dzp-d3n4o-c3rdz-rnhto-tovcx-zqe" => "type1", + "5jbfj-wygyf-jzcjw-s6ali-nlazr-nbejd-sfw6h-v44fg-hh34c-khde2-lqe" => "type1", + "7pwmx-4zsiq-saplf-kl2sd-4yvr3-la2yi-artrs-m7voc-htak4-fii5y-sqe" => "type1", + "a3xcb-ezfy5-yib3b-5bo75-5nok4-kaolh-a6akg-ilxck-qdbph-pwppb-dae" => "type1", + "bafm2-xnr2g-ztwfs-dnc5i-gqygr-sne73-6nyu2-66x2z-qnax4-ec5ru-hae" => "type1", + "in4qi-poj7g-j5gvw-34xys-4cf6k-uki3d-exjzf-th4cu-c5my6-sdht2-cae" => "type1", + "irpwa-t65vg-ellmb-rg37m-ge5or-g3crz-3vbri-m4spq-eldkq-v2vs7-pae" => "type1", + "kaoz3-4okcl-24amq-v5y4k-vim56-4njvo-fwvyk-v4grq-kuqum-7zm3p-4qe" => "type1", + "kno7y-tdoxx-da524-svbxm-wt7xn-nyit3-vtsd4-nimle-vahxa-q65e6-oqe" => "type1", + "mt54u-t6z4l-fkx5l-2xwfe-g3u34-kzd44-2d3f2-xu6ty-lpc6z-pex4y-5qe" => "type1", + "n6zwz-reuil-fdhjj-bvsph-rsnvl-ru3jg-2ywjx-bxt4w-j3jkz-vlheq-wqe" => "type1", + "ozim4-dez5l-gk24c-m7phw-e6i32-c7vxe-m6you-mkprj-moxq2-fhtyw-uae" => "type1", + "ptzzn-jphjl-476ql-lgyrk-37oa6-zxhat-ynn4f-fm2ry-r3cmf-lx7e6-uae" => "type1", + "pzdyu-3fz4j-jukm5-pimui-aqzjl-dlqxo-mpe42-ihxtg-rjzxt-ibdb6-aae" => "type1", + "qbij2-wkp76-ewolj-xsyp5-flevz-5kvx2-4hvgg-q23hh-43hbe-bxntn-xqe" => "type1", + "qnc5v-rhui3-tk2u3-hrxp5-u4bfn-wkcew-43fcq-mlasc-4344s-6vik6-kqe" => "type1", + "rfe2u-pdp5u-frzdb-r2ga3-5jept-24323-5pere-7lerg-toh4w-cwdsl-cqe" => "type1", + "tgmtp-wy3f4-hqron-bnvc3-scclx-b7fgg-gdnc2-dwvks-dn6ao-gbqso-5ae" => "type1", + "tqkdx-4m6y2-avyzo-hkd6d-jqwka-3ys6i-ju6do-2auph-gb4bj-o7neg-bqe" => "type1", + "u6j47-wepye-qtjf3-vkvnv-q6nfc-a3wky-grxp6-wbjuy-xejgs-qalg4-4ae" => "type1", + "vgfnl-4phvh-44pk3-yshmp-ckwz3-qnzob-l5wnj-pqn2j-vv5jh-3oewk-xqe" => "type1", + "wvxfb-eqeex-26z4r-vefff-llfh7-vv76x-pje4o-oh57m-eklny-zcs56-wae" => "type1", + "wzobn-oqy6r-qnhrl-6qvxp-3dizc-5gb2q-tbtnc-ptnfl-vyxkw-g7gxc-4qe" => "type1", + "yld6m-kvarh-m5a63-yxlak-4ukqc-zxa6g-aenp4-hjb3o-muwtu-h3zba-zae" => "type1", + "ywict-j2wwx-ioq63-wqlrz-pmcsg-dcxrw-ibgcp-chmr5-m3dla-cjvjk-2ae" => "type1", + "zos66-lmcn7-satbv-gcdzj-q3cdf-4n6zc-2hlei-gc453-uoh7r-4sj3w-vqe" => "type1", + "5ttnf-xhdip-7e2uw-iuikh-5dlli-r6jdc-nxjp7-bjumm-5usid-rpiqn-jqe" => "type3.1", + "ukmex-exflw-a4lyh-hrbdr-hjywz-v555q-cxq5y-md4as-2zt6b-fwxzt-aae" => "type3.1", + "2nasj-kdvvn-357il-5gkpl-qz2cx-dhad2-g2vj2-pfymt-lnmjq-yi6zo-oae" => "type0", + "2o33b-cheo6-ozp6n-sjrqc-cbro3-bslrm-kuhqz-wpncp-vlhji-jzeoj-6ae" => "type0", + "3wauh-knr46-4nk6d-z3r6b-rjzrp-6gadw-ginns-oivtb-cidku-yjnke-kae" => "type0", + "5lhja-xg5eh-66knd-ksj2u-pckjx-nvzub-7etfq-vjtm2-am5wb-kcztq-iqe" => "type0", + "7v72g-sof5q-riabw-dzefk-7p74b-wxwzs-dgvbv-rlrxx-2jpjy-zli4s-cqe" => "type0", + "ape3b-7ujpk-zcmrl-ltp6t-tm6pq-aabf6-r5qof-k4jat-bvv44-bk663-6ae" => "type0", + "aytiw-nezzr-cc3yp-lwl6o-r35hi-hnxsz-nfb7e-3bmy4-er6tf-wrtgv-dae" => "type0", + "dthnm-qd736-u5wwy-jraq2-tfmuv-53q5s-acqzu-2pavq-mdzf2-v6y56-rqe" => "type0", + "dtw2y-yx6vt-sjpuj-7wknd-wgjzj-6ksdq-wiwas-zrzgv-xkhz7-zijib-tqe" => "type0", + "epgnl-cf57d-uxm4m-a7qh6-vq3us-sk2p6-wtfxw-k3w24-bmso2-sdt67-mqe" => "type0", + "hpaia-icx23-hl6gx-dolk3-26qsx-ktskb-n4ij7-voctb-q6lmy-7ow7q-cqe" => "type0", + "hstd3-alijq-au4yp-ysavf-kixpi-dh72a-b7s2n-dakek-t4vt2-5hiwf-iqe" => "type0", + "jyi7x-vlpb7-rstkn-ps2nb-kim4l-4pv2f-bti74-uzjrs-5nz6l-hndb2-tqe" => "type0", + "k6dkt-olzfw-2kwvk-3eg5h-mnfqo-3w7aa-abk7p-hbg7g-eqrjm-vacoe-7qe" => "type0", + "mdkns-bq4nj-lgwt3-zouic-kgnzm-dahbu-cjkgv-bjhtp-5o4z2-ayivl-iae" => "type0", + "rjg6u-kzzdp-x6jzj-4ruai-bn5e6-mxnpf-4vymv-qb6qs-l5xid-s4433-3qe" => "type0", + "tg4tu-vyw23-64vck-yvrhu-yxdk3-5eafg-bdtbl-bd4ij-mqts6-lp6ll-lae" => "type0", + "vv3vr-t64kd-5m2ev-spobj-5xdx6-2zu6s-5gdf2-mnmsy-bdn5c-j24jk-hae" => "type0", + "w4x54-3byhj-wxenj-tgbsz-hsngw-ymi2e-olxhb-anm3d-6lyfa-j56ys-pqe" => "type0", + "wrnq7-qrniw-hbbj7-juo5h-oubib-kteh5-befcq-3bav4-epvki-43keh-vqe" => "type0", + "x3woi-nzeuf-w5pan-yzwf6-7fgdn-hxhuk-st6rv-6lzam-akknw-ipvze-4ae" => "type0", + "xsntz-cuxb5-c3xmg-vuzwb-2wsb6-jbfpj-jh6du-ynics-ziore-t7bpp-4ae" => "type0", + "y5xp4-nnr33-qeeeg-55unp-xr7f2-mkjfv-7qwe3-zxd7k-lmqd2-mftya-7ae" => "type0", + "ykydc-iog7g-744rt-mzbay-jnwat-e5bx5-invpc-f6d22-leoix-tide2-6qe" => "type0", + "3jfof-q5mok-apuqv-du4j7-jqe6i-iga26-zim7o-fcmxg-la35o-5hnqj-tqe" => "type1", + "5u6dm-zixbp-mbblj-xc7en-hsc7i-upw4t-3dzyb-63tfg-h3f5p-3q3b5-4qe" => "type1", + "6a7nv-kllgd-pgq3t-nnv2w-miosm-g7kzz-4ozr4-pzq2e-iozln-ufbpr-mqe" => "type1", + "6joub-fk2lr-mcxa6-lo2x5-e3alk-lrgf5-6slof-x7yxi-m6ceb-up5wv-qqe" => "type1", + "773ks-thhym-rpwf7-rrz2t-4apte-tq7tl-gwq3n-xfy53-hbgwr-doywj-fae" => "type1", + "apit2-z6thd-bi3t3-m45hx-axid2-azz5w-3iorh-zyzmy-mdqfu-few4m-7qe" => "type1", + "cgpcl-dxgge-6t4eb-7zmxr-vnjyt-em6qb-czhdw-q44mj-cz6v3-ahjd7-kae" => "type1", + "cwftn-crfgv-mhytn-tdjrf-xg45p-bqowe-b7lmz-r46vz-ii2ev-kr3bl-rqe" => "type1", + "epwlh-zpyza-66uqs-7zrr5-hvtpz-srxfs-acqqj-or2oy-zvky3-3eo2q-6ae" => "type1", + "hrdy4-ydse4-sko2n-rnvds-5ng7t-nc7ue-ivcxo-umbu5-zed7s-z5t3d-xqe" => "type1", + "m4z2g-kmsdn-ryakd-hbfuo-wpcmt-tjhmh-66nym-6bohu-jwafj-erzf7-vqe" => "type1", + "mvx7r-gcjs6-emuc5-ijos2-5egv7-ic35n-ocbfd-tjgvp-kydc5-5tshk-lqe" => "type1", + "otjix-uov33-zlilf-77him-mcnun-fbnwj-voya4-axinh-4vu4o-bw53l-6qe" => "type1", + "rs6nl-3tgam-nozcx-g77tr-hcslf-hpnhd-zl5ah-p4aei-pwqyr-rebml-vqe" => "type1", + "rukdg-aswl7-rj4ll-qpnza-vhmtw-qooxv-zf5kk-zhvnu-o5bxa-ftekj-dae" => "type1", + "smftg-i42r4-gjdo2-j5zsp-rnagz-jleds-scddh-2iloh-66uso-ttq2b-rqe" => "type1", + "tacq4-bm63j-csjzs-oqw3x-ko3gh-oxk4x-e6odx-ir7um-xvuog-2l366-7ae" => "type1", + "tq3rq-mnjxc-3szli-4qntc-2nojp-telh7-kav7a-hjlyy-odlqr-lp2ie-pae" => "type1", + "um5l7-ktiyh-sggql-murjx-27rjp-xw52j-gopyv-cvrl2-sc6ez-x34mv-7qe" => "type1", + "upg5h-ggk5u-6qxp7-ksz3r-osynn-z2wou-65klx-cuala-sd6y3-3lorr-dae" => "type1", + "wc3ak-klemd-6cflb-ekdoi-ygdnp-5fomb-oy2j4-3o53i-qdfyj-yvcqz-5ae" => "type1", + "wzlzb-tsogt-yb3nr-l44x6-moo2i-ccd2g-i3mu7-66e2n-6bacx-i2h47-hqe" => "type1", + "xo5aj-2kwg2-svhz6-kvtc7-hrzw3-4dsx2-mrioo-h4pcb-6nwap-w5dqx-vae" => "type1", + "yidup-dwmh5-hruhg-lx45z-jjtys-mym7n-bafb4-lillx-gstnz-o4vvl-gqe" => "type1", + "yov4k-2vzzr-kr3vj-6nypl-4ymyn-7earu-72mkn-c5yeq-mhveu-yjrdn-vqe" => "type1", + "zv7yn-pa2fv-x4jms-x4ynr-bghtf-zwviq-6wtyi-jywso-fqxoy-vbmpv-pqe" => "type1", + "a5vyy-7bgw2-67pfa-cgt2b-dgi4h-3nxi7-q5v5r-yzenb-yv3xy-fdu6y-hqe" => "type3.1", + "pak5w-tullm-az6nw-t6c7e-gmzhg-p4ale-m3hgo-qr334-7gy45-kkwtg-lqe" => "type3.1", + "2ftux-ivgjc-t4i2d-lq5hs-myjdg-3h2pz-l2ddl-nsxfc-wlaq7-fkbc5-6ae" => "type3", + "4nkoi-tdk2r-bfboy-5dokf-zc2tp-sokpx-hqfr5-6znc4-vqnxe-kr7ro-iqe" => "type3", + "5hni3-coaib-iuz4m-dxyvz-7bntg-hnwji-rghvw-sjvtj-nd2rd-jgjsn-jae" => "type3", + "7m3y7-s24j3-i5oji-6h64r-2ah6w-2z236-rh7ro-b3plo-rrdwn-xwhc2-aqe" => "type3", + "7pch3-7nre2-7su6p-ouf2h-zrpqi-oc4vw-3j2ce-swypv-xiu4y-qj6b2-7qe" => "type3", + "adplk-g2snv-bygay-o2r45-72zgu-kzk4z-pzhto-5nhwg-zsryj-s4llh-wqe" => "type3", + "dcbkk-imyhf-imyqt-ezzv4-g6krm-m7taq-l6mne-pqbmu-3ichw-y7x4r-2ae" => "type3", + "er26s-4rg3l-4pfha-wjj6n-a6zcm-x6hhu-kbmtm-xyhxt-yuqhx-hob4k-bqe" => "type3", + "fistr-5k762-tgmcm-jfqay-35nkt-nbjvg-3kta3-l74fi-2qr7j-tqdez-5qe" => "type3", + "hcvph-gzlqc-5ogrn-seple-vyr7i-gzgzo-eencn-dq2bp-kr4hx-4cujp-lqe" => "type3", + "hmawz-c25wh-ym2ei-c6idk-gkxvl-ndkvd-cipd2-a4rzi-xdlmx-nzw2t-4qe" => "type3", + "itcju-qgal4-v2k63-llj7k-3ntsi-jmhdg-tlkhw-zz4kk-gwfkq-whb7w-aae" => "type3", + "kax7o-5oyag-q7xbn-lpm4d-wkngc-dhbig-ca4sz-kdzkk-kgkln-khtsk-dqe" => "type3", + "kegk5-wu5c5-xpe2p-athmd-4dzq6-4gucp-n4ne3-uj2zk-e2jjl-3253l-6qe" => "type3", + "llbvn-f4hq5-tg7bl-tdvr7-zs2cb-7uwfz-dhshp-aaomw-urgqo-gyll2-tqe" => "type3", + "llwjk-sgobf-vxycz-muu34-oyozz-lkxak-pzc67-ek4pt-kqmfh-b2b6k-3qe" => "type3", + "lz3ap-rjo2a-ilqjt-jkyhb-t4ewt-b37eu-gl246-ltnyu-jrkec-6bi5m-cqe" => "type3.1", + "pmlsj-nmtc5-6pm7r-di22w-6ftne-5wjyw-xeivl-bzthp-fx7q3-5cgmo-hae" => "type3.1", + "qn5jg-h2if5-pw342-jegq5-fzrtg-n3gvv-e2xj5-47vpe-c7nyw-q6mwk-pqe" => "type3.1", + "qvskl-y3tsp-bnmtl-k4duq-vdcnn-zb4f2-vea23-x2vz2-jpv5d-jzwzx-eqe" => "type3.1", + "sjtwn-kupfb-fmu5p-twem2-vl6zm-uhqim-ahuup-pcwvi-g6v6g-qyybo-tae" => "type3.1", + "sqw45-bb5aa-rsstr-3lqro-2asg4-jqzqp-lp6az-2a2dr-tzil6-df7tw-gqe" => "type3.1", + "vt5q3-2bev5-yhmxc-uxp3l-j26fn-tnlfb-sdstt-yoqf4-bio44-yyycs-vae" => "type3.1", + "wr22o-nmso7-rmkqr-vzkv5-eg2cd-kxx3o-jbjnc-e5tjy-ety5p-rz4vf-uqe" => "type3.1", + "xatzx-j3mph-3xzxk-jz53j-afa3d-qxnac-fbejn-hvgpy-cmhlb-dkuhv-nqe" => "type3.1", + "4vpak-qnqtn-vggke-um4f2-zkk2m-awc6p-bkhi5-cm22y-zhdiz-yx5rt-cqe" => "type1", + "6jidn-uqgyz-hl2l3-ou2rg-slu73-iztvd-yhdu7-hjfuz-cy4wc-jvoqc-uae" => "type1", + "7ft3v-76mqt-5r3h4-qiwe4-mkobb-mcsue-qugga-ctzdk-623if-rynkj-jqe" => "type1", + "buqsd-72zlu-hbgr2-hzjnr-mgvyj-6ldtg-2hdsd-igtpb-q7p2l-4j43v-5ae" => "type1", + "cafm2-w5pj4-74zoz-e6ltm-daszu-hjgnz-v5gpi-ljqxl-q3sas-i6vkq-dae" => "type1", + "coaon-g3fvt-m4ylb-35ghy-q4ftv-vnprh-joe3e-dwyyj-l4vi7-jjbxh-4ae" => "type1", + "h6f4o-rlx45-6w5m5-posbc-btmsn-ffrp7-nzc3b-unl2c-sfjof-4a2x3-oae" => "type1", + "kdkp2-wa6ak-urj7z-rcaq7-htloq-662lb-3ncuv-z2frg-l6ig5-zqw23-5qe" => "type1", + "qknkg-ssedm-jiaaw-dst4j-reyue-zaymp-b3b4a-yk3gp-ix7ch-ibyhm-sae" => "type1", + "scrp5-iiht5-iwgac-u36ff-jou7l-b7udy-mbim6-5nlte-5ki2w-6wyky-qae" => "type1", + "xszxr-w2ae6-g2fof-f6hp4-cquvx-4vyu2-ux2tn-nij6l-b7hue-4r4kl-gqe" => "type1", + "ytf6u-gadvf-mhb2p-pd6nh-ff7ca-a6xjp-vkmno-k2xum-vyzg5-jc756-rqe" => "type1", + "67m4d-5jvep-mnm7z-fjsm7-7aec4-bycrw-dntq6-v4ahv-4mele-yljgc-tae" => "type3.1", + "yyjdt-kk2xx-ddp26-4rioj-spbg2-pvh5w-omag3-odlzq-nscru-4k4vq-oae" => "type3.1", + "35ddr-mltrg-nxmdq-34ttq-ic4ra-ygatb-vwo2p-7bz2e-lgkex-5djch-3qe" => "type3.1", + "bptaj-nejw4-osqqa-zwrej-ysl2o-5ffgj-hkjr6-2w6fi-jczex-vjutw-iae" => "type3.1", + "zgeaf-fcq4e-fcnht-g7mpg-sb7ff-r6awk-zvkwp-gkloc-rr6jl-ghsse-mqe" => "type3.1", + "7r7go-kuy2d-xradp-g2yf6-64ft4-tza2v-nbs66-jnxp6-37dda-d6or2-fqe" => "type3.1", + "ami6p-hbo6h-zqoh2-lfqs7-44lch-tlkzt-skuxx-wyxs2-anezm-z2gxp-3ae" => "type3.1", + "cp7d4-uiulo-a46se-yzti4-6qpmz-4rhug-d6rln-7aa5r-nc63x-6q7u7-aae" => "type3.1", + "dofld-ghkjo-hynoo-myl2n-mgqql-vsbou-5sowc-bcwtw-u4sr7-4w4dy-nqe" => "type3.1", + "dqhdk-ebgsg-zvcgz-wbwja-xdmng-li73v-yrkd2-mblw5-li2wp-blgx5-eqe" => "type3.1", + "dur57-qqxpo-wmql5-bd7cz-atitx-5ivxi-ht3ni-lvb43-ms4w4-uyep4-iae" => "type3.1", + "femah-ntebg-2txyb-6ixyr-kl62n-aoqmx-pxu7y-imzup-o72wc-qfosh-kae" => "type3.1", + "fi2eu-lgaic-n73rv-dsfr6-52z3t-7eto7-pmdg7-r7o7n-agotm-zw6o5-tae" => "type3.1", + "fwpkp-tbgp3-rhl4h-njm2i-ebtn2-5susq-o765o-hjop2-qktc7-uo2kf-xae" => "type3.1", + "l7nbu-afo7y-adwcg-m6ivf-cooqw-v3pwd-jf4w3-kgsbr-tek3p-7n3dh-3ae" => "type3.1", + "ognrk-q4exl-3wf25-yrrsy-mtezk-e3qww-k6s5v-2pikz-gto6z-dyl2y-eae" => "type3.1", + "q3sji-7croe-uqcsz-rva4e-orlkf-c4y44-b2sl4-shnns-l57fz-fn2wa-xqe" => "type3.1", + "r5zha-ytiej-livta-dgmfr-j6kma-rdl4t-hldjg-hbydh-ogyjt-fow5r-4qe" => "type3.1", + "s2p3k-c7zfo-3ogmz-esx75-id6pg-6xv72-kifmd-gp46u-ay6vt-d7i5d-5ae" => "type3.1", + "sspbf-fne25-z7m5y-imq3z-ihf3v-qbyhd-ohrqp-zzfk3-dbuu3-zs7se-pqe" => "type3.1", + "txh2c-fneot-jj3ys-cx355-22tvp-cw3az-ohxyy-rk7ok-d7ppu-efk4t-iqe" => "type3.1", + "uouxk-c246i-dgxzd-ql3a5-koofn-mclrv-toplo-bg76d-l4dzk-ngb3c-nae" => "type3.1", + "xqhoe-c5pck-wcytx-owvyr-mntbp-u22ct-nvcqi-bri5w-sjow2-rs4gp-sae" => "type3.1", + "33aap-jupxw-h3tb3-nohh2-dphn4-z6mfh-22pqt-if4yr-dmkd4-ekp7q-wae" => "type1", + "3rdjy-ua53x-dvx24-3mrl7-lnzd3-v6szk-vmznr-a4pmm-usjej-oa73a-xqe" => "type1", + "4j5xx-bj63k-iky3j-xrlt4-pel7s-t6wdq-hgqwz-c6xhn-llihg-wxska-rqe" => "type1", + "55j76-nq5ge-pnxrg-nrkcp-e6do5-3jdkq-7scbr-elclw-66em6-ynu5c-cqe" => "type1", + "5d6pj-kr2cs-yijtb-45xtd-li5fv-eovkz-g3gh2-huatz-ozd63-kezlr-wqe" => "type1", + "5v6bx-3443d-zvacu-gas5w-uopbm-yxnob-i6rsh-rlxqh-hna7u-sy2aj-yae" => "type1", + "a2lx7-l437r-ul75b-shaoa-sknxf-b34k6-iybxi-c2zay-5aohr-ojlv6-gqe" => "type1", + "bwzot-oixde-fgnwm-t5osn-omshy-eqwzh-7yhlu-3wc22-6633w-l3mru-hqe" => "type1", + "dhxxo-emlvl-tt7zy-gu7om-hwcrl-a3rlt-sgol5-phhtg-jnyjz-gn5dm-sqe" => "type1", + "e72ys-dnhrk-wj2j7-oodan-owb7k-daowf-2klmg-s7t62-oolk3-5f6oh-dae" => "type1", + "errkk-popbp-7utvb-nd7l3-lby3s-wll6n-livqh-qdp4e-j6nx5-vudqe-sqe" => "type1", + "fkhpc-ymdy7-qvh2x-pptvq-tifyh-e3khb-pkobq-rpsze-2eboq-aruhd-3ae" => "type1", + "gim2p-hz54o-mzd2s-siwwq-sxmty-viqul-v3jtq-encmq-g2n2f-2uqtg-3ae" => "type1", + "gs6io-zywbn-ax37l-3sijy-b72cq-ljqnf-dhz6e-x5qqz-7v6va-xterb-qae" => "type1", + "h36ga-tlsez-easa7-c7kdb-jdkkd-pgxn7-5egmc-g76hg-3zzy7-jzxyy-aae" => "type1", + "k3b33-kyjfw-dypnj-zsfqx-njyen-d42vy-dcnzs-cjcnv-mfxfc-ss7rx-zae" => "type1", + "lq5t5-rrciq-ezpi2-ijwlg-mj4nt-ejsvg-rvdfn-nmkb3-aq7us-4zztu-aae" => "type1", + "mtca7-xvsln-kkjuw-sinz5-3y5pm-zsovf-f3vne-chss4-srqse-bbv6d-iqe" => "type1", + "mvz7l-y2czz-gd6xc-owaoa-3whpt-juf5h-w764n-5yyri-evswc-r7xg5-nqe" => "type1", + "oi6nq-uqkvs-k3rzj-hboqs-h6mol-wa57w-yc73l-6yznj-4awfn-krvkd-zqe" => "type1", + "tliwu-apd5m-f5kxi-g37rt-cqcag-4kmua-v7dsq-xsmv4-pvmhr-la6hu-xae" => "type1", + "vg7jb-6eav3-k75ea-zioy3-34rho-juy6e-6ipa7-ncdum-tdafs-4iiue-7ae" => "type1", + "x3cey-uerdd-53a7n-d45e2-gjsnd-airg5-nrs5i-4xujk-c5ynl-4pie6-yqe" => "type1", + "xgd6f-syjy5-2yrgi-m2ldi-nz2yc-jq5ly-23dim-l2g2z-a6rky-lpkxb-pae" => "type1", + "ym7qx-7aotw-higwr-yqcdg-uybjt-4vwfh-bjk66-7hqxt-lk66c-soabg-nae" => "type1", + "z5yup-qltdh-utdc7-72xe2-onaxa-6mvn7-qfcd5-otcyc-rzo6a-jg3dk-vae" => "type1", + "2grd6-wl4h5-ckvc2-agagg-kn5it-axvmv-4nogx-frgyx-5ulsj-kuoyo-lae" => "type1", + "2h6dm-k5h3n-nmqek-m5gty-yy3xq-a3atf-54its-biy25-ijh3c-xvit4-mae" => "type1", + "2sjsi-fu2xz-jbtds-6rhym-cdfxi-dseok-64txt-exaul-e4usk-ouhx7-xqe" => "type1", + "4kimr-mx6yo-ncuyf-temzj-lii2h-ex7ub-gvopk-iecxu-3kkt7-wogi3-hae" => "type1", + "62yzt-726hf-rwk73-c3j6k-fjdls-7oqh5-exbnn-bvrsn-2soat-m4vg3-7ae" => "type1", + "7r7kx-pfeyy-apl6r-7m3mi-nhuan-zy5rd-l4bxq-6x5ak-a4632-sqoof-qqe" => "type1", + "afjfz-erkja-k7euy-yjjrk-tsvsw-hfad7-allt6-4lb3e-kscys-7uyge-7ae" => "type1", + "bew7t-tep7b-5hpwc-pzfcb-wvyfo-mhcu6-vnrkl-a2f6e-2aq4e-c4ezy-7ae" => "type1", + "chcww-h4keq-lv3sn-kly6y-g3msn-tfy4p-kvlnm-fjurq-5mrw3-i73x5-sqe" => "type1", + "d4ndk-jxgud-mf7j2-63lqc-2f64s-vouw3-l6k2k-akpwd-b4t4d-vur7h-jae" => "type1", + "dh4nc-xdofs-5bsqf-zny3q-zjfhj-sn76b-vizti-sjzmv-5jvqc-sjhv2-iae" => "type1", + "dpxqr-vscc4-sslft-6b6hn-gk344-rqsok-j65xm-3lrh5-p622q-ezrbs-bae" => "type1", + "dsnjt-rnuu4-vcgrm-wacun-da4y5-emipr-6bw7s-rxogu-7o2f3-a6zwb-yqe" => "type1", + "e6qmp-abz5d-6xvm2-4knuj-s4nph-cs3nq-2vwbp-ht7up-tui5u-jolgt-oae" => "type1", + "f4nlw-f6qsm-hxf5k-mkjr6-q5jgn-imnys-fhbhr-jzmxm-73iha-gpbji-oqe" => "type1", + "flzwv-lslut-uvzxu-nrlrj-64b4q-om4c6-amsu4-nswej-io3nd-4gkkg-6ae" => "type1", + "hm6f7-as4t3-33b2a-msirw-hhcc3-eq6yq-uoamo-eif7z-5d35z-u3pqd-yqe" => "type1", + "ieaic-obdck-f2vgl-nouey-jjh4k-oy7tm-3g6yo-l6uuc-y2oot-nytkj-hae" => "type1", + "inlh6-ii3uy-bpasa-uzngo-xle3x-d2p2a-ylu3h-neikw-tnmwv-wep5x-nae" => "type1", + "jyjmn-pp6dh-2fapa-l67th-dcga6-awrsr-pguqa-yb6e6-dkrfi-izgy6-sqe" => "type1", + "km5ur-yacty-d2nvm-sitx7-zvnw4-bdjuf-lm3qv-vtftj-fv4vz-3d5y7-kae" => "type1", + "mqriv-hkd6f-wety4-rbjcl-ibf7a-i7kpp-62jsh-6yrie-2ahci-aql6t-dqe" => "type1", + "ns7kv-kp2mu-mw7yi-krd4z-eqcvq-qeeej-mxtwb-c2ujq-xtq6x-orzoj-3qe" => "type1", + "t7ih7-rbewm-ftxuo-pw3gf-3dobc-lcryh-ew4wz-rwexh-667xe-a4yox-kqe" => "type1", + "vaqrd-u6ayx-65ttp-6y5yg-touwv-dvpxn-uoxpx-n3bzg-wbq4b-7lwx3-jqe" => "type1", + "wvdeg-tfq6p-ahjbt-mdicu-hh3hr-243lv-hd5pw-xz2ym-xgaz6-bwmg6-mae" => "type1", + "xbcli-rnhjh-26fm2-om2gn-ysqvs-kufba-vvz4a-dy32s-ntpwk-pdkfs-aae" => "type1", + "zxph7-ibisu-l2d7c-k6klg-za5kr-scrws-manc5-cknfw-thpyi-vzmij-xqe" => "type1", + "3rj6n-nnhqn-nad5q-y262v-fuhk4-myqv2-6qzld-ouk7q-fbsuo-l2fi3-6ae" => "type0", + "5beqz-aqftx-tt4an-3cywz-vvjra-nbcca-f6wh2-ptpes-gujhk-n4hzg-6ae" => "type0", + "arizm-7m2c5-weboj-gfxj5-ruyxk-ns4pn-zdw3i-oszdi-25ynb-ivraf-wqe" => "type0", + "b7ldm-xgdir-7eraz-sws5m-tzstj-clkrl-hs444-ty5nj-qnkrx-nofhs-hae" => "type0", + "d7uw7-epfl6-7vcof-oband-nmx6z-hvowm-kjcdj-xlyww-3bzft-ymnv4-hae" => "type0", + "ey524-belml-leby7-hvt63-znr5b-xwkgp-5epra-kdmez-ynelx-2m4ys-hqe" => "type0", + "ffsue-5rmb7-frfqk-gvfpg-gu2bo-udoa3-zputb-kexzk-gd667-fit5k-rae" => "type0", + "fpr2v-bm77g-drpwg-jjsb2-hsqtt-62nci-e37d3-qsi33-gs72f-txmoz-gqe" => "type0", + "gfof2-34zxj-zwxpm-5fjhq-flbqd-jmzfp-sodev-xo5tr-hwewh-o4hrk-bae" => "type0", + "h4llm-3sygc-lnrk5-23lmk-oiucg-cwoex-dpmt5-jxatu-c3ucp-fs2ml-uqe" => "type0", + "h6q73-6j5qi-ykpxw-k4phy-s3kur-ln5vr-n3q5b-hacip-iruyz-fxyl2-pqe" => "type0", + "hbq5x-ekn6c-bpmf3-22o73-2c7mf-2usjm-nfmva-auaxt-2lftt-ymj2i-2ae" => "type0", + "hpdlo-v3eea-mgusr-i4apc-qcdr2-mwkqn-mddxl-ij6qb-ggl27-jxd4s-gae" => "type0", + "i3fxs-jmlyi-euesj-kajlk-63rap-vzpwp-bztsh-fnxot-4bjq3-kibqb-jae" => "type0", + "iu2u3-zb6df-pwuoo-m373g-vxymy-x75ai-75ymz-vb4na-uujp6-qdr5r-4ae" => "type0", + "jd766-qqwa7-b7ae5-kjhpv-z4ngj-oa6kv-rr4eo-l4mt7-om5u6-4yyni-mae" => "type0", + "l4oi6-dgzx3-vczc4-2o6xz-5ieho-ovabl-2hwwq-tgo7o-a5m33-krneh-kae" => "type0", + "rk626-swiut-nx4ou-xvoar-s645r-m6tuj-avwmq-i5ipn-kyr2i-5ndru-gqe" => "type0", + "st4fx-z43wu-xzc3a-vnajq-ts2u6-v5ws5-q36gu-5amxb-665e4-bsq2a-gqe" => "type0", + "upjga-zefaq-hlcbj-bu25y-4l4i4-chatu-j7rhm-l34kv-topaz-mhee6-jae" => "type0", + "voy3z-xqkak-q4x4d-wo3ek-qy2c7-tcbry-qoeuk-twoxj-76iay-oxy23-yqe" => "type0", + "vsra4-xeixj-fpwtj-qs4m6-hijj4-zlopd-wcrgb-gyyhx-eu5gy-fbsea-fae" => "type0", + "x5wch-pmfrw-336to-dfhft-qz752-ncbbt-d67os-vmlqf-3je3g-cgzri-nae" => "type0", + "x7qvx-dqst5-md5xr-nqops-uhrzh-kqona-ixvpv-4uj4g-g5msl-o7ndx-hae" => "type0", + "xtdbd-xwpss-uif5m-x3ef5-riz3b-3zrnl-4tmrp-bl2ib-nolax-zwxhq-eae" => "type0", + "zg6yd-2tq2d-z3sp7-5tq5c-gbhim-vacvg-jnhd5-q42m3-qxsfg-6strj-yqe" => "type0", + "4ilsj-76cys-w3sdj-znt6m-vsv6q-ak5st-iawte-mr4o7-o5d6s-jhego-7qe" => "type3.1", + "5rvo7-6mntd-ibedt-p3a6p-u3iaa-uhpqg-wqel6-pfw5f-ppus7-5ek4z-sae" => "type3.1", + "gvm7l-ds4n6-vkyjn-gwalp-3vdo6-qfmq7-pxhu4-zvqcu-ozvb7-qz3gr-vqe" => "type3.1", + "h57j6-72adp-jfeng-6xb5w-mqsgt-srmsc-gny3c-unzsx-fqrvu-ggao6-yae" => "type3.1", + "np33o-vcmlt-zvs5o-wfwgf-l2qcj-htqw4-vdp4e-5vcki-6edum-ejtqz-jae" => "type3.1", + "rfkza-27bii-6jan4-u4zll-lkvmz-snmao-irmlj-arpdd-kyxrg-xnq3a-7ae" => "type3.1", + "vwsvq-sp3z5-kjy66-srr2f-esws7-mzchn-oeqdb-n3qvz-wrzni-4pcsy-lae" => "type3.1", + "xetvj-ysqpy-wnnd4-fbytw-n6arq-w7f5e-fhsuo-7xd67-lwpt2-novnx-iae" => "type3.1", + "23kbh-5xqrz-2avco-tddeh-p6724-shtvz-rg3nq-d7rhu-ztedb-vki46-dqe" => "type1", + "34p3x-4cyvb-4iw6w-ehded-ee2kt-pb4uj-so6au-4t5ml-ju57d-qv5te-4ae" => "type1", + "4n6sn-mbz3e-phvpb-yg3bo-umjvi-t7d46-ohiu6-vu2y3-ugpgl-hdn62-6ae" => "type1", + "7s6kl-ygytv-rqall-6j2ub-hb6ig-bv3wl-gn5rr-ddnmx-okxap-z6ctq-iae" => "type1", + "ag3ia-4abo6-35aa5-lglmf-i2c75-5rml3-cc7c3-jkbuv-qucsl-fun3x-bae" => "type1", + "asad5-qg3gv-p5hrf-7liwa-zkoia-t2imf-sztmb-okb3k-gc6ei-xzp77-5qe" => "type1", + "b7abc-sdaug-yr3ve-jnhph-hxv5p-zmfw4-ppb3y-nc7sp-czqfn-yiedy-pqe" => "type1", + "bz4m4-xsx2j-seecl-igygc-26lej-uhktf-hawf2-d6lof-2whss-3jobs-dae" => "type1", + "c2uja-izovs-ro6du-iwaru-kbxta-flozb-dbwot-ov3cp-irxs3-ue2ak-bae" => "type1", + "eg5r2-acsfn-4dqc7-55p76-fjz2g-nd56l-mu43e-bnnsu-g3xkd-mc2hm-lqe" => "type1", + "go5zz-xs6yg-mylwl-v7uob-7bg4b-wjzhe-vmrwe-uy7mz-ckaz2-idm33-rqe" => "type1", + "hlyvz-kdhgm-gysfq-btlb7-e6mgu-2544q-bfbmm-xbtbs-2lbzi-crbgr-oae" => "type1", + "icl45-5n6st-mi35c-d5w4w-xyrww-chwnd-qbsqd-fztab-xeu6a-wck2c-2ae" => "type1", + "j763l-tresl-kppkh-pnbov-65xrf-f34ox-t4ofk-lpslt-4afnk-34dmw-hqe" => "type1", + "ktqjk-r6yho-cqgxd-qaqqn-yhxvl-ez6ax-jk3n4-bfa2f-vprkh-eigf5-5qe" => "type1", + "kxaax-ngzuj-otcdb-jytm2-6urns-a7gos-u7xgu-7725n-bkmb2-ql7te-lae" => "type1", + "nyo3z-asy7t-jxc44-z4cvr-tts4h-2ipvj-aawbj-37xkl-7lcfq-psuoe-uqe" => "type1", + "pfmqh-xphm4-h4wkn-nafsx-aix6u-h4gbl-owfhy-wnm4y-vkqyu-nmlhl-aqe" => "type1", + "qnn43-fomt4-663kx-fqetc-54jan-ccsww-j54nw-quf3n-vudjb-qsfg3-pae" => "type1", + "rs26k-ldffa-z7lqu-rhjps-tumxa-arwvb-mdyye-mhevv-4dpjm-xo72t-mae" => "type1", + "rv6cf-76ajy-qgcld-d7gil-e7v22-3sqfv-yc2zs-sn5r5-465el-pbym3-xae" => "type1", + "rwze6-4mvpx-o5pdv-3sujg-2szcx-223oi-f3jnl-jx27g-2qs22-dmt2j-jae" => "type1", + "tfw5b-kpj3p-bniym-lllfz-fzcuq-4c65m-xqv3y-dy44l-fvmsn-yebii-pqe" => "type1", + "vjerv-rzs6v-lkdyu-6pr4p-v4tmu-a52va-cbhc7-bjt4b-gl44x-b4vmm-eae" => "type1", + "vmzrz-weexa-xlaci-rs37k-ld6pb-xe7x3-y7ryv-qvxkx-syybl-t7pcy-nqe" => "type1", + "wkza2-nuadi-tkorq-pw4sr-rejed-3stjg-wjiov-mzzbq-w3ytl-m5phl-iae" => "type1", + "xjhzy-fombc-kpbfq-xwltq-fia45-sbfq4-nkzor-42yr4-tj7wj-zoprs-xqe" => "type1", + "xmg5b-vziyw-6bzii-yfbmt-zll6k-v5bd5-m3ds6-7vdyy-wmnkd-lznvp-jqe" => "type1", + "2hl5j-s6wlm-t7ba2-rxp52-2ii5d-vsbjk-tfwt7-twdbr-rqlyw-hzumc-iqe" => "type1", + "2vmqi-lwsmy-rad75-wus6x-3rlbf-cgig6-rtheo-t42r2-i4vdg-jyguw-uqe" => "type1", + "4yqpb-nbwye-y6yv6-c5ffq-xvuho-tpw6j-zeqj3-y3u62-q4pai-ewmot-rqe" => "type1", + "6prwl-m5hqg-fzv2r-bnt44-hddso-y6cq3-w2ewq-t7auv-jga2o-gpsxf-4ae" => "type1", + "6xfo2-g6moa-wiadg-3nyup-47m5k-ee7rf-wjxij-lv3q5-kqivn-hsmgp-jqe" => "type1", + "7pwy2-67vvv-agagj-elfj4-gtef2-jxddy-ywxtt-74afr-aeuil-zecky-iae" => "type1", + "aicg2-docau-ham5w-5yl6j-k5jj5-tc5yu-eicbx-2plu3-ta6ey-ixhun-xqe" => "type1", + "ar2vm-visxo-z3dgi-wsjux-ql7qw-zlfpk-ubvwl-tszz3-i6d5h-jrguv-gae" => "type1", + "cvtri-noxxa-2yzff-iql3l-fugiz-pqutd-bph2v-xbri2-oiriz-txbxd-hae" => "type1", + "cwb7w-kd3lm-4jzgx-mljqg-ptquz-665kh-w2svb-22eca-fab6l-jelhv-3ae" => "type1", + "dtde4-pat5g-ai3p3-uajw4-yxpoc-ki7vb-jrfnt-hqk4b-dtzwv-66v66-hae" => "type1", + "gbavy-dj3ok-jnmaa-pnbhh-sgxlp-gzcap-mt5ox-v5cml-eivtx-sodxc-vae" => "type1", + "h6ebo-ijrui-q5jlj-mb7s5-lavj2-fxff7-2vui7-3pvi3-yhhtr-jfquh-4ae" => "type1", + "jwzgd-5fodi-b24f6-ghzrh-z4v2o-4fhvj-3dmmt-c3xmj-sj5pn-ifajg-2ae" => "type1", + "krwx5-u5srm-kqbyx-szogs-gaseq-qzef4-txjmy-ql5em-k6q5n-h3ono-bae" => "type1", + "kzlfn-xeqoz-zcrlc-no4r5-pfjdw-flzfm-cq7zw-bbipw-fw3u7-bsoa6-4qe" => "type1", + "lvac3-kgilc-zs5x5-skq6a-4ajcq-xkd6g-6twwm-ltyum-rphgd-f3iei-lqe" => "type1", + "oudap-3zumt-7w4gh-m42la-gb3gj-6wugv-e3tlc-fvdxz-7xuwm-6rr7n-rqe" => "type1", + "pptbq-moz46-a4j7y-njscm-t3d3o-wj2pz-3vbak-4fqnt-trxz6-f4fg7-eae" => "type1", + "pxyu4-nrrqd-w3vmd-egofs-gpia5-tljnw-gtnuv-rueu6-2sxij-24x3x-gqe" => "type1", + "si6b5-vl4vg-tzvkr-is64k-d7kns-egjpt-qanwn-b2j2r-jspxd-4nkh5-zae" => "type1", + "tehy2-2tjhm-vzlxo-ndtx3-4slxy-kxjkd-xynyf-vnirl-yvixb-yg6l2-nae" => "type1", + "voskz-5mamq-myzix-kxu4f-unkss-vuxxz-nnadv-ffsja-b4cac-khfwm-5qe" => "type1", + "vwdbg-uwo2y-ta6p2-mxwi2-6oztv-hzh4t-ugx3m-dkbbh-mgx7f-ifrah-6ae" => "type1", + "w2l33-vvva3-qvjdn-2vgqn-qqbif-i6fuy-ekpwi-ksq22-ru4rz-6ycrr-tae" => "type1", + "w53hu-bdzuz-h7h75-weodb-getvj-rr766-m2rtb-bigdq-l62cj-7atxw-2ae" => "type1", + "xyww7-j43q2-nqleq-agfny-e347w-zrshm-xc2bo-7g2wy-yqksi-hksrj-aae" => "type1", + "xzsfe-hynle-c5itt-xmho2-rg4ww-muhcu-ug5t5-ia6c5-yyeuf-zolpi-qqe" => "type1", + "27sux-76qjd-typcm-nsqtq-s32oz-fi57t-mvhxa-2v2cj-iwmq5-4ahba-lqe" => "type1", + "42hky-4faqe-makxq-qrpqo-ptmlt-lhhr4-p7kcr-h7n4l-zk2sj-6txec-5ae" => "type1", + "5mpdi-v4qj2-64are-tbe6r-6335g-6tksh-c7uzq-w3xyz-o4p5g-rwnll-vqe" => "type1", + "6oooh-jum6y-focwn-kx5ba-omunf-jksiz-25lqk-xvcvb-7afff-3ceei-nqe" => "type1", + "ggybn-cegt4-3e3xz-z43pe-cazxs-mtznq-kpyyu-tdtcf-op4o3-ourqw-cqe" => "type1", + "k4o6k-prjj5-ytwas-fgqyv-6y73m-3s5pz-fq3g3-7bexa-fdhmu-hk42z-lae" => "type1", + "ldvyr-wn6nd-6sfli-h7dfr-epv54-d6elz-xnfux-37uyn-f6ga3-wyedo-5qe" => "type1", + "lpblw-7kryv-ah7ju-34ctx-a62hs-gx3b6-shxea-ut6ot-fwazf-subej-qqe" => "type1", + "lpjte-7knvh-xgn6k-bf2fh-siduu-g5323-2z5o5-v27dh-2kxfa-hrktk-jae" => "type1", + "nioyi-lxtpk-fnexd-5ib4d-3x523-uu4ve-ro2tv-z5yuq-sf3dk-fdhrf-4qe" => "type1", + "oswv7-a355p-a5jlp-ko7pj-arrs2-rghho-dti4z-xgptn-szn55-jjr46-uqe" => "type1", + "saw4q-px4st-tqivd-luwao-njxl5-hjiuy-7j365-mvphm-a5g5x-zkooy-kae" => "type1", + "t3ddg-me5el-nt2c4-4p2a2-7qyyy-v5rpy-yosa3-k3z5g-ehe2c-7vq3g-4ae" => "type1", + "3lgs3-hz4d6-jgdro-tdx6k-vzqp3-cuq65-lgrmm-j7bj3-u2cku-2bw5k-fae" => "type3.1", + "627qw-ey6wj-efg3t-76ixt-o2x5p-r6bmb-vf4c6-ycmyy-oiba5-wloig-dqe" => "type3.1", + "7meyc-3kacw-uidhh-kqorx-mziq5-hnr2e-qhhcq-mgjpp-j6sep-73gvv-nae" => "type3.1", + "ldqxr-qdliy-howvj-2tamr-3fwul-liakv-7523k-tmhrz-5geyk-jlu7b-uae" => "type3.1", + "um4so-2axl2-74745-lhyhn-afhgh-yog7e-pcsso-mxxho-7x5ev-x65uz-eqe" => "type3.1", + "vgpqf-udio2-pndqt-qweze-okgmn-yzqh6-i7ym4-mospu-sxsn7-khoxs-wqe" => "type3.1", + "zbzin-kgyio-vai3o-ghyz4-36boi-4tjvv-7fis2-d2mlq-xk7fx-d4udt-xae" => "type3.1", + "gd36t-bwrnm-qq6rg-sas4p-6uabz-qki2e-nrq2k-sp6dj-l2lxa-5tavx-7ae" => "type3.1", + "mswad-oq7wj-5r4yy-b5qoy-cmv7z-wzfb3-ktn6l-rcnrz-mni2f-lsys6-wqe" => "type3.1", + "catzb-pqs4z-h25rr-jwdtx-hutzu-qqw4j-kkqux-w53ey-mg56o-tb4ay-fae" => "type3.1", + "ouffe-miylc-6zcwl-afv2d-lai62-qwzns-xtlji-p7pu2-qkx2e-x72y2-sqe" => "type3.1", + "qzvif-vpece-l4rga-6l6v7-tdegr-ycnty-p3zgo-qzghb-v5hmq-edjbv-lqe" => "type3.1", + "c4xi6-jnokz-uhpvd-lpwoe-hra3h-ph7ia-77clm-xfxf3-lskj2-4fohp-tqe" => "type3.1", + "myguv-fs4u6-jl53y-vk4pf-6xojw-4umwc-r55jy-jyy7q-f4gmu-wvliw-2qe" => "type3.1", + "q3vac-kcwo2-ruiht-nflb7-ifoev-vkjcw-quybi-ugvgn-pqfwp-jntxi-dqe" => "type3.1", + "rzm7j-37ied-ynlvt-zma7n-r4bjg-wxg2d-kr4me-ss4yr-3p5se-n4sry-wqe" => "type3.1", + "45huy-6h3k3-m7uao-7w4bu-dtcgx-4yxpc-s36gr-pq7k5-xesnw-vnjut-oae" => "type1", + "4bokb-dd7ie-6sx2e-s75z3-ncqvu-rfjzo-gfe4g-lzlma-bship-vmd2o-vqe" => "type1", + "6hkcx-vz4jv-4n33r-ywdvs-sefaa-tb2on-rac6s-4csut-iyahu-zmct2-5qe" => "type1", + "6qxes-2iftw-fq3we-unkdt-y7wnk-4v26t-fftzu-chgeh-t2ljb-qtevx-iae" => "type1", + "7tw3g-yend7-qcyyz-z4heh-3hpju-ueuhl-4j5tz-5chib-o522j-oke2x-eae" => "type1", + "hgbum-72ne6-onsua-rjul3-siwu5-da4zu-wkqro-rm4el-52osh-bhkgi-dqe" => "type1", + "a2e7m-evijx-geoc6-o5j6s-h45ye-f3hxj-w7dfi-25e2k-v5ylv-nf7ud-aqe" => "type1", + "gd2vp-cewud-bap4i-b3vb4-jbxhf-3ojbk-d2n6l-wg46d-vcovk-bjwyz-tqe" => "type1", + "gtc2a-34z6k-mld77-aucld-n6l4v-yv5hz-mon2d-narwo-b2z2k-thpyk-uae" => "type1", + "lkrgq-rqmms-blrdo-24alx-2jrtz-xl5qm-dk6xc-o5fvc-o2huw-bxtqs-wqe" => "type1", + "rp2ka-fxcyf-ghmgv-rgi3q-ugpxs-t2xfv-an2zj-yjzfk-kkpuu-pbha5-5qe" => "type1", + "tg4ec-b2g4p-h42kd-k7zvb-6rls2-i2q7l-gtr4h-ymr6v-rrez4-w6fao-oqe" => "type1", + "uk6n5-nw7vs-zeydi-qdmgp-xbevs-fnhce-35je2-3m5fu-l2wzj-wrcxb-5ae" => "type1", + "y7vmg-unfmv-u6gdl-piob6-7en2w-zmwjb-oqbpn-ufqkm-4i2cr-osl3d-iae" => "type1", + "25o6i-sdjsl-vjlfn-6duch-vskdu-26pf5-cwibg-zooqk-sdn2e-cgugm-tae" => "type1", + "7g3hi-3ypt3-4u3qa-6jmvf-eptxu-loh2i-poiih-fmgxl-7vlyd-g5hy3-nqe" => "type1", + "cgh42-4om34-lxkeh-wpu7r-7badk-rojad-7cysm-shbb3-xh3qj-rv6zj-uqe" => "type1", + "dl74z-6vpps-k6bpu-5hjsj-fb6lb-34tsv-ue5gt-bdkjn-35pt5-fdu2a-rae" => "type1", + "fopwg-cu6ej-o7qfl-zuon2-ia7lv-qre7y-hotf6-bs7wk-aj4we-nf646-dqe" => "type1", + "fsqi5-jxy3h-7enjy-d3734-zqvvu-s6nmh-o7xyk-6ahom-ecorw-o4oro-mae" => "type1", + "gogli-5653n-elex3-qlpqt-57upc-ahqpe-jel3r-jks5y-yy2ak-5zcgu-hae" => "type1", + "itbwd-42ljt-6wwrt-w4vt4-xbgnu-m32zi-ex3ss-j6df4-doach-da7bq-kae" => "type1", + "jgetp-jxcos-7t7xy-nhxmv-pgf5l-kbmms-e4wk7-xxtaw-ohuwb-cpqew-xqe" => "type1", + "q5nwx-7i5tz-xoxyj-baxhx-i4pu7-u645x-d4cpw-ydiqe-vlr4u-pqbxl-jqe" => "type1", + "qd2x7-pikqr-gsiqz-uwzwd-w4tz7-zjqn3-qvh7d-itbtb-5uqso-5pj6i-uae" => "type1", + "t3fh2-surtx-hjh7n-dmwzv-rsj56-krypw-yh27v-lad6d-xughx-xebse-iae" => "type1", + "v2pkj-vpsow-fp24q-zqwfj-p3nek-m52xz-oz6ra-blmra-73voj-jvwb5-gae" => "type1", + "w3phu-4ihah-tqdl6-n32hp-e5254-l6ybu-kvmmx-thj6t-q7z7k-swvi4-iqe" => "type1", + "3tbjz-erih4-dfm4f-ul544-mgyqe-eeesn-hhggl-ooq2x-czsh2-nuy6n-cae" => "type1", + "5oe2d-on34f-iyvla-5ejfo-34n5u-yllm4-zxdgu-scaji-uleio-yeszd-yqe" => "type1", + "ctqez-oqvmf-kkwto-wlehz-grh5a-l7enx-7e7ds-hcn75-mpdj5-pujdj-eae" => "type1", + "d2ffc-r2mqq-yx3u5-f5j47-davp4-lske5-57oal-ilwph-o2hex-tqaz2-7qe" => "type1", + "dgul7-oxqwq-uvafn-evmwt-mjrzb-okeie-rykhk-wgrmv-o6kkb-kpco3-qqe" => "type1", + "duvf4-lt2kx-alax2-hgno7-qwwxf-pkuxt-fgwwd-m6tmw-3krjd-mqdzn-2ae" => "type1", + "gnagd-f5kwr-7d2i4-e36x7-4tj7p-t4oki-4kvlc-pvnqr-annsa-sgawm-6qe" => "type1", + "hho73-ab64i-p5e6m-fotpu-gnbzo-hnytq-4ptxe-3uovn-vkii2-ammf6-aae" => "type1", + "ikdvw-5ohcj-bucuv-m2gp3-56pff-ridwp-nvq5u-edpdi-idqzy-qyko3-wae" => "type1", + "j63cj-up2z6-xh7m5-m2t5m-t4xi6-6tqz2-ibj4a-mmm2s-7o2bv-ynnc6-xae" => "type1", + "jtvnx-kem2o-icln6-b4oy6-n5ru5-dmksj-dfk5i-4ejvq-k3unp-47gjb-mae" => "type1", + "mzcnj-vnzbp-sjmlf-bmjcu-3qim4-7ouxc-woglo-vyqma-q7sxt-v3nez-rae" => "type1", + "ne7tg-d2fcq-mlxl3-2ljre-4op5b-oyx5m-lt4ao-ctx6y-4vxiy-56ep3-4ae" => "type1", + "p4khz-nv35h-omz5j-3lflh-f473a-nwumw-yi74i-xwozx-ykk5t-heidc-rqe" => "type1", + "ptxxp-fechw-m6taf-lrkaj-hwtas-rxq6p-o6tko-yho6t-cjfzc-zkgdd-sae" => "type1", + "pyeyr-5jtop-v3f4l-v23v4-5v3gs-2jdhq-3bvwo-fu3to-kehj6-iae3e-yqe" => "type1", + "r2ijy-htif2-j4xyp-ahqs6-ugxn4-tulc7-3bljc-jyobr-lt2kg-6i3au-6qe" => "type1", + "s52il-lowsg-eip4y-pt5lv-sbdpb-vg4gg-4iasu-egajp-yluji-znfz3-2qe" => "type1", + "sdytw-krjjp-mac6s-wnde7-sulf2-zxzdx-dpoan-4r5fn-qdgol-7buz3-mae" => "type1", + "ucumw-ex6s5-r7nyd-x546u-f4rcl-qllyh-waid4-xxzvn-25op7-gnsjy-bae" => "type1", + "ujv74-l3ksb-6sbwv-kagne-xhhlw-isomc-nvrdm-rxk54-bx4wx-2cjjo-pae" => "type1", + "uutlq-cgsfs-t5h2j-cxpra-iokzp-kormq-kzur6-xzegu-76tf6-anmti-dae" => "type1", + "uww6o-imb3e-w3kxy-feb2g-67bsc-ewq5x-big3k-6soeo-2qef5-eilfj-rqe" => "type1", + "vrccq-gbv6w-ejqbu-kkput-4gggb-gcpnl-wwckh-3tug6-7xly4-sg562-6qe" => "type1", + "w7ahu-lheyl-fxlh3-fcid6-ey4qj-7qjj7-yobio-qhb7u-jf5zg-6jp3i-mae" => "type1", + "wbz2k-b6che-eckjw-vdwro-gyxhj-276mr-lxijv-wvk6b-kdmqn-s7zzh-6qe" => "type1", + "wmoyy-drk3r-otwke-cikkn-cyd7x-v62ys-uijg4-mqtmn-3jm7g-crtd3-jqe" => "type1", + "zu5xp-ejlxh-jnmw7-pk3se-h3vfb-nmns6-kukie-e6lno-5fa6s-ssyki-aae" => "type1", + "3qje6-mry24-vsppb-xa5vi-n7x3s-fv7wu-te3gf-fy3zt-ukj4b-jqhd3-oqe" => "type1", + "5eupv-hmhnq-s5f2j-g4moj-o3v5m-z67oo-r5kwk-oilpf-j5a3g-orhbw-bqe" => "type1", + "5mmbg-lg2p2-vlsub-pueuj-yfns4-4afkc-si3r3-levsj-faans-77lhw-qqe" => "type1", + "637ly-bw4c6-d5yzl-2q7ul-n6xji-n3qxj-n7d45-kpfop-nz4xw-xfbup-oae" => "type1", + "afah3-52pph-ado2y-ftnsb-67fh2-nefzi-g6bg4-jpo55-fbm74-naxlf-sqe" => "type1", + "ahbuw-hjqct-utupn-vhttv-omfcl-ukoi6-gpdm7-guc5d-ikx46-x3kfu-fqe" => "type1", + "c2vrr-ynjrh-xturt-s77ah-a5efo-b726x-dgq4a-m7zdc-wvsa5-7ogz5-mqe" => "type1", + "ciujb-q722m-zr3kt-st4jb-pgo3f-sosku-t33vy-x3pxa-3swy5-gecvn-qqe" => "type1", + "dhnn4-c6aku-rkeom-5xxmr-75j3c-fujxc-aevdx-yggro-z3t3a-euhiv-uqe" => "type1", + "fx44r-evw23-wrr4q-63enw-5cz4p-2llms-aplox-vokem-wp3o5-jaggv-xqe" => "type1", + "gknly-c43zp-yegai-norxi-ogfq6-eq5dk-na6w3-hfmyk-kwq3s-g2eag-nqe" => "type1", + "iibos-j7mxr-q6d4w-fufdp-jo2se-vpzty-u7fxa-agw2u-i3ubn-cm7zm-oqe" => "type1", + "ivca6-b7ahu-we4ux-nru6y-gsifn-eqylj-tdaj6-q55vm-t6nru-kpc64-zqe" => "type1", + "jiy6l-jx7ea-k7y2u-tznfb-dcrm2-5fhrt-r63pd-wdfse-2ubgq-6pfvi-lae" => "type1", + "l2vnx-tzmcq-tx2me-h5lr6-p4v7a-5mcax-577kz-4wdwt-ytajx-zys32-3qe" => "type1", + "lj6xb-t5tcb-dol3s-ay52b-dulse-zyezk-25qyv-l7yqj-4zrc2-hp5qz-sae" => "type3", + "mhhqr-pkzrl-oc5nw-d73xk-2dlqw-2carw-ym4zn-ureso-kypbq-e4ucp-mqe" => "type1", + "mrvdj-jjgyv-vss5s-6sty5-blvzm-sse3j-ayctj-2uvds-psutp-bwtwk-tqe" => "type1", + "nafq3-4b66f-jzhd5-yggyt-27stn-paph5-rplxq-aqjxy-h443w-tgajm-wqe" => "type1", + "nupee-6cwoq-r4kaz-c5iik-fmdd5-bklsp-ltlmf-gnm6h-qftki-gszqj-vqe" => "type1", + "oxevh-yptgl-yzu2p-w6dyn-lr6jc-b2xaq-aatz2-yyfot-hm4pr-ucvh6-gae" => "type3", + "sbsrk-twbzq-m7cdk-immij-tivoc-sqykl-l3gwv-l4mei-yxitl-rtfjs-bqe" => "type1", + "tbra2-am7yk-64erk-n4hgk-t4u3t-yd6zb-4imgx-yi23u-u4dha-j55n7-eae" => "type1", + "uznxh-cff3i-uso5i-u27p7-ardz7-4vihf-vkme6-i76b5-oejzx-2xewb-lqe" => "type1", + "vkvln-5mamg-drimo-qqv23-pav3w-slpjc-za3ao-ognby-4ylfd-7hvo3-wae" => "type1", + "vxos4-tb4ln-4uvj3-3s3rw-42icw-ya2iz-pgxc2-edn5t-qcj44-d7ri7-rae" => "type1", + "x6ycj-fp3mu-5bnyn-ds36e-m64fb-zbbwy-fy2lp-nuqkk-rx4ts-lz5xw-eae" => "type1", + "xm3hn-t5bnb-eetse-rtv5u-ochhb-tsl3g-ax2xv-4iwdq-3kv6c-jmkiz-qqe" => "type1", + "zj5e3-wbiuy-2l2sy-d3dat-yt44k-kajnn-fwyyx-aui56-wlx2s-uzvox-3qe" => "type1", + "zudz4-2sqmw-qllkb-3xofn-jt35a-p476p-atmli-nozur-6kxzo-sf46k-xqe" => "type1", + "2kvun-y7qfw-krhhm-aftvi-ll56o-yvkuj-noc5w-xozic-mnewn-i47x5-tqe" => "type1", + "5v2bn-uoktd-3ne5o-lxcws-askra-k4iyq-fxt2j-3gdpc-t2gxb-hiybx-4ae" => "type1", + "6kuom-ucaom-u7xm5-s556x-3rnyk-hdjzd-wkxnx-wsejf-vpnhz-lniqi-nae" => "type1", + "awpkn-yaohh-7l6ta-dvz7u-mxrqo-7ahi2-ag22k-3e3dx-vjnaf-mkbmj-sqe" => "type1", + "clfg5-ls62b-c54r5-sgken-xuwyh-p2coy-rsod3-2rvlu-cvbwn-whuyc-uqe" => "type1", + "dhnjd-aemdm-qq2fc-lacqo-fwerg-tc3g6-xsbkg-pbwgf-y2rez-mh5ef-jqe" => "type1", + "e5xk3-7dbi6-2zaxv-7ng3v-if5va-yktmq-roase-rqvx3-dii3z-2kh3r-vqe" => "type1", + "ec62l-q44va-5lyw2-gbl4w-xcx55-c3qv4-q67vc-fu6s7-xpm2r-7tjrw-tae" => "type1", + "engai-flho6-5z6nk-4eirk-ozvgk-z73wc-bgayv-lbnls-djvfe-lheum-jae" => "type1", + "gc7j5-ullnt-he62c-pzbe6-bwvba-2jh7m-g5f5a-u2haz-nhwif-52nfa-bqe" => "type1", + "h3g2z-jgfid-7hvbw-jyfw7-4jiza-iyy4w-bvx44-nizgh-kct53-ffjtz-lae" => "type1", + "joich-t5ted-j3ibc-n7ytv-bomzl-jqimt-7gc72-xujql-xahtn-grunf-dqe" => "type1", + "kajie-lx37k-yqw6l-pmzhm-xlkw7-svttg-ju2gr-2yox2-ee6rb-kzx5k-qqe" => "type1", + "kdowl-chtwc-lkojq-lfb2l-p4w77-35pmk-3ixsh-hqhj3-uxi4k-jh7ge-mqe" => "type1", + "lzjli-4do6u-xcbtx-co7r7-35gzq-5mmqw-k4jb3-3s63m-facun-6riew-lae" => "type1", + "mhwuz-qcw22-7uxip-wmnc5-kv2wb-ttehc-pcy3g-nb65w-622zg-rgkks-lqe" => "type1", + "oaxev-miabf-nxzvk-inhz4-67wdw-urlun-euywx-656kk-iffmy-3gdp4-2ae" => "type1", + "onc5z-2bkny-x2rr2-2pgyk-kpnj3-i4xpr-7sz77-pmawv-ncyte-rviz5-nae" => "type1", + "pri5a-une2z-drxdd-pjg2y-chwiz-hsuhx-xe4l6-gnio4-cgjvm-virtb-3qe" => "type1", + "pvo54-4ykmv-7iumz-jxrld-qma55-wtgck-jmdui-r654s-bdjne-db7lf-kae" => "type1", + "rpmaq-iinj7-yufzr-4ekre-4sbok-wzkzu-guxeq-sxmvi-5rqm4-3kfe2-oae" => "type1", + "udlch-g42nv-er23m-bgpxk-oiymd-knkro-2dczg-hyv4x-2yiw6-q2jcz-hqe" => "type1", + "vudfj-wtx2a-hrx3r-rzc5m-dbuc2-hl5m6-b2liw-pxjzj-ugiwp-62frd-cqe" => "type1", + "wolwk-thbgv-pnuak-dodxy-l5owl-mrfgz-alrb6-whu2h-rbmbq-5obcq-wqe" => "type1", + "wzrqt-5cxzt-i5g2h-irxh3-33pao-qke5v-6rfz3-brj2a-ibjnw-ycqlx-xqe" => "type1", + "yzi7p-lnf22-u2wot-tgxzn-v3ubs-4yuqq-fywh7-4tkk7-ly2xh-r5byg-mqe" => "type1", + "yzwxm-j2n2r-whdxh-xqftn-lobyp-h5c5c-zomu3-uqc7k-gatex-aoayd-pae" => "type1", + "zcxej-wovzy-rxnvs-bcz3a-ruh4s-a3blo-24ej7-izr7h-toic5-ix5gu-qqe" => "type1", + "7jjmd-p43kv-2h3bp-yrcuv-gc3ia-zaess-2rj6p-p4xgr-f43xy-6hk55-xqe" => "type1", + "aid27-4kobq-rsaze-7bvbc-pwg2a-nrhj3-uhpne-yjdis-3rrx4-chcxm-rae" => "type1", + "cwaya-vbbgq-uch26-lqqm6-zrfhz-4edlc-3tytg-cstua-pykwr-f3vme-2ae" => "type1", + "exztv-hm2d7-ewdzm-5chfw-ej7vw-asjxc-ch63f-27j7c-ydije-kd3fs-hqe" => "type1", + "h46do-f3rxf-puasi-4uipi-5ymgk-s3rrv-yazcy-darzs-manii-si7hg-gqe" => "type1", + "k7ffp-zum3t-rtcce-kuexb-xloxn-dfp4x-b5i2j-jrh6n-jitao-xm2o6-nae" => "type1", + "lim7b-klnkx-adypc-pmet6-66zcr-zmhmh-t4zch-wlqrl-pnclk-qoadk-vae" => "type1", + "lovrr-efema-wp7pq-cfp2q-clwvx-c4vvp-7zb76-ixxoi-7jtke-joyqp-aae" => "type1", + "psfos-cul7d-luvsj-7er7k-oahxc-qx2qx-etnev-glh4c-5bybz-ivnlh-kqe" => "type1", + "qht2j-y3ztk-p4acr-rveul-kn37n-xc466-auaby-crl4s-cs5ki-vj27v-vae" => "type1", + "sfuvc-ddail-b7u54-5sthn-rk6vg-yvuf7-qjcv7-fxmgz-uqhtw-cuc3a-6ae" => "type1", + "wagpy-b4c35-wwopp-jmiro-bqnsq-uo2i5-iai72-4yitf-lo7vy-rs5wa-2ae" => "type1", + "yikms-ku6l6-3brmt-a7wji-oizgc-gopwj-my4wn-y2nsq-mjq6q-acuaw-7ae" => "type1", + "zuabx-u7xyp-pgluz-tdqxl-eubuu-oxg65-i5lup-qh2c5-5wool-rsvp4-oqe" => "type1", + } + .into_iter() + .map(|(k, v)| (k.to_string(), v.to_string())) + .collect(); +} diff --git a/rs/registry/canister/src/mutations/do_remove_node_operators.rs b/rs/registry/canister/src/mutations/do_remove_node_operators.rs index 28399444174..365af594de0 100644 --- a/rs/registry/canister/src/mutations/do_remove_node_operators.rs +++ b/rs/registry/canister/src/mutations/do_remove_node_operators.rs @@ -3,12 +3,11 @@ use crate::{common::LOG_PREFIX, registry::Registry}; #[cfg(target_arch = "wasm32")] use dfn_core::println; +use candid::CandidType; use ic_base_types::PrincipalId; -use ic_protobuf::registry::node_operator::v1::RemoveNodeOperatorsPayload; use ic_registry_keys::{make_node_operator_record_key, NODE_RECORD_KEY_PREFIX}; use ic_registry_transport::pb::v1::{registry_mutation, RegistryMutation}; - -use std::convert::TryFrom; +use serde::{Deserialize, Serialize}; use ic_protobuf::registry::node::v1::NodeRecord; use prost::Message; @@ -20,26 +19,21 @@ impl Registry { let mut mutations = vec![]; - // Node Operator IDs that are parsable as PrincipalIds and have an associated - // NodeOperatorRecord in the Registry - let mut valid_node_operator_ids = payload - .node_operators_to_remove + // Filter Node Operator IDs that have a NodeOperatorRecord in the Registry + let mut valid_node_operator_ids_to_remove: Vec = payload + .principal_ids_to_remove() .into_iter() - .filter_map(|bytes| { - PrincipalId::try_from(bytes) - .ok() - .filter(|node_operator_id| { - let node_operator_record_key = - make_node_operator_record_key(*node_operator_id).into_bytes(); - self.get(&node_operator_record_key, self.latest_version()) - .is_some() - }) + .filter(|node_operator_id| { + let node_operator_record_key = + make_node_operator_record_key(*node_operator_id).into_bytes(); + self.get(&node_operator_record_key, self.latest_version()) + .is_some() }) .collect(); - self.filter_node_operators_that_have_nodes(&mut valid_node_operator_ids); + self.filter_node_operators_that_have_nodes(&mut valid_node_operator_ids_to_remove); - for node_operator_id in valid_node_operator_ids { + for node_operator_id in valid_node_operator_ids_to_remove { let node_operator_record_key = make_node_operator_record_key(node_operator_id).into_bytes(); mutations.push(RegistryMutation { @@ -73,3 +67,50 @@ impl Registry { } } } + +/// The payload of a request to remove Node Operator records from the Registry +#[derive(Clone, Debug, Eq, PartialEq, CandidType, Deserialize, Serialize, Hash)] +pub struct RemoveNodeOperatorsPayload { + // Old compatibility field, required for Candid, to be removed in the future + pub node_operators_to_remove: Vec>, + + // New field, where the Node Operator IDs are passed as PrincipalIds instead of Vec + pub node_operator_principals_to_remove: Option, +} + +/// Wrapper message for the optional repeated field +#[derive(Clone, Debug, Eq, PartialEq, CandidType, Deserialize, Serialize, Hash)] +pub struct NodeOperatorPrincipals { + pub principals: Vec, +} + +impl RemoveNodeOperatorsPayload { + pub fn new(node_operators_to_remove: Vec) -> Self { + Self { + node_operators_to_remove: vec![], + node_operator_principals_to_remove: Some(NodeOperatorPrincipals { + principals: node_operators_to_remove, + }), + } + } + + pub fn principal_ids_to_remove(&self) -> Vec { + // Ensure only one of the fields is set to avoid confusing semantics. + // If the new field is present, panic if the old field is also set. + // This approach encourages clients to use the new field and allows for + // eventual deprecation of the old field. + match &self.node_operator_principals_to_remove { + Some(principals) if self.node_operators_to_remove.is_empty() => { + principals.principals.clone() + } + Some(_) => { + panic!("Cannot specify both node_operators_to_remove and node_operator_principals_to_remove"); + } + None => self + .node_operators_to_remove + .iter() + .filter_map(|bytes| PrincipalId::try_from(bytes.clone()).ok()) + .collect(), + } + } +} diff --git a/rs/registry/canister/src/mutations/node.rs b/rs/registry/canister/src/mutations/node.rs index a8c3dc8003d..1e7421e1ae1 100644 --- a/rs/registry/canister/src/mutations/node.rs +++ b/rs/registry/canister/src/mutations/node.rs @@ -8,22 +8,21 @@ use prost::Message; impl Registry { /// Get the Node record or panic on error with a message. pub fn get_node_or_panic(&self, node_id: NodeId) -> NodeRecord { - let RegistryValue { - value: node_record_vec, - version: _, - deletion_marker: _, - } = self - .get( - &make_node_record_key(node_id).into_bytes(), - self.latest_version(), - ) - .unwrap_or_else(|| { - panic!( - "{}node record for {:} not found in the registry.", - LOG_PREFIX, node_id - ) - }); + self.get_node(node_id).unwrap_or_else(|| { + panic!( + "{}node record for {:} not found in the registry.", + LOG_PREFIX, node_id + ); + }) + } + + /// Get the Node record if it exists in the Registry. + pub fn get_node(&self, node_id: NodeId) -> Option { + let reg_value: &RegistryValue = self.get( + &make_node_record_key(node_id).into_bytes(), + self.latest_version(), + )?; - NodeRecord::decode(node_record_vec.as_slice()).unwrap() + Some(NodeRecord::decode(reg_value.value.as_slice()).unwrap()) } } diff --git a/rs/registry/canister/src/mutations/node_management/common.rs b/rs/registry/canister/src/mutations/node_management/common.rs index b4abb8e3c7c..579d752ef1e 100644 --- a/rs/registry/canister/src/mutations/node_management/common.rs +++ b/rs/registry/canister/src/mutations/node_management/common.rs @@ -80,6 +80,39 @@ pub fn get_node_operator_id_for_node( ) } +pub fn get_node_provider_id_for_operator_id( + registry: &Registry, + node_operator_id: PrincipalId, +) -> Result { + let node_operator_key = make_node_operator_record_key(node_operator_id); + registry + .get(node_operator_key.as_bytes(), registry.latest_version()) + .map_or( + Err(format!( + "Node Operator Id {:} not found in the registry.", + node_operator_key + )), + |result| { + PrincipalId::try_from( + NodeOperatorRecord::decode(result.value.as_slice()) + .map_err(|_| { + format!( + "Could not decode node_operator_record for Node Operator Id {}", + node_operator_id + ) + })? + .node_provider_principal_id, + ) + .map_err(|_| { + format!( + "Could not decode node_provider_id from the Node Operator Record for the Id {}", + node_operator_id + ) + }) + }, + ) +} + pub fn get_node_operator_record( registry: &Registry, node_operator_id: PrincipalId, diff --git a/rs/registry/canister/src/mutations/node_management/do_add_node.rs b/rs/registry/canister/src/mutations/node_management/do_add_node.rs index 7f57ed9d217..b7bfeb0d5ce 100644 --- a/rs/registry/canister/src/mutations/node_management/do_add_node.rs +++ b/rs/registry/canister/src/mutations/node_management/do_add_node.rs @@ -48,7 +48,13 @@ impl Registry { let mut node_operator_record = get_node_operator_record(self, caller_id) .map_err(|err| format!("{}do_add_node: Aborting node addition: {}", LOG_PREFIX, err))?; - // 1. Clear out any nodes that already exist at this IP. + // 1. Validate keys and get the node id + let (node_id, valid_pks) = valid_keys_from_payload(&payload) + .map_err(|err| format!("{}do_add_node: {}", LOG_PREFIX, err))?; + + println!("{}do_add_node: The node id is {:?}", LOG_PREFIX, node_id); + + // 2. Clear out any nodes that already exist at this IP. // This will only succeed if: // - the same NO was in control of the original nodes. // - the nodes are no longer in subnets. @@ -57,26 +63,47 @@ impl Registry { // release dashboard.) let http_endpoint = connection_endpoint_from_string(&payload.http_endpoint); let nodes_with_same_ip = scan_for_nodes_by_ip(self, &http_endpoint.ip_addr); + let mut mutations = Vec::new(); + let num_removed_nodes = nodes_with_same_ip.len() as u64; if !nodes_with_same_ip.is_empty() { - for node_id in nodes_with_same_ip { - self.do_remove_node(RemoveNodeDirectlyPayload { node_id }, caller_id); + if nodes_with_same_ip.len() == 1 { + mutations = self.make_remove_or_replace_node_mutations( + RemoveNodeDirectlyPayload { + node_id: nodes_with_same_ip[0], + }, + caller_id, + Some(node_id), + ); + } else { + // In the unlikely situation that multiple nodes share the same IP address as the new node, + // this will remove the existing nodes. + // While the situation is unexpected, the behavior is backwards compatible. + // This may happen only if there is a bug in the registry code and the registry invariant isn't enforced, + // due to which the node id was not properly removed. + for previous_node_id in nodes_with_same_ip { + mutations.extend(self.make_remove_or_replace_node_mutations( + RemoveNodeDirectlyPayload { + node_id: previous_node_id, + }, + caller_id, + // If there are multiple nodes with the same IP, then each of them could in principle be in a (different) subnet. + // In that case replacing all different node ids with the same new node isn't an option. + // To cover for this corner case, we don't replace the node id but just remove the node and potentially fail. + None, + )); + } } - - // Update the NO record, as the available allowance may have changed. - node_operator_record = get_node_operator_record(self, caller_id).map_err(|err| { - format!("{}do_add_node: Aborting node addition: {}", LOG_PREFIX, err) - })? } - // 2. Check if adding one more node will get us over the cap for the Node Operator - if node_operator_record.node_allowance == 0 { + // 3. Check if adding one more node will get us over the cap for the Node Operator + if node_operator_record.node_allowance + num_removed_nodes == 0 { return Err(format!( "{}do_add_node: Node allowance for this Node Operator is exhausted", LOG_PREFIX )); } - // 3. Get valid type if type is in request + // 4. Get valid type if type is in request let node_reward_type = payload .node_reward_type .as_ref() @@ -91,10 +118,6 @@ impl Registry { .transpose()? .map(|node_reward_type| node_reward_type as i32); - // 4. Validate keys and get the node id - let (node_id, valid_pks) = valid_keys_from_payload(&payload) - .map_err(|err| format!("{}do_add_node: {}", LOG_PREFIX, err))?; - // 5. Validate the domain is valid let domain: Option = payload .domain @@ -127,8 +150,6 @@ impl Registry { } } - println!("{}do_add_node: The node id is {:?}", LOG_PREFIX, node_id); - // 7. Create the Node Record let node_record = NodeRecord { xnet: Some(connection_endpoint_from_string(&payload.xnet_endpoint)), @@ -142,17 +163,22 @@ impl Registry { }; // 8. Insert node, public keys, and crypto keys - let mut mutations = make_add_node_registry_mutations(node_id, node_record, valid_pks); + mutations.extend(make_add_node_registry_mutations( + node_id, + node_record, + valid_pks, + )); // 9. Update the Node Operator record - node_operator_record.node_allowance -= 1; + node_operator_record.node_allowance = + node_operator_record.node_allowance + num_removed_nodes - 1; let update_node_operator_record = make_update_node_operator_mutation(caller_id, &node_operator_record); mutations.push(update_node_operator_record); - // 10. Check invariants before applying mutations + // 10. Check invariants and then apply mutations self.maybe_apply_mutation_internal(mutations); println!("{}do_add_node finished: {:?}", LOG_PREFIX, payload); @@ -287,21 +313,23 @@ fn now() -> Result { #[cfg(test)] mod tests { - use std::str::FromStr; - - use crate::{ - common::test_helpers::invariant_compliant_registry, mutations::common::test::TEST_NODE_ID, - }; - use super::*; + use crate::common::test_helpers::{ + invariant_compliant_registry, prepare_registry_with_nodes, + registry_add_node_operator_for_node, registry_create_subnet_with_nodes, + }; + use crate::mutations::common::test::TEST_NODE_ID; + use ic_base_types::{NodeId, PrincipalId}; use ic_config::crypto::CryptoConfig; use ic_crypto_node_key_generation::generate_node_keys_once; use ic_protobuf::registry::node_operator::v1::NodeOperatorRecord; use ic_registry_canister_api::IPv4Config; use ic_registry_keys::{make_node_operator_record_key, make_node_record_key}; use ic_registry_transport::insert; + use itertools::Itertools; use lazy_static::lazy_static; use prost::Message; + use std::str::FromStr; /// Prepares the payload to add a new node, for tests. pub fn prepare_add_node_payload(mutation_id: u8) -> (AddNodePayload, ValidNodePublicKeys) { @@ -326,8 +354,8 @@ mod tests { ni_dkg_dealing_encryption_pk, transport_tls_cert, idkg_dealing_encryption_pk: Some(idkg_dealing_encryption_pk), - xnet_endpoint: format!("128.0.{mutation_id}.1:1234"), - http_endpoint: format!("128.0.{mutation_id}.1:4321"), + xnet_endpoint: format!("128.0.{mutation_id}.100:1234"), + http_endpoint: format!("128.0.{mutation_id}.100:4321"), chip_id: None, public_ipv4_config: None, domain: Some("api-example.com".to_string()), @@ -737,4 +765,143 @@ mod tests { .unwrap_err(); assert!(e.contains("do_add_node: There is already another node with the same IPv4 address")); } + + #[test] + fn should_add_node_and_replace_existing_node_in_subnet() { + // This test verifies that adding a new node replaces an existing node in a subnet + let mut registry = invariant_compliant_registry(0); + + // Add nodes to the registry + let (mutate_request, node_ids_and_dkg_pks) = prepare_registry_with_nodes(1, 6); + registry.maybe_apply_mutation_internal(mutate_request.mutations); + let node_ids: Vec = node_ids_and_dkg_pks.keys().cloned().collect(); + let node_operator_id = registry_add_node_operator_for_node(&mut registry, node_ids[0], 0); + + // Create a subnet with the first 4 nodes + let subnet_id = + registry_create_subnet_with_nodes(&mut registry, &node_ids_and_dkg_pks, &[0, 1, 2, 3]); + let subnet_record = registry.get_subnet_or_panic(subnet_id); + let subnet_membership = subnet_record + .membership + .iter() + .map(|bytes| NodeId::from(PrincipalId::try_from(bytes).unwrap())) + .collect::>(); + let expected_remove_node_id = node_ids[1]; // same offset as the subnet membership vector + let expected_remove_node = registry.get_node(subnet_membership[1]).unwrap(); + + println!( + "Original subnet membership (node ids): {:?}", + subnet_membership + ); + + // Add a new node with the same IP address and port as an existing node, which should replace the existing node + let (mut payload, _valid_pks) = prepare_add_node_payload(2); + let http = expected_remove_node.http.unwrap(); + payload + .http_endpoint + .clone_from(&format!("[{}]:{}", http.ip_addr, http.port)); + let new_node_id = registry + .do_add_node_(payload.clone(), node_operator_id) + .expect("failed to add a node"); + + // Verify the subnet record is updated with the new node + let subnet_record = registry.get_subnet_or_panic(subnet_id); + let mut expected_membership = subnet_membership.clone(); + expected_membership[1] = new_node_id; + expected_membership.sort(); + let actual_membership: Vec = subnet_record + .membership + .iter() + .map(|bytes| NodeId::from(PrincipalId::try_from(bytes).unwrap())) + .sorted() + .collect(); + assert_eq!(actual_membership, expected_membership); + + // Verify the old node is removed from the registry + assert!(registry.get_node(expected_remove_node_id).is_none()); + + // Verify the new node is present in the registry + assert!(registry.get_node(new_node_id).is_some()); + + // Verify node operator allowance is unchanged + let updated_operator = get_node_operator_record(®istry, node_operator_id).unwrap(); + assert_eq!(updated_operator.node_allowance, 0); + } + + #[test] + fn should_add_node_with_no_subnet_conflict() { + let mut registry = invariant_compliant_registry(0); + + // Add nodes to the registry + let (mutate_request, node_ids_and_dkg_pks) = prepare_registry_with_nodes(1, 4); + registry.maybe_apply_mutation_internal(mutate_request.mutations); + let node_ids: Vec = node_ids_and_dkg_pks.keys().cloned().collect(); + let node_operator_id = registry_add_node_operator_for_node(&mut registry, node_ids[0], 1); + + // Prepare payload to add a new node + let (payload, _valid_pks) = prepare_add_node_payload(2); + + // Add the new node + let new_node_id = registry + .do_add_node_(payload.clone(), node_operator_id) + .expect("failed to add a node"); + + // Verify the new node is present in the registry + assert!(registry.get_node(new_node_id).is_some()); + + // Verify node operator allowance is decremented + let updated_operator = get_node_operator_record(®istry, node_operator_id).unwrap(); + assert_eq!(updated_operator.node_allowance, 0); + + // Verify all nodes are in the registry + for node_id in node_ids { + assert!(registry.get_node(node_id).is_some()); + } + } + + #[test] + #[should_panic(expected = "Node allowance for this Node Operator is exhausted")] + fn should_panic_if_node_allowance_is_exhausted() { + let mut registry = invariant_compliant_registry(0); + + // Add nodes to the registry + let (mutate_request, node_ids_and_dkg_pks) = prepare_registry_with_nodes(1, 1); + registry.maybe_apply_mutation_internal(mutate_request.mutations); + let node_ids: Vec = node_ids_and_dkg_pks.keys().cloned().collect(); + let node_operator_id = registry_add_node_operator_for_node(&mut registry, node_ids[0], 0); + + // Prepare payload to add a new node + let (payload, _valid_pks) = prepare_add_node_payload(2); + + // Attempt to add the new node, which should panic due to exhausted allowance + registry + .do_add_node_(payload.clone(), node_operator_id) + .unwrap(); + } + + #[test] + fn should_add_node_and_update_allowance() { + let mut registry = invariant_compliant_registry(0); + + // Add nodes to the registry + let (mutate_request, node_ids_and_dkg_pks) = prepare_registry_with_nodes(1, 1); + registry.maybe_apply_mutation_internal(mutate_request.mutations); + let node_ids: Vec = node_ids_and_dkg_pks.keys().cloned().collect(); + let node_operator_id = registry_add_node_operator_for_node(&mut registry, node_ids[0], 1); + + // Prepare payload to add a new node + let (payload, _valid_pks) = prepare_add_node_payload(2); + + // Add the new node + let new_node_id = registry + .do_add_node_(payload.clone(), node_operator_id) + .expect("failed to add a node"); + + // Verify the new node is present in the registry + assert!(registry.get_node(new_node_id).is_some()); + + // Verify node operator allowance is decremented + let updated_operator = get_node_operator_record(®istry, node_operator_id).unwrap(); + assert_eq!(updated_operator.node_allowance, 0); + } } diff --git a/rs/registry/canister/src/mutations/node_management/do_remove_node_directly.rs b/rs/registry/canister/src/mutations/node_management/do_remove_node_directly.rs index 4316efe65b1..2fc11389747 100644 --- a/rs/registry/canister/src/mutations/node_management/do_remove_node_directly.rs +++ b/rs/registry/canister/src/mutations/node_management/do_remove_node_directly.rs @@ -1,7 +1,7 @@ use crate::mutations::node_management::common::{ find_subnet_for_node, get_node_operator_id_for_node, get_node_operator_record, - get_subnet_list_record, make_remove_node_registry_mutations, - make_update_node_operator_mutation, + get_node_provider_id_for_operator_id, get_subnet_list_record, + make_remove_node_registry_mutations, make_update_node_operator_mutation, }; use crate::{common::LOG_PREFIX, registry::Registry}; use candid::{CandidType, Deserialize}; @@ -9,6 +9,9 @@ use candid::{CandidType, Deserialize}; use dfn_core::println; use ic_base_types::{NodeId, PrincipalId}; use ic_registry_keys::{make_api_boundary_node_record_key, make_subnet_record_key}; +use ic_registry_transport::pb::v1::RegistryMutation; +use ic_registry_transport::upsert; +use prost::Message; impl Registry { /// Removes an existing node from the registry. @@ -20,10 +23,42 @@ impl Registry { "{}do_remove_node_directly started: {:?} caller: {:?}", LOG_PREFIX, payload, caller_id ); - self.do_remove_node(payload, caller_id); + self.do_remove_node(payload.clone(), caller_id); + + println!( + "{}do_remove_node_directly finished: {:?}", + LOG_PREFIX, payload + ); + } + + pub fn do_replace_node_with_another( + &mut self, + payload: RemoveNodeDirectlyPayload, + caller_id: PrincipalId, + new_node_id: NodeId, + ) { + let mutations = + self.make_remove_or_replace_node_mutations(payload, caller_id, Some(new_node_id)); + + // Check invariants and apply mutations + self.maybe_apply_mutation_internal(mutations); } pub fn do_remove_node(&mut self, payload: RemoveNodeDirectlyPayload, caller_id: PrincipalId) { + let mutations = self.make_remove_or_replace_node_mutations(payload, caller_id, None); + // Check invariants and apply mutations + self.maybe_apply_mutation_internal(mutations); + } + + // Prepare mutations for removing or replacing a node in the registry. + // If new_node_id is Some, the old node is in-place replaced with the new node, even if the old node is in a subnet. + // If new_node_id is None, the old node is only removed from the registry and is not allowed to be in a subnet. + pub fn make_remove_or_replace_node_mutations( + &mut self, + payload: RemoveNodeDirectlyPayload, + caller_id: PrincipalId, + new_node_id: Option, + ) -> Vec { // 1. Find the node operator id for this record // and abort if the node record is not found let node_operator_id = get_node_operator_id_for_node(self, payload.node_id) @@ -35,24 +70,58 @@ impl Registry { }) .unwrap(); - // 2. Get the caller ID and check that it matches the node's NO - assert_eq!( - node_operator_id, caller_id, - "The caller {}, does not match this Node's Operator id.", - caller_id - ); - - // 3. Ensure node is not in a subnet - let subnet_list_record = get_subnet_list_record(self); - let is_node_in_subnet = find_subnet_for_node(self, payload.node_id, &subnet_list_record); - if let Some(subnet_id) = is_node_in_subnet { - panic!("{}do_remove_node_directly: Cannot remove a node that is a member of a subnet. This node is a member of Subnet: {}", - LOG_PREFIX, - make_subnet_record_key(subnet_id) + // 2. Compare the caller_id (node operator) with the node's node operator and, if that fails, + // fall back to comparing the DC and the node provider ID for the caller and the node. + // That covers the case when the node provider added a new operator record in the same DC, and + // is trying to redeploy the nodes under the new operator. + // Hence, if the DC and the node provider of the caller and the original node operator match, + // the removal should succeed. + if caller_id != node_operator_id { + let node_operator_caller = get_node_operator_record(self, caller_id) + .map_err(|e| { + format!( + "{}do_remove_node_directly: Aborting node removal: {}", + LOG_PREFIX, e + ) + }) + .unwrap(); + let dc_caller = node_operator_caller.dc_id; + let dc_orig_node_operator = get_node_operator_record(self, node_operator_id) + .map_err(|e| { + format!( + "{}do_remove_node_directly: Aborting node removal: {}", + LOG_PREFIX, e + ) + }) + .unwrap() + .dc_id; + assert_eq!( + dc_caller, dc_orig_node_operator, + "The DC {} of the caller {}, does not match the DC of the node {}.", + dc_caller, caller_id, dc_orig_node_operator + ); + let node_provider_caller = get_node_provider_id_for_operator_id(self, caller_id) + .map_err(|e| { + format!( + "{}do_remove_node_directly: Aborting node removal: {}", + LOG_PREFIX, e + ) + }); + let node_provider_of_the_node = + get_node_provider_id_for_operator_id(self, node_operator_id).map_err(|e| { + format!( + "{}do_remove_node_directly: Aborting node removal: {}", + LOG_PREFIX, e + ) + }); + assert_eq!( + node_provider_caller, node_provider_of_the_node, + "The node provider {:?} of the caller {}, does not match the node provider {:?} of the node {}.", + node_provider_caller, caller_id, node_provider_of_the_node, payload.node_id ); } - // 4. Ensure the node is not an API Boundary Node. + // 3. Ensure the node is not an API Boundary Node. // In order to succeed, a corresponding ApiBoundaryNodeRecord should be removed first via proposal. let api_bn_id = self.get_api_boundary_node_record(payload.node_id); if api_bn_id.is_some() { @@ -63,6 +132,43 @@ impl Registry { ); } + // 4. Check if node is in a subnet, and if so, replace it in the subnet by updating the membership in the subnet record. + let subnet_list_record = get_subnet_list_record(self); + let is_node_in_subnet = find_subnet_for_node(self, payload.node_id, &subnet_list_record); + let mut mutations = vec![]; + if let Some(subnet_id) = is_node_in_subnet { + if new_node_id.is_some() { + // The node is in a subnet and is being replaced with a new node. + // Update the subnet record with the new node membership. + let mut subnet_record = self.get_subnet_or_panic(subnet_id); + + let mut subnet_membership: Vec = subnet_record + .membership + .iter() + .map(|bytes| NodeId::from(PrincipalId::try_from(bytes).unwrap())) + .collect(); + + subnet_membership.retain(|&id| id != payload.node_id); + subnet_membership.push(new_node_id.unwrap()); + + // Update the subnet record with the new membership (and double check that the new node is not in a subnet) + self.replace_subnet_record_membership( + subnet_id, + &mut subnet_record, + subnet_membership, + ); + mutations = vec![upsert( + make_subnet_record_key(subnet_id), + subnet_record.encode_to_vec(), + )]; + } else { + panic!("{}do_remove_node_directly: Cannot remove a node that is a member of a subnet. This node is a member of Subnet: {}", + LOG_PREFIX, + make_subnet_record_key(subnet_id) + ); + } + } + // 5. Retrieve the NO record and increment its node allowance by 1 let mut new_node_operator_record = get_node_operator_record(self, caller_id) .map_err(|err| { @@ -75,23 +181,17 @@ impl Registry { new_node_operator_record.node_allowance += 1; // 6. Finally, generate the following mutations: - // * Delete the node + // * Delete the node record // * Delete entries for node encryption keys // * Increment NO's allowance by 1 - let mut mutations = make_remove_node_registry_mutations(self, payload.node_id); + mutations.extend(make_remove_node_registry_mutations(self, payload.node_id)); // mutation to update node operator value mutations.push(make_update_node_operator_mutation( node_operator_id, &new_node_operator_record, )); - // 7. Apply mutations after checking invariants - self.maybe_apply_mutation_internal(mutations); - - println!( - "{}do_remove_node_directly finished: {:?}", - LOG_PREFIX, payload - ); + mutations } } @@ -103,23 +203,24 @@ pub struct RemoveNodeDirectlyPayload { #[cfg(test)] mod tests { - use std::str::FromStr; - - use ic_base_types::PrincipalId; + use super::*; + use crate::{ + common::test_helpers::{ + invariant_compliant_registry, prepare_registry_with_nodes, + prepare_registry_with_nodes_and_node_operator_id, registry_add_node_operator_for_node, + registry_create_subnet_with_nodes, + }, + mutations::common::test::TEST_NODE_ID, + }; + use ic_base_types::{NodeId, PrincipalId}; use ic_protobuf::registry::{ api_boundary_node::v1::ApiBoundaryNodeRecord, node_operator::v1::NodeOperatorRecord, }; - use ic_registry_keys::make_node_operator_record_key; + use ic_registry_keys::{make_node_operator_record_key, make_node_record_key}; use ic_registry_transport::insert; use ic_types::ReplicaVersion; use prost::Message; - - use crate::{ - common::test_helpers::{invariant_compliant_registry, prepare_registry_with_nodes}, - mutations::common::test::TEST_NODE_ID, - }; - - use super::*; + use std::str::FromStr; #[test] #[should_panic(expected = "Node Id 2vxsx-fae not found in the registry")] @@ -184,4 +285,276 @@ mod tests { registry.do_remove_node(payload, node_operator_id); } + + #[test] + #[should_panic( + expected = "assertion `left == right` failed: The node provider Ok(5yckv-7nzbm-aaaaa-aaaap-4ai) of the caller ziab2-3ora4-aaaaa-aaaap-4ai, does not match the node provider Ok(ahdmd-q5ybm-aaaaa-aaaap-4ai) of the node" + )] + fn should_panic_different_caller() { + // This test is only added for backward compatibility. + // It should be removed once all tests are updated to include operator record. + let mut registry = invariant_compliant_registry(0); + let operator1_id = PrincipalId::new_user_test_id(2000); + let operator2_id = PrincipalId::new_user_test_id(2001); + let operator_record_1 = NodeOperatorRecord { + node_operator_principal_id: operator1_id.to_vec(), + node_provider_principal_id: PrincipalId::new_user_test_id(3000).to_vec(), + dc_id: "dc1".to_string(), + node_allowance: 1, + ..Default::default() + }; + let operator_record_2 = NodeOperatorRecord { + node_operator_principal_id: operator2_id.to_vec(), + node_provider_principal_id: PrincipalId::new_user_test_id(3001).to_vec(), + dc_id: "dc1".to_string(), + node_allowance: 1, + ..Default::default() + }; + registry.maybe_apply_mutation_internal(vec![ + insert( + make_node_operator_record_key(operator1_id), + operator_record_1.encode_to_vec(), + ), + insert( + make_node_operator_record_key(operator2_id), + operator_record_2.encode_to_vec(), + ), + ]); + // Add node owned by operator1 to registry + let (mutate_request, node_ids_and_dkg_pks) = + prepare_registry_with_nodes_and_node_operator_id( + 1, /* mutation id */ + 1, /* node count */ + operator1_id, + ); + registry.maybe_apply_mutation_internal(mutate_request.mutations); + let node_id = node_ids_and_dkg_pks + .keys() + .next() + .expect("should contain at least one node ID") + .to_owned(); + + let payload = RemoveNodeDirectlyPayload { node_id }; + + registry.do_remove_node(payload, operator2_id); + } + + #[test] + fn should_succeed_remove_node_compare_dc_and_node_provider() { + let mut registry = invariant_compliant_registry(0); + // Add node operator1 and operator2 records, both under the same provider + let operator1_id = PrincipalId::new_user_test_id(2000); + let operator2_id = PrincipalId::new_user_test_id(2001); + let operator_record_1 = NodeOperatorRecord { + node_operator_principal_id: operator1_id.to_vec(), + node_provider_principal_id: PrincipalId::new_user_test_id(3000).to_vec(), + dc_id: "dc1".to_string(), + node_allowance: 1, + ..Default::default() + }; + let operator_record_2 = NodeOperatorRecord { + node_operator_principal_id: operator2_id.to_vec(), + node_provider_principal_id: PrincipalId::new_user_test_id(3000).to_vec(), + dc_id: "dc1".to_string(), + node_allowance: 1, + ..Default::default() + }; + registry.maybe_apply_mutation_internal(vec![ + insert( + make_node_operator_record_key(operator1_id), + operator_record_1.encode_to_vec(), + ), + insert( + make_node_operator_record_key(operator2_id), + operator_record_2.encode_to_vec(), + ), + ]); + // Add node owned by operator1 to registry + let (mutate_request, node_ids_and_dkg_pks) = + prepare_registry_with_nodes_and_node_operator_id( + 1, /* mutation id */ + 1, /* node count */ + operator1_id, + ); + registry.maybe_apply_mutation_internal(mutate_request.mutations); + let node_id = node_ids_and_dkg_pks + .keys() + .next() + .expect("should contain at least one node ID") + .to_owned(); + + let payload = RemoveNodeDirectlyPayload { node_id }; + + // Should succeed because both operator1 and operator2 are under the same provider + registry.do_remove_node(payload, operator2_id); + } + + #[test] + #[should_panic( + expected = "assertion `left == right` failed: The DC not-dc1 of the caller ziab2-3ora4-aaaaa-aaaap-4ai, does not match the DC of the node dc1." + )] + fn should_panic_remove_node_different_dc() { + let mut registry = invariant_compliant_registry(0); + // Add node operator1 and operator2 records, both under the same provider + let operator1_id = PrincipalId::new_user_test_id(2000); + let operator2_id = PrincipalId::new_user_test_id(2001); + let operator_record_1 = NodeOperatorRecord { + node_operator_principal_id: operator1_id.to_vec(), + node_provider_principal_id: PrincipalId::new_user_test_id(3000).to_vec(), + dc_id: "dc1".to_string(), + node_allowance: 1, + ..Default::default() + }; + let operator_record_2 = NodeOperatorRecord { + node_operator_principal_id: operator2_id.to_vec(), + node_provider_principal_id: PrincipalId::new_user_test_id(3000).to_vec(), + dc_id: "not-dc1".to_string(), + node_allowance: 1, + ..Default::default() + }; + registry.maybe_apply_mutation_internal(vec![ + insert( + make_node_operator_record_key(operator1_id), + operator_record_1.encode_to_vec(), + ), + insert( + make_node_operator_record_key(operator2_id), + operator_record_2.encode_to_vec(), + ), + ]); + // Add node owned by operator1 to registry + let (mutate_request, node_ids_and_dkg_pks) = + prepare_registry_with_nodes_and_node_operator_id( + 1, /* mutation id */ + 1, /* node count */ + operator1_id, + ); + registry.maybe_apply_mutation_internal(mutate_request.mutations); + let node_id = node_ids_and_dkg_pks + .keys() + .next() + .expect("should contain at least one node ID") + .to_owned(); + + let payload = RemoveNodeDirectlyPayload { node_id }; + + // Should fail because the DC of operator1 and operator2 does not match + registry.do_remove_node(payload, operator2_id); + } + #[test] + fn should_replace_node_in_subnet() { + let mut registry = invariant_compliant_registry(0); + + // Add nodes to the registry + let (mutate_request, node_ids_and_dkg_pks) = prepare_registry_with_nodes(1, 2); + registry.maybe_apply_mutation_internal(mutate_request.mutations); + let node_ids = node_ids_and_dkg_pks.keys().cloned().collect::>(); + let node_operator_id = registry_add_node_operator_for_node(&mut registry, node_ids[0], 0); + + // Create a subnet with the first node + let subnet_id = + registry_create_subnet_with_nodes(&mut registry, &node_ids_and_dkg_pks, &[0]); + + // Replace the node_ids[0] with node_ids[1], while node_ids[0] is in a subnet + let payload = RemoveNodeDirectlyPayload { + node_id: node_ids[0], + }; + + registry.do_replace_node_with_another(payload, node_operator_id, node_ids[1]); + + // Verify the subnet record is updated with the new node + let expected_membership: Vec = vec![node_ids[1]]; + let actual_membership: Vec = registry + .get_subnet_or_panic(subnet_id) + .membership + .iter() + .map(|bytes| NodeId::from(PrincipalId::try_from(bytes).unwrap())) + .collect(); + assert_eq!(actual_membership, expected_membership); + + // Verify the old node is removed from the registry + assert!(registry + .get( + make_node_record_key(node_ids[0]).as_bytes(), + registry.latest_version() + ) + .is_none()); + + // Verify the new node is present in the registry + assert!(registry.get_node(node_ids[1]).is_some()); + + // Verify node operator allowance increased by 1 + let updated_operator = get_node_operator_record(®istry, node_operator_id).unwrap(); + assert_eq!(updated_operator.node_allowance, 1); + } + + #[test] + #[should_panic(expected = "Cannot remove a node that is a member of a subnet")] + fn should_panic_if_removing_node_in_subnet_without_replacement() { + let mut registry = invariant_compliant_registry(0); + + // Add nodes to the registry + let (mutate_request, node_ids_and_dkg_pks) = prepare_registry_with_nodes(1, 1); + registry.maybe_apply_mutation_internal(mutate_request.mutations); + let node_ids: Vec = node_ids_and_dkg_pks.keys().cloned().collect(); + let node_operator_id = registry_add_node_operator_for_node(&mut registry, node_ids[0], 0); + + // Create a subnet with the first node + let _subnet_id = + registry_create_subnet_with_nodes(&mut registry, &node_ids_and_dkg_pks, &[0]); + + // Attempt to remove the node without replacement + let payload = RemoveNodeDirectlyPayload { + node_id: node_ids[0], + }; + + registry.do_remove_node(payload, node_operator_id); + } + + #[test] + fn should_replace_node_in_subnet_and_update_allowance() { + let mut registry = invariant_compliant_registry(0); + + // Add nodes to the registry + let (mutate_request, node_ids_and_dkg_pks) = prepare_registry_with_nodes(1, 2); + registry.maybe_apply_mutation_internal(mutate_request.mutations); + let node_ids = node_ids_and_dkg_pks.keys().cloned().collect::>(); + let node_operator_id = registry_add_node_operator_for_node(&mut registry, node_ids[0], 0); + + // Create a subnet with the first node + let subnet_id = + registry_create_subnet_with_nodes(&mut registry, &node_ids_and_dkg_pks, &[0]); + + // Replace the first node with the second node in the subnet + let payload = RemoveNodeDirectlyPayload { + node_id: node_ids[0], + }; + + registry.do_replace_node_with_another(payload, node_operator_id, node_ids[1]); + + // Verify the subnet record is updated with the new node + let expected_membership: Vec = vec![node_ids[1]]; + let actual_membership: Vec = registry + .get_subnet_or_panic(subnet_id) + .membership + .iter() + .map(|bytes| NodeId::from(PrincipalId::try_from(bytes).unwrap())) + .collect(); + assert_eq!(actual_membership, expected_membership); + + // Verify the old node is removed from the registry + assert!(registry + .get( + make_node_record_key(node_ids[0]).as_bytes(), + registry.latest_version() + ) + .is_none()); + + // Verify the new node is present in the registry + assert!(registry.get_node(node_ids[1]).is_some()); + + // Verify node operator allowance increased by 1 + let updated_operator = get_node_operator_record(®istry, node_operator_id).unwrap(); + assert_eq!(updated_operator.node_allowance, 1); + } } diff --git a/rs/registry/canister/src/mutations/node_management/do_remove_nodes.rs b/rs/registry/canister/src/mutations/node_management/do_remove_nodes.rs index eea3217ec4e..847c1457577 100644 --- a/rs/registry/canister/src/mutations/node_management/do_remove_nodes.rs +++ b/rs/registry/canister/src/mutations/node_management/do_remove_nodes.rs @@ -33,14 +33,21 @@ impl Registry { // 3. Loop through each node let mutations = nodes_to_be_removed .into_iter().flat_map(|node_to_remove| { + // 4. Skip nodes that are not in the registry. + // This tackles the race condition where a node is removed from the registry + // by another transaction before this transaction is processed. + if self.get_node(node_to_remove).is_none() { + println!("{}do_remove_nodes: node {} not found in registry, skipping", LOG_PREFIX, node_to_remove); + return vec![]; + }; - // 4. Find the node operator id for this record + // 5. Find the node operator id for this record // and abort if the node record is not found let node_operator_id = get_node_operator_id_for_node(self, node_to_remove) .map_err(|e| format!("{}do_remove_nodes: Aborting node removal: {}", LOG_PREFIX, e)) .unwrap(); - // 5. Ensure node is not in a subnet + // 6. Ensure node is not in a subnet let is_node_in_subnet = find_subnet_for_node(self, node_to_remove, &subnet_list_record); if let Some(subnet_id) = is_node_in_subnet { panic!("{}do_remove_nodes: Cannot remove a node that is a member of a subnet. This node is a member of Subnet: {}", @@ -49,7 +56,7 @@ impl Registry { ); } - // 6. Retrieve the NO record and increment its node allowance by 1 + // 7. Retrieve the NO record and increment its node allowance by 1 let mut new_node_operator_record = get_node_operator_record(self, node_operator_id) .map_err(|err| { format!( @@ -70,7 +77,7 @@ impl Registry { }; node_operator_hmap.insert(node_operator_id.to_string(), new_node_operator_record.node_allowance); - // 7. Finally, generate the following mutations: + // 8. Finally, generate the following mutations: // * Delete the node // * Delete entries for node encryption keys // * Increment NO's allowance by 1 @@ -97,3 +104,174 @@ pub struct RemoveNodesPayload { /// The list of Node IDs that will be removed pub node_ids: Vec, } + +#[cfg(test)] +mod tests { + use super::*; + use crate::common::test_helpers::{invariant_compliant_registry, prepare_registry_with_nodes}; + use ic_base_types::PrincipalId; + use ic_protobuf::registry::node_operator::v1::NodeOperatorRecord; + use ic_registry_keys::{make_node_operator_record_key, make_node_record_key}; + use ic_registry_transport::insert; + use prost::Message; + + #[test] + fn test_remove_nonexistent_node() { + let mut registry = invariant_compliant_registry(0); + + let nonexistent_node_id = NodeId::from(PrincipalId::new_user_test_id(999)); + let payload = RemoveNodesPayload { + node_ids: vec![nonexistent_node_id], + }; + + // Should not panic, just skip the nonexistent node + registry.do_remove_nodes(payload); + } + + #[test] + fn test_remove_single_node() { + let mut registry = invariant_compliant_registry(0); + + // Add a node to the registry + let (mutate_request, node_ids) = prepare_registry_with_nodes(1, 1); + registry.maybe_apply_mutation_internal(mutate_request.mutations); + + let node_id = *node_ids.keys().next().unwrap(); + let node_operator_id = + PrincipalId::try_from(registry.get_node_or_panic(node_id).node_operator_id).unwrap(); + + // Allow this node operator to onboard 1 more node; the initial value is not important in this test. + // We just want to later see that node_allowance gets incremented by `do_remove_nodes`. + let initial_allowance = 1; + let node_operator_record = NodeOperatorRecord { + node_allowance: initial_allowance, + ..Default::default() + }; + + registry.maybe_apply_mutation_internal(vec![insert( + make_node_operator_record_key(node_operator_id), + node_operator_record.encode_to_vec(), + )]); + + // Remove the node + let payload = RemoveNodesPayload { + node_ids: vec![node_id], + }; + registry.do_remove_nodes(payload); + // Verify node is removed + assert!(registry + .get( + make_node_record_key(node_id).as_bytes(), + registry.latest_version() + ) + .is_none()); + + // Verify node operator allowance was incremented + let updated_operator = get_node_operator_record(®istry, node_operator_id).unwrap(); + assert_eq!(updated_operator.node_allowance, initial_allowance + 1); + } + #[test] + fn test_remove_multiple_nodes_same_operator() { + let mut registry = invariant_compliant_registry(0); + + // Add multiple nodes to the registry + let (mutate_request, node_ids) = prepare_registry_with_nodes(1, 3); + registry.maybe_apply_mutation_internal(mutate_request.mutations); + + let node_ids: Vec = node_ids.keys().cloned().collect(); + let node_operator_id = + PrincipalId::try_from(registry.get_node_or_panic(node_ids[0]).node_operator_id) + .unwrap(); + + // Add node operator record + let initial_allowance = 0; + let node_operator_record = NodeOperatorRecord { + node_allowance: initial_allowance, + ..Default::default() + }; + + registry.maybe_apply_mutation_internal(vec![insert( + make_node_operator_record_key(node_operator_id), + node_operator_record.encode_to_vec(), + )]); + + // Remove two nodes + let payload = RemoveNodesPayload { + node_ids: node_ids[..2].to_vec(), + }; + + registry.do_remove_nodes(payload); + + // Verify the two nodes are removed + for node_id in &node_ids[..2] { + assert!(registry + .get( + make_node_record_key(*node_id).as_bytes(), + registry.latest_version() + ) + .is_none()); + } + + // Verify the third node is still present + assert!(registry.get_node(node_ids[2]).is_some()); + + // Verify node operator allowance was incremented by 2 + let updated_operator = get_node_operator_record(®istry, node_operator_id).unwrap(); + assert_eq!(updated_operator.node_allowance, initial_allowance + 2); + } + + #[test] + fn test_remove_duplicate_and_nonexistent_node_ids() { + let mut registry = invariant_compliant_registry(0); + + // Add a node to the registry + let (mutate_request, node_ids) = prepare_registry_with_nodes(1, 1); + registry.maybe_apply_mutation_internal(mutate_request.mutations); + + let node_id = node_ids.keys().next().unwrap().to_owned(); + let node_operator_id = + PrincipalId::try_from(registry.get_node_or_panic(node_id).node_operator_id).unwrap(); + + // Add node operator record + let initial_allowance = 0; + let node_operator_record = NodeOperatorRecord { + node_allowance: initial_allowance, + ..Default::default() + }; + + registry.maybe_apply_mutation_internal(vec![insert( + make_node_operator_record_key(node_operator_id), + node_operator_record.encode_to_vec(), + )]); + + // Try to remove the same node multiple times + let payload = RemoveNodesPayload { + node_ids: vec![ + node_id, + NodeId::from(PrincipalId::new_node_test_id(111)), + node_id, + NodeId::from(PrincipalId::new_node_test_id(222)), + node_id, + ], + }; + + registry.do_remove_nodes(payload); + + // Verify node is removed + assert!(registry + .get( + make_node_record_key(node_id).as_bytes(), + registry.latest_version() + ) + .is_none()); + + // Verify other node_ids are still in the registry + for other_node_id in node_ids.keys().skip(1) { + assert!(registry.get_node(*other_node_id).is_some()); + } + + // Verify node operator allowance was incremented only once + let updated_operator = get_node_operator_record(®istry, node_operator_id).unwrap(); + assert_eq!(updated_operator.node_allowance, initial_allowance + 1); + } +} diff --git a/rs/registry/canister/src/registry.rs b/rs/registry/canister/src/registry.rs index 0e46bd710ac..68469e72463 100644 --- a/rs/registry/canister/src/registry.rs +++ b/rs/registry/canister/src/registry.rs @@ -286,6 +286,11 @@ impl Registry { self.apply_mutations(mutations); } + #[cfg(test)] + pub fn apply_mutations_for_test(&mut self, mutations: Vec) { + self.apply_mutations(mutations); + } + /// Checks that invariants would hold after applying the mutations pub(crate) fn verify_mutations_internal(&self, mutations: &Vec) { let errors = self.verify_mutation_type(mutations.as_slice()); diff --git a/rs/registry/canister/src/registry_lifecycle.rs b/rs/registry/canister/src/registry_lifecycle.rs index 9801a1edfe9..741e32d3d82 100644 --- a/rs/registry/canister/src/registry_lifecycle.rs +++ b/rs/registry/canister/src/registry_lifecycle.rs @@ -1,7 +1,14 @@ use crate::{ - certification::recertify_registry, pb::v1::RegistryCanisterStableStorage, registry::Registry, + certification::recertify_registry, missing_node_types_map::MISSING_NODE_TYPES_MAP, + mutations::node_management::common::get_key_family, pb::v1::RegistryCanisterStableStorage, + registry::Registry, }; +use ic_base_types::{NodeId, PrincipalId}; +use ic_protobuf::registry::node::v1::{NodeRecord, NodeRewardType}; +use ic_registry_keys::{make_node_record_key, NODE_RECORD_KEY_PREFIX}; +use ic_registry_transport::{pb::v1::RegistryMutation, update}; use prost::Message; +use std::str::FromStr; pub fn canister_post_upgrade(registry: &mut Registry, stable_storage: &[u8]) { // Purposefully fail the upgrade if we can't find authz information. @@ -16,18 +23,18 @@ pub fn canister_post_upgrade(registry: &mut Registry, stable_storage: &[u8]) { ); // Registry data migrations should be implemented as follows: - // let mutation_batches_due_to_data_migrations = { - // let mutations = registry.compute_mutations_from_my_data_migration(); - // if mutations.is_empty() { - // 0 // No mutations required for this data migration. - // } else { - // registry.maybe_apply_mutation_internal(mutations); - // 1 // Single batch of mutations due to this data migration. - // } - // }; - // + let mutation_batches_due_to_data_migrations = { + let mutations = add_missing_node_types_to_nodes(registry); + if mutations.is_empty() { + 0 // No mutations required for this data migration. + } else { + registry.maybe_apply_mutation_internal(mutations); + 1 // Single batch of mutations due to this data migration. + } + }; + // When there are no migrations, `mutation_batches_due_to_data_migrations` should be set to `0`. - let mutation_batches_due_to_data_migrations = 0; + // let mutation_batches_due_to_data_migrations = 0; registry.check_global_state_invariants(&[]); // Registry::from_serializable_from guarantees this always passes in this function @@ -53,6 +60,34 @@ pub fn canister_post_upgrade(registry: &mut Registry, stable_storage: &[u8]) { } } +fn add_missing_node_types_to_nodes(registry: &Registry) -> Vec { + let missing_node_types_map = &MISSING_NODE_TYPES_MAP; + + let mut mutations = Vec::new(); + + for (id, record) in get_key_family::(registry, NODE_RECORD_KEY_PREFIX).into_iter() { + if record.node_reward_type.is_none() { + let reward_type = missing_node_types_map + .get(id.as_str()) + .map(|t| NodeRewardType::from(t.to_string())); + + if let Some(reward_type) = reward_type { + if reward_type != NodeRewardType::Unspecified { + let mut record = record; + record.node_reward_type = Some(reward_type as i32); + let node_id = NodeId::from(PrincipalId::from_str(&id).unwrap()); + mutations.push(update( + make_node_record_key(node_id), + record.encode_to_vec(), + )); + } + } + } + } + + mutations +} + #[cfg(test)] mod test { use super::*; @@ -61,6 +96,9 @@ mod test { registry::{EncodedVersion, Version}, registry_lifecycle::Registry, }; + use ic_base_types::{NodeId, PrincipalId}; + use ic_registry_keys::make_node_record_key; + use ic_registry_transport::insert; fn stable_storage_from_registry( registry: &Registry, @@ -167,4 +205,52 @@ mod test { let mut new_registry = Registry::new(); canister_post_upgrade(&mut new_registry, &stable_storage); } + + #[test] + fn test_migration_works_correctly() { + use std::str::FromStr; + let mut registry = invariant_compliant_registry(0); + + let mut node_additions = Vec::new(); + for (id, _) in MISSING_NODE_TYPES_MAP.iter() { + let record = NodeRecord { + xnet: None, + http: None, + node_operator_id: PrincipalId::new_anonymous().to_vec(), + chip_id: None, + hostos_version_id: None, + public_ipv4_config: None, + domain: None, + node_reward_type: None, + }; + + node_additions.push(insert( + make_node_record_key(NodeId::new(PrincipalId::from_str(id).unwrap())), + record.encode_to_vec(), + )); + } + + let nodes_expected = node_additions.len(); + assert_eq!(nodes_expected, 1418); + + registry.apply_mutations_for_test(node_additions); + + let mutations = add_missing_node_types_to_nodes(®istry); + assert_eq!(mutations.len(), nodes_expected); + + registry.apply_mutations_for_test(mutations); + + for (id, reward_type) in MISSING_NODE_TYPES_MAP.iter() { + let record = + registry.get_node_or_panic(NodeId::from(PrincipalId::from_str(id).unwrap())); + + let expected_reward_type = NodeRewardType::from(reward_type.clone()); + assert_eq!( + record.node_reward_type, + Some(expected_reward_type as i32), + "Assertion for Node {} failed", + id + ); + } + } } diff --git a/rs/registry/canister/tests/remove_node_operators.rs b/rs/registry/canister/tests/remove_node_operators.rs index 0ee9a9b8332..2436731b350 100644 --- a/rs/registry/canister/tests/remove_node_operators.rs +++ b/rs/registry/canister/tests/remove_node_operators.rs @@ -8,8 +8,10 @@ use ic_nns_test_utils::{ }, registry::invariant_compliant_mutation_as_atomic_req, }; -use ic_protobuf::registry::node_operator::v1::RemoveNodeOperatorsPayload; -use registry_canister::init::RegistryCanisterInitPayloadBuilder; +use registry_canister::{ + init::RegistryCanisterInitPayloadBuilder, + mutations::do_remove_node_operators::RemoveNodeOperatorsPayload, +}; #[test] fn test_the_anonymous_user_cannot_remove_node_operators() { @@ -22,9 +24,7 @@ fn test_the_anonymous_user_cannot_remove_node_operators() { ) .await; - let payload = RemoveNodeOperatorsPayload { - node_operators_to_remove: vec![], - }; + let payload = RemoveNodeOperatorsPayload::new(vec![]); // The anonymous end-user tries to remove node operators, bypassing // the Governance canister. This should be rejected. @@ -72,9 +72,7 @@ fn test_a_canister_other_than_the_governance_canister_cannot_remove_node_operato ) .await; - let payload = RemoveNodeOperatorsPayload { - node_operators_to_remove: vec![], - }; + let payload = RemoveNodeOperatorsPayload::new(vec![]); // The attacker canister tries to remove node operators, pretending // to be the Governance canister. This should have no effect. diff --git a/rs/registry/canister/tests/remove_nodes.rs b/rs/registry/canister/tests/remove_nodes.rs index 2c220dbdad9..b3fe119debf 100644 --- a/rs/registry/canister/tests/remove_nodes.rs +++ b/rs/registry/canister/tests/remove_nodes.rs @@ -352,7 +352,7 @@ fn remove_nodes_removes_all_keys() { // Add the node along with keys and certs let mut mutations = make_add_node_registry_mutations(node_id, node, valid_pks); - // Add node operator records + // Add node operator record mutations.push(insert( make_node_operator_record_key(user_test_id(NO_ID).get()).as_bytes(), node_operator.encode_to_vec(), diff --git a/rs/registry/canister/unreleased_changelog.md b/rs/registry/canister/unreleased_changelog.md new file mode 100644 index 00000000000..a84eaed0fd0 --- /dev/null +++ b/rs/registry/canister/unreleased_changelog.md @@ -0,0 +1,27 @@ +# How This File Is Used + +In general, upcoming/unreleased behavior changes are described here. For details +on the process that this file is part of, see +`rs/nervous_system/changelog_process.md`. + + +# Next Upgrade Proposal + +## Added + +## Changed + +## Deprecated + +## Removed + +## Fixed + +### Backfill node_reward_type for existing nodes + +A one-time migration to fill in the `node_reward_type` field for existing nodes was added. Previously, there was no +on-chain connection between the specific nodes and their reward types. This data came from off-chain sources +at DFINITY. In the future, the `node_reward_type` will be used to determine the reward type for each node, and +it will be a required field for node registration in the IC. + +## Security diff --git a/rs/registry/canister_data_provider/BUILD.bazel b/rs/registry/canister_data_provider/BUILD.bazel new file mode 100644 index 00000000000..2a1e3428680 --- /dev/null +++ b/rs/registry/canister_data_provider/BUILD.bazel @@ -0,0 +1,43 @@ +load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test") + +package(default_visibility = ["//visibility:public"]) + +DEPENDENCIES = [ + # Keep sorted. + "//rs/interfaces/registry", + "//rs/nns/common", + "//rs/nns/constants", + "//rs/registry/transport", + "//rs/types/types", + "@crate_index//:anyhow", + "@crate_index//:candid", + "@crate_index//:ic-cdk", + "@crate_index//:ic-stable-structures", + "@crate_index//:itertools", +] + +MACRO_DEPENDENCIES = [] + +DEV_DEPENDENCIES = [] + +MACRO_DEV_DEPENDENCIES = [] + +ALIASES = {} + +rust_library( + name = "ic_registry_canister_data_provider", + srcs = glob(["src/**/*.rs"]), + aliases = ALIASES, + crate_name = "ic_registry_canister_data_provider", + proc_macro_deps = MACRO_DEPENDENCIES, + version = "0.9.0", + deps = DEPENDENCIES, +) + +rust_test( + name = "ic_registry_canister_data_provider_test", + aliases = ALIASES, + crate = ":ic_registry_canister_data_provider", + proc_macro_deps = MACRO_DEPENDENCIES + MACRO_DEV_DEPENDENCIES, + deps = DEPENDENCIES + DEV_DEPENDENCIES, +) diff --git a/rs/registry/canister_data_provider/Cargo.toml b/rs/registry/canister_data_provider/Cargo.toml new file mode 100644 index 00000000000..f52ee3d14d0 --- /dev/null +++ b/rs/registry/canister_data_provider/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "ic-registry-canister-data-provider" +version.workspace = true +authors.workspace = true +description.workspace = true +documentation.workspace = true +edition.workspace = true + +[dependencies] +ic-interfaces-registry = { path = "../../interfaces/registry" } +ic-registry-transport = { path = "../transport" } +ic-types = { path = "../../types/types" } +ic-stable-structures = { workspace = true } +ic-nns-constants = { path = "../../nns/constants" } +itertools = { workspace = true } +ic-nns-common = { path = "../../nns/common" } +anyhow = { workspace = true } +ic-cdk = { workspace = true } +candid = { workspace = true } +ic-registry-canister-client = { path = "../canister-client" } diff --git a/rs/registry/canister_data_provider/src/lib.rs b/rs/registry/canister_data_provider/src/lib.rs new file mode 100644 index 00000000000..ac2b05f3584 --- /dev/null +++ b/rs/registry/canister_data_provider/src/lib.rs @@ -0,0 +1,231 @@ +use candid::Principal; +use ic_interfaces_registry::{ + RegistryDataProvider, RegistryTransportRecord, ZERO_REGISTRY_VERSION, +}; +use ic_nns_constants::REGISTRY_CANISTER_ID; +use ic_registry_transport::pb::v1::RegistryDelta; +use ic_registry_transport::{ + deserialize_get_changes_since_response, serialize_get_changes_since_request, +}; +use ic_stable_structures::memory_manager::VirtualMemory; +use ic_stable_structures::storable::Bound; +use ic_stable_structures::{DefaultMemoryImpl, StableBTreeMap, Storable}; +use ic_types::registry::RegistryDataProviderError; +use ic_types::RegistryVersion; +use itertools::Itertools; +use std::borrow::Cow; +use std::cmp::Ordering; +use std::collections::HashSet; +use std::marker::PhantomData; + +pub struct StorableRegistryValue(Option>); + +impl Storable for StorableRegistryValue { + fn to_bytes(&self) -> Cow<'_, [u8]> { + self.0.to_bytes() + } + + fn from_bytes(bytes: Cow<'_, [u8]>) -> Self { + Self(Option::from_bytes(bytes)) + } + + const BOUND: Bound = Bound::Unbounded; +} + +#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Default)] +pub struct StorableRegistryKey { + version: u64, + key: String, +} + +// This value is set as 2 times the max key size present in the registry +const MAX_REGISTRY_KEY_SIZE: u32 = 200; + +impl Storable for StorableRegistryKey { + fn to_bytes(&self) -> Cow<[u8]> { + let mut storable_key = vec![]; + let version_b = self.version.to_be_bytes().to_vec(); + let key_b = self.key.as_bytes().to_vec(); + + storable_key.extend_from_slice(&version_b); + storable_key.extend_from_slice(&key_b); + + Cow::Owned(storable_key) + } + + fn from_bytes(bytes: Cow<[u8]>) -> Self { + let bytes = bytes.as_ref(); + let (version_bytes, key_bytes) = bytes.split_at(8); + let version = u64::from_be_bytes(version_bytes.try_into().expect("Invalid version bytes")); + let key = String::from_utf8(key_bytes.to_vec()).expect("Invalid UTF-8 in key"); + + Self { version, key } + } + const BOUND: Bound = Bound::Bounded { + max_size: MAX_REGISTRY_KEY_SIZE + size_of::() as u32, + is_fixed_size: false, + }; +} + +type Memory = VirtualMemory; + +pub trait StableMemoryBorrower: Send + Sync { + fn with_borrow( + f: impl FnOnce(&StableBTreeMap) -> R, + ) -> R; + fn with_borrow_mut( + f: impl FnOnce(&mut StableBTreeMap) -> R, + ) -> R; +} + +/// This registry data provider is designed to work with the `ic-registry-canister-client` +/// in canisters, enabling the retrieval and storage of a registry copy in stable memory. +/// +/// - If `keys_to_keep` is `Some(keys)`, only the specified `keys` will be stored in stable memory, +/// while all other keys from the registry will be discarded. +/// - If `keys_to_keep` is `None`, all keys from the registry will be retained in stable memory. +pub struct CanisterDataProvider { + keys_to_keep: Option>, + _store: PhantomData, +} + +impl CanisterDataProvider { + pub fn new(keys_to_retain: Option>) -> Self { + Self { + keys_to_keep: keys_to_retain, + _store: PhantomData, + } + } + + async fn get_registry_changes_since(&self, version: u64) -> anyhow::Result> { + let buff = serialize_get_changes_since_request(version)?; + let response = ic_cdk::api::call::call_raw( + Principal::from(REGISTRY_CANISTER_ID), + "get_changes_since", + buff, + 0, + ) + .await + .map_err(|(code, msg)| (code as i32, msg)) + .unwrap(); + let (registry_delta, _) = deserialize_get_changes_since_response(response)?; + Ok(registry_delta) + } + + fn get_latest_version(&self) -> Option { + S::with_borrow(|local_registry| local_registry.last_key_value().map(|(k, _)| k.version)) + } + + fn add_deltas(&self, deltas: Vec) -> anyhow::Result<()> { + for delta in deltas { + let string_key = std::str::from_utf8(&delta.key[..]) + .map_err(|_| anyhow::anyhow!("Failed to convert key {:?} to string", delta.key))?; + + if let Some(keys) = &self.keys_to_keep { + if keys.iter().all(|prefix| !string_key.starts_with(prefix)) { + continue; + } + } + + for value in delta.values.into_iter() { + S::with_borrow_mut(|local_registry| { + local_registry.insert( + StorableRegistryKey { + key: string_key.to_string(), + version: value.version, + }, + StorableRegistryValue(if value.deletion_marker { + None + } else { + Some(value.value) + }), + ); + }) + } + } + Ok(()) + } + + // This function can be called in a timer to periodically update the registry stored + pub async fn sync_registry_stored(&self) -> anyhow::Result<()> { + let mut update_registry_version = self + .get_latest_version() + .unwrap_or(ZERO_REGISTRY_VERSION.get()); + + loop { + let remote_latest_version = ic_nns_common::registry::get_latest_version().await; + + ic_cdk::println!( + "local version: {} remote version: {}", + update_registry_version, + remote_latest_version + ); + + match update_registry_version.cmp(&remote_latest_version) { + Ordering::Less => { + ic_cdk::println!( + "Registry version local {} < remote {}", + update_registry_version, + remote_latest_version + ); + } + Ordering::Equal => { + ic_cdk::println!( + "Local Registry version {} is up to date", + update_registry_version + ); + break; + } + Ordering::Greater => { + let message = format!( + "Registry version local {} > remote {}, this should never happen", + update_registry_version, remote_latest_version + ); + + ic_cdk::trap(message.as_str()); + } + } + + if let Ok(deltas) = self + .get_registry_changes_since(update_registry_version) + .await + { + update_registry_version = deltas + .iter() + .flat_map(|delta| delta.values.iter().map(|v| v.version)) + .max() + .unwrap_or(update_registry_version); + self.add_deltas(deltas)?; + }; + } + Ok(()) + } +} + +impl RegistryDataProvider for CanisterDataProvider { + fn get_updates_since( + &self, + version: RegistryVersion, + ) -> Result, RegistryDataProviderError> { + S::with_borrow(|local_registry| { + let start_key = StorableRegistryKey { + version: version.get(), + ..Default::default() + }; + + let from_start_key = local_registry + .range(start_key..) + .map(|(storable_key, value)| RegistryTransportRecord { + version: RegistryVersion::from(storable_key.version), + key: storable_key.key, + value: value.0, + }) + .collect_vec(); + + Ok(from_start_key) + }) + } +} + +#[cfg(test)] +mod tests; diff --git a/rs/registry/canister_data_provider/src/main.rs b/rs/registry/canister_data_provider/src/main.rs new file mode 100644 index 00000000000..ef2b1d0d58e --- /dev/null +++ b/rs/registry/canister_data_provider/src/main.rs @@ -0,0 +1,70 @@ +use ic_interfaces_registry::RegistryClient; +use ic_registry_canister_client::CanisterRegistryClient; +use ic_registry_canister_data_provider::{ + CanisterDataProvider, StableMemoryBorrower, StorableRegistryKey, StorableRegistryValue, +}; +use ic_stable_structures::memory_manager::{MemoryId, MemoryManager, VirtualMemory}; +use ic_stable_structures::{DefaultMemoryImpl, StableBTreeMap}; +use std::cell::RefCell; + +use ic_cdk::spawn; +use std::sync::Arc; + +type Memory = VirtualMemory; +thread_local! { + /// Memory manager instance for handling stable memory allocation + pub static MEMORY_MANAGER: RefCell> = + RefCell::new(MemoryManager::init(DefaultMemoryImpl::default())); + + /// Stores registry data with typed keys and values in canister stable memory + pub static REGISTRY: RefCell> = + RefCell::new(StableBTreeMap::init( + MEMORY_MANAGER.with(|m| m.borrow().get(MemoryId::new(0))) + )); +} + +/// Implements StableMemoryBorrower for mutable and immutable stable memory access to the registry store +#[derive(Default)] +pub struct StableMemoryStore; +impl StableMemoryBorrower for StableMemoryStore { + fn with_borrow( + f: impl FnOnce( + &StableBTreeMap< + StorableRegistryKey, + StorableRegistryValue, + VirtualMemory, + >, + ) -> R, + ) -> R { + REGISTRY.with_borrow(|registry_stored| f(registry_stored)) + } + fn with_borrow_mut( + f: impl FnOnce( + &mut StableBTreeMap< + StorableRegistryKey, + StorableRegistryValue, + VirtualMemory, + >, + ) -> R, + ) -> R { + REGISTRY.with_borrow_mut(|registry_stored| f(registry_stored)) + } +} + +fn main() { + let local_registry: Arc> = + Arc::new(CanisterDataProvider::new(Default::default())); + let registry_client: CanisterRegistryClient = + CanisterRegistryClient::new(local_registry.clone()); + + spawn(async move { + // Update local registry from remote registry canister storing it in stable memory + local_registry.sync_registry_stored().await.unwrap(); + + // Update the cache + registry_client.update_to_latest_version(); + + // Example fetch latest version of the registry + let _latest_registry_version = registry_client.get_latest_version(); + }); +} diff --git a/rs/registry/canister_data_provider/src/tests.rs b/rs/registry/canister_data_provider/src/tests.rs new file mode 100644 index 00000000000..d9972382b84 --- /dev/null +++ b/rs/registry/canister_data_provider/src/tests.rs @@ -0,0 +1,111 @@ +use super::*; +use ic_stable_structures::memory_manager::{MemoryId, MemoryManager}; +use std::cell::RefCell; + +thread_local! { + static MEMORY_MANAGER: RefCell> = + RefCell::new(MemoryManager::init(DefaultMemoryImpl::default())); + + static DUMMY_REGISTRY: RefCell>> = + RefCell::new(StableBTreeMap::init( + MEMORY_MANAGER.with(|m| m.borrow().get(MemoryId::new(0))) + )); + +} + +#[derive(Default)] +pub struct DummyStore; +impl StableMemoryBorrower for DummyStore { + fn with_borrow( + f: impl FnOnce(&StableBTreeMap) -> R, + ) -> R { + DUMMY_REGISTRY.with_borrow(|registry_stored| f(registry_stored)) + } + + fn with_borrow_mut( + f: impl FnOnce(&mut StableBTreeMap) -> R, + ) -> R { + DUMMY_REGISTRY.with_borrow_mut(|registry_stored| f(registry_stored)) + } +} + +fn generate_deltas(num: usize) -> Vec { + (1..=num) + .map(|i| RegistryDelta { + key: format!("test_key{}", i).into_bytes(), + values: vec![ic_registry_transport::pb::v1::RegistryValue { + version: i as u64, + value: format!("value{}", i).into_bytes(), + deletion_marker: false, + }], + }) + .collect() +} + +#[test] +fn test_add_deltas_correctly() { + let provider = CanisterDataProvider::::new(None); + let deltas = generate_deltas(10); + + provider.add_deltas(deltas).unwrap(); + let len = DUMMY_REGISTRY.with_borrow(|registry_stored| registry_stored.len()); + + assert_eq!(len, 10); + // Test `get_updates_since` + let updates_since = provider + .get_updates_since(RegistryVersion::from(5)) + .unwrap(); + + // Verify that only updates with version >= 5 are returned + assert_eq!(updates_since.len(), 6); // 5 through 10 + + for (i, update) in updates_since.iter().enumerate() { + let expected_version = (5 + i) as u64; + + assert_eq!(update.version.get(), expected_version); + assert_eq!(update.key, format!("test_key{}", expected_version)); + assert_eq!( + update.value, + Some(format!("value{}", expected_version).into_bytes()) + ); + } +} + +#[test] +fn test_add_deltas_with_keys_to_retain() { + let keys_to_retain = HashSet::from(["test_key1".to_string(), "test_key3".to_string()]); + let provider = CanisterDataProvider::::new(Some(keys_to_retain)); + let deltas = self::generate_deltas(5); + + provider.add_deltas(deltas).unwrap(); + + let updates_since = provider + .get_updates_since(RegistryVersion::from(0)) + .unwrap(); + + assert_eq!(updates_since.len(), 2); + assert_eq!(updates_since[0].version.get(), 1); + assert_eq!(updates_since[0].key.as_str(), "test_key1"); + assert_eq!( + updates_since[0].value, + Some("value1".to_string().into_bytes()) + ); + + assert_eq!(updates_since[1].version.get(), 3); + assert_eq!(updates_since[1].key.as_str(), "test_key3"); + assert_eq!( + updates_since[1].value, + Some("value3".to_string().into_bytes()) + ); + + let updates_since = provider + .get_updates_since(RegistryVersion::from(2)) + .unwrap(); + + assert_eq!(updates_since[0].version.get(), 3); + assert_eq!(updates_since[0].key.as_str(), "test_key3"); + assert_eq!( + updates_since[0].value, + Some("value3".to_string().into_bytes()) + ); +} diff --git a/rs/registry/nns_data_provider/src/certification.rs b/rs/registry/nns_data_provider/src/certification.rs index 79df4bd3b0e..efd31d9325f 100644 --- a/rs/registry/nns_data_provider/src/certification.rs +++ b/rs/registry/nns_data_provider/src/certification.rs @@ -231,7 +231,7 @@ where struct ProtobufVisitor(PhantomData); - impl<'de, T: prost::Message + Default> serde::de::Visitor<'de> for ProtobufVisitor { + impl serde::de::Visitor<'_> for ProtobufVisitor { type Value = Protobuf; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/rs/registry/regedit/src/tests.rs b/rs/registry/regedit/src/tests.rs index 13cdf4177a4..ec26e50c733 100644 --- a/rs/registry/regedit/src/tests.rs +++ b/rs/registry/regedit/src/tests.rs @@ -135,6 +135,7 @@ pub fn run_ic_prep() -> (TempDir, IcPrepStateDir) { node_operator_principal_id: None, secret_key_store: None, domain: None, + node_reward_type: None, }, ); diff --git a/rs/replay/BUILD.bazel b/rs/replay/BUILD.bazel index 3e1ac174640..0f0c57c7d8c 100644 --- a/rs/replay/BUILD.bazel +++ b/rs/replay/BUILD.bazel @@ -7,6 +7,7 @@ DEPENDENCIES = [ "//rs/canister_sandbox:backend_lib", "//rs/config", "//rs/consensus", + "//rs/consensus/dkg", "//rs/consensus/utils", "//rs/crypto", "//rs/crypto/for_verification_only", diff --git a/rs/replay/Cargo.toml b/rs/replay/Cargo.toml index 4397dbfaac0..fbadf17d59c 100644 --- a/rs/replay/Cargo.toml +++ b/rs/replay/Cargo.toml @@ -15,6 +15,7 @@ ic-canister-client = { path = "../canister_client" } ic-canister-sandbox-backend-lib = { path = "../canister_sandbox" } ic-config = { path = "../config" } ic-consensus = { path = "../consensus" } +ic-consensus-dkg = { path = "../consensus/dkg" } ic-consensus-utils = { path = "../consensus/utils" } ic-crypto-for-verification-only = { path = "../crypto/for_verification_only" } ic-cycles-account-manager = { path = "../cycles_account_manager" } diff --git a/rs/replay/src/backup.rs b/rs/replay/src/backup.rs index e06414ec149..b97a4996fbd 100644 --- a/rs/replay/src/backup.rs +++ b/rs/replay/src/backup.rs @@ -4,7 +4,7 @@ use crate::{ }; use ic_artifact_pool::consensus_pool::ConsensusPoolImpl; use ic_config::artifact_pool::BACKUP_GROUP_SIZE; -use ic_consensus::consensus::dkg_key_manager::DkgKeyManager; +use ic_consensus_dkg::DkgKeyManager; use ic_consensus_utils::pool_reader::PoolReader; use ic_crypto_for_verification_only::CryptoComponentForVerificationOnly; use ic_interfaces::{ diff --git a/rs/replay/src/lib.rs b/rs/replay/src/lib.rs index f4d2b04668a..ed78f2e69d5 100644 --- a/rs/replay/src/lib.rs +++ b/rs/replay/src/lib.rs @@ -240,10 +240,12 @@ fn cmd_get_recovery_cup( let low_threshold_transcript = summary .dkg .current_transcript(&NiDkgTag::LowThreshold) + .expect("No current low threshold transcript available") .clone(); let high_threshold_transcript = summary .dkg .current_transcript(&NiDkgTag::HighThreshold) + .expect("No current high threshold transcript available") .clone(); let initial_ni_dkg_transcript_low_threshold = Some(InitialNiDkgTranscriptRecord::from(low_threshold_transcript)); @@ -266,7 +268,7 @@ fn cmd_get_recovery_cup( chain_key_initializations: vec![], }; - let cup = ic_consensus::dkg::make_registry_cup_from_cup_contents( + let cup = ic_consensus::make_registry_cup_from_cup_contents( &*player.registry, player.subnet_id, cup_contents, diff --git a/rs/replay/src/player.rs b/rs/replay/src/player.rs index 5898e9be313..1c4bbf774d0 100644 --- a/rs/replay/src/player.rs +++ b/rs/replay/src/player.rs @@ -313,6 +313,7 @@ impl Player { Arc::clone(&state_manager) as Arc<_>, state_manager.get_fd_factory(), completed_execution_messages_tx, + &state_manager.state_layout().tmp(), ); let message_routing = Arc::new(MessageRoutingImpl::new( state_manager.clone(), diff --git a/rs/replay/src/validator.rs b/rs/replay/src/validator.rs index ddef2da4e5d..d370b302b31 100644 --- a/rs/replay/src/validator.rs +++ b/rs/replay/src/validator.rs @@ -8,8 +8,9 @@ use ic_artifact_pool::{consensus_pool::ConsensusPoolImpl, dkg_pool::DkgPoolImpl} use ic_config::{artifact_pool::ArtifactPoolConfig, Config}; use ic_consensus::{ certification::CertificationCrypto, - consensus::{dkg_key_manager::DkgKeyManager, validator::Validator, ValidatorMetrics}, + consensus::{validator::Validator, ValidatorMetrics}, }; +use ic_consensus_dkg::DkgKeyManager; use ic_consensus_utils::{ active_high_threshold_nidkg_id, crypto::ConsensusCrypto, membership::Membership, pool_reader::PoolReader, registry_version_at_height, diff --git a/rs/replica/Cargo.toml b/rs/replica/Cargo.toml index 727fdb68426..e1253355c52 100644 --- a/rs/replica/Cargo.toml +++ b/rs/replica/Cargo.toml @@ -15,6 +15,7 @@ ic-btc-adapter-client = { path = "../bitcoin/client" } ic-btc-consensus = { path = "../bitcoin/consensus" } ic-config = { path = "../config" } ic-consensus = { path = "../consensus" } +ic-consensus-dkg = { path = "../consensus/dkg" } ic-crypto = { path = "../crypto" } ic-crypto-sha2 = { path = "../crypto/sha2" } ic-cycles-account-manager = { path = "../cycles_account_manager" } diff --git a/rs/replica/setup_ic_network/BUILD.bazel b/rs/replica/setup_ic_network/BUILD.bazel index e65ff55b14d..bba68f83a82 100644 --- a/rs/replica/setup_ic_network/BUILD.bazel +++ b/rs/replica/setup_ic_network/BUILD.bazel @@ -6,6 +6,7 @@ DEPENDENCIES = [ # Keep sorted. "//rs/artifact_pool", "//rs/config", + "//rs/consensus/dkg", "//rs/consensus/utils", "//rs/crypto/interfaces/sig_verification", "//rs/crypto/tls_interfaces", diff --git a/rs/replica/setup_ic_network/Cargo.toml b/rs/replica/setup_ic_network/Cargo.toml index 9305f10cd2f..a9e464849a7 100644 --- a/rs/replica/setup_ic_network/Cargo.toml +++ b/rs/replica/setup_ic_network/Cargo.toml @@ -12,6 +12,7 @@ ic-artifact-manager = { path = "../../p2p/artifact_manager" } ic-artifact-pool = { path = "../../artifact_pool" } ic-config = { path = "../../config" } ic-consensus = { path = "../../consensus" } +ic-consensus-dkg = { path = "../../consensus/dkg" } ic-consensus-manager = { path = "../../p2p/consensus_manager" } ic-consensus-utils = { path = "../../consensus/utils" } ic-crypto-interfaces-sig-verification = { path = "../../crypto/interfaces/sig_verification" } diff --git a/rs/replica/setup_ic_network/src/lib.rs b/rs/replica/setup_ic_network/src/lib.rs index 832bc7c5741..36873775268 100644 --- a/rs/replica/setup_ic_network/src/lib.rs +++ b/rs/replica/setup_ic_network/src/lib.rs @@ -12,10 +12,11 @@ use ic_artifact_pool::{ use ic_config::{artifact_pool::ArtifactPoolConfig, transport::TransportConfig}; use ic_consensus::{ certification::{CertificationCrypto, CertifierBouncer, CertifierImpl}, - consensus::{dkg_key_manager::DkgKeyManager, ConsensusBouncer, ConsensusImpl}, - dkg, idkg, + consensus::{ConsensusBouncer, ConsensusImpl}, + idkg, }; -use ic_consensus_manager::ConsensusManagerBuilder; +use ic_consensus_dkg::DkgBouncer; +use ic_consensus_manager::{AbortableBroadcastChannel, AbortableBroadcastChannelBuilder}; use ic_consensus_utils::{crypto::ConsensusCrypto, pool_reader::PoolReader}; use ic_crypto_interfaces_sig_verification::IngressSigVerifier; use ic_crypto_tls_interfaces::TlsConfig; @@ -27,10 +28,10 @@ use ic_https_outcalls_consensus::{ use ic_ingress_manager::{bouncer::IngressBouncer, IngressManager, RandomStateKind}; use ic_interfaces::{ batch_payload::BatchPayloadBuilder, + consensus_pool::{ConsensusBlockCache, ConsensusPoolCache}, execution_environment::IngressHistoryReader, messaging::{MessageRouting, XNetPayloadBuilder}, - p2p::artifact_manager::JoinGuard, - p2p::state_sync::StateSyncClient, + p2p::{artifact_manager::JoinGuard, state_sync::StateSyncClient}, self_validating_payload::SelfValidatingPayloadBuilder, time_source::{SysTimeSource, TimeSource}, }; @@ -45,8 +46,11 @@ use ic_replicated_state::ReplicatedState; use ic_state_manager::state_sync::types::StateSyncMessage; use ic_types::{ artifact::UnvalidatedArtifactMutation, - canister_http::{CanisterHttpRequest, CanisterHttpResponse}, - consensus::{CatchUpPackage, HasHeight}, + canister_http::{CanisterHttpRequest, CanisterHttpResponse, CanisterHttpResponseShare}, + consensus::{ + certification::CertificationMessage, dkg, idkg::IDkgMessage, CatchUpPackage, + ConsensusMessage, HasHeight, + }, malicious_flags::MaliciousFlags, messages::SignedIngress, replica_config::ReplicaConfig, @@ -57,7 +61,10 @@ use std::{ str::FromStr, sync::{Arc, Mutex, RwLock}, }; -use tokio::sync::{mpsc::UnboundedSender, watch}; +use tokio::sync::{ + mpsc::{unbounded_channel, UnboundedSender}, + watch, +}; use tower_http::trace::TraceLayer; /// [IC-1718]: Whether the `hashes-in-blocks` feature is enabled. If the flag is set to `true`, we @@ -65,21 +72,255 @@ use tower_http::trace::TraceLayer; /// we will reconstruct the blocks by looking up the referenced ingress messages in the ingress /// pool or, if they are not there, by fetching missing ingress messages from peers who are /// advertising the blocks. -const HASHES_IN_BLOCKS_FEATURE_ENABLED: bool = true; +const HASHES_IN_BLOCKS_FEATURE_ENABLED: bool = false; -pub const MAX_ADVERT_BUFFER: usize = 100_000; /// This limit is used to protect against a malicious peer advertising many ingress messages. /// If no malicious peers are present the ingress pools are bounded by a separate limit. const SLOT_TABLE_LIMIT_INGRESS: usize = 50_000; const SLOT_TABLE_NO_LIMIT: usize = usize::MAX; -/// The collection of all artifact pools. +/// Artifact pools excluding the consensus one. struct ArtifactPools { ingress_pool: Arc>, certification_pool: Arc>, dkg_pool: Arc>, idkg_pool: Arc>, - canister_http_pool: Arc>, + https_outcalls_pool: Arc>, +} + +impl ArtifactPools { + fn new( + log: &ReplicaLogger, + metrics_registry: &MetricsRegistry, + node_id: NodeId, + config: ArtifactPoolConfig, + catch_up_package: &CatchUpPackage, + ) -> Self { + let ingress_pool = Arc::new(RwLock::new(IngressPoolImpl::new( + node_id, + config.clone(), + metrics_registry.clone(), + log.clone(), + ))); + + let mut idkg_pool = IDkgPoolImpl::new( + config.clone(), + log.clone(), + metrics_registry.clone(), + Box::new(idkg::IDkgStatsImpl::new(metrics_registry.clone())), + ); + idkg_pool.add_initial_dealings(catch_up_package); + let idkg_pool = Arc::new(RwLock::new(idkg_pool)); + + let certification_pool = Arc::new(RwLock::new(CertificationPoolImpl::new( + node_id, + config, + log.clone(), + metrics_registry.clone(), + ))); + let dkg_pool = Arc::new(RwLock::new(DkgPoolImpl::new( + metrics_registry.clone(), + log.clone(), + ))); + let https_outcalls_pool = Arc::new(RwLock::new(CanisterHttpPoolImpl::new( + metrics_registry.clone(), + log.clone(), + ))); + Self { + ingress_pool, + certification_pool, + dkg_pool, + idkg_pool, + https_outcalls_pool, + } + } +} + +struct Bouncers { + ingress: Arc, + consensus: Arc, + certifier: Arc, + dkg: Arc, + idkg: Arc, + https_outcalls: Arc, +} + +impl Bouncers { + fn new( + log: &ReplicaLogger, + metrics_registry: &MetricsRegistry, + subnet_id: SubnetId, + time_source: Arc, + message_router: Arc, + consensus_pool_cache: Arc, + consensus_block_cache: Arc, + state_reader: Arc>, + ) -> Self { + let ingress = Arc::new(IngressBouncer::new(time_source.clone())); + let consensus = Arc::new(ConsensusBouncer::new(metrics_registry, message_router)); + let dkg = Arc::new(DkgBouncer::new(metrics_registry)); + let certifier = Arc::new(CertifierBouncer::new( + metrics_registry, + consensus_pool_cache.clone(), + )); + let idkg = Arc::new(idkg::IDkgBouncer::new( + metrics_registry, + subnet_id, + consensus_block_cache, + state_reader.clone(), + )); + + let https_outcalls = Arc::new(CanisterHttpGossipImpl::new( + consensus_pool_cache.clone(), + state_reader.clone(), + log.clone(), + )); + + Self { + ingress, + consensus, + dkg, + idkg, + certifier, + https_outcalls, + } + } +} + +struct AbortableBroadcastChannels { + ingress: AbortableBroadcastChannel, + consensus: AbortableBroadcastChannel, + certifier: AbortableBroadcastChannel, + dkg: AbortableBroadcastChannel, + idkg: AbortableBroadcastChannel, + https_outcalls: AbortableBroadcastChannel, +} + +impl AbortableBroadcastChannels { + fn new( + log: &ReplicaLogger, + metrics_registry: &MetricsRegistry, + rt_handle: &tokio::runtime::Handle, + node_id: NodeId, + subnet_id: SubnetId, + state_reader: Arc>, + message_router: Arc, + consensus_pool: Arc>, + time_source: Arc, + artifact_pools: &ArtifactPools, + ) -> (Self, AbortableBroadcastChannelBuilder) { + let consensus_pool_cache = consensus_pool.read().unwrap().get_cache(); + let consensus_block_cache = consensus_pool.read().unwrap().get_block_cache(); + let bouncers = Bouncers::new( + log, + metrics_registry, + subnet_id, + time_source.clone(), + message_router.clone(), + consensus_pool_cache.clone(), + consensus_block_cache, + state_reader.clone(), + ); + + let mut new_p2p_consensus: ic_consensus_manager::AbortableBroadcastChannelBuilder = + ic_consensus_manager::AbortableBroadcastChannelBuilder::new( + log.clone(), + rt_handle.clone(), + metrics_registry.clone(), + ); + + let consensus = if HASHES_IN_BLOCKS_FEATURE_ENABLED { + let assembler = ic_artifact_downloader::FetchStrippedConsensusArtifact::new( + log.clone(), + rt_handle.clone(), + consensus_pool.clone(), + artifact_pools.ingress_pool.clone(), + bouncers.consensus, + metrics_registry.clone(), + node_id, + ); + new_p2p_consensus.abortable_broadcast_channel(assembler, SLOT_TABLE_NO_LIMIT) + } else { + let assembler = ic_artifact_downloader::FetchArtifact::new( + log.clone(), + rt_handle.clone(), + consensus_pool.clone(), + bouncers.consensus, + metrics_registry.clone(), + ); + new_p2p_consensus.abortable_broadcast_channel(assembler, SLOT_TABLE_NO_LIMIT) + }; + + let ingress = { + #[allow(clippy::disallowed_methods)] + let assembler = ic_artifact_downloader::FetchArtifact::new( + log.clone(), + rt_handle.clone(), + artifact_pools.ingress_pool.clone(), + bouncers.ingress, + metrics_registry.clone(), + ); + new_p2p_consensus.abortable_broadcast_channel(assembler, SLOT_TABLE_LIMIT_INGRESS) + }; + + let certifier = { + let assembler = ic_artifact_downloader::FetchArtifact::new( + log.clone(), + rt_handle.clone(), + artifact_pools.certification_pool.clone(), + bouncers.certifier, + metrics_registry.clone(), + ); + new_p2p_consensus.abortable_broadcast_channel(assembler, SLOT_TABLE_NO_LIMIT) + }; + + let dkg = { + let assembler = ic_artifact_downloader::FetchArtifact::new( + log.clone(), + rt_handle.clone(), + artifact_pools.dkg_pool.clone(), + bouncers.dkg, + metrics_registry.clone(), + ); + new_p2p_consensus.abortable_broadcast_channel(assembler, SLOT_TABLE_NO_LIMIT) + }; + + let idkg = { + let assembler = ic_artifact_downloader::FetchArtifact::new( + log.clone(), + rt_handle.clone(), + artifact_pools.idkg_pool.clone(), + bouncers.idkg, + metrics_registry.clone(), + ); + + new_p2p_consensus.abortable_broadcast_channel(assembler, SLOT_TABLE_NO_LIMIT) + }; + + let https_outcalls = { + let assembler = ic_artifact_downloader::FetchArtifact::new( + log.clone(), + rt_handle.clone(), + artifact_pools.https_outcalls_pool.clone(), + bouncers.https_outcalls, + metrics_registry.clone(), + ); + + new_p2p_consensus.abortable_broadcast_channel(assembler, SLOT_TABLE_NO_LIMIT) + }; + + ( + Self { + ingress, + consensus, + certifier, + dkg, + idkg, + https_outcalls, + }, + new_p2p_consensus, + ) + } } pub type CanisterHttpAdapterClient = @@ -104,10 +345,10 @@ pub fn setup_consensus_and_p2p( subnet_id: SubnetId, tls_config: Arc, state_manager: Arc>, + state_sync_client: Arc>, state_reader: Arc>, consensus_pool: Arc>, catch_up_package: CatchUpPackage, - state_sync_client: Arc>, xnet_payload_builder: Arc, self_validating_payload_builder: Arc, query_stats_payload_builder: Box, @@ -126,47 +367,46 @@ pub fn setup_consensus_and_p2p( UnboundedSender>, Vec>, ) { + let time_source = Arc::new(SysTimeSource::new()); let consensus_pool_cache = consensus_pool.read().unwrap().get_cache(); + let artifact_pools = ArtifactPools::new( + log, + metrics_registry, + node_id, + artifact_pool_config, + &catch_up_package, + ); - let (ingress_pool, ingress_sender, join_handles, mut p2p_consensus) = start_consensus( + // Start the IO components of the IC protocol (a.k.a. P2P). + // P2P components includes the quic transport and anything that needs to register a handler within transport (including their dependencies). + let (channels, p2p_builder) = AbortableBroadcastChannels::new( log, metrics_registry, rt_handle, node_id, subnet_id, - artifact_pool_config, - catch_up_package, - Arc::clone(&consensus_crypto) as Arc<_>, - Arc::clone(&certifier_crypto) as Arc<_>, - Arc::clone(&ingress_sig_crypto) as Arc<_>, - Arc::clone(®istry_client), - state_manager, - state_reader, - xnet_payload_builder, - self_validating_payload_builder, - query_stats_payload_builder, - message_router, - ingress_history_reader, + state_reader.clone(), + message_router.clone(), consensus_pool.clone(), - malicious_flags, - cycles_account_manager, - registry_poll_delay_duration_ms, - canister_http_adapter_client, - max_certified_height_tx, + time_source.clone(), + &artifact_pools, ); - // StateSync receive side => handler definition - let (state_sync_router, state_sync_manager_rx) = ic_state_sync_manager::build_axum_router( - state_sync_client.clone(), - log.clone(), - metrics_registry, - ); + // Consensus receive side + handler definition + let consensus_manager_router = p2p_builder.router(); - // Consensus receive side => handler definition + // StateSync receive side + handler definition + let (state_sync_manager_router, state_sync_manager_runner) = + ic_state_sync_manager::build_state_sync_manager( + log, + metrics_registry, + rt_handle, + state_sync_client.clone(), + ); // Merge all receive side handlers => router - let p2p_router = state_sync_router - .merge(p2p_consensus.router()) + let p2p_router = state_sync_manager_router + .merge(consensus_manager_router) .layer(TraceLayer::new_for_http()); // Quic transport let (_, topology_watcher) = ic_peer_manager::start_peer_manager( @@ -197,32 +437,48 @@ pub fn setup_consensus_and_p2p( )); // Start the main event loops for StateSync and Consensus + let _abortable_broadcast_manager = p2p_builder.start(quic_transport.clone(), topology_watcher); + let _state_sync_manager = state_sync_manager_runner.start(quic_transport.clone()); - let _state_sync_manager = ic_state_sync_manager::start_state_sync_manager( + // End of IO/P2P stack initialization. + start_consensus( log, metrics_registry, - rt_handle, - quic_transport.clone(), - state_sync_client, - state_sync_manager_rx, - ); - - let _cancellation_token = p2p_consensus.run(quic_transport, topology_watcher); - - (ingress_pool, ingress_sender, join_handles) + node_id, + subnet_id, + artifact_pools, + channels, + Arc::clone(&consensus_crypto) as Arc<_>, + Arc::clone(&certifier_crypto) as Arc<_>, + Arc::clone(&ingress_sig_crypto) as Arc<_>, + Arc::clone(®istry_client), + state_manager, + state_reader, + xnet_payload_builder, + self_validating_payload_builder, + query_stats_payload_builder, + message_router, + ingress_history_reader, + consensus_pool.clone(), + malicious_flags, + cycles_account_manager, + registry_poll_delay_duration_ms, + canister_http_adapter_client, + max_certified_height_tx, + time_source, + ) } -/// The function creates the Consensus stack (including all Consensus clients) -/// and starts the artifact manager event loop for each client. +/// The function creates the consensus protocols and the event loops that drive them forward. +/// The event loops are written in SANS-IO style (https://www.firezone.dev/blog/sans-io, ) #[allow(clippy::too_many_arguments, clippy::type_complexity)] fn start_consensus( log: &ReplicaLogger, metrics_registry: &MetricsRegistry, - rt_handle: &tokio::runtime::Handle, node_id: NodeId, subnet_id: SubnetId, - artifact_pool_config: ArtifactPoolConfig, - catch_up_package: CatchUpPackage, + artifact_pools: ArtifactPools, + abortable_broadcast_channels: AbortableBroadcastChannels, // ConsensusCrypto is an extension of the Crypto trait and we can // not downcast traits. consensus_crypto: Arc, @@ -242,34 +498,15 @@ fn start_consensus( registry_poll_delay_duration_ms: u64, canister_http_adapter_client: CanisterHttpAdapterClient, max_certified_height_tx: watch::Sender, + time_source: Arc, ) -> ( Arc>, UnboundedSender>, Vec>, - ConsensusManagerBuilder, ) { - let time_source = Arc::new(SysTimeSource::new()); - let mut new_p2p_consensus: ic_consensus_manager::ConsensusManagerBuilder = - ic_consensus_manager::ConsensusManagerBuilder::new( - log.clone(), - rt_handle.clone(), - metrics_registry.clone(), - ); - - let artifact_pools = init_artifact_pools( - node_id, - artifact_pool_config, - metrics_registry, - log, - catch_up_package, - time_source.as_ref(), - ); - - let mut join_handles = vec![]; - let consensus_pool_cache = consensus_pool.read().unwrap().get_cache(); let consensus_time = consensus_pool.read().unwrap().get_consensus_time(); - let replica_config = ReplicaConfig { node_id, subnet_id }; + // --------------- PAYLOAD BUILDERS WITH ARTIFACT POOLS FOLLOW --------------------------------- let ingress_manager = Arc::new(IngressManager::new( time_source.clone(), consensus_time, @@ -287,8 +524,8 @@ fn start_consensus( RandomStateKind::Random, )); - let canister_http_payload_builder = Arc::new(CanisterHttpPayloadBuilderImpl::new( - artifact_pools.canister_http_pool.clone(), + let https_outcalls_payload_builder = Arc::new(CanisterHttpPayloadBuilderImpl::new( + artifact_pools.https_outcalls_pool.clone(), consensus_pool_cache.clone(), consensus_crypto.clone(), state_reader.clone(), @@ -297,307 +534,135 @@ fn start_consensus( metrics_registry, log.clone(), )); + // ------------------------------------------------------------------------ - let dkg_key_manager = Arc::new(Mutex::new(DkgKeyManager::new( + let replica_config = ReplicaConfig { node_id, subnet_id }; + let dkg_key_manager = Arc::new(Mutex::new(ic_consensus_dkg::DkgKeyManager::new( metrics_registry.clone(), Arc::clone(&consensus_crypto), log.clone(), &PoolReader::new(&*consensus_pool.read().unwrap()), ))); - let (consensus_tx, consensus_rx) = tokio::sync::mpsc::channel(MAX_ADVERT_BUFFER); - let (certification_tx, certification_rx) = tokio::sync::mpsc::channel(MAX_ADVERT_BUFFER); - let (dkg_tx, dkg_rx) = tokio::sync::mpsc::channel(MAX_ADVERT_BUFFER); - let (ingress_tx, ingress_rx) = tokio::sync::mpsc::channel(MAX_ADVERT_BUFFER); - let (idkg_tx, idkg_rx) = tokio::sync::mpsc::channel(MAX_ADVERT_BUFFER); - let (http_outcalls_tx, http_outcalls_rx) = tokio::sync::mpsc::channel(MAX_ADVERT_BUFFER); - - { - let consensus_impl = ConsensusImpl::new( - replica_config.clone(), - Arc::clone(®istry_client), - consensus_pool_cache.clone(), - Arc::clone(&consensus_crypto), - Arc::clone(&ingress_manager) as Arc<_>, - xnet_payload_builder, - self_validating_payload_builder, - canister_http_payload_builder, - Arc::from(query_stats_payload_builder), - Arc::clone(&artifact_pools.dkg_pool) as Arc<_>, - Arc::clone(&artifact_pools.idkg_pool) as Arc<_>, - Arc::clone(&dkg_key_manager) as Arc<_>, - message_router.clone(), - Arc::clone(&state_manager) as Arc<_>, - Arc::clone(&time_source) as Arc<_>, - registry_poll_delay_duration_ms, - malicious_flags.clone(), - metrics_registry.clone(), - log.clone(), - ); - - let consensus_pool = Arc::clone(&consensus_pool); - - // Create the consensus client. - let (client, jh) = create_artifact_handler( - consensus_tx, - consensus_impl, - time_source.clone(), - consensus_pool.clone(), - metrics_registry.clone(), - ); - - join_handles.push(jh); - - let bouncer = Arc::new(ConsensusBouncer::new(metrics_registry, message_router)); - if HASHES_IN_BLOCKS_FEATURE_ENABLED { - let assembler = ic_artifact_downloader::FetchStrippedConsensusArtifact::new( - log.clone(), - rt_handle.clone(), - consensus_pool, - artifact_pools.ingress_pool.clone(), - bouncer, - metrics_registry.clone(), - node_id, - ); - new_p2p_consensus.add_client(consensus_rx, client, assembler, SLOT_TABLE_NO_LIMIT); - } else { - let assembler = ic_artifact_downloader::FetchArtifact::new( - log.clone(), - rt_handle.clone(), - consensus_pool, - bouncer, - metrics_registry.clone(), - ); - new_p2p_consensus.add_client(consensus_rx, client, assembler, SLOT_TABLE_NO_LIMIT); - }; - }; - - let ingress_sender = { - // Create the ingress client. - let (client, jh) = create_ingress_handlers( - ingress_tx, - Arc::clone(&time_source) as Arc<_>, - Arc::clone(&artifact_pools.ingress_pool), - ingress_manager, - metrics_registry.clone(), - ); - - join_handles.push(jh); - - let bouncer = Arc::new(IngressBouncer::new(time_source.clone())); - let assembler = ic_artifact_downloader::FetchArtifact::new( - log.clone(), - rt_handle.clone(), - artifact_pools.ingress_pool.clone(), - bouncer, - metrics_registry.clone(), - ); - - new_p2p_consensus.add_client( - ingress_rx, - client.clone(), - assembler, - SLOT_TABLE_LIMIT_INGRESS, - ); - client - }; - - { - let certifier = CertifierImpl::new( - replica_config, - Arc::clone(®istry_client), - Arc::clone(&certifier_crypto), - Arc::clone(&state_manager) as Arc<_>, - Arc::clone(&consensus_pool_cache) as Arc<_>, - metrics_registry.clone(), - log.clone(), - max_certified_height_tx, - ); + let mut join_handles = vec![]; - // Create the certification client. - let (client, jh) = create_artifact_handler( - certification_tx, - certifier, - Arc::clone(&time_source) as Arc<_>, - Arc::clone(&artifact_pools.certification_pool), - metrics_registry.clone(), - ); - join_handles.push(jh); + let consensus_impl = ConsensusImpl::new( + replica_config.clone(), + Arc::clone(®istry_client), + consensus_pool_cache.clone(), + Arc::clone(&consensus_crypto), + Arc::clone(&ingress_manager) as Arc<_>, + xnet_payload_builder, + self_validating_payload_builder, + https_outcalls_payload_builder, + Arc::from(query_stats_payload_builder), + Arc::clone(&artifact_pools.dkg_pool) as Arc<_>, + Arc::clone(&artifact_pools.idkg_pool) as Arc<_>, + Arc::clone(&dkg_key_manager) as Arc<_>, + message_router.clone(), + Arc::clone(&state_manager) as Arc<_>, + Arc::clone(&time_source) as Arc<_>, + registry_poll_delay_duration_ms, + malicious_flags.clone(), + metrics_registry.clone(), + log.clone(), + ); + // Create the consensus client. + join_handles.push(create_artifact_handler( + abortable_broadcast_channels.consensus, + consensus_impl, + time_source.clone(), + consensus_pool.clone(), + metrics_registry.clone(), + )); + #[allow(clippy::disallowed_methods)] + let (user_ingress_tx, user_ingress_rx) = unbounded_channel(); + join_handles.push(create_ingress_handlers( + abortable_broadcast_channels.ingress, + user_ingress_rx, + Arc::clone(&time_source) as Arc<_>, + Arc::clone(&artifact_pools.ingress_pool), + ingress_manager, + metrics_registry.clone(), + )); - let bouncer = CertifierBouncer::new(metrics_registry, Arc::clone(&consensus_pool_cache)); - let assembler = ic_artifact_downloader::FetchArtifact::new( - log.clone(), - rt_handle.clone(), - artifact_pools.certification_pool, - Arc::new(bouncer), - metrics_registry.clone(), - ); - new_p2p_consensus.add_client(certification_rx, client, assembler, SLOT_TABLE_NO_LIMIT); - }; - - { - // Create the DKG client. - let (client, jh) = create_artifact_handler( - dkg_tx, - dkg::DkgImpl::new( - node_id, - Arc::clone(&consensus_crypto), - Arc::clone(&consensus_pool_cache), - dkg_key_manager, - metrics_registry.clone(), - log.clone(), - ), - Arc::clone(&time_source) as Arc<_>, - Arc::clone(&artifact_pools.dkg_pool), + // Create the certification client. + let certifier = CertifierImpl::new( + replica_config, + Arc::clone(®istry_client), + Arc::clone(&certifier_crypto), + Arc::clone(&state_manager) as Arc<_>, + Arc::clone(&consensus_pool_cache) as Arc<_>, + metrics_registry.clone(), + log.clone(), + max_certified_height_tx, + ); + join_handles.push(create_artifact_handler( + abortable_broadcast_channels.certifier, + certifier, + Arc::clone(&time_source) as Arc<_>, + artifact_pools.certification_pool, + metrics_registry.clone(), + )); + // Create the DKG client. + join_handles.push(create_artifact_handler( + abortable_broadcast_channels.dkg, + ic_consensus_dkg::DkgImpl::new( + node_id, + Arc::clone(&consensus_crypto), + Arc::clone(&consensus_pool_cache), + dkg_key_manager, metrics_registry.clone(), - ); - join_handles.push(jh); - - let bouncer = Arc::new(dkg::DkgBouncer::new(metrics_registry)); - let assembler = ic_artifact_downloader::FetchArtifact::new( log.clone(), - rt_handle.clone(), - artifact_pools.dkg_pool, - bouncer, - metrics_registry.clone(), - ); - new_p2p_consensus.add_client(dkg_rx, client, assembler, SLOT_TABLE_NO_LIMIT); - }; - - { - let finalized = consensus_pool_cache.finalized_block(); - let chain_key_config = - registry_client.get_chain_key_config(subnet_id, registry_client.get_latest_version()); - info!( - log, - "IDKG: finalized_height = {:?}, chain_key_config = {:?}, \ + ), + Arc::clone(&time_source) as Arc<_>, + artifact_pools.dkg_pool, + metrics_registry.clone(), + )); + let finalized = consensus_pool_cache.finalized_block(); + let chain_key_config = + registry_client.get_chain_key_config(subnet_id, registry_client.get_latest_version()); + info!( + log, + "IDKG: finalized_height = {:?}, chain_key_config = {:?}, \ DKG interval start = {:?}, is_summary = {}, has_idkg_payload = {}", - finalized.height(), - chain_key_config, - finalized.payload.as_ref().dkg_interval_start_height(), - finalized.payload.as_ref().is_summary(), - finalized.payload.as_ref().as_idkg().is_some(), - ); - - let (client, jh) = create_artifact_handler( - idkg_tx, - idkg::IDkgImpl::new( - node_id, - consensus_pool.read().unwrap().get_block_cache(), - Arc::clone(&consensus_crypto), - Arc::clone(&state_reader), - metrics_registry.clone(), - log.clone(), - malicious_flags, - ), - Arc::clone(&time_source) as Arc<_>, - Arc::clone(&artifact_pools.idkg_pool), - metrics_registry.clone(), - ); - - join_handles.push(jh); - - let bouncer = Arc::new(idkg::IDkgBouncer::new( - metrics_registry, - subnet_id, + finalized.height(), + chain_key_config, + finalized.payload.as_ref().dkg_interval_start_height(), + finalized.payload.as_ref().is_summary(), + finalized.payload.as_ref().as_idkg().is_some(), + ); + join_handles.push(create_artifact_handler( + abortable_broadcast_channels.idkg, + idkg::IDkgImpl::new( + node_id, consensus_pool.read().unwrap().get_block_cache(), + Arc::clone(&consensus_crypto), Arc::clone(&state_reader), - )); - let assembler = ic_artifact_downloader::FetchArtifact::new( - log.clone(), - rt_handle.clone(), - artifact_pools.idkg_pool, - bouncer, - metrics_registry.clone(), - ); - new_p2p_consensus.add_client(idkg_rx, client, assembler, SLOT_TABLE_NO_LIMIT); - }; - - { - let (client, jh) = create_artifact_handler( - http_outcalls_tx, - CanisterHttpPoolManagerImpl::new( - Arc::clone(&state_reader), - Arc::new(Mutex::new(canister_http_adapter_client)), - Arc::clone(&consensus_crypto), - Arc::clone(&consensus_pool_cache), - ReplicaConfig { subnet_id, node_id }, - Arc::clone(®istry_client), - metrics_registry.clone(), - log.clone(), - ), - Arc::clone(&time_source) as Arc<_>, - Arc::clone(&artifact_pools.canister_http_pool), metrics_registry.clone(), - ); - join_handles.push(jh); - - let bouncer = Arc::new(CanisterHttpGossipImpl::new( - Arc::clone(&consensus_pool_cache), - Arc::clone(&state_reader), - log.clone(), - )); - let assembler = ic_artifact_downloader::FetchArtifact::new( log.clone(), - rt_handle.clone(), - artifact_pools.canister_http_pool, - bouncer, - metrics_registry.clone(), - ); - new_p2p_consensus.add_client(http_outcalls_rx, client, assembler, SLOT_TABLE_NO_LIMIT); - }; - - ( - artifact_pools.ingress_pool, - ingress_sender, - join_handles, - new_p2p_consensus, - ) -} - -fn init_artifact_pools( - node_id: NodeId, - config: ArtifactPoolConfig, - metrics_registry: &MetricsRegistry, - log: &ReplicaLogger, - catch_up_package: CatchUpPackage, - time_source: &dyn TimeSource, -) -> ArtifactPools { - let ingress_pool = Arc::new(RwLock::new(IngressPoolImpl::new( - node_id, - config.clone(), + malicious_flags, + ), + Arc::clone(&time_source) as Arc<_>, + artifact_pools.idkg_pool, metrics_registry.clone(), - log.clone(), - ))); - - let mut idkg_pool = IDkgPoolImpl::new( - config.clone(), - log.clone(), + )); + join_handles.push(create_artifact_handler( + abortable_broadcast_channels.https_outcalls, + CanisterHttpPoolManagerImpl::new( + Arc::clone(&state_reader), + Arc::new(Mutex::new(canister_http_adapter_client)), + Arc::clone(&consensus_crypto), + Arc::clone(&consensus_pool_cache), + ReplicaConfig { subnet_id, node_id }, + Arc::clone(®istry_client), + metrics_registry.clone(), + log.clone(), + ), + Arc::clone(&time_source) as Arc<_>, + artifact_pools.https_outcalls_pool, metrics_registry.clone(), - Box::new(idkg::IDkgStatsImpl::new(metrics_registry.clone())), - ); - idkg_pool.add_initial_dealings(&catch_up_package, time_source); - let idkg_pool = Arc::new(RwLock::new(idkg_pool)); + )); - let certification_pool = Arc::new(RwLock::new(CertificationPoolImpl::new( - node_id, - config, - log.clone(), - metrics_registry.clone(), - ))); - let dkg_pool = Arc::new(RwLock::new(DkgPoolImpl::new( - metrics_registry.clone(), - log.clone(), - ))); - let canister_http_pool = Arc::new(RwLock::new(CanisterHttpPoolImpl::new( - metrics_registry.clone(), - log.clone(), - ))); - ArtifactPools { - ingress_pool, - certification_pool, - dkg_pool, - idkg_pool, - canister_http_pool, - } + (artifact_pools.ingress_pool, user_ingress_tx, join_handles) } diff --git a/rs/replica/src/setup_ic_stack.rs b/rs/replica/src/setup_ic_stack.rs index de81d6a9b4b..f967fdd9301 100755 --- a/rs/replica/src/setup_ic_stack.rs +++ b/rs/replica/src/setup_ic_stack.rs @@ -106,7 +106,7 @@ pub fn construct_ic_stack( // This case is only possible if the replica is started without an orchestrator which // is currently only possible in the local development mode with `dfx`. None => { - let registry_cup = ic_consensus::dkg::make_registry_cup(&*registry, subnet_id, log) + let registry_cup = ic_consensus::make_registry_cup(&*registry, subnet_id, log) .expect("Couldn't create a registry CUP"); info!( @@ -204,6 +204,7 @@ pub fn construct_ic_stack( state_manager.clone(), state_manager.get_fd_factory(), completed_execution_messages_tx, + &state_manager.state_layout().tmp(), ); // ---------- MESSAGE ROUTING DEPS FOLLOW ---------- let certified_stream_store: Arc = @@ -256,7 +257,10 @@ pub fn construct_ic_stack( metrics_registry, log.clone(), )); - // ---------- BITCOIN INTEGRATION DEPS FOLLOW ---------- + // ---------- PAYLOAD BUILDERS WITHOUT ARTIFACT POOL FOLLOW ----------- + let query_stats_payload_builder = execution_services + .query_stats_payload_builder + .into_payload_builder(state_manager.clone(), node_id, log.clone()); let BitcoinAdapterClients { btc_testnet_client, btc_mainnet_client, @@ -276,7 +280,7 @@ pub fn construct_ic_stack( config.bitcoin_payload_builder_config, log.clone(), )); - // ---------- HTTPS OUTCALLS DEPS FOLLOW ---------- + // ---------- HTTPS OUTCALLS PAYLOAD BUILDER DEPS FOLLOW ---------- let canister_http_adapter_client = setup_canister_http_client( rt_handle_main.clone(), metrics_registry, @@ -287,10 +291,6 @@ pub fn construct_ic_stack( subnet_type, delegation_from_nns.clone(), ); - // ---------- QUERY STATS DEPS FOLLOW ----------- - let query_stats_payload_builder = execution_services - .query_stats_payload_builder - .into_payload_builder(state_manager.clone(), node_id, log.clone()); // ---------- CONSENSUS AND P2P DEPS FOLLOW ---------- let state_sync = StateSync::new(state_manager.clone(), log.clone()); let (max_certified_height_tx, max_certified_height_rx) = watch::channel(Height::from(0)); @@ -306,10 +306,10 @@ pub fn construct_ic_stack( subnet_id, Arc::clone(&crypto) as Arc<_>, Arc::clone(&state_manager) as Arc<_>, + Arc::new(state_sync) as Arc<_>, Arc::clone(&state_manager) as Arc<_>, consensus_pool, catch_up_package, - Arc::new(state_sync), xnet_payload_builder, self_validating_payload_builder, query_stats_payload_builder, diff --git a/rs/replica_tests/src/lib.rs b/rs/replica_tests/src/lib.rs index 5df1d565e45..c21a123df43 100644 --- a/rs/replica_tests/src/lib.rs +++ b/rs/replica_tests/src/lib.rs @@ -218,6 +218,7 @@ pub fn get_ic_config() -> IcConfig { node_operator_principal_id: None, secret_key_store: Some(node_sks), domain: None, + node_reward_type: None, }, ); @@ -752,7 +753,7 @@ pub struct UniversalCanisterWithStateMachine<'a> { canister_id: CanisterId, } -impl<'a> UniversalCanisterWithStateMachine<'a> { +impl UniversalCanisterWithStateMachine<'_> { pub fn canister_id(&self) -> CanisterId { self.canister_id } diff --git a/rs/replicated_state/BUILD.bazel b/rs/replicated_state/BUILD.bazel index 7a20362c223..fe5acc9a3ce 100644 --- a/rs/replicated_state/BUILD.bazel +++ b/rs/replicated_state/BUILD.bazel @@ -120,7 +120,7 @@ rust_bench( name = "replicated_state_intmap_bench", testonly = True, srcs = [ - "benches/bench_allocator.rs", + "benches/bench_intmap.rs", ], deps = [":replicated_state"] + BIN_DEPENDENCIES, ) diff --git a/rs/replicated_state/benches/bench_intmap.rs b/rs/replicated_state/benches/bench_intmap.rs index 9c15511bbb5..a425a8030ad 100644 --- a/rs/replicated_state/benches/bench_intmap.rs +++ b/rs/replicated_state/benches/bench_intmap.rs @@ -1,7 +1,7 @@ use criterion::{black_box, BatchSize, BenchmarkId, Criterion}; use criterion_time::ProcessTime; -use ic_replicated_state::page_map::int_map::IntMap; +use ic_replicated_state::page_map::int_map::{AsInt, IntMap, MutableIntMap}; use std::collections::{BTreeMap, HashMap}; use std::sync::Arc; @@ -11,57 +11,208 @@ fn value(k: u64) -> Value { Arc::new(k.to_be_bytes().to_vec()) } +const BENCH_SIZES: &[u64] = &[10, 100, 1000]; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +struct Key128(i32, usize); + +impl AsInt for Key128 { + type Repr = u128; + + #[inline] + fn as_int(&self) -> u128 { + (self.0 as u128) << 64 | self.1 as u128 + } +} + +fn key128(i: u64) -> Key128 { + Key128(i as i32 / 4, i as usize % 4) +} + fn bench_intmap(c: &mut Criterion) { let mut group = c.benchmark_group("Insert"); - for n in [10u64, 100, 1000].iter().cloned() { + for &n in BENCH_SIZES.iter() { group.bench_function(BenchmarkId::new("patricia", n), |b| { b.iter(|| { - let m: IntMap<_> = (0..n).map(|x| (x, value(x))).collect(); + let m: IntMap = (0..n).map(|x| (x * 13 % n, value(x))).collect(); + black_box(m); + }) + }); + group.bench_function(BenchmarkId::new("mpatricia", n), |b| { + b.iter(|| { + let m: MutableIntMap = (0..n).map(|x| (x * 13 % n, value(x))).collect(); + black_box(m); + }) + }); + group.bench_function(BenchmarkId::new("mpatricia_128", n), |b| { + b.iter(|| { + let m: MutableIntMap = + (0..n).map(|x| (key128(x * 13 % n), value(x))).collect(); black_box(m); }) }); group.bench_function(BenchmarkId::new("cow_btree", n), |b| { b.iter(|| { - let m: Arc> = Arc::new((0..n).map(|x| (x, value(x))).collect()); + let m: Arc> = + Arc::new((0..n).map(|x| (x * 13 % n, value(x))).collect()); + black_box(m); + }) + }); + group.bench_function(BenchmarkId::new("cow_btree_128", n), |b| { + b.iter(|| { + let m: Arc> = + Arc::new((0..n).map(|x| (key128(x * 13 % n), value(x))).collect()); black_box(m); }) }); group.bench_function(BenchmarkId::new("cow_hash", n), |b| { b.iter(|| { - let m: Arc> = Arc::new((0..n).map(|x| (x, value(x))).collect()); + let m: Arc> = + Arc::new((0..n).map(|x| (x * 13 % n, value(x))).collect()); + black_box(m); + }) + }); + group.bench_function(BenchmarkId::new("cow_hash_128", n), |b| { + b.iter(|| { + let m: Arc> = + Arc::new((0..n).map(|x| (key128(x * 13 % n), value(x))).collect()); black_box(m); }) }); } group.finish(); - let mut group = c.benchmark_group("Lookup"); - for n in [10u64, 100, 1000].iter().cloned() { - let patricia_map: IntMap = (0..n).map(|x| (x, value(x))).collect(); + let mut group = c.benchmark_group("Remove"); + for &n in BENCH_SIZES.iter() { + let patricia_map: IntMap = (0..n).map(|x| (x, value(x))).collect(); + let mpatricia_map: MutableIntMap = (0..n).map(|x| (x, value(x))).collect(); let btree_map: Arc> = Arc::new((0..n).map(|x| (x, value(x))).collect()); let hash_map: Arc> = Arc::new((0..n).map(|x| (x, value(x))).collect()); + + group.bench_function(BenchmarkId::new("patricia", n), |b| { + b.iter_batched( + || patricia_map.clone(), + |mut map| { + for i in 0..n { + map = map.remove(&(i * 13 % n)).0; + map = map.remove(&(i * 13 % n + n)).0; + } + black_box(map); + }, + BatchSize::SmallInput, + ); + }); + group.bench_function(BenchmarkId::new("mpatricia", n), |b| { + b.iter_batched( + || mpatricia_map.clone(), + |mut map| { + for i in 0..n { + map.remove(&(i * 13 % n)); + map.remove(&(i * 13 % n + n)); + } + black_box(map); + }, + BatchSize::SmallInput, + ); + }); + group.bench_function(BenchmarkId::new("cow_btree", n), |b| { + b.iter_batched( + || Arc::clone(&btree_map), + |mut map| { + let map = Arc::make_mut(&mut map); + for i in 0..n { + map.remove(&(i * 13 % n)); + map.remove(&(i * 13 % n + n)); + } + black_box(map); + }, + BatchSize::SmallInput, + ); + }); + group.bench_function(BenchmarkId::new("cow_hash", n), |b| { + b.iter_batched( + || Arc::clone(&hash_map), + |mut map| { + let map = Arc::make_mut(&mut map); + for i in 0..n { + map.remove(&(i * 13 % n)); + map.remove(&(i * 13 % n + n)); + } + black_box(map); + }, + BatchSize::SmallInput, + ); + }); + } + group.finish(); + + let mut group = c.benchmark_group("Lookup"); + for &n in BENCH_SIZES.iter() { + const N: u64 = 5; + let kv = |x| (N * x, value(x)); + let kv128 = |x| (key128(N * x), value(x)); + + let patricia_map: IntMap = (0..n).map(kv).collect(); + let mpatricia_map: MutableIntMap = (0..n).map(kv).collect(); + let patricia_128_map: MutableIntMap = (0..n).map(kv128).collect(); + let btree_map: Arc> = Arc::new((0..n).map(kv).collect()); + let btree_128_map: Arc> = Arc::new((0..n).map(kv128).collect()); + let hash_map: Arc> = Arc::new((0..n).map(kv).collect()); + let hash_128_map: Arc> = Arc::new((0..n).map(kv128).collect()); group.bench_function(BenchmarkId::new("patricia", n), |b| { b.iter(|| { for i in 0..n { - black_box(patricia_map.get(i)); - black_box(patricia_map.get(i + n)); + black_box(patricia_map.get(&(i * N))); + black_box(patricia_map.get(&(i * N + 3))); + } + }); + }); + group.bench_function(BenchmarkId::new("mpatricia", n), |b| { + b.iter(|| { + for i in 0..n { + black_box(mpatricia_map.get(&(i * N))); + black_box(mpatricia_map.get(&(i * N + 3))); + } + }); + }); + group.bench_function(BenchmarkId::new("mpatricia_128", n), |b| { + b.iter(|| { + for i in 0..n { + black_box(patricia_128_map.get(&key128(i * N))); + black_box(patricia_128_map.get(&key128(i * N + 3))); } }); }); group.bench_function(BenchmarkId::new("cow_btree", n), |b| { b.iter(|| { for i in 0..n { - black_box(btree_map.get(&i)); - black_box(btree_map.get(&(i + n))); + black_box(btree_map.get(&(i * N))); + black_box(btree_map.get(&(i * N + 3))); + } + }); + }); + group.bench_function(BenchmarkId::new("cow_btree_128", n), |b| { + b.iter(|| { + for i in 0..n { + black_box(btree_128_map.get(&key128(i * N))); + black_box(btree_128_map.get(&key128(i * N + 3))); } }); }); group.bench_function(BenchmarkId::new("cow_hash", n), |b| { b.iter(|| { for i in 0..n { - black_box(hash_map.get(&i)); - black_box(hash_map.get(&(i + n))); + black_box(hash_map.get(&(i * N))); + black_box(hash_map.get(&(i * N + 3))); + } + }); + }); + group.bench_function(BenchmarkId::new("cow_hash_128", n), |b| { + b.iter(|| { + for i in 0..n { + black_box(hash_128_map.get(&key128(i * N))); + black_box(hash_128_map.get(&key128(i * N + 3))); } }); }); @@ -69,9 +220,13 @@ fn bench_intmap(c: &mut Criterion) { group.finish(); let mut group = c.benchmark_group("Union"); - for n in [10u64, 100, 1000].iter().cloned() { - let patricia_lmap: IntMap = (0..n).map(|x| (x, value(x))).collect(); - let patricia_rmap: IntMap = (n / 2..n + n / 2).map(|x| (x, value(x))).collect(); + for &n in BENCH_SIZES.iter() { + let patricia_lmap: IntMap = (0..n).map(|x| (x, value(x))).collect(); + let patricia_rmap: IntMap = (n / 2..n + n / 2).map(|x| (x, value(x))).collect(); + + let mpatricia_lmap: MutableIntMap = (0..n).map(|x| (x, value(x))).collect(); + let mpatricia_rmap: MutableIntMap = + (n / 2..n + n / 2).map(|x| (x, value(x))).collect(); let btree_lmap: Arc> = Arc::new((0..n).map(|x| (x, value(x))).collect()); @@ -91,6 +246,16 @@ fn bench_intmap(c: &mut Criterion) { BatchSize::SmallInput, ); }); + group.bench_function(BenchmarkId::new("mpatricia", n), |b| { + b.iter_batched( + || (mpatricia_lmap.clone(), mpatricia_rmap.clone()), + |(mut l, r)| { + l.union(r); + black_box(l); + }, + BatchSize::SmallInput, + ); + }); group.bench_function(BenchmarkId::new("cow_btree", n), |b| { b.iter_batched( || (Arc::clone(&btree_lmap), Arc::clone(&btree_rmap)), @@ -121,8 +286,9 @@ fn bench_intmap(c: &mut Criterion) { group.finish(); let mut group = c.benchmark_group("Iter"); - for n in [10u64, 100, 1000].iter().cloned() { - let patricia_map: IntMap = (0..n).map(|x| (x, value(x))).collect(); + for &n in BENCH_SIZES.iter() { + let patricia_map: IntMap = (0..n).map(|x| (x, value(x))).collect(); + let mpatricia_map: MutableIntMap = (0..n).map(|x| (x, value(x))).collect(); let btree_map: Arc> = Arc::new((0..n).map(|x| (x, value(x))).collect()); let hash_map: Arc> = Arc::new((0..n).map(|x| (x, value(x))).collect()); @@ -134,6 +300,13 @@ fn bench_intmap(c: &mut Criterion) { } }); }); + group.bench_function(BenchmarkId::new("mpatricia", n), |b| { + b.iter(|| { + for e in mpatricia_map.iter() { + black_box(e); + } + }); + }); group.bench_function(BenchmarkId::new("cow_btree", n), |b| { b.iter(|| { for e in btree_map.iter() { diff --git a/rs/replicated_state/src/canister_state.rs b/rs/replicated_state/src/canister_state.rs index 216f49763d0..44c2cc9591b 100644 --- a/rs/replicated_state/src/canister_state.rs +++ b/rs/replicated_state/src/canister_state.rs @@ -375,6 +375,13 @@ impl CanisterState { + self.system_state.snapshots_memory_usage } + /// Returns the amount of Wasm memory currently used by the canister in bytes. + pub fn wasm_memory_usage(&self) -> NumBytes { + self.execution_state + .as_ref() + .map_or(NumBytes::from(0), |es| es.wasm_memory_usage()) + } + /// Returns the amount of execution memory (heap, stable, globals, Wasm) /// currently used by the canister in bytes. pub fn execution_memory_usage(&self) -> NumBytes { @@ -426,13 +433,6 @@ impl CanisterState { + NumBytes::from(self.system_state.certified_data.len() as u64) } - /// Sets the (transient) size in bytes of guaranteed responses from this - /// canister routed into streams and not yet garbage collected. - pub(super) fn set_stream_guaranteed_responses_size_bytes(&mut self, size_bytes: usize) { - self.system_state - .set_stream_guaranteed_responses_size_bytes(size_bytes); - } - /// Returns the current memory allocation of the canister. pub fn memory_allocation(&self) -> MemoryAllocation { self.system_state.memory_allocation @@ -564,6 +564,12 @@ impl CanisterState { .map_or(NumBytes::from(0), |es| es.heap_delta()) + self.system_state.wasm_chunk_store.heap_delta() } + + /// Updates status of `OnLowWasmMemory` hook. + pub fn update_on_low_wasm_memory_hook_condition(&mut self) { + self.system_state + .update_on_low_wasm_memory_hook_status(self.memory_usage(), self.wasm_memory_usage()); + } } /// The result of `next_execution()` function. diff --git a/rs/replicated_state/src/canister_state/execution_state.rs b/rs/replicated_state/src/canister_state/execution_state.rs index 3cecdb1f4ae..82bad2c4423 100644 --- a/rs/replicated_state/src/canister_state/execution_state.rs +++ b/rs/replicated_state/src/canister_state/execution_state.rs @@ -479,7 +479,7 @@ pub struct ExecutionState { pub next_scheduled_method: NextScheduledMethod, /// Checks if execution is in Wasm64 mode. - pub is_wasm64: bool, + pub wasm_execution_mode: WasmExecutionMode, } // We have to implement it by hand as embedder_cache can not be compared for @@ -499,7 +499,7 @@ impl PartialEq for ExecutionState { metadata, last_executed_round, next_scheduled_method, - is_wasm64, + wasm_execution_mode, } = rhs; ( @@ -511,7 +511,7 @@ impl PartialEq for ExecutionState { &self.metadata, &self.last_executed_round, &self.next_scheduled_method, - &self.is_wasm64, + &self.wasm_execution_mode, ) == ( &wasm_binary.binary, wasm_memory, @@ -521,7 +521,7 @@ impl PartialEq for ExecutionState { metadata, last_executed_round, next_scheduled_method, - is_wasm64, + wasm_execution_mode, ) } } @@ -531,7 +531,7 @@ impl ExecutionState { /// The state will be created with empty stable memory, but may have wasm /// memory from data sections in the wasm module. /// The state will be created with last_executed_round = 0, a - /// default next_scheduled_method, and is_wasm64 = false. + /// default next_scheduled_method, and wasm_execution_mode = WasmExecutionMode::Wasm32. /// Be sure to change these if needed. pub fn new( canister_root: PathBuf, @@ -552,7 +552,7 @@ impl ExecutionState { metadata: wasm_metadata, last_executed_round: ExecutionRound::from(0), next_scheduled_method: NextScheduledMethod::default(), - is_wasm64: false, + wasm_execution_mode: WasmExecutionMode::Wasm32, } } @@ -561,13 +561,18 @@ impl ExecutionState { self.exports.has_method(method) } + /// Returns the Wasm memory currently used by the `ExecutionState`. + pub fn wasm_memory_usage(&self) -> NumBytes { + num_bytes_try_from(self.wasm_memory.size) + .expect("could not convert from wasm memory number of pages to bytes") + } + /// Returns the memory currently used by the `ExecutionState`. pub fn memory_usage(&self) -> NumBytes { // We use 8 bytes per global. let globals_size_bytes = 8 * self.exported_globals.len() as u64; let wasm_binary_size_bytes = self.wasm_binary.binary.len() as u64; - num_bytes_try_from(self.wasm_memory.size) - .expect("could not convert from wasm memory number of pages to bytes") + self.wasm_memory_usage() + num_bytes_try_from(self.stable_memory.size) .expect("could not convert from stable memory number of pages to bytes") + NumBytes::from(globals_size_bytes) @@ -791,6 +796,29 @@ impl TryFrom for WasmMetadata { } } +/// Keeps track of how a canister is executing. +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum WasmExecutionMode { + Wasm32, + Wasm64, +} + +impl WasmExecutionMode { + pub fn is_wasm64(&self) -> bool { + match self { + WasmExecutionMode::Wasm32 => false, + WasmExecutionMode::Wasm64 => true, + } + } + pub fn from_is_wasm64(is_wasm64: bool) -> Self { + if is_wasm64 { + WasmExecutionMode::Wasm64 + } else { + WasmExecutionMode::Wasm32 + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/rs/replicated_state/src/canister_state/queues.rs b/rs/replicated_state/src/canister_state/queues.rs index a29e1db5a70..b5dee5a66cf 100644 --- a/rs/replicated_state/src/canister_state/queues.rs +++ b/rs/replicated_state/src/canister_state/queues.rs @@ -10,6 +10,7 @@ use self::message_pool::{ Context, InboundReference, Kind, MessagePool, OutboundReference, SomeReference, }; use self::queue::{CanisterQueue, IngressQueue, InputQueue, OutputQueue}; +use crate::page_map::int_map::MutableIntMap; use crate::replicated_state::MR_SYNTHETIC_REJECT_MESSAGE_MAX_LEN; use crate::{CanisterState, CheckpointLoadingMetrics, InputQueueType, InputSource, StateError}; use ic_base_types::PrincipalId; @@ -141,7 +142,7 @@ pub struct CanisterQueues { /// no corresponding message in the message pool; or entry in the compact /// response maps (which record the `CallbackIds` of expired / shed inbound /// best-effort responses). - canister_queues: BTreeMap, + canister_queues: BTreeMap, Arc)>, /// Backing store for `canister_queues` references, combining a `MessagePool` /// and maps of compact responses (`CallbackIds` of expired / shed responses), @@ -151,6 +152,7 @@ pub struct CanisterQueues { /// Slot and memory reservation stats. Message count and size stats are /// maintained separately in the `MessagePool`. + #[validate_eq(CompareWithValidateEq)] queue_stats: QueueStats, /// Round-robin schedule for `pop_input()` across ingress, local subnet senders @@ -164,7 +166,7 @@ pub struct CanisterQueues { /// /// Used for response deduplication (whether due to a locally generated reject /// response to a best-effort call; or due to a malicious / buggy subnet). - callbacks_with_enqueued_response: BTreeSet, + callbacks_with_enqueued_response: MutableIntMap, } /// Circular iterator that consumes output queue messages: loops over output @@ -180,7 +182,7 @@ pub struct CanisterQueues { pub struct CanisterOutputQueuesIterator<'a> { /// Priority queue of non-empty output queues. The next message to be popped /// / peeked is the one at the front of the first queue. - queues: VecDeque<(&'a CanisterId, &'a mut OutputQueue)>, + queues: VecDeque<(&'a CanisterId, &'a mut Arc)>, /// Mutable store holding the messages referenced by `queues`. store: &'a mut MessageStoreImpl, @@ -194,7 +196,7 @@ impl<'a> CanisterOutputQueuesIterator<'a> { /// `CanisterQueues::canister_queues` (a map of `CanisterId` to an input queue, /// output queue pair) and `MessagePool`. fn new( - queues: &'a mut BTreeMap, + queues: &'a mut BTreeMap, Arc)>, store: &'a mut MessageStoreImpl, ) -> Self { let queues: VecDeque<_> = queues @@ -281,7 +283,7 @@ impl<'a> CanisterOutputQueuesIterator<'a> { /// Computes the number of (potentially stale) messages left in `queues`. /// /// Time complexity: `O(n)`. - fn compute_size(queues: &VecDeque<(&'a CanisterId, &'a mut OutputQueue)>) -> usize { + fn compute_size(queues: &VecDeque<(&'a CanisterId, &'a mut Arc)>) -> usize { queues.iter().map(|(_, q)| q.len()).sum() } } @@ -364,13 +366,13 @@ struct MessageStoreImpl { /// `CanisterInput::DeadlineExpired` by `peek_input()` / `pop_input()` (and /// "inflated" by `SystemState` into `SysUnknown` reject responses based on the /// callback). - expired_callbacks: BTreeMap, + expired_callbacks: MutableIntMap, /// Compact reject responses (`CallbackIds`) replacing best-effort responses /// that were shed. These are returned as `CanisterInput::ResponseDropped` by /// `peek_input()` / `pop_input()` (and "inflated" by `SystemState` into /// `SysUnknown` reject responses based on the callback). - shed_responses: BTreeMap, + shed_responses: MutableIntMap, } impl MessageStoreImpl { @@ -383,10 +385,15 @@ impl MessageStoreImpl { /// next non-stale reference. /// /// Panics if the reference at the front of the queue is stale. - fn queue_pop_and_advance(&mut self, queue: &mut CanisterQueue) -> Option + fn queue_pop_and_advance(&mut self, queue: &mut Arc>) -> Option where MessageStoreImpl: MessageStore, { + if queue.len() == 0 { + return None; + } + + let queue = Arc::make_mut(queue); let reference = queue.pop()?; // Advance to the next non-stale reference. @@ -548,8 +555,8 @@ trait InboundMessageStore: MessageStore { /// Time complexity: `O(n * log(n))`. fn callbacks_with_enqueued_response( &self, - canister_queues: &BTreeMap, - ) -> Result, String>; + canister_queues: &BTreeMap, Arc)>, + ) -> Result, String>; } impl InboundMessageStore for MessageStoreImpl { @@ -561,9 +568,9 @@ impl InboundMessageStore for MessageStoreImpl { fn callbacks_with_enqueued_response( &self, - canister_queues: &BTreeMap, - ) -> Result, String> { - let mut callbacks = BTreeSet::new(); + canister_queues: &BTreeMap, Arc)>, + ) -> Result, String> { + let mut callbacks = MutableIntMap::new(); canister_queues .values() .flat_map(|(input_queue, _)| input_queue.iter()) @@ -598,7 +605,7 @@ impl InboundMessageStore for MessageStoreImpl { } }; - if callbacks.insert(callback_id) { + if callbacks.insert(callback_id, ()).is_none() { Ok(()) } else { Err(format!( @@ -654,7 +661,11 @@ impl CanisterQueues { F: FnMut(&CanisterId, &RequestOrResponse) -> Result<(), ()>, { for (canister_id, (_, queue)) in self.canister_queues.iter_mut() { - while let Some(reference) = queue.peek() { + loop { + let Some(reference) = queue.peek() else { + break; + }; + let queue = Arc::make_mut(queue); let Some(msg) = self.store.pool.get(reference) else { // Expired / dropped message. Pop it and advance. assert_eq!(Some(reference), queue.pop()); @@ -740,18 +751,19 @@ impl CanisterQueues { } // Safe to already (attempt to) reserve an output slot here, as the `push()` // below is guaranteed to succeed due to the check above. - if let Err(e) = output_queue.try_reserve_response_slot() { + if let Err(e) = Arc::make_mut(output_queue).try_reserve_response_slot() { return Err((e, msg)); } - input_queue + Arc::make_mut(input_queue) } RequestOrResponse::Response(ref response) => { match self.canister_queues.get_mut(&sender) { Some((queue, _)) if queue.check_has_reserved_response_slot().is_ok() => { // Check against duplicate responses. - if !self + if self .callbacks_with_enqueued_response - .insert(response.originator_reply_callback) + .insert(response.originator_reply_callback, ()) + .is_some() { debug_assert_eq!(Ok(()), self.test_invariants()); if response.deadline == NO_DEADLINE { @@ -768,7 +780,7 @@ impl CanisterQueues { return Ok(false); } } - queue + Arc::make_mut(queue) } // Queue does not exist or has no reserved slot for this response. @@ -787,7 +799,8 @@ impl CanisterQueues { // aleady checked for a matching callback). Silently drop it. debug_assert!(self .callbacks_with_enqueued_response - .contains(&response.originator_reply_callback)); + .get(&response.originator_reply_callback) + .is_some()); return Ok(false); } } @@ -844,7 +857,11 @@ impl CanisterQueues { }; // Check against duplicate responses. - if !self.callbacks_with_enqueued_response.insert(callback_id) { + if self + .callbacks_with_enqueued_response + .insert(callback_id, ()) + .is_some() + { // There is already a response enqueued for the callback. return Ok(false); } @@ -860,7 +877,7 @@ impl CanisterQueues { } let reference = self.store.push_inbound_timeout_response(callback_id); - input_queue.push_response(reference); + Arc::make_mut(input_queue).push_response(reference); self.queue_stats.on_push_timeout_response(); // Add sender canister ID to the appropriate input schedule queue if it is not @@ -911,7 +928,10 @@ impl CanisterQueues { if let Some(msg_) = &msg { if let Some(callback_id) = msg_.response_callback_id() { - assert!(self.callbacks_with_enqueued_response.remove(&callback_id)); + assert!(self + .callbacks_with_enqueued_response + .remove(&callback_id) + .is_some()); } debug_assert_eq!(Ok(()), self.test_invariants()); debug_assert_eq!(Ok(()), self.schedules_ok(&|_| InputQueueType::RemoteSubnet)); @@ -964,7 +984,7 @@ impl CanisterQueues { if self .canister_queues .get(sender) - .map_or(false, |(input_queue, _)| input_queue.len() != 0) + .is_some_and(|(input_queue, _)| input_queue.len() != 0) { self.input_schedule.reschedule(*sender, input_queue_type); break; @@ -1076,7 +1096,7 @@ impl CanisterQueues { if let Err(e) = output_queue.check_has_request_slot() { return Err((e, request)); } - if let Err(e) = input_queue.try_reserve_response_slot() { + if let Err(e) = Arc::make_mut(input_queue).try_reserve_response_slot() { return Err((e, request)); } @@ -1084,7 +1104,7 @@ impl CanisterQueues { .on_push_request(&request, Context::Outbound); let reference = self.store.pool.insert_outbound_request(request, time); - output_queue.push_request(reference); + Arc::make_mut(output_queue).push_request(reference); debug_assert_eq!(Ok(()), self.test_invariants()); Ok(()) @@ -1113,7 +1133,7 @@ impl CanisterQueues { let (input_queue, _output_queue) = get_or_insert_queues(&mut self.canister_queues, &request.receiver); - input_queue.try_reserve_response_slot()?; + Arc::make_mut(input_queue).try_reserve_response_slot()?; self.queue_stats .on_push_request(&request, Context::Outbound); debug_assert_eq!(Ok(()), self.test_invariants()); @@ -1172,7 +1192,7 @@ impl CanisterQueues { .expect("pushing response into inexistent output queue") .1; let reference = self.store.pool.insert_outbound_response(response); - output_queue.push_response(reference); + Arc::make_mut(output_queue).push_response(reference); debug_assert_eq!(Ok(()), self.test_invariants()); } @@ -1324,13 +1344,6 @@ impl CanisterQueues { .oversized_guaranteed_requests_extra_bytes } - /// Sets the (transient) size in bytes of guaranteed responses routed from - /// output queues into streams and not yet garbage collected. - pub(super) fn set_stream_guaranteed_responses_size_bytes(&mut self, size_bytes: usize) { - self.queue_stats - .transient_stream_guaranteed_responses_size_bytes = size_bytes; - } - /// Garbage collects all input and output queue pairs that are both empty. /// /// Because there is no useful information in an empty queue, there is no @@ -1491,12 +1504,13 @@ impl CanisterQueues { .expect("No matching queue for dropped message."); if input_queue.peek() == Some(reference) { + let input_queue = Arc::make_mut(input_queue); input_queue.pop(); self.store.queue_advance(input_queue); } // Release the outbound response slot. - output_queue.release_reserved_response_slot(); + Arc::make_mut(output_queue).release_reserved_response_slot(); self.queue_stats.on_drop_input_request(&request); } } @@ -1530,6 +1544,7 @@ impl CanisterQueues { // a queue containing references `[1, 2]`; `1` and `2` expire as part of the // same `time_out_messages()` call; `on_message_dropped(1)` will also pop `2`). if output_queue.peek() == Some(reference) { + let output_queue = Arc::make_mut(output_queue); output_queue.pop(); self.store.queue_advance(output_queue); } @@ -1548,9 +1563,10 @@ impl CanisterQueues { // request that was still in an output queue. assert!(self .callbacks_with_enqueued_response - .insert(response.originator_reply_callback)); + .insert(response.originator_reply_callback, ()) + .is_none()); let reference = self.store.insert_inbound(response.into()); - input_queue.push_response(reference); + Arc::make_mut(input_queue).push_response(reference); // If the input queue is not already in a sender schedule, add it. if input_queue.len() == 1 { @@ -1602,7 +1618,7 @@ impl CanisterQueues { self.input_schedule.test_invariants( self.canister_queues .iter() - .map(|(canister_id, (input_queue, _))| (canister_id, input_queue)), + .map(|(canister_id, (input_queue, _))| (canister_id, &**input_queue)), &input_queue_type_fn, ) } @@ -1627,8 +1643,6 @@ impl CanisterQueues { let calculated_stats = Self::calculate_queue_stats( &self.canister_queues, self.queue_stats.guaranteed_response_memory_reservations, - self.queue_stats - .transient_stream_guaranteed_responses_size_bytes, ); if self.queue_stats != calculated_stats { return Err(format!( @@ -1655,13 +1669,13 @@ impl CanisterQueues { /// Computes stats for the given canister queues. Used when deserializing and in /// `debug_assert!()` checks. Takes the number of memory reservations from the /// caller, as the queues have no need to track memory reservations, so it - /// cannot be computed. Same with the size of guaranteed responses in streams. + /// cannot be computed. Size of guaranteed responses in streams is ignored as it is + /// limited. /// /// Time complexity: `O(canister_queues.len())`. fn calculate_queue_stats( - canister_queues: &BTreeMap, + canister_queues: &BTreeMap, Arc)>, guaranteed_response_memory_reservations: usize, - transient_stream_guaranteed_responses_size_bytes: usize, ) -> QueueStats { let (input_queues_reserved_slots, output_queues_reserved_slots) = canister_queues .values() @@ -1673,7 +1687,6 @@ impl CanisterQueues { guaranteed_response_memory_reservations, input_queues_reserved_slots, output_queues_reserved_slots, - transient_stream_guaranteed_responses_size_bytes, } } } @@ -1684,12 +1697,12 @@ impl CanisterQueues { /// Written as a free function in order to avoid borrowing the full /// `CanisterQueues`, which then requires looking up the queues again. fn get_or_insert_queues<'a>( - canister_queues: &'a mut BTreeMap, + canister_queues: &'a mut BTreeMap, Arc)>, canister_id: &CanisterId, -) -> (&'a mut InputQueue, &'a mut OutputQueue) { +) -> (&'a mut Arc, &'a mut Arc) { let (input_queue, output_queue) = canister_queues.entry(*canister_id).or_insert_with(|| { - let input_queue = CanisterQueue::new(DEFAULT_QUEUE_CAPACITY); - let output_queue = CanisterQueue::new(DEFAULT_QUEUE_CAPACITY); + let input_queue = Arc::new(CanisterQueue::new(DEFAULT_QUEUE_CAPACITY)); + let output_queue = Arc::new(CanisterQueue::new(DEFAULT_QUEUE_CAPACITY)); (input_queue, output_queue) }); (input_queue, output_queue) @@ -1731,7 +1744,7 @@ fn input_queue_type_fn<'a>( impl From<&CanisterQueues> for pb_queues::CanisterQueues { fn from(item: &CanisterQueues) -> Self { fn callback_references_to_proto( - callback_references: &BTreeMap, + callback_references: &MutableIntMap, ) -> Vec { callback_references .iter() @@ -1749,8 +1762,8 @@ impl From<&CanisterQueues> for pb_queues::CanisterQueues { .iter() .map(|(canid, (iq, oq))| CanisterQueuePair { canister_id: Some(pb_types::CanisterId::from(*canid)), - input_queue: Some(iq.into()), - output_queue: Some(oq.into()), + input_queue: Some((&**iq).into()), + output_queue: Some((&**oq).into()), }) .collect(), pool: if item.store.pool != MessagePool::default() { @@ -1780,7 +1793,7 @@ impl TryFrom<(pb_queues::CanisterQueues, &dyn CheckpointLoadingMetrics)> for Can fn callback_references_try_from_proto( callback_references: Vec, - ) -> Result, ProxyDecodeError> + ) -> Result, ProxyDecodeError> { callback_references .into_iter() @@ -1826,7 +1839,7 @@ impl TryFrom<(pb_queues::CanisterQueues, &dyn CheckpointLoadingMetrics)> for Can } }); - Ok((canister_id, (iq, oq))) + Ok((canister_id, (Arc::new(iq), Arc::new(oq)))) }) .collect::>()?; @@ -1841,7 +1854,6 @@ impl TryFrom<(pb_queues::CanisterQueues, &dyn CheckpointLoadingMetrics)> for Can let queue_stats = Self::calculate_queue_stats( &canister_queues, item.guaranteed_response_memory_reservations as usize, - 0, ); let input_schedule = InputSchedule::try_from(( @@ -1880,13 +1892,12 @@ impl TryFrom<(pb_queues::CanisterQueues, &dyn CheckpointLoadingMetrics)> for Can } /// Tracks slot and guaranteed response memory reservations across input and -/// output queues; and holds a (transient) byte size of responses already routed -/// into streams (tracked separately, at the replicated state level, as messages -/// are routed to and GC-ed from streams). +/// output queues. Transient byte size of responses already routed into streams +/// is ignored as the streams size is limited. /// /// Stats for the enqueued messages themselves (counts and sizes by kind, /// context and class) are tracked separately in `message_pool::MessageStats`. -#[derive(Clone, Eq, PartialEq, Debug, Default)] +#[derive(Clone, Eq, PartialEq, Debug, Default, ValidateEq)] struct QueueStats { /// Count of guaranteed response memory reservations across input and output /// queues. This is equivalent to the number of outstanding (inbound or outbound) @@ -1909,22 +1920,12 @@ struct QueueStats { /// Count of slots reserved in output queues. Note that this is different from /// memory reservations for guaranteed responses. output_queues_reserved_slots: usize, - - /// Transient: size in bytes of guaranteed responses routed from `output_queues` - /// into streams and not yet garbage collected. - /// - /// This is updated by `ReplicatedState::put_streams()`, called by MR after - /// every streams mutation (induction, routing, GC). And is (re)populated during - /// checkpoint loading by `ReplicatedState::new_from_checkpoint()`. - transient_stream_guaranteed_responses_size_bytes: usize, } impl QueueStats { - /// Returns the memory usage of reservations for guaranteed responses plus - /// guaranteed responses in streans. + /// Returns the memory usage of reservations for guaranteed responses. pub fn guaranteed_response_memory_usage(&self) -> usize { self.guaranteed_response_memory_reservations * MAX_RESPONSE_COUNT_BYTES - + self.transient_stream_guaranteed_responses_size_bytes } /// Updates the stats to reflect the enqueueing of the given message in the given diff --git a/rs/replicated_state/src/canister_state/queues/message_pool.rs b/rs/replicated_state/src/canister_state/queues/message_pool.rs index f0507ccbf55..592d1bd8059 100644 --- a/rs/replicated_state/src/canister_state/queues/message_pool.rs +++ b/rs/replicated_state/src/canister_state/queues/message_pool.rs @@ -1,4 +1,5 @@ use super::CanisterInput; +use crate::page_map::int_map::{AsInt, MutableIntMap}; use ic_protobuf::proxy::{try_from_option_field, ProxyDecodeError}; use ic_protobuf::state::queues::v1 as pb_queues; use ic_types::messages::{ @@ -8,7 +9,7 @@ use ic_types::time::CoarseTime; use ic_types::{CountBytes, Time}; use ic_validate_eq::ValidateEq; use ic_validate_eq_derive::ValidateEq; -use std::collections::{BTreeMap, BTreeSet}; +use std::collections::BTreeSet; use std::marker::PhantomData; use std::ops::{AddAssign, SubAssign}; use std::sync::Arc; @@ -131,6 +132,33 @@ impl Id { } } +impl AsInt for Id { + type Repr = u64; + + #[inline] + fn as_int(&self) -> u64 { + self.0 + } +} + +impl AsInt for (CoarseTime, Id) { + type Repr = u128; + + #[inline] + fn as_int(&self) -> u128 { + (self.0.as_secs_since_unix_epoch() as u128) << 64 | self.1 .0 as u128 + } +} + +impl AsInt for (usize, Id) { + type Repr = u128; + + #[inline] + fn as_int(&self) -> u128 { + (self.0 as u128) << 64 | self.1 .0 as u128 + } +} + /// A typed reference -- inbound (`CanisterInput`) or outbound /// (`RequestOrResponse`) -- to a message in the `MessagePool`. #[derive(Debug)] @@ -214,6 +242,15 @@ impl From> for Id { } } +impl AsInt for Reference { + type Repr = u64; + + #[inline] + fn as_int(&self) -> u64 { + self.0 + } +} + /// A reference to an inbound message (returned as a `CanisterInput`). pub(super) type InboundReference = Reference; @@ -327,7 +364,7 @@ impl TryFrom for CallbackReferenc pub(super) struct MessagePool { /// Pool contents. #[validate_eq(CompareWithValidateEq)] - messages: BTreeMap, + messages: MutableIntMap, /// Records the (implicit) deadlines of all the outbound guaranteed response /// requests (only). @@ -337,7 +374,7 @@ pub(super) struct MessagePool { /// `outbound_guaranteed_request_deadlines.keys().collect() == messages.keys().filter(|id| (id.context(), id.class(), id.kind()) == (Context::Outbound, Class::GuaranteedResponse, Kind::Request)).collect()` /// * The deadline matches the one recorded in `deadline_queue`: /// `outbound_guaranteed_request_deadlines.iter().all(|(id, deadline)| deadline_queue.contains(&(deadline, id)))` - outbound_guaranteed_request_deadlines: BTreeMap, + outbound_guaranteed_request_deadlines: MutableIntMap, /// Running message stats for the pool. message_stats: MessageStats, @@ -348,13 +385,13 @@ pub(super) struct MessagePool { /// by deadline. /// /// Message IDs break ties, ensuring deterministic ordering. - deadline_queue: BTreeSet<(CoarseTime, Id)>, + deadline_queue: MutableIntMap<(CoarseTime, Id), ()>, /// Load shedding priority queue. Holds all best-effort messages, ordered by /// size. /// /// Message IDs break ties, ensuring deterministic ordering. - size_queue: BTreeSet<(usize, Id)>, + size_queue: MutableIntMap<(usize, Id), ()>, /// A monotonically increasing counter used to generate unique message IDs. message_id_generator: u64, @@ -470,7 +507,7 @@ impl MessagePool { // all best-effort messages except responses in input queues; plus guaranteed // response requests in output queues if actual_deadline != NO_DEADLINE { - self.deadline_queue.insert((actual_deadline, id)); + self.deadline_queue.insert((actual_deadline, id), ()); // Record in the outbound guaranteed response deadline map, iff it's an outbound // guaranteed response request. @@ -483,7 +520,7 @@ impl MessagePool { // Record in load shedding queue iff it's a best-effort message. if class == Class::BestEffort { - self.size_queue.insert((size_bytes, id)); + self.size_queue.insert((size_bytes, id), ()); } reference @@ -552,7 +589,7 @@ impl MessagePool { .outbound_guaranteed_request_deadlines .remove(&id) .unwrap(); - let removed = self.deadline_queue.remove(&(deadline, id)); + let removed = self.deadline_queue.remove(&(deadline, id)).is_some(); debug_assert!(removed); } @@ -564,7 +601,7 @@ impl MessagePool { // All other best-effort messages do expire. (_, BestEffort, _) => { - let removed = self.deadline_queue.remove(&(msg.deadline(), id)); + let removed = self.deadline_queue.remove(&(msg.deadline(), id)).is_some(); debug_assert!(removed); } } @@ -573,7 +610,7 @@ impl MessagePool { /// Removes the given message from the load shedding queue. fn remove_from_size_queue(&mut self, id: Id, msg: &RequestOrResponse) { if id.class() == Class::BestEffort { - let removed = self.size_queue.remove(&(msg.count_bytes(), id)); + let removed = self.size_queue.remove(&(msg.count_bytes(), id)).is_some(); debug_assert!(removed); } } @@ -582,7 +619,7 @@ impl MessagePool { /// /// Time complexity: `O(log(self.len()))`. pub(super) fn has_expired_deadlines(&self, now: Time) -> bool { - if let Some((deadline, _)) = self.deadline_queue.first() { + if let Some((deadline, _)) = self.deadline_queue.min_key() { let now = CoarseTime::floor(now); if *deadline < now { return true; @@ -602,7 +639,7 @@ impl MessagePool { } let now = CoarseTime::floor(now); - if self.deadline_queue.first().unwrap().0 >= now { + if self.deadline_queue.min_key().unwrap().0 >= now { // No expired messages, bail out. return Vec::new(); } @@ -614,7 +651,7 @@ impl MessagePool { // Take and return all expired messages. let expired = temp .into_iter() - .map(|(_, id)| { + .map(|((_, id), _)| { let msg = self.take_impl(id).unwrap(); if id.is_outbound_guaranteed_request() { self.outbound_guaranteed_request_deadlines.remove(&id); @@ -633,7 +670,8 @@ impl MessagePool { /// /// Time complexity: `O(log(self.len()))`. pub(super) fn shed_largest_message(&mut self) -> Option<(SomeReference, RequestOrResponse)> { - if let Some((_, id)) = self.size_queue.pop_last() { + if let Some(&(size_bytes, id)) = self.size_queue.max_key() { + self.size_queue.remove(&(size_bytes, id)).unwrap(); debug_assert_eq!(Class::BestEffort, id.class()); let msg = self.take_impl(id).unwrap(); @@ -661,7 +699,7 @@ impl MessagePool { /// `debug_assert!()` checks. /// /// Time complexity: `O(n)`. - fn calculate_message_stats(messages: &BTreeMap) -> MessageStats { + fn calculate_message_stats(messages: &MutableIntMap) -> MessageStats { let mut stats = MessageStats::default(); for (id, msg) in messages.iter() { stats += MessageStats::stats_delta(msg, id.context()); @@ -754,11 +792,14 @@ impl MessagePool { /// Time complexity: `O(n * log(n))`. #[allow(clippy::type_complexity)] fn calculate_priority_queues( - messages: &BTreeMap, - outbound_guaranteed_request_deadlines: &BTreeMap, - ) -> (BTreeSet<(CoarseTime, Id)>, BTreeSet<(usize, Id)>) { - let mut expected_deadline_queue = BTreeSet::new(); - let mut expected_size_queue = BTreeSet::new(); + messages: &MutableIntMap, + outbound_guaranteed_request_deadlines: &MutableIntMap, + ) -> ( + MutableIntMap<(CoarseTime, Id), ()>, + MutableIntMap<(usize, Id), ()>, + ) { + let mut expected_deadline_queue = MutableIntMap::new(); + let mut expected_size_queue = MutableIntMap::new(); messages.iter().for_each(|(id, msg)| { use Class::*; use Context::*; @@ -767,7 +808,7 @@ impl MessagePool { // Outbound guaranteed response requests have (separately recorded) deadlines. (Outbound, GuaranteedResponse, Request) => { let deadline = outbound_guaranteed_request_deadlines.get(id).unwrap(); - expected_deadline_queue.insert((*deadline, *id)); + expected_deadline_queue.insert((*deadline, *id), ()); } // All other guaranteed response messages neither expire nor can be shed. @@ -776,13 +817,13 @@ impl MessagePool { // Inbound best-effort responses don't have expiration deadlines, but can be // shed. (Inbound, BestEffort, Response) => { - expected_size_queue.insert((msg.count_bytes(), *id)); + expected_size_queue.insert((msg.count_bytes(), *id), ()); } // All other best-effort messages are enqueued in both priority queues. (_, BestEffort, _) => { - expected_deadline_queue.insert((msg.deadline(), *id)); - expected_size_queue.insert((msg.count_bytes(), *id)); + expected_deadline_queue.insert((msg.deadline(), *id), ()); + expected_size_queue.insert((msg.count_bytes(), *id), ()); } } }); @@ -821,7 +862,7 @@ impl TryFrom for MessagePool { fn try_from(item: pb_queues::MessagePool) -> Result { let message_count = item.messages.len(); - let messages: BTreeMap<_, _> = item + let messages: MutableIntMap<_, _> = item .messages .into_iter() .map(|entry| { diff --git a/rs/replicated_state/src/canister_state/queues/message_pool/tests.rs b/rs/replicated_state/src/canister_state/queues/message_pool/tests.rs index 3f2ab229095..48e850920f3 100644 --- a/rs/replicated_state/src/canister_state/queues/message_pool/tests.rs +++ b/rs/replicated_state/src/canister_state/queues/message_pool/tests.rs @@ -70,6 +70,9 @@ fn test_insert() { (time(50 + REQUEST_LIFETIME.as_secs() as u32), id5) }, pool.deadline_queue + .iter() + .map(|((t, id), _)| (*t, *id)) + .collect() ); // All best-effort messages should be in the load shedding queue. @@ -102,7 +105,7 @@ fn test_insert_outbound_request_deadline_rounding() { pool.insert_outbound_request(request(NO_DEADLINE).into(), current_time); - assert_eq!(expected_deadline, pool.deadline_queue.first().unwrap().0); + assert_eq!(expected_deadline, pool.deadline_queue.min_key().unwrap().0); } #[test] @@ -233,6 +236,9 @@ fn test_expiration() { (time(40 + REQUEST_LIFETIME.as_secs() as u32), id4) }, pool.deadline_queue + .iter() + .map(|((t, id), _)| (*t, *id)) + .collect() ); // There are expiring messages. assert!(pool.has_expired_deadlines(t_max)); @@ -1028,9 +1034,12 @@ fn time(seconds_since_unix_epoch: u32) -> CoarseTime { CoarseTime::from_secs_since_unix_epoch(seconds_since_unix_epoch) } -fn assert_exact_messages_in_queue(messages: BTreeSet, queue: &BTreeSet<(T, Id)>) { +fn assert_exact_messages_in_queue(messages: BTreeSet, queue: &MutableIntMap<(T, Id), ()>) +where + (T, Id): AsInt, +{ assert_eq!(messages.len(), queue.len()); - assert_eq!(messages, queue.iter().map(|(_, id)| *id).collect()) + assert_eq!(messages, queue.iter().map(|((_, id), ())| *id).collect()) } /// Generates an `InboundReference` for a request of the given class. diff --git a/rs/replicated_state/src/canister_state/queues/queue/tests.rs b/rs/replicated_state/src/canister_state/queues/queue/tests.rs index 0f4e222cb78..7b0e1400687 100644 --- a/rs/replicated_state/src/canister_state/queues/queue/tests.rs +++ b/rs/replicated_state/src/canister_state/queues/queue/tests.rs @@ -218,83 +218,77 @@ fn canister_queue_push_without_reserved_slot_panics() { /// Generator for an arbitrary inbound message reference. fn arbitrary_message_reference() -> impl Strategy + Clone { prop_oneof![ - 1 => any::().prop_map(|gen| new_request_reference(gen, Class::GuaranteedResponse)), - 1 => any::().prop_map(|gen| new_request_reference(gen, Class::BestEffort)), - 1 => any::().prop_map(|gen| new_response_reference(gen, Class::GuaranteedResponse)), - 1 => any::().prop_map(|gen| new_response_reference(gen, Class::BestEffort)), + any::().prop_map(|gen| new_request_reference(gen, Class::GuaranteedResponse)), + any::().prop_map(|gen| new_request_reference(gen, Class::BestEffort)), + any::().prop_map(|gen| new_response_reference(gen, Class::GuaranteedResponse)), + any::().prop_map(|gen| new_response_reference(gen, Class::BestEffort)), ] } -proptest! { - #[test] - fn canister_queue_push_and_pop( - mut references in proptest::collection::vec_deque( - arbitrary_message_reference(), - 10..20, - ) - ) { - // Create a queue with large enough capacity. - let mut queue = InputQueue::new(20); - - // Push all references onto the queue. - for reference in references.iter() { - match reference { - reference if reference.kind() == Kind::Request => { - queue.push_request(*reference); - } - reference => { - queue.try_reserve_response_slot().unwrap(); - queue.push_response(*reference); - } +#[test_strategy::proptest] +fn canister_queue_push_and_pop( + #[strategy(proptest::collection::vec_deque(arbitrary_message_reference(), 10..20))] + mut references: VecDeque, +) { + // Create a queue with large enough capacity. + let mut queue = InputQueue::new(20); + + // Push all references onto the queue. + for reference in references.iter() { + match reference { + reference if reference.kind() == Kind::Request => { + queue.push_request(*reference); + } + reference => { + queue.try_reserve_response_slot().unwrap(); + queue.push_response(*reference); } - prop_assert_eq!(Ok(()), queue.check_invariants()); - } - - // Check the contents of the queue via `peek` and `pop`. - while let Some(r) = queue.peek() { - let reference = references.pop_front(); - prop_assert_eq!(reference, Some(r)); - prop_assert_eq!(reference, queue.pop()); } + prop_assert_eq!(Ok(()), queue.check_invariants()); + } - // All references should have been consumed. - prop_assert!(references.is_empty()); + // Check the contents of the queue via `peek` and `pop`. + while let Some(r) = queue.peek() { + let reference = references.pop_front(); + prop_assert_eq!(reference, Some(r)); + prop_assert_eq!(reference, queue.pop()); } - #[test] - fn encode_roundtrip( - references in proptest::collection::vec_deque( - arbitrary_message_reference(), - 10..20, - ), - reserved_slots in 0..3 - ) { - let mut queue = CanisterQueue::new(DEFAULT_QUEUE_CAPACITY); - - // Push all references onto the queue. - for reference in references.iter() { - match reference { - reference if reference.kind() == Kind::Request => { - queue.push_request(*reference); - } - reference => { - queue.try_reserve_response_slot().unwrap(); - queue.push_response(*reference); - } + // All references should have been consumed. + prop_assert!(references.is_empty()); +} + +#[test_strategy::proptest] +fn encode_roundtrip( + #[strategy(proptest::collection::vec_deque(arbitrary_message_reference(), 10..20))] + references: VecDeque, + #[strategy(0..3)] reserved_slots: i32, +) { + let mut queue = CanisterQueue::new(DEFAULT_QUEUE_CAPACITY); + + // Push all references onto the queue. + for reference in references.iter() { + match reference { + reference if reference.kind() == Kind::Request => { + queue.push_request(*reference); + } + reference => { + queue.try_reserve_response_slot().unwrap(); + queue.push_response(*reference); } - prop_assert_eq!(Ok(()), queue.check_invariants()); - } - // And make `reserved_slots` additional reservations. - for _ in 0..reserved_slots { - queue.try_reserve_response_slot().unwrap(); } prop_assert_eq!(Ok(()), queue.check_invariants()); + } + // And make `reserved_slots` additional reservations. + for _ in 0..reserved_slots { + queue.try_reserve_response_slot().unwrap(); + } + prop_assert_eq!(Ok(()), queue.check_invariants()); - let encoded: pb_queues::CanisterQueue = (&queue).into(); - let decoded = encoded.try_into().unwrap(); + let encoded: pb_queues::CanisterQueue = (&queue).into(); + let decoded = encoded.try_into().unwrap(); - assert_eq!(queue, decoded); - } + prop_assert_eq!(queue, decoded); } #[test] diff --git a/rs/replicated_state/src/canister_state/queues/tests.rs b/rs/replicated_state/src/canister_state/queues/tests.rs index 01dd7dcae96..db572865a24 100644 --- a/rs/replicated_state/src/canister_state/queues/tests.rs +++ b/rs/replicated_state/src/canister_state/queues/tests.rs @@ -49,24 +49,32 @@ impl CanisterQueuesFixture { } } - fn push_input_request(&mut self) -> Result { + fn push_input_request( + &mut self, + deadline: CoarseTime, + ) -> Result { self.queues.push_input( RequestBuilder::default() .sender(self.other) .receiver(self.this) + .deadline(deadline) .build() .into(), LocalSubnet, ) } - fn push_input_response(&mut self) -> Result { + fn push_input_response( + &mut self, + deadline: CoarseTime, + ) -> Result { self.last_callback_id += 1; self.queues.push_input( ResponseBuilder::default() .originator(self.this) .respondent(self.other) .originator_reply_callback(CallbackId::from(self.last_callback_id)) + .deadline(deadline) .build() .into(), LocalSubnet, @@ -91,7 +99,10 @@ impl CanisterQueuesFixture { self.queues.pop_input() } - fn push_output_request(&mut self) -> Result<(), (StateError, Arc)> { + fn push_output_request( + &mut self, + deadline: CoarseTime, + ) -> Result<(), (StateError, Arc)> { self.last_callback_id += 1; self.queues.push_output_request( Arc::new( @@ -99,17 +110,19 @@ impl CanisterQueuesFixture { .sender(self.this) .receiver(self.other) .sender_reply_callback(CallbackId::from(self.last_callback_id)) + .deadline(deadline) .build(), ), UNIX_EPOCH, ) } - fn push_output_response(&mut self) { + fn push_output_response(&mut self, deadline: CoarseTime) { self.queues.push_output_response(Arc::new( ResponseBuilder::default() .originator(self.other) .respondent(self.this) + .deadline(deadline) .build(), )); } @@ -198,7 +211,7 @@ pub fn input_queue_type_from_local_canisters( #[test] fn can_push_output_request() { let mut fixture = CanisterQueuesFixture::new(); - fixture.push_output_request().unwrap(); + fixture.push_output_request(NO_DEADLINE).unwrap(); } /// Cannot push guaranteed response to output queues without having pushed an @@ -207,7 +220,7 @@ fn can_push_output_request() { #[should_panic(expected = "assertion failed: self.guaranteed_response_memory_reservations > 0")] fn cannot_push_output_response_guaranteed_without_input_request() { let mut fixture = CanisterQueuesFixture::new(); - fixture.push_output_response(); + fixture.push_output_response(NO_DEADLINE); } /// Cannot push best-effort response to output queues without having pushed an @@ -229,26 +242,26 @@ fn cannot_push_output_response_best_effort_without_input_request() { fn enqueuing_unexpected_response_does_not_panic() { let mut fixture = CanisterQueuesFixture::new(); // Enqueue a request to create a queue for `other`. - assert!(fixture.push_input_request().unwrap()); + assert!(fixture.push_input_request(NO_DEADLINE).unwrap()); // Now `other` sends an unexpected `Response`. We should return an error, not // panic. - fixture.push_input_response().unwrap_err(); + fixture.push_input_response(NO_DEADLINE).unwrap_err(); } /// Can push response to output queues after pushing input request. #[test] fn can_push_output_response_after_input_request() { let mut fixture = CanisterQueuesFixture::new(); - assert!(fixture.push_input_request().unwrap()); + assert!(fixture.push_input_request(NO_DEADLINE).unwrap()); fixture.pop_input().unwrap(); - fixture.push_output_response(); + fixture.push_output_response(NO_DEADLINE); } /// Can push one request to the induction pool. #[test] fn can_push_input_request() { let mut fixture = CanisterQueuesFixture::new(); - assert!(fixture.push_input_request().unwrap()); + assert!(fixture.push_input_request(NO_DEADLINE).unwrap()); } /// Cannot push response to the induction pool without pushing output @@ -256,7 +269,7 @@ fn can_push_input_request() { #[test] fn cannot_push_input_response_without_output_request() { let mut fixture = CanisterQueuesFixture::new(); - fixture.push_input_response().unwrap_err(); + fixture.push_input_response(NO_DEADLINE).unwrap_err(); } /// Can push response to input queues after pushing request to output @@ -264,9 +277,9 @@ fn cannot_push_input_response_without_output_request() { #[test] fn can_push_input_response_after_output_request() { let mut fixture = CanisterQueuesFixture::new(); - fixture.push_output_request().unwrap(); + fixture.push_output_request(NO_DEADLINE).unwrap(); fixture.pop_output().unwrap(); - assert!(fixture.push_input_response().unwrap()); + assert!(fixture.push_input_response(NO_DEADLINE).unwrap()); } #[test] @@ -346,14 +359,14 @@ fn push_input_response_duplicate_best_effort_response() { #[test] fn test_available_output_request_slots_dont_counts() { let mut fixture = CanisterQueuesFixture::new(); - assert!(fixture.push_input_request().unwrap()); + assert!(fixture.push_input_request(NO_DEADLINE).unwrap()); assert_eq!( DEFAULT_QUEUE_CAPACITY, fixture.available_output_request_slots() ); fixture.pop_input().unwrap(); - fixture.push_output_response(); + fixture.push_output_response(NO_DEADLINE); assert_eq!( DEFAULT_QUEUE_CAPACITY, fixture.available_output_request_slots() @@ -367,7 +380,7 @@ fn test_available_output_request_slots_counts() { let mut fixture = CanisterQueuesFixture::new(); // Check that output request counts. - fixture.push_output_request().unwrap(); + fixture.push_output_request(NO_DEADLINE).unwrap(); assert_eq!( DEFAULT_QUEUE_CAPACITY - 1, fixture.available_output_request_slots() @@ -381,7 +394,7 @@ fn test_available_output_request_slots_counts() { ); // Check that input response counts. - assert!(fixture.push_input_response().unwrap()); + assert!(fixture.push_input_response(NO_DEADLINE).unwrap()); assert_eq!( DEFAULT_QUEUE_CAPACITY - 1, fixture.available_output_request_slots() @@ -395,9 +408,9 @@ fn test_available_output_request_slots_counts_timed_out_output_requests() { let mut fixture = CanisterQueuesFixture::new(); // Need output response to pin timed out request behind. - assert!(fixture.push_input_request().unwrap()); + assert!(fixture.push_input_request(NO_DEADLINE).unwrap()); fixture.pop_input().unwrap(); - fixture.push_output_response(); + fixture.push_output_response(NO_DEADLINE); // All output request slots are still available. assert_eq!( @@ -406,7 +419,7 @@ fn test_available_output_request_slots_counts_timed_out_output_requests() { ); // Push output request, then time it out. - fixture.push_output_request().unwrap(); + fixture.push_output_request(NO_DEADLINE).unwrap(); fixture.time_out_all_messages_with_deadlines(); // Pop the reject response, to isolate the timed out request. @@ -424,18 +437,18 @@ fn test_backpressure_with_timed_out_requests() { let mut fixture = CanisterQueuesFixture::new(); // Need output response to pin timed out requests behind. - assert!(fixture.push_input_request().unwrap()); + assert!(fixture.push_input_request(NO_DEADLINE).unwrap()); fixture.pop_input(); - fixture.push_output_response(); + fixture.push_output_response(NO_DEADLINE); // Push `DEFAULT_QUEUE_CAPACITY` output requests and time them all out. for _ in 0..DEFAULT_QUEUE_CAPACITY { - fixture.push_output_request().unwrap(); + fixture.push_output_request(NO_DEADLINE).unwrap(); } fixture.time_out_all_messages_with_deadlines(); // Check that no new request can be pushed. - assert!(fixture.push_output_request().is_err()); + assert!(fixture.push_output_request(NO_DEADLINE).is_err()); } /// Checks that `available_output_request_slots` counts timed out output @@ -446,7 +459,7 @@ fn test_available_output_request_slots() { // Fill the output queue with requests. for _ in 0..DEFAULT_QUEUE_CAPACITY { - fixture.push_output_request().unwrap(); + fixture.push_output_request(NO_DEADLINE).unwrap(); } // No output request slots are available. assert_eq!(0, fixture.available_output_request_slots()); @@ -475,7 +488,7 @@ fn test_deadline_expired_input() { let mut fixture = CanisterQueuesFixture::new(); // Enqueue a "deadline expired" compact reject response. - fixture.push_output_request().unwrap(); + fixture.push_output_request(NO_DEADLINE).unwrap(); fixture.pop_output().unwrap(); assert_eq!(Ok(true), fixture.try_push_deadline_expired_input()); @@ -524,7 +537,7 @@ fn test_try_push_deadline_expired_input_no_reserved_slot() { let mut fixture = CanisterQueuesFixture::new(); // Enqueue an input request, to create the input queue. - assert!(fixture.push_input_request().unwrap()); + assert!(fixture.push_input_request(NO_DEADLINE).unwrap()); // Pushing a deadline expired input without a reserved slot signals a bug. assert_eq!( @@ -538,9 +551,9 @@ fn test_try_push_deadline_expired_input_with_same_callback_id() { let mut fixture = CanisterQueuesFixture::new(); // Push an input response. - fixture.push_output_request().unwrap(); + fixture.push_output_request(NO_DEADLINE).unwrap(); fixture.pop_output().unwrap(); - assert!(fixture.push_input_response().unwrap()); + assert!(fixture.push_input_response(NO_DEADLINE).unwrap()); // Sanity check. assert_eq!(1, fixture.queues.input_queues_message_count()); @@ -700,7 +713,7 @@ fn test_message_picking_round_robin_on_one_queue() { let mut fixture = CanisterQueuesFixture::new(); assert!(fixture.pop_input().is_none()); for _ in 0..3 { - assert!(fixture.push_input_request().unwrap()); + assert!(fixture.push_input_request(NO_DEADLINE).unwrap()); } for _ in 0..3 { @@ -2213,7 +2226,6 @@ fn test_stats_best_effort() { guaranteed_response_memory_reservations: 0, input_queues_reserved_slots: 1, output_queues_reserved_slots: 1, - transient_stream_guaranteed_responses_size_bytes: 0, }; assert_eq!(expected_queue_stats, queues.queue_stats); // Two best-effort response requests, two best-effort responses. @@ -2275,7 +2287,6 @@ fn test_stats_best_effort() { guaranteed_response_memory_reservations: 0, input_queues_reserved_slots: 0, output_queues_reserved_slots: 1, - transient_stream_guaranteed_responses_size_bytes: 0, }; assert_eq!(expected_queue_stats, queues.queue_stats); // Only one best-effort reject response (the dropped response is no longer in @@ -2369,7 +2380,6 @@ fn test_stats_guaranteed_response() { guaranteed_response_memory_reservations: 2, input_queues_reserved_slots: 1, output_queues_reserved_slots: 1, - transient_stream_guaranteed_responses_size_bytes: 0, }; assert_eq!(expected_queue_stats, queues.queue_stats); // Two guaranteed response requests, two guaranteed responses. @@ -2439,7 +2449,6 @@ fn test_stats_guaranteed_response() { guaranteed_response_memory_reservations: 1, input_queues_reserved_slots: 0, output_queues_reserved_slots: 1, - transient_stream_guaranteed_responses_size_bytes: 0, }; assert_eq!(expected_queue_stats, queues.queue_stats); // And we have all-zero message stats. @@ -2499,7 +2508,6 @@ fn test_stats_oversized_requests() { guaranteed_response_memory_reservations: 2, input_queues_reserved_slots: 2, output_queues_reserved_slots: 2, - transient_stream_guaranteed_responses_size_bytes: 0, }; assert_eq!(expected_queue_stats, queues.queue_stats); // Two best-effort requests, two oversized guaranteed requests, 4 requests in all. @@ -2564,7 +2572,6 @@ fn test_stats_oversized_requests() { guaranteed_response_memory_reservations: 1, input_queues_reserved_slots: 0, output_queues_reserved_slots: 2, - transient_stream_guaranteed_responses_size_bytes: 0, }; assert_eq!(expected_queue_stats, queues.queue_stats); @@ -2657,9 +2664,6 @@ fn test_garbage_collect_restores_defaults() { let mut queues = CanisterQueues::default(); assert_eq!(CanisterQueues::default(), queues); - // Set the transient response size to a non-zero value. - queues.set_stream_guaranteed_responses_size_bytes(123); - // Push and pop an ingress message. queues.push_ingress(IngressBuilder::default().receiver(this).build()); assert!(queues.pop_input().is_some()); @@ -3350,10 +3354,74 @@ mod mainnet_compatibility_tests { fn serialize() { let mut fixture = CanisterQueuesFixture::new_with_ids(CANISTER_ID, OTHER_CANISTER_ID); - assert!(fixture.push_input_request().unwrap()); - fixture.push_output_request().unwrap(); - assert!(fixture.push_input_response().unwrap()); - fixture.push_output_response(); + assert!(fixture.push_input_request(NO_DEADLINE).unwrap()); + fixture.push_output_request(NO_DEADLINE).unwrap(); + assert!(fixture.push_input_response(NO_DEADLINE).unwrap()); + fixture.push_output_response(NO_DEADLINE); + + let pb_queues: pb_queues::CanisterQueues = (&fixture.queues).into(); + let serialized = pb_queues.encode_to_vec(); + + let output_path = std::path::Path::new(OUTPUT_NAME); + File::create(output_path) + .unwrap() + .write_all(&serialized) + .unwrap(); + } + + #[test] + #[ignore] + fn deserialize() { + let serialized = std::fs::read(OUTPUT_NAME).expect("Could not read file"); + let pb_queues = pb_queues::CanisterQueues::decode(&serialized as &[u8]) + .expect("Failed to deserialize the protobuf"); + let queues = CanisterQueues::try_from(( + pb_queues, + &StrictMetrics as &dyn CheckpointLoadingMetrics, + )) + .expect("Failed to convert the protobuf to CanisterQueues"); + let mut fixture = CanisterQueuesFixture { + queues, + this: CANISTER_ID, + other: OTHER_CANISTER_ID, + last_callback_id: 0, + }; + assert_matches!(fixture.pop_input(), Some(CanisterInput::Request(req)) if req.deadline == NO_DEADLINE); + assert_matches!(fixture.pop_input(), Some(CanisterInput::Response(rep)) if rep.deadline == NO_DEADLINE); + assert_eq!(fixture.pop_input(), None); + assert!(!fixture.queues.has_input()); + + assert_matches!(fixture.pop_output(), Some(RequestOrResponse::Request(req)) if req.deadline == NO_DEADLINE); + assert_matches!(fixture.pop_output(), Some(RequestOrResponse::Response(rep)) if rep.deadline == NO_DEADLINE); + assert_eq!(fixture.pop_input(), None); + assert!(!fixture.queues.has_output()); + } + } + + #[cfg(test)] + mod best_effort_test { + + use super::super::*; + use super::*; + + const OUTPUT_NAME: &str = "queues.pbuf"; + const CANISTER_ID: CanisterId = CanisterId::from_u64(42); + const OTHER_CANISTER_ID: CanisterId = CanisterId::from_u64(13); + + #[test] + #[ignore] + fn serialize() { + let mut fixture = CanisterQueuesFixture::new_with_ids(CANISTER_ID, OTHER_CANISTER_ID); + + assert!(fixture.push_input_request(NO_DEADLINE).unwrap()); + fixture.push_output_request(NO_DEADLINE).unwrap(); + assert!(fixture.push_input_response(NO_DEADLINE).unwrap()); + fixture.push_output_response(NO_DEADLINE); + + assert!(fixture.push_input_request(SOME_DEADLINE).unwrap()); + fixture.push_output_request(SOME_DEADLINE).unwrap(); + assert!(fixture.push_input_response(SOME_DEADLINE).unwrap()); + fixture.push_output_response(SOME_DEADLINE); let pb_queues: pb_queues::CanisterQueues = (&fixture.queues).into(); let serialized = pb_queues.encode_to_vec(); @@ -3382,13 +3450,17 @@ mod mainnet_compatibility_tests { other: OTHER_CANISTER_ID, last_callback_id: 0, }; - assert_matches!(fixture.pop_input(), Some(CanisterInput::Request(_))); - assert_matches!(fixture.pop_input(), Some(CanisterInput::Response(_))); + assert_matches!(fixture.pop_input(), Some(CanisterInput::Request(req)) if req.deadline == NO_DEADLINE); + assert_matches!(fixture.pop_input(), Some(CanisterInput::Response(rep)) if rep.deadline == NO_DEADLINE); + assert_matches!(fixture.pop_input(), Some(CanisterInput::Request(req)) if req.deadline == SOME_DEADLINE); + assert_matches!(fixture.pop_input(), Some(CanisterInput::Response(rep)) if rep.deadline == SOME_DEADLINE); assert_eq!(fixture.pop_input(), None); assert!(!fixture.queues.has_input()); - assert_matches!(fixture.pop_output(), Some(RequestOrResponse::Request(_))); - assert_matches!(fixture.pop_output(), Some(RequestOrResponse::Response(_))); + assert_matches!(fixture.pop_output(), Some(RequestOrResponse::Request(req)) if req.deadline == NO_DEADLINE); + assert_matches!(fixture.pop_output(), Some(RequestOrResponse::Response(rep)) if rep.deadline == NO_DEADLINE); + assert_matches!(fixture.pop_output(), Some(RequestOrResponse::Request(req)) if req.deadline == SOME_DEADLINE); + assert_matches!(fixture.pop_output(), Some(RequestOrResponse::Response(rep)) if rep.deadline == SOME_DEADLINE); assert_eq!(fixture.pop_input(), None); assert!(!fixture.queues.has_output()); } diff --git a/rs/replicated_state/src/canister_state/system_state.rs b/rs/replicated_state/src/canister_state/system_state.rs index c00f0ddafbc..e28a39457b9 100644 --- a/rs/replicated_state/src/canister_state/system_state.rs +++ b/rs/replicated_state/src/canister_state/system_state.rs @@ -1,6 +1,11 @@ mod call_context_manager; +mod task_queue; pub mod wasm_chunk_store; +pub use self::task_queue::{ + is_low_wasm_memory_hook_condition_satisfied, OnLowWasmMemoryHookStatus, TaskQueue, +}; + use self::wasm_chunk_store::{WasmChunkStore, WasmChunkStoreMetadata}; pub use super::queues::memory_required_to_push_request; use super::queues::{can_push, CanisterInput}; @@ -13,9 +18,8 @@ use crate::{ }; pub use call_context_manager::{CallContext, CallContextAction, CallContextManager, CallOrigin}; use ic_base_types::NumSeconds; -use ic_config::flag_status::FlagStatus; use ic_error_types::RejectCode; -use ic_interfaces::execution_environment::{ExecutionRoundType, HypervisorError}; +use ic_interfaces::execution_environment::HypervisorError; use ic_logger::{error, ReplicaLogger}; use ic_management_canister_types::{ CanisterChange, CanisterChangeDetails, CanisterChangeOrigin, CanisterStatusType, @@ -279,294 +283,6 @@ impl CanisterHistory { } } -/// `TaskQueue` represents the implementation of queue structure for canister tasks satisfying the following conditions: -/// -/// 1. If there is a `Paused` or `Aborted` task it will be returned first. -/// 2. If an `OnLowWasmMemoryHook` is ready to be executed, it will be returned next. -/// 3. All other tasks will be returned based on the order in which they are added to the queue. -#[derive(Clone, Eq, PartialEq, Debug, Default)] -pub struct TaskQueue { - /// Keeps `PausedExecution`, or `PausedInstallCode`, or `AbortedExecution`, - /// or `AbortedInstallCode` task if there is one. - paused_or_aborted_task: Option, - - /// Status of low_on_wasm_memory hook execution. - on_low_wasm_memory_hook_status: OnLowWasmMemoryHookStatus, - - /// Queue of `Heartbeat` and `GlobalTimer` tasks. - queue: VecDeque, -} - -impl TaskQueue { - pub fn from_checkpoint( - queue: VecDeque, - on_low_wasm_memory_hook_status: OnLowWasmMemoryHookStatus, - canister_id: &CanisterId, - ) -> Self { - let mut mut_queue = queue; - - // Extraction of paused_or_aborted_task from queue will be removed in the follow-up EXC-1752 when - // we introduce CanisterStateBits version of TaskQueue, so the conversion will be implicit. - let paused_or_aborted_task = match mut_queue.front() { - Some(ExecutionTask::AbortedInstallCode { .. }) - | Some(ExecutionTask::PausedExecution { .. }) - | Some(ExecutionTask::PausedInstallCode(_)) - | Some(ExecutionTask::AbortedExecution { .. }) => mut_queue.pop_front(), - Some(ExecutionTask::OnLowWasmMemory) - | Some(ExecutionTask::Heartbeat) - | Some(ExecutionTask::GlobalTimer) - | None => None, - }; - - let queue = TaskQueue { - paused_or_aborted_task, - on_low_wasm_memory_hook_status, - queue: mut_queue, - }; - - // Because paused tasks are not allowed in checkpoint rounds when - // checking dts invariants that is equivalent to disabling dts. - queue.check_dts_invariants( - FlagStatus::Disabled, - ExecutionRoundType::CheckpointRound, - canister_id, - ); - - queue - } - - pub fn front(&self) -> Option<&ExecutionTask> { - self.paused_or_aborted_task.as_ref().or_else(|| { - if self.on_low_wasm_memory_hook_status.is_ready() { - Some(&ExecutionTask::OnLowWasmMemory) - } else { - self.queue.front() - } - }) - } - - pub fn pop_front(&mut self) -> Option { - self.paused_or_aborted_task.take().or_else(|| { - if self.on_low_wasm_memory_hook_status.is_ready() { - self.on_low_wasm_memory_hook_status = OnLowWasmMemoryHookStatus::Executed; - Some(ExecutionTask::OnLowWasmMemory) - } else { - self.queue.pop_front() - } - }) - } - - pub fn remove(&mut self, task: ExecutionTask) { - match task { - ExecutionTask::OnLowWasmMemory => { - self.on_low_wasm_memory_hook_status.update(false); - } - ExecutionTask::Heartbeat - | ExecutionTask::GlobalTimer - | ExecutionTask::AbortedInstallCode { .. } - | ExecutionTask::PausedExecution { .. } - | ExecutionTask::PausedInstallCode(_) - | ExecutionTask::AbortedExecution { .. } => unreachable!( - "Unsuccessful removal of the task {:?}. Removal of task from TaskQueue is only supported for OnLowWasmMemory type.", task - ), - }; - } - - pub fn enqueue(&mut self, task: ExecutionTask) { - match task { - ExecutionTask::AbortedInstallCode { .. } - | ExecutionTask::PausedExecution { .. } - | ExecutionTask::PausedInstallCode(_) - | ExecutionTask::AbortedExecution { .. } => { - debug_assert!(self.paused_or_aborted_task.is_none()); - self.paused_or_aborted_task = Some(task); - } - ExecutionTask::OnLowWasmMemory => { - self.on_low_wasm_memory_hook_status.update(true); - } - ExecutionTask::Heartbeat | ExecutionTask::GlobalTimer => self.queue.push_front(task), - }; - } - - pub fn is_empty(&self) -> bool { - self.paused_or_aborted_task.is_none() - && !self.on_low_wasm_memory_hook_status.is_ready() - && self.queue.is_empty() - } - - pub fn len(&self) -> usize { - self.queue.len() - + self.paused_or_aborted_task.as_ref().map_or(0, |_| 1) - + if self.on_low_wasm_memory_hook_status.is_ready() { - 1 - } else { - 0 - } - } - - /// peek_hook_status will be removed in the follow-up EXC-1752. - pub fn peek_hook_status(&self) -> OnLowWasmMemoryHookStatus { - self.on_low_wasm_memory_hook_status - } - - /// get_queue will be removed in the follow-up EXC-1752. - pub fn get_queue(&self) -> VecDeque { - let mut queue = self.queue.clone(); - if let Some(task) = self.paused_or_aborted_task.as_ref() { - queue.push_front(task.clone()); - } - queue - } - - /// `check_dts_invariants` should only be called after round execution. - /// - /// It checks that the following properties are satisfied: - /// 1. Heartbeat, GlobalTimer tasks exist only during the round and must not exist after the round. - /// 2. Paused executions can exist only in ordinary rounds (not checkpoint rounds). - /// 3. If deterministic time slicing is disabled, then there are no paused tasks. - /// Aborted tasks may still exist if DTS was disabled in recent checkpoints. - pub fn check_dts_invariants( - &self, - deterministic_time_slicing: FlagStatus, - current_round_type: ExecutionRoundType, - id: &CanisterId, - ) { - if let Some(paused_or_aborted_task) = &self.paused_or_aborted_task { - match paused_or_aborted_task { - ExecutionTask::PausedExecution { .. } | ExecutionTask::PausedInstallCode(_) => { - assert_eq!( - current_round_type, - ExecutionRoundType::OrdinaryRound, - "Unexpected paused execution {:?} after a checkpoint round in canister {:?}", - paused_or_aborted_task, - id - ); - - assert_eq!( - deterministic_time_slicing, - FlagStatus::Enabled, - "Unexpected paused execution {:?} with disabled DTS in canister: {:?}", - paused_or_aborted_task, - id - ); - } - ExecutionTask::AbortedExecution { .. } - | ExecutionTask::AbortedInstallCode { .. } => {} - ExecutionTask::Heartbeat - | ExecutionTask::GlobalTimer - | ExecutionTask::OnLowWasmMemory => { - unreachable!( - "Unexpected on task type {:?} in TaskQueue::paused_or_aborted_task in canister {:?} .", paused_or_aborted_task, id - ) - } - } - } - - if let Some(task) = self.queue.front() { - match task { - ExecutionTask::Heartbeat => { - panic!( - "Unexpected heartbeat task after a round in canister {:?}", - id - ); - } - ExecutionTask::GlobalTimer => { - panic!( - "Unexpected global timer task after a round in canister {:?}", - id - ); - } - ExecutionTask::OnLowWasmMemory - | ExecutionTask::AbortedExecution { .. } - | ExecutionTask::AbortedInstallCode { .. } - | ExecutionTask::PausedExecution { .. } - | ExecutionTask::PausedInstallCode(_) => { - unreachable!( - "Unexpected task type {:?} in TaskQueue::queue, after a round in canister {:?}", task, id - ); - } - } - } - } - - /// Removes aborted install code task. - pub fn remove_aborted_install_code_task(&mut self) { - if let Some(ExecutionTask::AbortedInstallCode { .. }) = &self.paused_or_aborted_task { - self.paused_or_aborted_task = None; - } - } - - /// Removes `Heartbeat` and `GlobalTimer` tasks. - pub fn remove_heartbeat_and_global_timer(&mut self) { - for task in self.queue.iter() { - debug_assert!( - *task == ExecutionTask::Heartbeat || *task == ExecutionTask::GlobalTimer, - "Unexpected task type {:?} in TaskQueue::queue.", - task - ); - } - - self.queue.clear(); - } - - /// Returns `PausedExecution` or `PausedInstallCode` task. - pub fn get_paused_task(&self) -> Option<&ExecutionTask> { - if let Some(task) = &self.paused_or_aborted_task { - match task { - ExecutionTask::PausedExecution { .. } | ExecutionTask::PausedInstallCode(_) => { - Some(task) - } - ExecutionTask::AbortedExecution { .. } - | ExecutionTask::AbortedInstallCode { .. } => None, - ExecutionTask::Heartbeat - | ExecutionTask::GlobalTimer - | ExecutionTask::OnLowWasmMemory => unreachable!( - "Unexpected on task type in the in TaskQueue::paused_or_aborted_task." - ), - } - } else { - None - } - } - - /// Replace `PausedExecution` or `PausedInstallCode` with corresponding - /// `AbortedExecution` or `AbortedInstallCode` respectively. - pub fn replace_paused_with_aborted_task(&mut self, aborted_task: ExecutionTask) { - match &aborted_task { - ExecutionTask::AbortedExecution { .. } => assert!( - matches!( - self.paused_or_aborted_task, - Some(ExecutionTask::PausedExecution { .. }) - ), - "Received aborted task {:?} is not compatible with paused task {:?}.", - aborted_task, - self.paused_or_aborted_task - ), - ExecutionTask::AbortedInstallCode { .. } => assert!( - matches!( - self.paused_or_aborted_task, - Some(ExecutionTask::PausedInstallCode(_)) - ), - "Received aborted task {:?} is not compatible with paused task {:?}.", - aborted_task, - self.paused_or_aborted_task - ), - ExecutionTask::Heartbeat - | ExecutionTask::GlobalTimer - | ExecutionTask::OnLowWasmMemory - | ExecutionTask::PausedExecution { .. } - | ExecutionTask::PausedInstallCode(_) => { - unreachable!( - "Unexpected task type {:?} of the aborted task.", - aborted_task - ) - } - }; - - self.paused_or_aborted_task = Some(aborted_task); - } -} - /// State that is controlled and owned by the system (IC). /// /// Contains structs needed for running and maintaining the canister on the IC. @@ -682,65 +398,6 @@ pub struct SystemState { pub snapshots_memory_usage: NumBytes, } -/// A wrapper around the different statuses of `OnLowWasmMemory` hook execution. -#[derive(Clone, Copy, Eq, PartialEq, Debug, Default, Deserialize, Serialize)] -pub enum OnLowWasmMemoryHookStatus { - #[default] - ConditionNotSatisfied, - Ready, - Executed, -} - -impl OnLowWasmMemoryHookStatus { - fn update(&mut self, is_hook_condition_satisfied: bool) { - *self = if is_hook_condition_satisfied { - match *self { - Self::ConditionNotSatisfied | Self::Ready => Self::Ready, - Self::Executed => Self::Executed, - } - } else { - Self::ConditionNotSatisfied - }; - } - - fn is_ready(&self) -> bool { - *self == Self::Ready - } -} - -impl From<&OnLowWasmMemoryHookStatus> for pb::OnLowWasmMemoryHookStatus { - fn from(item: &OnLowWasmMemoryHookStatus) -> Self { - use OnLowWasmMemoryHookStatus::*; - - match *item { - ConditionNotSatisfied => Self::ConditionNotSatisfied, - Ready => Self::Ready, - Executed => Self::Executed, - } - } -} - -impl TryFrom for OnLowWasmMemoryHookStatus { - type Error = ProxyDecodeError; - - fn try_from(value: pb::OnLowWasmMemoryHookStatus) -> Result { - match value { - pb::OnLowWasmMemoryHookStatus::Unspecified => Err(ProxyDecodeError::ValueOutOfRange { - typ: "OnLowWasmMemoryHookStatus", - err: format!( - "Unexpected value of status of on low wasm memory hook: {:?}", - value - ), - }), - pb::OnLowWasmMemoryHookStatus::ConditionNotSatisfied => { - Ok(OnLowWasmMemoryHookStatus::ConditionNotSatisfied) - } - pb::OnLowWasmMemoryHookStatus::Ready => Ok(OnLowWasmMemoryHookStatus::Ready), - pb::OnLowWasmMemoryHookStatus::Executed => Ok(OnLowWasmMemoryHookStatus::Executed), - } - } -} - /// A wrapper around the different canister statuses. #[derive(Clone, Eq, PartialEq, Debug)] pub enum CanisterStatus { @@ -1935,13 +1592,6 @@ impl SystemState { self.canister_history.get_memory_usage() } - /// Sets the (transient) size in bytes of guaranteed responses from this - /// canister routed into streams and not yet garbage collected. - pub(super) fn set_stream_guaranteed_responses_size_bytes(&mut self, size_bytes: usize) { - self.queues - .set_stream_guaranteed_responses_size_bytes(size_bytes); - } - /// Method used only by the dashboard. pub fn collect_controllers_as_string(&self) -> String { self.controllers @@ -2356,6 +2006,41 @@ impl SystemState { _ => None, } } + + /// Enqueues or removes `OnLowWasmMemory` task from `task_queue` + /// depending if the condition for `OnLowWasmMemoryHook` is satisfied: + /// + /// 1. In the case of `memory_allocation` + /// `wasm_memory_threshold >= min(memory_allocation - memory_usage_without_wasm_memory, wasm_memory_limit) - wasm_memory_usage` + /// 2. Without memory allocation + /// `wasm_memory_threshold >= wasm_memory_limit - wasm_memory_usage` + /// + /// Note: if `wasm_memory_limit` is not set, its default value is 4 GiB. + pub fn update_on_low_wasm_memory_hook_status( + &mut self, + memory_usage: NumBytes, + wasm_memory_usage: NumBytes, + ) { + let memory_allocation = match self.memory_allocation { + MemoryAllocation::Reserved(bytes) => Some(bytes), + MemoryAllocation::BestEffort => None, + }; + + let wasm_memory_limit = self.wasm_memory_limit; + let wasm_memory_threshold = self.wasm_memory_threshold; + + if is_low_wasm_memory_hook_condition_satisfied( + memory_usage, + wasm_memory_usage, + memory_allocation, + wasm_memory_limit, + wasm_memory_threshold, + ) { + self.task_queue.enqueue(ExecutionTask::OnLowWasmMemory); + } else { + self.task_queue.remove(ExecutionTask::OnLowWasmMemory); + } + } } /// Implements memory limits verification for pushing a canister-to-canister @@ -2628,259 +2313,3 @@ pub mod testing { }; } } - -#[cfg(test)] -mod tests { - use std::sync::Arc; - - use crate::{ - canister_state::system_state::OnLowWasmMemoryHookStatus, - metadata_state::subnet_call_context_manager::InstallCodeCallId, ExecutionTask, - }; - - use super::{PausedExecutionId, TaskQueue}; - - use ic_test_utilities_types::messages::IngressBuilder; - use ic_types::{ - messages::{CanisterCall, CanisterMessageOrTask, CanisterTask}, - Cycles, - }; - #[test] - fn test_on_low_wasm_memory_hook_start_status_condition_not_satisfied() { - let mut status = OnLowWasmMemoryHookStatus::ConditionNotSatisfied; - status.update(false); - assert_eq!(status, OnLowWasmMemoryHookStatus::ConditionNotSatisfied); - - let mut status = OnLowWasmMemoryHookStatus::ConditionNotSatisfied; - status.update(true); - assert_eq!(status, OnLowWasmMemoryHookStatus::Ready); - } - - #[test] - fn test_on_low_wasm_memory_hook_start_status_ready() { - let mut status = OnLowWasmMemoryHookStatus::Ready; - status.update(false); - assert_eq!(status, OnLowWasmMemoryHookStatus::ConditionNotSatisfied); - - let mut status = OnLowWasmMemoryHookStatus::Ready; - status.update(true); - assert_eq!(status, OnLowWasmMemoryHookStatus::Ready); - } - - #[test] - fn test_on_low_wasm_memory_hook_start_status_executed() { - let mut status = OnLowWasmMemoryHookStatus::Executed; - status.update(false); - assert_eq!(status, OnLowWasmMemoryHookStatus::ConditionNotSatisfied); - - let mut status = OnLowWasmMemoryHookStatus::Executed; - status.update(true); - assert_eq!(status, OnLowWasmMemoryHookStatus::Executed); - } - - #[test] - #[should_panic(expected = "Unexpected task type")] - fn test_replace_paused_with_aborted_task_heartbeat() { - let mut task_queue = TaskQueue::default(); - task_queue.replace_paused_with_aborted_task(ExecutionTask::Heartbeat); - } - - #[test] - #[should_panic(expected = "Unexpected task type")] - fn test_replace_paused_with_aborted_task_global_timer() { - let mut task_queue = TaskQueue::default(); - task_queue.replace_paused_with_aborted_task(ExecutionTask::GlobalTimer); - } - - #[test] - #[should_panic(expected = "Unexpected task type")] - fn test_replace_paused_with_aborted_task_on_low_wasm_memory() { - let mut task_queue = TaskQueue::default(); - task_queue.replace_paused_with_aborted_task(ExecutionTask::OnLowWasmMemory); - } - - #[test] - #[should_panic(expected = "Unexpected task type")] - fn test_replace_paused_with_aborted_task_on_paused_execution() { - let mut task_queue = TaskQueue::default(); - task_queue.replace_paused_with_aborted_task(ExecutionTask::PausedExecution { - id: PausedExecutionId(0), - input: CanisterMessageOrTask::Task(CanisterTask::Heartbeat), - }); - } - - #[test] - #[should_panic(expected = "Unexpected task type")] - fn test_replace_paused_with_aborted_task_on_paused_install_code() { - let mut task_queue = TaskQueue::default(); - task_queue.replace_paused_with_aborted_task(ExecutionTask::PausedInstallCode( - PausedExecutionId(0), - )); - } - - #[test] - #[should_panic(expected = "is not compatible with paused task")] - fn test_replace_paused_with_aborted_task_on_paused_install_code_aborted_execution() { - let mut task_queue = TaskQueue::default(); - task_queue.enqueue(ExecutionTask::PausedInstallCode(PausedExecutionId(0))); - - task_queue.replace_paused_with_aborted_task(ExecutionTask::AbortedExecution { - input: CanisterMessageOrTask::Task(CanisterTask::Heartbeat), - prepaid_execution_cycles: Cycles::zero(), - }); - } - - #[test] - #[should_panic(expected = "is not compatible with paused task")] - fn test_replace_paused_with_aborted_task_on_paused_execution_aborted_install_code() { - let mut task_queue = TaskQueue::default(); - task_queue.enqueue(ExecutionTask::PausedExecution { - id: PausedExecutionId(0), - input: CanisterMessageOrTask::Task(CanisterTask::Heartbeat), - }); - - let ingress = Arc::new(IngressBuilder::new().method_name("test_ingress").build()); - - let aborted_install_code = ExecutionTask::AbortedInstallCode { - message: CanisterCall::Ingress(Arc::clone(&ingress)), - prepaid_execution_cycles: Cycles::new(1), - call_id: InstallCodeCallId::new(0), - }; - - task_queue.replace_paused_with_aborted_task(aborted_install_code); - } - - #[test] - #[should_panic(expected = "Unsuccessful removal of the task")] - fn test_task_queue_remove_heartbeat() { - let mut task_queue = TaskQueue::default(); - task_queue.remove(ExecutionTask::Heartbeat); - } - - #[test] - #[should_panic(expected = "Unsuccessful removal of the task")] - fn test_task_queue_remove_global_timer() { - let mut task_queue = TaskQueue::default(); - task_queue.remove(ExecutionTask::GlobalTimer); - } - - #[test] - #[should_panic(expected = "Unsuccessful removal of the task")] - fn test_task_queue_remove_paused_install_code() { - let mut task_queue = TaskQueue::default(); - task_queue.remove(ExecutionTask::PausedInstallCode(PausedExecutionId(0))); - } - - #[test] - #[should_panic(expected = "Unsuccessful removal of the task")] - fn test_task_queue_remove_paused_execution() { - let mut task_queue = TaskQueue::default(); - task_queue.remove(ExecutionTask::PausedInstallCode(PausedExecutionId(0))); - } - - #[test] - #[should_panic(expected = "Unsuccessful removal of the task")] - fn test_task_queue_remove_aborted_install_code() { - let mut task_queue = TaskQueue::default(); - - let ingress = Arc::new(IngressBuilder::new().method_name("test_ingress").build()); - - task_queue.remove(ExecutionTask::AbortedInstallCode { - message: CanisterCall::Ingress(Arc::clone(&ingress)), - prepaid_execution_cycles: Cycles::new(1), - call_id: InstallCodeCallId::new(0), - }); - } - - #[test] - #[should_panic(expected = "Unsuccessful removal of the task")] - fn test_task_queue_remove_aborted_execution() { - let mut task_queue = TaskQueue::default(); - task_queue.remove(ExecutionTask::AbortedExecution { - input: CanisterMessageOrTask::Task(CanisterTask::Heartbeat), - prepaid_execution_cycles: Cycles::zero(), - }); - } - - #[test] - fn test_task_queue_remove_on_low_wasm_memory_hook() { - let mut task_queue = TaskQueue::default(); - assert!(task_queue.is_empty()); - - // Queue is empty, so remove should be no_op. - task_queue.remove(ExecutionTask::OnLowWasmMemory); - assert!(task_queue.is_empty()); - - // ExecutionTask::OnLowWasmMemory is added to queue. - task_queue.enqueue(ExecutionTask::OnLowWasmMemory); - assert_eq!(task_queue.len(), 1); - assert_eq!(task_queue.front(), Some(&ExecutionTask::OnLowWasmMemory)); - - // After removing queue is empty. - task_queue.remove(ExecutionTask::OnLowWasmMemory); - assert!(task_queue.is_empty()); - - // ExecutionTask::OnLowWasmMemory can be added to the queue again. - task_queue.enqueue(ExecutionTask::OnLowWasmMemory); - assert_eq!(task_queue.len(), 1); - assert_eq!(task_queue.front(), Some(&ExecutionTask::OnLowWasmMemory)); - } - - #[test] - fn test_task_queue_pop_front_on_low_wasm_memory() { - let mut task_queue = TaskQueue::default(); - - // `ExecutionTask::OnLowWasmMemory` is added to queue. - task_queue.enqueue(ExecutionTask::OnLowWasmMemory); - assert_eq!(task_queue.len(), 1); - - assert_eq!(task_queue.pop_front(), Some(ExecutionTask::OnLowWasmMemory)); - assert!(task_queue.is_empty()); - - // After `pop` of `OnLowWasmMemory` from queue `OnLowWasmMemoryHookStatus` - // will be `Executed` so `enqueue` of `OnLowWasmMemory` is no-op. - task_queue.enqueue(ExecutionTask::OnLowWasmMemory); - assert!(task_queue.is_empty()); - - // After removing `OnLowWasmMemory` from queue `OnLowWasmMemoryHookStatus` - // will become `ConditionNotSatisfied`. - task_queue.remove(ExecutionTask::OnLowWasmMemory); - assert!(task_queue.is_empty()); - - // So now `enqueue` of `OnLowWasmMemory` will set `OnLowWasmMemoryHookStatus` - // to `Ready`. - task_queue.enqueue(ExecutionTask::OnLowWasmMemory); - assert_eq!(task_queue.len(), 1); - - assert_eq!(task_queue.pop_front(), Some(ExecutionTask::OnLowWasmMemory)); - } - - #[test] - fn test_task_queue_test_enqueue() { - let mut task_queue = TaskQueue::default(); - assert!(task_queue.is_empty()); - - task_queue.enqueue(ExecutionTask::Heartbeat); - task_queue.enqueue(ExecutionTask::PausedInstallCode(PausedExecutionId(0))); - task_queue.enqueue(ExecutionTask::GlobalTimer); - task_queue.enqueue(ExecutionTask::OnLowWasmMemory); - - assert!(!task_queue.is_empty()); - assert_eq!(task_queue.len(), 4); - - // Disregarding order of `enqueue` operations, if there is - // paused task, it should be returned the first. - assert_eq!( - task_queue.pop_front(), - Some(ExecutionTask::PausedInstallCode(PausedExecutionId(0))) - ); - - // Disregarding order of `enqueue` operations, if there is OnLowWasmMemory - // task, it should be returned right after paused or aborted task if there is one. - assert_eq!(task_queue.pop_front(), Some(ExecutionTask::OnLowWasmMemory)); - - // The rest of the tasks should be returned in the LIFO order. - assert_eq!(task_queue.pop_front(), Some(ExecutionTask::GlobalTimer)); - assert_eq!(task_queue.pop_front(), Some(ExecutionTask::Heartbeat)); - } -} diff --git a/rs/replicated_state/src/canister_state/system_state/call_context_manager.rs b/rs/replicated_state/src/canister_state/system_state/call_context_manager.rs index eb79a67cf09..3982278d4bf 100644 --- a/rs/replicated_state/src/canister_state/system_state/call_context_manager.rs +++ b/rs/replicated_state/src/canister_state/system_state/call_context_manager.rs @@ -1,6 +1,7 @@ #[cfg(test)] mod tests; +use crate::page_map::int_map::{AsInt, MutableIntMap}; use ic_interfaces::execution_environment::HypervisorError; use ic_management_canister_types::IC_00; use ic_protobuf::proxy::{try_from_option_field, ProxyDecodeError}; @@ -18,12 +19,14 @@ use ic_types::{ PrincipalId, Time, UserId, }; use serde::{Deserialize, Serialize}; -use std::collections::btree_map::Entry; -use std::collections::{BTreeMap, BTreeSet}; +use std::collections::BTreeSet; use std::convert::{From, TryFrom, TryInto}; use std::sync::Arc; use std::time::Duration; +#[cfg(test)] +use std::collections::BTreeMap; + /// Contains all context information related to an incoming call. #[derive(Clone, Eq, PartialEq, Debug)] pub struct CallContext { @@ -277,8 +280,8 @@ impl CallContextManagerStats { /// /// Time complexity: `O(n)`. pub(crate) fn calculate_stats( - call_contexts: &BTreeMap, - callbacks: &BTreeMap>, + call_contexts: &MutableIntMap, + callbacks: &MutableIntMap>, ) -> CallContextManagerStats { let unresponded_canister_update_call_contexts = call_contexts .values() @@ -320,11 +323,13 @@ impl CallContextManagerStats { /// (since this response was just delivered). /// /// Time complexity: `O(n)`. - #[allow(dead_code)] + #[cfg(test)] pub(crate) fn calculate_unresponded_callbacks_per_respondent( - callbacks: &BTreeMap>, + callbacks: &MutableIntMap>, aborted_or_paused_response: Option<&Response>, ) -> BTreeMap { + use std::collections::btree_map::Entry; + let mut callback_counts = callbacks.values().fold( BTreeMap::::new(), |mut counts, callback| { @@ -365,9 +370,9 @@ impl CallContextManagerStats { /// plus one for a paused or aborted canister request execution, if any. /// /// Time complexity: `O(n)`. - #[allow(dead_code)] + #[cfg(test)] pub(crate) fn calculate_unresponded_call_contexts_per_originator( - call_contexts: &BTreeMap, + call_contexts: &MutableIntMap, aborted_or_paused_request: Option<&Request>, ) -> BTreeMap { let mut unresponded_canister_update_call_contexts = call_contexts @@ -418,11 +423,14 @@ pub struct CallContextManager { next_callback_id: u64, /// Call contexts (including deleted ones) that still have open callbacks. - call_contexts: BTreeMap, + call_contexts: MutableIntMap, + + /// Counts of open callbacks per call context. + outstanding_callbacks: MutableIntMap, /// Callbacks still awaiting response, plus the callback of the currently /// paused or aborted DTS response execution, if any. - callbacks: BTreeMap>, + callbacks: MutableIntMap>, /// Callback deadline priority queue. Holds all not-yet-expired best-effort /// callbacks, ordered by deadline. `CallbackIds` break ties, ensuring @@ -430,7 +438,7 @@ pub struct CallContextManager { /// /// When a `CallbackId` is returned by `expired_callbacks()`, it is removed from /// the queue. This ensures that each callback is expired at most once. - unexpired_callbacks: BTreeSet<(CoarseTime, CallbackId)>, + unexpired_callbacks: MutableIntMap<(CoarseTime, CallbackId), ()>, /// Guaranteed response and overall callback and call context stats. stats: CallContextManagerStats, @@ -575,7 +583,7 @@ impl CallContextManager { /// Returns the currently open `CallContexts` maintained by this /// `CallContextManager`. - pub fn call_contexts(&self) -> &BTreeMap { + pub fn call_contexts(&self) -> &MutableIntMap { &self.call_contexts } @@ -594,18 +602,21 @@ impl CallContextManager { call_context_id: CallContextId, cycles: Cycles, ) -> Result<&CallContext, &str> { - let call_context = self + let mut call_context = self .call_contexts - .get_mut(&call_context_id) + .remove(&call_context_id) .ok_or("Canister accepted cycles from invalid call context")?; - call_context - .withdraw_cycles(cycles) - .map_err(|_| "Canister accepted more cycles than available from call context")?; - Ok(call_context) + let res = call_context.withdraw_cycles(cycles); + self.call_contexts.insert(call_context_id, call_context); + + match res { + Ok(()) => Ok(self.call_contexts.get(&call_context_id).unwrap()), + Err(()) => Err("Canister accepted more cycles than available from call context"), + } } /// Returns the `Callback`s maintained by this `CallContextManager`. - pub fn callbacks(&self) -> &BTreeMap> { + pub fn callbacks(&self) -> &MutableIntMap> { &self.callbacks } @@ -642,9 +653,9 @@ impl CallContextManager { OutstandingCalls::No }; - let context = self + let mut context = self .call_contexts - .get_mut(&call_context_id) + .remove(&call_context_id) .unwrap_or_else(|| panic!("no call context with ID={}", call_context_id)); // Update call context `instructions_executed += instructions_used` context.instructions_executed = context @@ -663,65 +674,56 @@ impl CallContextManager { let (action, call_context) = match (result, responded, outstanding_calls) { (Ok(None), Responded::No, OutstandingCalls::Yes) | (Err(_), Responded::No, OutstandingCalls::Yes) => { + self.call_contexts.insert(call_context_id, context); (CallContextAction::NotYetResponded, None) } (Ok(None), Responded::Yes, OutstandingCalls::Yes) | (Err(_), Responded::Yes, OutstandingCalls::Yes) => { + self.call_contexts.insert(call_context_id, context); (CallContextAction::AlreadyResponded, None) } (Ok(None), Responded::Yes, OutstandingCalls::No) - | (Err(_), Responded::Yes, OutstandingCalls::No) => ( - CallContextAction::AlreadyResponded, - self.call_contexts.remove(&call_context_id), - ), + | (Err(_), Responded::Yes, OutstandingCalls::No) => { + (CallContextAction::AlreadyResponded, Some(context)) + } (Ok(None), Responded::No, OutstandingCalls::No) => { self.stats.on_call_context_response(&context.call_origin); let refund = context.available_cycles; - ( - CallContextAction::NoResponse { refund }, - self.call_contexts.remove(&call_context_id), - ) + (CallContextAction::NoResponse { refund }, Some(context)) } (Ok(Some(WasmResult::Reply(payload))), Responded::No, OutstandingCalls::No) => { self.stats.on_call_context_response(&context.call_origin); let refund = context.available_cycles; - ( - CallContextAction::Reply { payload, refund }, - self.call_contexts.remove(&call_context_id), - ) + (CallContextAction::Reply { payload, refund }, Some(context)) } (Ok(Some(WasmResult::Reply(payload))), Responded::No, OutstandingCalls::Yes) => { self.stats.on_call_context_response(&context.call_origin); let refund = context.available_cycles; context.mark_responded(); + self.call_contexts.insert(call_context_id, context); (CallContextAction::Reply { payload, refund }, None) } (Ok(Some(WasmResult::Reject(payload))), Responded::No, OutstandingCalls::No) => { self.stats.on_call_context_response(&context.call_origin); let refund = context.available_cycles; - ( - CallContextAction::Reject { payload, refund }, - self.call_contexts.remove(&call_context_id), - ) + (CallContextAction::Reject { payload, refund }, Some(context)) } (Ok(Some(WasmResult::Reject(payload))), Responded::No, OutstandingCalls::Yes) => { self.stats.on_call_context_response(&context.call_origin); let refund = context.available_cycles; context.mark_responded(); + self.call_contexts.insert(call_context_id, context); (CallContextAction::Reject { payload, refund }, None) } (Err(error), Responded::No, OutstandingCalls::No) => { self.stats.on_call_context_response(&context.call_origin); let refund = context.available_cycles; - ( - CallContextAction::Fail { error, refund }, - self.call_contexts.remove(&call_context_id), - ) + (CallContextAction::Fail { error, refund }, Some(context)) } // The following can never happen since we handle at the SystemApi level if a canister @@ -748,18 +750,17 @@ impl CallContextManager { // TODO: Remove, this is only used in tests. #[cfg(test)] fn mark_responded(&mut self, call_context_id: CallContextId) -> Result<(), String> { - let call_context = self + let mut call_context = self .call_contexts - .get_mut(&call_context_id) + .remove(&call_context_id) .ok_or(format!("Call context not found: {}", call_context_id))?; - if call_context.responded { - return Ok(()); - } - - call_context.mark_responded(); + if !call_context.responded { + call_context.mark_responded(); - self.stats - .on_call_context_response(&call_context.call_origin); + self.stats + .on_call_context_response(&call_context.call_origin); + } + self.call_contexts.insert(call_context_id, call_context); debug_assert!(self.stats_ok()); Ok(()) @@ -773,10 +774,21 @@ impl CallContextManager { self.stats.on_register_callback(&callback); if callback.deadline != NO_DEADLINE { self.unexpired_callbacks - .insert((callback.deadline, callback_id)); + .insert((callback.deadline, callback_id), ()); } + self.outstanding_callbacks.insert( + callback.call_context_id, + self.outstanding_callbacks + .get(&callback.call_context_id) + .unwrap_or(&0) + + 1, + ); self.callbacks.insert(callback_id, Arc::new(callback)); + debug_assert_eq!( + calculate_outstanding_callbacks(&self.callbacks), + self.outstanding_callbacks + ); debug_assert!(self.stats_ok()); callback_id @@ -786,11 +798,27 @@ impl CallContextManager { /// the callback and return it. pub(super) fn unregister_callback(&mut self, callback_id: CallbackId) -> Option> { self.callbacks.remove(&callback_id).inspect(|callback| { + let outstanding_callbacks = *self + .outstanding_callbacks + .get(&callback.call_context_id) + .unwrap_or(&0); + if outstanding_callbacks <= 1 { + self.outstanding_callbacks.remove(&callback.call_context_id); + } else { + self.outstanding_callbacks + .insert(callback.call_context_id, outstanding_callbacks - 1); + } + self.stats.on_unregister_callback(callback); if callback.deadline != NO_DEADLINE { self.unexpired_callbacks .remove(&(callback.deadline, callback_id)); } + + debug_assert_eq!( + calculate_outstanding_callbacks(&self.callbacks), + self.outstanding_callbacks + ); debug_assert!(self.stats_ok()); }) } @@ -799,7 +827,7 @@ impl CallContextManager { /// whose deadlines are `< now`. pub(super) fn has_expired_callbacks(&self, now: CoarseTime) -> bool { self.unexpired_callbacks - .first() + .min_key() .map(|(deadline, _)| *deadline < now) .unwrap_or(false) } @@ -819,7 +847,7 @@ impl CallContextManager { expired_callbacks .into_iter() - .map(|(_, callback_id)| callback_id) + .map(|((_, callback_id), ())| callback_id) } /// Returns the call origin, which is either the message ID of the ingress @@ -839,14 +867,11 @@ impl CallContextManager { } /// Returns the number of outstanding calls for a given call context. - // - // TODO: This could be made more efficient by tracking the callback count per - // call context in a map. pub fn outstanding_calls(&self, call_context_id: CallContextId) -> usize { - self.callbacks - .iter() - .filter(|(_, callback)| callback.call_context_id == call_context_id) - .count() + *self + .outstanding_callbacks + .get(&call_context_id) + .unwrap_or(&0) } /// Expose the `next_callback_id` field so that the canister sandbox can @@ -954,7 +979,9 @@ impl CallContextManager { // subset of all best-effort callbacks. let all_callback_deadlines = calculate_callback_deadlines(&self.callbacks); debug_assert!( - all_callback_deadlines.is_superset(&self.unexpired_callbacks), + self.unexpired_callbacks + .iter() + .all(|(key, ())| all_callback_deadlines.contains(key)), "unexpired_callbacks: {:?}, all_callback_deadlines: {:?}", self.unexpired_callbacks, all_callback_deadlines @@ -972,21 +999,26 @@ impl CallContextManager { ) -> Vec { let mut reject_responses = Vec::new(); - for call_context in self.call_contexts.values_mut() { - if !call_context.has_responded() { - // Generate a reject response. - if let Some(response) = reject(call_context) { - reject_responses.push(response) - } + let call_contexts = std::mem::take(&mut self.call_contexts); + self.call_contexts = call_contexts + .into_iter() + .map(|(id, mut call_context)| { + if !call_context.has_responded() { + // Generate a reject response. + if let Some(response) = reject(&call_context) { + reject_responses.push(response) + } - call_context.mark_responded(); - self.stats - .on_call_context_response(&call_context.call_origin); - } + call_context.mark_responded(); + self.stats + .on_call_context_response(&call_context.call_origin); + } - // Mark the call context as deleted. - call_context.mark_deleted(); - } + // Mark the call context as deleted. + call_context.mark_deleted(); + (id, call_context) + }) + .collect(); debug_assert!(self.stats_ok()); reject_responses @@ -1041,7 +1073,7 @@ impl From<&CallContextManager> for pb::CallContextManager { unexpired_callbacks: item .unexpired_callbacks .iter() - .map(|(_, id)| id.get()) + .map(|((_, id), ())| id.get()) .collect(), } } @@ -1050,8 +1082,8 @@ impl From<&CallContextManager> for pb::CallContextManager { impl TryFrom for CallContextManager { type Error = ProxyDecodeError; fn try_from(value: pb::CallContextManager) -> Result { - let mut call_contexts = BTreeMap::::new(); - let mut callbacks = BTreeMap::>::new(); + let mut call_contexts = MutableIntMap::::new(); + let mut callbacks = MutableIntMap::>::new(); for pb::CallContextEntry { call_context_id, call_context, @@ -1075,6 +1107,7 @@ impl TryFrom for CallContextManager { )?), ); } + let outstanding_callbacks = calculate_outstanding_callbacks(&callbacks); let unexpired_callbacks = value .unexpired_callbacks .into_iter() @@ -1086,7 +1119,7 @@ impl TryFrom for CallContextManager { callback_id )) })?; - Ok((callback.deadline, callback_id)) + Ok(((callback.deadline, callback_id), ())) }) .collect::>()?; let stats = CallContextManagerStats::calculate_stats(&call_contexts, &callbacks); @@ -1095,6 +1128,7 @@ impl TryFrom for CallContextManager { next_call_context_id: value.next_call_context_id, next_callback_id: value.next_callback_id, call_contexts, + outstanding_callbacks, callbacks, unexpired_callbacks, stats, @@ -1109,7 +1143,7 @@ impl TryFrom for CallContextManager { /// /// Time complexity: `O(n)`. fn calculate_callback_deadlines( - callbacks: &BTreeMap>, + callbacks: &MutableIntMap>, ) -> BTreeSet<(CoarseTime, CallbackId)> { callbacks .iter() @@ -1118,6 +1152,36 @@ fn calculate_callback_deadlines( .collect() } +/// Calculates the counts of callbacks per call context. +/// +/// Time complexity: `O(n)`. +fn calculate_outstanding_callbacks( + callbacks: &MutableIntMap>, +) -> MutableIntMap { + callbacks + .iter() + .map(|(_, callback)| callback.call_context_id) + .fold( + MutableIntMap::::new(), + |mut counts, call_context_id| { + counts.insert( + call_context_id, + counts.get(&call_context_id).unwrap_or(&0) + 1, + ); + counts + }, + ) +} + +impl AsInt for (CoarseTime, CallbackId) { + type Repr = u128; + + #[inline] + fn as_int(&self) -> u128 { + (self.0.as_secs_since_unix_epoch() as u128) << 64 | self.1.get() as u128 + } +} + pub mod testing { use super::{CallContext, CallContextManager}; use ic_types::messages::CallContextId; diff --git a/rs/replicated_state/src/canister_state/system_state/task_queue.rs b/rs/replicated_state/src/canister_state/system_state/task_queue.rs new file mode 100644 index 00000000000..db5221e93a2 --- /dev/null +++ b/rs/replicated_state/src/canister_state/system_state/task_queue.rs @@ -0,0 +1,672 @@ +use crate::ExecutionTask; +use ic_config::flag_status::FlagStatus; +use ic_interfaces::execution_environment::ExecutionRoundType; +use ic_protobuf::proxy::ProxyDecodeError; +use ic_protobuf::state::canister_state_bits::v1 as pb; +use ic_types::CanisterId; +use ic_types::NumBytes; +use serde::{Deserialize, Serialize}; +use std::collections::VecDeque; + +/// A wrapper around the different statuses of `OnLowWasmMemory` hook execution. +#[derive(Clone, Copy, Eq, PartialEq, Debug, Default, Deserialize, Serialize)] +pub enum OnLowWasmMemoryHookStatus { + #[default] + ConditionNotSatisfied, + Ready, + Executed, +} + +impl OnLowWasmMemoryHookStatus { + pub(crate) fn update(&mut self, is_hook_condition_satisfied: bool) { + *self = if is_hook_condition_satisfied { + match *self { + Self::ConditionNotSatisfied | Self::Ready => Self::Ready, + Self::Executed => Self::Executed, + } + } else { + Self::ConditionNotSatisfied + }; + } + + fn is_ready(&self) -> bool { + *self == Self::Ready + } +} + +impl From<&OnLowWasmMemoryHookStatus> for pb::OnLowWasmMemoryHookStatus { + fn from(item: &OnLowWasmMemoryHookStatus) -> Self { + use OnLowWasmMemoryHookStatus::*; + + match *item { + ConditionNotSatisfied => Self::ConditionNotSatisfied, + Ready => Self::Ready, + Executed => Self::Executed, + } + } +} + +impl TryFrom for OnLowWasmMemoryHookStatus { + type Error = ProxyDecodeError; + + fn try_from(value: pb::OnLowWasmMemoryHookStatus) -> Result { + match value { + pb::OnLowWasmMemoryHookStatus::Unspecified => Err(ProxyDecodeError::ValueOutOfRange { + typ: "OnLowWasmMemoryHookStatus", + err: format!( + "Unexpected value of status of on low wasm memory hook: {:?}", + value + ), + }), + pb::OnLowWasmMemoryHookStatus::ConditionNotSatisfied => { + Ok(OnLowWasmMemoryHookStatus::ConditionNotSatisfied) + } + pb::OnLowWasmMemoryHookStatus::Ready => Ok(OnLowWasmMemoryHookStatus::Ready), + pb::OnLowWasmMemoryHookStatus::Executed => Ok(OnLowWasmMemoryHookStatus::Executed), + } + } +} + +/// `TaskQueue` represents the implementation of queue structure for canister tasks satisfying the following conditions: +/// +/// 1. If there is a `Paused` or `Aborted` task it will be returned first. +/// 2. If an `OnLowWasmMemoryHook` is ready to be executed, it will be returned next. +/// 3. All other tasks will be returned based on the order in which they are added to the queue. +#[derive(Clone, Eq, PartialEq, Debug, Default)] +pub struct TaskQueue { + /// Keeps `PausedExecution`, or `PausedInstallCode`, or `AbortedExecution`, + /// or `AbortedInstallCode` task if there is one. + paused_or_aborted_task: Option, + + /// Status of low_on_wasm_memory hook execution. + on_low_wasm_memory_hook_status: OnLowWasmMemoryHookStatus, + + /// Queue of `Heartbeat` and `GlobalTimer` tasks. + queue: VecDeque, +} + +impl TaskQueue { + pub fn from_checkpoint( + queue: VecDeque, + on_low_wasm_memory_hook_status: OnLowWasmMemoryHookStatus, + canister_id: &CanisterId, + ) -> Self { + let mut mut_queue = queue; + + // Extraction of paused_or_aborted_task from queue will be removed in the follow-up EXC-1752 when + // we introduce CanisterStateBits version of TaskQueue, so the conversion will be implicit. + let paused_or_aborted_task = match mut_queue.front() { + Some(ExecutionTask::AbortedInstallCode { .. }) + | Some(ExecutionTask::PausedExecution { .. }) + | Some(ExecutionTask::PausedInstallCode(_)) + | Some(ExecutionTask::AbortedExecution { .. }) => mut_queue.pop_front(), + Some(ExecutionTask::OnLowWasmMemory) + | Some(ExecutionTask::Heartbeat) + | Some(ExecutionTask::GlobalTimer) + | None => None, + }; + + let queue = TaskQueue { + paused_or_aborted_task, + on_low_wasm_memory_hook_status, + queue: mut_queue, + }; + + // Because paused tasks are not allowed in checkpoint rounds when + // checking dts invariants that is equivalent to disabling dts. + queue.check_dts_invariants( + FlagStatus::Disabled, + ExecutionRoundType::CheckpointRound, + canister_id, + ); + + queue + } + + pub fn front(&self) -> Option<&ExecutionTask> { + self.paused_or_aborted_task.as_ref().or_else(|| { + if self.on_low_wasm_memory_hook_status.is_ready() { + Some(&ExecutionTask::OnLowWasmMemory) + } else { + self.queue.front() + } + }) + } + + pub fn pop_front(&mut self) -> Option { + self.paused_or_aborted_task.take().or_else(|| { + if self.on_low_wasm_memory_hook_status.is_ready() { + self.on_low_wasm_memory_hook_status = OnLowWasmMemoryHookStatus::Executed; + Some(ExecutionTask::OnLowWasmMemory) + } else { + self.queue.pop_front() + } + }) + } + + pub fn remove(&mut self, task: ExecutionTask) { + match task { + ExecutionTask::OnLowWasmMemory => { + self.on_low_wasm_memory_hook_status.update(false); + } + ExecutionTask::Heartbeat + | ExecutionTask::GlobalTimer + | ExecutionTask::AbortedInstallCode { .. } + | ExecutionTask::PausedExecution { .. } + | ExecutionTask::PausedInstallCode(_) + | ExecutionTask::AbortedExecution { .. } => unreachable!( + "Unsuccessful removal of the task {:?}. Removal of task from TaskQueue is only supported for OnLowWasmMemory type.", task + ), + }; + } + + pub fn enqueue(&mut self, task: ExecutionTask) { + match task { + ExecutionTask::AbortedInstallCode { .. } + | ExecutionTask::PausedExecution { .. } + | ExecutionTask::PausedInstallCode(_) + | ExecutionTask::AbortedExecution { .. } => { + debug_assert!(self.paused_or_aborted_task.is_none()); + self.paused_or_aborted_task = Some(task); + } + ExecutionTask::OnLowWasmMemory => { + self.on_low_wasm_memory_hook_status.update(true); + } + ExecutionTask::Heartbeat | ExecutionTask::GlobalTimer => self.queue.push_front(task), + }; + } + + pub fn is_empty(&self) -> bool { + self.paused_or_aborted_task.is_none() + && !self.on_low_wasm_memory_hook_status.is_ready() + && self.queue.is_empty() + } + + pub fn len(&self) -> usize { + self.queue.len() + + self.paused_or_aborted_task.as_ref().map_or(0, |_| 1) + + if self.on_low_wasm_memory_hook_status.is_ready() { + 1 + } else { + 0 + } + } + + /// peek_hook_status will be removed in the follow-up EXC-1752. + pub fn peek_hook_status(&self) -> OnLowWasmMemoryHookStatus { + self.on_low_wasm_memory_hook_status + } + + /// get_queue will be removed in the follow-up EXC-1752. + pub fn get_queue(&self) -> VecDeque { + let mut queue = self.queue.clone(); + if let Some(task) = self.paused_or_aborted_task.as_ref() { + queue.push_front(task.clone()); + } + queue + } + + /// `check_dts_invariants` should only be called after round execution. + /// + /// It checks that the following properties are satisfied: + /// 1. Heartbeat, GlobalTimer tasks exist only during the round and must not exist after the round. + /// 2. Paused executions can exist only in ordinary rounds (not checkpoint rounds). + /// 3. If deterministic time slicing is disabled, then there are no paused tasks. + /// Aborted tasks may still exist if DTS was disabled in recent checkpoints. + pub fn check_dts_invariants( + &self, + deterministic_time_slicing: FlagStatus, + current_round_type: ExecutionRoundType, + id: &CanisterId, + ) { + if let Some(paused_or_aborted_task) = &self.paused_or_aborted_task { + match paused_or_aborted_task { + ExecutionTask::PausedExecution { .. } | ExecutionTask::PausedInstallCode(_) => { + assert_eq!( + current_round_type, + ExecutionRoundType::OrdinaryRound, + "Unexpected paused execution {:?} after a checkpoint round in canister {:?}", + paused_or_aborted_task, + id + ); + + assert_eq!( + deterministic_time_slicing, + FlagStatus::Enabled, + "Unexpected paused execution {:?} with disabled DTS in canister: {:?}", + paused_or_aborted_task, + id + ); + } + ExecutionTask::AbortedExecution { .. } + | ExecutionTask::AbortedInstallCode { .. } => {} + ExecutionTask::Heartbeat + | ExecutionTask::GlobalTimer + | ExecutionTask::OnLowWasmMemory => { + unreachable!( + "Unexpected on task type {:?} in TaskQueue::paused_or_aborted_task in canister {:?} .", paused_or_aborted_task, id + ) + } + } + } + + if let Some(task) = self.queue.front() { + match task { + ExecutionTask::Heartbeat => { + panic!( + "Unexpected heartbeat task after a round in canister {:?}", + id + ); + } + ExecutionTask::GlobalTimer => { + panic!( + "Unexpected global timer task after a round in canister {:?}", + id + ); + } + ExecutionTask::OnLowWasmMemory + | ExecutionTask::AbortedExecution { .. } + | ExecutionTask::AbortedInstallCode { .. } + | ExecutionTask::PausedExecution { .. } + | ExecutionTask::PausedInstallCode(_) => { + unreachable!( + "Unexpected task type {:?} in TaskQueue::queue, after a round in canister {:?}", task, id + ); + } + } + } + } + + /// Removes aborted install code task. + pub fn remove_aborted_install_code_task(&mut self) { + if let Some(ExecutionTask::AbortedInstallCode { .. }) = &self.paused_or_aborted_task { + self.paused_or_aborted_task = None; + } + } + + /// Removes `Heartbeat` and `GlobalTimer` tasks. + pub fn remove_heartbeat_and_global_timer(&mut self) { + for task in self.queue.iter() { + debug_assert!( + *task == ExecutionTask::Heartbeat || *task == ExecutionTask::GlobalTimer, + "Unexpected task type {:?} in TaskQueue::queue.", + task + ); + } + + self.queue.clear(); + } + + /// Returns `PausedExecution` or `PausedInstallCode` task. + pub fn get_paused_task(&self) -> Option<&ExecutionTask> { + if let Some(task) = &self.paused_or_aborted_task { + match task { + ExecutionTask::PausedExecution { .. } | ExecutionTask::PausedInstallCode(_) => { + Some(task) + } + ExecutionTask::AbortedExecution { .. } + | ExecutionTask::AbortedInstallCode { .. } => None, + ExecutionTask::Heartbeat + | ExecutionTask::GlobalTimer + | ExecutionTask::OnLowWasmMemory => unreachable!( + "Unexpected on task type in the in TaskQueue::paused_or_aborted_task." + ), + } + } else { + None + } + } + + /// Replace `PausedExecution` or `PausedInstallCode` with corresponding + /// `AbortedExecution` or `AbortedInstallCode` respectively. + pub fn replace_paused_with_aborted_task(&mut self, aborted_task: ExecutionTask) { + match &aborted_task { + ExecutionTask::AbortedExecution { .. } => assert!( + matches!( + self.paused_or_aborted_task, + Some(ExecutionTask::PausedExecution { .. }) + ), + "Received aborted task {:?} is not compatible with paused task {:?}.", + aborted_task, + self.paused_or_aborted_task + ), + ExecutionTask::AbortedInstallCode { .. } => assert!( + matches!( + self.paused_or_aborted_task, + Some(ExecutionTask::PausedInstallCode(_)) + ), + "Received aborted task {:?} is not compatible with paused task {:?}.", + aborted_task, + self.paused_or_aborted_task + ), + ExecutionTask::Heartbeat + | ExecutionTask::GlobalTimer + | ExecutionTask::OnLowWasmMemory + | ExecutionTask::PausedExecution { .. } + | ExecutionTask::PausedInstallCode(_) => { + unreachable!( + "Unexpected task type {:?} of the aborted task.", + aborted_task + ) + } + }; + + self.paused_or_aborted_task = Some(aborted_task); + } +} + +/// Condition for `OnLowWasmMemoryHook` is satisfied if the following holds: +/// +/// 1. In the case of `memory_allocation` +/// `wasm_memory_threshold >= min(memory_allocation - memory_usage_without_wasm_memory, wasm_memory_limit) - wasm_memory_usage` +/// 2. Without memory allocation +/// `wasm_memory_threshold >= wasm_memory_limit - wasm_memory_usage` +/// +/// Note: if `wasm_memory_limit` is not set, its default value is 4 GiB. +pub fn is_low_wasm_memory_hook_condition_satisfied( + memory_usage: NumBytes, + wasm_memory_usage: NumBytes, + memory_allocation: Option, + wasm_memory_limit: Option, + wasm_memory_threshold: NumBytes, +) -> bool { + // If wasm memory limit is not set, the default is 4 GiB. Wasm memory + // limit is ignored for query methods, response callback handlers, + // global timers, heartbeats, and canister pre_upgrade. + let wasm_memory_limit = + wasm_memory_limit.unwrap_or_else(|| NumBytes::new(4 * 1024 * 1024 * 1024)); + + debug_assert!( + wasm_memory_usage <= memory_usage, + "Wasm memory usage {} is greater that memory usage {}.", + wasm_memory_usage, + memory_usage + ); + + let memory_usage_without_wasm_memory = + NumBytes::new(memory_usage.get().saturating_sub(wasm_memory_usage.get())); + + // If the canister has memory allocation, then it maximum allowed Wasm memory can be calculated + // as min(memory_allocation - memory_usage_without_wasm_memory, wasm_memory_limit). + let wasm_capacity = memory_allocation.map_or_else( + || wasm_memory_limit, + |memory_allocation| { + debug_assert!( + memory_usage_without_wasm_memory <= memory_allocation, + "Used non-Wasm memory: {:?} is larger than memory allocation: {:?}.", + memory_usage_without_wasm_memory, + memory_allocation + ); + std::cmp::min( + memory_allocation - memory_usage_without_wasm_memory, + wasm_memory_limit, + ) + }, + ); + + // Conceptually we can think that the remaining Wasm memory is + // equal to `wasm_capacity - wasm_memory_usage` and that should + // be compared with `wasm_memory_threshold` when checking for + // the condition for the hook. However, since `wasm_memory_limit` + // is ignored in some executions as stated above it is possible + // that `wasm_memory_usage` is greater than `wasm_capacity` to + // avoid overflowing subtraction we adopted inequality. + wasm_capacity < wasm_memory_usage + wasm_memory_threshold +} + +#[cfg(test)] +mod tests { + use std::sync::Arc; + + use crate::{ + canister_state::system_state::OnLowWasmMemoryHookStatus, + metadata_state::subnet_call_context_manager::InstallCodeCallId, ExecutionTask, + }; + + use super::TaskQueue; + use crate::canister_state::system_state::PausedExecutionId; + use ic_test_utilities_types::messages::IngressBuilder; + use ic_types::{ + messages::{CanisterCall, CanisterMessageOrTask, CanisterTask}, + Cycles, + }; + + #[test] + fn test_on_low_wasm_memory_hook_start_status_condition_not_satisfied() { + let mut status = OnLowWasmMemoryHookStatus::ConditionNotSatisfied; + status.update(false); + assert_eq!(status, OnLowWasmMemoryHookStatus::ConditionNotSatisfied); + + let mut status = OnLowWasmMemoryHookStatus::ConditionNotSatisfied; + status.update(true); + assert_eq!(status, OnLowWasmMemoryHookStatus::Ready); + } + + #[test] + fn test_on_low_wasm_memory_hook_start_status_ready() { + let mut status = OnLowWasmMemoryHookStatus::Ready; + status.update(false); + assert_eq!(status, OnLowWasmMemoryHookStatus::ConditionNotSatisfied); + + let mut status = OnLowWasmMemoryHookStatus::Ready; + status.update(true); + assert_eq!(status, OnLowWasmMemoryHookStatus::Ready); + } + + #[test] + fn test_on_low_wasm_memory_hook_start_status_executed() { + let mut status = OnLowWasmMemoryHookStatus::Executed; + status.update(false); + assert_eq!(status, OnLowWasmMemoryHookStatus::ConditionNotSatisfied); + + let mut status = OnLowWasmMemoryHookStatus::Executed; + status.update(true); + assert_eq!(status, OnLowWasmMemoryHookStatus::Executed); + } + + #[test] + #[should_panic(expected = "Unexpected task type")] + fn test_replace_paused_with_aborted_task_heartbeat() { + let mut task_queue = TaskQueue::default(); + task_queue.replace_paused_with_aborted_task(ExecutionTask::Heartbeat); + } + + #[test] + #[should_panic(expected = "Unexpected task type")] + fn test_replace_paused_with_aborted_task_global_timer() { + let mut task_queue = TaskQueue::default(); + task_queue.replace_paused_with_aborted_task(ExecutionTask::GlobalTimer); + } + + #[test] + #[should_panic(expected = "Unexpected task type")] + fn test_replace_paused_with_aborted_task_on_low_wasm_memory() { + let mut task_queue = TaskQueue::default(); + task_queue.replace_paused_with_aborted_task(ExecutionTask::OnLowWasmMemory); + } + + #[test] + #[should_panic(expected = "Unexpected task type")] + fn test_replace_paused_with_aborted_task_on_paused_execution() { + let mut task_queue = TaskQueue::default(); + task_queue.replace_paused_with_aborted_task(ExecutionTask::PausedExecution { + id: PausedExecutionId(0), + input: CanisterMessageOrTask::Task(CanisterTask::Heartbeat), + }); + } + + #[test] + #[should_panic(expected = "Unexpected task type")] + fn test_replace_paused_with_aborted_task_on_paused_install_code() { + let mut task_queue = TaskQueue::default(); + task_queue.replace_paused_with_aborted_task(ExecutionTask::PausedInstallCode( + PausedExecutionId(0), + )); + } + + #[test] + #[should_panic(expected = "is not compatible with paused task")] + fn test_replace_paused_with_aborted_task_on_paused_install_code_aborted_execution() { + let mut task_queue = TaskQueue::default(); + task_queue.enqueue(ExecutionTask::PausedInstallCode(PausedExecutionId(0))); + + task_queue.replace_paused_with_aborted_task(ExecutionTask::AbortedExecution { + input: CanisterMessageOrTask::Task(CanisterTask::Heartbeat), + prepaid_execution_cycles: Cycles::zero(), + }); + } + + #[test] + #[should_panic(expected = "is not compatible with paused task")] + fn test_replace_paused_with_aborted_task_on_paused_execution_aborted_install_code() { + let mut task_queue = TaskQueue::default(); + task_queue.enqueue(ExecutionTask::PausedExecution { + id: PausedExecutionId(0), + input: CanisterMessageOrTask::Task(CanisterTask::Heartbeat), + }); + + let ingress = Arc::new(IngressBuilder::new().method_name("test_ingress").build()); + + let aborted_install_code = ExecutionTask::AbortedInstallCode { + message: CanisterCall::Ingress(Arc::clone(&ingress)), + prepaid_execution_cycles: Cycles::new(1), + call_id: InstallCodeCallId::new(0), + }; + + task_queue.replace_paused_with_aborted_task(aborted_install_code); + } + + #[test] + #[should_panic(expected = "Unsuccessful removal of the task")] + fn test_task_queue_remove_heartbeat() { + let mut task_queue = TaskQueue::default(); + task_queue.remove(ExecutionTask::Heartbeat); + } + + #[test] + #[should_panic(expected = "Unsuccessful removal of the task")] + fn test_task_queue_remove_global_timer() { + let mut task_queue = TaskQueue::default(); + task_queue.remove(ExecutionTask::GlobalTimer); + } + + #[test] + #[should_panic(expected = "Unsuccessful removal of the task")] + fn test_task_queue_remove_paused_install_code() { + let mut task_queue = TaskQueue::default(); + task_queue.remove(ExecutionTask::PausedInstallCode(PausedExecutionId(0))); + } + + #[test] + #[should_panic(expected = "Unsuccessful removal of the task")] + fn test_task_queue_remove_paused_execution() { + let mut task_queue = TaskQueue::default(); + task_queue.remove(ExecutionTask::PausedInstallCode(PausedExecutionId(0))); + } + + #[test] + #[should_panic(expected = "Unsuccessful removal of the task")] + fn test_task_queue_remove_aborted_install_code() { + let mut task_queue = TaskQueue::default(); + + let ingress = Arc::new(IngressBuilder::new().method_name("test_ingress").build()); + + task_queue.remove(ExecutionTask::AbortedInstallCode { + message: CanisterCall::Ingress(Arc::clone(&ingress)), + prepaid_execution_cycles: Cycles::new(1), + call_id: InstallCodeCallId::new(0), + }); + } + + #[test] + #[should_panic(expected = "Unsuccessful removal of the task")] + fn test_task_queue_remove_aborted_execution() { + let mut task_queue = TaskQueue::default(); + task_queue.remove(ExecutionTask::AbortedExecution { + input: CanisterMessageOrTask::Task(CanisterTask::Heartbeat), + prepaid_execution_cycles: Cycles::zero(), + }); + } + + #[test] + fn test_task_queue_remove_on_low_wasm_memory_hook() { + let mut task_queue = TaskQueue::default(); + assert!(task_queue.is_empty()); + + // Queue is empty, so remove should be no_op. + task_queue.remove(ExecutionTask::OnLowWasmMemory); + assert!(task_queue.is_empty()); + + // ExecutionTask::OnLowWasmMemory is added to queue. + task_queue.enqueue(ExecutionTask::OnLowWasmMemory); + assert_eq!(task_queue.len(), 1); + assert_eq!(task_queue.front(), Some(&ExecutionTask::OnLowWasmMemory)); + + // After removing queue is empty. + task_queue.remove(ExecutionTask::OnLowWasmMemory); + assert!(task_queue.is_empty()); + + // ExecutionTask::OnLowWasmMemory can be added to the queue again. + task_queue.enqueue(ExecutionTask::OnLowWasmMemory); + assert_eq!(task_queue.len(), 1); + assert_eq!(task_queue.front(), Some(&ExecutionTask::OnLowWasmMemory)); + } + + #[test] + fn test_task_queue_pop_front_on_low_wasm_memory() { + let mut task_queue = TaskQueue::default(); + + // `ExecutionTask::OnLowWasmMemory` is added to queue. + task_queue.enqueue(ExecutionTask::OnLowWasmMemory); + assert_eq!(task_queue.len(), 1); + + assert_eq!(task_queue.pop_front(), Some(ExecutionTask::OnLowWasmMemory)); + assert!(task_queue.is_empty()); + + // After `pop` of `OnLowWasmMemory` from queue `OnLowWasmMemoryHookStatus` + // will be `Executed` so `enqueue` of `OnLowWasmMemory` is no-op. + task_queue.enqueue(ExecutionTask::OnLowWasmMemory); + assert!(task_queue.is_empty()); + + // After removing `OnLowWasmMemory` from queue `OnLowWasmMemoryHookStatus` + // will become `ConditionNotSatisfied`. + task_queue.remove(ExecutionTask::OnLowWasmMemory); + assert!(task_queue.is_empty()); + + // So now `enqueue` of `OnLowWasmMemory` will set `OnLowWasmMemoryHookStatus` + // to `Ready`. + task_queue.enqueue(ExecutionTask::OnLowWasmMemory); + assert_eq!(task_queue.len(), 1); + + assert_eq!(task_queue.pop_front(), Some(ExecutionTask::OnLowWasmMemory)); + } + + #[test] + fn test_task_queue_test_enqueue() { + let mut task_queue = TaskQueue::default(); + assert!(task_queue.is_empty()); + + task_queue.enqueue(ExecutionTask::Heartbeat); + task_queue.enqueue(ExecutionTask::PausedInstallCode(PausedExecutionId(0))); + task_queue.enqueue(ExecutionTask::GlobalTimer); + task_queue.enqueue(ExecutionTask::OnLowWasmMemory); + + assert!(!task_queue.is_empty()); + assert_eq!(task_queue.len(), 4); + + // Disregarding order of `enqueue` operations, if there is + // paused task, it should be returned the first. + assert_eq!( + task_queue.pop_front(), + Some(ExecutionTask::PausedInstallCode(PausedExecutionId(0))) + ); + + // Disregarding order of `enqueue` operations, if there is OnLowWasmMemory + // task, it should be returned right after paused or aborted task if there is one. + assert_eq!(task_queue.pop_front(), Some(ExecutionTask::OnLowWasmMemory)); + + // The rest of the tasks should be returned in the LIFO order. + assert_eq!(task_queue.pop_front(), Some(ExecutionTask::GlobalTimer)); + assert_eq!(task_queue.pop_front(), Some(ExecutionTask::Heartbeat)); + } +} diff --git a/rs/replicated_state/src/canister_state/system_state/wasm_chunk_store.rs b/rs/replicated_state/src/canister_state/system_state/wasm_chunk_store.rs index 0de59887a31..4223bd0b700 100644 --- a/rs/replicated_state/src/canister_state/system_state/wasm_chunk_store.rs +++ b/rs/replicated_state/src/canister_state/system_state/wasm_chunk_store.rs @@ -374,23 +374,23 @@ mod tests { const MB: usize = 1024 * 1024; const MAX_SIZE: NumBytes = NumBytes::new(20 * MB as u64); - proptest! { - #[test] - // Try chunks 2x as big as the size limit. - // If all inserts below the size limit succeeded, we'd expect 50 * - // .5 MiB = 25 MiB total. So set the max size below that to - // evenutally hit the size limit. - fn insert_result_matches_can_insert(vecs in prop_vec((any::(), 0..2 * MB), 100)) { - let mut store = WasmChunkStore::new_for_testing(); - for (byte, length) in vecs { - let chunk = vec![byte; length]; - let check = store.can_insert_chunk(MAX_SIZE, &chunk); - let hash = store.insert_chunk(MAX_SIZE, &chunk); - if hash.is_ok() { - assert_eq!(check, Ok(())); - } else { - assert_eq!(check.unwrap_err(), hash.unwrap_err()); - } + #[test_strategy::proptest] + // Try chunks 2x as big as the size limit. + // If all inserts below the size limit succeeded, we'd expect 50 * + // .5 MiB = 25 MiB total. So set the max size below that to + // evenutally hit the size limit. + fn insert_result_matches_can_insert( + #[strategy(prop_vec((any::(), 0..2 * MB), 100))] vecs: Vec<(u8, usize)>, + ) { + let mut store = WasmChunkStore::new_for_testing(); + for (byte, length) in vecs { + let chunk = vec![byte; length]; + let check = store.can_insert_chunk(MAX_SIZE, &chunk); + let hash = store.insert_chunk(MAX_SIZE, &chunk); + if hash.is_ok() { + prop_assert_eq!(check, Ok(())); + } else { + prop_assert_eq!(check.unwrap_err(), hash.unwrap_err()); } } } diff --git a/rs/replicated_state/src/metadata_state.rs b/rs/replicated_state/src/metadata_state.rs index 5d55f2380ed..b77aa920c7f 100644 --- a/rs/replicated_state/src/metadata_state.rs +++ b/rs/replicated_state/src/metadata_state.rs @@ -35,7 +35,6 @@ use ic_types::{ ingress::{IngressState, IngressStatus}, messages::{ is_subnet_id, CanisterCall, MessageId, Payload, RejectContext, RequestOrResponse, Response, - NO_DEADLINE, }, node_id_into_protobuf, node_id_try_from_option, nominal_cycles::NominalCycles, @@ -72,7 +71,7 @@ pub struct SystemMetadata { pub ingress_history: IngressHistoryState, /// XNet stream state indexed by the _destination_ subnet id. - pub(super) streams: Arc, + pub(super) streams: Arc, /// The canister ID ranges from which this subnet generates canister IDs. canister_allocation_ranges: CanisterIdRanges, @@ -730,10 +729,7 @@ impl TryFrom<(pb_metadata::SystemMetadata, &dyn CheckpointLoadingMetrics)> for S // Ingress history is persisted separately. We rely on `load_checkpoint()` to // properly set this value. ingress_history: Default::default(), - streams: Arc::new(Streams { - guaranteed_responses_size_bytes: Streams::calculate_stats(&streams), - streams, - }), + streams: Arc::new(streams), network_topology: try_from_option_field( item.network_topology, "SystemMetadata::network_topology", @@ -800,7 +796,7 @@ impl SystemMetadata { } /// Returns a reference to the streams. - pub fn streams(&self) -> &Streams { + pub fn streams(&self) -> &StreamMap { &self.streams } @@ -1514,256 +1510,6 @@ impl From for StreamSlice { } } -/// Wrapper around a private `StreamMap` plus stats. -#[derive(Clone, Eq, PartialEq, Debug, Default)] -pub struct Streams { - /// Map of streams by destination `SubnetId`. - streams: StreamMap, - - /// Map of response sizes in bytes by respondent `CanisterId`. - guaranteed_responses_size_bytes: BTreeMap, -} - -impl Streams { - pub fn new() -> Self { - Default::default() - } - - /// Returns a reference to the wrapped `StreamMap`. - pub fn streams(&self) -> &StreamMap { - &self.streams - } - - /// Returns a reference to the stream for the given destination subnet. - pub fn get(&self, destination: &SubnetId) -> Option<&Stream> { - self.streams.get(destination) - } - - /// Returns an iterator over all `(&SubnetId, &Stream)` pairs. - pub fn iter(&self) -> impl Iterator { - self.streams.iter() - } - - /// Returns an iterator over all `&SubnetId` keys. - pub fn keys(&self) -> impl Iterator { - self.streams.keys() - } - - /// Pushes the given message onto the stream for the given destination - /// subnet. - pub fn push(&mut self, destination: SubnetId, msg: RequestOrResponse) { - if let RequestOrResponse::Response(response) = &msg { - if response.deadline == NO_DEADLINE { - *self - .guaranteed_responses_size_bytes - .entry(response.respondent) - .or_default() += msg.count_bytes(); - } - } - - self.streams.entry(destination).or_default().push(msg); - - #[cfg(debug_assertions)] - self.debug_validate_stats(); - } - - /// Returns a mutable reference to the stream for the given destination - /// subnet. - pub fn get_mut(&mut self, destination: &SubnetId) -> Option { - // Can't (easily) validate stats when `StreamHandle` gets dropped, but we should - // at least do it before. - #[cfg(debug_assertions)] - self.debug_validate_stats(); - - match self.streams.get_mut(destination) { - Some(stream) => Some(StreamHandle::new( - stream, - &mut self.guaranteed_responses_size_bytes, - )), - None => None, - } - } - - /// Returns a mutable reference to the stream for the given destination - /// subnet, inserting it if it doesn't already exist. - pub fn get_mut_or_insert(&mut self, destination: SubnetId) -> StreamHandle { - // Can't (easily) validate stats when `StreamHandle` gets dropped, but we should - // at least do it before. - #[cfg(debug_assertions)] - self.debug_validate_stats(); - - StreamHandle::new( - self.streams.entry(destination).or_default(), - &mut self.guaranteed_responses_size_bytes, - ) - } - - /// Returns the guaranteed response sizes by responder canister stat. - pub fn guaranteed_responses_size_bytes(&self) -> &BTreeMap { - &self.guaranteed_responses_size_bytes - } - - /// Prunes zero-valued guaranteed response sizes entries. - /// - /// This is triggered explicitly by `ReplicatedState` after it has updated the - /// canisters' copies of these values (including the zeroes). - pub fn prune_zero_guaranteed_responses_size_bytes(&mut self) { - self.guaranteed_responses_size_bytes - .retain(|_, &mut value| value != 0); - } - - /// Computes the `guaranteed_responses_size_bytes` map from scratch. Used when - /// deserializing and in asserts. - /// - /// Time complexity: O(num_messages). - pub fn calculate_stats(streams: &StreamMap) -> BTreeMap { - let mut guaranteed_responses_size_bytes: BTreeMap = BTreeMap::new(); - for (_, stream) in streams.iter() { - for (_, msg) in stream.messages().iter() { - if let RequestOrResponse::Response(response) = msg { - if response.deadline == NO_DEADLINE { - *guaranteed_responses_size_bytes - .entry(response.respondent) - .or_default() += msg.count_bytes(); - } - } - } - } - guaranteed_responses_size_bytes - } - - /// Checks that the running accounting of the sizes of responses in streams is - /// accurate. - #[cfg(debug_assertions)] - fn debug_validate_stats(&self) { - let mut nonzero_guaranteed_responses_size_bytes = - self.guaranteed_responses_size_bytes.clone(); - nonzero_guaranteed_responses_size_bytes.retain(|_, &mut value| value != 0); - debug_assert_eq!( - Streams::calculate_stats(&self.streams), - nonzero_guaranteed_responses_size_bytes - ); - } -} - -/// A mutable reference to a stream owned by a `Streams` struct; bundled with -/// the `Streams`' stats, to be updated on stream mutations. -pub struct StreamHandle<'a> { - stream: &'a mut Stream, - - guaranteed_responses_size_bytes: &'a mut BTreeMap, -} - -impl<'a> StreamHandle<'a> { - pub fn new( - stream: &'a mut Stream, - guaranteed_responses_size_bytes: &'a mut BTreeMap, - ) -> Self { - Self { - stream, - guaranteed_responses_size_bytes, - } - } - - /// Returns a reference to the message queue. - pub fn messages(&self) -> &StreamIndexedQueue { - self.stream.messages() - } - - /// Returns the stream's begin index. - pub fn messages_begin(&self) -> StreamIndex { - self.stream.messages_begin() - } - - /// Returns the stream's end index. - pub fn messages_end(&self) -> StreamIndex { - self.stream.messages_end() - } - - /// Returns a reference to the reject signals. - pub fn reject_signals(&self) -> &VecDeque { - self.stream.reject_signals() - } - - /// Returns the index just beyond the last sent signal. - pub fn signals_end(&self) -> StreamIndex { - self.stream.signals_end - } - - /// Appends the given message to the tail of the stream. - /// - /// Returns the byte size of the pushed message. - pub fn push(&mut self, message: RequestOrResponse) -> usize { - let size_bytes = message.count_bytes(); - if let RequestOrResponse::Response(response) = &message { - if response.deadline == NO_DEADLINE { - *self - .guaranteed_responses_size_bytes - .entry(response.respondent) - .or_default() += size_bytes; - } - } - self.stream.push(message); - size_bytes - } - - /// Pushes an accept signal. Since these are not explicitly encoded, this - /// just increments `signals_end`. - pub fn push_accept_signal(&mut self) { - self.stream.push_accept_signal(); - } - - /// Appends a reject signal (the current `signals_end`) to the tail of the - /// reject signals; and then increments `signals_end`. - pub fn push_reject_signal(&mut self, reason: RejectReason) { - self.stream.push_reject_signal(reason); - } - - /// Garbage collects messages before `new_begin`, collecting and returning all - /// messages for which a reject signal was received. - pub fn discard_messages_before( - &mut self, - new_begin: StreamIndex, - reject_signals: &VecDeque, - ) -> Vec<(RejectReason, RequestOrResponse)> { - // Update stats for each discarded message. - for (index, msg) in self.stream.messages().iter() { - if index >= new_begin { - break; - } - if let RequestOrResponse::Response(response) = &msg { - if response.deadline == NO_DEADLINE { - let canister_guaranteed_responses_size_bytes = self - .guaranteed_responses_size_bytes - .get_mut(&response.respondent) - .expect( - "No `guaranteed_responses_size_bytes` entry for discarded response", - ); - *canister_guaranteed_responses_size_bytes -= msg.count_bytes(); - } - } - } - - self.stream - .discard_messages_before(new_begin, reject_signals) - } - - /// Garbage collects signals before `new_signals_begin`. - pub fn discard_signals_before(&mut self, new_signals_begin: StreamIndex) { - self.stream.discard_signals_before(new_signals_begin); - } - - /// Returns a reference to the reverse stream flags. - pub fn reverse_stream_flags(&self) -> &StreamFlags { - &self.stream.reverse_stream_flags - } - - /// Sets the reverse stream flags. - pub fn set_reverse_stream_flags(&mut self, flags: StreamFlags) { - self.stream.set_reverse_stream_flags(flags); - } -} - #[derive(Clone, Eq, PartialEq, Debug)] /// State associated with the history of statuses of ingress messages as they /// traversed through the system. @@ -2356,32 +2102,6 @@ impl pub(crate) mod testing { use super::*; - /// Testing only: Exposes `Streams` internals for use in other modules' - /// tests. - pub trait StreamsTesting { - /// Testing only: Modifies `SystemMetadata::streams` by applying the - /// provided function. - fn modify_streams(&mut self, f: F); - } - - impl StreamsTesting for Streams { - fn modify_streams(&mut self, f: F) { - f(&mut self.streams); - - // Update `guaranteed_responses_size_bytes`, retaining all previous keys with a - // default byte size of zero (so that the respective canister's - // `transient_stream_guaranteed_responses_size_bytes` is correctly reset to - // zero). - self.guaranteed_responses_size_bytes - .values_mut() - .for_each(|size| *size = 0); - for (canister_id, size_bytes) in Streams::calculate_stats(&self.streams) { - self.guaranteed_responses_size_bytes - .insert(canister_id, size_bytes); - } - } - } - /// Early warning system / stumbling block forcing the authors of changes adding /// or removing replicated state fields to think about and/or ask the Message /// Routing team to think about any repercussions to the subnet splitting logic. diff --git a/rs/replicated_state/src/metadata_state/tests.rs b/rs/replicated_state/src/metadata_state/tests.rs index 7ed1bc4818a..c1a7addbd3f 100644 --- a/rs/replicated_state/src/metadata_state/tests.rs +++ b/rs/replicated_state/src/metadata_state/tests.rs @@ -12,7 +12,7 @@ use ic_test_utilities_types::{ canister_test_id, message_test_id, node_test_id, subnet_test_id, user_test_id, SUBNET_0, SUBNET_1, SUBNET_2, }, - messages::{RequestBuilder, ResponseBuilder}, + messages::RequestBuilder, xnet::{StreamHeaderBuilder, StreamSliceBuilder}, }; use ic_types::{ @@ -130,246 +130,6 @@ fn entries_sorted_lexicographically() { assert_eq!(actual, expected); } -#[test] -fn streams_stats() { - // Two local canisters, `local_a` and `local_b`. - let local_a = canister_test_id(1); - let local_b = canister_test_id(2); - // Two remote canisters, `remote_1` on `SUBNET_1` and `remote_2` on `SUBNET_2`. - let remote_1 = canister_test_id(3); - let remote_2 = canister_test_id(4); - - fn request(sender: CanisterId, receiver: CanisterId) -> RequestOrResponse { - RequestBuilder::default() - .sender(sender) - .receiver(receiver) - .build() - .into() - } - fn response( - respondent: CanisterId, - originator: CanisterId, - payload: &str, - ) -> (RequestOrResponse, usize) { - let rep: RequestOrResponse = ResponseBuilder::default() - .respondent(respondent) - .originator(originator) - .response_payload(Payload::Data(payload.as_bytes().to_vec())) - .build() - .into(); - let req_bytes = rep.count_bytes(); - (rep, req_bytes) - } - - // A bunch of requests and responses from local canisters to remote ones. - let req_a1 = request(local_a, remote_1); - let (rep_a1, rep_a1_size) = response(local_a, remote_1, "a"); - let (rep_b1, rep_b1_size) = response(local_b, remote_1, "bb"); - let (rep_b2, rep_b2_size) = response(local_b, remote_2, "ccc"); - - let mut streams = Streams::new(); - // Empty response size map. - let mut expected_responses_size = Default::default(); - assert_eq!( - streams.guaranteed_responses_size_bytes(), - &expected_responses_size - ); - - streams.push(SUBNET_1, req_a1); - // Pushed a request, response size stats are unchanged. - assert_eq!( - streams.guaranteed_responses_size_bytes(), - &expected_responses_size - ); - - // Push response via `Streams::push()`. - streams.push(SUBNET_1, rep_a1); - // `rep_a1` is now accounted for against `local_a`. - expected_responses_size.insert(local_a, rep_a1_size); - assert_eq!( - streams.guaranteed_responses_size_bytes(), - &expected_responses_size - ); - - // Push response via `StreamHandle::push()`. - streams.get_mut(&SUBNET_1).unwrap().push(rep_b1); - // `rep_b1` is accounted for against `local_b`. - expected_responses_size.insert(local_b, rep_b1_size); - assert_eq!( - streams.guaranteed_responses_size_bytes(), - &expected_responses_size - ); - - // Push response via `StreamHandle::push()` after `get_mut_or_insert()`. - streams.get_mut_or_insert(SUBNET_2).push(rep_b2); - // `rep_b2` is accounted for against `local_b`. - *expected_responses_size.get_mut(&local_b).unwrap() += rep_b2_size; - assert_eq!( - streams.guaranteed_responses_size_bytes(), - &expected_responses_size - ); - - // Discard `req_a1` and `rep_a1` from the stream for `SUBNET_1`. - streams - .get_mut(&SUBNET_1) - .unwrap() - .discard_messages_before(2.into(), &Default::default()); - // No more responses from `local_a` in `streams`. - *expected_responses_size.get_mut(&local_a).unwrap() = 0; - assert_eq!( - streams.guaranteed_responses_size_bytes(), - &expected_responses_size - ); - - streams.prune_zero_guaranteed_responses_size_bytes(); - // Zero valued entry for `local_a` pruned. - expected_responses_size.remove(&local_a); - assert_eq!( - streams.guaranteed_responses_size_bytes(), - &expected_responses_size - ); - - // Discard `rep_b2` from the stream for `SUBNET_2`. - streams - .get_mut(&SUBNET_2) - .unwrap() - .discard_messages_before(1.into(), &Default::default()); - // `rep_b2` is gone. - *expected_responses_size.get_mut(&local_b).unwrap() -= rep_b2_size; - assert_eq!( - streams.guaranteed_responses_size_bytes(), - &expected_responses_size - ); -} - -#[test] -fn streams_stats_best_effort_messages() { - let local = canister_test_id(1); - let remote = canister_test_id(2); - - let request = |sender: CanisterId, receiver: CanisterId| -> RequestOrResponse { - RequestBuilder::default() - .sender(sender) - .receiver(receiver) - .deadline(CoarseTime::from_secs_since_unix_epoch(1)) - .build() - .into() - }; - let response = - |respondent: CanisterId, originator: CanisterId, payload: &str| -> RequestOrResponse { - ResponseBuilder::default() - .respondent(respondent) - .originator(originator) - .response_payload(Payload::Data(payload.as_bytes().to_vec())) - .deadline(CoarseTime::from_secs_since_unix_epoch(1)) - .build() - .into() - }; - - // A bunch of best-effort requests and responses from the local canister to the remote one. - let req = request(local, remote); - let rep_1 = response(local, remote, "a"); - let rep_2 = response(local, remote, "bb"); - let rep_3 = response(local, remote, "ccc"); - - let mut streams = Streams::new(); - - // Expecting no guaranteed responses throughout. - let expected_responses_size = BTreeMap::default(); - assert_eq!( - streams.guaranteed_responses_size_bytes(), - &expected_responses_size - ); - - streams.push(SUBNET_1, req); - // Pushed a request, response size stats are unchanged. - assert_eq!( - streams.guaranteed_responses_size_bytes(), - &expected_responses_size - ); - - // Push response via `Streams::push()`. - streams.push(SUBNET_1, rep_1); - assert_eq!( - streams.guaranteed_responses_size_bytes(), - &expected_responses_size - ); - - // Push response via `StreamHandle::push()`. - streams.get_mut(&SUBNET_1).unwrap().push(rep_2); - assert_eq!( - streams.guaranteed_responses_size_bytes(), - &expected_responses_size - ); - - // Push response via `StreamHandle::push()` after `get_mut_or_insert()`. - streams.get_mut_or_insert(SUBNET_2).push(rep_3); - assert_eq!( - streams.guaranteed_responses_size_bytes(), - &expected_responses_size - ); - - // Discard everything from the stream for `SUBNET_1`. - streams - .get_mut(&SUBNET_1) - .unwrap() - .discard_messages_before(3.into(), &Default::default()); - assert_eq!( - streams.guaranteed_responses_size_bytes(), - &expected_responses_size - ); - - streams.prune_zero_guaranteed_responses_size_bytes(); - assert_eq!( - streams.guaranteed_responses_size_bytes(), - &expected_responses_size - ); - - // Discard `rep_b2` from the stream for `SUBNET_2`. - streams - .get_mut(&SUBNET_2) - .unwrap() - .discard_messages_before(1.into(), &Default::default()); - assert_eq!( - streams.guaranteed_responses_size_bytes(), - &expected_responses_size - ); -} - -#[test] -fn streams_stats_after_deserialization() { - let mut system_metadata = SystemMetadata::new(SUBNET_0, SubnetType::Application); - let streams = Arc::make_mut(&mut system_metadata.streams); - - streams.push( - SUBNET_1, - ResponseBuilder::default() - .respondent(canister_test_id(1)) - .originator(canister_test_id(2)) - .build() - .into(), - ); - - let system_metadata_proto: ic_protobuf::state::system_metadata::v1::SystemMetadata = - (&system_metadata).into(); - let deserialized_system_metadata = ( - system_metadata_proto, - &DummyMetrics as &dyn CheckpointLoadingMetrics, - ) - .try_into() - .unwrap(); - - // Ensure that the deserialized `SystemMetadata` is equal to the original. - assert_eq!(system_metadata, deserialized_system_metadata); - // Double-check that the stats match. - assert_eq!( - system_metadata.streams.guaranteed_responses_size_bytes(), - deserialized_system_metadata - .streams - .guaranteed_responses_size_bytes() - ); -} - #[test] fn init_allocation_ranges_if_empty() { let own_subnet_id = SUBNET_0; @@ -684,10 +444,8 @@ fn system_metadata_split() { // Only ingress messages for `CANISTER_2` should be retained on `SUBNET_B`. let is_canister_on_subnet_b = |canister_id: CanisterId| canister_id == CANISTER_2; - let streams = Streams { - streams: btreemap! { SUBNET_C => Stream::new(StreamIndexedQueue::with_begin(13.into()), 14.into()) }, - guaranteed_responses_size_bytes: btreemap! { CANISTER_1 => 169 }, - }; + let streams = + btreemap! { SUBNET_C => Stream::new(StreamIndexedQueue::with_begin(13.into()), 14.into()) }; // Use uncommon `SubnetType::VerifiedApplication` to make it more likely to // detect a regression in the subnet type assigned to subnet B. @@ -1694,30 +1452,6 @@ fn stream_pushing_signals_increments_signals_end() { assert_eq!(StreamIndex::new(32), stream.signals_end()); } -#[test] -fn stream_handle_pushing_signals_increments_signals_end() { - let mut stream = generate_stream( - MessageConfig { - begin: 30, - count: 0, - }, - SignalConfig { end: 30 }, - ); - assert!(stream.reject_signals().is_empty()); - - let mut guaranteed_responses_size_bytes = BTreeMap::default(); - let mut handle = StreamHandle::new(&mut stream, &mut guaranteed_responses_size_bytes); - - handle.push_accept_signal(); - assert_eq!(StreamIndex::new(31), handle.signals_end()); - handle.push_reject_signal(RejectReason::CanisterNotFound); - assert_eq!( - &VecDeque::from([RejectSignal::new(RejectReason::CanisterNotFound, 31.into()),]), - handle.reject_signals() - ); - assert_eq!(StreamIndex::new(32), handle.signals_end()); -} - #[test] fn stream_roundtrip_encoding() { let mut messages = StreamIndexedQueue::with_begin(30.into()); @@ -2309,69 +2043,65 @@ const BATCH_TIME_RANGE: Range = (u64::MAX / 2)..(u64::MAX / 2 + MAX_NUM_DAY #[allow(dead_code)] const NODE_ID_RANGE: Range = 0..20; -proptest! { - /// Checks that `check_soft_invariants()` does not return an error when observing random - /// node IDs at random mostly sorted and slightly permuted timestamps. - /// Such invariants are checked indirectly at the bottom of `observe()` where - /// `check_soft_invariants()` is called. There is an additional call to - /// `check_soft_invariants()` at the end of the test to ensure the test doesn't - /// silently pass when the production code is changed. - /// Querying `metrics_since()` is also checked using completely random time stamps to - /// ensure there are no hidden panics. - #[test] - fn blockmaker_metrics_check_soft_invariants( - (mut time_u64, random_time_u64, node_ids_u64) in (0..MAX_NUM_DAYS) - .prop_flat_map(|num_elements| { - ( - proptest::collection::vec(BATCH_TIME_RANGE, num_elements), - proptest::collection::vec(any::(), num_elements), - proptest::collection::vec(NODE_ID_RANGE, num_elements), - ) - }) - ) { - // Sort timestamps, then slightly permute them by inserting some - // duplicates and swapping elements in some places. - time_u64.sort(); - if !time_u64.is_empty() { - for index in 0..(time_u64.len() - 1) { - if time_u64[index] % 23 == 0 { - time_u64[index + 1] = time_u64[index]; - } - if time_u64[index] % 27 == 0 { - time_u64.swap(index, index + 1); - } +/// Checks that `check_soft_invariants()` does not return an error when observing random +/// node IDs at random mostly sorted and slightly permuted timestamps. +/// Such invariants are checked indirectly at the bottom of `observe()` where +/// `check_soft_invariants()` is called. There is an additional call to +/// `check_soft_invariants()` at the end of the test to ensure the test doesn't +/// silently pass when the production code is changed. +/// Querying `metrics_since()` is also checked using completely random time stamps to +/// ensure there are no hidden panics. +#[test_strategy::proptest] +fn blockmaker_metrics_check_soft_invariants( + #[strategy(0..MAX_NUM_DAYS)] _num_elements: usize, + #[strategy(proptest::collection::vec(BATCH_TIME_RANGE, #_num_elements))] mut time_u64: Vec, + #[strategy(proptest::collection::vec(any::(), #_num_elements))] random_time_u64: Vec, + #[strategy(proptest::collection::vec(NODE_ID_RANGE, #_num_elements))] node_ids_u64: Vec, +) { + // Sort timestamps, then slightly permute them by inserting some + // duplicates and swapping elements in some places. + time_u64.sort(); + if !time_u64.is_empty() { + for index in 0..(time_u64.len() - 1) { + if time_u64[index] % 23 == 0 { + time_u64[index + 1] = time_u64[index]; + } + if time_u64[index] % 27 == 0 { + time_u64.swap(index, index + 1); } } + } - let mut metrics = BlockmakerMetricsTimeSeries::default(); - // Observe a unique node ID first to ensure the pruning process - // is triggered once the metrics reach capacity. + let mut metrics = BlockmakerMetricsTimeSeries::default(); + // Observe a unique node ID first to ensure the pruning process + // is triggered once the metrics reach capacity. + metrics.observe( + Time::from_nanos_since_unix_epoch(0), + &BlockmakerMetrics { + blockmaker: node_test_id(NODE_ID_RANGE.end + 10), + failed_blockmakers: vec![], + }, + ); + // Observe random node IDs at random increasing timestamps; `check_runtime_invariants()` + // will be triggered passively each time `observe()` is called. + // Additionally, query snapshots at random times and consume the iterator to ensure + // there are no hidden panics in `metrics_since()`. + for ((batch_time_u64, query_time_u64), node_id_u64) in time_u64 + .into_iter() + .zip(random_time_u64.into_iter()) + .zip(node_ids_u64.into_iter()) + { metrics.observe( - Time::from_nanos_since_unix_epoch(0), + Time::from_nanos_since_unix_epoch(batch_time_u64), &BlockmakerMetrics { - blockmaker: node_test_id(NODE_ID_RANGE.end + 10), - failed_blockmakers: vec![], - } + blockmaker: node_test_id(node_id_u64), + failed_blockmakers: vec![node_test_id(node_id_u64 + 1)], + }, ); - // Observe random node IDs at random increasing timestamps; `check_runtime_invariants()` - // will be triggered passively each time `observe()` is called. - // Additionally, query snapshots at random times and consume the iterator to ensure - // there are no hidden panics in `metrics_since()`. - for ((batch_time_u64, query_time_u64), node_id_u64) in time_u64 - .into_iter() - .zip(random_time_u64.into_iter()) - .zip(node_ids_u64.into_iter()) - { - metrics.observe( - Time::from_nanos_since_unix_epoch(batch_time_u64), - &BlockmakerMetrics { - blockmaker: node_test_id(node_id_u64), - failed_blockmakers: vec![node_test_id(node_id_u64 + 1)], - } - ); - metrics.metrics_since(Time::from_nanos_since_unix_epoch(query_time_u64)).count(); - } - - prop_assert!(metrics.check_soft_invariants().is_ok()); + metrics + .metrics_since(Time::from_nanos_since_unix_epoch(query_time_u64)) + .count(); } + + prop_assert!(metrics.check_soft_invariants().is_ok()); } diff --git a/rs/replicated_state/src/page_map.rs b/rs/replicated_state/src/page_map.rs index 46fce36445b..580cfad7848 100644 --- a/rs/replicated_state/src/page_map.rs +++ b/rs/replicated_state/src/page_map.rs @@ -133,7 +133,7 @@ struct WriteBuffer<'a> { start_index: PageIndex, } -impl<'a> WriteBuffer<'a> { +impl WriteBuffer<'_> { fn apply_to_file(&mut self, file: &mut File, path: &Path) -> Result<(), PersistenceError> { use std::io::{Seek, SeekFrom}; @@ -167,7 +167,7 @@ impl<'a> WriteBuffer<'a> { /// operation. This allows us to simplify canister state management: we can /// simply have a copy of the whole PageMap in every canister snapshot. #[derive(Clone, Debug, Default)] -pub(crate) struct PageDelta(IntMap); +pub(crate) struct PageDelta(IntMap); impl PageDelta { /// Gets content of the page at the specified index. @@ -176,21 +176,19 @@ impl PageDelta { /// allocating pages in this `PageDelta`. It serves as a witness that /// the contents of the page is still valid. fn get_page(&self, page_index: PageIndex) -> Option<&PageBytes> { - self.0.get(page_index.get()).map(|p| p.contents()) + self.0.get(&page_index).map(|p| p.contents()) } /// Returns a reference to the page at the specified index. fn get_page_ref(&self, page_index: PageIndex) -> Option<&Page> { - self.0.get(page_index.get()) + self.0.get(&page_index) } /// Returns (lower, upper), where: /// - lower is the largest index/page smaller or equal to the given page index. /// - upper is the smallest index/page larger or equal to the given page index. fn bounds(&self, page_index: PageIndex) -> Bounds { - let (lower, upper) = self.0.bounds(page_index.get()); - let map_index = |(k, v)| (PageIndex::new(k), v); - (lower.map(map_index), upper.map(map_index)) + self.0.bounds(&page_index) } /// Modifies this delta in-place by applying all the entries in `rhs` to it. @@ -199,8 +197,8 @@ impl PageDelta { } /// Enumerates all the pages in this delta. - fn iter(&self) -> impl Iterator { - self.0.iter().map(|(idx, page)| (PageIndex::new(idx), page)) + fn iter(&self) -> impl Iterator { + self.0.iter() } /// Returns true if the page delta contains no pages. @@ -210,8 +208,8 @@ impl PageDelta { /// Returns the largest page index in the page delta. /// If the page delta is empty, then it returns `None`. - fn max_page_index(&self) -> Option { - self.0.max_key().map(PageIndex::from) + fn max_page_index(&self) -> Option<&PageIndex> { + self.0.max_key() } /// Returns the number of pages in the page delta. @@ -225,7 +223,7 @@ where I: IntoIterator, { fn from(delta: I) -> Self { - Self(delta.into_iter().map(|(i, p)| (i.get(), p)).collect()) + Self(delta.into_iter().collect()) } } @@ -358,7 +356,7 @@ pub enum MemoryMapOrData<'a> { Data(&'a [u8]), } -impl<'a> MemoryInstructions<'a> { +impl MemoryInstructions<'_> { // Filters and cuts any instructions that do not fall into `new_range`. pub fn restrict_to_range(&mut self, new_range: &Range) { self.range = PageIndex::new(std::cmp::max(self.range.start.get(), new_range.start.get())) @@ -562,7 +560,7 @@ impl PageMap { self.page_allocator.serialize_page_delta( pages .iter() - .map(|index| (*index, self.page_delta.get_page_ref(*index).unwrap())), + .map(|index| (index, self.page_delta.get_page_ref(*index).unwrap())), ) } @@ -652,7 +650,7 @@ impl PageMap { } /// Returns the iterator over delta pages in this `PageMap` - pub fn delta_pages_iter(&self) -> impl Iterator + '_ { + pub fn delta_pages_iter(&self) -> impl Iterator + '_ { self.page_delta .iter() .map(|(index, page)| (index, page.contents())) @@ -699,7 +697,7 @@ impl PageMap { if result_range.end < max_range.end { let (_, upper_bound) = page_delta.bounds(result_range.end); match upper_bound { - Some((key, page)) if key < max_range.end => { + Some((&key, page)) if key < max_range.end => { if include { let end = PageIndex::new(key.get() + 1); instructions.push((key..end, MemoryMapOrData::Data(page.contents()))); @@ -729,7 +727,7 @@ impl PageMap { let (lower_bound, _) = page_delta.bounds(PageIndex::new(result_range.start.get() - 1)); match lower_bound { - Some((key, page)) if key >= max_range.start => { + Some((&key, page)) if key >= max_range.start => { let end = PageIndex::new(key.get() + 1); if include { instructions.push((key..end, MemoryMapOrData::Data(page.contents()))); @@ -849,7 +847,7 @@ impl PageMap { } pub fn get_page_delta_indices(&self) -> Vec { - self.page_delta.iter().map(|(index, _)| index).collect() + self.page_delta.iter().map(|(index, _)| *index).collect() } /// Whether there are any page deltas @@ -937,10 +935,10 @@ impl PageMap { let mut last_applied_index: Option = None; let num_host_pages = self.num_host_pages() as u64; for (index, _) in page_delta.iter() { - debug_assert!(self.page_delta.0.get(index.get()).is_some()); - assert!(index < num_host_pages.into()); + debug_assert!(self.page_delta.0.get(index).is_some()); + assert!(*index < num_host_pages.into()); - if last_applied_index.is_some() && last_applied_index.unwrap() >= index { + if last_applied_index.is_some() && last_applied_index.unwrap() >= *index { continue; } diff --git a/rs/replicated_state/src/page_map/int_map.rs b/rs/replicated_state/src/page_map/int_map.rs index dfee6bc72d5..238af11f507 100644 --- a/rs/replicated_state/src/page_map/int_map.rs +++ b/rs/replicated_state/src/page_map/int_map.rs @@ -3,72 +3,115 @@ #[cfg(test)] mod test; -use std::{cmp::Ordering, sync::Arc}; +use ic_validate_eq::ValidateEq; +use phantom_newtype::Id; +use std::cmp::Ordering; +use std::fmt::Debug; +use std::ops::{BitAnd, BitOr, BitXor, Not, Shl, Sub}; +use std::sync::Arc; /// Big-endian patricia trees. +/// +/// `K` and `V` are the key and value types, respectively, where `K` can be +/// represented as an unsigned integer type (`u64` or `u128`). #[derive(Clone, Debug, Default)] -enum Tree { +enum Tree { /// An empty tree. + /// /// Allowing empty trees simplifies the code a bit. #[default] Empty, + /// A key-value pair. - Leaf(u64, T), + Leaf(K, V), + /// A binary fork. /// /// Invariants: /// /// * Both left and right subtrees aren't empty, i.e., - /// + /// ``` /// left.len() > 0 && right.len() > 0 + /// ``` /// - /// * For each leaf L in left and right, the key bits match the prefix up - /// to but not including the branching bit, i.e., + /// * For each leaf `L` in `left` and `right`, the key bits match the prefix + /// up to but not including the branching bit, i.e., + /// ``` + /// matches_prefix(L.key, prefix, branching_bit) == true + /// ``` /// - /// matches_prefix(L.key, prefix, branching_bit) == true. + /// * For each leaf `L` in the left subtree: + /// ``` + /// L.key & (1 << branching_bit) == 0 + /// ``` /// - /// * For each leaf L in the left subtree: + /// * For each leaf `L` in the right subtree: + /// ``` + /// L.key & (1 << branching_bit) != 0 + /// ``` /// - /// L.key & (1 << branching_bit) == 0. - /// - /// * For each leaf L in the right subtree: - /// - /// L.key & (1 << branching_bit) == 1. - /// - /// * 0 ≤ branching_bit ≤ 63. + /// * ``` + /// 0 ≤ branching_bit < K::Repr::size_bits(). + /// ``` Branch { - prefix: u64, + prefix: K::Repr, branching_bit: u8, - left: Arc>, - right: Arc>, + left: Arc>, + right: Arc>, }, } -/// Creates a branch node having subtrees t0 and t1 as children. +/// Creates a branch node having subtrees `t0` and `t1` as children. /// -/// Precondition: p0 ≠ p1 +/// Precondition: `p0 ≠ p1` #[inline] -fn join(p0: u64, t0: Arc>, p1: u64, t1: Arc>) -> Tree { - let b = branching_bit(p0, p1); +fn join( + p0: K::Repr, + t0: Arc>, + p1: K::Repr, + t1: Arc>, +) -> Tree { + debug_assert_eq!( + p0, + match t0.as_ref() { + Tree::Leaf(k, _) => k.as_int(), + Tree::Branch { prefix, .. } => *prefix, + Tree::Empty => panic!("expected a leaf or branch"), + } + ); + debug_assert_eq!( + p1, + match t1.as_ref() { + Tree::Leaf(k, _) => k.as_int(), + Tree::Branch { prefix, .. } => *prefix, + Tree::Empty => panic!("expected a leaf or branch"), + } + ); + + let branching_bit = branching_bit(p0, p1); + let prefix = mask(p0, branching_bit); - if p0 & (1 << b) == 0 { + // NB: This assumes that `K::Repr` is an unsigned integer type. This is ensured + // by the `IntKey` extending `sealed::UnsignedInt` (which is only implemented + // for unsigned integer types). + if p0 < p1 { Tree::Branch { - prefix: mask(p0, b), - branching_bit: b, + prefix, + branching_bit, left: t0, right: t1, } } else { Tree::Branch { - prefix: mask(p0, b), - branching_bit: b, + prefix, + branching_bit, left: t1, right: t0, } } } -/// Modifies the contents of an Arc, creating a copy if necessary. +/// Modifies the contents of an `Arc`, creating a copy if necessary. #[inline] fn with_arc(mut p: Arc, f: impl FnOnce(T) -> T) -> Arc { let dst = Arc::make_mut(&mut p); @@ -76,7 +119,17 @@ fn with_arc(mut p: Arc, f: impl FnOnce(T) -> T) -> Arc p } -/// Extracts a value from an Arc, cloning its content if necessary. +/// Calls the given function on the mutable contents of an `Arc`, creating a +/// copy if necessary. +#[inline] +fn with_arc2(mut p: Arc, f: impl FnOnce(T) -> (T, V)) -> (Arc, V) { + let dst = Arc::make_mut(&mut p); + let v; + (*dst, v) = f(std::mem::take(dst)); + (p, v) +} + +/// Extracts a value from an `Arc`, cloning its content if necessary. #[inline] fn take_arc(p: Arc) -> T { match Arc::try_unwrap(p) { @@ -86,20 +139,140 @@ fn take_arc(p: Arc) -> T { } /// The return type of the `bounds()` method. +/// /// See the comments of the public `bounds()` method. -pub(crate) type Bounds<'a, K, V> = (Option<(K, &'a V)>, Option<(K, &'a V)>); +pub(crate) type Bounds<'a, K, V> = (Option<(&'a K, &'a V)>, Option<(&'a K, &'a V)>); + +mod int_key { + /// Limit the types that can be used as `IntKey` to unsigned integer types. + /// + /// Warning! Do not implement this trait for other types. + pub(super) trait UnsignedInt {} + + impl UnsignedInt for u64 {} + impl UnsignedInt for u128 {} +} + +/// An integer key type for `IntMap` (implemented for `u64` and `u128`). +#[allow(private_bounds)] +pub trait IntKey: + Sized + + Eq + + Ord + + Copy + + Sub + + BitAnd + + BitOr + + BitXor + + Not + + Shl + + Debug + + int_key::UnsignedInt +{ + /// The type's zero value. + fn zero() -> Self; + + /// The type's unit value. + fn one() -> Self; + + /// The type's maximum value. + fn max_value() -> Self; + + /// The type's size in bits. + #[inline] + fn size_bits() -> u8 { + size_of::() as u8 * 8 + } + + /// Returns the number of leading zeros in the binary representation of `self`. + fn leading_zeros(self) -> u32; +} + +impl IntKey for u64 { + #[inline] + fn zero() -> Self { + 0 + } + + #[inline] + fn one() -> Self { + 1 + } + + #[inline] + fn max_value() -> Self { + Self::MAX + } + + #[inline] + fn leading_zeros(self) -> u32 { + self.leading_zeros() + } +} -impl Tree { - fn get(&self, key: u64) -> Option<&T> { +impl IntKey for u128 { + #[inline] + fn zero() -> Self { + 0 + } + + #[inline] + fn one() -> Self { + 1 + } + + #[inline] + fn max_value() -> Self { + Self::MAX + } + + #[inline] + fn leading_zeros(self) -> u32 { + self.leading_zeros() + } +} + +/// Conversion from actual key type (`K`) to `IntKey`. +/// +/// The compiler doesn't like us using `From` / `Into` for this, so we define +/// our own trait instead. +pub trait AsInt: Copy + Ord { + type Repr: IntKey; + + fn as_int(&self) -> Self::Repr; +} + +impl AsInt for u64 { + type Repr = u64; + + #[inline] + fn as_int(&self) -> u64 { + *self + } +} + +impl AsInt for Id { + type Repr = u64; + + #[inline] + fn as_int(&self) -> u64 { + self.get() + } +} + +impl Tree { + fn get(&self, key: K::Repr) -> Option<&V> { match self { Tree::Empty => None, + Tree::Leaf(k, v) => { - if key == *k { + if key == k.as_int() { Some(v) } else { None } } + Tree::Branch { prefix, branching_bit, @@ -108,7 +281,7 @@ impl Tree { } => { if !matches_prefix(key, *prefix, *branching_bit) { None - } else if key & (1 << branching_bit) == 0 { + } else if key & (K::Repr::one() << *branching_bit) == K::Repr::zero() { (*left).get(key) } else { (*right).get(key) @@ -118,55 +291,62 @@ impl Tree { } /// See the comments of the public `bounds()` method. - fn bounds(&self, key: u64) -> Bounds { + fn bounds(&self, key: &K) -> Bounds { match self { Tree::Empty => (None, None), + Tree::Leaf(k, v) => match key.cmp(k) { - Ordering::Less => (None, Some((*k, v))), - Ordering::Equal => (Some((key, v)), Some((key, v))), - Ordering::Greater => (Some((*k, v)), None), + Ordering::Less => (None, Some((k, v))), + Ordering::Equal => (Some((k, v)), Some((k, v))), + Ordering::Greater => (Some((k, v)), None), }, + Tree::Branch { prefix, branching_bit, left, right, - } => match mask(key, *branching_bit).cmp(prefix) { - Ordering::Less => (None, (*left).min()), - Ordering::Greater => ((*right).max(), None), - Ordering::Equal => { - if key & (1 << branching_bit) == 0 { - let (start, end) = (*left).bounds(key); - if end.is_none() { - (start, (*right).min()) - } else { - (start, end) - } - } else { - let (start, end) = (*right).bounds(key); - if start.is_none() { - ((*left).max(), end) + } => { + let key_int = key.as_int(); + match mask(key_int, *branching_bit).cmp(prefix) { + Ordering::Less => (None, (*left).min()), + Ordering::Greater => ((*right).max(), None), + Ordering::Equal => { + if key_int & (K::Repr::one() << *branching_bit) == K::Repr::zero() { + let (start, end) = (*left).bounds(key); + if end.is_none() { + (start, (*right).min()) + } else { + (start, end) + } } else { - (start, end) + let (start, end) = (*right).bounds(key); + if start.is_none() { + ((*left).max(), end) + } else { + (start, end) + } } } } - }, + } } } - // Returns the smallest key/value pair in the given tree. - // If the tree is empty, then it returns `None`. - fn min(&self) -> Option<(u64, &T)> { + /// Returns the smallest key/value pair in this tree. + /// If the tree is empty, then it returns `None`. + fn min(&self) -> Option<(&K, &V)> { let mut node = self; loop { match node { Tree::Empty => { return None; } + Tree::Leaf(k, v) => { - return Some((*k, v)); + return Some((k, v)); } + Tree::Branch { prefix: _, branching_bit: _, @@ -179,18 +359,20 @@ impl Tree { } } - // Returns the largest key/value pair in the given tree. - // If the tree is empty, then it returns `None`. - fn max(&self) -> Option<(u64, &T)> { + /// Returns the largest key/value pair in this tree. + /// If the tree is empty, then it returns `None`. + fn max(&self) -> Option<(&K, &V)> { let mut node = self; loop { match node { Tree::Empty => { return None; } + Tree::Leaf(k, v) => { - return Some((*k, v)); + return Some((k, v)); } + Tree::Branch { prefix: _, branching_bit: _, @@ -203,72 +385,141 @@ impl Tree { } } - fn insert(self, key: u64, value: T) -> Self { + fn insert(self, key: K, value: V) -> (Self, Option) { match self { - Tree::Empty => Tree::Leaf(key, value), + Tree::Empty => (Tree::Leaf(key, value), None), + Tree::Leaf(k, v) => { if k == key { - Tree::Leaf(k, value) + (Tree::Leaf(k, value), Some(v)) } else { - join( - key, - Arc::new(Tree::Leaf(key, value)), - k, - Arc::new(Tree::Leaf(k, v)), + ( + join( + key.as_int(), + Arc::new(Tree::Leaf(key, value)), + k.as_int(), + Arc::new(Tree::Leaf(k, v)), + ), + None, ) } } + Tree::Branch { prefix, branching_bit, left, right, } => { - if matches_prefix(key, prefix, branching_bit) { - if key & (1 << branching_bit) == 0 { - Tree::Branch { - prefix, - branching_bit, - left: with_arc(left, |l| l.insert(key, value)), - right, - } + let key_int = key.as_int(); + if matches_prefix(key_int, prefix, branching_bit) { + if key_int & (K::Repr::one() << branching_bit) == K::Repr::zero() { + let (left, res) = with_arc2(left, |l| l.insert(key, value)); + ( + Tree::Branch { + prefix, + branching_bit, + left, + right, + }, + res, + ) } else { - Tree::Branch { - prefix, - branching_bit, - left, - right: with_arc(right, |r| r.insert(key, value)), - } + let (right, res) = with_arc2(right, |r| r.insert(key, value)); + ( + Tree::Branch { + prefix, + branching_bit, + left, + right, + }, + res, + ) } } else { - join( - key, - Arc::new(Tree::Leaf(key, value)), - prefix, - Arc::new(Tree::Branch { + ( + join( + key_int, + Arc::new(Tree::Leaf(key, value)), prefix, - branching_bit, - left, - right, - }), + Arc::new(Tree::Branch { + prefix, + branching_bit, + left, + right, + }), + ), + None, ) } } } } + fn remove(self, key: &K) -> (Self, Option) { + match self { + Tree::Empty => (Tree::Empty, None), + + Tree::Leaf(k, v) if &k == key => (Tree::Empty, Some(v)), + + Tree::Leaf(..) => (self, None), + + Tree::Branch { + prefix, + branching_bit, + left, + right, + } if matches_prefix(key.as_int(), prefix, branching_bit) => { + if key.as_int() & (K::Repr::one() << branching_bit) == K::Repr::zero() { + let (left, res) = take_arc(left).remove(key); + match left { + Tree::Empty => (take_arc(right), res), + _ => ( + Tree::Branch { + prefix, + branching_bit, + left: Arc::new(left), + right, + }, + res, + ), + } + } else { + let (right, res) = take_arc(right).remove(key); + match right { + Tree::Empty => (take_arc(left), res), + _ => ( + Tree::Branch { + prefix, + branching_bit, + left, + right: Arc::new(right), + }, + res, + ), + } + } + } + + Tree::Branch { .. } => (self, None), + } + } + fn union(self, other: Self) -> Self { match (self, other) { (Tree::Empty, t) | (t, Tree::Empty) => t, - (Tree::Leaf(k, v), t) => t.insert(k, v), + + (Tree::Leaf(k, v), t) => t.insert(k, v).0, + (t, Tree::Leaf(k, v)) => { - if t.get(k).is_some() { + if t.get(k.as_int()).is_some() { // In case of collision, retain the value in `self`. t } else { - t.insert(k, v) + t.insert(k, v).0 } } + ( Tree::Branch { prefix: p0, @@ -292,14 +543,14 @@ impl Tree { right: with_arc(right0, move |r| r.union(take_arc(right1))), } } else if b0 > b1 && matches_prefix(p1, p0, b0) { + // Pattern p1 contains p0 as a sub-pattern. let t = Tree::Branch { prefix: p1, branching_bit: b1, left: left1, right: right1, }; - // Pattern p1 contains p0 as a sub-pattern. - if p1 & (1 << b0) == 0 { + if p1 & (K::Repr::one() << b0) == K::Repr::zero() { Tree::Branch { prefix: p0, branching_bit: b0, @@ -322,7 +573,7 @@ impl Tree { left: left0, right: right0, }; - if p0 & (1 << b1) == 0 { + if p0 & (K::Repr::one() << b1) == K::Repr::zero() { Tree::Branch { prefix: p1, branching_bit: b1, @@ -356,23 +607,93 @@ impl Tree { } } + /// Splits the tree in two just before the given key. + pub fn split(self, key: &K) -> (Self, Self) { + match self { + Tree::Empty => (Tree::Empty, Tree::Empty), + + Tree::Leaf(k, _) if k.as_int() < key.as_int() => (self, Tree::Empty), + + Tree::Leaf(..) => (Tree::Empty, self), + + Tree::Branch { + prefix, + branching_bit, + left, + right, + } if matches_prefix(key.as_int(), prefix, branching_bit) => { + if key.as_int() & (K::Repr::one() << branching_bit) == K::Repr::zero() { + let (ll, lr) = take_arc(left).split(key); + (ll, lr.union(take_arc(right))) + } else { + let (rl, rr) = take_arc(right).split(key); + (take_arc(left).union(rl), rr) + } + } + + Tree::Branch { prefix, .. } => { + if prefix < key.as_int() { + (self, Tree::Empty) + } else { + (Tree::Empty, self) + } + } + } + } + fn len(&self) -> usize { match self { Tree::Empty => 0, + Tree::Leaf(_, _) => 1, + Tree::Branch { left, right, .. } => left.len() + right.len(), } } } -/// Purely functional persistent sorted map with an integer key. +impl ValidateEq for Tree +where + K: AsInt + PartialEq + std::fmt::Debug, + V: ValidateEq + Clone, +{ + fn validate_eq(&self, rhs: &Self) -> Result<(), String> { + let mut left_iter = IntMapIter::new(self); + let mut right_iter = IntMapIter::new(rhs); + + loop { + match (left_iter.next(), right_iter.next()) { + (None, None) => return Ok(()), + + (Some((lk, lv)), Some((rk, rv))) => { + if lk != rk { + return Err(format!("Key divergence: {:#?} != {:#?}", lk, rk)); + } + if let Err(err) = lv.validate_eq(rv) { + return Err(format!("Value divergence @{:#?}: {}", lk, err)); + } + } + + _ => { + return Err(format!( + "Length divergence: {} != {}", + self.len(), + rhs.len() + )) + } + } + } + } +} + +/// Purely functional persistent sorted map with an integer-like key. /// -/// Persistence means that the map can be cheaply (in O(1)) cloned, and each +/// Persistence means that the map can be cheaply cloned (in `O(1)`), and each /// version can be modified independently. /// /// This data structure provides blazingly fast lookups (often comparable to or -/// surpassing a HashMap), while inserts are relatively expensive (2-10x slower -/// than a HashMap, depending on the size of the value and the map). +/// surpassing a `HashMap`) with relatively expensive inserts (2-10x slower than +/// a `HashMap`, depending on the size of the value and the map). /// /// The implementation is based on big-endian patricia trees. /// @@ -380,54 +701,74 @@ impl Tree { /// September 1998, pages 77-86, /// http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.37.5452 #[derive(Clone, Debug)] -pub struct IntMap(Tree); +pub struct IntMap(Tree); -impl Default for IntMap { +impl Default for IntMap { fn default() -> Self { Self(Tree::Empty) } } -impl IntMap { +impl IntMap { /// Creates a new empty map. pub fn new() -> Self { Self::default() } - /// Looks up a value by integer key. + /// Looks up a value by key. /// - /// Complexity: O(min(N, 64)) - pub fn get(&self, key: u64) -> Option<&T> { - self.0.get(key) - } - - // Returns `(lower, upper)` inclusive bounds for the given key such that: - // - `lower` is the largest key/value pair in the tree that is smaller or equal to the - // given key. If such a key doesn't exist then, `lower = None`. - // - `upper` is the smallest key/value pair in the tree that is larger or equal to the - // given key. If such a key doesn't exist then, `upper = None`. - // - // In all cases the following post-conditions hold: - // - lower.0 <= key <= upper.0, - // - for all i in [lower.0 + 1..upper.0 - 1]: self.get(i) == None, - // - lower == Some((k, v)) implies self.get(k) == v, - // - upper == Some((k, v)) implies self.get(k) == v, - // - // Complexity: O(min(N, 64)) - pub fn bounds(&self, key: u64) -> Bounds { + /// Complexity: `O(min(N, |key|))`. + pub fn get(&self, key: &K) -> Option<&V> { + self.0.get(key.as_int()) + } + + /// Returns `true` if the map contains the specified key. + /// + /// Complexity: `O(min(N, |key|))`. + pub fn contains_key(&self, key: &K) -> bool { + self.0.get(key.as_int()).is_some() + } + + /// Returns `(lower, upper)` inclusive bounds for the given key such that: + /// - `lower` is the largest key/value pair in the tree that is smaller than + /// or equal to the given key. If such a key doesn't exist, then + /// `lower = None`. + /// - `upper` is the smallest key/value pair in the tree that is larger than + /// or equal to the given key. If such a key doesn't exist, then + /// `upper = None`. + /// + /// In all cases the following post-conditions hold: + /// - `lower.0 <= key <= upper.0`, + /// - `for all i in [lower.0 + 1..upper.0 - 1]: self.get(i) == None`, + /// - `lower == Some((k, v))` implies `self.get(k) == v`, + /// - `upper == Some((k, v))` implies `self.get(k) == v`, + /// + /// Complexity: `O(min(N, |key|))`. + pub fn bounds(&self, key: &K) -> Bounds { self.0.bounds(key) } - /// Inserts a new entry into this map. + /// Inserts a new entry into this map. Returns the mutated map and the previous + /// value for the key, if any. /// - /// Complexity: O(min(N, 64)) - pub fn insert(self, key: u64, value: T) -> Self { - Self(self.0.insert(key, value)) + /// Complexity: `O(min(N, |key|))`. + pub fn insert(self, key: K, value: V) -> (Self, Option) { + let (tree, res) = self.0.insert(key, value); + (Self(tree), res) + } + + /// Removes the entry with the given key from this map. Returns the mutated map + /// and the removed value, if any. + /// + /// Complexity: `O(min(N, |key|))`. + pub fn remove(self, key: &K) -> (Self, Option) { + let (tree, res) = self.0.remove(key); + (Self(tree), res) } /// Unions two maps, preferring entries from self in case of a collision. /// - /// Complexity: O(N + M) + /// Complexity: `O(N + M)` pub fn union(self, other: Self) -> Self { Self(self.0.union(other.0)) } @@ -435,64 +776,299 @@ impl IntMap { /// Returns an iterator over key-value pairs. /// The keys are guaranteed to be sorted. /// - /// A full traversal requires O(N) operations. - pub fn iter(&self) -> IntMapIter<'_, T> { + /// A full traversal requires `O(N)` operations. + pub fn iter(&self) -> IntMapIter<'_, K, V> { IntMapIter::new(&self.0) } + /// Returns an iterator over the keys. + /// The keys are guaranteed to be sorted. + /// + /// A full traversal requires `O(N)` operations. + pub fn keys(&self) -> impl Iterator { + IntMapIter::new(&self.0).map(|(k, _v)| k) + } + /// Returns the number of entries in this map. /// - /// Complexity: O(N) + /// Complexity: `O(N)` pub fn len(&self) -> usize { self.0.len() } /// Returns true if this map is empty. /// - /// Complexity: O(1) + /// Complexity: `O(1)` pub fn is_empty(&self) -> bool { matches!(self.0, Tree::Empty) } - /// Returns the largest key in the given tree. + /// Returns the largest key in this map. /// If the tree is empty, then it returns `None`. - pub fn max_key(&self) -> Option { + /// + /// Complexity: `O(min(N, |key|))`. + pub fn max_key(&self) -> Option<&K> { self.0.max().map(|(k, _v)| k) } } -impl std::iter::FromIterator<(u64, T)> for IntMap { - fn from_iter(iter: I) -> Self +impl std::iter::FromIterator<(K, V)> for IntMap { + fn from_iter(iter: Iter) -> Self + where + Iter: IntoIterator, + { + let mut m = Self::new(); + for (k, v) in iter { + m = m.insert(k, v).0; + } + m + } +} + +impl PartialEq for IntMap { + fn eq(&self, other: &Self) -> bool { + self.iter().eq(other.iter()) + } +} +impl Eq for IntMap {} + +impl ValidateEq for IntMap +where + K: AsInt + PartialEq + std::fmt::Debug, + V: ValidateEq + Clone, +{ + fn validate_eq(&self, rhs: &Self) -> Result<(), String> { + self.0.validate_eq(&rhs.0) + } +} + +/// An internally mutable variant of `IntMap`. +/// +/// The underlying tree is still a persistent data structure, so different +/// copies of the same `MutableIntMap` can be modified independently. And the +/// performance is similar, since it's based on the same internals. +#[derive(Clone, Debug)] +pub struct MutableIntMap { + tree: Tree, + len: usize, +} + +impl Default for MutableIntMap { + fn default() -> Self { + Self { + tree: Tree::Empty, + len: 0, + } + } +} + +impl MutableIntMap { + /// Creates a new empty map. + pub fn new() -> Self { + Self::default() + } + + /// Looks up a value by integer key. + /// + /// Complexity: `O(min(N, |key|))`. + pub fn get(&self, key: &K) -> Option<&V> { + self.tree.get(key.as_int()) + } + + /// Returns `true` if the map contains a value for the specified key. + /// + /// Complexity: `O(min(N, |key|))`. + pub fn contains_key(&self, key: &K) -> bool { + self.tree.get(key.as_int()).is_some() + } + + /// Returns `(lower, upper)` inclusive bounds for the given key such that: + /// - `lower` is the largest key/value pair in the tree that is smaller than + /// or equal to the given key. If such a key doesn't exist, then + /// `lower = None`. + /// - `upper` is the smallest key/value pair in the tree that is larger than + /// or equal to the given key. If such a key doesn't exist, then + /// `upper = None`. + /// + /// In all cases the following post-conditions hold: + /// - `lower.0 <= key <= upper.0`, + /// - `for all i in [lower.0 + 1..upper.0 - 1]: self.get(i) == None`, + /// - `lower == Some((k, v))` implies `self.get(k) == v`, + /// - `upper == Some((k, v))` implies `self.get(k) == v`, + /// + /// Complexity: `O(min(N, |key|))`. + pub fn bounds(&self, key: &K) -> Bounds { + self.tree.bounds(key) + } + + /// Inserts a new entry into this map. Returns the previous value for the key, + /// if any. + /// + /// Complexity: `O(min(N, |key|))`. + pub fn insert(&mut self, key: K, value: V) -> Option { + let tree = std::mem::take(&mut self.tree); + let res; + (self.tree, res) = tree.insert(key, value); + + if res.is_none() { + self.len += 1; + } + debug_assert_eq!(self.tree.len(), self.len); + + res + } + + /// Removes and returns the entry with the given key, if any, from this map. + /// + /// Complexity: `O(min(N, |key|))`. + pub fn remove(&mut self, key: &K) -> Option { + let tree = std::mem::take(&mut self.tree); + let res; + (self.tree, res) = tree.remove(key); + + if res.is_some() { + self.len -= 1; + } + debug_assert_eq!(self.tree.len(), self.len); + + res + } + + /// Unions two maps, preferring entries from `self`` in case of a collision. + /// + /// Complexity: `O(N + M)` + pub fn union(&mut self, other: Self) { + let tree = std::mem::take(&mut self.tree); + self.tree = tree.union(other.tree); + // TODO(MR-645): Have `Tree::union()` also return the new length. + self.len = self.tree.len(); + } + + /// Splits the collection into two at the given key. Returns everything after + /// the given key, including the key. + /// + /// Complexity: `O(min(N, |key|))`. + pub fn split_off(&mut self, key: &K) -> Self { + let tree = std::mem::take(&mut self.tree); + let right; + (self.tree, right) = tree.split(key); + + let old_len = self.len; + // TODO(MR-645): Have `Tree::split()` also return the new lengths. + self.len = self.tree.len(); + + Self { + tree: right, + len: old_len - self.len, + } + } + + /// Returns an iterator over key-value pairs. + /// The keys are guaranteed to be sorted. + /// + /// A full traversal requires O(N) operations. + pub fn iter(&self) -> IntMapIter<'_, K, V> { + IntMapIter::new(&self.tree) + } + + /// Returns an iterator over the keys. + /// The keys are guaranteed to be sorted. + /// + /// A full traversal requires O(N) operations. + pub fn keys(&self) -> impl Iterator { + IntMapIter::new(&self.tree).map(|(k, _v)| k) + } + + /// Returns an iterator over the values, in order by key. + /// + /// A full traversal requires O(N) operations. + pub fn values(&self) -> impl Iterator { + IntMapIter::new(&self.tree).map(|(_k, v)| v) + } + + /// Returns the number of entries in this map. + /// + /// Complexity: `O(1)` + pub fn len(&self) -> usize { + self.len + } + + /// Returns true if this map is empty. + /// + /// Complexity: `O(1)` + pub fn is_empty(&self) -> bool { + matches!(self.tree, Tree::Empty) + } + + /// Returns the smallest key in this map. + /// If the tree is empty, then it returns `None`. + /// + /// Complexity: `O(min(N, |key|))`. + pub fn min_key(&self) -> Option<&K> { + self.tree.min().map(|(k, _v)| k) + } + + /// Returns the largest key in this map. + /// If the tree is empty, then it returns `None`. + /// + /// Complexity: `O(min(N, |key|))`. + pub fn max_key(&self) -> Option<&K> { + self.tree.max().map(|(k, _v)| k) + } +} + +impl std::iter::FromIterator<(K, V)> for MutableIntMap { + fn from_iter(iter: Iter) -> Self where - I: IntoIterator, + Iter: IntoIterator, { let mut m = Self::new(); for (k, v) in iter { - m = m.insert(k, v); + m.insert(k, v); } m } } -/// Iterates over an IntMap, visiting keys in sorted order. -pub struct IntMapIter<'a, T>( +impl PartialEq for MutableIntMap { + fn eq(&self, other: &Self) -> bool { + self.iter().eq(other.iter()) + } +} +impl Eq for MutableIntMap {} + +impl ValidateEq for MutableIntMap +where + K: AsInt + PartialEq + std::fmt::Debug, + V: ValidateEq + Clone, +{ + fn validate_eq(&self, rhs: &Self) -> Result<(), String> { + if self.len != rhs.len { + return Err(format!("Length divergence: {} != {}", self.len, rhs.len)); + } + self.tree.validate_eq(&rhs.tree) + } +} + +/// Iterates over an `IntMap`, visiting keys in sorted order. +pub struct IntMapIter<'a, K: AsInt, V>( /// The stack of subtrees we haven't visited yet. - /// Trees in the back should be visited first. - Vec<&'a Tree>, + /// Trees in the back are visited first. + Vec<&'a Tree>, ); -impl<'a, T> IntMapIter<'a, T> { - fn new(root: &'a Tree) -> Self { +impl<'a, K: AsInt, V> IntMapIter<'a, K, V> { + fn new(root: &'a Tree) -> Self { Self(vec![root]) } } -impl<'a, T> std::iter::Iterator for IntMapIter<'a, T> { - type Item = (u64, &'a T); +impl<'a, K: AsInt, V: Clone> std::iter::Iterator for IntMapIter<'a, K, V> { + type Item = (&'a K, &'a V); fn next(&mut self) -> Option { let mut p = self.0.pop()?; - // Find the most-left subtree, pushing all the right nodes onto the + // Find the leftmost subtree, pushing all the right hand side nodes onto the // stack. while let Tree::Branch { left, right, .. } = p { self.0.push(right); @@ -500,30 +1076,77 @@ impl<'a, T> std::iter::Iterator for IntMapIter<'a, T> { } match p { Tree::Empty => None, - Tree::Leaf(k, v) => Some((*k, v)), + Tree::Leaf(k, v) => Some((k, v)), Tree::Branch { .. } => unreachable!(), } } } +/// Consuming iterator over an `IntMap`, visiting keys in sorted order. +/// +/// A full traversal requires `O(N)` operations. +pub struct IntMapIntoIter( + /// The stack of subtrees we haven't visited yet. + /// Trees in the back should be visited first. + Vec>, +); + +impl IntMapIntoIter { + fn new(root: Tree) -> Self { + Self(vec![root]) + } +} + +impl std::iter::Iterator for IntMapIntoIter { + type Item = (K, V); + + fn next(&mut self) -> Option { + let mut p = self.0.pop()?; + // Find the leftmost subtree, pushing all the right hand side nodes onto the + // stack. + while let Tree::Branch { left, right, .. } = p { + self.0.push(take_arc(right)); + p = take_arc(left); + } + match p { + Tree::Empty => None, + Tree::Leaf(k, v) => Some((k, v)), + Tree::Branch { .. } => unreachable!(), + } + } +} + +impl IntoIterator for MutableIntMap { + type Item = (K, V); + type IntoIter = IntMapIntoIter; + + fn into_iter(self) -> Self::IntoIter { + IntMapIntoIter::new(self.tree) + } +} + /// Finds the most significant bit in which two bit patterns disagree. #[inline] -fn branching_bit(p0: u64, p1: u64) -> u8 { +fn branching_bit(p0: I, p1: I) -> u8 { debug_assert_ne!(p0, p1); - let zs = (p0 ^ p1).leading_zeros(); - (63 - zs) as u8 + let zs = (p0 ^ p1).leading_zeros() as u8; + I::size_bits() - 1 - zs } /// Clears all the key bits at or lower than the branching bit. #[inline] -fn mask(key: u64, branching_bit: u8) -> u64 { - debug_assert!(branching_bit <= 63); - let m = 1 << branching_bit; - key & !(m | (m - 1)) +fn mask(key: I, branching_bit: u8) -> I { + debug_assert!(branching_bit < I::size_bits()); + + // FYI: Using precomputed masks here and when testing the branching bit improves + // `u128` performance by about 20%, but has no effect on `u64` performance. + // Because two `[u128; 128]` arrays are potentially large for the CPU cache (2 + // KiB each), it is probably best to stick with computing the masks on the fly. + key & ((I::max_value() << 1) << branching_bit) } /// Checks if the key matches the branch prefix. #[inline] -fn matches_prefix(key: u64, branch_prefix: u64, branching_bit: u8) -> bool { +fn matches_prefix(key: I, branch_prefix: I, branching_bit: u8) -> bool { mask(key, branching_bit) == branch_prefix } diff --git a/rs/replicated_state/src/page_map/int_map/test.rs b/rs/replicated_state/src/page_map/int_map/test.rs index 4ec31df495f..3c13bb1e12e 100644 --- a/rs/replicated_state/src/page_map/int_map/test.rs +++ b/rs/replicated_state/src/page_map/int_map/test.rs @@ -1,12 +1,14 @@ -use super::IntMap; +use super::*; +use proptest::prelude::*; +use std::collections::BTreeMap; #[test] fn test_int_map_consecutive_inserts() { - let m: IntMap = (0..100u64).map(|x| (x, x + 100)).collect(); + let m: IntMap = (0..100u64).map(|x| (x, x + 100)).collect(); for i in 0..100u64 { assert_eq!( - m.get(i).cloned(), + m.get(&i).cloned(), Some(i + 100), "failed to find inserted values, map: {:?}", m @@ -16,59 +18,57 @@ fn test_int_map_consecutive_inserts() { #[test] fn test_int_map_sparse_inserts() { - let m: IntMap = (0..100u64) + let m: IntMap = (0..100u64) .filter(|x| x % 2 == 0) .map(|x| (x, x + 100)) .collect(); for i in 0..100u64 { if i % 2 == 0 { - assert_eq!(m.get(i).cloned(), Some(i + 100)); + assert_eq!(m.get(&i).cloned(), Some(i + 100)); } else { - assert_eq!(m.get(i).cloned(), None); + assert_eq!(m.get(&i).cloned(), None); } } } #[test] fn test_int_map_union() { - let lmap: IntMap = (1..101u64).map(|x| (x, x)).collect(); - let rmap: IntMap = (50..150u64).map(|x| (x, x + 100)).collect(); + let lmap: IntMap = (1..101u64).map(|x| (x, x)).collect(); + let rmap: IntMap = (50..150u64).map(|x| (x, x + 100)).collect(); let m = rmap.union(lmap); - assert!(m.get(0).is_none()); + assert!(m.get(&0).is_none()); for i in 1..50u64 { - assert_eq!(m.get(i).cloned(), Some(i)); + assert_eq!(m.get(&i).cloned(), Some(i)); } for i in 50..150u64 { - assert_eq!(m.get(i).cloned(), Some(i + 100), "Map: {:?}", m); + assert_eq!(m.get(&i).cloned(), Some(i + 100), "Map: {:?}", m); } - assert!(m.get(150).is_none()); + assert!(m.get(&150).is_none()); } #[test] fn test_iter() { - use std::collections::BTreeMap; - - let int_map: IntMap<_> = (1..100u64).map(|x| (x, x)).collect(); + let int_map: IntMap = (1..100u64).map(|x| (x, x)).collect(); let btree_map: BTreeMap<_, _> = (1..100u64).map(|x| (x, x)).collect(); - assert!(int_map.iter().eq(btree_map.iter().map(|(k, v)| (*k, v)))); + assert!(int_map.iter().eq(btree_map.iter())); } #[test] fn test_int_map_bounds() { - let m: IntMap = (10..=100u64).map(|x| (7 * x, 0)).collect(); + let m: IntMap = (10..=100u64).map(|x| (7 * x, 0)).collect(); for i in 0..800 { - let (start, end) = m.bounds(i); + let (start, end) = m.bounds(&i); if (70..=700).contains(&i) { - assert_eq!(start, Some(((i / 7) * 7, &0))); - assert_eq!(end, Some((((i + 6) / 7) * 7, &0))); + assert_eq!(start, Some((&((i / 7) * 7), &0))); + assert_eq!(end, Some((&(((i + 6) / 7) * 7), &0))); } else if i < 70 { assert_eq!(start, None); - assert_eq!(end, Some((70, &0))); + assert_eq!(end, Some((&70, &0))); } else { - assert_eq!(start, Some((700, &0))); + assert_eq!(start, Some((&700, &0))); assert_eq!(end, None) } } @@ -76,23 +76,444 @@ fn test_int_map_bounds() { #[test] fn test_max_key() { - let m = IntMap::::new(); + let m = IntMap::::new(); assert_eq!(m.max_key(), None); - let m = m.insert(100, 101); - assert_eq!(m.max_key(), Some(100)); - let m = m.insert(10, 101); - assert_eq!(m.max_key(), Some(100)); - let m = m.insert(1000, 101); - assert_eq!(m.max_key(), Some(1000)); - let m = m.insert(1000000, 101); - assert_eq!(m.max_key(), Some(1000000)); + let m = m.insert(100, 101).0; + assert_eq!(m.max_key(), Some(&100)); + let m = m.insert(10, 101).0; + assert_eq!(m.max_key(), Some(&100)); + let m = m.insert(1000, 101).0; + assert_eq!(m.max_key(), Some(&1000)); + let m = m.insert(1000000, 101).0; + assert_eq!(m.max_key(), Some(&1000000)); } #[test] fn test_max_key_range() { - let mut m = IntMap::::new(); + let mut m = IntMap::::new(); for i in 0..1000u64 { - m = m.insert(i, i + 100); - assert_eq!(m.max_key(), Some(i)); + m = m.insert(i, i + 100).0; + assert_eq!(m.max_key(), Some(&i)); + } +} + +#[test_strategy::proptest] +fn test_insert(#[strategy(proptest::collection::vec(0u64..20u64, 10))] keys: Vec) { + let mut btree_map = BTreeMap::new(); + let mut int_map = IntMap::new(); + let mut mutable_int_map = MutableIntMap::new(); + for (value, key) in keys.into_iter().enumerate() { + let expected = btree_map.insert(key, value); + + let previous; + (int_map, previous) = int_map.insert(key, value); + prop_assert!(is_well_formed(&int_map.0)); + prop_assert_eq!(expected, previous); + prop_assert_eq!(btree_map.len(), int_map.len()); + prop_assert!(btree_map.iter().eq(int_map.iter())); + + prop_assert!(is_well_formed(&mutable_int_map.tree)); + prop_assert_eq!(expected, mutable_int_map.insert(key, value)); + prop_assert_eq!(btree_map.len(), mutable_int_map.len()); + prop_assert!(btree_map.iter().eq(mutable_int_map.iter())); + } +} + +/// Creates 3 maps with identical contents, from the given keys. +#[allow(clippy::type_complexity)] +fn make_maps( + keys: Vec, +) -> ( + BTreeMap, + IntMap, + MutableIntMap, +) { + let mut btree_map = BTreeMap::new(); + let mut int_map = IntMap::new(); + let mut mutable_int_map = MutableIntMap::new(); + for (value, key) in keys.into_iter().enumerate() { + btree_map.insert(key, value); + int_map = int_map.insert(key, value).0; + mutable_int_map.insert(key, value); + } + assert!(is_well_formed(&int_map.0)); + assert!(is_well_formed(&mutable_int_map.tree)); + (btree_map, int_map, mutable_int_map) +} + +#[test_strategy::proptest] +fn test_lookup( + #[strategy(proptest::collection::vec(0u64..20u64, 0..10))] keys: Vec, + #[strategy(proptest::collection::vec(0u64..20u64, 10))] lookups: Vec, +) { + let (btree_map, int_map, mutable_int_map) = make_maps(keys); + + for key in lookups { + prop_assert_eq!(btree_map.get(&key), int_map.get(&key)); + prop_assert_eq!(btree_map.get(&key), mutable_int_map.get(&key)); + + prop_assert_eq!(btree_map.contains_key(&key), int_map.contains_key(&key)); + prop_assert_eq!( + btree_map.contains_key(&key), + mutable_int_map.contains_key(&key) + ); + } +} + +#[test_strategy::proptest] +fn test_bounds( + #[strategy(proptest::collection::vec(0u64..20u64, 0..10))] keys: Vec, + #[strategy(proptest::collection::vec(0u64..20u64, 10))] lookups: Vec, +) { + let (btree_map, int_map, mutable_int_map) = make_maps(keys); + + for key in lookups { + let (lower, upper) = int_map.bounds(&key); + prop_assert_eq!((lower, upper), mutable_int_map.bounds(&key)); + + if lower == upper { + if let Some((k, v)) = lower { + // Exact match. + prop_assert_eq!(k, &key); + prop_assert_eq!(btree_map.get(&key), Some(v)); + } else { + // Empty map. + prop_assert!(int_map.is_empty()); + } + continue; + } + + prop_assert_eq!(btree_map.range(..key).next_back(), lower); + prop_assert_eq!(btree_map.range(key..).next(), upper); + } + + prop_assert_eq!( + btree_map.last_key_value().map(|(k, _)| k), + int_map.max_key() + ); + prop_assert_eq!( + btree_map.first_key_value().map(|(k, _)| k), + mutable_int_map.min_key() + ); + prop_assert_eq!( + btree_map.last_key_value().map(|(k, _)| k), + mutable_int_map.max_key() + ); +} + +#[test_strategy::proptest] +fn test_remove( + #[strategy(proptest::collection::vec(0u64..20u64, 0..10))] inserts: Vec, + #[strategy(proptest::collection::vec(0u64..20u64, 10))] removes: Vec, +) { + let (mut btree_map, mut int_map, mut mutable_int_map) = make_maps(inserts); + + for key in removes { + let expected = btree_map.remove(&key); + + let removed; + (int_map, removed) = int_map.remove(&key); + prop_assert!(is_well_formed(&int_map.0)); + prop_assert_eq!(expected, removed); + prop_assert_eq!(btree_map.len(), int_map.len()); + prop_assert!(btree_map.iter().eq(int_map.iter())); + + prop_assert!(is_well_formed(&mutable_int_map.tree)); + prop_assert_eq!(expected, mutable_int_map.remove(&key)); + prop_assert_eq!(btree_map.len(), mutable_int_map.len()); + prop_assert!(btree_map.iter().eq(mutable_int_map.iter())); + } +} + +#[test_strategy::proptest] +fn test_union( + #[strategy(proptest::collection::vec(0u64..20u64, 0..10))] first: Vec, + #[strategy(proptest::collection::vec(0u64..20u64, 0..10))] second: Vec, +) { + let (mut first_btree_map, first_int_map, mut mutable_int_map) = make_maps(first); + let (mut btree_map, second_int_map, second_mutable_int_map) = make_maps(second); + + btree_map.append(&mut first_btree_map); + let int_map = first_int_map.union(second_int_map); + mutable_int_map.union(second_mutable_int_map); + + prop_assert!(is_well_formed(&int_map.0)); + prop_assert_eq!(btree_map.len(), int_map.len()); + prop_assert!(btree_map.iter().eq(int_map.iter())); + + prop_assert!(is_well_formed(&mutable_int_map.tree)); + prop_assert_eq!(btree_map.len(), mutable_int_map.len()); + prop_assert!(btree_map.iter().eq(mutable_int_map.iter())); +} + +#[test_strategy::proptest] +fn test_split_off( + #[strategy(proptest::collection::vec(0u64..20u64, 0..10))] keys: Vec, + #[strategy(0u64..20u64)] split_key: u64, +) { + let (mut btree_map, _, mut mutable_int_map) = make_maps(keys); + + let btree_right = btree_map.split_off(&split_key); + let mutable_int_right = mutable_int_map.split_off(&split_key); + + prop_assert!(is_well_formed(&mutable_int_map.tree)); + prop_assert_eq!(btree_map.len(), mutable_int_map.len()); + prop_assert!(btree_map.iter().eq(mutable_int_map.iter())); + + prop_assert!(is_well_formed(&mutable_int_right.tree)); + prop_assert_eq!(btree_right.len(), mutable_int_right.len()); + prop_assert!(btree_right.iter().eq(mutable_int_right.iter())); +} + +#[test_strategy::proptest] +fn test_len(#[strategy(proptest::collection::vec(0u64..20u64, 0..10))] keys: Vec) { + let (btree_map, int_map, mutable_int_map) = make_maps(keys); + + prop_assert_eq!(btree_map.len(), int_map.len()); + prop_assert_eq!(btree_map.len(), mutable_int_map.len()); + + prop_assert_eq!(btree_map.is_empty(), int_map.is_empty()); + prop_assert_eq!(btree_map.is_empty(), mutable_int_map.is_empty()); +} + +#[test_strategy::proptest] +fn test_iterators(#[strategy(proptest::collection::vec(0u64..20u64, 0..10))] keys: Vec) { + let (btree_map, int_map, mutable_int_map) = make_maps(keys); + + prop_assert!(btree_map.iter().eq(int_map.iter())); + prop_assert!(btree_map.iter().eq(mutable_int_map.iter())); + + prop_assert!(btree_map.keys().eq(int_map.keys())); + prop_assert!(btree_map.keys().eq(mutable_int_map.keys())); + + prop_assert!(btree_map.values().eq(mutable_int_map.values())); + + prop_assert!(btree_map.into_iter().eq(mutable_int_map.into_iter())); +} + +#[test_strategy::proptest] +fn test_from_iter(#[strategy(proptest::collection::vec(0u64..20u64, 0..10))] keys: Vec) { + let (btree_map, _, mutable_int_map) = make_maps(keys); + + let int_map = btree_map.clone().into_iter().collect::>(); + prop_assert!(is_well_formed(&int_map.0)); + prop_assert_eq!(btree_map.len(), int_map.len()); + prop_assert!(btree_map.iter().eq(int_map.iter())); + + let mutable_int_map = mutable_int_map.into_iter().collect::>(); + prop_assert!(is_well_formed(&mutable_int_map.tree)); + prop_assert_eq!(btree_map.len(), mutable_int_map.len()); + prop_assert!(btree_map.iter().eq(mutable_int_map.iter())); +} + +#[test_strategy::proptest] +fn test_eq(#[strategy(proptest::collection::vec(0u64..20u64, 0..10))] keys: Vec) { + use ic_validate_eq::ValidateEq; + use std::fmt::Debug; + + #[derive(Clone, Debug, PartialEq)] + struct Foo(usize); + impl ValidateEq for Foo { + fn validate_eq(&self, rhs: &Self) -> Result<(), String> { + if self.0 != rhs.0 { + return Err(format!("lhs = {:#?}, rhs = {:#?}", self, rhs)); + } + Ok(()) + } + } + + fn assert_eq(lhs: &M, rhs: &M) -> Result<(), TestCaseError> + where + M: Debug + PartialEq + ValidateEq, + { + prop_assert_eq!(lhs, rhs); + prop_assert_eq!(Ok(()), lhs.validate_eq(rhs)); + Ok(()) + } + fn assert_ne(lhs: &M, rhs: &M) -> Result<(), TestCaseError> + where + M: Debug + PartialEq + ValidateEq, + { + prop_assert_ne!(lhs, rhs); + prop_assert_ne!(Ok(()), lhs.validate_eq(rhs)); + Ok(()) + } + + let mut int_map = IntMap::new(); + let mut mutable_int_map = MutableIntMap::new(); + for (value, key) in keys.into_iter().enumerate() { + int_map = int_map.insert(key, Foo(value)).0; + mutable_int_map.insert(key, Foo(value)); + } + + let initial_int_map = int_map.clone(); + let initial_mutable_int_map = mutable_int_map.clone(); + assert_eq(&initial_int_map, &int_map)?; + assert_eq(&initial_mutable_int_map, &mutable_int_map)?; + + // No longer equal after an insert. + let int_map = int_map.insert(99, Foo(13)).0; + prop_assert!(mutable_int_map.insert(99, Foo(13)).is_none()); + assert_ne(&initial_int_map, &int_map)?; + assert_ne(&initial_mutable_int_map, &mutable_int_map)?; + + // Need a non-empty map to test equality after remove. + if !initial_int_map.is_empty() { + let key = initial_int_map.max_key().unwrap(); + + // No longer equal after a remove. + let int_map = initial_int_map.clone().remove(key).0; + let mut mutable_int_map = initial_mutable_int_map.clone(); + prop_assert!(mutable_int_map.remove(key).is_some()); + assert_ne(&initial_int_map, &int_map)?; + assert_ne(&initial_mutable_int_map, &mutable_int_map)?; + } +} + +#[test_strategy::proptest] +fn test_u64_values( + #[strategy(proptest::collection::vec(any::(), 0..10))] keys: Vec, + #[strategy(proptest::collection::vec(any::(), 10))] lookups: Vec, +) { + let (btree_map, int_map, mutable_int_map) = make_maps(keys); + + for key in lookups { + prop_assert_eq!(btree_map.get(&key), int_map.get(&key)); + prop_assert_eq!(btree_map.get(&key), mutable_int_map.get(&key)); + } +} + +#[test_strategy::proptest] +fn test_u128_values( + #[strategy(proptest::collection::vec(any::(), 0..10))] keys: Vec, + #[strategy(proptest::collection::vec(any::(), 10))] lookups: Vec, +) { + #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] + struct Key128(u64, u64); + impl Key128 { + fn new(value: u128) -> Self { + Key128((value >> 64) as u64, value as u64) + } + } + impl AsInt for Key128 { + type Repr = u128; + + #[inline] + fn as_int(&self) -> u128 { + (self.0 as u128) << 64 | self.1 as u128 + } + } + + let mut btree_map = BTreeMap::new(); + let mut int_map = IntMap::new(); + let mut mutable_int_map = MutableIntMap::new(); + for (value, key) in keys.into_iter().enumerate() { + let key = Key128::new(key); + btree_map.insert(key, value); + int_map = int_map.insert(key, value).0; + mutable_int_map.insert(key, value); + } + prop_assert!(is_well_formed(&int_map.0)); + prop_assert!(is_well_formed(&mutable_int_map.tree)); + + for key in lookups { + let key = Key128::new(key); + prop_assert_eq!(btree_map.get(&key), int_map.get(&key)); + prop_assert_eq!(btree_map.get(&key), mutable_int_map.get(&key)); + } +} + +#[test] +fn test_validate_eq() { + use ic_validate_eq::ValidateEq; + + #[derive(Clone, Debug, PartialEq)] + struct MyU64(u64); + impl ValidateEq for MyU64 { + fn validate_eq(&self, rhs: &Self) -> Result<(), String> { + if self.0 != rhs.0 { + return Err(format!("{} != {}", self.0, rhs.0)); + } + Ok(()) + } + } + + fn do_validate_eq(lhs: &[(u64, u64)], rhs: &[(u64, u64)]) -> Result<(), String> { + let left: IntMap = lhs.iter().map(|(k, v)| (*k, MyU64(*v))).collect(); + let right: IntMap = rhs.iter().map(|(k, v)| (*k, MyU64(*v))).collect(); + left.validate_eq(&right) + } + + assert!(do_validate_eq(&[], &[]).is_ok()); + assert!(do_validate_eq(&[(1, 2)], &[(1, 2)]).is_ok()); + assert!(do_validate_eq(&[(1, 2), (7, 8)], &[(1, 2), (7, 8)]).is_ok()); + + assert_eq!( + Err("Length divergence: 0 != 1".to_string()), + do_validate_eq(&[], &[(1, 2)]) + ); + assert_eq!( + Err("Length divergence: 2 != 1".to_string()), + do_validate_eq(&[(1, 2), (7, 8)], &[(1, 2)]) + ); + assert_eq!( + Err("Key divergence: 7 != 8".to_string()), + do_validate_eq(&[(1, 2), (7, 8)], &[(1, 2), (8, 8)]) + ); + assert_eq!( + Err("Value divergence @7: 8 != 7".to_string()), + do_validate_eq(&[(1, 2), (7, 8)], &[(1, 2), (7, 7)]) + ); +} + +/// Returns `true` iff the tree is well formed. +fn is_well_formed(tree: &Tree) -> bool { + match tree { + Tree::Empty => true, + + Tree::Leaf(_, _) => true, + + Tree::Branch { + prefix, + branching_bit, + left, + right, + } => { + let valid_left = match left.as_ref() { + Tree::Leaf(k, _) => { + matches_prefix(k.as_int(), *prefix, *branching_bit) + && k.as_int() & (K::Repr::one() << *branching_bit) == K::Repr::zero() + } + Tree::Branch { + prefix: p, + branching_bit: b, + .. + } => { + b < branching_bit + && matches_prefix(*p, *prefix, *branching_bit) + && *p & (K::Repr::one() << *branching_bit) == K::Repr::zero() + && is_well_formed(left.as_ref()) + } + Tree::Empty => false, + }; + let valid_right = match right.as_ref() { + Tree::Leaf(k, _) => { + matches_prefix(k.as_int(), *prefix, *branching_bit) + && k.as_int() & (K::Repr::one() << *branching_bit) != K::Repr::zero() + } + Tree::Branch { + prefix: p, + branching_bit: b, + .. + } => { + b < branching_bit + && matches_prefix(*p, *prefix, *branching_bit) + && *p & (K::Repr::one() << *branching_bit) != K::Repr::zero() + && is_well_formed(right.as_ref()) + } + Tree::Empty => false, + }; + valid_left && valid_right + } } } diff --git a/rs/replicated_state/src/page_map/page_allocator.rs b/rs/replicated_state/src/page_map/page_allocator.rs index 2792dde4acf..4a8d133be67 100644 --- a/rs/replicated_state/src/page_map/page_allocator.rs +++ b/rs/replicated_state/src/page_map/page_allocator.rs @@ -118,7 +118,7 @@ impl PageAllocator { /// The generic parameters simplify the usage with `PageDelta::iter()`. pub fn serialize_page_delta<'a, I>(&'a self, page_delta: I) -> PageDeltaSerialization where - I: IntoIterator, + I: IntoIterator, { self.0.serialize_page_delta(page_delta) } diff --git a/rs/replicated_state/src/page_map/page_allocator/mmap.rs b/rs/replicated_state/src/page_map/page_allocator/mmap.rs index 35f7bba7693..850c5a95289 100644 --- a/rs/replicated_state/src/page_map/page_allocator/mmap.rs +++ b/rs/replicated_state/src/page_map/page_allocator/mmap.rs @@ -262,11 +262,11 @@ impl PageAllocatorInner { // See the comments of the corresponding method in `PageAllocator`. pub fn serialize_page_delta<'a, I>(&'a self, page_delta: I) -> PageDeltaSerialization where - I: IntoIterator, + I: IntoIterator, { let pages: Vec<_> = page_delta .into_iter() - .map(|(page_index, page)| MmapPageSerialization { + .map(|(&page_index, page)| MmapPageSerialization { page_index, file_offset: page.0.offset, validation: page.0.validation, @@ -629,7 +629,7 @@ impl MmapBasedPageAllocatorCore { MmapAdvise::MADV_HUGEPAGE, ) }.unwrap_or_else(|err| { - // We don't need to panic, madvise failing is not a problem, + // We don't need to panic, madvise failing is not a problem, // it will only mean that we are not using huge pages. println!( "MmapPageAllocator failed to madvise {} bytes at address {:?} for memory file #{}: {}", diff --git a/rs/replicated_state/src/page_map/page_allocator/page_bytes.rs b/rs/replicated_state/src/page_map/page_allocator/page_bytes.rs index 360278efc5c..3c5720c6542 100644 --- a/rs/replicated_state/src/page_map/page_allocator/page_bytes.rs +++ b/rs/replicated_state/src/page_map/page_allocator/page_bytes.rs @@ -19,7 +19,7 @@ where struct PageBytesVisitor; -impl<'de> Visitor<'de> for PageBytesVisitor { +impl Visitor<'_> for PageBytesVisitor { type Value = PageBytes; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { diff --git a/rs/replicated_state/src/page_map/storage.rs b/rs/replicated_state/src/page_map/storage.rs index eb1774f7c79..9c92209e657 100644 --- a/rs/replicated_state/src/page_map/storage.rs +++ b/rs/replicated_state/src/page_map/storage.rs @@ -161,7 +161,8 @@ pub(crate) struct StorageImpl { overlays: Vec, } -pub fn verify(storage_layout: &dyn StorageLayout) -> Result<(), PersistenceError> { +/// Validate that the overlay files are loadable. +pub fn validate(storage_layout: &dyn StorageLayout) -> Result<(), PersistenceError> { StorageImpl::load(storage_layout)?; Ok(()) } @@ -473,7 +474,7 @@ impl OverlayFile { for (index, data) in delta.iter() { let shard = index.get() / lsmt_config.shard_num_pages; page_data[shard as usize].push(data.contents()); - page_indices[shard as usize].push(index); + page_indices[shard as usize].push(*index); } for shard in 0..num_shards { diff --git a/rs/replicated_state/src/page_map/storage/tests.rs b/rs/replicated_state/src/page_map/storage/tests.rs index e92f73d1221..c64ffbb8d6f 100644 --- a/rs/replicated_state/src/page_map/storage/tests.rs +++ b/rs/replicated_state/src/page_map/storage/tests.rs @@ -9,7 +9,7 @@ use std::{ use crate::page_map::{ storage::{ - verify, Checkpoint, FileIndex, MergeCandidate, MergeDestination, OverlayFile, + validate, Checkpoint, FileIndex, MergeCandidate, MergeDestination, OverlayFile, PageIndexRange, Shard, Storage, StorageLayout, CURRENT_OVERLAY_VERSION, PAGE_INDEX_RANGE_NUM_BYTES, SIZE_NUM_BYTES, VERSION_NUM_BYTES, }, @@ -562,7 +562,7 @@ fn write_overlays_and_verify_with_tempdir( page_map.update( combined_delta .iter() - .map(|(i, p)| (i, p.contents())) + .map(|(i, p)| (*i, p.contents())) .collect::>() .as_slice(), ); @@ -1388,7 +1388,7 @@ fn overlapping_shards_is_an_error() { tempdir.path().join("000000_010_vmemory_0.overlay"), ] ); - assert!(verify(&ShardedTestStorageLayout { + assert!(validate(&ShardedTestStorageLayout { dir_path: tempdir.path().to_path_buf(), base: tempdir.path().join("vmemory_0.bin"), overlay_suffix: "vmemory_0.overlay".to_owned(), @@ -1399,7 +1399,7 @@ fn overlapping_shards_is_an_error() { tempdir.path().join("000000_011_vmemory_0.overlay"), ) .unwrap(); - assert!(verify(&ShardedTestStorageLayout { + assert!(validate(&ShardedTestStorageLayout { dir_path: tempdir.path().to_path_buf(), base: tempdir.path().join("vmemory_0.bin"), overlay_suffix: "vmemory_0.overlay".to_owned(), @@ -1425,7 +1425,7 @@ fn returns_an_error_if_file_size_is_not_a_multiple_of_page_size() { .write_all(&vec![1; PAGE_SIZE / 2]) .unwrap(); - match verify(&base_only_storage_layout(heap_file.to_path_buf())) { + match validate(&base_only_storage_layout(heap_file.to_path_buf())) { Err(err) => assert!( err.is_invalid_heap_file(), "Expected invalid heap file error, got {:?}", @@ -1566,10 +1566,8 @@ mod proptest_tests { prop_vec(instruction_strategy(), 1..20) } - proptest! { - #[test] - fn random_instructions(instructions in instructions_strategy()) { - write_overlays_and_verify(instructions); - } + #[test_strategy::proptest] + fn random_instructions(#[strategy(instructions_strategy())] instructions: Vec) { + write_overlays_and_verify(instructions); } } diff --git a/rs/replicated_state/src/replicated_state.rs b/rs/replicated_state/src/replicated_state.rs index 606361756bf..383ba48353f 100644 --- a/rs/replicated_state/src/replicated_state.rs +++ b/rs/replicated_state/src/replicated_state.rs @@ -1,6 +1,9 @@ use super::{ canister_state::CanisterState, - metadata_state::{IngressHistoryState, Stream, Streams, SystemMetadata}, + metadata_state::{ + subnet_call_context_manager::{IDkgDealingsContext, SignWithThresholdContext}, + IngressHistoryState, Stream, StreamMap, SystemMetadata, + }, }; use crate::{ canister_snapshots::CanisterSnapshots, @@ -8,10 +11,6 @@ use crate::{ queues::{CanisterInput, CanisterQueuesLoopDetector}, system_state::{push_input, CanisterOutputQueuesIterator}, }, - metadata_state::{ - subnet_call_context_manager::{IDkgDealingsContext, SignWithThresholdContext}, - StreamMap, - }, CanisterQueues, }; use ic_base_types::PrincipalId; @@ -438,18 +437,44 @@ impl ReplicatedState { epoch_query_stats: RawQueryStats, canister_snapshots: CanisterSnapshots, ) -> Self { - let mut res = Self { + Self { canister_states, metadata, subnet_queues, consensus_queue: Vec::new(), epoch_query_stats, canister_snapshots, - }; - res.update_stream_guaranteed_responses_size_bytes(); - res + } } + /// References into _all_ fields. + pub fn component_refs( + &self, + ) -> ( + &BTreeMap, + &SystemMetadata, + &CanisterQueues, + &Vec, + &RawQueryStats, + &CanisterSnapshots, + ) { + let ReplicatedState { + ref canister_states, + ref metadata, + ref subnet_queues, + ref consensus_queue, + ref epoch_query_stats, + ref canister_snapshots, + } = self; + ( + canister_states, + metadata, + subnet_queues, + consensus_queue, + epoch_query_stats, + canister_snapshots, + ) + } pub fn canister_state(&self, canister_id: &CanisterId) -> Option<&CanisterState> { self.canister_states.get(canister_id) } @@ -910,16 +935,6 @@ impl ReplicatedState { &self.epoch_query_stats } - /// Updates the byte size of guaranteed responses in streams for each canister. - fn update_stream_guaranteed_responses_size_bytes(&mut self) { - for (canister_id, size_bytes) in self.metadata.streams.guaranteed_responses_size_bytes() { - if let Some(canister_state) = self.canister_states.get_mut(canister_id) { - canister_state.set_stream_guaranteed_responses_size_bytes(*size_bytes); - } - } - Arc::make_mut(&mut self.metadata.streams).prune_zero_guaranteed_responses_size_bytes() - } - /// Returns the number of canisters in this `ReplicatedState`. pub fn num_canisters(&self) -> usize { self.canister_states.len() @@ -1252,8 +1267,6 @@ impl ReplicatedState { |canister_id| canister_states.contains_key(&canister_id), subnet_queues, ); - - self.update_stream_guaranteed_responses_size_bytes(); } } @@ -1282,33 +1295,31 @@ pub trait ReplicatedStateMessageRouting { fn streams(&self) -> &StreamMap; /// Removes the streams from this `ReplicatedState`. - fn take_streams(&mut self) -> Streams; + fn take_streams(&mut self) -> StreamMap; /// Atomically replaces the streams. - fn put_streams(&mut self, streams: Streams); + fn put_streams(&mut self, streams: StreamMap); } impl ReplicatedStateMessageRouting for ReplicatedState { fn streams(&self) -> &StreamMap { - self.metadata.streams.streams() + &self.metadata.streams } - fn take_streams(&mut self) -> Streams { + fn take_streams(&mut self) -> StreamMap { std::mem::take(Arc::make_mut(&mut self.metadata.streams)) } - fn put_streams(&mut self, streams: Streams) { - // Should never replace a non-empty Streams via `put_streams()`. - assert!(self.metadata.streams.streams().is_empty()); + fn put_streams(&mut self, streams: StreamMap) { + // Should never replace a non-empty StreamMap via `put_streams()`. + assert!(self.metadata.streams.is_empty()); *Arc::make_mut(&mut self.metadata.streams) = streams; - self.update_stream_guaranteed_responses_size_bytes(); } } pub mod testing { use super::*; - use crate::metadata_state::testing::StreamsTesting; /// Exposes `ReplicatedState` internals for use in other crates' unit tests. pub trait ReplicatedStateTesting { @@ -1353,7 +1364,7 @@ pub mod testing { fn modify_streams(&mut self, f: F) { let mut streams = self.take_streams(); - streams.modify_streams(f); + f(&mut streams); self.put_streams(streams); } diff --git a/rs/replicated_state/tests/metadata_state.rs b/rs/replicated_state/tests/metadata_state.rs index 485c77b1b58..ad4a1d27462 100644 --- a/rs/replicated_state/tests/metadata_state.rs +++ b/rs/replicated_state/tests/metadata_state.rs @@ -1,26 +1,21 @@ use ic_protobuf::state::{queues::v1 as pb_queues, system_metadata::v1 as pb_metadata}; +use ic_replicated_state::{metadata_state::SubnetMetrics, Stream}; use ic_test_utilities_state::{arb_stream, arb_subnet_metrics}; -use proptest::prelude::*; use std::convert::TryInto; -proptest! { - #[test] - fn roundtrip_conversion_stream_proptest(stream in arb_stream(0, 10, 0, 100)) { - assert_eq!( - stream, - pb_queues::Stream::from(&stream) - .try_into() - .unwrap() - ); - } +#[test_strategy::proptest] +fn roundtrip_conversion_stream_proptest(#[strategy(arb_stream(0, 10, 0, 100))] stream: Stream) { + assert_eq!(stream, pb_queues::Stream::from(&stream).try_into().unwrap()); +} - #[test] - fn roundtrip_conversion_subnet_metrics(subnet_metrics in arb_subnet_metrics()) { - assert_eq!( - subnet_metrics, - pb_metadata::SubnetMetrics::from(&subnet_metrics) - .try_into() - .unwrap() - ); - } +#[test_strategy::proptest] +fn roundtrip_conversion_subnet_metrics( + #[strategy(arb_subnet_metrics())] subnet_metrics: SubnetMetrics, +) { + assert_eq!( + subnet_metrics, + pb_metadata::SubnetMetrics::from(&subnet_metrics) + .try_into() + .unwrap() + ); } diff --git a/rs/replicated_state/tests/replicated_state.rs b/rs/replicated_state/tests/replicated_state.rs index 9ef252598df..3df3c525500 100644 --- a/rs/replicated_state/tests/replicated_state.rs +++ b/rs/replicated_state/tests/replicated_state.rs @@ -156,7 +156,7 @@ impl ReplicatedStateFixture { fn push_to_streams(&mut self, msgs: Vec) { let mut streams = self.state.take_streams(); for msg in msgs.into_iter() { - streams.push(SUBNET_ID, msg); + streams.entry(SUBNET_ID).or_default().push(msg); } self.state.put_streams(streams); } @@ -341,30 +341,6 @@ fn memory_taken_by_subnet_queues() { assert_wasm_custom_sections_memory_taken(0, &fixture); } -#[test] -fn memory_taken_by_stream_responses() { - let mut fixture = ReplicatedStateFixture::new(); - - // Zero memory used initially. - assert_execution_memory_taken(0, &fixture); - assert_message_memory_taken(0, &fixture); - assert_canister_history_memory_taken(0, &fixture); - assert_wasm_custom_sections_memory_taken(0, &fixture); - - // Push a request and a response into a stream. - let response = response_to(OTHER_CANISTER_ID); - fixture.push_to_streams(vec![ - request_to(OTHER_CANISTER_ID).into(), - response.clone().into(), - ]); - - // Memory only used by response, not request. - assert_execution_memory_taken(0, &fixture); - assert_message_memory_taken(response.count_bytes(), &fixture); - assert_canister_history_memory_taken(0, &fixture); - assert_wasm_custom_sections_memory_taken(0, &fixture); -} - #[test] fn memory_taken_by_wasm_custom_sections() { let mut custom_sections: BTreeMap = BTreeMap::new(); @@ -998,223 +974,294 @@ fn compatibility_for_input_source() { ); } -proptest! { - #[test] - fn peek_and_next_consistent( - (mut replicated_state, _, total_requests) in arb_replicated_state_with_output_queues(SUBNET_ID, 10, 10, Some(5)) - ) { - let mut output_iter = replicated_state.output_into_iter(); +#[test_strategy::proptest] +fn peek_and_next_consistent( + #[strategy(arb_replicated_state_with_output_queues(SUBNET_ID, 10, 10, Some(5)))] + state_and_queues: ( + ReplicatedState, + VecDeque>, + usize, + ), +) { + let (mut replicated_state, _, total_requests) = state_and_queues; - let mut num_requests = 0; - while let Some(msg) = output_iter.peek() { - num_requests += 1; + let mut output_iter = replicated_state.output_into_iter(); + + let mut num_requests = 0; + while let Some(msg) = output_iter.peek() { + num_requests += 1; + prop_assert_eq!(Some(msg.clone()), output_iter.next()); + } + + drop(output_iter); + prop_assert_eq!(total_requests, num_requests); + prop_assert_eq!(replicated_state.output_message_count(), 0); +} + +/// Replicated state with multiple canisters, each with multiple output queues +/// of size 1. Some messages are consumed, some (size 1) queues are excluded. +/// +/// Expect consumed + excluded to equal initial size. Expect the messages in +/// excluded queues to be left in the state. +#[test_strategy::proptest] +fn peek_and_next_consistent_with_exclude_queue( + #[strategy(arb_replicated_state_with_output_queues(SUBNET_ID, 10, 10, None))] state_and_queues: ( + ReplicatedState, + VecDeque>, + usize, + ), + #[strategy(0..=1)] start: i32, + #[strategy(2..=5)] exclude_step: i32, +) { + let (mut replicated_state, _, total_requests) = state_and_queues; + + let mut output_iter = replicated_state.output_into_iter(); + + let mut i = start; + let mut excluded = 0; + let mut consumed = 0; + while let Some(msg) = output_iter.peek() { + i += 1; + if i % exclude_step == 0 { + output_iter.exclude_queue(); + excluded += 1; + } else { prop_assert_eq!(Some(msg.clone()), output_iter.next()); + consumed += 1; } - - drop(output_iter); - prop_assert_eq!(total_requests, num_requests); - prop_assert_eq!(replicated_state.output_message_count(), 0); } - /// Replicated state with multiple canisters, each with multiple output queues - /// of size 1. Some messages are consumed, some (size 1) queues are excluded. - /// - /// Expect consumed + excluded to equal initial size. Expect the messages in - /// excluded queues to be left in the state. - #[test] - fn peek_and_next_consistent_with_exclude_queue( - (mut replicated_state, _, total_requests) in arb_replicated_state_with_output_queues(SUBNET_ID, 10, 10, None), - start in 0..=1, - exclude_step in 2..=5, - ) { - let mut output_iter = replicated_state.output_into_iter(); + drop(output_iter); + prop_assert_eq!(total_requests, excluded + consumed); + prop_assert_eq!(replicated_state.output_message_count(), excluded); +} - let mut i = start; - let mut excluded = 0; - let mut consumed = 0; - while let Some(msg) = output_iter.peek() { - i += 1; - if i % exclude_step == 0 { - output_iter.exclude_queue(); - excluded += 1; - } else { - prop_assert_eq!(Some(msg.clone()), output_iter.next()); - consumed += 1; - } +#[test_strategy::proptest] +fn iter_yields_correct_elements( + #[strategy(arb_replicated_state_with_output_queues(SUBNET_ID, 10, 10, None))] state_and_queues: ( + ReplicatedState, + VecDeque>, + usize, + ), +) { + let (mut replicated_state, mut raw_requests, _total_requests) = state_and_queues; + + let mut output_iter = replicated_state.output_into_iter(); + + for msg in &mut output_iter { + let mut requests = raw_requests.pop_front().unwrap(); + while requests.is_empty() { + requests = raw_requests.pop_front().unwrap(); + } + + if let Some(raw_msg) = requests.pop_front() { + prop_assert_eq!(&msg, &raw_msg, "Popped message does not correspond with expected message. popped: {:?}. expected: {:?}.", msg, raw_msg); + } else { + prop_assert!( + false, + "Pop yielded an element that was not contained in the respective queue" + ); } - drop(output_iter); - prop_assert_eq!(total_requests, excluded + consumed); - prop_assert_eq!(replicated_state.output_message_count(), excluded); + raw_requests.push_back(requests); } - #[test] - fn iter_yields_correct_elements( - (mut replicated_state, mut raw_requests, _total_requests) in arb_replicated_state_with_output_queues(SUBNET_ID, 10, 10, None), - ) { + drop(output_iter); + // Ensure that actually all elements have been consumed. + prop_assert_eq!( + raw_requests + .iter() + .map(|requests| requests.len()) + .sum::(), + 0 + ); + prop_assert_eq!(replicated_state.output_message_count(), 0); +} + +#[test_strategy::proptest] +fn iter_with_exclude_queue_yields_correct_elements( + #[strategy(arb_replicated_state_with_output_queues(SUBNET_ID, 10, 10, None))] state_and_queues: ( + ReplicatedState, + VecDeque>, + usize, + ), + #[strategy(0..=1)] start: i32, + #[strategy(2..=5)] ignore_step: i32, +) { + let (mut replicated_state, mut raw_requests, total_requests) = state_and_queues; + + let mut consumed = 0; + let mut ignored_requests = Vec::new(); + // Check whether popping elements with ignores in between yields the expected messages + { let mut output_iter = replicated_state.output_into_iter(); - for msg in &mut output_iter { + let mut i = start; + while let Some(msg) = output_iter.peek() { let mut requests = raw_requests.pop_front().unwrap(); while requests.is_empty() { requests = raw_requests.pop_front().unwrap(); } + i += 1; + if i % ignore_step == 0 { + // Popping the front of the requests will amount to the same as ignoring as + // we use queues of size one in this test. + let popped = requests.pop_front().unwrap(); + prop_assert_eq!(msg, &popped); + output_iter.exclude_queue(); + ignored_requests.push(popped); + // We push the queue to the front as the canister gets another chance if one + // of its queues are ignored in the current implementation. + raw_requests.push_front(requests); + continue; + } + + let msg = output_iter.next().unwrap(); if let Some(raw_msg) = requests.pop_front() { - prop_assert_eq!(&msg, &raw_msg, "Popped message does not correspond with expected message. popped: {:?}. expected: {:?}.", msg, raw_msg); + consumed += 1; + prop_assert_eq!( + &msg, + &raw_msg, + "Popped message does not correspond with expected message. popped: {:?}. expected: {:?}.", + msg, + raw_msg + ); } else { - prop_assert!(false, "Pop yielded an element that was not contained in the respective queue"); + prop_assert!( + false, + "Pop yielded an element that was not contained in the respective queue" + ); } raw_requests.push_back(requests); } - - drop(output_iter); - // Ensure that actually all elements have been consumed. - prop_assert_eq!(raw_requests.iter().map(|requests| requests.len()).sum::(), 0); - prop_assert_eq!(replicated_state.output_message_count(), 0); } - #[test] - fn iter_with_exclude_queue_yields_correct_elements( - (mut replicated_state, mut raw_requests, total_requests) in arb_replicated_state_with_output_queues(SUBNET_ID, 10, 10, None), - start in 0..=1, - ignore_step in 2..=5, - ) { - let mut consumed = 0; - let mut ignored_requests = Vec::new(); - // Check whether popping elements with ignores in between yields the expected messages - { - let mut output_iter = replicated_state.output_into_iter(); - - let mut i = start; - while let Some(msg) = output_iter.peek() { - - let mut requests = raw_requests.pop_front().unwrap(); - while requests.is_empty() { - requests = raw_requests.pop_front().unwrap(); - } - - i += 1; - if i % ignore_step == 0 { - // Popping the front of the requests will amount to the same as ignoring as - // we use queues of size one in this test. - let popped = requests.pop_front().unwrap(); - prop_assert_eq!(msg, &popped); - output_iter.exclude_queue(); - ignored_requests.push(popped); - // We push the queue to the front as the canister gets another chance if one - // of its queues are ignored in the current implementation. - raw_requests.push_front(requests); - continue; - } - - let msg = output_iter.next().unwrap(); - if let Some(raw_msg) = requests.pop_front() { - consumed += 1; - prop_assert_eq!(&msg, &raw_msg, "Popped message does not correspond with expected message. popped: {:?}. expected: {:?}.", msg, raw_msg); - } else { - prop_assert!(false, "Pop yielded an element that was not contained in the respective queue"); - } - - raw_requests.push_back(requests); - } - } - - let remaining_output = replicated_state.output_message_count(); + let remaining_output = replicated_state.output_message_count(); - prop_assert_eq!(remaining_output, total_requests - consumed); - prop_assert_eq!(remaining_output, ignored_requests.len()); + prop_assert_eq!(remaining_output, total_requests - consumed); + prop_assert_eq!(remaining_output, ignored_requests.len()); - for raw in ignored_requests { - let queues = if let Some(canister) = replicated_state.canister_states.get_mut(&raw.sender()) { - canister.system_state.queues_mut() - } else { - replicated_state.subnet_queues_mut() - }; - - let msg = queues.pop_canister_output(&raw.receiver()).unwrap(); - prop_assert_eq!(raw, msg); - } + for raw in ignored_requests { + let queues = if let Some(canister) = replicated_state.canister_states.get_mut(&raw.sender()) + { + canister.system_state.queues_mut() + } else { + replicated_state.subnet_queues_mut() + }; - prop_assert_eq!(replicated_state.output_message_count(), 0); + let msg = queues.pop_canister_output(&raw.receiver()).unwrap(); + prop_assert_eq!(raw, msg); } - #[test] - fn ignore_leaves_state_untouched( - (mut replicated_state, _, _) in arb_replicated_state_with_output_queues(SUBNET_ID, 10, 10, Some(5)), - ) { - let expected_state = replicated_state.clone(); - { - let mut output_iter = replicated_state.output_into_iter(); - - while output_iter.peek().is_some() { - output_iter.exclude_queue(); - } - } + prop_assert_eq!(replicated_state.output_message_count(), 0); +} - prop_assert_eq!(expected_state, replicated_state); - } +#[test_strategy::proptest] +fn ignore_leaves_state_untouched( + #[strategy(arb_replicated_state_with_output_queues(SUBNET_ID, 10, 10, Some(5)))] + state_and_queues: ( + ReplicatedState, + VecDeque>, + usize, + ), +) { + let (mut replicated_state, _, _) = state_and_queues; - #[test] - fn peek_next_loop_with_exclude_queue_terminates( - (mut replicated_state, _, _) in arb_replicated_state_with_output_queues(SUBNET_ID, 10, 10, Some(5)), - start in 0..=1, - ignore_step in 2..=5, - ) { + let expected_state = replicated_state.clone(); + { let mut output_iter = replicated_state.output_into_iter(); - let mut i = start; while output_iter.peek().is_some() { - i += 1; - if i % ignore_step == 0 { - output_iter.exclude_queue(); - continue; - } - output_iter.next(); + output_iter.exclude_queue(); } } - #[test] - fn iter_with_stale_entries_terminates( - (mut replicated_state, _, total_requests) in arb_replicated_state_with_output_queues(SUBNET_ID, 10, 10, Some(5)), - batch_time_seconds in any::(), - ) { - const NANOS_PER_SEC: u64 = 1_000_000_000; - replicated_state.metadata.batch_time = Time::from_nanos_since_unix_epoch(batch_time_seconds as u64 * NANOS_PER_SEC); - let timed_out_messages = replicated_state.time_out_messages(); - - // Just consume all output messages. - // - // We cannot check the exact ordering because timing out some messages messes it - // up, both across canisters and across a cainster's output queues. - let output_messages = replicated_state.output_into_iter().count(); - - // All messages have either been timed out or output. - prop_assert_eq!(total_requests, timed_out_messages + output_messages); - prop_assert_eq!(replicated_state.output_message_count(), 0); - } + prop_assert_eq!(expected_state, replicated_state); +} - #[test] - fn peek_next_loop_with_stale_entries_terminates( - (mut replicated_state, _, total_requests) in arb_replicated_state_with_output_queues(SUBNET_ID, 10, 10, Some(5)), - batch_time in any::(), - ) { - const NANOS_PER_SEC: u64 = 1_000_000_000; - replicated_state.metadata.batch_time = Time::from_nanos_since_unix_epoch(batch_time as u64 * NANOS_PER_SEC); - let timed_out_messages = replicated_state.time_out_messages(); +#[test_strategy::proptest] +fn peek_next_loop_with_exclude_queue_terminates( + #[strategy(arb_replicated_state_with_output_queues(SUBNET_ID, 10, 10, Some(5)))] + state_and_queues: ( + ReplicatedState, + VecDeque>, + usize, + ), + #[strategy(0..=1)] start: i32, + #[strategy(2..=5)] ignore_step: i32, +) { + let (mut replicated_state, _, _) = state_and_queues; - let mut output_iter = replicated_state.output_into_iter(); + let mut output_iter = replicated_state.output_into_iter(); - let mut output_messages = 0; - while let Some(msg) = output_iter.peek() { - output_messages += 1; - prop_assert_eq!(Some(msg.clone()), output_iter.next()); + let mut i = start; + while output_iter.peek().is_some() { + i += 1; + if i % ignore_step == 0 { + output_iter.exclude_queue(); + continue; } - drop(output_iter); + output_iter.next(); + } +} + +#[test_strategy::proptest] +fn iter_with_stale_entries_terminates( + #[strategy(arb_replicated_state_with_output_queues(SUBNET_ID, 10, 10, Some(5)))] + state_and_queues: ( + ReplicatedState, + VecDeque>, + usize, + ), + #[strategy(any::())] batch_time_seconds: u32, +) { + let (mut replicated_state, _, total_requests) = state_and_queues; + + const NANOS_PER_SEC: u64 = 1_000_000_000; + replicated_state.metadata.batch_time = + Time::from_nanos_since_unix_epoch(batch_time_seconds as u64 * NANOS_PER_SEC); + let timed_out_messages = replicated_state.time_out_messages(); - // All messages have either been timed out or output. - prop_assert_eq!(total_requests, timed_out_messages + output_messages); - prop_assert_eq!(replicated_state.output_message_count(), 0); + // Just consume all output messages. + // + // We cannot check the exact ordering because timing out some messages messes it + // up, both across canisters and across a cainster's output queues. + let output_messages = replicated_state.output_into_iter().count(); + + // All messages have either been timed out or output. + prop_assert_eq!(total_requests, timed_out_messages + output_messages); + prop_assert_eq!(replicated_state.output_message_count(), 0); +} + +#[test_strategy::proptest] +fn peek_next_loop_with_stale_entries_terminates( + #[strategy(arb_replicated_state_with_output_queues(SUBNET_ID, 10, 10, Some(5)))] + state_and_queues: ( + ReplicatedState, + VecDeque>, + usize, + ), + #[strategy(any::())] batch_time_seconds: u32, +) { + let (mut replicated_state, _, total_requests) = state_and_queues; + + const NANOS_PER_SEC: u64 = 1_000_000_000; + replicated_state.metadata.batch_time = + Time::from_nanos_since_unix_epoch(batch_time_seconds as u64 * NANOS_PER_SEC); + let timed_out_messages = replicated_state.time_out_messages(); + + let mut output_iter = replicated_state.output_into_iter(); + + let mut output_messages = 0; + while let Some(msg) = output_iter.peek() { + output_messages += 1; + prop_assert_eq!(Some(msg.clone()), output_iter.next()); } + drop(output_iter); + + // All messages have either been timed out or output. + prop_assert_eq!(total_requests, timed_out_messages + output_messages); + prop_assert_eq!(replicated_state.output_message_count(), 0); } diff --git a/rs/rosetta-api/icp/BUILD.bazel b/rs/rosetta-api/icp/BUILD.bazel index 199d511bd7e..e1fa7c56348 100644 --- a/rs/rosetta-api/icp/BUILD.bazel +++ b/rs/rosetta-api/icp/BUILD.bazel @@ -74,7 +74,6 @@ DEV_DEPENDENCIES = [ "//rs/ledger_suite/icrc1", "//rs/ledger_suite/icrc1/test_utils", "//rs/ledger_suite/icrc1/tokens_u256", - "//rs/nns/governance", "//rs/nns/governance/init", "//rs/nns/handlers/root/impl:root", "//rs/nns/test_utils", diff --git a/rs/rosetta-api/icp/Cargo.toml b/rs/rosetta-api/icp/Cargo.toml index b0f4855169a..2f9d00b6d75 100644 --- a/rs/rosetta-api/icp/Cargo.toml +++ b/rs/rosetta-api/icp/Cargo.toml @@ -59,7 +59,6 @@ futures = { workspace = true } ic-base-types = { path = "../../types/base_types" } ic-crypto-ed25519 = { path = "../../crypto/ed25519" } ic-ledger-canister-blocks-synchronizer-test-utils = { path = "ledger_canister_blocks_synchronizer/test_utils" } -ic-nns-governance = { path = "../../nns/governance" } ic-rosetta-test-utils = { path = "test_utils" } ic-types = { path = "../../types/types" } proptest = { workspace = true } diff --git a/rs/rosetta-api/icp/client/BUILD.bazel b/rs/rosetta-api/icp/client/BUILD.bazel index 6ce1355d941..946528dcd1a 100644 --- a/rs/rosetta-api/icp/client/BUILD.bazel +++ b/rs/rosetta-api/icp/client/BUILD.bazel @@ -8,6 +8,7 @@ DEPENDENCIES = [ "//rs/crypto/ed25519", "//rs/crypto/secp256k1", "//rs/ledger_suite/icp:icp_ledger", + "//rs/nns/governance/api", "//rs/rosetta-api/common/rosetta_core:rosetta-core", "//rs/rosetta-api/icp:ic-rosetta-api", "//rs/rosetta-api/icp:rosetta-api", diff --git a/rs/rosetta-api/icp/client/Cargo.toml b/rs/rosetta-api/icp/client/Cargo.toml index 6205a4a50ce..d894781b6a5 100644 --- a/rs/rosetta-api/icp/client/Cargo.toml +++ b/rs/rosetta-api/icp/client/Cargo.toml @@ -24,6 +24,7 @@ ic-crypto-ed25519 = { path = "../../../crypto/ed25519" } icp-ledger = { path = "../../../ledger_suite/icp" } icrc-ledger-types = { path = "../../../../packages/icrc-ledger-types" } ic-base-types = { path = "../../../types/base_types" } +ic-nns-governance-api = { path = "../../../nns/governance/api" } [dev-dependencies] ic-icp-rosetta-runner = { path = "../runner" } diff --git a/rs/rosetta-api/icp/client/src/lib.rs b/rs/rosetta-api/icp/client/src/lib.rs index 265d7d62ccd..8da437b9a10 100644 --- a/rs/rosetta-api/icp/client/src/lib.rs +++ b/rs/rosetta-api/icp/client/src/lib.rs @@ -3,6 +3,8 @@ use anyhow::Context; use candid::Nat; use candid::Principal; use ic_base_types::PrincipalId; +use ic_nns_governance_api::pb::v1::Proposal; +use ic_rosetta_api::ledger_client::pending_proposals_response::PendingProposalsResponse; use ic_rosetta_api::models::seconds::Seconds; use ic_rosetta_api::models::AccountType; use ic_rosetta_api::models::BlockIdentifier; @@ -16,6 +18,7 @@ use ic_rosetta_api::request_types::KeyMetadata; use ic_rosetta_api::request_types::NeuronIdentifierMetadata; use ic_rosetta_api::request_types::NeuronInfoMetadata; use ic_rosetta_api::request_types::PublicKeyOrPrincipal; +use ic_rosetta_api::request_types::RegisterVoteMetadata; use ic_rosetta_api::request_types::RequestType; use ic_rosetta_api::request_types::SetDissolveTimestampMetadata; use ic_rosetta_api::request_types::SpawnMetadata; @@ -32,6 +35,7 @@ use rosetta_core::identifiers::TransactionIdentifier; use rosetta_core::models::CurveType; use rosetta_core::models::RosettaSupportedKeyPair; use rosetta_core::objects::Amount; +use rosetta_core::objects::ObjectMap; use rosetta_core::objects::Operation; use rosetta_core::objects::PublicKey; use rosetta_core::objects::Signature; @@ -40,7 +44,6 @@ use rosetta_core::response_types::*; use serde::{Deserialize, Serialize}; use std::fmt::Debug; use url::ParseError; - pub struct RosettaClient { pub url: Url, pub http_client: Client, @@ -304,6 +307,37 @@ impl RosettaClient { }]) } + pub async fn build_register_vote_operations( + signer_principal: Principal, + neuron_index: u64, + proposal: u64, + vote: i32, + ) -> anyhow::Result> { + Ok(vec![Operation { + operation_identifier: OperationIdentifier { + index: 0, + network_index: None, + }, + related_operations: None, + type_: "REGISTER_VOTE".to_string(), + status: None, + account: Some(rosetta_core::identifiers::AccountIdentifier::from( + AccountIdentifier::new(PrincipalId(signer_principal), None), + )), + amount: None, + coin_change: None, + metadata: Some( + RegisterVoteMetadata { + neuron_index, + vote, + proposal: Some(proposal), + } + .try_into() + .map_err(|e| anyhow::anyhow!("Failed to convert metadata: {:?}", e))?, + ), + }]) + } + pub async fn build_change_auto_stake_maturity_operations( signer_principal: Principal, neuron_index: u64, @@ -1129,6 +1163,59 @@ impl RosettaClient { .await } + // Register a vote on a proposal using a specific neuron. + pub async fn register_vote( + &self, + network_identifier: NetworkIdentifier, + signer_keypair: &T, + register_vote_args: RosettaRegisterVoteArgs, + ) -> anyhow::Result + where + T: RosettaSupportedKeyPair, + { + let register_vote_operations = RosettaClient::build_register_vote_operations( + signer_keypair.generate_principal_id()?.0, + register_vote_args.neuron_index.unwrap_or(0), + register_vote_args.proposal, + register_vote_args.vote, + ) + .await?; + + self.make_submit_and_wait_for_transaction( + signer_keypair, + network_identifier, + register_vote_operations, + None, + None, + ) + .await + } + + // Retrieves the list of proposals that are currently pending. + pub async fn get_pending_proposals( + &self, + network_identifier: NetworkIdentifier, + ) -> anyhow::Result, String> { + let response = self + .call(CallRequest::new( + network_identifier.clone(), + "get_pending_proposals".to_owned(), + ObjectMap::new(), + )) + .await + .unwrap(); + + let pending_proposals: Vec = + PendingProposalsResponse::try_from(Some(response.result)) + .unwrap() + .pending_proposals + .into_iter() + .map(|p| p.proposal.unwrap()) + .collect(); + + Ok(pending_proposals) + } + /// A neuron can be set to automatically restake its maturity. pub async fn change_auto_stake_maturity( &self, @@ -1536,6 +1623,47 @@ impl RosettaSetNeuronDissolveDelayArgsBuilder { } } +pub struct RosettaRegisterVoteArgs { + pub neuron_index: Option, + pub proposal: u64, + pub vote: i32, +} + +impl RosettaRegisterVoteArgs { + pub fn builder(proposal: u64, vote: i32) -> RosettaRegisterVoteArgsBuilder { + RosettaRegisterVoteArgsBuilder::new(proposal, vote) + } +} + +pub struct RosettaRegisterVoteArgsBuilder { + proposal: u64, + vote: i32, + neuron_index: Option, +} + +impl RosettaRegisterVoteArgsBuilder { + pub fn new(proposal: u64, vote: i32) -> Self { + Self { + proposal, + vote, + neuron_index: None, + } + } + + pub fn with_neuron_index(mut self, neuron_index: u64) -> Self { + self.neuron_index = Some(neuron_index); + self + } + + pub fn build(self) -> RosettaRegisterVoteArgs { + RosettaRegisterVoteArgs { + proposal: self.proposal, + vote: self.vote, + neuron_index: self.neuron_index, + } + } +} + pub struct RosettaIncreaseNeuronStakeArgs { pub neuron_index: Option, pub additional_stake: Nat, diff --git a/rs/rosetta-api/icp/ledger_canister_blocks_synchronizer/BUILD.bazel b/rs/rosetta-api/icp/ledger_canister_blocks_synchronizer/BUILD.bazel index c2642f20c1b..39f15f2ecf0 100644 --- a/rs/rosetta-api/icp/ledger_canister_blocks_synchronizer/BUILD.bazel +++ b/rs/rosetta-api/icp/ledger_canister_blocks_synchronizer/BUILD.bazel @@ -17,6 +17,7 @@ DEPENDENCIES = [ "@crate_index//:chrono", "@crate_index//:ciborium", "@crate_index//:ic-agent", + "@crate_index//:reqwest", "@crate_index//:rusqlite", "@crate_index//:serde", "@crate_index//:tokio", diff --git a/rs/rosetta-api/icp/ledger_canister_blocks_synchronizer/Cargo.toml b/rs/rosetta-api/icp/ledger_canister_blocks_synchronizer/Cargo.toml index 453821ec207..65e6441cf66 100644 --- a/rs/rosetta-api/icp/ledger_canister_blocks_synchronizer/Cargo.toml +++ b/rs/rosetta-api/icp/ledger_canister_blocks_synchronizer/Cargo.toml @@ -20,6 +20,7 @@ ic-ledger-hash-of = { path = "../../../../packages/ic-ledger-hash-of" } ic-types = { path = "../../../types/types" } icp-ledger = { path = "../../../ledger_suite/icp" } on_wire = { path = "../../../rust_canisters/on_wire" } +reqwest = { workspace = true } rusqlite = { version = "~0.28.0", features = ["bundled"] } serde = { workspace = true } tokio = { workspace = true } diff --git a/rs/rosetta-api/icp/ledger_canister_blocks_synchronizer/src/canister_access.rs b/rs/rosetta-api/icp/ledger_canister_blocks_synchronizer/src/canister_access.rs index 23eef1784ab..81fae203c4c 100644 --- a/rs/rosetta-api/icp/ledger_canister_blocks_synchronizer/src/canister_access.rs +++ b/rs/rosetta-api/icp/ledger_canister_blocks_synchronizer/src/canister_access.rs @@ -1,6 +1,5 @@ #![allow(clippy::disallowed_types)] use dfn_protobuf::{ProtoBuf, ToProto}; -use ic_agent::agent::http_transport::reqwest_transport::ReqwestTransport; use ic_agent::identity::AnonymousIdentity; use ic_agent::{Agent, AgentError, NonceGenerator}; use ic_ledger_core::block::EncodedBlock; @@ -47,7 +46,8 @@ fn make_agent(url: Url) -> Result { let is_exchanges_testnet = url.host_str() == Some("exchanges.testnet.dfinity.network"); Agent::builder() .with_identity(AnonymousIdentity) - .with_transport(ReqwestTransport::create(url)?) + .with_url(url) + .with_http_client(reqwest::Client::new()) .with_nonce_generator(TimestampBlob::default()) // The testnet has an old replica version and the query // verification wouldn't work so we disable it diff --git a/rs/rosetta-api/icp/src/convert.rs b/rs/rosetta-api/icp/src/convert.rs index d15d8496331..f24984635b5 100644 --- a/rs/rosetta-api/icp/src/convert.rs +++ b/rs/rosetta-api/icp/src/convert.rs @@ -36,7 +36,6 @@ use std::convert::{TryFrom, TryInto}; /// This module converts from ledger_canister data structures to Rosetta data /// structures - pub fn to_rosetta_core_transaction( transaction_index: BlockIndex, transaction: Transaction, diff --git a/rs/rosetta-api/icp/src/ledger_client/handle_neuron_info.rs b/rs/rosetta-api/icp/src/ledger_client/handle_neuron_info.rs index 33afba58fad..7032f59820c 100644 --- a/rs/rosetta-api/icp/src/ledger_client/handle_neuron_info.rs +++ b/rs/rosetta-api/icp/src/ledger_client/handle_neuron_info.rs @@ -15,7 +15,7 @@ pub fn handle_neuron_info( // Check the response from governance call. let response: Result = candid::decode_one(bytes.as_ref()) .map_err(|err| format!("Could not decode NEURON_INFO response: {}", err))?; - return match response { + match response { Err(e) => Ok(Err(ApiError::InvalidRequest( false, format!("Could not retrieve neuron information: {}", e.error_message).into(), @@ -32,9 +32,9 @@ pub fn handle_neuron_info( hotkeys: neuron.hot_keys, staked_maturity_e8s: neuron.staked_maturity_e8s_equivalent, }); - return Ok(Ok(Some(output))); + Ok(Ok(Some(output))) } - }; + } } fn neuron_state(neuron: &Neuron) -> models::NeuronState { diff --git a/rs/rosetta-api/icp/src/request_handler/construction_payloads.rs b/rs/rosetta-api/icp/src/request_handler/construction_payloads.rs index eb596881af6..adbbc337072 100644 --- a/rs/rosetta-api/icp/src/request_handler/construction_payloads.rs +++ b/rs/rosetta-api/icp/src/request_handler/construction_payloads.rs @@ -9,7 +9,6 @@ use on_wire::IntoWire; use rand::Rng; use std::{collections::HashMap, sync::Arc, time::Duration}; -use crate::request_types::RefreshVotingPower; use crate::{ convert, convert::{make_read_state_from_update, to_arg, to_model_account_identifier}, @@ -25,8 +24,9 @@ use crate::{ request_handler::{make_sig_data, verify_network_id, RosettaRequestHandler}, request_types::{ AddHotKey, ChangeAutoStakeMaturity, Disburse, Follow, ListNeurons, MergeMaturity, - NeuronInfo, PublicKeyOrPrincipal, RegisterVote, RemoveHotKey, RequestType, - SetDissolveTimestamp, Spawn, Stake, StakeMaturity, StartDissolve, StopDissolve, + NeuronInfo, PublicKeyOrPrincipal, RefreshVotingPower, RegisterVote, RemoveHotKey, + RequestType, SetDissolveTimestamp, Spawn, Stake, StakeMaturity, StartDissolve, + StopDissolve, }, }; use ic_nns_governance_api::pb::v1::{ @@ -435,6 +435,8 @@ fn handle_list_neurons( include_neurons_readable_by_caller: true, include_empty_neurons_readable_by_caller: None, include_public_neurons_in_full_neurons: None, + page_number: None, + page_size: None, }; let update = HttpCanisterUpdate { canister_id: Blob(ic_nns_constants::GOVERNANCE_CANISTER_ID.get().to_vec()), diff --git a/rs/rosetta-api/icp/src/rosetta_server.rs b/rs/rosetta-api/icp/src/rosetta_server.rs index 1fb6ca756e2..1b5a93dd3cd 100644 --- a/rs/rosetta-api/icp/src/rosetta_server.rs +++ b/rs/rosetta-api/icp/src/rosetta_server.rs @@ -315,7 +315,7 @@ async fn rosetta_metrics() -> HttpResponse { let encoder = prometheus::TextEncoder::new(); encoder.encode(&metrics, &mut buffer).unwrap(); HttpResponse::Ok() - .content_type("text/html; charset=utf-8") + .content_type("text/plain; version=0.0.4; charset=utf-8") .body(String::from_utf8(buffer).unwrap()) } diff --git a/rs/rosetta-api/icp/tests/integration_tests/BUILD.bazel b/rs/rosetta-api/icp/tests/integration_tests/BUILD.bazel index 31998102aab..de7d458b565 100644 --- a/rs/rosetta-api/icp/tests/integration_tests/BUILD.bazel +++ b/rs/rosetta-api/icp/tests/integration_tests/BUILD.bazel @@ -43,7 +43,6 @@ rust_test_suite( "//rs/canister_sandbox:sandbox_launcher", "//rs/ledger_suite/icp/ledger:ledger-canister-wasm-notify-method", "//rs/pocket_ic_server:pocket-ic-server", - "//rs/replica", "//rs/rosetta-api/icp:ic-rosetta-api-rosetta-blocks", "//rs/rosetta-api/icp/test_utils/sender_canister:ic-sender-canister", "@mainnet_icp_ledger_canister//file", @@ -52,8 +51,8 @@ rust_test_suite( "CANISTER_LAUNCHER": "$(rootpath //rs/canister_sandbox)", "LEDGER_CANISTER_NOTIFY_METHOD_WASM_PATH": "$(rootpath //rs/ledger_suite/icp/ledger:ledger-canister-wasm-notify-method)", "POCKET_IC_BIN": "$(rootpath //rs/pocket_ic_server:pocket-ic-server)", - "REPLICA_BIN": "$(rootpath //rs/replica)", "ROSETTA_PATH": "$(rootpath //rs/rosetta-api/icp:ic-rosetta-api-rosetta-blocks)", + "RUST_TEST_THREADS": "4", "SANDBOX_LAUNCHER": "$(rootpath //rs/canister_sandbox:sandbox_launcher)", "IC_SENDER_CANISTER_WASM_PATH": "$(rootpath //rs/rosetta-api/icp/test_utils/sender_canister:ic-sender-canister)", "ICP_LEDGER_DEPLOYED_VERSION_WASM_PATH": "$(rootpath @mainnet_icp_ledger_canister//file)", @@ -63,8 +62,7 @@ rust_test_suite( flaky = True, proc_macro_deps = [ ], - # The test runs replica binary that constantly uses more than 100% cpu core + rosetta server. - tags = ["cpu:3"], + tags = ["cpu:4"], deps = DEPENDENCIES + [ ":rosetta-integration-tests-lib", "//rs/rosetta-api/icp/test_utils/sender_canister:ic_sender_canister_lib", diff --git a/rs/rosetta-api/icp/tests/integration_tests/tests/tests.rs b/rs/rosetta-api/icp/tests/integration_tests/tests/tests.rs index 9461ff50c0f..9da29d50400 100644 --- a/rs/rosetta-api/icp/tests/integration_tests/tests/tests.rs +++ b/rs/rosetta-api/icp/tests/integration_tests/tests/tests.rs @@ -18,7 +18,6 @@ use icp_rosetta_integration_tests::{start_rosetta, RosettaContext}; use icrc_ledger_types::icrc1::account::Account; use icrc_ledger_types::icrc1::transfer::{BlockIndex, TransferArg, TransferError}; use num_traits::cast::ToPrimitive; -use pocket_ic::WasmResult; use pocket_ic::{nonblocking::PocketIc, PocketIcBuilder}; use rosetta_core::objects::ObjectMap; use serde::Deserialize; @@ -412,7 +411,7 @@ fn matches_blockchain_is_empty_error(error: &rosetta_core::miscellaneous::Error) .as_ref() .unwrap() .get("error_message") - .map_or(false, |e| { + .is_some_and( |e| { e == "Blockchain is empty" || e == "Block not found: 0" || e == "RosettaBlocks was activated and there are no RosettaBlocks in the database yet. The synch is ongoing, please wait until the first RosettaBlock is written to the database." }) } @@ -506,7 +505,7 @@ async fn test_rosetta_blocks_mode_enabled() { ); } -// a simple trait to simplify unwrapping and decoding a WasmResult +// a simple trait to simplify unwrapping and decoding Vec trait UnwrapCandid { fn unwrap(&self) -> &[u8]; fn unwrap_as Deserialize<'a>>(&self) -> T { @@ -514,12 +513,9 @@ trait UnwrapCandid { } } -impl UnwrapCandid for WasmResult { +impl UnwrapCandid for Vec { fn unwrap(&self) -> &[u8] { - match self { - WasmResult::Reply(bytes) => bytes, - WasmResult::Reject(err) => panic!("Cannot unwrap Reject: {err}"), - } + self.as_slice() } } diff --git a/rs/rosetta-api/icp/tests/system_tests/common/governance_client.rs b/rs/rosetta-api/icp/tests/system_tests/common/governance_client.rs new file mode 100644 index 00000000000..40df7b50ea9 --- /dev/null +++ b/rs/rosetta-api/icp/tests/system_tests/common/governance_client.rs @@ -0,0 +1,111 @@ +use candid::{Decode, Encode, Principal}; +use ic_agent::Agent; +use ic_base_types::PrincipalId; +use ic_nns_common::{pb::v1::ProposalId, types::NeuronId}; +use ic_nns_governance_api::pb::v1::Motion; +use ic_nns_governance_api::pb::v1::ProposalActionRequest; +use ic_nns_governance_api::{ + pb::v1::{ + manage_neuron_response, manage_neuron_response::MakeProposalResponse, MakeProposalRequest, + ManageNeuronResponse, ProposalInfo, + }, + proposal_submission_helpers::create_make_proposal_payload, +}; + +pub struct GovernanceClient { + agent: Agent, + governance_principal: Principal, +} + +impl GovernanceClient { + pub fn new(agent: Agent, governance_principal: Principal) -> GovernanceClient { + GovernanceClient { + agent, + governance_principal, + } + } + + pub async fn submit_proposal( + &self, + principal: Principal, + neuron_id: NeuronId, + title: &str, + summary: &str, + motion_text: &str, + ) -> ProposalId { + let proposal = MakeProposalRequest { + title: Some(title.to_string()), + summary: summary.to_string(), + action: Some(ProposalActionRequest::Motion(Motion { + motion_text: motion_text.to_string(), + })), + ..Default::default() + }; + + let neuron_controller = PrincipalId::from(principal); + + let manage_neuron = create_make_proposal_payload(proposal.clone(), &neuron_id); + let arg = Encode!(&manage_neuron).expect("Error while encoding arg."); + let res = self + .agent + .update(&self.governance_principal, "manage_neuron") + .with_arg(arg) + .call_and_wait() + .await + .expect("Error while calling endpoint."); + //Make sure that the one making the proposal is also the controller of the neuron + assert_eq!( + PrincipalId::from(self.agent.get_principal().unwrap()), + neuron_controller + ); + let manage_neuron_res = + Decode!(res.as_slice(), ManageNeuronResponse).expect("Error while decoding response."); + if let ManageNeuronResponse { + command: + Some(manage_neuron_response::Command::MakeProposal(MakeProposalResponse { + proposal_id, + .. + })), + } = manage_neuron_res + { + assert!(proposal_id.is_some()); + let arg = Encode!(&proposal_id.unwrap().id).expect("Error while encoding arg."); + self.agent + .query(&self.governance_principal, "get_proposal_info") + .with_arg(arg) + .call() + .await + .expect("Error while calling endpoint."); + proposal_id.unwrap() + } else { + panic!( + "Making Proposal was unsuccessful --> Response : {:?}", + manage_neuron_res + ) + } + } + + pub async fn get_pending_proposals(&self) -> Vec { + let arg = Encode!(&"").expect("Error while encoding arg."); + let res = self + .agent + .query(&self.governance_principal, "get_pending_proposals") + .with_arg(arg) + .call() + .await + .expect("Error while calling endpoint."); + Decode!(res.as_slice(), Vec).expect("Error while decoding response.") + } + + pub async fn get_proposal_info(&self, proposal_id: ProposalId) -> Option { + let arg = Encode!(&proposal_id.id).expect("Error while encoding arg."); + let res = self + .agent + .query(&self.governance_principal, "get_proposal_info") + .with_arg(arg) + .call() + .await + .expect("Error while calling endpoint."); + Decode!(res.as_slice(), Option).expect("Error while decoding response.") + } +} diff --git a/rs/rosetta-api/icp/tests/system_tests/common/mod.rs b/rs/rosetta-api/icp/tests/system_tests/common/mod.rs index a408f89b3b8..2a8f3a0db71 100644 --- a/rs/rosetta-api/icp/tests/system_tests/common/mod.rs +++ b/rs/rosetta-api/icp/tests/system_tests/common/mod.rs @@ -1,3 +1,4 @@ pub mod constants; +pub mod governance_client; pub mod system_test_environment; pub mod utils; diff --git a/rs/rosetta-api/icp/tests/system_tests/common/system_test_environment.rs b/rs/rosetta-api/icp/tests/system_tests/common/system_test_environment.rs index 5fd21385eb3..549a477da87 100644 --- a/rs/rosetta-api/icp/tests/system_tests/common/system_test_environment.rs +++ b/rs/rosetta-api/icp/tests/system_tests/common/system_test_environment.rs @@ -1,3 +1,4 @@ +use super::utils::memo_bytebuf_to_u64; use crate::common::utils::get_custom_agent; use crate::common::utils::get_test_agent; use crate::common::utils::wait_for_rosetta_to_catch_up_with_icp_ledger; @@ -6,7 +7,7 @@ use crate::common::{ utils::test_identity, }; use candid::{Encode, Principal}; -use ic_agent::Identity; +use ic_agent::{Agent, Identity}; use ic_icp_rosetta_client::RosettaClient; use ic_icp_rosetta_client::RosettaTransferArgs; use ic_icp_rosetta_runner::RosettaOptions; @@ -41,8 +42,6 @@ use rosetta_core::identifiers::NetworkIdentifier; use std::collections::HashMap; use tempfile::TempDir; -use super::utils::memo_bytebuf_to_u64; - pub struct RosettaTestingEnvironment { pub pocket_ic: PocketIc, pub rosetta_context: RosettaContext, @@ -154,6 +153,10 @@ impl RosettaTestingEnvironment { .await; self } + + pub async fn get_test_agent(&self) -> Agent { + get_test_agent(self.pocket_ic.url().unwrap().port().unwrap()).await + } } pub struct RosettaTestingEnvironmentBuilder { diff --git a/rs/rosetta-api/icp/tests/system_tests/common/utils.rs b/rs/rosetta-api/icp/tests/system_tests/common/utils.rs index 1a7f685ed57..ed7e116a6f0 100644 --- a/rs/rosetta-api/icp/tests/system_tests/common/utils.rs +++ b/rs/rosetta-api/icp/tests/system_tests/common/utils.rs @@ -1,6 +1,5 @@ use crate::common::constants::MAX_ROSETTA_SYNC_ATTEMPTS; use candid::{Decode, Encode}; -use ic_agent::agent::http_transport::ReqwestTransport; use ic_agent::identity::BasicIdentity; use ic_agent::Agent; use ic_agent::Identity; @@ -8,9 +7,9 @@ use ic_icp_rosetta_client::RosettaClient; use ic_ledger_core::block::BlockType; use ic_nns_constants::GOVERNANCE_CANISTER_ID; use ic_nns_constants::LEDGER_CANISTER_ID; -use ic_nns_governance::pb::v1::ListNeurons; -use ic_nns_governance::pb::v1::ListNeuronsResponse; use ic_nns_governance_api::pb::v1::GovernanceError; +use ic_nns_governance_api::pb::v1::ListNeurons; +use ic_nns_governance_api::pb::v1::ListNeuronsResponse; use ic_rosetta_api::convert::to_hash; use icp_ledger::GetBlocksArgs; use icp_ledger::QueryEncodedBlocksResponse; @@ -37,10 +36,10 @@ pub async fn get_custom_agent(basic_identity: Arc, port: u16) -> A let replica_url = Url::parse(&format!("http://localhost:{}", port)).unwrap(); // Setup the agent - let transport = ReqwestTransport::create(replica_url.clone()).unwrap(); let agent = Agent::builder() + .with_url(replica_url.clone()) + .with_http_client(reqwest::Client::new()) .with_identity(basic_identity) - .with_arc_transport(Arc::new(transport)) .build() .unwrap(); @@ -181,6 +180,8 @@ pub async fn list_neurons(agent: &Agent) -> ListNeuronsResponse { include_neurons_readable_by_caller: true, include_empty_neurons_readable_by_caller: Some(true), include_public_neurons_in_full_neurons: None, + page_number: None, + page_size: None, }) .unwrap() ) diff --git a/rs/rosetta-api/icp/tests/system_tests/test_cases/mod.rs b/rs/rosetta-api/icp/tests/system_tests/test_cases/mod.rs index b0e6ea23db5..a762093ef1f 100644 --- a/rs/rosetta-api/icp/tests/system_tests/test_cases/mod.rs +++ b/rs/rosetta-api/icp/tests/system_tests/test_cases/mod.rs @@ -7,3 +7,4 @@ pub mod network; pub mod neuron_management; pub mod search_transactions; pub mod transfers; +pub mod voting; diff --git a/rs/rosetta-api/icp/tests/system_tests/test_cases/neuron_management.rs b/rs/rosetta-api/icp/tests/system_tests/test_cases/neuron_management.rs index 71d9d0a2d5a..7252733304b 100644 --- a/rs/rosetta-api/icp/tests/system_tests/test_cases/neuron_management.rs +++ b/rs/rosetta-api/icp/tests/system_tests/test_cases/neuron_management.rs @@ -15,8 +15,8 @@ use ic_icp_rosetta_client::{ RosettaStakeMaturityArgs, }; use ic_icrc1_test_utils::basic_identity_strategy; -use ic_nns_governance::pb::v1::neuron::DissolveState; -use ic_nns_governance::pb::v1::KnownNeuronData; +use ic_nns_governance_api::pb::v1::neuron::DissolveState; +use ic_nns_governance_api::pb::v1::KnownNeuronData; use ic_rosetta_api::ledger_client::list_known_neurons_response::ListKnownNeuronsResponse; use ic_rosetta_api::ledger_client::list_neurons_response::ListNeuronsResponse; use ic_rosetta_api::ledger_client::neuron_response::NeuronResponse; @@ -388,7 +388,7 @@ fn test_start_and_stop_neuron_dissolve() { let neuron = list_neurons(&agent).await.full_neurons[0].to_owned(); assert!( matches!( - neuron.dissolve_state.unwrap(), + neuron.dissolve_state.clone().unwrap(), DissolveState::WhenDissolvedTimestampSeconds(_) ), "Neuron should be in WhenDissolvedTimestampSeconds state, but is instead: {:?}", @@ -611,9 +611,9 @@ fn test_disburse_neuron() { // We now update the neuron so it is in state DISSOLVED let now = env.pocket_ic.get_time().await.duration_since(UNIX_EPOCH).unwrap().as_secs(); neuron.dissolve_state = Some(DissolveState::WhenDissolvedTimestampSeconds(now - 1)); - update_neuron(&agent, neuron.into()).await; + update_neuron(&agent, neuron).await; - match list_neurons(&agent).await.full_neurons[0].dissolve_state.unwrap() { + match list_neurons(&agent).await.full_neurons[0].dissolve_state.clone().unwrap() { DissolveState::WhenDissolvedTimestampSeconds (d) => { // The neuron should now be in DISSOLVED state assert!(d = Arc::new(test_identity()); +} + +/// Test neuron voting and proposal resolution. +#[test] +fn test_neuron_voting() { + let rt = Runtime::new().unwrap(); + rt.block_on(async { + let env = setup_environment().await; + + let governance_client = GovernanceClient::new( + env.get_test_agent().await, + Principal::from(GOVERNANCE_CANISTER_ID), + ); + + // Create neurons + let neuron_ids = create_neurons(&env, INITIAL_BALANCE / 10, DISSOLVE_DELAY_6_MONTHS).await; + + // Ensure no proposals exist initially + assert!(env + .rosetta_client + .get_pending_proposals(env.network_identifier.clone()) + .await + .unwrap() + .is_empty()); + + // Submit multiple proposals + for i in 0..3 { + governance_client + .submit_proposal( + TEST_IDENTITY.sender().unwrap(), + neuron_ids[0].into(), + &format!("dummy title {}", i), + &format!("test summary {}", i), + &format!("dummy text {}", i), + ) + .await; + } + + // Ensure all proposals are pending and have the expected details + let all_proposals = env + .rosetta_client + .get_pending_proposals(env.network_identifier.clone()) + .await + .unwrap(); + let all_proposals_from_governance = governance_client.get_pending_proposals().await; + + assert_eq!(all_proposals.len(), 3); + assert_eq!(all_proposals_from_governance.len(), 3); + + let expected_proposals = vec![ + Proposal { + title: Some("dummy title 0".to_string()), + summary: "test summary 0".to_string(), + action: Some(Action::Motion(Motion { + motion_text: "dummy text 0".to_string(), + })), + url: "".to_string(), + }, + Proposal { + title: Some("dummy title 1".to_string()), + summary: "test summary 1".to_string(), + action: Some(Action::Motion(Motion { + motion_text: "dummy text 1".to_string(), + })), + url: "".to_string(), + }, + Proposal { + title: Some("dummy title 2".to_string()), + summary: "test summary 2".to_string(), + action: Some(Action::Motion(Motion { + motion_text: "dummy text 2".to_string(), + })), + url: "".to_string(), + }, + ]; + + // Verify all proposals are pending and have the expected details both from + // Rosetta and the governance canister directly. + assert_eq!(all_proposals, expected_proposals); + for (i, proposal) in all_proposals_from_governance.iter().enumerate() { + assert_eq!(proposal.proposal.clone().unwrap(), expected_proposals[i]); + } + + let proposal_ids = all_proposals_from_governance + .iter() + .map(|proposal| proposal.id.unwrap().id) + .collect::>(); + + // Vote on the first proposal and verify the YES vote was correctly registered + register_vote(&env, proposal_ids[0], NEURON_INDEX[1], Vote::Yes) + .await + .unwrap(); + let voted_proposal_info = governance_client + .get_proposal_info(all_proposals_from_governance[0].id.unwrap()) + .await + .unwrap(); + assert_eq!( + extract_votes(&voted_proposal_info, &neuron_ids), + vec![VOTE_YES, VOTE_YES, VOTE_UNSPECIFIED, VOTE_UNSPECIFIED] + ); + + // Register remaining votes for the first proposal + register_vote(&env, proposal_ids[0], NEURON_INDEX[2], Vote::No) + .await + .unwrap(); + register_vote(&env, proposal_ids[0], NEURON_INDEX[3], Vote::No) + .await + .unwrap(); + let voted_proposal_info = governance_client + .get_proposal_info(all_proposals_from_governance[0].id.unwrap()) + .await + .unwrap(); + assert_eq!( + extract_votes(&voted_proposal_info, &neuron_ids), + vec![VOTE_YES, VOTE_YES, VOTE_NO, VOTE_NO] + ); + + // Verify the first proposal is no longer pending + let pending_proposals = env + .rosetta_client + .get_pending_proposals(env.network_identifier.clone()) + .await + .unwrap(); + let pending_proposals_from_governance = governance_client.get_pending_proposals().await; + assert_eq!( + pending_proposals_from_governance, + vec![ + all_proposals_from_governance[1].clone(), + all_proposals_from_governance[2].clone() + ] + ); + assert_eq!( + pending_proposals, + vec![all_proposals[1].clone(), all_proposals[2].clone()] + ); + + // Vote on the second proposal and verify the No vote was correctly registered + register_vote(&env, proposal_ids[1], NEURON_INDEX[2], Vote::No) + .await + .unwrap(); + let voted_proposal_info = governance_client + .get_proposal_info(all_proposals_from_governance[1].id.unwrap()) + .await + .unwrap(); + assert_eq!( + extract_votes(&voted_proposal_info, &neuron_ids), + vec![VOTE_YES, VOTE_UNSPECIFIED, VOTE_NO, VOTE_UNSPECIFIED] + ); + + // Register remaining votes for the second proposal + register_vote(&env, proposal_ids[1], NEURON_INDEX[1], Vote::Yes) + .await + .unwrap(); + register_vote(&env, proposal_ids[1], NEURON_INDEX[3], Vote::No) + .await + .unwrap(); + let voted_proposal_info = governance_client + .get_proposal_info(all_proposals_from_governance[1].id.unwrap()) + .await + .unwrap(); + assert_eq!( + extract_votes(&voted_proposal_info, &neuron_ids), + vec![VOTE_YES, VOTE_YES, VOTE_NO, VOTE_NO] + ); + + // Verify the second proposal is no longer pending + let pending_proposals = env + .rosetta_client + .get_pending_proposals(env.network_identifier.clone()) + .await + .unwrap(); + let pending_proposals_from_governance = governance_client.get_pending_proposals().await; + assert_eq!( + pending_proposals_from_governance, + vec![all_proposals_from_governance[2].clone()] + ); + assert_eq!(pending_proposals, vec![all_proposals[2].clone()]); + + // Register votes for the third proposal and verify the No vote was correctly registered + register_vote(&env, proposal_ids[2], NEURON_INDEX[3], Vote::No) + .await + .unwrap(); + let voted_proposal_info = governance_client + .get_proposal_info(all_proposals_from_governance[2].id.unwrap()) + .await + .unwrap(); + assert_eq!( + extract_votes(&voted_proposal_info, &neuron_ids), + vec![VOTE_YES, VOTE_UNSPECIFIED, VOTE_UNSPECIFIED, VOTE_NO] + ); + + // Try to vote again on the same proposal with the same neuron and verify the error + let invalid_vote_result = + register_vote(&env, proposal_ids[2], NEURON_INDEX[3], Vote::No).await; + assert!(invalid_vote_result + .unwrap_err() + .to_string() + .contains("NeuronAlreadyVoted")); + + // Register remaining votes for the third proposal and verify the proposal is no longer pending + register_vote(&env, proposal_ids[2], NEURON_INDEX[1], Vote::Yes) + .await + .unwrap(); + register_vote(&env, proposal_ids[2], NEURON_INDEX[2], Vote::No) + .await + .unwrap(); + let pending_proposals = env + .rosetta_client + .get_pending_proposals(env.network_identifier.clone()) + .await + .unwrap(); + let pending_proposals_from_governance = governance_client.get_pending_proposals().await; + assert_eq!(pending_proposals_from_governance.len(), 0); + assert_eq!(pending_proposals.len(), 0); + }); +} + +/// Set up the Rosetta testing environment with initial balances. +async fn setup_environment() -> RosettaTestingEnvironment { + RosettaTestingEnvironment::builder() + .with_initial_balances( + vec![( + AccountIdentifier::from(TEST_IDENTITY.sender().unwrap()), + icp_ledger::Tokens::from_e8s(INITIAL_BALANCE), + )] + .into_iter() + .collect(), + ) + .with_governance_canister() + .build() + .await +} + +/// Create neurons with the specified dissolve delay. +async fn create_neurons( + env: &RosettaTestingEnvironment, + staked_amount: u64, + dissolve_delay: u64, +) -> Vec { + join_all( + NEURON_INDEX + .iter() + .map(|&index| create_neuron_with_dissolve(env, staked_amount, index, dissolve_delay)), + ) + .await +} + +async fn set_dissolve_delay_from_now( + env: &RosettaTestingEnvironment, + neuron_index: u64, + dissolve_period_secs: u64, +) { + let dissolve_timestamp_secs = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs() + + dissolve_period_secs; + env.rosetta_client + .set_neuron_dissolve_delay( + env.network_identifier.clone(), + &(*TEST_IDENTITY).clone(), + RosettaSetNeuronDissolveDelayArgs::builder(dissolve_timestamp_secs) + .with_neuron_index(neuron_index) + .build(), + ) + .await + .unwrap(); +} + +async fn create_neuron_with_dissolve( + env: &RosettaTestingEnvironment, + staked_amount: u64, + neuron_index: u64, + dissolve_period_secs: u64, +) -> NeuronId { + let _ = tracing_subscriber::fmt::try_init(); + let neuron_response = env + .rosetta_client + .create_neuron( + env.network_identifier.clone(), + &(*TEST_IDENTITY).clone(), + RosettaCreateNeuronArgs::builder(staked_amount.into()) + .with_from_subaccount([0; 32]) + .with_neuron_index(neuron_index) + .build(), + ) + .await + .unwrap(); + set_dissolve_delay_from_now(env, neuron_index, dissolve_period_secs).await; + if let serde_json::Value::Number(n) = + &neuron_response.metadata.unwrap()["operations"][0]["metadata"]["neuron_id"] + { + return NeuronId { + id: n.as_u64().unwrap(), + }; + } + panic!("Neuron creation failed"); +} + +async fn register_vote( + env: &RosettaTestingEnvironment, + proposal_id: u64, + voter_neuron_index: u64, + vote: Vote, +) -> anyhow::Result { + env.rosetta_client + .register_vote( + env.network_identifier.clone(), + &(*TEST_IDENTITY).clone(), + RosettaRegisterVoteArgs::builder(proposal_id, vote as i32) + .with_neuron_index(voter_neuron_index) + .build(), + ) + .await +} + +fn extract_votes(proposal_info: &ProposalInfo, neuron_ids: &[NeuronId]) -> Vec { + neuron_ids + .iter() + .map(|neuron_id| { + proposal_info + .ballots + .get(&neuron_id.id) + .map(|ballot| ballot.vote) + .unwrap() + }) + .collect() +} diff --git a/rs/rosetta-api/icrc1/BUILD.bazel b/rs/rosetta-api/icrc1/BUILD.bazel index 19b289e1c3c..f7b1f0413bf 100644 --- a/rs/rosetta-api/icrc1/BUILD.bazel +++ b/rs/rosetta-api/icrc1/BUILD.bazel @@ -6,6 +6,7 @@ load("@rules_pkg//:pkg.bzl", "pkg_tar") load("@rules_pkg//pkg:mappings.bzl", "pkg_attributes", "pkg_mkdirs") load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library", "rust_test") load("//bazel:defs.bzl", "rust_test_suite_with_extra_srcs") +load("//rs/tests:system_tests.bzl", "oci_tar") package(default_visibility = ["//visibility:public"]) @@ -277,6 +278,12 @@ expand_template( ], ) +oci_tar( + name = "icrc_rosetta_image.tar", + image = ":ic_icrc_rosetta_image", + repo_tags = ["icrc-rosetta:image"], +) + # Push image with: # $ bazel run //:push_ic_icrc_rosetta_image # diff --git a/rs/rosetta-api/icrc1/src/construction_api/types.rs b/rs/rosetta-api/icrc1/src/construction_api/types.rs index d7d9dc9c12e..9bc343418db 100644 --- a/rs/rosetta-api/icrc1/src/construction_api/types.rs +++ b/rs/rosetta-api/icrc1/src/construction_api/types.rs @@ -40,19 +40,19 @@ pub struct SignedTransaction<'a> { pub envelopes: Vec>, } -impl<'a> std::fmt::Display for SignedTransaction<'a> { +impl std::fmt::Display for SignedTransaction<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", hex::encode(serde_cbor::ser::to_vec(self).unwrap())) } } -impl<'a> FromStr for SignedTransaction<'a> { +impl FromStr for SignedTransaction<'_> { type Err = anyhow::Error; fn from_str(s: &str) -> Result { serde_cbor::from_slice(hex::decode(s)?.as_slice()).map_err(|err| anyhow!("{:?}", err)) } } -impl<'a> SignedTransaction<'a> { +impl SignedTransaction<'_> { pub fn get_lowest_ingress_expiry(&self) -> Option { self.envelopes .iter() diff --git a/rs/rosetta-api/icrc1/src/main.rs b/rs/rosetta-api/icrc1/src/main.rs index 356bba5f546..1f109e31e32 100644 --- a/rs/rosetta-api/icrc1/src/main.rs +++ b/rs/rosetta-api/icrc1/src/main.rs @@ -7,9 +7,7 @@ use axum::{ Router, }; use clap::{Parser, ValueEnum}; -use ic_agent::{ - agent::http_transport::reqwest_transport::ReqwestTransport, identity::AnonymousIdentity, Agent, -}; +use ic_agent::{identity::AnonymousIdentity, Agent}; use ic_base_types::CanisterId; use ic_icrc_rosetta::{ common::constants::{BLOCK_SYNC_WAIT_SECS, MAX_BLOCK_SYNC_WAIT_SECS}, @@ -294,10 +292,11 @@ async fn main() -> Result<()> { let ic_agent = Agent::builder() .with_identity(AnonymousIdentity) - .with_transport(ReqwestTransport::create( + .with_url( Url::parse(&network_url) .context(format!("Failed to parse URL {}", network_url.clone()))?, - )?) + ) + .with_http_client(reqwest::Client::new()) .build()?; // Only fetch root key if the network is not the mainnet diff --git a/rs/rosetta-api/icrc1/tests/common/local_replica.rs b/rs/rosetta-api/icrc1/tests/common/local_replica.rs index 3c3bd8a3d29..aa800fc4df7 100644 --- a/rs/rosetta-api/icrc1/tests/common/local_replica.rs +++ b/rs/rosetta-api/icrc1/tests/common/local_replica.rs @@ -2,7 +2,7 @@ use candid::{Encode, Principal}; use ic_agent::identity::BasicIdentity; use ic_agent::Agent; -use ic_agent::{agent::http_transport::reqwest_transport::ReqwestTransport, Identity}; +use ic_agent::Identity; use ic_base_types::PrincipalId; use ic_icrc1_ledger::FeatureFlags; use ic_icrc1_ledger::{InitArgs, InitArgsBuilder, LedgerArgument}; @@ -39,10 +39,10 @@ pub async fn get_custom_agent(basic_identity: Arc, port: u16) -> A let replica_url = Url::parse(&format!("http://localhost:{}", port)).unwrap(); // Setup the agent - let transport = ReqwestTransport::create(replica_url.clone()).unwrap(); let agent = Agent::builder() + .with_url(replica_url.clone()) .with_identity(basic_identity) - .with_arc_transport(Arc::new(transport)) + .with_http_client(reqwest::Client::new()) .build() .unwrap(); diff --git a/rs/rosetta-api/local/cluster/.helmignore b/rs/rosetta-api/local/cluster/.helmignore new file mode 100644 index 00000000000..0e8a0eb36f4 --- /dev/null +++ b/rs/rosetta-api/local/cluster/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/rs/rosetta-api/local/cluster/Chart.yaml b/rs/rosetta-api/local/cluster/Chart.yaml new file mode 100644 index 00000000000..55e2a86dacf --- /dev/null +++ b/rs/rosetta-api/local/cluster/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: cluster +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.31.0" diff --git a/rs/rosetta-api/local/cluster/README.md b/rs/rosetta-api/local/cluster/README.md new file mode 100644 index 00000000000..7132e93121f --- /dev/null +++ b/rs/rosetta-api/local/cluster/README.md @@ -0,0 +1,119 @@ +# Local Rosetta API Cluster + +This directory contains scripts and configurations for setting up a local Kubernetes cluster using Minikube to deploy the Rosetta API services. The cluster includes monitoring tools such as Prometheus, cAdvisor, and Grafana. + +## Purpose + +The purpose of this cluster is to provide a local development and testing environment for the Rosetta API services. It allows developers to deploy and test their services in a controlled environment with monitoring capabilities. + +## What It Does + +- Sets up a Minikube cluster with a specified profile. +- Installs Prometheus, cAdvisor, and Grafana for monitoring. +- Deploys the Rosetta API services using Helm charts. +- Optionally loads local Docker images for the services. +- Forwards ports for Prometheus, Grafana, and the Rosetta API services. + +## Tools + +### Minikube + +Minikube is a tool that runs a single-node Kubernetes cluster on your local machine. It is useful for development and testing purposes. + +### Prometheus + +Prometheus is an open-source monitoring and alerting toolkit. It is used to collect and store metrics from various sources, including applications and infrastructure. + +### cAdvisor + +cAdvisor (Container Advisor) is an open-source container resource usage and performance analysis agent. It provides insights into the resource usage and performance characteristics of running containers. + +### Grafana + +Grafana is an open-source platform for monitoring and observability. It provides a web-based interface for visualizing and analyzing metrics collected by Prometheus and other data sources. + +## Usage + +### Prerequisites + +The `deploy.sh` script eventually uses Docker, Minikube, Kubectl and Helm, but fear not, it will assist you in installing those if it detects they are absent. +It was only tested on Ubuntu servers. + +WARNING: The script *doesn't* work when run from this repository's dev container (at `./ci/container/`). + +## Deploying Prod Images + +To create and set-up the local cluster and install the production containers for ICP-Rosetta and ICRC1-Rosetta pointing at DFINITY's test ledgers, simply do: +```bash +./deploy.sh [options] +``` + +You can make the rosetta nodes point to other ledgers by using these flags: +- `--icp-ledger `: Set the ICP Ledger ID (default: `xafvr-biaaa-aaaai-aql5q-cai`). If `prod`, will point to the official ICP ledger. +- `--icp-symbol `: Set the ICP token symbol (default: `TESTICP`). +- `--icrc1-ledger `: Set the ICRC1 Ledger ID (default: `3jkp5-oyaaa-aaaaj-azwqa-cai`). + +ATTENTION: The first run might take a few minutes to finish as it'll create the cluster and install the necessary charts in it. After that, all the script will do is re-deploy the rosetta images with different configuration if needed. + +## Deploying Local Images + +### Build the local containers +In order to build rosetta containers with local changes, you need to do it from inside the dev container: + +```bash +$ ./ci/container/container-run.sh + +# Build the TAR file target +$ bazel build //rs/rosetta-api/icp:rosetta_image.tar + +# Move the resulting TAR file to a place that can be accessed outside the dev container +$ mv bazel-bin/rs/rosetta-api/icp/rosetta_image.tar /tmp + +# Same for ICRC1 +$ bazel build //rs/rosetta-api/icrc1:icrc_rosetta_image.tar +$ mv bazel-bin/rs/rosetta-api/icrc1/icrc_rosetta_image.tar /tmp + +# Exit the dev container +$ exit +``` + +### Deploy the local containers + +You can use the following flags to deploy additional containers with the TAR files generated above: + +- `--local-icp-image-tar `: Path to local ICP image tar file. +- `--local-icrc1-image-tar `: Path to local ICRC1 image tar file. + +Example that deploys local versions for both: + +```bash +./deploy.sh --local-icp-image-tar /tmp/rosetta_image.tar --local-icrc1-image-tar /tmp/icrc_rosetta_image.tar +``` + +The services and pods deployed with those images will have a `-local` in their names. + + +### Cleaning up + +You can add the `--clean` flag to any usage of `./deploy.sh`. That will wipe out the current cluster and install it from scratch. + +For example, the following command installs all prod and local images in a clean cluster: + +```bash +./deploy.sh --local-icp-image-tar /tmp/rosetta_image.tar --local-icrc1-image-tar /tmp/icrc_rosetta_image.tar --clean +``` + +## Monitoring with Grafana + +Grafana will run on port 3000. If you're running this in a remote devenv, you'll need to forward your local machine port to your devenv's one in order to access the service from your browser. + +The first time you open `http://localhost:3000`, you'll be asked for login credentials. Use `admin` for both username and password. You'll be asked to change the password, you can either do so or just skip, it doesn't matter. + +Once in Grafana, import a new dashboard. As an option to import, you'll see a text box to input a json file. Copy and paste the contents of the `rosetta_load_dashboard.json` file in this directory. + +Services and pods with suffix `-latest` represent jobs running with the prod images while the ones with suffix `-local` are the ones running with the locally built ones. + + +## Notes +- The script will automatically install Minikube if they are not found. +- The script uses a dedicated Minikube profile (`local-rosetta`) to avoid conflicts with other Minikube clusters. diff --git a/rs/rosetta-api/local/cluster/deploy.sh b/rs/rosetta-api/local/cluster/deploy.sh new file mode 100755 index 00000000000..89c99e43385 --- /dev/null +++ b/rs/rosetta-api/local/cluster/deploy.sh @@ -0,0 +1,282 @@ +#!/bin/bash + +set -e + +# Usage: deploy.sh [options] +# Options: +# --icp-ledger Set the ICP Ledger ID (default: xafvr-biaaa-aaaai-aql5q-cai) +# --icp-symbol Set the ICP token symbol (default: TESTICP) +# --icrc1-ledger Set the ICRC1 Ledger ID (default: 3jkp5-oyaaa-aaaaj-azwqa-cai) +# --local-icp-image-tar Path to local ICP image tar file +# --local-icrc1-image-tar Path to local ICRC1 image tar file +# --clean Clean up Minikube cluster and Helm chart before deploying +# --stop Stop the Minikube cluster +# --help Display this help message + +# Default values +ICP_LEDGER="xafvr-biaaa-aaaai-aql5q-cai" +ICP_SYMBOL="TESTICP" +ICRC1_LEDGER="3jkp5-oyaaa-aaaaj-azwqa-cai" +LOCAL_ICP_IMAGE_TAR="" +LOCAL_ICRC1_IMAGE_TAR="" +CLEAN=false +STOP=false +MINIKUBE_PROFILE="local-rosetta" + +# Parse arguments +while [[ "$#" -gt 0 ]]; do + case $1 in + --icp-ledger) + ICP_LEDGER="$2" + shift + ;; + --icp-symbol) + ICP_SYMBOL="$2" + shift + ;; + --icrc1-ledger) + ICRC1_LEDGER="$2" + shift + ;; + --local-icp-image-tar) + LOCAL_ICP_IMAGE_TAR="$2" + shift + ;; + --local-icrc1-image-tar) + LOCAL_ICRC1_IMAGE_TAR="$2" + shift + ;; + --clean) CLEAN=true ;; + --stop) STOP=true ;; + --help) + sed -n '5,14p' "$0" + exit 0 + ;; + *) + echo "Unknown parameter passed: $1" + exit 1 + ;; + esac + shift +done + +# Function that waits for a resource to be ready +wait_for_ready() { + local resource_type="$1" # The type of the resource (e.g., pod, deployment) + local resource_label="$2" # The label of the resource + local namespace="$3" # The namespace of the resource (optional, defaults to 'default') + local timeout=$4 # Timeout in seconds + + # Default namespace if not provided + namespace="${namespace:-default}" + + # wait 2 seconds + sleep 2 + + kubectl wait --namespace $namespace --for=condition=Ready $resource_type -l $resource_label --timeout=${timeout}s --context="$MINIKUBE_PROFILE" +} + +# Stop Minikube cluster if --stop flag is set +[[ "$STOP" == true ]] && { + echo "Stopping Minikube cluster..." + minikube stop -p "$MINIKUBE_PROFILE" + exit 0 +} + +# Set default values for prod +[[ "$ICP_LEDGER" == "prod" ]] && ICP_LEDGER="ryjl3-tyaaa-aaaaa-aaaba-cai" && ICP_SYMBOL="ICP" + +# Ensure Docker is installed +command -v docker &>/dev/null || { + read -p "Docker not found, do you want to install Docker? (y/n): " install_docker + if [[ "$install_docker" == "y" ]]; then + echo "Installing Docker..." + curl -fsSL https://get.docker.com -o get-docker.sh + sh get-docker.sh + rm get-docker.sh + else + echo "Docker is required. Exiting..." + exit 1 + fi +} + +# Ensure Docker is running +docker info &>/dev/null || { + echo "Docker is not running. Please start Docker and try again." + echo "Usually that can be done with "sudo systemctl start docker" or "sudo service docker start"" + exit 1 +} + +# Ensure kubectl is installed +command -v kubectl &>/dev/null || { + read -p "kubectl not found, do you want to install kubectl? (y/n): " install_kubectl + if [[ "$install_kubectl" == "y" ]]; then + echo "Installing kubectl..." + curl -LO "https://dl.k8s.io/release/v1.31.5/bin/linux/amd64/kubectl" + chmod +x kubectl + sudo mv kubectl /usr/local/bin/ + else + echo "kubectl is required. Exiting..." + exit 1 + fi +} + +# Ensure Minikube is installed +command -v minikube &>/dev/null || { + read -p "Minikube not found, do you want to install Minikube? (y/n): " install_minikube + if [[ "$install_minikube" == "y" ]]; then + echo "Installing Minikube..." + curl -Lo minikube https://storage.googleapis.com/minikube/releases/v1.34.0/minikube-linux-amd64 + chmod +x minikube + sudo mv minikube /usr/local/bin/ + else + echo "Minikube is required. Exiting..." + exit 1 + fi +} + +# Ensure Helm is installed +command -v helm &>/dev/null || { + read -p "Helm not found, do you want to install Helm? (y/n): " install_helm + if [[ "$install_helm" == "y" ]]; then + echo "Installing Helm..." + curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash + else + echo "Helm is required. Exiting..." + exit 1 + fi +} + +# Clean up Minikube cluster and Helm chart if --clean flag is set +[[ "$CLEAN" == true ]] && { + echo "Cleaning up Minikube cluster and Helm chart..." + helm uninstall local-rosetta || true + minikube delete -p "$MINIKUBE_PROFILE" +} + +# Start Minikube with the specified profile if not already running +minikube status -p "$MINIKUBE_PROFILE" &>/dev/null || { + echo "Starting Minikube with profile $MINIKUBE_PROFILE..." + minikube start -p "$MINIKUBE_PROFILE" +} + +# Add Helm repositories and update +helm repo add prometheus-community https://prometheus-community.github.io/helm-charts +helm repo add ckotzbauer https://ckotzbauer.github.io/helm-charts +helm repo update + +# Function to check if a port forward exists and create it if not +port_forward() { + local namespace=$1 + local svc=$2 + local port=$3 + pgrep -f "kubectl port-forward -n $namespace svc/$svc $port --context=$MINIKUBE_PROFILE" &>/dev/null || { + echo "Forwarding $svc port..." + kubectl port-forward -n $namespace svc/$svc $port --context="$MINIKUBE_PROFILE" &>/dev/null & + } +} + +# Install or upgrade Prometheus +helm list -n monitoring --kube-context="$MINIKUBE_PROFILE" | grep -q prometheus || { + echo "Installing Prometheus..." + helm install prometheus prometheus-community/prometheus --namespace monitoring --create-namespace --kube-context="$MINIKUBE_PROFILE" --values prometheus_values.yaml +} + +# Wait for Prometheus server to be ready +echo "Waiting for Prometheus server to be ready..." +wait_for_ready pod app.kubernetes.io/instance=prometheus monitoring 300 + +# Forward Prometheus port if not already forwarded +port_forward monitoring prometheus-server 9090:80 + +# Install or upgrade cAdvisor +helm list -n monitoring --kube-context="$MINIKUBE_PROFILE" | grep -q cadvisor || { + echo "Installing cAdvisor..." + helm install cadvisor ckotzbauer/cadvisor --namespace monitoring --create-namespace --kube-context="$MINIKUBE_PROFILE" +} + +# Wait for cAdvisor server to be ready +echo "Waiting for cAdvisor server to be ready..." +wait_for_ready pod app=cadvisor monitoring 300 + +# Install or upgrade kube-prometheus +helm list -n monitoring --kube-context="$MINIKUBE_PROFILE" | grep -q kube-prometheus || { + echo "Installing kube-prometheus..." + helm install kube-prometheus prometheus-community/kube-prometheus-stack --namespace monitoring --kube-context="$MINIKUBE_PROFILE" +} + +# Function to load a local TAR if provided +load_local_tar() { + local tar_path=$1 + local from_image_tag=$2 + local to_image_tag=$3 + [[ -n "$tar_path" ]] && { + echo "Loading local image $to_image_tag into Minikube..." + eval $(minikube -p "$MINIKUBE_PROFILE" docker-env) + docker load -i "$tar_path" + docker tag "$from_image_tag" "$to_image_tag" + eval $(minikube -p "$MINIKUBE_PROFILE" docker-env -u) + } + return 0 +} + +# Load local ICP and ICRC1 images if provided +load_local_tar "$LOCAL_ICP_IMAGE_TAR" rosetta:image icp-rosetta:local +load_local_tar "$LOCAL_ICRC1_IMAGE_TAR" icrc-rosetta:image icrc-rosetta:local + +echo "Deploying Helm chart..." +# Deploy or upgrade the Helm chart +helm upgrade --install local-rosetta . \ + --set icpConfig.canisterId="$ICP_LEDGER" \ + --set icpConfig.tokenSymbol="$ICP_SYMBOL" \ + --set icrcConfig.ledgerId="$ICRC1_LEDGER" \ + --set icpConfig.useLocallyBuilt=$([[ -n "$LOCAL_ICP_IMAGE_TAR" ]] && echo "true" || echo "false") \ + --set icrcConfig.useLocallyBuilt=$([[ -n "$LOCAL_ICRC1_IMAGE_TAR" ]] && echo "true" || echo "false") \ + --kube-context="$MINIKUBE_PROFILE" + +# Wait for Grafana server to be ready +echo "Waiting for Grafana server to be ready..." +wait_for_ready pod app=grafana monitoring 300 + +# Forward Grafana port if not already forwarded +port_forward monitoring grafana 3000:80 + +# Function to check if a service exists and print its URL +print_service_url() { + local namespace=$1 + local service=$2 + if kubectl get -n "$namespace" svc "$service" --context="$MINIKUBE_PROFILE" &>/dev/null; then + local nodePort=$(kubectl get -n "$namespace" svc "$service" -o jsonpath='{.spec.ports[0].nodePort}' --context="$MINIKUBE_PROFILE") + echo "$service: http://localhost:$nodePort" + else + echo "$service is not present." + fi +} + +# Wait for the rosetta services to be ready and forward the ports +for service in icp-rosetta-local icp-rosetta-latest icrc-rosetta-local icrc-rosetta-latest; do + if kubectl get -n rosetta-api svc "$service" --context="$MINIKUBE_PROFILE" &>/dev/null; then + echo "Waiting for $service server to be ready..." + wait_for_ready pod app="$service" rosetta-api 300 + + # Find the nodeport for the service + nodePort=$(kubectl get -n rosetta-api svc "$service" -o jsonpath='{.spec.ports[0].nodePort}' --context="$MINIKUBE_PROFILE") + + echo "Forwarding $service port to http://localhost:$nodePort..." + + # Forward the port if it is not already forwarded + port_forward rosetta-api "$service" "$nodePort:3000" + fi +done + +# Print the URLs +echo "" +echo "************************************" +echo "Deployment complete. Access the services at the following URLs:" +print_service_url rosetta-api icp-rosetta-local +print_service_url rosetta-api icp-rosetta-latest +print_service_url rosetta-api icrc-rosetta-local +print_service_url rosetta-api icrc-rosetta-latest +echo "Prometheus: http://localhost:9090" +echo "Grafana: http://localhost:3000" +echo "************************************" diff --git a/rs/rosetta-api/local/cluster/prometheus_values.yaml b/rs/rosetta-api/local/cluster/prometheus_values.yaml new file mode 100644 index 00000000000..7a02244dfb6 --- /dev/null +++ b/rs/rosetta-api/local/cluster/prometheus_values.yaml @@ -0,0 +1,13 @@ +server: + global: + scrape_interval: 5s + evaluation_interval: 5s + scrape_timeout: 5s + scrape_configs: + - job_name: 'kubernetes-pods' + kubernetes_sd_configs: + - role: pod + relabel_configs: + - source_labels: [__meta_kubernetes_pod_label_app] + action: keep + regex: .* \ No newline at end of file diff --git a/rs/rosetta-api/local/cluster/rosetta_load_dashboard.json b/rs/rosetta-api/local/cluster/rosetta_load_dashboard.json new file mode 100644 index 00000000000..d0e3a7cc52f --- /dev/null +++ b/rs/rosetta-api/local/cluster/rosetta_load_dashboard.json @@ -0,0 +1,1235 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 1, + "id": 1, + "links": [], + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 12, + "panels": [], + "title": "Rosetta API", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 1 + }, + "id": 11, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "max by(app) (rosetta_synched_block_height{namespace=\"rosetta-api\"})", + "format": "time_series", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "Synched ({{app}})", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "max by(app) (rosetta_target_block_height{namespace=\"rosetta-api\"})", + "format": "time_series", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "Target ({{app}})", + "range": true, + "refId": "B", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "max by(app) (rosetta_verified_block_height{namespace=\"rosetta-api\"})", + "format": "time_series", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "Verified ({{app}})", + "range": true, + "refId": "C", + "useBackend": false + } + ], + "title": "Synched Blocks", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 6, + "y": 1 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "ledger_sync_attempt_duration_seconds", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{app}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Sync Duration", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 12, + "y": 1 + }, + "id": 13, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "disableTextWrap": false, + "editorMode": "code", + "exemplar": false, + "expr": "blockchain_sync_errors_total", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": true, + "interval": "", + "legendFormat": "{{app}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Total Sync Errors", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "bars", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 18, + "y": 1 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "disableTextWrap": false, + "editorMode": "code", + "exemplar": false, + "expr": "count_over_time(ledger_sync_blocks_fetched_total[2m])", + "format": "time_series", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "interval": "1", + "legendFormat": "{{app}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Total blocks fetched from ledger", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 9 + }, + "id": 8, + "panels": [], + "title": "System Resources", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 0, + "y": 10 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "disableTextWrap": false, + "editorMode": "code", + "exemplar": false, + "expr": "rate(container_fs_reads_total{namespace=\"rosetta-api\"}[5m])", + "format": "time_series", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "interval": "1", + "legendFormat": "{{pod}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Reads/s", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "binBps" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 6, + "y": 10 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(container_fs_reads_bytes_total{namespace=\"rosetta-api\"}[5m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "interval": "1", + "legendFormat": "{{pod}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Reads Bytes/s", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 12, + "y": 10 + }, + "id": 4, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "disableTextWrap": false, + "editorMode": "code", + "exemplar": false, + "expr": "rate(container_fs_writes_total{namespace=\"rosetta-api\"}[5m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "interval": "1", + "legendFormat": "{{pod}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Writes/s", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "binBps" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 18, + "y": 10 + }, + "id": 3, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(container_fs_writes_bytes_total{namespace=\"rosetta-api\"}[5m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "interval": "1", + "legendFormat": "{{pod}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Writes Bytes/s", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 8, + "x": 0, + "y": 19 + }, + "id": 5, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "container_memory_rss{namespace=~\"rosetta-api\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "interval": "1", + "legendFormat": "{{pod}}-rss", + "range": true, + "refId": "B", + "useBackend": false + } + ], + "title": "Memory Usage", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 8, + "x": 8, + "y": 19 + }, + "id": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(container_cpu_usage_seconds_total{namespace=\"rosetta-api\"}[5m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "interval": "1", + "legendFormat": "{{pod}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "CPU usage", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 8, + "x": 16, + "y": 19 + }, + "id": 7, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "Prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "container_oom_events_total{namespace=~\"rosetta-api\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "interval": "1", + "legendFormat": "{{pod}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "OOMs", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Rosetta", + "uid": "rosetta", + "version": 2, + "weekStart": "" +} \ No newline at end of file diff --git a/rs/rosetta-api/local/cluster/templates/_helpers.tpl b/rs/rosetta-api/local/cluster/templates/_helpers.tpl new file mode 100644 index 00000000000..b16527f89f9 --- /dev/null +++ b/rs/rosetta-api/local/cluster/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "cluster.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "cluster.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "cluster.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "cluster.labels" -}} +helm.sh/chart: {{ include "cluster.chart" . }} +{{ include "cluster.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "cluster.selectorLabels" -}} +app.kubernetes.io/name: {{ include "cluster.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "cluster.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "cluster.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/rs/rosetta-api/local/cluster/templates/grafana.yaml b/rs/rosetta-api/local/cluster/templates/grafana.yaml new file mode 100644 index 00000000000..661b008a247 --- /dev/null +++ b/rs/rosetta-api/local/cluster/templates/grafana.yaml @@ -0,0 +1,69 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: grafana + namespace: monitoring +spec: + replicas: 1 + selector: + matchLabels: + app: grafana + template: + metadata: + labels: + app: grafana + spec: + containers: + - name: grafana + image: grafana/grafana:latest + env: + - name: GF_SECURITY_ADMIN_USER + value: "admin" + - name: GF_SECURITY_ADMIN_PASSWORD + value: "admin" + ports: + - containerPort: 3000 + volumeMounts: + - name: grafana-storage + mountPath: /var/lib/grafana + - name: grafana-datasources + mountPath: /etc/grafana/provisioning/datasources + readOnly: true + volumes: + - name: grafana-storage + emptyDir: {} + - name: grafana-datasources + configMap: + name: grafana-datasources +--- +apiVersion: v1 +kind: Service +metadata: + name: grafana + namespace: monitoring +spec: + type: ClusterIP + ports: + - port: 80 + targetPort: 3000 + selector: + app: grafana +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: grafana-datasources + namespace: monitoring + labels: + app: grafana + component: server +data: + datasource.yaml: | + apiVersion: 1 + datasources: + - name: Prometheus + type: prometheus + access: proxy + url: http://prometheus-server.monitoring.svc.cluster.local + isDefault: true + editable: true diff --git a/rs/rosetta-api/local/cluster/templates/namespace.yaml b/rs/rosetta-api/local/cluster/templates/namespace.yaml new file mode 100644 index 00000000000..19bb88f80a2 --- /dev/null +++ b/rs/rosetta-api/local/cluster/templates/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: rosetta-api \ No newline at end of file diff --git a/rs/rosetta-api/local/cluster/templates/rosetta-icp.yaml b/rs/rosetta-api/local/cluster/templates/rosetta-icp.yaml new file mode 100644 index 00000000000..bab2236f66f --- /dev/null +++ b/rs/rosetta-api/local/cluster/templates/rosetta-icp.yaml @@ -0,0 +1,76 @@ +{{- range $.Values.icpRosettaServices }} +# Only add the service if not locally built or locally built is enabled for it +{{- if or (not .isLocallyBuilt) $.Values.icpConfig.useLocallyBuilt }} +apiVersion: v1 +kind: Service +metadata: + name: {{ .name }} + namespace: {{ .namespace }} + labels: + app: {{ .name }} + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: "{{ .containerPort }}" +spec: + selector: + app: {{ .name }} + ports: + - name: http + protocol: TCP + port: {{ .containerPort }} + targetPort: {{ .containerPort }} + nodePort: {{ .nodePort }} + type: NodePort +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .name }} + namespace: {{ .namespace }} +spec: + replicas: {{ $.Values.replicaCount }} + selector: + matchLabels: + app: {{ .name }} + template: + metadata: + labels: + app: {{ .name }} + spec: + containers: + - name: {{ .name }} + image: "{{ .image }}" + imagePullPolicy: IfNotPresent + args: + - "--mainnet" + - "--expose-metrics" + - "--store-location" + - {{ $.Values.icpConfig.storeLocation | quote }} + - "--token-sybol" + - {{ $.Values.icpConfig.tokenSymbol | quote }} + - "--canister-id" + - {{ $.Values.icpConfig.canisterId | quote }} + - "--port" + - {{ .containerPort | quote}} + ports: + - containerPort: {{ .containerPort }} + volumeMounts: + {{- range $.Values.volumeMounts }} + - name: {{ .name }} + mountPath: {{ .mountPath }} + {{- end }} + resources: + limits: + memory: {{ .resources.limits.memory }} + cpu: {{ .resources.limits.cpu }} + requests: + memory: {{ .resources.requests.memory }} + cpu: {{ .resources.requests.cpu }} + volumes: + {{- range $.Values.volumes }} + - name: {{ .name }} + emptyDir: {} + {{- end }} +--- +{{- end }} +{{- end }} diff --git a/rs/rosetta-api/local/cluster/templates/rosetta-icrc.yaml b/rs/rosetta-api/local/cluster/templates/rosetta-icrc.yaml new file mode 100644 index 00000000000..f5655b92047 --- /dev/null +++ b/rs/rosetta-api/local/cluster/templates/rosetta-icrc.yaml @@ -0,0 +1,74 @@ +{{- range .Values.icrcRosettaServices }} +# Only add the service if not locally built or locally built is enabled for it +{{- if or (not .isLocallyBuilt) $.Values.icrcConfig.useLocallyBuilt }} +apiVersion: v1 +kind: Service +metadata: + name: {{ .name }} + namespace: {{ .namespace }} + labels: + app: {{ .name }} + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: "{{ .containerPort }}" +spec: + selector: + app: {{ .name }} + ports: + - name: http + protocol: TCP + port: {{ .containerPort }} + targetPort: {{ .containerPort }} + nodePort: {{ .nodePort }} + type: NodePort +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .name }} + namespace: {{ .namespace }} +spec: + replicas: {{ $.Values.replicaCount }} + selector: + matchLabels: + app: {{ .name }} + template: + metadata: + labels: + app: {{ .name }} + spec: + containers: + - name: {{ .name }} + image: "{{ .image }}" + imagePullPolicy: IfNotPresent + args: + - "--port" + - {{ .containerPort | quote}} + - "--network-type" + - {{ $.Values.icrcConfig.networkType | quote }} + - "--ledger-id" + - {{ $.Values.icrcConfig.ledgerId | quote }} + - "--store-file" + - {{ $.Values.icrcConfig.storeFile | quote }} + ports: + - containerPort: {{ .containerPort }} + volumeMounts: + {{- range $.Values.volumeMounts }} + - name: {{ .name }} + mountPath: {{ .mountPath }} + {{- end }} + resources: + limits: + memory: {{ .resources.limits.memory }} + cpu: {{ .resources.limits.cpu }} + requests: + memory: {{ .resources.requests.memory }} + cpu: {{ .resources.requests.cpu }} + volumes: + {{- range $.Values.volumes }} + - name: {{ .name }} + emptyDir: {} + {{- end }} +--- +{{- end }} +{{- end }} diff --git a/rs/rosetta-api/local/cluster/values.yaml b/rs/rosetta-api/local/cluster/values.yaml new file mode 100644 index 00000000000..0c670486e3a --- /dev/null +++ b/rs/rosetta-api/local/cluster/values.yaml @@ -0,0 +1,72 @@ +commonResources: &commonResources + limits: + memory: "256Mi" + cpu: "5" + requests: + memory: "128Mi" + cpu: "500m" + +icrcConfig: + networkType: mainnet + ledgerId: vtrom-gqaaa-aaaaq-aabia-cai + storeFile: /data/db.sqlite + useLocallyBuilt: false + +icpConfig: + storeLocation: /data + tokenSymbol: ICP + canisterId: xafvr-biaaa-aaaai-aql5q-cai + useLocallyBuilt: false + +baseService: &baseService + namespace: "rosetta-api" + containerPort: 3000 + resources: *commonResources + +icrcRosettaServices: + - <<: *baseService + name: icrc-rosetta-latest + image: dfinity/ic-icrc-rosetta-api:latest + nodePort: 30085 + isLocallyBuilt: false + + - <<: *baseService + name: icrc-rosetta-local + image: icrc-rosetta:local + nodePort: 30086 + isLocallyBuilt: true + +icpRosettaServices: + - <<: *baseService + name: icp-rosetta-latest + image: dfinity/rosetta-api:latest + nodePort: 30087 + isLocallyBuilt: false + + - <<: *baseService + name: icp-rosetta-local + image: icp-rosetta:local + nodePort: 30088 + isLocallyBuilt: true + +cadvisor: + image: + repository: gcr.io/cadvisor/cadvisor + tag: latest + pullPolicy: IfNotPresent + + resources: + limits: + memory: "512Mi" + cpu: "1" + requests: + memory: "256Mi" + cpu: "500m" + + service: + type: ClusterIP + port: 8080 + + nodeSelector: {} + tolerations: [] + affinity: {} \ No newline at end of file diff --git a/rs/rosetta-api/scripts/download_latest_icrc1_ledger.sh b/rs/rosetta-api/scripts/download_latest_icrc1_ledger.sh index 4aa452f7be4..78a04ae29f0 100755 --- a/rs/rosetta-api/scripts/download_latest_icrc1_ledger.sh +++ b/rs/rosetta-api/scripts/download_latest_icrc1_ledger.sh @@ -1,30 +1,25 @@ #!/usr/bin/env bash - set -uo pipefail -#set -x - -COMMITS=$(curl -sLf -H "Accept: application/vnd.github+json" \ - -H "X-GitHub-Api-Version: 2022-11-28" \ - "https://api.github.com/repos/dfinity/ic/commits?per_page=100" \ - | jq '.[].sha' | tr -d \") -if [ "$?" -ne "0" ]; then - echo >&2 "Unable to fetch the commits from dfinity/ic. Please try again" - exit 1 -fi +### Configuration +RELEASE_TAG_PREFIX=ledger-suite-icrc -for COMMIT in $COMMITS; do +### Download a specific release +## Download the ICRC ledger WASM and did files for a specific release. The files are downloaded +## from the github release page for the given release. +download_release() { + RELEASE=$1 STATUS_CODE=$(curl -s -o /dev/null -w "%{http_code}" -L --head \ - "https://download.dfinity.systems/ic/$COMMIT/canisters/ic-icrc1-ledger.wasm.gz") + "https://github.com/dfinity/ic/releases/download/${RELEASE}/ic-icrc1-ledger.wasm.gz") if (($STATUS_CODE >= 200)) && (($STATUS_CODE < 300)); then - echo "Found artifacts for commit $COMMIT. Downloading icrc1_ledger.did and icrc1_ledger.wasm.gz" - curl -sLf "https://raw.githubusercontent.com/dfinity/ic/$COMMIT/rs/ledger_suite/icrc1/ledger/ledger.did" \ + echo "Found artifacts for release $RELEASE. Downloading icrc1_ledger.did and icrc1_ledger.wasm.gz" + curl -sLf "https://github.com/dfinity/ic/releases/download/${RELEASE}/ledger.did" \ -o icrc1_ledger.did if [ "$?" -ne "0" ]; then echo >&2 "Unable to download the icrc1 ledger did file. Please try again" exit 2 fi - curl -sLf "https://download.dfinity.systems/ic/$COMMIT/canisters/ic-icrc1-ledger.wasm.gz" \ + curl -sLf "https://github.com/dfinity/ic/releases/download/${RELEASE}/ic-icrc1-ledger.wasm.gz" \ -o icrc1_ledger.wasm.gz if [ "$?" -ne "0" ]; then echo >&2 "Unable to download the icrc1 ledger wasm file. Please try again" @@ -32,7 +27,48 @@ for COMMIT in $COMMITS; do fi exit 0 fi -done +} + +### Find and download the latest ICRC ledger WASM and did file +## List the releases from the repository, looking for the most recent release where the corresponding +## tag starts with the expected prefix. Retrieves releases one page at a time, stopping if no release +## was found in some predefined maximum number of pages. Once a release is found, download the ledger +## WASM and did files. +find_and_download_release() { + PAGE=1 + ITEMS_PER_PAGE=100 + MAX_PAGES=10 + while true; do + ITEM=0 + # Unauthenticated requests are rate limited (per IP address) to 60 requests/hr + # https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28#primary-rate-limit-for-unauthenticated-users + REL_JSON=$(curl -L \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + https://api.github.com/repos/dfinity/ic/releases\?per_page\=${ITEMS_PER_PAGE}\&page\=${PAGE}) + if [ "$?" -ne "0" ]; then + echo >&2 "Unable to fetch the releases from dfinity/ic." + exit 1 + fi + while [ ${ITEM} -lt ${ITEMS_PER_PAGE} ]; do + RELEASE=$(echo ${REL_JSON} | jq ".[${ITEM}].tag_name" | tr -d '"') + if [ "$?" -ne "0" ]; then + echo >&2 "Error parsing release from response." + exit 1 + fi + if [[ ${RELEASE} == ${RELEASE_TAG_PREFIX}* ]]; then + download_release "${RELEASE}" + break + else + ITEM=$((ITEM + 1)) + fi + done + PAGE=$((PAGE + 1)) + if [ ${PAGE} -gt ${MAX_PAGES} ]; then + echo "No ${RELEASE_TAG_PREFIX} release found in the first ${MAX_PAGES} with ${ITEMS_PER_PAGE} items per page, aborting." + exit 1 + fi + done +} -echo "No commits with artifacts found" -exit 4 +find_and_download_release diff --git a/rs/rust_canisters/canister_test/src/canister.rs b/rs/rust_canisters/canister_test/src/canister.rs index 035fa531ee2..63fd6a6a469 100644 --- a/rs/rust_canisters/canister_test/src/canister.rs +++ b/rs/rust_canisters/canister_test/src/canister.rs @@ -5,7 +5,7 @@ use ic_canister_client::{Agent, Sender}; use ic_config::Config; use ic_management_canister_types::CanisterStatusType::Stopped; pub use ic_management_canister_types::{ - self as ic00, CanisterIdRecord, CanisterInstallMode, CanisterStatusResult, InstallCodeArgs, + self as ic00, CanisterIdRecord, CanisterInstallMode, InstallCodeArgs, ProvisionalCreateCanisterWithCyclesArgs, IC_00, }; use ic_registry_transport::pb::v1::RegistryMutation; @@ -532,7 +532,7 @@ pub struct Canister<'a> { wasm: Option, } -impl<'a> Canister<'a> { +impl Canister<'_> { pub fn is_runtime_local(&self) -> bool { match self.runtime { Runtime::Remote(_) => false, @@ -542,7 +542,7 @@ impl<'a> Canister<'a> { } } -impl<'a> fmt::Debug for Canister<'a> { +impl fmt::Debug for Canister<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "client-side view of canister {}", self.canister_id) } @@ -808,7 +808,7 @@ impl<'a> Canister<'a> { .await; stop_res?; loop { - let status_res: Result = self + let status_res: Result = self .runtime .get_management_canister_with_effective_canister_id(self.canister_id().into()) .update_("canister_status", candid, (self.as_record(),)) @@ -891,7 +891,7 @@ pub struct Install<'a> { pub num_cycles: Option, } -impl<'a> Query<'a> { +impl Query<'_> { pub async fn bytes(&self, payload: Vec) -> Result, String> { let canister = self.canister; match canister.runtime { @@ -972,7 +972,7 @@ impl<'a> Query<'a> { } } -impl<'a> Update<'a> { +impl Update<'_> { pub async fn bytes(&self, payload: Vec) -> Result, String> { let canister = self.canister; match canister.runtime { diff --git a/rs/rust_canisters/dfn_candid/src/lib.rs b/rs/rust_canisters/dfn_candid/src/lib.rs index 7c31d2e8b82..c292858a37b 100644 --- a/rs/rust_canisters/dfn_candid/src/lib.rs +++ b/rs/rust_canisters/dfn_candid/src/lib.rs @@ -116,7 +116,7 @@ impl FromWire /// this is a private mirror of the type in dfn_core::api which generates the /// serialization/deserialization for it without putting a dependency on candid /// in dfn_core - +/// /// This is a bit of a weird type witness. Candid is multi arity in both inputs /// and outputs the outputs don't fit in well with rust. To make writing candid /// nicer we assume that every function is going to try and return one value, if diff --git a/rs/rust_canisters/dfn_core/BUILD.bazel b/rs/rust_canisters/dfn_core/BUILD.bazel index 77febd3f22c..1614836dca5 100644 --- a/rs/rust_canisters/dfn_core/BUILD.bazel +++ b/rs/rust_canisters/dfn_core/BUILD.bazel @@ -6,8 +6,6 @@ package(default_visibility = [ # Keep sorted. "//publish/canisters:__pkg__", "//rs/ledger_suite/icp:__subpackages__", - "//rs/ledger_suite/icrc1/benchmark/generator:__pkg__", - "//rs/ledger_suite/icrc1/benchmark/worker:__pkg__", "//rs/nervous_system/canisters:__subpackages__", "//rs/nervous_system/clients:__pkg__", "//rs/nervous_system/common:__subpackages__", diff --git a/rs/rust_canisters/dfn_core/src/stable.rs b/rs/rust_canisters/dfn_core/src/stable.rs index 68dbfe1b541..d4cfd4df04b 100644 --- a/rs/rust_canisters/dfn_core/src/stable.rs +++ b/rs/rust_canisters/dfn_core/src/stable.rs @@ -11,7 +11,6 @@ const PAGE_SIZE: f64 = 64.0 * 1024.0; /// +--------+-----------------+--------> /// | length | content | junk /// +--------+-----------------+--------> - const LENGTH_BYTES: u32 = 4; pub fn stable64_size() -> u64 { diff --git a/rs/rust_canisters/dfn_http_metrics/BUILD.bazel b/rs/rust_canisters/dfn_http_metrics/BUILD.bazel index d73f46ba426..7f261d8bee6 100644 --- a/rs/rust_canisters/dfn_http_metrics/BUILD.bazel +++ b/rs/rust_canisters/dfn_http_metrics/BUILD.bazel @@ -4,8 +4,6 @@ package(default_visibility = [ # Keep sorted. "//rs/ledger_suite/icp:__subpackages__", "//rs/ledger_suite/icrc1:__pkg__", - "//rs/ledger_suite/icrc1/benchmark/generator:__pkg__", - "//rs/ledger_suite/icrc1/benchmark/worker:__pkg__", "//rs/ledger_suite/icrc1/ledger:__pkg__", "//rs/nns:__subpackages__", "//rs/registry/canister:__pkg__", diff --git a/rs/rust_canisters/memory_test/src/main.rs b/rs/rust_canisters/memory_test/src/main.rs index 8c09b785fbf..6014e0207cc 100644 --- a/rs/rust_canisters/memory_test/src/main.rs +++ b/rs/rust_canisters/memory_test/src/main.rs @@ -423,7 +423,7 @@ fn copy() { let step = operation.step.unwrap_or(ELEMENT_SIZE) / ELEMENT_SIZE; let value = operation.value.unwrap_or_else(|| rand(0, u8::MAX)); // Address can't exceed 4GiB WASM memory - let len = (operation.size as usize + ELEMENT_SIZE - 1) / ELEMENT_SIZE; + let len = (operation.size as usize).div_ceil(ELEMENT_SIZE); assert!(2 * len <= MEMORY_LEN); MEMORY.with(|memory| { let mut memory_ref = memory.borrow_mut(); diff --git a/rs/rust_canisters/pmap/canister/main.rs b/rs/rust_canisters/pmap/canister/main.rs index 9bf061382c9..fbfbe9a8784 100644 --- a/rs/rust_canisters/pmap/canister/main.rs +++ b/rs/rust_canisters/pmap/canister/main.rs @@ -1,3 +1,6 @@ +// TODO: EXC-1841 +#![allow(static_mut_refs)] + use dfn_core::api::print; use dfn_macro::{query, update}; use std::ptr::{addr_of, addr_of_mut}; diff --git a/rs/rust_canisters/random_traffic_test/BUILD.bazel b/rs/rust_canisters/random_traffic_test/BUILD.bazel index 23eaf726e71..356e46469df 100644 --- a/rs/rust_canisters/random_traffic_test/BUILD.bazel +++ b/rs/rust_canisters/random_traffic_test/BUILD.bazel @@ -8,14 +8,15 @@ DEPENDENCIES = [ "//rs/types/error_types", "//rs/types/types", "@crate_index//:candid", - "@crate_index//:ic-cdk", + "@crate_index//:ic_cdk_next", "@crate_index//:futures", "@crate_index//:rand", "@crate_index//:serde", + "@crate_index//:serde_bytes", ] MACRO_DEPENDENCIES = [ - "@crate_index//:ic-cdk-macros", + "@crate_index//:ic_cdk_macros_next", ] rust_library( diff --git a/rs/rust_canisters/random_traffic_test/Cargo.toml b/rs/rust_canisters/random_traffic_test/Cargo.toml index 44814930525..58bf565c329 100644 --- a/rs/rust_canisters/random_traffic_test/Cargo.toml +++ b/rs/rust_canisters/random_traffic_test/Cargo.toml @@ -10,10 +10,11 @@ path = "src/main.rs" [dependencies] candid = { workspace = true } ic-base-types = { path = "../../types/base_types" } -ic-cdk = { workspace = true } -ic-cdk-macros = { workspace = true } +ic-cdk = { git = "https://github.com/dfinity/cdk-rs", rev = "4e287ce51636b0e70768c193da38d2fc5324ea15" } +ic-cdk-macros = { git = "https://github.com/dfinity/cdk-rs", rev = "4e287ce51636b0e70768c193da38d2fc5324ea15" } ic-error-types = { path = "../../types/error_types" } ic-types = { path = "../../types/types" } futures = { workspace = true } rand = { workspace = true } serde = { workspace = true } +serde_bytes = { workspace = true } diff --git a/rs/rust_canisters/random_traffic_test/src/lib.rs b/rs/rust_canisters/random_traffic_test/src/lib.rs index e36fda86353..9577671b003 100644 --- a/rs/rust_canisters/random_traffic_test/src/lib.rs +++ b/rs/rust_canisters/random_traffic_test/src/lib.rs @@ -5,31 +5,41 @@ use ic_types::messages::MAX_INTER_CANISTER_PAYLOAD_IN_BYTES_U64; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; use std::ops::RangeInclusive; +use std::time::Duration; -/// A full config for generating random calls and replies. Ranges are stored as individual u32 +/// A full config for generating random calls and replies. Ranges are stored as `(u32, u32)` /// because ranges don't implement `CandidType`. #[derive(Serialize, Deserialize, Clone, Debug, CandidType, Hash)] pub struct Config { + /// A list of canister IDs, i.e. receivers for calls made by this canister. pub receivers: Vec, - pub call_bytes_min: u32, - pub call_bytes_max: u32, - pub reply_bytes_min: u32, - pub reply_bytes_max: u32, - pub instructions_count_min: u32, - pub instructions_count_max: u32, + /// `(min, max)` for the payload size in bytes included in a call. + pub call_bytes_range: (u32, u32), + /// `(min, max)` for the payload size in bytes included in a reply. + pub reply_bytes_range: (u32, u32), + /// `(min, max)` for the simulated number of instructions to generate a reply. + pub instructions_count_range: (u32, u32), + /// `(min, max)` for the timeout in seconds used for best-effort calls. + pub timeout_secs_range: (u32, u32), + /// The maximum number of calls attempted per heartbeat. + pub calls_per_heartbeat: u32, + /// The probability for a making a dowstream call rather than reply in %. + pub downstream_call_percentage: u32, + /// The probability for making a best-effort call rather a guaranteed response call in %. + pub best_effort_call_percentage: u32, } impl Default for Config { - /// Default using the full range of payloads and no delayed responses. fn default() -> Self { Self { receivers: vec![], - call_bytes_min: 0, - call_bytes_max: MAX_INTER_CANISTER_PAYLOAD_IN_BYTES_U64 as u32, - reply_bytes_min: 0, - reply_bytes_max: MAX_INTER_CANISTER_PAYLOAD_IN_BYTES_U64 as u32, - instructions_count_min: 0, - instructions_count_max: 0, + call_bytes_range: (0, MAX_INTER_CANISTER_PAYLOAD_IN_BYTES_U64 as u32), + reply_bytes_range: (0, MAX_INTER_CANISTER_PAYLOAD_IN_BYTES_U64 as u32), + instructions_count_range: (0, 0), + timeout_secs_range: (1, 100), + calls_per_heartbeat: 0, + downstream_call_percentage: 0, + best_effort_call_percentage: 100, } } } @@ -50,6 +60,10 @@ impl Config { call_bytes: RangeInclusive, reply_bytes: RangeInclusive, instructions_count: RangeInclusive, + timeout_secs: RangeInclusive, + calls_per_heartbeat: u32, + downstream_call_percentage: u32, + best_effort_call_percentage: u32, ) -> Result { // Sanity checks. After passing these, the canister should run as intended. if call_bytes.is_empty() { @@ -67,24 +81,28 @@ impl Config { if instructions_count.is_empty() { return Err("empty instructions_count range".to_string()); } + if timeout_secs.is_empty() { + return Err("empty timeout range".to_string()); + } Ok(Self { receivers, - call_bytes_min: *call_bytes.start(), - call_bytes_max: *call_bytes.end(), - reply_bytes_min: *reply_bytes.start(), - reply_bytes_max: *reply_bytes.end(), - instructions_count_min: *instructions_count.start(), - instructions_count_max: *instructions_count.end(), + call_bytes_range: (*call_bytes.start(), *call_bytes.end()), + reply_bytes_range: (*reply_bytes.start(), *reply_bytes.end()), + instructions_count_range: (*instructions_count.start(), *instructions_count.end()), + timeout_secs_range: (*timeout_secs.start(), *timeout_secs.end()), + calls_per_heartbeat, + downstream_call_percentage, + best_effort_call_percentage, }) } } /// Records the outcome of an outgoing call. #[derive(Serialize, Deserialize, Clone, PartialEq, Eq, CandidType)] -pub enum Reply { - /// A response including a data payload of a distinct size was received. - Bytes(u32), +pub enum Response { + /// A reply including a data payload of a distinct size was received. + Reply(u32), /// The call was rejected with a reject code and a reject message. Reject(u32, String), } @@ -102,38 +120,54 @@ pub struct Record { pub call_depth: u32, /// The number of bytes included in the payload. pub sent_bytes: u32, - /// The kind of reply received, i.e. a payload or a reject response. - pub reply: Option, + /// The timeout in seconds set for a best-effort call; `None` for a guaranteed response call. + pub timeout_secs: Option, + /// The kind of response received, i.e. a reply with a payload or a reject response; + /// and the duration after which the response was received (from when the call was made). + pub duration_and_response: Option<(Duration, Response)>, } /// Human readable printer. impl std::fmt::Debug for Record { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { - match &self.caller { - None => write!( - f, - "{} ({:x}) | ", - &self.receiver.to_string()[..5], - self.call_tree_id, - ), - Some(caller) => write!( + write!( + f, + "{} ({:x}) ", + &self.receiver.to_string()[..5], + self.call_tree_id + )?; + + // A timeout indicates a best-effort message. + if let Some(timeout_secs) = self.timeout_secs { + write!(f, "timeout[s]: {} ", timeout_secs)?; + } + + // A caller indicates a downstream call. + if let Some(caller) = self.caller { + write!( f, - "{} ({:x}) (caller {} @ depth {}) | ", - &self.receiver.to_string()[..5], - self.call_tree_id, + "(caller {} @ depth {}) ", &caller.to_string()[..5], - self.call_depth, - ), - }?; + self.call_depth + )?; + } - write!(f, "sending {} bytes | ", self.sent_bytes)?; + write!(f, " sending {} bytes | ", self.sent_bytes)?; - match &self.reply { + match &self.duration_and_response { None => write!(f, "..."), - Some(Reply::Bytes(bytes)) => write!(f, "received {} bytes", bytes), - Some(Reply::Reject(error_code, error_msg)) => write!( + Some((call_duration, Response::Reply(bytes))) => { + write!( + f, + "duration[s]: {}, received {} bytes", + call_duration.as_secs(), + bytes + ) + } + Some((call_duration, Response::Reject(error_code, error_msg))) => write!( f, - "reject({}): {error_msg}", + "duration[s]: {}, reject({}): {error_msg}", + call_duration.as_secs(), RejectCode::try_from(*error_code as u64).unwrap(), ), } @@ -148,9 +182,11 @@ pub struct Metrics { pub downstream_calls_attempted: u32, pub calls_replied: u32, pub calls_rejected: u32, + pub calls_unknown_outcome: u32, pub sent_bytes: u32, pub received_bytes: u32, pub rejected_bytes: u32, + pub unknown_outcome_bytes: u32, } /// Extracts some basic metrics from the records. @@ -164,12 +200,18 @@ pub fn extract_metrics(records: &BTreeMap) -> Metrics { metrics.downstream_calls_attempted += 1; } - match &record.reply { - Some(Reply::Bytes(received_bytes)) => { + match &record.duration_and_response { + Some((_, Response::Reply(received_bytes))) => { metrics.calls_replied += 1; metrics.received_bytes += received_bytes; } - Some(Reply::Reject(_, _)) => { + Some((_, Response::Reject(reject_code, _))) + if RejectCode::try_from(*reject_code as u64).unwrap() == RejectCode::SysUnknown => + { + metrics.calls_unknown_outcome += 1; + metrics.unknown_outcome_bytes += record.sent_bytes; + } + Some((_, Response::Reject(..))) => { metrics.calls_rejected += 1; metrics.rejected_bytes += record.sent_bytes; } diff --git a/rs/rust_canisters/random_traffic_test/src/main.rs b/rs/rust_canisters/random_traffic_test/src/main.rs index ce928a0553e..90ffa7e0669 100644 --- a/rs/rust_canisters/random_traffic_test/src/main.rs +++ b/rs/rust_canisters/random_traffic_test/src/main.rs @@ -1,7 +1,11 @@ -use candid::{CandidType, Encode}; +use candid::CandidType; use futures::future::select_all; use ic_base_types::{CanisterId, PrincipalId}; -use ic_cdk::{api, caller, id}; +use ic_cdk::{ + api, + call::{Call, CallError, CallResult, ConfigurableCall, SendableCall}, + setup, +}; use ic_cdk_macros::{heartbeat, init, query, update}; use rand::{ distributions::{Distribution, WeightedIndex}, @@ -15,29 +19,29 @@ use std::cell::{Cell, RefCell}; use std::collections::BTreeMap; use std::future::Future; use std::hash::{DefaultHasher, Hasher}; -use std::ops::RangeInclusive; +use std::time::Duration; thread_local! { - /// Random number generator used for determining payload sizes et.al. - static RNG: RefCell = RefCell::new(StdRng::seed_from_u64(13)); - /// Weight for making a reply used in a weighted binomial distribution. - static REPLY_WEIGHT: Cell = const { Cell::new(1) }; - /// Weight for making a downstream call used in a weighted binomial distribution. - static CALL_WEIGHT: Cell = const { Cell::new(0) }; /// A configuration holding parameters determining the canisters behavior, such as the range /// of payload bytes it should send. static CONFIG: RefCell = RefCell::default(); - /// The maximum number of calls each heartbeat will attempt to make. - static MAX_CALLS_PER_HEARTBEAT: Cell = Cell::default(); + /// Random number generator used for determining payload sizes et.al. + static RNG: RefCell = RefCell::new(StdRng::seed_from_u64(13)); /// A hasher used to generate unique call tree IDs. static HASHER: RefCell = RefCell::default(); /// An index for each attempted call; starts at 0 and then increments with each call. static CALL_INDEX: Cell = Cell::default(); - /// A collection of records; one record for each call. Keeps track of how each call went, - /// whether it was rejected or not and how many bytes were received. - static RECORDS: RefCell> = RefCell::default(); + /// A collection of timestamps and records; one record for each call. Keeps track of whether it was + /// rejected or not and how many bytes were sent and received. + static RECORDS: RefCell> = RefCell::default(); /// A counter for synchronous rejections. static SYNCHRONOUS_REJECTIONS_COUNT: Cell = Cell::default(); + /// A `COIN` that can be 'flipped' to determine whether to make a downstream call or not. + /// The default value set here will yield only 'reply'. + static DOWNSTREAM_CALL_COIN: RefCell> = RefCell::new(WeightedIndex::::new([0, 100]).unwrap()); + /// A `COIN` that can be 'flipped' to determine whether to make a best-effort call or a guaranteed response call. + /// The default value set here will yield only 'best_effort' + static BEST_EFFORT_CALL_COIN: RefCell> = RefCell::new(WeightedIndex::::new([100, 0]).unwrap()); } /// The intercanister message sent to `handle_call()` by the heartbeat of this canister @@ -48,38 +52,31 @@ struct Message { call_tree_id: u32, /// The depth of the call starting from 0 and incrementing by 1 for each downstream call. call_depth: u32, - /// A payload of a certain size; it otherwise does not any contain information. - payload: Vec, + /// Optional padding, to bring the payload to the desired byte size. + #[serde(with = "serde_bytes")] + padding: Vec, } impl Message { /// Creates a new `Message` of size `bytes_count`; may slightly exceed the target for /// very small numbers. - fn new(call_tree_id: u32, call_depth: u32, bytes_count: usize) -> Self { + fn new(call_tree_id: u32, call_depth: u32, bytes_count: u32) -> Self { Self { call_tree_id, call_depth, - payload: vec![0_u8; bytes_count.saturating_sub(std::mem::size_of::())], + padding: vec![0_u8; (bytes_count as usize).saturating_sub(std::mem::size_of::())], } } /// Returns the number of bytes the message consists of. fn count_bytes(&self) -> usize { - std::mem::size_of::() + self.payload.len() + std::mem::size_of::() + self.padding.len() } } -/// Returns a random receiver if any. -fn choose_receiver() -> Option { - CONFIG.with_borrow(|config| { - RNG.with_borrow_mut(|rng| config.receivers.as_slice().choose(rng).cloned()) - }) -} - -/// Generates a random `u32` contained in `range`. -fn gen_range(range: RangeInclusive) -> u32 { - RNG.with_borrow_mut(|rng| rng.gen_range(range)) -} +/// Wrapper around the reply from `handle_call()` such that `serde_bytes` can be used. +#[derive(Serialize, Deserialize, CandidType)] +struct Reply(#[serde(with = "serde_bytes")] Vec); /// Returns the next call index. fn next_call_index() -> u32 { @@ -97,48 +94,59 @@ fn next_call_tree_id() -> u32 { }) } -/// Returns `true` if `error_msg` corresponds to a synchronous rejection. -fn synchronous_rejection(error_msg: &str) -> bool { - error_msg.contains("Couldn't send message") +/// Generates a random `u32` by sampling `min..=max`. +fn sample((min, max): (u32, u32)) -> u32 { + RNG.with_borrow_mut(|rng| rng.gen_range(min..=max)) } -/// Returns the call bytes range as defined in `CONFIG`. -fn call_bytes_range() -> RangeInclusive { - CONFIG.with_borrow(|config| config.call_bytes_min..=config.call_bytes_max) +/// Generates a random payload size for a call. +fn gen_call_bytes() -> u32 { + CONFIG.with_borrow(|config| sample(config.call_bytes_range)) } -/// Returns the reply bytes range as defined in `CONFIG`. -fn reply_bytes_range() -> RangeInclusive { - CONFIG.with_borrow(|config| config.reply_bytes_min..=config.reply_bytes_max) +/// Generates a random payload size for a reply. +fn gen_reply_bytes() -> u32 { + CONFIG.with_borrow(|config| sample(config.reply_bytes_range)) } -/// Returns the instructions count range as defined in `CONFIG`. -fn instructions_count_range() -> RangeInclusive { - CONFIG.with_borrow(|config| config.instructions_count_min..=config.instructions_count_max) +/// Generates a random number of simulated instructions for generating a reply. +fn gen_instructions_count() -> u32 { + CONFIG.with_borrow(|config| sample(config.instructions_count_range)) } -/// Sets the test config; returns the current config. -#[update] -fn set_config(config: Config) -> Config { - CONFIG.replace(config) +/// Generates a timeout in seconds for a best-effort call. +fn gen_timeout_secs() -> u32 { + CONFIG.with_borrow(|config| sample(config.timeout_secs_range)) } -/// Sets the requests per round to be sent each heart beat; returns the current value. -#[update] -fn set_max_calls_per_heartbeat(max_calls_per_heartbeat: u32) -> u32 { - MAX_CALLS_PER_HEARTBEAT.replace(max_calls_per_heartbeat) +/// Picks a random receiver from `config.receivers` if any; otherwise return own canister ID. +fn receiver() -> CanisterId { + match CONFIG.with_borrow(|config| { + RNG.with_borrow_mut(|rng| config.receivers.as_slice().choose(rng).cloned()) + }) { + Some(receiver) => receiver, + None => CanisterId::try_from(api::canister_self().as_slice()).unwrap(), + } } -/// Sets the reply weight; returns the current weight. +/// Sets the test config; returns the current config. #[update] -fn set_reply_weight(reply_weight: u32) -> u32 { - REPLY_WEIGHT.replace(reply_weight) -} +fn set_config(config: Config) -> Config { + fn to_weights(mut percentage: u32) -> [u32; 2] { + if percentage > 100 { + percentage = 100; + } + [percentage, 100 - percentage] + } -/// Sets the call weight; returns the current weight. -#[update] -fn set_call_weight(call_weight: u32) -> u32 { - CALL_WEIGHT.replace(call_weight) + // Update `COINS`. + DOWNSTREAM_CALL_COIN + .replace(WeightedIndex::::new(to_weights(config.downstream_call_percentage)).unwrap()); + BEST_EFFORT_CALL_COIN.replace( + WeightedIndex::::new(to_weights(config.best_effort_call_percentage)).unwrap(), + ); + + CONFIG.replace(config) } /// Seeds `RNG`. @@ -147,10 +155,26 @@ fn seed_rng(seed: u64) { RNG.with_borrow_mut(|rng| *rng = StdRng::seed_from_u64(seed)); } +/// Sets `CONFIG` such that the canister stops making calls altogether; returns the config +/// prior to modifying. +#[update] +fn stop_chatter() -> Config { + set_config(CONFIG.with_borrow(|config| Config { + calls_per_heartbeat: 0, + downstream_call_percentage: 0, + ..config.clone() + })) +} + /// Returns the canister records. #[query] fn records() -> BTreeMap { - RECORDS.with_borrow(|records| records.clone()) + RECORDS.with_borrow(|records| { + records + .iter() + .map(|(index, (_, record))| (*index, record.clone())) + .collect() + }) } /// Returns the number of synchronous rejections. @@ -159,85 +183,126 @@ fn synchronous_rejections_count() -> u32 { SYNCHRONOUS_REJECTIONS_COUNT.get() } +/// Flip the `DOWNSTREAM_CALL_COIN` to determine whether we should make a downstream call or reply +/// instead. +fn should_make_downstream_call() -> bool { + RNG.with_borrow_mut(|rng| DOWNSTREAM_CALL_COIN.with_borrow(|coin| coin.sample(rng)) == 0) +} + +/// Flip the `BEST_EFFORT_COIN` to determine whether we should make a best-effort call or a +/// guaranteed response call. +fn should_make_best_effort_call() -> bool { + RNG.with_borrow_mut(|rng| BEST_EFFORT_CALL_COIN.with_borrow(|coin| coin.sample(rng)) == 0) +} + /// Generates a future for a randomized call that can be awaited; inserts a new record at `index` -/// that must updated (or removed) after awaiting the call. For each call, the call index is +/// that must be updated (or removed) after awaiting the call. For each call, the call index is /// incremented by 1, such that successive calls have adjacent indices. fn setup_call( - receiver: CanisterId, call_tree_id: u32, call_depth: u32, -) -> (impl Future>>, u32) { - let msg = Message::new( - call_tree_id, - call_depth, - gen_range(call_bytes_range()) as usize, - ); +) -> (impl Future>, u32) { + let msg = Message::new(call_tree_id, call_depth, gen_call_bytes()); + let sent_bytes = msg.count_bytes() as u32; + let receiver = receiver(); + let caller = (call_depth > 0).then_some(CanisterId::unchecked_from_principal(PrincipalId( + api::msg_caller(), + ))); + let timeout_secs = should_make_best_effort_call().then_some(gen_timeout_secs()); + + let call = Call::new(receiver.into(), "handle_call"); + let call = call.with_arg(msg); + let call = match timeout_secs { + Some(timeout_secs) => call.change_timeout(timeout_secs), + None => call.with_guaranteed_response(), + }; - // Inserts a new call record at the next `index`. + // Once the call was successfully generated, insert a call timestamp and record at `index`. let index = next_call_index(); RECORDS.with_borrow_mut(|records| { records.insert( index, - Record { - receiver, - caller: (call_depth > 0) - .then_some(CanisterId::unchecked_from_principal(PrincipalId(caller()))), - call_tree_id, - call_depth, - sent_bytes: msg.count_bytes() as u32, - reply: None, - }, - ); + ( + api::time(), + Record { + receiver, + caller, + call_tree_id, + call_depth, + sent_bytes, + timeout_secs, + duration_and_response: None, + }, + ), + ) }); - let future = api::call::call_raw(receiver.into(), "handle_call", Encode!(&msg).unwrap(), 0); - - (future, index) + (call.call(), index) } -/// Updates the record at `index` using the `response` to the corresponding call. +/// Updates the record at `index` using the `result` of the corresponding call. /// /// Removes the record for a synchronous rejection since those can be quite numerous when the /// subnet is at its limits. Note that since the call `index` is part of the records, removing /// the records for synchronous rejections will result in gaps in these numbers thus they are /// still included indirectly. -fn update_record(response: &api::call::CallResult>, index: u32) { - // Updates the `Reply` at `index` in `RECORDS`. - let set_reply_in_call_record = move |reply: Reply| { +fn update_record(result: &CallResult, index: u32) { + // Updates the `Response` at `index` in `RECORDS`. + let set_reply_in_call_record = move |response: Response| { RECORDS.with_borrow_mut(|records| { - let record = records.get_mut(&index).unwrap(); - assert!(record.reply.is_none(), "duplicate reply received"); - record.reply = Some(reply); + let (call_timestamp, record) = records.get_mut(&index).unwrap(); + assert!( + record.duration_and_response.is_none(), + "duplicate reply received" + ); + let response_timestamp = api::time(); + assert!( + response_timestamp >= *call_timestamp, + "retrograde blocktime" + ); + + let call_duration = Duration::from_nanos(response_timestamp - *call_timestamp); + record.duration_and_response = Some((call_duration, response)); }); }; - match response { - Err((_, msg)) if synchronous_rejection(msg) => { + + match result { + Err(CallError::CallRejected(rejection)) if rejection.is_sync() => { // Remove the record for synchronous rejections. SYNCHRONOUS_REJECTIONS_COUNT.set(SYNCHRONOUS_REJECTIONS_COUNT.get() + 1); RECORDS.with_borrow_mut(|records| { - assert!(records.remove(&index).unwrap().reply.is_none()) + assert!(records + .remove(&index) + .unwrap() + .1 + .duration_and_response + .is_none()) }); } - Err((reject_code, msg)) => { - set_reply_in_call_record(Reply::Reject(*reject_code as u32, msg.to_string())); + Err(CallError::CallRejected(rejection)) => { + set_reply_in_call_record(Response::Reject( + rejection.reject_code().into(), + rejection.reject_message().to_string(), + )); } - Ok(result) => { - set_reply_in_call_record(Reply::Bytes(result.len() as u32)); + Err(CallError::CandidDecodeFailed(error)) => { + unreachable!("{error}"); + } + + Ok(reply) => { + set_reply_in_call_record(Response::Reply(reply.0.len() as u32)); } } } -/// Generates `MAX_CALLS_PER_HEARTBEAT` calls as futures. The records are updated whenever a -/// reply comes in, i.e. not only after all of them have completed. +/// Generates `calls_per_heartbeat` call futures. They are awaited; whenever a call concludes +/// its record is updated (or removed in case of a synchronous rejection). #[heartbeat] async fn heartbeat() { let (mut futures, mut record_indices) = (Vec::new(), Vec::new()); - for _ in 0..MAX_CALLS_PER_HEARTBEAT.get() { - let Some(receiver) = choose_receiver() else { - return; - }; - let (future, index) = setup_call(receiver, next_call_tree_id(), 0); - futures.push(future); + for _ in 0..CONFIG.with_borrow(|config| config.calls_per_heartbeat) { + let (future, index) = setup_call(next_call_tree_id(), 0); + futures.push(Box::pin(future)); record_indices.push(index); } @@ -256,28 +321,13 @@ async fn heartbeat() { /// Handles incoming calls; this method is called from the heartbeat method. /// /// Replies if: -/// - sampling the weighted binomial distribution tells us to do so. +/// - flipping the coin tells us to do so. /// - if it tells us not to do so but the attempted downstream call fails for any reason. #[update] -async fn handle_call(msg: Message) -> Vec { - // Samples a weighted binomial distribution to decide whether to make a downstream call (true) - // or reply (false). Defaults to `false` for bad weights (e.g. both 0). - fn should_make_downstream_call() -> bool { - RNG.with_borrow_mut(|rng| { - WeightedIndex::new([CALL_WEIGHT.get(), REPLY_WEIGHT.get()]) - .map_or(false, |dist| dist.sample(rng) == 0) - }) - } - - // Make downstream calls until - // - sampling the distribution tells us to stop. - // - setting up a call fails. - // - a downstream call is rejected for any reason. +async fn handle_call(msg: Message) -> Reply { + // Make downstream calls as long as flipping the coin tells us to do so. while should_make_downstream_call() { - let Some(receiver) = choose_receiver() else { - break; - }; - let (future, record_index) = setup_call(receiver, msg.call_tree_id, msg.call_depth + 1); + let (future, record_index) = setup_call(msg.call_tree_id, msg.call_depth + 1); let result = future.await; update_record(&result, record_index); @@ -289,20 +339,21 @@ async fn handle_call(msg: Message) -> Vec { } } - let payload_bytes = gen_range(reply_bytes_range()); - let instructions_count = gen_range(instructions_count_range()); + let payload_bytes = gen_reply_bytes(); // Do some thinking. - let counts = api::performance_counter(0) + instructions_count as u64; + let counts = api::performance_counter(0) + gen_instructions_count() as u64; while counts > api::performance_counter(0) {} - vec![0_u8; payload_bytes as usize] + Reply(vec![0_u8; payload_bytes as usize]) } /// Initializes the `HASHER` by hashing our own canister ID. #[init] fn initialize_hasher() { - HASHER.with_borrow_mut(|hasher| hasher.write(id().as_slice())); + HASHER.with_borrow_mut(|hasher| hasher.write(api::canister_self().as_slice())); } -fn main() {} +fn main() { + setup(); +} diff --git a/rs/rust_canisters/tests/test/canister_management.rs b/rs/rust_canisters/tests/test/canister_management.rs index f3c206c88e5..e40df5ec821 100644 --- a/rs/rust_canisters/tests/test/canister_management.rs +++ b/rs/rust_canisters/tests/test/canister_management.rs @@ -2,7 +2,7 @@ use assert_matches::assert_matches; use candid::{Decode, Encode}; use canister_test::{local_test_e, Canister, Runtime, Wasm}; use ic_error_types::ErrorCode; -use ic_management_canister_types::{self as ic00, CanisterIdRecord, CanisterStatusResult, IC_00}; +use ic_management_canister_types::{self as ic00, CanisterIdRecord, CanisterStatusResultV2, IC_00}; use ic_test_utilities::universal_canister::UNIVERSAL_CANISTER_WASM; use ic_test_utilities::universal_canister::{ wasm as universal_canister_argument_builder, CallArgs, @@ -32,7 +32,7 @@ fn test_set_controller() { // aaaaa-aa" // // The anonymous user is not allowed to do a "canister_status" - let res: Result = runtime + let res: Result = runtime .get_management_canister_with_effective_canister_id( universal_canister.canister_id().into(), ) @@ -59,7 +59,7 @@ fn test_set_controller() { .update_("update", bytes, arg) .await .unwrap(); - let status = Decode!(&status_bytes, CanisterStatusResult).unwrap(); + let status = Decode!(&status_bytes, CanisterStatusResultV2).unwrap(); assert_eq!(status.controller(), universal_canister.canister_id().get()); Ok(()) diff --git a/rs/sns/BUILD.bazel b/rs/sns/BUILD.bazel new file mode 100644 index 00000000000..218d8921e51 --- /dev/null +++ b/rs/sns/BUILD.bazel @@ -0,0 +1 @@ +# This file intentionally left blank. diff --git a/rs/sns/cli/BUILD.bazel b/rs/sns/cli/BUILD.bazel index 4b71f72f1a7..a9e61b92a96 100644 --- a/rs/sns/cli/BUILD.bazel +++ b/rs/sns/cli/BUILD.bazel @@ -7,29 +7,35 @@ DEPENDENCIES = [ # Keep sorted. "//rs/crypto/sha2", "//rs/nervous_system/agent", + "//rs/nervous_system/candid_utils", "//rs/nervous_system/common", "//rs/nervous_system/common/test_keys", "//rs/nervous_system/humanize", "//rs/nervous_system/proto", + "//rs/nns/cmc", "//rs/nns/common", "//rs/nns/constants", "//rs/nns/governance/api", "//rs/nns/sns-wasm", - "//rs/sns/governance", + "//rs/sns/governance/api", "//rs/sns/init", "//rs/sns/root", "//rs/types/base_types", + "//rs/types/management_canister_types", "@crate_index//:anyhow", "@crate_index//:base64", "@crate_index//:candid", "@crate_index//:clap", + "@crate_index//:dfx-core", "@crate_index//:futures", "@crate_index//:hex", "@crate_index//:ic-agent", + "@crate_index//:ic-wasm", "@crate_index//:itertools", "@crate_index//:json-patch", "@crate_index//:pretty_assertions", "@crate_index//:serde", + "@crate_index//:serde_cbor", "@crate_index//:serde_json", "@crate_index//:serde_yaml", "@crate_index//:tempfile", diff --git a/rs/sns/cli/Cargo.toml b/rs/sns/cli/Cargo.toml index 6df0cf7bd47..a9f00aed991 100644 --- a/rs/sns/cli/Cargo.toml +++ b/rs/sns/cli/Cargo.toml @@ -17,28 +17,34 @@ path = "src/lib.rs" anyhow = { workspace = true } base64 = { workspace = true } candid = { workspace = true } +candid-utils = { path = "../../nervous_system/candid_utils" } clap = { workspace = true } +dfx-core = { workspace = true } futures = { workspace = true } hex = { workspace = true } ic-agent = { workspace = true } ic-base-types = { path = "../../types/base_types" } ic-crypto-sha2 = { path = "../../crypto/sha2" } +ic-management-canister-types = { path = "../../types/management_canister_types" } ic-nervous-system-agent = { path = "../../nervous_system/agent" } ic-nervous-system-common = { path = "../../nervous_system/common" } ic-nervous-system-common-test-keys = { path = "../../nervous_system/common/test_keys" } ic-nervous-system-humanize = { path = "../../nervous_system/humanize" } ic-nervous-system-proto = { path = "../../nervous_system/proto" } +cycles-minting-canister = { path = "../../nns/cmc" } ic-nns-common = { path = "../../nns/common" } ic-nns-constants = { path = "../../nns/constants" } ic-nns-governance-api = { path = "../../nns/governance/api" } -ic-sns-governance = { path = "../governance" } +ic-sns-governance-api = { path = "../governance/api" } ic-sns-init = { path = "../init" } ic-sns-root = { path = "../root" } ic-sns-wasm = { path = "../../nns/sns-wasm" } +ic-wasm = { workspace = true } itertools = { workspace = true } json-patch = "0.2.6" pretty_assertions = { workspace = true } serde = { workspace = true } +serde_cbor = { workspace = true } serde_json = { workspace = true } serde_yaml = { workspace = true } tempfile = { workspace = true } diff --git a/rs/sns/cli/src/deploy.rs b/rs/sns/cli/src/deploy.rs index c266dd2dc5f..8400fdef06c 100644 --- a/rs/sns/cli/src/deploy.rs +++ b/rs/sns/cli/src/deploy.rs @@ -14,7 +14,7 @@ use crate::{call_dfx, call_dfx_or_panic, get_identity, hex_encode_candid, Deploy use anyhow::{anyhow, Context, Result}; use ic_base_types::PrincipalId; use ic_nns_constants::ROOT_CANISTER_ID as NNS_ROOT_CANISTER_ID; -use ic_sns_governance::pb::v1::ListNeuronsResponse; +use ic_sns_governance_api::pb::v1::ListNeuronsResponse; use ic_sns_init::{pb::v1::SnsInitPayload, SnsCanisterIds, SnsCanisterInitPayloads}; use ic_sns_root::pb::v1::ListSnsCanistersResponse; diff --git a/rs/sns/cli/src/lib.rs b/rs/sns/cli/src/lib.rs index fd5f82d58c2..789e1c1cb5a 100644 --- a/rs/sns/cli/src/lib.rs +++ b/rs/sns/cli/src/lib.rs @@ -2,10 +2,10 @@ use crate::{ deploy::DirectSnsDeployerForTests, health::HealthArgs, init_config_file::InitConfigFileArgs, neuron_id_to_candid_subaccount::NeuronIdToCandidSubaccountArgs, prepare_canisters::PrepareCanistersArgs, propose::ProposeArgs, + upgrade_sns_controlled_canister::UpgradeSnsControlledCanisterArgs, }; use anyhow::{anyhow, bail, Context, Result}; use candid::{CandidType, Decode, Encode, IDLArgs}; -use clap::Parser; use ic_agent::Agent; use ic_base_types::PrincipalId; use ic_crypto_sha2::Sha256; @@ -28,7 +28,6 @@ use std::{ sync::Once, }; use tempfile::NamedTempFile; - pub mod deploy; pub mod health; pub mod init_config_file; @@ -38,11 +37,15 @@ pub mod prepare_canisters; pub mod propose; mod table; pub mod unit_helpers; -mod utils; +pub mod upgrade_sns_controlled_canister; +use clap::{ArgGroup, Args, Parser}; +pub mod utils; #[cfg(test)] mod tests; +pub const MAINNET_NETWORK: &str = "https://ic0.app"; + /// We use a giant tail to avoid colliding with/stomping on identity that a user /// might have created for themselves. const TEST_NEURON_1_OWNER_DFX_IDENTITY_NAME: &str = @@ -57,6 +60,47 @@ const TEST_NEURON_1_OWNER_DFX_IDENTITY_NAME: &str = pub struct CliArgs { #[clap(subcommand)] pub sub_command: SubCommand, + + /// The user identity to run this command as. It contains your principal as well as some things DFX associates with it like the wallet. + #[arg(long, global = true)] + identity: Option, + + #[command(flatten)] + network: NetworkOpt, +} + +#[derive(Args, Clone, Debug, Default)] +#[clap( +group(ArgGroup::new("network-select").multiple(false)), +)] +pub struct NetworkOpt { + /// Override the compute network to connect to. By default, the local network is used. + /// A valid URL (starting with `http:` or `https:`) can be used here, and a special + /// ephemeral network will be created specifically for this request. E.g. + /// "http://localhost:12345/" is a valid network name. + #[arg(long, global(true), group = "network-select")] + network: Option, + + /// Shorthand for --network=playground. + /// Borrows short-lived canisters on the real IC network instead of creating normal canisters. + #[clap(long, global(true), group = "network-select")] + playground: bool, + + /// Shorthand for --network=ic. + #[clap(long, global(true), group = "network-select")] + ic: bool, +} + +impl NetworkOpt { + pub fn to_network_name(&self) -> Option { + if self.playground { + Some("playground".to_string()) + } else if self.ic { + Some("ic".to_string()) + } else { + self.network.clone() + } + } } #[derive(Debug, Parser)] @@ -80,11 +124,24 @@ pub enum SubCommand { List(list::ListArgs), /// Check SNSes for warnings and errors. Health(HealthArgs), + /// Uploads a given Wasm to a (newly deployed) store canister and submits a proposal to upgrade + /// using that Wasm. + UpgradeSnsControlledCanister(UpgradeSnsControlledCanisterArgs), } impl CliArgs { - pub fn agent(&self) -> Result { - crate::utils::get_mainnet_agent() + pub async fn agent(&self) -> Result { + let network = match self.network.to_network_name() { + Some(network) => network, + None => { + eprintln!( + "No network specified. Defaulting to the local network. To connect to the mainnet IC instead, try passing `--network=ic`" + ); + "local".to_string() + } + }; + + crate::utils::get_agent(&network, self.identity.clone()).await } } @@ -509,6 +566,8 @@ impl NnsGovernanceCanister { proposer: &NeuronIdOrSubaccount, proposal: &Proposal, ) -> Result { + // TODO: Jira ticket NNS1-3555 + #[allow(non_local_definitions)] impl Request for ManageNeuron { type Response = ManageNeuronResponse; const METHOD_NAME: &'static str = "manage_neuron"; @@ -599,13 +658,13 @@ enum RunCommandError<'a> { }, } -impl<'a> Display for RunCommandError<'a> { +impl Display for RunCommandError<'_> { fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { write!(formatter, "{}", self.new_report()) } } -impl<'a> RunCommandError<'a> { +impl RunCommandError<'_> { fn new_report(&self) -> String { match self { RunCommandError::UnableToRunCommand { command, error } => { diff --git a/rs/sns/cli/src/main.rs b/rs/sns/cli/src/main.rs index 564cd638991..5a2ff962f9f 100644 --- a/rs/sns/cli/src/main.rs +++ b/rs/sns/cli/src/main.rs @@ -5,7 +5,8 @@ use clap::Parser; use ic_sns_cli::{ add_sns_wasm_for_tests, deploy_testflight, health, init_config_file, list, - neuron_id_to_candid_subaccount, prepare_canisters, propose, CliArgs, SubCommand, + neuron_id_to_candid_subaccount, prepare_canisters, propose, upgrade_sns_controlled_canister, + CliArgs, SubCommand, }; #[tokio::main] @@ -17,7 +18,7 @@ async fn main() -> Result<()> { } }; - let agent = args.agent()?; + let agent = args.agent().await?; match args.sub_command { SubCommand::DeployTestflight(args) => deploy_testflight(args), @@ -28,5 +29,8 @@ async fn main() -> Result<()> { SubCommand::NeuronIdToCandidSubaccount(args) => neuron_id_to_candid_subaccount::exec(args), SubCommand::List(args) => list::exec(args, &agent).await, SubCommand::Health(args) => health::exec(args, &agent).await, + SubCommand::UpgradeSnsControlledCanister(args) => { + upgrade_sns_controlled_canister::exec(args, &agent).await + } } } diff --git a/rs/sns/cli/src/neuron_id_to_candid_subaccount.rs b/rs/sns/cli/src/neuron_id_to_candid_subaccount.rs index 2b1be5cea17..ef8631aa290 100644 --- a/rs/sns/cli/src/neuron_id_to_candid_subaccount.rs +++ b/rs/sns/cli/src/neuron_id_to_candid_subaccount.rs @@ -1,9 +1,8 @@ -use std::str::FromStr; - -use anyhow::{anyhow, Result}; -use candid::IDLValue; +use anyhow::{bail, Result}; +use candid_utils::printing; use clap::Parser; -use ic_sns_governance::pb::v1::NeuronId; +use ic_sns_governance_api::pb::v1::NeuronId; +use std::str::FromStr; #[derive(Clone, Debug)] pub struct ParsedSnsNeuron(pub NeuronId); @@ -29,17 +28,18 @@ pub struct NeuronIdToCandidSubaccountArgs { } pub fn neuron_id_to_subaccount(args: NeuronIdToCandidSubaccountArgs) -> Result { - let subaccount = args - .neuron_id - .0 - .subaccount() - .map_err(|e| anyhow!(e.error_message))? - .to_vec(); + let subaccount = args.neuron_id.0.id.to_vec(); + + // Subaccounts are arbitrary 32-byte values. + if subaccount.len() != 32 { + bail!(format!( + "Invalid subaccount, expected 32 bytes, got {} bytes.", + subaccount.len() + )); + } // We'll convert it to a candid string. - let idl = IDLValue::try_from_candid_type(&subaccount) - .unwrap() - .to_string(); + let idl = printing::pretty(&subaccount).unwrap(); if args.escaped { Ok(idl.replace('\\', "\\\\").replace('\"', "\\\"")) @@ -92,7 +92,7 @@ fn test_neuron_id_to_subaccount_escaped() { #[test] fn test_neuron_id_to_subaccount_fail() { let neuron_id = ParsedSnsNeuron::from_str( - "9f5f9fda77a03e7177126d0be8c99e931a5381731d00da53ede363140e1be5", // two characters too short + "9f5f9fda77a03e7177126d0be8c99e931a5381731d00da53ede363140e1be5", // one character too short ) .unwrap() .0; @@ -101,9 +101,10 @@ fn test_neuron_id_to_subaccount_fail() { neuron_id: ParsedSnsNeuron(neuron_id), escaped: false, }; - let error = neuron_id_to_subaccount(args).unwrap_err(); + let error = neuron_id_to_subaccount(args).unwrap_err().to_string(); - assert!(error - .to_string() - .contains("could not convert slice to array")); + assert_eq!( + error, + "Invalid subaccount, expected 32 bytes, got 31 bytes." + ); } diff --git a/rs/sns/cli/src/propose.rs b/rs/sns/cli/src/propose.rs index 498ae8cf3a1..eda497df975 100644 --- a/rs/sns/cli/src/propose.rs +++ b/rs/sns/cli/src/propose.rs @@ -12,7 +12,6 @@ use ic_nns_constants::ROOT_CANISTER_ID; use ic_nns_governance_api::pb::v1::{ manage_neuron::NeuronIdOrSubaccount, proposal::Action, Proposal, }; -use ic_sns_governance::pb::v1::governance::Mode; use itertools::Itertools; use std::{ collections::HashSet, @@ -170,6 +169,17 @@ pub fn exec(args: ProposeArgs) -> Result<()> { Ok(()) } +fn functions_disallowed_in_pre_initialization_swap() -> Vec<&'static str> { + vec![ + "ManageNervousSystemParameters", + "TransferSnsTreasuryFunds", + "MintSnsTokens", + "UpgradeSnsControlledCanister", + "RegisterDappCanisters", + "DeregisterDappCanisters", + ] +} + fn confirmation_messages(proposal: &Proposal) -> Result> { let csns = match &proposal.action { Some(Action::CreateServiceNervousSystem(csns)) => csns, @@ -204,9 +214,9 @@ Then, if the swap completes successfully, the SNS will take sole control. If the r#"A CreateServiceNervousSystem proposal will be submitted. If adopted, this proposal will create an SNS that controls no canisters."#.to_string() }; - let disallowed_types = Mode::proposal_types_disallowed_in_pre_initialization_swap() + let disallowed_types = functions_disallowed_in_pre_initialization_swap() .into_iter() - .map(|t| format!(" - {}", t.name)) + .map(|t| format!(" - {}", t)) .join("\n"); let allowed_proposals = format!( r#"After the proposal is adopted, a swap is started. While the swap is running, the SNS will be in a restricted mode. @@ -470,9 +480,9 @@ fn save_proposal_id_to_file(path: &Path, proposal_id: &ProposalId) -> Result<(), #[cfg(test)] mod test { - use crate::init_config_file::friendly::SnsConfigurationFile; - use super::*; + use crate::init_config_file::friendly::SnsConfigurationFile; + use pretty_assertions::assert_eq; #[test] fn confirmation_messages_test() { @@ -511,12 +521,12 @@ Then, if the swap completes successfully, the SNS will take sole control. If the - 5zxxw-63ouu-faaaa-aaaap-4ai"#, r#"After the proposal is adopted, a swap is started. While the swap is running, the SNS will be in a restricted mode. Within this restricted mode, some proposal actions will not be allowed: - - Manage nervous system parameters - - Transfer SNS treasury funds - - Mint SNS tokens - - Upgrade SNS controlled canister - - Register dapp canisters - - Deregister Dapp Canisters + - ManageNervousSystemParameters + - TransferSnsTreasuryFunds + - MintSnsTokens + - UpgradeSnsControlledCanister + - RegisterDappCanisters + - DeregisterDappCanisters Once the swap is completed, the SNS will be in normal mode and these proposal actions will become available again."#, ]; assert_eq!(observed_messages, expected_messages); diff --git a/rs/sns/cli/src/upgrade_sns_controlled_canister.rs b/rs/sns/cli/src/upgrade_sns_controlled_canister.rs new file mode 100644 index 00000000000..624d66b39ca --- /dev/null +++ b/rs/sns/cli/src/upgrade_sns_controlled_canister.rs @@ -0,0 +1,607 @@ +use crate::neuron_id_to_candid_subaccount::ParsedSnsNeuron; +use anyhow::{bail, Context, Result}; +use candid::{CandidType, Encode, Nat, Principal}; +use candid_utils::{ + printing, + validation::{encode_upgrade_args, encode_upgrade_args_without_service}, +}; +use clap::Parser; +use cycles_minting_canister::{CanisterSettingsArgs, CreateCanister, SubnetSelection}; +use ic_agent::{export::reqwest::Url, Agent}; +use ic_base_types::{CanisterId, PrincipalId}; +use ic_management_canister_types::{BoundedVec, CanisterInstallMode}; +use ic_nervous_system_agent::{ + management_canister, nns, + sns::{self, root::SnsCanisters}, + CallCanisters, Request, +}; +use ic_nns_constants::CYCLES_LEDGER_CANISTER_ID; +use ic_sns_governance_api::pb::v1::{ + proposal::Action, ChunkedCanisterWasm, Proposal, UpgradeSnsControlledCanister, +}; +use ic_wasm::{metadata, utils::parse_wasm}; +use itertools::{Either, Itertools}; +use serde::Deserialize; +use serde_cbor::Value; +use std::{ + collections::BTreeSet, + fs::File, + io::{Read, Write}, + path::PathBuf, +}; + +const RAW_WASM_HEADER: [u8; 4] = [0, 0x61, 0x73, 0x6d]; +const GZIPPED_WASM_HEADER: [u8; 3] = [0x1f, 0x8b, 0x08]; + +// TODO: Compute more precisely the cycles amount needed for the store canister. +// The cycle fee for create request is 0.1T cycles. +pub const CANISTER_CREATE_FEE: u128 = 100_000_000_000_u128; + +pub const STORE_CANISTER_INITIAL_CYCLES_BALANCE: u128 = 500_000_000_000_u128; // 0.5T + +/// The arguments used to configure the upgrade_sns_controlled_canister command. +#[derive(Debug, Parser)] +pub struct UpgradeSnsControlledCanisterArgs { + /// SNS neuron ID (subaccount) to be used for proposing the upgrade. + /// + /// If not specified, the proposal payload will be printed at the end. + #[clap(long)] + sns_neuron_id: Option, + + /// ID of the target canister to be upgraded. + #[clap(long)] + target_canister_id: CanisterId, + + /// Path to a ICP WASM module file (may be gzipped). + #[clap(long)] + wasm_path: PathBuf, + + /// Upgrade argument for the Candid service. + #[clap(long)] + candid_arg: Option, + + /// URL (starting with https://) of a web page with a public announcement of this upgrade. + #[clap(long)] + proposal_url: Url, + + /// Human-readable text explaining why this upgrade is being done (may be markdown). + #[clap(long)] + summary: String, +} + +pub struct Wasm { + path: PathBuf, + bytes: Vec, + module_hash: [u8; 32], +} + +impl Wasm { + pub fn bytes(&self) -> &[u8] { + &self.bytes + } + + pub fn module_hash(&self) -> [u8; 32] { + self.module_hash + } + + pub fn path(&self) -> String { + self.path.display().to_string() + } +} + +impl TryFrom for Wasm { + type Error = String; + + fn try_from(path: PathBuf) -> Result { + let mut file = match File::open(&path) { + Err(err) => { + return Err(format!( + "Cannot open Wasm file under {}: {}", + path.display(), + err, + )); + } + Ok(file) => file, + }; + + // Create a buffer to store the file's content + let mut bytes = Vec::new(); + + // Read the file's content into the buffer + if let Err(err) = file.read_to_end(&mut bytes) { + return Err(format!("Cannot read Wasm file {}: {}", path.display(), err,)); + } + + // Smoke test: Is this a ICP Wasm? + if !bytes.starts_with(&RAW_WASM_HEADER) && !bytes.starts_with(&GZIPPED_WASM_HEADER) { + return Err("The file does not look like a valid ICP Wasm module.".to_string()); + } + + let module_hash = ic_crypto_sha2::Sha256::hash(&bytes); + + Ok(Self { + path, + bytes, + module_hash, + }) + } +} + +/// Attempts to validate `args` against the Candid service defined in `wasm`. +/// +/// If `args` is Some, returns the byte encoding of `args` in the Ok result. +/// +/// This function prints warnings into STDERR. +pub fn validate_candid_arg_for_wasm(wasm: &Wasm, args: Option) -> Result>> { + let wasm_module = parse_wasm(wasm.bytes(), false)?; + + print!("Checking that the Wasm metadata contains Candid service definition ... "); + std::io::stdout().flush().unwrap(); + + let candid_service = metadata::list_metadata(&wasm_module) + .into_iter() + .find_map(|section| { + let mut section = section.split(' ').collect::>(); + if section.is_empty() { + // This cannot practically happen, as it would imply that all characters of + // the section are whitespaces. + return None; + } + + // Consume this section's visibility specification, e.g. "icp:public" or "icp:private". + let _visibility = section.remove(0).to_string(); + + // The conjunction of the remaining parts are the section's name. + let name = section.join(" "); + + if name != "candid:service" { + return None; + } + + // Read the actual contents of this section. + metadata::get_metadata(&wasm_module, &name).map(|contents| contents.to_vec()) + }) + .map(|bytes: Vec| std::str::from_utf8(&bytes).unwrap().to_string()); + + let canister_arg = if let Some(candid_service) = candid_service { + println!("✔️"); + + print!("Validating the upgrade arg against the Candid service definition ... "); + std::io::stdout().flush().unwrap(); + let candid_arg_bytes = encode_upgrade_args(candid_service, args).unwrap(); + println!("✔️"); + + candid_arg_bytes + } else { + eprintln!( + "\n\ + ⚠️ Skipping upgrade argument validation: Wasm file has no Candid definition! \n\ + ⚠️ Please consider adding it as follows:\n\ + cargo install ic-wasm\n\ + ic-wasm -o augmented-{} {} metadata -v public candid:service -f service.did", + wasm.path(), + wasm.path(), + ); + + // Proceed with whatever argument the user has specified without validation. + args.map(|args| encode_upgrade_args_without_service(args).unwrap()) + }; + + std::io::stdout().flush().unwrap(); + std::io::stderr().flush().unwrap(); + + Ok(canister_arg) +} + +/// Checks if `canister_id` is an ID of a canister that exists and has some Wasm code installed. +/// +/// In the Ok result, returns a tuple with the following components: +/// 1. Set of controllers. +/// 2. Module hash of the installed code. +/// +/// See https://internetcomputer.org/docs/current/references/ic-interface-spec#state-tree-canister-information +/// +/// This function is analogous to `dfx canister info`. +pub async fn fetch_canister_info( + agent: &Agent, + canister_id: CanisterId, +) -> Result<(BTreeSet, Vec)> { + let module_hash = agent + .read_state_canister_info(canister_id.get().0, "module_hash") + .await + .context("Cannot read target canister's module hash.")?; + + let controllers_blob = agent + .read_state_canister_info(canister_id.get().0, "controllers") + .await + .context("Cannot read canister controllers.")?; + + let cbor: Value = serde_cbor::from_slice(&controllers_blob) + .expect("Invalid cbor data for controller controllers."); + + let Value::Array(controllers) = cbor else { + bail!("Expected controllers to be an array, but got {cbor:?}"); + }; + + let (controllers, errors): (BTreeSet<_>, Vec<_>) = + controllers.into_iter().partition_map(|value| { + let Value::Bytes(bytes) = value else { + let err = + format!("Expected canister controller to be of type bytes, got {value:?}",); + return Either::Right(err); + }; + match Principal::try_from(&bytes) { + Err(err) => { + let err = format!("Cannot interpret canister controller principal: {err}"); + Either::Right(err) + } + Ok(principal) => Either::Left(PrincipalId(principal)), + } + }); + + if !errors.is_empty() { + let err = format!( + "Problems with canister controllers:\n - {}", + errors.join("\n - ") + ); + bail!(err); + } + + Ok((controllers, module_hash)) +} + +/// Attempts to create an empty canister on the same subnet as `next_to`. +/// +/// Returns the ID of the newly created canister in the Ok result. +pub async fn create_canister_next_to( + agent: &Agent, + next_to: CanisterId, + controllers: Vec, + cycles_amount: u128, + name: &str, +) -> Result { + // This is expected to be `None` if we're running against a local replica. + let subnet_selection = nns::registry::get_subnet_for_canister(agent, next_to) + .await + .map(|subnet| SubnetSelection::Subnet { subnet }) + .ok(); + + let canister_id = cycles_ledger_create_canister( + agent, + cycles_amount, + subnet_selection, + Some(CanisterSettingsArgs { + controllers: Some(BoundedVec::new(controllers)), + ..Default::default() + }), + ) + .await + .map_err(|err| { + if let CreateCanisterError::InsufficientFunds { balance } = err { + let err = format!( + "Requested creating the {} canister with {} cycles, but the caller identity has \ + only {} cycles on the cycles ledger. Please buy more cycles using \ + `dfx cycles convert --amount AMOUNT --network NETWORK` and try again.", + name, cycles_amount, balance, + ); + anyhow::anyhow!(err) + } else { + anyhow::anyhow!(format!("{:?}", err)) + } + })? + .canister_id; + + CanisterId::try_from_principal_id(canister_id).map_err(|err| anyhow::anyhow!(err)) +} + +pub async fn exec(args: UpgradeSnsControlledCanisterArgs, agent: &Agent) -> Result<()> { + // Prepare. + + let UpgradeSnsControlledCanisterArgs { + sns_neuron_id, + target_canister_id, + wasm_path, + candid_arg, + proposal_url, + summary, + } = args; + + let caller_principal = PrincipalId(agent.get_principal().map_err(|err| anyhow::anyhow!(err))?); + + print!("Getting target canister info ... "); + std::io::stdout().flush().unwrap(); + let (target_controllers, current_module_hash) = + fetch_canister_info(agent, target_canister_id).await?; + println!("✔️"); + + print!("Finding the SNS controlling this target canister ... "); + std::io::stdout().flush().unwrap(); + let sns = { + let (user_controllers, canister_controllers): (Vec<_>, Vec<_>) = + target_controllers.into_iter().partition_map(|controller| { + if controller.is_self_authenticating() { + Either::Left(controller) + } else { + Either::Right(controller) + } + }); + + if user_controllers.contains(&caller_principal) { + println!( + "\n\ + ⚠️ the target is controlled by the caller, which means it is not decentralized.\n\ + ⚠️ Proceed upgrading it directly." + ); + + None // no SNS + } else { + assert!( + !canister_controllers.is_empty(), + "The target canister is not controlled by an SNS." + ); + + assert_eq!( + canister_controllers.len(), + 1, + "The target canister has more than one canister controller!" + ); + + let canister_id = *canister_controllers.first().unwrap(); + + // TODO: Check that this is indeed an SNS canister controlling the target. + // + // This is expected to be `None` if we're running against a local replica. + // let root_subnet = nns::registry::get_subnet_for_canister(agent, canister_id) + // .await + // .ok(); + // let sns_subnets = sns_w.list_sns_subnets().await.unwrap(); + // assert!( + // sns_subnets.contains(&root_subnet), + // "Target canister is not controlled by an SNS!", + // ); + + let root_canister = sns::root::RootCanister { canister_id }; + + let SnsCanisters { sns, dapps } = root_canister.list_sns_canisters(agent).await?; + + // Check that the target is indeed controlled by this SNS. + if !BTreeSet::from_iter(&dapps[..]).contains(&target_canister_id.get()) { + bail!( + "{} is not one of the canisters controlled by the SNS with Root canister {}", + target_canister_id.get(), + root_canister.canister_id, + ); + } + + Some(sns) + } + }; + println!("✔️"); + + print!("Checking that we have a viable Wasm for this upgrade ... "); + std::io::stdout().flush().unwrap(); + let wasm = Wasm::try_from(wasm_path).unwrap(); + assert_ne!( + wasm.module_hash().to_vec(), + current_module_hash, + "Target canister is already running Wasm module with SHA256 {}. Nothing to do.", + format_full_hash(&wasm.module_hash()), + ); + println!("✔️"); + + // Save `candid_arg` for reference, in case we need it for error reporting later on. + let upgrade_args = candid_arg.clone(); + + let canister_upgrade_arg = validate_candid_arg_for_wasm(&wasm, upgrade_args).unwrap(); + + print!("Creating a store canister on the same subnet as the target ... "); + std::io::stdout().flush().unwrap(); + let store_canister_controllers = if let Some(sns) = &sns { + vec![ + caller_principal, + sns.root.canister_id, + sns.governance.canister_id, + ] + } else { + vec![caller_principal] + }; + let cycles_amount = STORE_CANISTER_INITIAL_CYCLES_BALANCE; + let store_canister_id = create_canister_next_to( + agent, + target_canister_id, + store_canister_controllers, + cycles_amount, + "store", + ) + .await + .unwrap(); + println!("✔️"); + + print!("Uploading the chunks into the store canister ... "); + std::io::stdout().flush().unwrap(); + let chunk_hashes_list = management_canister::upload_wasm( + agent, + store_canister_id, + wasm.bytes().to_vec(), + Some( + |chunk_index: usize, num_chunks: usize, chunk_hash: &Vec| { + print!( + "\n Uploaded chunk {chunk_index}/{num_chunks}: {}", + format_full_hash(chunk_hash) + ); + std::io::stdout().flush().unwrap(); + }, + ), + ) + .await? + .into_iter() + .map(|chunk_hash| chunk_hash.hash) + .collect(); + println!("✔️"); + + let Some(sns) = &sns else { + unimplemented!( + "Direct canister upgrades are not implemented yet. Please use DFX:\n{}", + suggested_install_command(&wasm.path(), &candid_arg) + ); + }; + + print!("Forming SNS proposal to upgrade target canister ... "); + std::io::stdout().flush().unwrap(); + let sns_governance = sns::governance::GovernanceCanister { + canister_id: sns.governance.canister_id, + }; + let proposal = Proposal { + title: format!( + "Upgrade SNS-controlled canister {}", + target_canister_id.get() + ), + summary, + url: proposal_url.to_string(), + action: Some(Action::UpgradeSnsControlledCanister( + UpgradeSnsControlledCanister { + canister_id: Some(target_canister_id.get()), + new_canister_wasm: vec![], + canister_upgrade_arg, + mode: Some(CanisterInstallMode::Upgrade as i32), + chunked_canister_wasm: Some(ChunkedCanisterWasm { + wasm_module_hash: wasm.module_hash().to_vec(), + store_canister_id: Some(store_canister_id.get()), + chunk_hashes_list, + }), + }, + )), + }; + + if let Some(sns_neuron_id) = sns_neuron_id { + let proposal_id = sns_governance + .submit_proposal(agent, sns_neuron_id.0, proposal) + .await?; + println!("✔️"); + + let proposal_url = format!( + "https://nns.ic0.app/proposal/?u={}&proposal={}", + sns.root.canister_id, proposal_id.id, + ); + println!( + "Successfully proposed to upgrade SNS-controlled canister, see details here:\n\ + {proposal_url}", + ); + } else { + println!("✔️"); + let proposal_str = printing::pretty(&proposal).unwrap(); + println!("{proposal_str}"); + } + + Ok(()) +} + +pub type BlockIndex = Nat; + +#[derive(CandidType, Deserialize, Debug, Clone)] +pub enum CreateCanisterError { + InsufficientFunds { + balance: Nat, + }, + TooOld, + CreatedInFuture { + ledger_time: u64, + }, + TemporarilyUnavailable, + Duplicate { + duplicate_of: Nat, + // If the original transaction created a canister then this field will contain the canister id. + canister_id: Option, + }, + FailedToCreate { + fee_block: Option, + refund_block: Option, + error: String, + }, + GenericError { + message: String, + error_code: Nat, + }, +} + +// ```candid +// type CreateCanisterArgs = record { +// from_subaccount : opt vec nat8; +// created_at_time : opt nat64; +// amount : nat; +// creation_args : opt CmcCreateCanisterArgs; +// }; +// ``` +#[derive(Clone, Eq, PartialEq, Debug, CandidType, Deserialize)] +pub struct CreateCanisterArgs { + from_subaccount: Option>, + created_at_time: Option, + amount: Nat, + creation_args: Option, +} + +// ```candid +// type CreateCanisterSuccess = record { +// block_id : BlockIndex; +// canister_id : principal; +// }; +// ``` +#[derive(Clone, Eq, PartialEq, Debug, CandidType, Deserialize)] +pub struct CreateCanisterSuccess { + block_id: BlockIndex, + canister_id: PrincipalId, +} + +impl Request for CreateCanisterArgs { + fn method(&self) -> &'static str { + "create_canister" + } + + fn update(&self) -> bool { + true + } + + fn payload(&self) -> std::result::Result, candid::Error> { + Encode!(self) + } + + type Response = Result; +} + +pub async fn cycles_ledger_create_canister( + agent: &C, + cycles_amount: u128, + subnet_selection: Option, + settings: Option, +) -> Result { + let request = CreateCanisterArgs { + from_subaccount: None, + created_at_time: None, + amount: Nat::from(cycles_amount), + creation_args: Some(CreateCanister { + subnet_selection, + settings, + ..Default::default() + }), + }; + agent + .call(CYCLES_LEDGER_CANISTER_ID, request) + .await + .expect("Cannot create canister") +} + +fn format_full_hash(hash: &[u8]) -> String { + hash.iter() + .map(|b| format!("{:02x}", b)) + .collect::>() + .join("") +} + +fn suggested_install_command(wasm_path_str: &str, candid_arg: &Option) -> String { + let arg_suggestion = if let Some(candid_arg) = candid_arg { + format!(" --argument '{}'", candid_arg) + } else { + "".to_string() + }; + format!("dfx canister install --mode auto --wasm {wasm_path_str} CANISTER_NAME{arg_suggestion}") +} diff --git a/rs/sns/cli/src/utils.rs b/rs/sns/cli/src/utils.rs index cc2782b123a..5e0f58f2ec1 100644 --- a/rs/sns/cli/src/utils.rs +++ b/rs/sns/cli/src/utils.rs @@ -1,21 +1,43 @@ -use anyhow::{anyhow, Result}; +use anyhow::Context; +use anyhow::Result; +use dfx_core::interface::{builder::IdentityPicker, dfx::DfxInterface}; use futures::{stream, StreamExt}; use ic_agent::Agent; use ic_nervous_system_agent::sns::Sns; use itertools::Itertools; use serde::{Deserialize, Serialize}; -fn get_agent(ic_url: &str) -> Result { - Agent::builder() - .with_url(ic_url) - .with_verify_query_signatures(false) - .build() - .map_err(|e| anyhow!(e)) +/// Gets an agent for a given network and identity. This is similar to the code DFX uses internally to get an agent. +/// If no identity is provided, it will use the identity currently selected in the DFX CLI. +pub async fn get_agent(network_name: &str, identity: Option) -> Result { + let interface = dfx_interface(network_name, identity) + .await + .context("Failed to get dfx interface")?; + Ok(interface.agent().clone()) } -pub fn get_mainnet_agent() -> Result { - let ic_url = "https://ic0.app/"; - get_agent(ic_url) +/// Gets a dfx interface for a given network and identity. This is similar to the code DFX uses internally to build the interface. +/// So this function allows the DFX SNS Extension to use the same interface as DFX itself. +/// If no identity is provided, it will use the identity currently selected in the DFX CLI. +pub async fn dfx_interface(network_name: &str, identity: Option) -> Result { + let interface_builder = { + let identity = identity + .clone() + .map(IdentityPicker::Named) + .unwrap_or(IdentityPicker::Selected); + DfxInterface::builder() + .with_identity(identity) + .with_network_named(network_name) + }; + let interface = interface_builder.build().await.context(format!( + "Failed to build dfx interface with network `{network_name}` and identity `{identity:?}`" + ))?; + if !interface.network_descriptor().is_ic { + interface.agent().fetch_root_key().await.context(format!( + "Failed to fetch root key from network `{network_name}`." + ))?; + } + Ok(interface) } #[derive(Debug, Serialize, Deserialize)] diff --git a/rs/sns/governance/BUILD.bazel b/rs/sns/governance/BUILD.bazel index 7cdf3aa0716..0553c44e059 100644 --- a/rs/sns/governance/BUILD.bazel +++ b/rs/sns/governance/BUILD.bazel @@ -62,6 +62,7 @@ DEPENDENCIES = [ "@crate_index//:rust_decimal", "@crate_index//:serde", "@crate_index//:serde_bytes", + "@crate_index//:serde_json", "@crate_index//:strum", ] @@ -180,6 +181,15 @@ rust_test( deps = [":governance"] + DEPENDENCIES + DEV_DEPENDENCIES + [":build_script"], ) +rust_test( + name = "governance_test--test-feature", + srcs = glob(["src/**/*.rs"]), + aliases = ALIASES, + crate_features = ["test"], + proc_macro_deps = MACRO_DEPENDENCIES + MACRO_DEV_DEPENDENCIES, + deps = [":governance"] + DEPENDENCIES + DEV_DEPENDENCIES + [":build_script"], +) + rust_test( name = "canister_unit_test", srcs = glob(["canister/**/*.rs"]), diff --git a/rs/sns/governance/CHANGELOG.md b/rs/sns/governance/CHANGELOG.md new file mode 100644 index 00000000000..51b7b4e0ad0 --- /dev/null +++ b/rs/sns/governance/CHANGELOG.md @@ -0,0 +1,39 @@ +# Changelog + +Proposals before 2025 are NOT listed in here, because this process was +introduced later. (We could back fill those later though.) + +The process that populates this file is described in +`rs/nervous_system/changelog_process.md`. In general though, the entries you see +here were moved from the adjacent `unreleased_changelog.md` file. + + +INSERT NEW RELEASES HERE + +* https://nns.ic0.app/proposal/?proposal=134906 + + Enable upgrading SNS-controlled canisters using chunked WASMs. This is implemented as an extension +of the existing `UpgradeSnsControllerCanister` proposal type with new field `chunked_canister_wasm`. +This field can be used for specifying an upgrade of an SNS-controlled *target* canister using +a potentially large WASM module (over 2 MiB) uploaded to some *store* canister, which: + * must be installed on the same subnet as target. + * must have SNS Root as one of its controllers. + * must have enough cycles for performing the upgrade. + + +# 2025-01-20: Proposal 134906 + +http://dashboard.internetcomputer.org/proposals/134906 + +## Added + +Enable upgrading SNS-controlled canisters using chunked WASMs. This is implemented as an extension +of the existing `UpgradeSnsControllerCanister` proposal type with new field `chunked_canister_wasm`. +This field can be used for specifying an upgrade of an SNS-controlled *target* canister using +a potentially large WASM module (over 2 MiB) uploaded to some *store* canister, which: +* must be installed on the same subnet as target. +* must have SNS Root as one of its controllers. +* must have enough cycles for performing the upgrade. + + +END diff --git a/rs/sns/governance/Cargo.toml b/rs/sns/governance/Cargo.toml index 8c76978b518..6d00f0868f6 100644 --- a/rs/sns/governance/Cargo.toml +++ b/rs/sns/governance/Cargo.toml @@ -79,6 +79,7 @@ rust_decimal = "1.36.0" rust_decimal_macros = "1.36.0" serde = { workspace = true } serde_bytes = { workspace = true } +serde_json = { workspace = true } strum = { workspace = true } strum_macros = { workspace = true } canbench-rs = { version = "0.1.7", optional = true } diff --git a/rs/sns/governance/api/src/ic_sns_governance.pb.v1.rs b/rs/sns/governance/api/src/ic_sns_governance.pb.v1.rs index da87417e25f..243ef08e4b9 100644 --- a/rs/sns/governance/api/src/ic_sns_governance.pb.v1.rs +++ b/rs/sns/governance/api/src/ic_sns_governance.pb.v1.rs @@ -1,90 +1,66 @@ -// This file is @generated by prost-build. +use std::collections::BTreeMap; + /// A principal with a particular set of permissions over a neuron. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct NeuronPermission { /// The principal that has the permissions. - #[prost(message, optional, tag = "1")] - pub principal: ::core::option::Option<::ic_base_types::PrincipalId>, + pub principal: Option<::ic_base_types::PrincipalId>, /// The list of permissions that this principal has. - #[prost(enumeration = "NeuronPermissionType", repeated, tag = "2")] - pub permission_type: ::prost::alloc::vec::Vec, + pub permission_type: Vec, } /// The id of a specific neuron, which equals the neuron's subaccount on the ledger canister /// (the account that holds the neuron's staked tokens). #[derive( + Default, candid::CandidType, candid::Deserialize, - comparable::Comparable, + Debug, Eq, std::hash::Hash, Clone, PartialEq, - ::prost::Message, + PartialOrd, + Ord, )] pub struct NeuronId { - #[prost(bytes = "vec", tag = "1")] #[serde(with = "serde_bytes")] - pub id: ::prost::alloc::vec::Vec, + pub id: Vec, } /// A sequence of NeuronIds, which is used to get prost to generate a type isomorphic to Option>. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct NeuronIds { - #[prost(message, repeated, tag = "1")] - pub neuron_ids: ::prost::alloc::vec::Vec, + pub neuron_ids: Vec, } /// The id of a specific proposal. -#[derive(candid::CandidType, candid::Deserialize, comparable::Comparable, serde::Serialize)] -#[self_describing] -#[derive(Clone, Copy, PartialEq, ::prost::Message)] -pub struct ProposalId { - #[prost(uint64, tag = "1")] - pub id: u64, -} #[derive( + Default, candid::CandidType, candid::Deserialize, - comparable::Comparable, + Debug, + serde::Serialize, Clone, + Copy, PartialEq, - ::prost::Message, )] +pub struct ProposalId { + pub id: u64, +} +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct DisburseMaturityInProgress { /// This field is the quantity of maturity in e8s that has been decremented from a Neuron to /// be modulated and disbursed as SNS tokens. - #[prost(uint64, tag = "1")] pub amount_e8s: u64, - #[prost(uint64, tag = "2")] pub timestamp_of_disbursement_seconds: u64, - #[prost(message, optional, tag = "3")] - pub account_to_disburse_to: ::core::option::Option, - #[prost(uint64, optional, tag = "4")] - pub finalize_disbursement_timestamp_seconds: ::core::option::Option, + pub account_to_disburse_to: Option, + pub finalize_disbursement_timestamp_seconds: Option, } /// A neuron in the governance system. -#[derive(candid::CandidType, candid::Deserialize, comparable::Comparable)] -#[compare_default] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct Neuron { /// The unique id of this neuron. - #[prost(message, optional, tag = "1")] - pub id: ::core::option::Option, + pub id: Option, /// The principal or list of principals with a particular set of permissions over a neuron. - #[prost(message, repeated, tag = "2")] - pub permissions: ::prost::alloc::vec::Vec, + pub permissions: Vec, /// The cached record of the neuron's staked governance tokens, measured in /// fractions of 10E-8 of a governance token. /// @@ -92,7 +68,6 @@ pub struct Neuron { /// that can be set by each SNS. Neurons that are created by claiming a neuron, spawning a neuron, /// or splitting a neuron must have at least that stake (in the case of splitting both the parent neuron /// and the new neuron must have at least that stake). - #[prost(uint64, tag = "3")] pub cached_neuron_stake_e8s: u64, /// TODO NNS1-1052 - Update if this ticket is done and fees are burned / minted instead of tracked in this attribute. /// @@ -100,10 +75,8 @@ pub struct Neuron { /// due to making proposals that were subsequently rejected. /// Must be smaller than 'cached_neuron_stake_e8s'. When a neuron is /// disbursed, these governance tokens will be burned. - #[prost(uint64, tag = "4")] pub neuron_fees_e8s: u64, /// The timestamp, in seconds from the Unix epoch, when the neuron was created. - #[prost(uint64, tag = "5")] pub created_timestamp_seconds: u64, /// The timestamp, in seconds from the Unix epoch, when this neuron has entered /// the non-dissolving state. This is either the creation time or the last time at @@ -112,30 +85,25 @@ pub struct Neuron { /// This value is meaningless when the neuron is dissolving, since a /// dissolving neurons always has age zero. The canonical value of /// this field for a dissolving neuron is `u64::MAX`. - #[prost(uint64, tag = "6")] pub aging_since_timestamp_seconds: u64, /// The neuron's followees, specified as a map of proposal functions IDs to followees neuron IDs. /// The map's keys are represented by integers as Protobuf does not support enum keys in maps. - #[prost(btree_map = "uint64, message", tag = "11")] - pub followees: ::prost::alloc::collections::BTreeMap, + pub followees: BTreeMap, /// The accumulated unstaked maturity of the neuron, measured in "e8s equivalent", i.e., in equivalent of /// 10E-8 of a governance token. /// /// The unit is "equivalent" to insist that, while this quantity is on the /// same scale as the governance token, maturity is not directly convertible to /// governance tokens: conversion requires a minting event and the conversion rate is variable. - #[prost(uint64, tag = "12")] pub maturity_e8s_equivalent: u64, /// A percentage multiplier to be applied when calculating the voting power of a neuron. /// The multiplier's unit is a integer percentage in the range of 0 to 100. The /// voting_power_percentage_multiplier can only be less than 100 for a developer neuron /// that is created at SNS initialization. - #[prost(uint64, tag = "13")] pub voting_power_percentage_multiplier: u64, /// The ID of the NNS neuron whose Community Fund participation resulted in the /// creation of this SNS neuron. - #[prost(uint64, optional, tag = "14")] - pub source_nns_neuron_id: ::core::option::Option, + pub source_nns_neuron_id: Option, /// The accumulated staked maturity of the neuron, in "e8s equivalent" (see /// "maturity_e8s_equivalent"). Staked maturity becomes regular maturity once /// the neuron is dissolved. @@ -145,12 +113,10 @@ pub struct Neuron { /// and rewards. Once the neuron is dissolved, this maturity will be "moved" /// to 'maturity_e8s_equivalent' and will be able to be spawned (with maturity /// modulation). - #[prost(uint64, optional, tag = "15")] - pub staked_maturity_e8s_equivalent: ::core::option::Option, + pub staked_maturity_e8s_equivalent: Option, /// If set and true the maturity rewarded to this neuron for voting will be /// automatically staked and will contribute to the neuron's voting power. - #[prost(bool, optional, tag = "16")] - pub auto_stake_maturity: ::core::option::Option, + pub auto_stake_maturity: Option, /// The duration that this neuron is vesting. /// /// A neuron that is vesting is non-dissolving and cannot start dissolving until the vesting duration has elapsed. @@ -159,14 +125,12 @@ pub struct Neuron { /// for a particular SNS instance might be 1 year, but the devs of the project may set their vesting duration to 3 /// years and dissolve delay to 1 year in order to prove that they are making a minimum 4 year commitment to the /// project. - #[prost(uint64, optional, tag = "17")] - pub vesting_period_seconds: ::core::option::Option, + pub vesting_period_seconds: Option, /// Disburse maturity operations that are currently underway. /// The entries are sorted by `timestamp_of_disbursement_seconds`-values, /// with the oldest entries first, i.e. it holds for all i that: /// entry\[i\].timestamp_of_disbursement_seconds <= entry\[i+1\].timestamp_of_disbursement_seconds - #[prost(message, repeated, tag = "18")] - pub disburse_maturity_in_progress: ::prost::alloc::vec::Vec, + pub disburse_maturity_in_progress: Vec, /// The neuron's dissolve state, specifying whether the neuron is dissolving, /// non-dissolving, or dissolved. /// @@ -182,23 +146,14 @@ pub struct Neuron { /// `Dissolved`. All other states represent the dissolved /// state. That is, (a) `when_dissolved_timestamp_seconds` is set and in the past, /// (b) `when_dissolved_timestamp_seconds` is set to zero, (c) neither value is set. - #[prost(oneof = "neuron::DissolveState", tags = "7, 8")] - pub dissolve_state: ::core::option::Option, + pub dissolve_state: Option, } /// Nested message and enum types in `Neuron`. pub mod neuron { /// A list of a neuron's followees for a specific function. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct Followees { - #[prost(message, repeated, tag = "1")] - pub followees: ::prost::alloc::vec::Vec, + pub followees: Vec, } /// The neuron's dissolve state, specifying whether the neuron is dissolving, /// non-dissolving, or dissolved. @@ -215,15 +170,7 @@ pub mod neuron { /// `Dissolved`. All other states represent the dissolved /// state. That is, (a) `when_dissolved_timestamp_seconds` is set and in the past, /// (b) `when_dissolved_timestamp_seconds` is set to zero, (c) neither value is set. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Oneof, - )] + #[derive(candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub enum DissolveState { /// When the dissolve timer is running, this stores the timestamp, /// in seconds from the Unix epoch, at which the neuron is dissolved. @@ -232,7 +179,6 @@ pub mod neuron { /// may pause dissolving, in which case `dissolve_delay_seconds` /// will get assigned to: `when_dissolved_timestamp_seconds - /// `. - #[prost(uint64, tag = "7")] WhenDissolvedTimestampSeconds(u64), /// When the dissolve timer is stopped, this stores how much time, /// in seconds, the dissolve timer will be started with if the neuron is set back to 'Dissolving'. @@ -241,7 +187,6 @@ pub mod neuron { /// dissolving, in which case `when_dissolved_timestamp_seconds` /// will get assigned to: ` + /// dissolve_delay_seconds`. - #[prost(uint64, tag = "8")] DissolveDelaySeconds(u64), } } @@ -257,77 +202,46 @@ pub mod neuron { /// /// Note that the target, validator and rendering methods can all coexist in /// the same canister or be on different canisters. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct NervousSystemFunction { /// The unique id of this function. /// /// Ids 0-999 are reserved for native governance proposals and can't /// be used by generic NervousSystemFunction's. - #[prost(uint64, tag = "1")] pub id: u64, /// A short (<256 chars) description of the NervousSystemFunction. - #[prost(string, tag = "2")] - pub name: ::prost::alloc::string::String, + pub name: String, /// An optional description of what the NervousSystemFunction does. - #[prost(string, optional, tag = "3")] - pub description: ::core::option::Option<::prost::alloc::string::String>, - #[prost(oneof = "nervous_system_function::FunctionType", tags = "4, 5")] - pub function_type: ::core::option::Option, + pub description: Option, + pub function_type: Option, } /// Nested message and enum types in `NervousSystemFunction`. pub mod nervous_system_function { - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct GenericNervousSystemFunction { /// The id of the target canister that will be called to execute the proposal. - #[prost(message, optional, tag = "2")] - pub target_canister_id: ::core::option::Option<::ic_base_types::PrincipalId>, + pub target_canister_id: Option<::ic_base_types::PrincipalId>, /// The name of the method that will be called to execute the proposal. /// The signature of the method must be equivalent to the following: /// (proposal_data: ProposalData) -> Result<(), String>. - #[prost(string, optional, tag = "3")] - pub target_method_name: ::core::option::Option<::prost::alloc::string::String>, + pub target_method_name: Option, /// The id of the canister that will be called to validate the proposal before /// it is put up for a vote. - #[prost(message, optional, tag = "4")] - pub validator_canister_id: ::core::option::Option<::ic_base_types::PrincipalId>, + pub validator_canister_id: Option<::ic_base_types::PrincipalId>, /// The name of the method that will be called to validate the proposal /// before it is put up for a vote. /// The signature of the method must be equivalent to the following: /// (proposal_data: ProposalData) -> Result - #[prost(string, optional, tag = "5")] - pub validator_method_name: ::core::option::Option<::prost::alloc::string::String>, + pub validator_method_name: Option, } - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Oneof, - )] + #[derive(candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub enum FunctionType { /// Whether this is a native function (i.e. a Action::Motion or /// Action::UpgradeSnsControlledCanister) or one of user-defined /// NervousSystemFunctions. - #[prost(message, tag = "4")] NativeNervousSystemFunction(super::Empty), /// Whether this is a GenericNervousSystemFunction which can call /// any canister. - #[prost(message, tag = "5")] GenericNervousSystemFunction(GenericNervousSystemFunction), } } @@ -335,39 +249,27 @@ pub mod nervous_system_function { /// that is not build into the standard SNS and calls a canister outside /// the SNS for execution. /// The canister and method to call are derived from the `function_id`. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct ExecuteGenericNervousSystemFunction { /// This enum value determines what canister to call and what /// function to call on that canister. /// /// 'function_id` must be in the range `\[1000--u64:MAX\]` as this /// can't be used to execute native functions. - #[prost(uint64, tag = "1")] pub function_id: u64, /// The payload of the nervous system function's payload. - #[prost(bytes = "vec", tag = "2")] #[serde(with = "serde_bytes")] - pub payload: ::prost::alloc::vec::Vec, + pub payload: Vec, } /// A proposal function that should guide the future strategy of the SNS's /// ecosystem but does not have immediate effect in the sense that a method is executed. -#[derive(candid::CandidType, candid::Deserialize, comparable::Comparable)] -#[self_describing] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct Motion { /// The text of the motion, which can at most be 100kib. - #[prost(string, tag = "1")] - pub motion_text: ::prost::alloc::string::String, + pub motion_text: String, } -/// A proposal function that upgrades a canister that is controlled by the -/// SNS governance canister. + +/// Represents a WASM split into smaller chunks, each of which can safely be sent around the ICP. #[derive( candid::CandidType, candid::Deserialize, @@ -376,51 +278,50 @@ pub struct Motion { PartialEq, ::prost::Message, )] +pub struct ChunkedCanisterWasm { + /// Obligatory check sum of the overall WASM to be reassembled from chunks. + #[prost(bytes = "vec", tag = "1")] + pub wasm_module_hash: ::prost::alloc::vec::Vec, + /// Obligatory; indicates which canister stores the WASM chunks. + #[prost(message, optional, tag = "2")] + pub store_canister_id: ::core::option::Option<::ic_base_types::PrincipalId>, + /// Specifies a list of hash values for the chunks that comprise this WASM. Must contain at least + /// one chunk. + #[prost(bytes = "vec", repeated, tag = "3")] + pub chunk_hashes_list: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, +} + +/// A proposal function that upgrades a canister that is controlled by the +/// SNS governance canister. +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct UpgradeSnsControlledCanister { /// The id of the canister that is upgraded. - #[prost(message, optional, tag = "1")] - pub canister_id: ::core::option::Option<::ic_base_types::PrincipalId>, + pub canister_id: Option<::ic_base_types::PrincipalId>, /// The new wasm module that the canister is upgraded to. - #[prost(bytes = "vec", tag = "2")] #[serde(with = "serde_bytes")] - pub new_canister_wasm: ::prost::alloc::vec::Vec, + pub new_canister_wasm: Vec, /// Arguments passed to the post-upgrade method of the new wasm module. - #[prost(bytes = "vec", optional, tag = "3")] #[serde(deserialize_with = "ic_utils::deserialize::deserialize_option_blob")] - pub canister_upgrade_arg: ::core::option::Option<::prost::alloc::vec::Vec>, + pub canister_upgrade_arg: Option>, /// Canister install_code mode. - #[prost( - enumeration = "::ic_protobuf::types::v1::CanisterInstallMode", - optional, - tag = "4" - )] - pub mode: ::core::option::Option, + pub mode: Option, + /// If the entire WASM does not fit into the 2 MiB ingress limit, then `new_canister_wasm` should be + /// an empty, and this field should be set instead. + pub chunked_canister_wasm: ::core::option::Option, } /// A proposal to transfer SNS treasury funds to (optionally a Subaccount of) the /// target principal. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct TransferSnsTreasuryFunds { - #[prost(enumeration = "transfer_sns_treasury_funds::TransferFrom", tag = "1")] pub from_treasury: i32, /// The amount to transfer, in e8s. - #[prost(uint64, tag = "2")] pub amount_e8s: u64, /// An optional memo to use for the transfer. - #[prost(uint64, optional, tag = "3")] - pub memo: ::core::option::Option, + pub memo: Option, /// The principal to transfer the funds to. - #[prost(message, optional, tag = "4")] - pub to_principal: ::core::option::Option<::ic_base_types::PrincipalId>, + pub to_principal: Option<::ic_base_types::PrincipalId>, /// An (optional) Subaccount of the principal to transfer the funds to. - #[prost(message, optional, tag = "5")] - pub to_subaccount: ::core::option::Option, + pub to_subaccount: Option, } /// Nested message and enum types in `TransferSnsTreasuryFunds`. pub mod transfer_sns_treasury_funds { @@ -429,16 +330,14 @@ pub mod transfer_sns_treasury_funds { #[derive( candid::CandidType, candid::Deserialize, - comparable::Comparable, + Debug, Clone, Copy, - Debug, PartialEq, Eq, Hash, PartialOrd, Ord, - ::prost::Enumeration, )] #[repr(i32)] pub enum TransferFrom { @@ -459,7 +358,7 @@ pub mod transfer_sns_treasury_funds { } } /// Creates an enum from field names used in the ProtoBuf definition. - pub fn from_str_name(value: &str) -> ::core::option::Option { + pub fn from_str_name(value: &str) -> Option { match value { "TRANSFER_FROM_UNSPECIFIED" => Some(Self::Unspecified), "TRANSFER_FROM_ICP_TREASURY" => Some(Self::IcpTreasury), @@ -471,212 +370,113 @@ pub mod transfer_sns_treasury_funds { } /// A proposal function that changes the ledger's parameters. /// Fields with None values will remain unchanged. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct ManageLedgerParameters { - #[prost(uint64, optional, tag = "1")] - pub transfer_fee: ::core::option::Option, - #[prost(string, optional, tag = "2")] - pub token_name: ::core::option::Option<::prost::alloc::string::String>, - #[prost(string, optional, tag = "3")] - pub token_symbol: ::core::option::Option<::prost::alloc::string::String>, - #[prost(string, optional, tag = "4")] - pub token_logo: ::core::option::Option<::prost::alloc::string::String>, + pub transfer_fee: Option, + pub token_name: Option, + pub token_symbol: Option, + pub token_logo: Option, } /// A proposal to mint SNS tokens to (optionally a Subaccount of) the /// target principal. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct MintSnsTokens { /// The amount to transfer, in e8s. - #[prost(uint64, optional, tag = "1")] - pub amount_e8s: ::core::option::Option, + pub amount_e8s: Option, /// An optional memo to use for the transfer. - #[prost(uint64, optional, tag = "2")] - pub memo: ::core::option::Option, + pub memo: Option, /// The principal to transfer the funds to. - #[prost(message, optional, tag = "3")] - pub to_principal: ::core::option::Option<::ic_base_types::PrincipalId>, + pub to_principal: Option<::ic_base_types::PrincipalId>, /// An (optional) Subaccount of the principal to transfer the funds to. - #[prost(message, optional, tag = "4")] - pub to_subaccount: ::core::option::Option, + pub to_subaccount: Option, } /// A proposal function to change the values of SNS metadata. /// Fields with None values will remain unchanged. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct ManageSnsMetadata { /// Base64 representation of the logo. Max length is 341334 characters, roughly 256 Kb. - #[prost(string, optional, tag = "1")] - pub logo: ::core::option::Option<::prost::alloc::string::String>, + pub logo: Option, /// Url string, must be between 10 and 256 characters. - #[prost(string, optional, tag = "2")] - pub url: ::core::option::Option<::prost::alloc::string::String>, + pub url: Option, /// Name string, must be between 4 and 255 characters. - #[prost(string, optional, tag = "3")] - pub name: ::core::option::Option<::prost::alloc::string::String>, + pub name: Option, /// Description string, must be between 10 and 10000 characters. - #[prost(string, optional, tag = "4")] - pub description: ::core::option::Option<::prost::alloc::string::String>, + pub description: Option, } /// A proposal function to upgrade the SNS to the next version. The versions are such that only /// one kind of canister will update at the same time. /// This returns an error if the canister cannot be upgraded or no upgrades are available. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct UpgradeSnsToNextVersion {} /// A proposal to register a list of dapps in the root canister. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct RegisterDappCanisters { /// The canister IDs to be registered (i.e. under the management of the SNS). /// The canisters must be already controlled by the SNS root canister before /// making this proposal. Any controllers besides the root canister will be /// removed when the proposal is executed. /// At least one canister ID is required. - #[prost(message, repeated, tag = "1")] - pub canister_ids: ::prost::alloc::vec::Vec<::ic_base_types::PrincipalId>, + pub canister_ids: Vec<::ic_base_types::PrincipalId>, } /// A proposal to remove a list of dapps from the SNS and assign them to new controllers -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct DeregisterDappCanisters { /// The canister IDs to be deregistered (i.e. removed from the management of the SNS). - #[prost(message, repeated, tag = "1")] - pub canister_ids: ::prost::alloc::vec::Vec<::ic_base_types::PrincipalId>, + pub canister_ids: Vec<::ic_base_types::PrincipalId>, /// The new controllers for the deregistered canisters. - #[prost(message, repeated, tag = "2")] - pub new_controllers: ::prost::alloc::vec::Vec<::ic_base_types::PrincipalId>, + pub new_controllers: Vec<::ic_base_types::PrincipalId>, } /// A proposal to manage the settings of one or more dapp canisters. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct ManageDappCanisterSettings { /// The canister IDs of the dapp canisters to be modified. - #[prost(message, repeated, tag = "1")] - pub canister_ids: ::prost::alloc::vec::Vec<::ic_base_types::PrincipalId>, + pub canister_ids: Vec<::ic_base_types::PrincipalId>, /// Below are fields under CanisterSettings defined at /// - #[prost(uint64, optional, tag = "2")] - pub compute_allocation: ::core::option::Option, - #[prost(uint64, optional, tag = "3")] - pub memory_allocation: ::core::option::Option, - #[prost(uint64, optional, tag = "4")] - pub freezing_threshold: ::core::option::Option, - #[prost(uint64, optional, tag = "5")] - pub reserved_cycles_limit: ::core::option::Option, - #[prost(enumeration = "LogVisibility", optional, tag = "6")] - pub log_visibility: ::core::option::Option, - #[prost(uint64, optional, tag = "7")] - pub wasm_memory_limit: ::core::option::Option, - #[prost(uint64, optional, tag = "8")] - pub wasm_memory_threshold: ::core::option::Option, + pub compute_allocation: Option, + pub memory_allocation: Option, + pub freezing_threshold: Option, + pub reserved_cycles_limit: Option, + pub log_visibility: Option, + pub wasm_memory_limit: Option, + pub wasm_memory_threshold: Option, } /// Unlike `Governance.Version`, this message has optional fields and is the recommended one /// to use in APIs that can evolve. For example, the SNS Governance could eventually support /// a shorthand notation for SNS versions, enabling clients to specify SNS versions without having /// to set each individual SNS framework canister's WASM hash. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct SnsVersion { /// The hash of the Governance canister WASM. - #[prost(bytes = "vec", optional, tag = "1")] - pub governance_wasm_hash: ::core::option::Option<::prost::alloc::vec::Vec>, + pub governance_wasm_hash: Option>, /// The hash of the Swap canister WASM. - #[prost(bytes = "vec", optional, tag = "2")] - pub swap_wasm_hash: ::core::option::Option<::prost::alloc::vec::Vec>, + pub swap_wasm_hash: Option>, /// The hash of the Root canister WASM. - #[prost(bytes = "vec", optional, tag = "3")] - pub root_wasm_hash: ::core::option::Option<::prost::alloc::vec::Vec>, + pub root_wasm_hash: Option>, /// The hash of the Index canister WASM. - #[prost(bytes = "vec", optional, tag = "4")] - pub index_wasm_hash: ::core::option::Option<::prost::alloc::vec::Vec>, + pub index_wasm_hash: Option>, /// The hash of the Ledger canister WASM. - #[prost(bytes = "vec", optional, tag = "5")] - pub ledger_wasm_hash: ::core::option::Option<::prost::alloc::vec::Vec>, + pub ledger_wasm_hash: Option>, /// The hash of the Ledger Archive canister WASM. - #[prost(bytes = "vec", optional, tag = "6")] - pub archive_wasm_hash: ::core::option::Option<::prost::alloc::vec::Vec>, + pub archive_wasm_hash: Option>, } -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct AdvanceSnsTargetVersion { /// If not specified, the target will advance to the latest SNS version known to this SNS. - #[prost(message, optional, tag = "1")] - pub new_target: ::core::option::Option, + pub new_target: Option, } /// A proposal is the immutable input of a proposal submission. -#[derive(candid::CandidType, candid::Deserialize, comparable::Comparable)] -#[compare_default] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct Proposal { /// The proposal's title as a text, which can be at most 256 bytes. - #[prost(string, tag = "1")] - pub title: ::prost::alloc::string::String, + pub title: String, /// The description of the proposal which is a short text, composed /// using a maximum of 30000 bytes of characters. - #[prost(string, tag = "2")] - pub summary: ::prost::alloc::string::String, + pub summary: String, /// The web address of additional content required to evaluate the /// proposal, specified using HTTPS. The URL string must not be longer than /// 2000 bytes. - #[prost(string, tag = "3")] - pub url: ::prost::alloc::string::String, + pub url: String, /// The action that the proposal proposes to take on adoption. /// /// Each action is associated with an function id that can be used for following. @@ -686,11 +486,7 @@ pub struct Proposal { /// /// See `impl From<&Action> for u64` in src/types.rs for the implementation /// of this mapping. - #[prost( - oneof = "proposal::Action", - tags = "4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19" - )] - pub action: ::core::option::Option, + pub action: Option, } /// Nested message and enum types in `Proposal`. pub mod proposal { @@ -703,24 +499,20 @@ pub mod proposal { /// /// See `impl From<&Action> for u64` in src/types.rs for the implementation /// of this mapping. - #[derive( - candid::CandidType, candid::Deserialize, comparable::Comparable, strum_macros::EnumIter, - )] + #[derive(candid::CandidType, candid::Deserialize, Debug)] #[allow(clippy::large_enum_variant)] - #[derive(Clone, PartialEq, ::prost::Oneof)] + #[derive(Clone, PartialEq)] pub enum Action { /// The `Unspecified` action is used as a fallback when /// following. That is, if no followees are specified for a given /// action, the followees for this action are used instead. /// /// Id = 0. - #[prost(message, tag = "4")] Unspecified(super::Empty), /// A motion that should guide the future strategy of the SNS's ecosystem /// but does not have immediate effect in the sense that a method is executed. /// /// Id = 1. - #[prost(message, tag = "5")] Motion(super::Motion), /// Change the nervous system's parameters. /// Note that a change of a parameter will only affect future actions where @@ -732,92 +524,73 @@ pub mod proposal { /// neurons created before this change may have less stake. /// /// Id = 2. - #[prost(message, tag = "6")] ManageNervousSystemParameters(super::NervousSystemParameters), /// Upgrade a canister that is controlled by the SNS governance canister. /// /// Id = 3. - #[prost(message, tag = "7")] UpgradeSnsControlledCanister(super::UpgradeSnsControlledCanister), /// Add a new NervousSystemFunction, of generic type, to be executable by proposal. /// /// Id = 4. - #[prost(message, tag = "8")] AddGenericNervousSystemFunction(super::NervousSystemFunction), /// Remove a NervousSystemFunction, of generic type, from being executable by proposal. /// /// Id = 5. - #[prost(uint64, tag = "9")] RemoveGenericNervousSystemFunction(u64), /// Execute a method outside the SNS canisters. /// /// Ids \in \[1000, u64::MAX\]. - #[prost(message, tag = "10")] ExecuteGenericNervousSystemFunction(super::ExecuteGenericNervousSystemFunction), /// Execute an upgrade to next version on the blessed SNS upgrade path. /// /// Id = 7. - #[prost(message, tag = "11")] UpgradeSnsToNextVersion(super::UpgradeSnsToNextVersion), /// Modify values of SnsMetadata. /// /// Id = 8. - #[prost(message, tag = "12")] ManageSnsMetadata(super::ManageSnsMetadata), /// Transfer SNS treasury funds (ICP or SNS token) to an account. /// Id = 9. - #[prost(message, tag = "13")] TransferSnsTreasuryFunds(super::TransferSnsTreasuryFunds), /// Register one or more dapp canister(s) in the SNS root canister. /// /// Id = 10. - #[prost(message, tag = "14")] RegisterDappCanisters(super::RegisterDappCanisters), /// Deregister one or more dapp canister(s) in the SNS root canister. /// /// Id = 11. - #[prost(message, tag = "15")] DeregisterDappCanisters(super::DeregisterDappCanisters), /// Mint SNS tokens to an account. /// /// Id = 12. - #[prost(message, tag = "16")] MintSnsTokens(super::MintSnsTokens), /// Change some parameters on the ledger. /// /// Id = 13. - #[prost(message, tag = "17")] ManageLedgerParameters(super::ManageLedgerParameters), /// Change canister settings for one or more dapp canister(s). /// /// Id = 14. - #[prost(message, tag = "18")] ManageDappCanisterSettings(super::ManageDappCanisterSettings), /// Advance SNS target version. /// /// Id = 15. - #[prost(message, tag = "19")] AdvanceSnsTargetVersion(super::AdvanceSnsTargetVersion), } } -#[derive(candid::CandidType, candid::Deserialize, comparable::Comparable)] -#[compare_default] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct GovernanceError { - #[prost(enumeration = "governance_error::ErrorType", tag = "1")] pub error_type: i32, - #[prost(string, tag = "2")] - pub error_message: ::prost::alloc::string::String, + pub error_message: String, } /// Nested message and enum types in `GovernanceError`. pub mod governance_error { #[derive( candid::CandidType, candid::Deserialize, - comparable::Comparable, + Debug, Clone, Copy, - Debug, PartialEq, Eq, Hash, @@ -911,7 +684,7 @@ pub mod governance_error { } } /// Creates an enum from field names used in the ProtoBuf definition. - pub fn from_str_name(value: &str) -> ::core::option::Option { + pub fn from_str_name(value: &str) -> Option { match value { "ERROR_TYPE_UNSPECIFIED" => Some(Self::Unspecified), "ERROR_TYPE_UNAVAILABLE" => Some(Self::Unavailable), @@ -942,73 +715,46 @@ pub mod governance_error { /// automatically caused by a neuron following other neurons. /// /// Once a ballot's vote is set it cannot be changed. -#[derive(candid::CandidType, candid::Deserialize, comparable::Comparable)] -#[self_describing] -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct Ballot { /// The ballot's vote. - #[prost(enumeration = "Vote", tag = "1")] pub vote: i32, /// The voting power associated with the ballot. The voting power of a ballot /// associated with a neuron and a proposal is set at the proposal's creation /// time to the neuron's voting power at that time. - #[prost(uint64, tag = "2")] pub voting_power: u64, /// The time when the ballot's vote was populated with a decision (YES or NO, not /// UNDECIDED) in seconds since the UNIX epoch. This is only meaningful once a /// decision has been made and set to zero when the proposal associated with the /// ballot is created. - #[prost(uint64, tag = "3")] pub cast_timestamp_seconds: u64, } /// A tally of votes associated with a proposal. -#[derive(candid::CandidType, candid::Deserialize, comparable::Comparable)] -#[self_describing] -#[derive(Clone, Copy, PartialEq, ::prost::Message)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct Tally { /// The time when this tally was made, in seconds from the Unix epoch. - #[prost(uint64, tag = "1")] pub timestamp_seconds: u64, /// The number of yes votes, in voting power unit. - #[prost(uint64, tag = "2")] pub yes: u64, /// The number of no votes, in voting power unit. - #[prost(uint64, tag = "3")] pub no: u64, /// The total voting power unit of eligible neurons that can vote /// on the proposal that this tally is associated with (i.e., the sum /// of the voting power of yes, no, and undecided votes). /// This should always be greater than or equal to yes + no. - #[prost(uint64, tag = "4")] pub total: u64, } /// The wait-for-quiet state associated with a proposal, storing the /// data relevant to the "wait-for-quiet" implementation. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct WaitForQuietState { /// The current deadline of the proposal associated with this /// WaitForQuietState, in seconds from the Unix epoch. - #[prost(uint64, tag = "1")] pub current_deadline_timestamp_seconds: u64, } /// The ProposalData that contains everything related to a proposal: /// the proposal itself (immutable), as well as mutable data such as ballots. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct ProposalData { /// The proposal's action. /// Types 0-999 are reserved for current (and future) core governance @@ -1031,61 +777,49 @@ pub struct ProposalData { /// Id 13 - ManageLedgerParameters proposals. /// Id 14 - ManageDappCanisterSettings proposals. /// Id 15 - AdvanceSnsTargetVersion proposals. - #[prost(uint64, tag = "1")] pub action: u64, /// This is stored here temporarily. It is also stored on the map /// that contains proposals. /// /// The unique id for this proposal. - #[prost(message, optional, tag = "2")] - pub id: ::core::option::Option, + pub id: Option, /// The NeuronId of the Neuron that made this proposal. - #[prost(message, optional, tag = "3")] - pub proposer: ::core::option::Option, + pub proposer: Option, /// The amount of governance tokens in e8s to be /// charged to the proposer if the proposal is rejected. - #[prost(uint64, tag = "4")] pub reject_cost_e8s: u64, /// The proposal originally submitted. - #[prost(message, optional, tag = "5")] - pub proposal: ::core::option::Option, + pub proposal: Option, /// The timestamp, in seconds from the Unix epoch, /// when this proposal was made. - #[prost(uint64, tag = "6")] pub proposal_creation_timestamp_seconds: u64, /// The ballots associated with a proposal, given as a map which /// maps the neurons' NeuronId to the neurons' ballots. This is /// only present as long as the proposal is not settled with /// respect to rewards. - #[prost(btree_map = "string, message", tag = "7")] - pub ballots: ::prost::alloc::collections::BTreeMap<::prost::alloc::string::String, Ballot>, + pub ballots: BTreeMap, /// The latest tally. The tally is computed only for open proposals when /// they are processed. Once a proposal is decided, i.e., /// ProposalDecisionStatus isn't open anymore, the tally never changes /// again. (But the ballots may still change as neurons may vote after /// the proposal has been decided.) - #[prost(message, optional, tag = "8")] - pub latest_tally: ::core::option::Option, + pub latest_tally: Option, /// The timestamp, in seconds since the Unix epoch, when this proposal /// was adopted or rejected. If not specified, the proposal is still 'open'. - #[prost(uint64, tag = "9")] pub decided_timestamp_seconds: u64, /// The timestamp, in seconds since the Unix epoch, when the (previously /// adopted) proposal has been executed. If not specified (i.e., still has /// the default value zero), the proposal has not (yet) been executed /// successfully. - #[prost(uint64, tag = "10")] pub executed_timestamp_seconds: u64, /// The timestamp, in seconds since the Unix epoch, when the (previously /// adopted) proposal has failed to be executed. If not specified (i.e., /// still has the default value zero), the proposal has not (yet) failed /// to execute. - #[prost(uint64, tag = "11")] pub failed_timestamp_seconds: u64, /// The reason why the (previously adopted) proposal has failed to execute. /// If not specified, the proposal has not (yet) failed to execute. - #[prost(message, optional, tag = "12")] - pub failure_reason: ::core::option::Option, + pub failure_reason: Option, /// OBSOLETE: Superseded by reward_event_end_timestamp_seconds. However, old /// proposals use this (old) field, not the new one, since they predate the new /// field. Therefore, to correctly detect whether a proposal has been rewarded, @@ -1099,11 +833,9 @@ pub struct ProposalData { /// no reward event taking this proposal into consideration happened yet. /// /// This field matches field round in RewardEvent. - #[prost(uint64, tag = "13")] pub reward_event_round: u64, /// The proposal's wait-for-quiet state. This needs to be saved in stable memory. - #[prost(message, optional, tag = "14")] - pub wait_for_quiet_state: ::core::option::Option, + pub wait_for_quiet_state: Option, /// The proposal's payload rendered as text, for display in text/UI frontends. /// This is set if the proposal is considered valid at time of submission. /// @@ -1113,8 +845,7 @@ pub struct ProposalData { /// Proposals with action of type GenericNervousSystemFunction provide no /// guarantee on the style of rendering as this is performed by the /// GenericNervousSystemFunction validator_canister. - #[prost(string, optional, tag = "15")] - pub payload_text_rendering: ::core::option::Option<::prost::alloc::string::String>, + pub payload_text_rendering: Option, /// Deprecated. From now on, this field will be set to true when new proposals /// are created. However, there ARE old proposals where this is set to false. /// @@ -1122,149 +853,83 @@ pub struct ProposalData { /// directly to Settled /// /// TODO(NNS1-2731): Delete this. - #[prost(bool, tag = "16")] pub is_eligible_for_rewards: bool, /// The initial voting period of the proposal, identical in meaning to the one in /// NervousSystemParameters, and duplicated here so the parameters can be changed /// without affecting existing proposals. - #[prost(uint64, tag = "17")] pub initial_voting_period_seconds: u64, /// The wait_for_quiet_deadline_increase_seconds of the proposal, identical in /// meaning to the one in NervousSystemParameters, and duplicated here so the /// parameters can be changed without affecting existing proposals. - #[prost(uint64, tag = "18")] pub wait_for_quiet_deadline_increase_seconds: u64, /// If populated, then the proposal is considered "settled" in terms of voting /// rewards. Prior to distribution of rewards, but after votes are no longer /// accepted, it is considered "ready to settle". - #[prost(uint64, optional, tag = "19")] - pub reward_event_end_timestamp_seconds: ::core::option::Option, + pub reward_event_end_timestamp_seconds: Option, /// Minimum "yes" votes needed for proposal adoption, as a fraction of the /// total voting power. Example: 300 basis points represents a requirement that /// 3% of the total voting power votes to adopt the proposal. - #[prost(message, optional, tag = "20")] - pub minimum_yes_proportion_of_total: - ::core::option::Option<::ic_nervous_system_proto::pb::v1::Percentage>, + pub minimum_yes_proportion_of_total: Option<::ic_nervous_system_proto::pb::v1::Percentage>, /// Minimum "yes" votes needed for proposal adoption, as a fraction of the /// exercised voting power. Example: 50_000 basis points represents a /// requirement that 50% of the exercised voting power votes to adopt the /// proposal. - #[prost(message, optional, tag = "21")] - pub minimum_yes_proportion_of_exercised: - ::core::option::Option<::ic_nervous_system_proto::pb::v1::Percentage>, + pub minimum_yes_proportion_of_exercised: Option<::ic_nervous_system_proto::pb::v1::Percentage>, /// In general, this holds data retrieved at proposal submission/creation time and used later /// during execution. This varies based on the action of the proposal. - #[prost(oneof = "proposal_data::ActionAuxiliary", tags = "22, 23, 24")] - pub action_auxiliary: ::core::option::Option, + pub action_auxiliary: Option, } /// Nested message and enum types in `ProposalData`. pub mod proposal_data { - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct TransferSnsTreasuryFundsActionAuxiliary { - #[prost(message, optional, tag = "1")] - pub valuation: ::core::option::Option, + pub valuation: Option, } - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct MintSnsTokensActionAuxiliary { - #[prost(message, optional, tag = "1")] - pub valuation: ::core::option::Option, + pub valuation: Option, } - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct AdvanceSnsTargetVersionActionAuxiliary { /// Corresponds to the Some(target_version) from an AdvanceSnsTargetVersion proposal, or /// to the last SNS version known to this SNS at the time of AdvanceSnsTargetVersion creation. - #[prost(message, optional, tag = "1")] - pub target_version: ::core::option::Option, + pub target_version: Option, } /// In general, this holds data retrieved at proposal submission/creation time and used later /// during execution. This varies based on the action of the proposal. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Oneof, - )] + #[derive(candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub enum ActionAuxiliary { - #[prost(message, tag = "22")] TransferSnsTreasuryFunds(TransferSnsTreasuryFundsActionAuxiliary), - #[prost(message, tag = "23")] MintSnsTokens(MintSnsTokensActionAuxiliary), - #[prost(message, tag = "24")] AdvanceSnsTargetVersion(AdvanceSnsTargetVersionActionAuxiliary), } } -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct Valuation { - #[prost(enumeration = "valuation::Token", optional, tag = "1")] - pub token: ::core::option::Option, - #[prost(message, optional, tag = "2")] - pub account: ::core::option::Option, - #[prost(uint64, optional, tag = "3")] - pub timestamp_seconds: ::core::option::Option, - #[prost(message, optional, tag = "4")] - pub valuation_factors: ::core::option::Option, + pub token: Option, + pub account: Option, + pub timestamp_seconds: Option, + pub valuation_factors: Option, } /// Nested message and enum types in `Valuation`. pub mod valuation { - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct ValuationFactors { - #[prost(message, optional, tag = "1")] - pub tokens: ::core::option::Option<::ic_nervous_system_proto::pb::v1::Tokens>, - #[prost(message, optional, tag = "2")] - pub icps_per_token: ::core::option::Option<::ic_nervous_system_proto::pb::v1::Decimal>, - #[prost(message, optional, tag = "3")] - pub xdrs_per_icp: ::core::option::Option<::ic_nervous_system_proto::pb::v1::Decimal>, + pub tokens: Option<::ic_nervous_system_proto::pb::v1::Tokens>, + pub icps_per_token: Option<::ic_nervous_system_proto::pb::v1::Decimal>, + pub xdrs_per_icp: Option<::ic_nervous_system_proto::pb::v1::Decimal>, } #[derive( candid::CandidType, candid::Deserialize, - comparable::Comparable, + Debug, Clone, Copy, - Debug, PartialEq, Eq, Hash, PartialOrd, Ord, - ::prost::Enumeration, )] #[repr(i32)] pub enum Token { @@ -1285,7 +950,7 @@ pub mod valuation { } } /// Creates an enum from field names used in the ProtoBuf definition. - pub fn from_str_name(value: &str) -> ::core::option::Option { + pub fn from_str_name(value: &str) -> Option { match value { "TOKEN_UNSPECIFIED" => Some(Self::Unspecified), "TOKEN_ICP" => Some(Self::Icp), @@ -1304,29 +969,19 @@ pub mod valuation { /// on the subnet). /// /// Required invariant: the canister code assumes that all system parameters are always set. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct NervousSystemParameters { /// The number of e8s (10E-8 of a token) that a rejected /// proposal costs the proposer. - #[prost(uint64, optional, tag = "1")] - pub reject_cost_e8s: ::core::option::Option, + pub reject_cost_e8s: Option, /// The minimum number of e8s (10E-8 of a token) that can be staked in a neuron. /// /// To ensure that staking and disbursing of the neuron work, the chosen value /// must be larger than the transaction_fee_e8s. - #[prost(uint64, optional, tag = "2")] - pub neuron_minimum_stake_e8s: ::core::option::Option, + pub neuron_minimum_stake_e8s: Option, /// The transaction fee that must be paid for ledger transactions (except /// minting and burning governance tokens). - #[prost(uint64, optional, tag = "3")] - pub transaction_fee_e8s: ::core::option::Option, + pub transaction_fee_e8s: Option, /// The maximum number of proposals to keep, per action. When the /// total number of proposals for a given action is greater than this /// number, the oldest proposals that have reached final decision state @@ -1335,8 +990,7 @@ pub struct NervousSystemParameters { /// /// The number must be larger than zero and at most be as large as the /// defined ceiling MAX_PROPOSALS_TO_KEEP_PER_ACTION_CEILING. - #[prost(uint32, optional, tag = "4")] - pub max_proposals_to_keep_per_action: ::core::option::Option, + pub max_proposals_to_keep_per_action: Option, /// The initial voting period of a newly created proposal. /// A proposal's voting period may then be further increased during /// a proposal's lifecycle due to the wait-for-quiet algorithm. @@ -1344,8 +998,7 @@ pub struct NervousSystemParameters { /// The voting period must be between (inclusive) the defined floor /// INITIAL_VOTING_PERIOD_SECONDS_FLOOR and ceiling /// INITIAL_VOTING_PERIOD_SECONDS_CEILING. - #[prost(uint64, optional, tag = "5")] - pub initial_voting_period_seconds: ::core::option::Option, + pub initial_voting_period_seconds: Option, /// The wait for quiet algorithm extends the voting period of a proposal when /// there is a flip in the majority vote during the proposal's voting period. /// This parameter determines the maximum time period that the voting period @@ -1356,8 +1009,7 @@ pub struct NervousSystemParameters { /// The maximum total voting period extension is 2 * wait_for_quiet_deadline_increase_seconds. /// For more information, see the wiki page on the wait-for-quiet algorithm: /// - #[prost(uint64, optional, tag = "18")] - pub wait_for_quiet_deadline_increase_seconds: ::core::option::Option, + pub wait_for_quiet_deadline_increase_seconds: Option, /// TODO NNS1-2169: This field currently has no effect. /// TODO NNS1-2169: Design and implement this feature. /// @@ -1367,34 +1019,28 @@ pub struct NervousSystemParameters { /// If unset, neurons will have no followees by default. /// The set of followees for each function can be at most of size /// max_followees_per_function. - #[prost(message, optional, tag = "6")] - pub default_followees: ::core::option::Option, + pub default_followees: Option, /// The maximum number of allowed neurons. When this maximum is reached, no new /// neurons will be created until some are removed. /// /// This number must be larger than zero and at most as large as the defined /// ceiling MAX_NUMBER_OF_NEURONS_CEILING. - #[prost(uint64, optional, tag = "7")] - pub max_number_of_neurons: ::core::option::Option, + pub max_number_of_neurons: Option, /// The minimum dissolve delay a neuron must have to be eligible to vote. /// /// The chosen value must be smaller than max_dissolve_delay_seconds. - #[prost(uint64, optional, tag = "8")] - pub neuron_minimum_dissolve_delay_to_vote_seconds: ::core::option::Option, + pub neuron_minimum_dissolve_delay_to_vote_seconds: Option, /// The maximum number of followees each neuron can establish for each nervous system function. /// /// This number can be at most as large as the defined ceiling /// MAX_FOLLOWEES_PER_FUNCTION_CEILING. - #[prost(uint64, optional, tag = "9")] - pub max_followees_per_function: ::core::option::Option, + pub max_followees_per_function: Option, /// The maximum dissolve delay that a neuron can have. That is, the maximum /// that a neuron's dissolve delay can be increased to. The maximum is also enforced /// when saturating the dissolve delay bonus in the voting power computation. - #[prost(uint64, optional, tag = "10")] - pub max_dissolve_delay_seconds: ::core::option::Option, + pub max_dissolve_delay_seconds: Option, /// The age of a neuron that saturates the age bonus for the voting power computation. - #[prost(uint64, optional, tag = "12")] - pub max_neuron_age_for_age_bonus: ::core::option::Option, + pub max_neuron_age_for_age_bonus: Option, /// The max number of proposals for which ballots are still stored, i.e., /// unsettled proposals. If this number of proposals is reached, new proposals /// can only be added in exceptional cases (for few proposals it is defined @@ -1403,26 +1049,21 @@ pub struct NervousSystemParameters { /// /// This number must be larger than zero and at most as large as the defined /// ceiling MAX_NUMBER_OF_PROPOSALS_WITH_BALLOTS_CEILING. - #[prost(uint64, optional, tag = "14")] - pub max_number_of_proposals_with_ballots: ::core::option::Option, + pub max_number_of_proposals_with_ballots: Option, /// The default set of neuron permissions granted to the principal claiming a neuron. - #[prost(message, optional, tag = "15")] - pub neuron_claimer_permissions: ::core::option::Option, + pub neuron_claimer_permissions: Option, /// The superset of neuron permissions a principal with permission /// `NeuronPermissionType::ManagePrincipals` for a given neuron can grant to another /// principal for this same neuron. /// If this set changes via a ManageNervousSystemParameters proposal, previous /// neurons' permissions will be unchanged and only newly granted permissions will be affected. - #[prost(message, optional, tag = "16")] - pub neuron_grantable_permissions: ::core::option::Option, + pub neuron_grantable_permissions: Option, /// The maximum number of principals that can have permissions for a neuron - #[prost(uint64, optional, tag = "17")] - pub max_number_of_principals_per_neuron: ::core::option::Option, + pub max_number_of_principals_per_neuron: Option, /// When this field is not populated, voting rewards are "disabled". Once this /// is set, it probably should not be changed, because the results would /// probably be pretty confusing. - #[prost(message, optional, tag = "19")] - pub voting_rewards_parameters: ::core::option::Option, + pub voting_rewards_parameters: Option, /// E.g. if a large dissolve delay can double the voting power of a neuron, /// then this field would have a value of 100, indicating a maximum of /// 100% additional voting power. @@ -1430,14 +1071,12 @@ pub struct NervousSystemParameters { /// For no bonus, this should be set to 0. /// /// To achieve functionality equivalent to NNS, this should be set to 100. - #[prost(uint64, optional, tag = "20")] - pub max_dissolve_delay_bonus_percentage: ::core::option::Option, + pub max_dissolve_delay_bonus_percentage: Option, /// Analogous to the previous field (see the previous comment), /// but this one relates to neuron age instead of dissolve delay. /// /// To achieve functionality equivalent to NNS, this should be set to 25. - #[prost(uint64, optional, tag = "21")] - pub max_age_bonus_percentage: ::core::option::Option, + pub max_age_bonus_percentage: Option, /// By default, maturity modulation is enabled; however, an SNS can use this /// field to disable it. When disabled, this canister will still poll the /// Cycles Minting Canister (CMC), and store the value received therefrom. @@ -1446,18 +1085,12 @@ pub struct NervousSystemParameters { /// The reason we call this "disabled" instead of (positive) "enabled" is so /// that the PB default (bool fields are false) and our application default /// (enabled) agree. - #[prost(bool, optional, tag = "22")] - pub maturity_modulation_disabled: ::core::option::Option, + pub maturity_modulation_disabled: Option, + /// Whether to automatically advance the SNS target version after a new upgrade is published + /// by the NNS. If not specified, defaults to false for backward compatibility. + pub automatically_advance_target_version: Option, } -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct VotingRewardsParameters { /// The amount of time between reward events. /// @@ -1473,14 +1106,12 @@ pub struct VotingRewardsParameters { /// within a few seconds of this. /// /// This supersedes super.reward_distribution_period_seconds. - #[prost(uint64, optional, tag = "1")] - pub round_duration_seconds: ::core::option::Option, + pub round_duration_seconds: Option, /// The amount of time that the growth rate changes (presumably, decreases) /// from the initial growth rate to the final growth rate. (See the two /// *_reward_rate_basis_points fields bellow.) The transition is quadratic, and /// levels out at the end of the growth rate transition period. - #[prost(uint64, optional, tag = "3")] - pub reward_rate_transition_duration_seconds: ::core::option::Option, + pub reward_rate_transition_duration_seconds: Option, /// The amount of rewards is proportional to token_supply * current_rate. In /// turn, current_rate is somewhere between `initial_reward_rate_basis_points` /// and `final_reward_rate_basis_points`. In the first reward period, it is the @@ -1490,37 +1121,19 @@ pub struct VotingRewardsParameters { /// quadratic, and levels out at the end of the growth rate transition period. /// /// (A basis point is one in ten thousand.) - #[prost(uint64, optional, tag = "4")] - pub initial_reward_rate_basis_points: ::core::option::Option, - #[prost(uint64, optional, tag = "5")] - pub final_reward_rate_basis_points: ::core::option::Option, + pub initial_reward_rate_basis_points: Option, + pub final_reward_rate_basis_points: Option, } /// The set of default followees that every newly created neuron will follow per function. /// This is specified as a mapping of proposal functions to followees for that function. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct DefaultFollowees { - #[prost(btree_map = "uint64, message", tag = "1")] - pub followees: ::prost::alloc::collections::BTreeMap, + pub followees: BTreeMap, } /// A wrapper for a list of neuron permissions. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct NeuronPermissionList { - #[prost(enumeration = "NeuronPermissionType", repeated, tag = "1")] - pub permissions: ::prost::alloc::vec::Vec, + pub permissions: Vec, } /// A record of when voting rewards were determined, and neuron maturity /// increased for participation in voting on proposals. @@ -1531,14 +1144,7 @@ pub struct NeuronPermissionList { /// To make it a little easier to eventually deduplicate NNS and SNS governance /// code, tags should be chosen so that it is new to BOTH this and the NNS /// RewardEvent. (This also applies to other message definitions.) -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct RewardEvent { /// DEPRECATED: Use end_timestamp_seconds instead. /// @@ -1554,23 +1160,19 @@ pub struct RewardEvent { /// it was not possible to process a reward event for a while. This means that /// successive values in this field might not be consecutive, but they usually /// are. - #[prost(uint64, tag = "1")] pub round: u64, /// Not to be confused with round_end_timestampe_seconds. This is just used to /// record when the calculation (of voting rewards) was performed, not the time /// range/events (i.e. proposals) that was operated on. - #[prost(uint64, tag = "2")] pub actual_timestamp_seconds: u64, /// The list of proposals that were taken into account during /// this reward event. - #[prost(message, repeated, tag = "3")] - pub settled_proposals: ::prost::alloc::vec::Vec, + pub settled_proposals: Vec, /// The total amount of reward that was distributed during this reward event. /// /// The unit is "e8s equivalent" to insist that, while this quantity is on /// the same scale as governance tokens, maturity is not directly convertible /// to governance tokens: conversion requires a minting event. - #[prost(uint64, tag = "4")] pub distributed_e8s_equivalent: u64, /// All proposals that were "ready to settle" up to this time were /// considered. @@ -1587,8 +1189,7 @@ pub struct RewardEvent { /// Being able to change round duration does not exist in NNS (yet), and there /// is (currently) no intention to add that feature, but it could be done by /// making similar changes. - #[prost(uint64, optional, tag = "5")] - pub end_timestamp_seconds: ::core::option::Option, + pub end_timestamp_seconds: Option, /// In some cases, the rewards that would have been distributed in one round are /// "rolled over" into the next reward event. This field keeps track of how many /// rounds have passed since the last time rewards were distributed (rather @@ -1607,8 +1208,7 @@ pub struct RewardEvent { /// settled to distribute rewards for. /// /// In both of these cases, the rewards purse rolls over into the next round. - #[prost(uint64, optional, tag = "6")] - pub rounds_since_last_distribution: ::core::option::Option, + pub rounds_since_last_distribution: Option, /// The total amount of rewards that was available during the reward event. /// /// The e8s_equivalent_to_be_rolled_over method returns this when @@ -1619,32 +1219,25 @@ pub struct RewardEvent { /// Warning: There is a field with the same name in NNS, but different tags are /// used. Also, this uses the `optional` keyword (whereas, the NNS analog does /// not). - #[prost(uint64, optional, tag = "8")] - pub total_available_e8s_equivalent: ::core::option::Option, + pub total_available_e8s_equivalent: Option, } /// The representation of the whole governance system, containing all /// information about the governance system that must be kept /// across upgrades of the governance system, i.e. kept in stable memory. -#[derive(candid::CandidType, candid::Deserialize, comparable::Comparable)] -#[compare_default] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct Governance { /// The current set of neurons registered in governance as a map from /// neuron IDs to neurons. - #[prost(btree_map = "string, message", tag = "1")] - pub neurons: ::prost::alloc::collections::BTreeMap<::prost::alloc::string::String, Neuron>, + pub neurons: BTreeMap, /// The current set of proposals registered in governance as a map /// from proposal IDs to the proposals' data. - #[prost(btree_map = "uint64, message", tag = "2")] - pub proposals: ::prost::alloc::collections::BTreeMap, + pub proposals: BTreeMap, /// The nervous system parameters that define and can be set by /// each nervous system. - #[prost(message, optional, tag = "8")] - pub parameters: ::core::option::Option, + pub parameters: Option, /// TODO IC-1168: update when rewards are introduced /// The latest reward event. - #[prost(message, optional, tag = "9")] - pub latest_reward_event: ::core::option::Option, + pub latest_reward_event: Option, /// The in-flight neuron ledger commands as a map from neuron IDs /// to commands. /// @@ -1666,87 +1259,54 @@ pub struct Governance { /// Because we know exactly what was going on, we should have the /// information necessary to reconcile the state, using custom code /// added on upgrade, if necessary. - #[prost(btree_map = "string, message", tag = "10")] - pub in_flight_commands: ::prost::alloc::collections::BTreeMap< - ::prost::alloc::string::String, - governance::NeuronInFlightCommand, - >, + pub in_flight_commands: BTreeMap, /// The timestamp that is considered genesis for the governance /// system, in seconds since the Unix epoch. That is, the time /// at which `canister_init` was run for the governance canister. - #[prost(uint64, tag = "11")] pub genesis_timestamp_seconds: u64, - #[prost(message, optional, tag = "13")] - pub metrics: ::core::option::Option, + pub metrics: Option, /// The canister ID of the ledger canister. - #[prost(message, optional, tag = "16")] - pub ledger_canister_id: ::core::option::Option<::ic_base_types::PrincipalId>, + pub ledger_canister_id: Option<::ic_base_types::PrincipalId>, /// The canister ID of the root canister. - #[prost(message, optional, tag = "17")] - pub root_canister_id: ::core::option::Option<::ic_base_types::PrincipalId>, + pub root_canister_id: Option<::ic_base_types::PrincipalId>, /// ID to NervousSystemFunction (which has an id field). - #[prost(btree_map = "uint64, message", tag = "18")] - pub id_to_nervous_system_functions: - ::prost::alloc::collections::BTreeMap, - #[prost(enumeration = "governance::Mode", tag = "19")] + pub id_to_nervous_system_functions: BTreeMap, pub mode: i32, /// The canister ID of the swap canister. /// /// When this is unpopulated, mode should be Normal, and when this is /// populated, mode should be PreInitializationSwap. - #[prost(message, optional, tag = "20")] - pub swap_canister_id: ::core::option::Option<::ic_base_types::PrincipalId>, - #[prost(message, optional, tag = "21")] - pub sns_metadata: ::core::option::Option, + pub swap_canister_id: Option<::ic_base_types::PrincipalId>, + pub sns_metadata: Option, /// The initialization parameters used to spawn an SNS - #[prost(string, tag = "22")] - pub sns_initialization_parameters: ::prost::alloc::string::String, + pub sns_initialization_parameters: String, /// Current version that this SNS is running. - #[prost(message, optional, tag = "23")] - pub deployed_version: ::core::option::Option, + pub deployed_version: Option, /// Version SNS is in process of upgrading to. - #[prost(message, optional, tag = "24")] - pub pending_version: ::core::option::Option, - #[prost(message, optional, tag = "30")] - pub target_version: ::core::option::Option, + pub pending_version: Option, + pub target_version: Option, /// True if the run_periodic_tasks function is currently finalizing disburse maturity, meaning /// that it should finish before being called again. - #[prost(bool, optional, tag = "25")] - pub is_finalizing_disburse_maturity: ::core::option::Option, - #[prost(message, optional, tag = "26")] - pub maturity_modulation: ::core::option::Option, - #[prost(message, optional, tag = "29")] - pub cached_upgrade_steps: ::core::option::Option, + pub is_finalizing_disburse_maturity: Option, + pub maturity_modulation: Option, + pub cached_upgrade_steps: Option, /// Information about the timers that perform periodic tasks of this Governance canister. - #[prost(message, optional, tag = "31")] - pub timers: ::core::option::Option<::ic_nervous_system_proto::pb::v1::Timers>, - #[prost(message, optional, tag = "32")] - pub upgrade_journal: ::core::option::Option, + pub timers: Option<::ic_nervous_system_proto::pb::v1::Timers>, + pub upgrade_journal: Option, } /// Nested message and enum types in `Governance`. pub mod governance { + use super::*; use crate::format_full_hash; use serde::ser::SerializeStruct; /// The commands that require a neuron lock. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct NeuronInFlightCommand { /// The timestamp at which the command was issued, for debugging /// purposes. - #[prost(uint64, tag = "1")] pub timestamp: u64, - #[prost( - oneof = "neuron_in_flight_command::Command", - tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 20" - )] - pub command: ::core::option::Option, + pub command: Option, } /// Nested message and enum types in `NeuronInFlightCommand`. pub mod neuron_in_flight_command { @@ -1758,67 +1318,36 @@ pub mod governance { /// no value in actually storing the command itself, and this placeholder /// can generally be used in all sync cases. #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, + Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq, )] pub struct SyncCommand {} - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Oneof, - )] + #[derive(candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub enum Command { - #[prost(message, tag = "2")] Disburse(super::super::manage_neuron::Disburse), - #[prost(message, tag = "3")] Split(super::super::manage_neuron::Split), - #[prost(message, tag = "4")] MergeMaturity(super::super::manage_neuron::MergeMaturity), - #[prost(message, tag = "5")] DisburseMaturity(super::super::manage_neuron::DisburseMaturity), - #[prost(message, tag = "6")] ClaimOrRefreshNeuron(super::super::manage_neuron::ClaimOrRefresh), - #[prost(message, tag = "7")] AddNeuronPermissions(super::super::manage_neuron::AddNeuronPermissions), - #[prost(message, tag = "8")] RemoveNeuronPermissions(super::super::manage_neuron::RemoveNeuronPermissions), - #[prost(message, tag = "9")] Configure(super::super::manage_neuron::Configure), - #[prost(message, tag = "10")] Follow(super::super::manage_neuron::Follow), - #[prost(message, tag = "11")] MakeProposal(super::super::Proposal), - #[prost(message, tag = "12")] RegisterVote(super::super::manage_neuron::RegisterVote), - #[prost(message, tag = "13")] FinalizeDisburseMaturity(super::super::manage_neuron::FinalizeDisburseMaturity), - #[prost(message, tag = "20")] SyncCommand(SyncCommand), } } /// Metrics that are too costly to compute each time when they are /// requested. - #[derive(candid::CandidType, candid::Deserialize, comparable::Comparable)] - #[compare_default] - #[derive(Clone, PartialEq, ::prost::Message)] + #[derive(candid::CandidType, candid::Deserialize, Debug, Default, Clone, PartialEq)] pub struct GovernanceCachedMetrics { /// The timestamp when these metrics were computed, as seconds since /// Unix epoch. - #[prost(uint64, tag = "1")] pub timestamp_seconds: u64, /// The total supply of governance tokens in the ledger canister. - #[prost(uint64, tag = "2")] pub total_supply_governance_tokens: u64, /// The number of dissolving neurons (i.e., in NeuronState::Dissolving). - #[prost(uint64, tag = "3")] pub dissolving_neurons_count: u64, /// The number of staked governance tokens in dissolving neurons /// (i.e., in NeuronState::Dissolving) grouped by the neurons' dissolve delay @@ -1826,16 +1355,13 @@ pub mod governance { /// This is given as a map from dissolve delays (rounded to years) /// to the sum of staked tokens in the dissolving neurons that have this /// dissolve delay. - #[prost(btree_map = "uint64, double", tag = "4")] - pub dissolving_neurons_e8s_buckets: ::prost::alloc::collections::BTreeMap, + pub dissolving_neurons_e8s_buckets: BTreeMap, /// The number of dissolving neurons (i.e., in NeuronState::Dissolving) /// grouped by their dissolve delay rounded to years. /// This is given as a map from dissolve delays (rounded to years) to /// the number of dissolving neurons that have this dissolve delay. - #[prost(btree_map = "uint64, uint64", tag = "5")] - pub dissolving_neurons_count_buckets: ::prost::alloc::collections::BTreeMap, + pub dissolving_neurons_count_buckets: BTreeMap, /// The number of non-dissolving neurons (i.e., in NeuronState::NotDissolving). - #[prost(uint64, tag = "6")] pub not_dissolving_neurons_count: u64, /// The number of staked governance tokens in non-dissolving neurons /// (i.e., in NeuronState::NotDissolving) grouped by the neurons' dissolve delay @@ -1843,65 +1369,45 @@ pub mod governance { /// This is given as a map from dissolve delays (rounded to years) /// to the sum of staked tokens in the non-dissolving neurons that have this /// dissolve delay. - #[prost(btree_map = "uint64, double", tag = "7")] - pub not_dissolving_neurons_e8s_buckets: ::prost::alloc::collections::BTreeMap, + pub not_dissolving_neurons_e8s_buckets: BTreeMap, /// The number of non-dissolving neurons (i.e., in NeuronState::NotDissolving) /// grouped by their dissolve delay rounded to years. /// This is given as a map from dissolve delays (rounded to years) to /// the number of non-dissolving neurons that have this dissolve delay. - #[prost(btree_map = "uint64, uint64", tag = "8")] - pub not_dissolving_neurons_count_buckets: ::prost::alloc::collections::BTreeMap, + pub not_dissolving_neurons_count_buckets: BTreeMap, /// The number of dissolved neurons (i.e., in NeuronState::Dissolved). - #[prost(uint64, tag = "9")] pub dissolved_neurons_count: u64, /// The number of staked governance tokens in dissolved neurons /// (i.e., in NeuronState::Dissolved). - #[prost(uint64, tag = "10")] pub dissolved_neurons_e8s: u64, /// The number of neurons that are garbage collectable, i.e., that /// have a cached stake smaller than the ledger transaction fee. - #[prost(uint64, tag = "11")] pub garbage_collectable_neurons_count: u64, /// The number of neurons that have an invalid stake, i.e., that /// have a cached stake that is larger than zero but smaller than the /// minimum neuron stake defined in the nervous system parameters. - #[prost(uint64, tag = "12")] pub neurons_with_invalid_stake_count: u64, /// The total amount of governance tokens that are staked in neurons, /// measured in fractions of 10E-8 of a governance token. - #[prost(uint64, tag = "13")] pub total_staked_e8s: u64, /// TODO: rather than taking six months, it would be more interesting to take the respective SNS's eligibility boarder here. /// The number of neurons with a dissolve delay of less than six months. - #[prost(uint64, tag = "14")] pub neurons_with_less_than_6_months_dissolve_delay_count: u64, /// The number of governance tokens in neurons with a dissolve delay of /// less than six months. - #[prost(uint64, tag = "15")] pub neurons_with_less_than_6_months_dissolve_delay_e8s: u64, } /// Metadata about this SNS. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct SnsMetadata { /// The logo for the SNS project represented as a base64 encoded string. - #[prost(string, optional, tag = "1")] - pub logo: ::core::option::Option<::prost::alloc::string::String>, + pub logo: Option, /// Url to the dapp controlled by the SNS project. - #[prost(string, optional, tag = "2")] - pub url: ::core::option::Option<::prost::alloc::string::String>, + pub url: Option, /// Name of the SNS project. This may differ from the name of the associated token. - #[prost(string, optional, tag = "3")] - pub name: ::core::option::Option<::prost::alloc::string::String>, + pub name: Option, /// Description of the SNS project. - #[prost(string, optional, tag = "4")] - pub description: ::core::option::Option<::prost::alloc::string::String>, + pub description: Option, } impl serde::Serialize for Version { @@ -1931,87 +1437,48 @@ pub mod governance { /// A version of the SNS defined by the WASM hashes of its canisters. #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Eq, - std::hash::Hash, - Clone, - PartialEq, - ::prost::Message, + candid::CandidType, candid::Deserialize, Debug, Eq, std::hash::Hash, Clone, PartialEq, )] pub struct Version { /// The hash of the Root canister WASM. - #[prost(bytes = "vec", tag = "1")] #[serde(with = "serde_bytes")] - pub root_wasm_hash: ::prost::alloc::vec::Vec, + pub root_wasm_hash: Vec, /// The hash of the Governance canister WASM. - #[prost(bytes = "vec", tag = "2")] #[serde(with = "serde_bytes")] - pub governance_wasm_hash: ::prost::alloc::vec::Vec, + pub governance_wasm_hash: Vec, /// The hash of the Ledger canister WASM. - #[prost(bytes = "vec", tag = "3")] #[serde(with = "serde_bytes")] - pub ledger_wasm_hash: ::prost::alloc::vec::Vec, + pub ledger_wasm_hash: Vec, /// The hash of the Swap canister WASM. - #[prost(bytes = "vec", tag = "4")] #[serde(with = "serde_bytes")] - pub swap_wasm_hash: ::prost::alloc::vec::Vec, + pub swap_wasm_hash: Vec, /// The hash of the Ledger Archive canister WASM. - #[prost(bytes = "vec", tag = "5")] #[serde(with = "serde_bytes")] - pub archive_wasm_hash: ::prost::alloc::vec::Vec, + pub archive_wasm_hash: Vec, /// The hash of the Index canister WASM. - #[prost(bytes = "vec", tag = "6")] #[serde(with = "serde_bytes")] - pub index_wasm_hash: ::prost::alloc::vec::Vec, + pub index_wasm_hash: Vec, } #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - serde::Serialize, - Clone, - PartialEq, - ::prost::Message, + candid::CandidType, candid::Deserialize, Debug, serde::Serialize, Clone, PartialEq, )] pub struct Versions { - #[prost(message, repeated, tag = "1")] - pub versions: ::prost::alloc::vec::Vec, + pub versions: Vec, } /// An upgrade in progress, defined as a version target and a time at which it is considered failed. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct PendingVersion { /// Version to be upgraded to - #[prost(message, optional, tag = "1")] - pub target_version: ::core::option::Option, + pub target_version: Option, /// Seconds since UNIX epoch to mark this as a failed version if not in sync with current version - #[prost(uint64, tag = "2")] pub mark_failed_at_seconds: u64, /// Lock to avoid checking over and over again. Also, it is a counter for how many times we have attempted to check, /// allowing us to fail in case we otherwise have gotten stuck. - #[prost(uint64, tag = "3")] pub checking_upgrade_lock: u64, /// The proposal that initiated this upgrade - #[prost(uint64, optional, tag = "4")] - pub proposal_id: ::core::option::Option, + pub proposal_id: Option, } - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct MaturityModulation { /// When X maturity is disbursed, the amount that goes to the destination /// account is X * (1 + y) where y = current_basis_points / 10_000. @@ -2020,52 +1487,37 @@ pub mod governance { /// /// There is a positive relationship between the price of ICP (in XDR) and /// this value. - #[prost(int32, optional, tag = "1")] - pub current_basis_points: ::core::option::Option, + pub current_basis_points: Option, /// When current_basis_points was last updated (seconds since UNIX epoch). - #[prost(uint64, optional, tag = "2")] - pub updated_at_timestamp_seconds: ::core::option::Option, + pub updated_at_timestamp_seconds: Option, } /// The sns's local cache of the upgrade steps recieved from SNS-W. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct CachedUpgradeSteps { /// The upgrade steps that have been returned from SNS-W the last time we /// called list_upgrade_steps. - #[prost(message, optional, tag = "1")] - pub upgrade_steps: ::core::option::Option, + pub upgrade_steps: Option, /// The timestamp of the request we sent to list_upgrade_steps. /// It's possible that this is greater than the response_timestamp_seconds, because /// we update it as soon as we send the request, and only update the /// response_timestamp and the upgrade_steps when we receive the response. /// The primary use of this is that we can avoid calling list_upgrade_steps /// more frequently than necessary. - #[prost(uint64, optional, tag = "2")] - pub requested_timestamp_seconds: ::core::option::Option, + pub requested_timestamp_seconds: Option, /// The timestamp of the response we received from list_upgrade_steps (stored in upgrade_steps). - #[prost(uint64, optional, tag = "3")] - pub response_timestamp_seconds: ::core::option::Option, + pub response_timestamp_seconds: Option, } #[derive( candid::CandidType, candid::Deserialize, - comparable::Comparable, - strum_macros::EnumIter, + Debug, Clone, Copy, - Debug, PartialEq, Eq, Hash, PartialOrd, Ord, - ::prost::Enumeration, )] #[repr(i32)] pub enum Mode { @@ -2090,7 +1542,7 @@ pub mod governance { } } /// Creates an enum from field names used in the ProtoBuf definition. - pub fn from_str_name(value: &str) -> ::core::option::Option { + pub fn from_str_name(value: &str) -> Option { match value { "MODE_UNSPECIFIED" => Some(Self::Unspecified), "MODE_NORMAL" => Some(Self::Normal), @@ -2101,114 +1553,50 @@ pub mod governance { } } /// Request message for 'get_metadata'. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct GetMetadataRequest {} /// Response message for 'get_metadata'. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct GetMetadataResponse { - #[prost(string, optional, tag = "1")] - pub logo: ::core::option::Option<::prost::alloc::string::String>, - #[prost(string, optional, tag = "2")] - pub url: ::core::option::Option<::prost::alloc::string::String>, - #[prost(string, optional, tag = "3")] - pub name: ::core::option::Option<::prost::alloc::string::String>, - #[prost(string, optional, tag = "4")] - pub description: ::core::option::Option<::prost::alloc::string::String>, + pub logo: Option, + pub url: Option, + pub name: Option, + pub description: Option, } /// Request message for 'get_sns_initialization_parameters' -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct GetSnsInitializationParametersRequest {} /// Response message for 'get_sns_initialization_parameters' -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct GetSnsInitializationParametersResponse { - #[prost(string, tag = "1")] - pub sns_initialization_parameters: ::prost::alloc::string::String, + pub sns_initialization_parameters: String, } /// Request for the SNS's currently running version. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct GetRunningSnsVersionRequest {} /// Response with the SNS's currently running version and any upgrades /// that are in progress. /// GetUpgradeJournal is a superior API to this one that should -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct GetRunningSnsVersionResponse { /// The currently deployed version of the SNS. - #[prost(message, optional, tag = "1")] - pub deployed_version: ::core::option::Option, + pub deployed_version: Option, /// The upgrade in progress, if any. - #[prost(message, optional, tag = "2")] - pub pending_version: - ::core::option::Option, + pub pending_version: Option, } /// Nested message and enum types in `GetRunningSnsVersionResponse`. pub mod get_running_sns_version_response { /// The same as PendingVersion (stored in the governance proto). They are separated to make it easy to change one without changing the other. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct UpgradeInProgress { /// Version to be upgraded to - #[prost(message, optional, tag = "1")] - pub target_version: ::core::option::Option, + pub target_version: Option, /// Seconds since UNIX epoch to mark this as a failed version if not in sync with current version - #[prost(uint64, tag = "2")] pub mark_failed_at_seconds: u64, /// Lock to avoid checking over and over again. Also, it is a counter for how many times we have attempted to check, /// allowing us to fail in case we otherwise have gotten stuck. - #[prost(uint64, tag = "3")] pub checking_upgrade_lock: u64, /// The proposal that initiated this upgrade - #[prost(uint64, tag = "4")] pub proposal_id: u64, } } @@ -2216,174 +1604,74 @@ pub mod get_running_sns_version_response { /// Failed if it is past the time when it should have been marked as failed. /// This is useful in the case where the asynchronous process may have failed to /// complete -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct FailStuckUpgradeInProgressRequest {} /// Response to FailStuckUpgradeInProgressRequest -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct FailStuckUpgradeInProgressResponse {} /// Empty message to use in oneof fields that represent empty /// enums. #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - serde::Serialize, - Clone, - Copy, - PartialEq, - ::prost::Message, + candid::CandidType, candid::Deserialize, Debug, serde::Serialize, Clone, Copy, PartialEq, )] pub struct Empty {} /// An operation that modifies a neuron. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct ManageNeuron { /// The modified neuron's subaccount which also serves as the neuron's ID. - #[prost(bytes = "vec", tag = "1")] #[serde(with = "serde_bytes")] - pub subaccount: ::prost::alloc::vec::Vec, - #[prost( - oneof = "manage_neuron::Command", - tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13" - )] - pub command: ::core::option::Option, + pub subaccount: Vec, + pub command: Option, } /// Nested message and enum types in `ManageNeuron`. pub mod manage_neuron { /// The operation that increases a neuron's dissolve delay. It can be /// increased up to a maximum defined in the nervous system parameters. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct IncreaseDissolveDelay { /// The additional dissolve delay that should be added to the neuron's /// current dissolve delay. - #[prost(uint32, tag = "1")] pub additional_dissolve_delay_seconds: u32, } /// The operation that starts dissolving a neuron, i.e., changes a neuron's /// state such that it is dissolving. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct StartDissolving {} /// The operation that stops dissolving a neuron, i.e., changes a neuron's /// state such that it is non-dissolving. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct StopDissolving {} /// An (idempotent) alternative to IncreaseDissolveDelay where the dissolve delay /// is passed as an absolute timestamp in seconds since the Unix epoch. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct SetDissolveTimestamp { /// The time when the neuron (newly) should become dissolved, in seconds /// since the Unix epoch. - #[prost(uint64, tag = "1")] pub dissolve_timestamp_seconds: u64, } /// Changes auto-stake maturity for this Neuron. While on, auto-stake /// maturity will cause all the maturity generated by voting rewards /// to this neuron to be automatically staked and contribute to the /// voting power of the neuron. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct ChangeAutoStakeMaturity { - #[prost(bool, tag = "1")] pub requested_setting_for_auto_stake_maturity: bool, } /// Commands that only configure a given neuron, but do not interact /// with the outside world. They all require the caller to have /// `NeuronPermissionType::ConfigureDissolveState` for the neuron. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct Configure { - #[prost(oneof = "configure::Operation", tags = "1, 2, 3, 4, 5")] - pub operation: ::core::option::Option, + pub operation: Option, } /// Nested message and enum types in `Configure`. pub mod configure { - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Oneof, - )] + #[derive(candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub enum Operation { - #[prost(message, tag = "1")] IncreaseDissolveDelay(super::IncreaseDissolveDelay), - #[prost(message, tag = "2")] StartDissolving(super::StartDissolving), - #[prost(message, tag = "3")] StopDissolving(super::StopDissolving), - #[prost(message, tag = "4")] SetDissolveTimestamp(super::SetDissolveTimestamp), - #[prost(message, tag = "5")] ChangeAutoStakeMaturity(super::ChangeAutoStakeMaturity), } } @@ -2392,36 +1680,20 @@ pub mod manage_neuron { /// Thereby, the neuron's accumulated fees are burned and (if relevant in /// the given nervous system) the token equivalent of the neuron's accumulated /// maturity are minted and also transferred to the specified account. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct Disburse { /// The (optional) amount to disburse out of the neuron. If not specified the cached /// stake is used. - #[prost(message, optional, tag = "1")] - pub amount: ::core::option::Option, + pub amount: Option, /// The ledger account to which the disbursed tokens are transferred. - #[prost(message, optional, tag = "2")] - pub to_account: ::core::option::Option, + pub to_account: Option, } /// Nested message and enum types in `Disburse`. pub mod disburse { #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, + Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq, )] pub struct Amount { - #[prost(uint64, tag = "1")] pub e8s: u64, } } @@ -2434,97 +1706,51 @@ pub mod manage_neuron { /// the dissolve state. The parent neuron's fees and maturity (if applicable in the given /// nervous system) remain in the parent neuron and the child neuron's fees and maturity /// are initialized to be zero. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct Split { /// The amount of governance tokens (in measured in fractions of 10E-8 of /// a governance token) to be split to the child neuron. - #[prost(uint64, tag = "1")] pub amount_e8s: u64, /// The nonce that is used to compute the child neuron's /// subaccount which also serves as the child neuron's ID. This nonce /// is also used as the memo field in the ledger transfer that transfers /// the stake from the parent to the child neuron. - #[prost(uint64, tag = "2")] pub memo: u64, } /// The operation that merges a given percentage of a neuron's maturity (if applicable /// to the nervous system) to the neuron's stake. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct MergeMaturity { /// The percentage of maturity to merge, from 1 to 100. - #[prost(uint32, tag = "1")] pub percentage_to_merge: u32, } /// Stake the maturity of a neuron. /// The caller can choose a percentage of of the current maturity to stake. /// If 'percentage_to_stake' is not provided, all of the neuron's current /// maturity will be staked. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct StakeMaturity { /// The percentage of maturity to stake, from 1 to 100 (inclusive). - #[prost(uint32, optional, tag = "1")] - pub percentage_to_stake: ::core::option::Option, + pub percentage_to_stake: Option, } /// Disburse the maturity of a neuron to any ledger account. If an account /// is not specified, the caller's account will be used. The caller can choose /// a percentage of the current maturity to disburse to the ledger account. The /// resulting amount to disburse must be greater than or equal to the /// transaction fee. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct DisburseMaturity { /// The percentage to disburse, from 1 to 100 - #[prost(uint32, tag = "1")] pub percentage_to_disburse: u32, /// The (optional) principal to which to transfer the stake. - #[prost(message, optional, tag = "2")] - pub to_account: ::core::option::Option, + pub to_account: Option, } - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct FinalizeDisburseMaturity { /// The amount to be disbursed in e8s of the governance token. - #[prost(uint64, tag = "1")] pub amount_to_be_disbursed_e8s: u64, /// The principal to which to transfer the stake (required). - #[prost(message, optional, tag = "2")] - pub to_account: ::core::option::Option, + pub to_account: Option, } /// The operation that adds a new follow relation to a neuron, specifying /// that it follows a set of followee neurons for a given proposal function. @@ -2546,85 +1772,42 @@ pub mod manage_neuron { /// then it becomes a catch-all follow rule, which will be used to vote /// automatically on proposals with actions for which no /// specific rule has been specified. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct Follow { /// The function id of the proposal function defining for which proposals /// this follow relation is relevant. - #[prost(uint64, tag = "1")] pub function_id: u64, /// The list of followee neurons, specified by their neuron ID. - #[prost(message, repeated, tag = "2")] - pub followees: ::prost::alloc::vec::Vec, + pub followees: Vec, } /// The operation that registers a given vote from the neuron for a given /// proposal (a directly cast vote as opposed to a vote that is cast as /// a result of a follow relation). - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct RegisterVote { /// The ID of the proposal that the vote is cast for. - #[prost(message, optional, tag = "1")] - pub proposal: ::core::option::Option, + pub proposal: Option, /// The vote that is cast to adopt or reject the proposal. - #[prost(enumeration = "super::Vote", tag = "2")] pub vote: i32, } /// The operation that claims a new neuron (if it does not exist yet) or /// refreshes the stake of the neuron (if it already exists). - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct ClaimOrRefresh { - #[prost(oneof = "claim_or_refresh::By", tags = "2, 3")] - pub by: ::core::option::Option, + pub by: Option, } /// Nested message and enum types in `ClaimOrRefresh`. pub mod claim_or_refresh { /// (see MemoAndController below) - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct MemoAndController { /// The memo(nonce) that is used to compute the neuron's subaccount /// (where the tokens were staked to). - #[prost(uint64, tag = "1")] pub memo: u64, /// The principal for which the neuron should be claimed. - #[prost(message, optional, tag = "2")] - pub controller: ::core::option::Option<::ic_base_types::PrincipalId>, + pub controller: Option<::ic_base_types::PrincipalId>, } - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Oneof, - )] + #[derive(candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub enum By { /// The memo and principal used to define the neuron to be claimed /// or refreshed. Specifically, the memo (nonce) and the given principal @@ -2633,12 +1816,10 @@ pub mod manage_neuron { /// refreshing a neuron were transferred to. /// If 'controller' is omitted, the id of the principal who calls this /// operation will be used. - #[prost(message, tag = "2")] MemoAndController(MemoAndController), /// The neuron ID of a neuron that should be refreshed. This just serves /// as an alternative way to specify a neuron to be refreshed, but cannot /// be used to claim new neurons. - #[prost(message, tag = "3")] NeuronId(super::super::Empty), } } @@ -2647,386 +1828,177 @@ pub mod manage_neuron { /// If the PrincipalId doesn't have existing permissions, a new entry will be added for it /// with the provided permissions. If a principalId already has permissions for the neuron, /// the new permissions will be added to the existing set. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct AddNeuronPermissions { /// The PrincipalId that the permissions will be granted to. - #[prost(message, optional, tag = "1")] - pub principal_id: ::core::option::Option<::ic_base_types::PrincipalId>, + pub principal_id: Option<::ic_base_types::PrincipalId>, /// The set of permissions that will be granted to the PrincipalId. - #[prost(message, optional, tag = "2")] - pub permissions_to_add: ::core::option::Option, + pub permissions_to_add: Option, } /// Remove a set of permissions from the Neuron for the given PrincipalId. If a PrincipalId has all of /// its permissions removed, it will be removed from the neuron's permissions list. This is a dangerous /// operation as its possible to remove all permissions for a neuron and no longer be able to modify /// it's state, i.e. disbursing the neuron back into the governance token. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct RemoveNeuronPermissions { /// The PrincipalId that the permissions will be revoked from. - #[prost(message, optional, tag = "1")] - pub principal_id: ::core::option::Option<::ic_base_types::PrincipalId>, + pub principal_id: Option<::ic_base_types::PrincipalId>, /// The set of permissions that will be revoked from the PrincipalId. - #[prost(message, optional, tag = "2")] - pub permissions_to_remove: ::core::option::Option, + pub permissions_to_remove: Option, } - #[derive(candid::CandidType, candid::Deserialize, comparable::Comparable)] + #[derive(candid::CandidType, candid::Deserialize, Debug)] #[allow(clippy::large_enum_variant)] - #[derive(Clone, PartialEq, ::prost::Oneof)] + #[derive(Clone, PartialEq)] pub enum Command { - #[prost(message, tag = "2")] Configure(Configure), - #[prost(message, tag = "3")] Disburse(Disburse), - #[prost(message, tag = "4")] Follow(Follow), /// Making a proposal is defined by a proposal, which contains the proposer neuron. /// Making a proposal will implicitly cast a yes vote for the proposing neuron. - #[prost(message, tag = "5")] MakeProposal(super::Proposal), - #[prost(message, tag = "6")] RegisterVote(RegisterVote), - #[prost(message, tag = "7")] Split(Split), - #[prost(message, tag = "8")] ClaimOrRefresh(ClaimOrRefresh), - #[prost(message, tag = "9")] MergeMaturity(MergeMaturity), - #[prost(message, tag = "10")] DisburseMaturity(DisburseMaturity), - #[prost(message, tag = "11")] AddNeuronPermissions(AddNeuronPermissions), - #[prost(message, tag = "12")] RemoveNeuronPermissions(RemoveNeuronPermissions), - #[prost(message, tag = "13")] StakeMaturity(StakeMaturity), } } /// The response of a ManageNeuron command. /// There is a dedicated response type for each `ManageNeuron.command` field. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct ManageNeuronResponse { - #[prost( - oneof = "manage_neuron_response::Command", - tags = "1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13" - )] - pub command: ::core::option::Option, + pub command: Option, } /// Nested message and enum types in `ManageNeuronResponse`. pub mod manage_neuron_response { /// The response to the ManageNeuron command 'configure'. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct ConfigureResponse {} /// The response to the ManageNeuron command 'disburse'. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct DisburseResponse { /// The block height of the ledger where the tokens were disbursed to the /// given account. - #[prost(uint64, tag = "1")] pub transfer_block_height: u64, } /// The response to the ManageNeuron command 'merge_maturity'. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct MergeMaturityResponse { /// The maturity that was merged in fractions of /// 10E-8 of a governance token. - #[prost(uint64, tag = "1")] pub merged_maturity_e8s: u64, /// The resulting cached stake of the modified neuron /// in fractions of 10E-8 of a governance token. - #[prost(uint64, tag = "2")] pub new_stake_e8s: u64, } - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct DisburseMaturityResponse { /// This field is deprecated and is populated with the same value as `amount_deducted_e8s`. - #[prost(uint64, tag = "2")] pub amount_disbursed_e8s: u64, /// The amount of maturity in e8s of the governance token deducted from the Neuron. /// This amount will undergo maturity modulation if enabled, and may be increased or /// decreased at the time of disbursement. - #[prost(uint64, optional, tag = "3")] - pub amount_deducted_e8s: ::core::option::Option, + pub amount_deducted_e8s: Option, } - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct StakeMaturityResponse { - #[prost(uint64, tag = "1")] pub maturity_e8s: u64, - #[prost(uint64, tag = "2")] pub staked_maturity_e8s: u64, } /// The response to the ManageNeuron command 'follow'. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct FollowResponse {} /// The response to the ManageNeuron command 'make_proposal'. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct MakeProposalResponse { /// The ID of the created proposal. - #[prost(message, optional, tag = "1")] - pub proposal_id: ::core::option::Option, + pub proposal_id: Option, } /// The response to the ManageNeuron command 'register_vote'. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct RegisterVoteResponse {} /// The response to the ManageNeuron command 'split'. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct SplitResponse { /// The ID of the 'child neuron' that was newly created. - #[prost(message, optional, tag = "1")] - pub created_neuron_id: ::core::option::Option, + pub created_neuron_id: Option, } /// The response to the ManageNeuron command 'claim_or_refresh'. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct ClaimOrRefreshResponse { /// The neuron ID of the neuron that was newly claimed or /// refreshed. - #[prost(message, optional, tag = "1")] - pub refreshed_neuron_id: ::core::option::Option, + pub refreshed_neuron_id: Option, } /// The response to the ManageNeuron command 'add_neuron_permissions'. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct AddNeuronPermissionsResponse {} /// The response to the ManageNeuron command 'remove_neuron_permissions'. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct RemoveNeuronPermissionsResponse {} - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Oneof, - )] + #[derive(candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub enum Command { - #[prost(message, tag = "1")] Error(super::GovernanceError), - #[prost(message, tag = "2")] Configure(ConfigureResponse), - #[prost(message, tag = "3")] Disburse(DisburseResponse), - #[prost(message, tag = "4")] Follow(FollowResponse), - #[prost(message, tag = "5")] MakeProposal(MakeProposalResponse), - #[prost(message, tag = "6")] RegisterVote(RegisterVoteResponse), - #[prost(message, tag = "7")] Split(SplitResponse), - #[prost(message, tag = "8")] ClaimOrRefresh(ClaimOrRefreshResponse), - #[prost(message, tag = "9")] MergeMaturity(MergeMaturityResponse), - #[prost(message, tag = "10")] DisburseMaturity(DisburseMaturityResponse), - #[prost(message, tag = "11")] AddNeuronPermission(AddNeuronPermissionsResponse), - #[prost(message, tag = "12")] RemoveNeuronPermission(RemoveNeuronPermissionsResponse), - #[prost(message, tag = "13")] StakeMaturity(StakeMaturityResponse), } } /// An operation that attempts to get a neuron by a given neuron ID. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct GetNeuron { - #[prost(message, optional, tag = "1")] - pub neuron_id: ::core::option::Option, + pub neuron_id: Option, } /// A response to the GetNeuron command. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct GetNeuronResponse { /// The response to a GetNeuron command is either an error or /// the requested neuron. - #[prost(oneof = "get_neuron_response::Result", tags = "1, 2")] - pub result: ::core::option::Option, + pub result: Option, } /// Nested message and enum types in `GetNeuronResponse`. pub mod get_neuron_response { /// The response to a GetNeuron command is either an error or /// the requested neuron. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Oneof, - )] + #[derive(candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub enum Result { - #[prost(message, tag = "1")] Error(super::GovernanceError), - #[prost(message, tag = "2")] Neuron(super::Neuron), } } /// An operation that attempts to get a proposal by a given proposal ID. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct GetProposal { - #[prost(message, optional, tag = "1")] - pub proposal_id: ::core::option::Option, + pub proposal_id: Option, } /// A response to the GetProposal command. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct GetProposalResponse { /// The response to a GetProposal command is either an error or /// the proposal data corresponding to the requested proposal. - #[prost(oneof = "get_proposal_response::Result", tags = "1, 2")] - pub result: ::core::option::Option, + pub result: Option, } /// Nested message and enum types in `GetProposalResponse`. pub mod get_proposal_response { /// The response to a GetProposal command is either an error or /// the proposal data corresponding to the requested proposal. - #[derive(candid::CandidType, candid::Deserialize, comparable::Comparable)] + #[derive(candid::CandidType, candid::Deserialize, Debug)] #[allow(clippy::large_enum_variant)] - #[derive(Clone, PartialEq, ::prost::Oneof)] + #[derive(Clone, PartialEq)] pub enum Result { - #[prost(message, tag = "1")] Error(super::GovernanceError), - #[prost(message, tag = "2")] Proposal(super::ProposalData), } } @@ -3037,18 +2009,10 @@ pub mod get_proposal_response { /// Proposals are stored using an increasing id where the most recent proposals /// have the highest ids. ListProposals reverses the list and paginates backwards /// using `before_proposal`, so the first element returned is the latest proposal. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct ListProposals { /// Limit the number of Proposals returned in each page, from 1 to 100. /// If a value outside of this range is provided, 100 will be used. - #[prost(uint32, tag = "1")] pub limit: u32, /// The proposal ID specifying which proposals to return. /// This should be set to the last proposal of the previously returned page and @@ -3056,12 +2020,10 @@ pub struct ListProposals { /// If this is specified, then only the proposals that have a proposal ID strictly /// lower than the specified one are returned. If this is not specified /// then the list of proposals starts with the most recent proposal's ID. - #[prost(message, optional, tag = "2")] - pub before_proposal: ::core::option::Option, + pub before_proposal: Option, /// A list of proposal types, specifying that proposals of the given /// types should be excluded in this list. - #[prost(uint64, repeated, tag = "3")] - pub exclude_type: ::prost::alloc::vec::Vec, + pub exclude_type: Vec, /// A list of proposal reward statuses, specifying that only proposals that /// that have one of the define reward statuses should be included /// in the list. @@ -3070,48 +2032,29 @@ pub struct ListProposals { /// Example: If users are only interested in proposals for which they can /// receive voting rewards they can use this to filter for proposals /// with reward status PROPOSAL_REWARD_STATUS_ACCEPT_VOTES. - #[prost(enumeration = "ProposalRewardStatus", repeated, tag = "4")] - pub include_reward_status: ::prost::alloc::vec::Vec, + pub include_reward_status: Vec, /// A list of proposal decision statuses, specifying that only proposals that /// that have one of the define decision statuses should be included /// in the list. /// If this list is empty, no restriction is applied. - #[prost(enumeration = "ProposalDecisionStatus", repeated, tag = "5")] - pub include_status: ::prost::alloc::vec::Vec, + pub include_status: Vec, } /// A response to the ListProposals command. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct ListProposalsResponse { /// The returned list of proposals' ProposalData. - #[prost(message, repeated, tag = "1")] - pub proposals: ::prost::alloc::vec::Vec, + pub proposals: Vec, /// Whether ballots cast by the caller are included in the returned proposals. - #[prost(bool, optional, tag = "2")] - pub include_ballots_by_caller: ::core::option::Option, + pub include_ballots_by_caller: Option, } /// An operation that lists all neurons tracked in the Governance state in a /// paginated fashion. /// Listing of all neurons can be accomplished using `limit` and `start_page_at`. /// To only list neurons associated with a given principal, use `of_principal`. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct ListNeurons { /// Limit the number of Neurons returned in each page, from 1 to 100. /// If a value outside of this range is provided, 100 will be used. - #[prost(uint32, tag = "1")] pub limit: u32, /// Used to indicate where the next page of Neurons should start. Should be /// set to the last neuron of the previously returned page and will not be @@ -3119,702 +2062,329 @@ pub struct ListNeurons { /// size limit starting at the "0th" Neuron. Neurons are not kept in any specific /// order, but their ordering is deterministic, so this can be used to return all /// the neurons one page at a time. - #[prost(message, optional, tag = "2")] - pub start_page_at: ::core::option::Option, + pub start_page_at: Option, /// A principal ID, specifying that only neurons for which this principal has /// any permissions should be included in the list. /// If this is not specified, no restriction is applied. - #[prost(message, optional, tag = "3")] - pub of_principal: ::core::option::Option<::ic_base_types::PrincipalId>, + pub of_principal: Option<::ic_base_types::PrincipalId>, } /// A response to the ListNeurons command. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct ListNeuronsResponse { /// The returned list of neurons. - #[prost(message, repeated, tag = "1")] - pub neurons: ::prost::alloc::vec::Vec, + pub neurons: Vec, } /// The response to the list_nervous_system_functions query. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct ListNervousSystemFunctionsResponse { /// Current set of nervous system function, both native and user-defined, /// that can be executed by proposal. - #[prost(message, repeated, tag = "1")] - pub functions: ::prost::alloc::vec::Vec, + pub functions: Vec, /// Set of nervous system function ids that are reserved and cannot be /// used to add new NervousSystemFunctions. - #[prost(uint64, repeated, tag = "2")] - pub reserved_ids: ::prost::alloc::vec::Vec, + pub reserved_ids: Vec, } -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct SetMode { - #[prost(enumeration = "governance::Mode", tag = "1")] pub mode: i32, } -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct SetModeResponse {} -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct GetMode {} -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct GetModeResponse { - #[prost(enumeration = "governance::Mode", optional, tag = "1")] - pub mode: ::core::option::Option, + pub mode: Option, } /// The request for the `claim_swap_neurons` method. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct ClaimSwapNeuronsRequest { /// The set of parameters that define the neurons created in `claim_swap_neurons`. For /// each NeuronRecipe, one neuron will be created. - #[prost(message, optional, tag = "2")] - pub neuron_recipes: ::core::option::Option, + pub neuron_recipes: Option, } /// Nested message and enum types in `ClaimSwapNeuronsRequest`. pub mod claim_swap_neurons_request { /// Replacement for NeuronParameters. Contains the information needed to set up /// a neuron for a swap participant. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct NeuronRecipe { /// The principal that should be the controller of the SNS neuron - #[prost(message, optional, tag = "1")] - pub controller: ::core::option::Option<::ic_base_types::PrincipalId>, + pub controller: Option<::ic_base_types::PrincipalId>, /// The ID of the SNS neuron - #[prost(message, optional, tag = "2")] - pub neuron_id: ::core::option::Option, + pub neuron_id: Option, /// The SNS neuron's stake in e8s (10E-8 of a token) - #[prost(uint64, optional, tag = "3")] - pub stake_e8s: ::core::option::Option, + pub stake_e8s: Option, /// The duration in seconds that the neuron's dissolve delay will be set to. - #[prost(uint64, optional, tag = "4")] - pub dissolve_delay_seconds: ::core::option::Option, + pub dissolve_delay_seconds: Option, /// The neurons this neuron should follow - #[prost(message, optional, tag = "5")] - pub followees: ::core::option::Option, - #[prost(oneof = "neuron_recipe::Participant", tags = "6, 7")] - pub participant: ::core::option::Option, + pub followees: Option, + pub participant: Option, } /// Nested message and enum types in `NeuronRecipe`. pub mod neuron_recipe { /// The info that for a participant in the Neurons' Fund - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct NeuronsFund { /// The neuron ID of the NNS neuron that participated in the Neurons' Fund. - #[prost(uint64, optional, tag = "1")] - pub nns_neuron_id: ::core::option::Option, + pub nns_neuron_id: Option, /// The controller of the NNS neuron that participated in the Neurons' Fund. - #[prost(message, optional, tag = "2")] - pub nns_neuron_controller: ::core::option::Option<::ic_base_types::PrincipalId>, + pub nns_neuron_controller: Option<::ic_base_types::PrincipalId>, /// The hotkeys of the NNS neuron that participated in the Neurons' Fund. - #[prost(message, optional, tag = "3")] - pub nns_neuron_hotkeys: - ::core::option::Option<::ic_nervous_system_proto::pb::v1::Principals>, + pub nns_neuron_hotkeys: Option<::ic_nervous_system_proto::pb::v1::Principals>, } /// The info that for a direct participant #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, + Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq, )] pub struct Direct {} - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Oneof, - )] + #[derive(candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub enum Participant { - #[prost(message, tag = "6")] Direct(Direct), - #[prost(message, tag = "7")] NeuronsFund(NeuronsFund), } } /// Needed to cause prost to generate a type isomorphic to /// Optional>. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct NeuronRecipes { - #[prost(message, repeated, tag = "1")] - pub neuron_recipes: ::prost::alloc::vec::Vec, + pub neuron_recipes: Vec, } } /// The response for the `claim_swap_neurons` method. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct ClaimSwapNeuronsResponse { /// ClaimSwapNeurons will either return an error, in which /// no requested neurons were claimed, or a vector with /// various neuron statuses for the requested neuron ids. - #[prost( - oneof = "claim_swap_neurons_response::ClaimSwapNeuronsResult", - tags = "4, 5" - )] - pub claim_swap_neurons_result: - ::core::option::Option, + pub claim_swap_neurons_result: Option, } /// Nested message and enum types in `ClaimSwapNeuronsResponse`. pub mod claim_swap_neurons_response { /// The ok result from `claim_swap_neurons. For every requested neuron, /// a SwapNeuron message is returned, and should equal the count of /// `ClaimSwapNeuronsRequest.neuron_recipes`. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct ClaimedSwapNeurons { - #[prost(message, repeated, tag = "1")] - pub swap_neurons: ::prost::alloc::vec::Vec, + pub swap_neurons: Vec, } /// SwapNeuron associates the status of a neuron attempting to be /// claimed with a NeuronId. The `id` field will correspond with a /// `ClaimSwapNeuronsRequest.neuron_recipes.neuron_id` field in /// the request object used in `claim_swap_neurons`. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, - )] + #[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct SwapNeuron { - #[prost(message, optional, tag = "1")] - pub id: ::core::option::Option, + pub id: Option, /// The status of claiming of a requested Sale neuron. - #[prost(enumeration = "super::ClaimedSwapNeuronStatus", tag = "2")] pub status: i32, } /// ClaimSwapNeurons will either return an error, in which /// no requested neurons were claimed, or a vector with /// various neuron statuses for the requested neuron ids. - #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Oneof, - )] + #[derive(candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub enum ClaimSwapNeuronsResult { - #[prost(message, tag = "4")] Ok(ClaimedSwapNeurons), - #[prost(enumeration = "super::ClaimSwapNeuronsError", tag = "5")] Err(i32), } } -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct GetMaturityModulationRequest {} -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct GetMaturityModulationResponse { - #[prost(message, optional, tag = "1")] - pub maturity_modulation: ::core::option::Option, + pub maturity_modulation: Option, } /// A request to add maturity to a neuron. The associated endpoint is only /// available when governance is compiled with the `test` feature enabled. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct AddMaturityRequest { - #[prost(message, optional, tag = "1")] - pub id: ::core::option::Option, - #[prost(uint64, optional, tag = "2")] - pub amount_e8s: ::core::option::Option, + pub id: Option, + pub amount_e8s: Option, } /// The response to a request to add maturity to a neuron. The associated endpoint is only /// available when governance is compiled with the `test` feature enabled. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct AddMaturityResponse { - #[prost(uint64, optional, tag = "1")] - pub new_maturity_e8s: ::core::option::Option, + pub new_maturity_e8s: Option, } /// A test-only API that advances the target version of the SNS. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct AdvanceTargetVersionRequest { - #[prost(message, optional, tag = "1")] - pub target_version: ::core::option::Option, + pub target_version: Option, } /// The response to a request to advance the target version of the SNS. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct AdvanceTargetVersionResponse {} /// A test-only API that refreshes the cached upgrade steps. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct RefreshCachedUpgradeStepsRequest {} /// The response to a request to refresh the cached upgrade steps. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct RefreshCachedUpgradeStepsResponse {} /// Represents a single entry in the upgrade journal. #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - serde::Serialize, - Clone, - PartialEq, - ::prost::Message, + Default, candid::CandidType, candid::Deserialize, Debug, serde::Serialize, Clone, PartialEq, )] pub struct UpgradeJournalEntry { - #[prost(uint64, optional, tag = "6")] - pub timestamp_seconds: ::core::option::Option, - #[prost(oneof = "upgrade_journal_entry::Event", tags = "1, 7, 2, 3, 4, 5")] - pub event: ::core::option::Option, + pub timestamp_seconds: Option, + pub event: Option, } /// Nested message and enum types in `UpgradeJournalEntry`. pub mod upgrade_journal_entry { #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - serde::Serialize, - Clone, - PartialEq, - ::prost::Message, + candid::CandidType, candid::Deserialize, Debug, serde::Serialize, Clone, PartialEq, )] pub struct UpgradeStepsRefreshed { - #[prost(message, optional, tag = "2")] - pub upgrade_steps: ::core::option::Option, + pub upgrade_steps: Option, } #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - serde::Serialize, - Clone, - PartialEq, - ::prost::Message, + candid::CandidType, candid::Deserialize, Debug, serde::Serialize, Clone, PartialEq, )] pub struct UpgradeStepsReset { - #[prost(string, optional, tag = "1")] - pub human_readable: ::core::option::Option<::prost::alloc::string::String>, - #[prost(message, optional, tag = "2")] - pub upgrade_steps: ::core::option::Option, + pub human_readable: Option, + pub upgrade_steps: Option, } #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - serde::Serialize, - Clone, - PartialEq, - ::prost::Message, + candid::CandidType, candid::Deserialize, Debug, serde::Serialize, Clone, PartialEq, )] pub struct TargetVersionSet { - #[prost(message, optional, tag = "1")] - pub old_target_version: ::core::option::Option, - #[prost(message, optional, tag = "2")] - pub new_target_version: ::core::option::Option, + pub old_target_version: Option, + pub new_target_version: Option, + pub is_advanced_automatically: Option, } #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - serde::Serialize, - Clone, - PartialEq, - ::prost::Message, + candid::CandidType, candid::Deserialize, Debug, serde::Serialize, Clone, PartialEq, )] pub struct TargetVersionReset { - #[prost(message, optional, tag = "1")] - pub old_target_version: ::core::option::Option, - #[prost(message, optional, tag = "2")] - pub new_target_version: ::core::option::Option, - #[prost(string, optional, tag = "3")] - pub human_readable: ::core::option::Option<::prost::alloc::string::String>, + pub old_target_version: Option, + pub new_target_version: Option, + pub human_readable: Option, } #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - serde::Serialize, - Clone, - PartialEq, - ::prost::Message, + candid::CandidType, candid::Deserialize, Debug, serde::Serialize, Clone, PartialEq, )] pub struct UpgradeStarted { - #[prost(message, optional, tag = "1")] - pub current_version: ::core::option::Option, - #[prost(message, optional, tag = "2")] - pub expected_version: ::core::option::Option, - #[prost(oneof = "upgrade_started::Reason", tags = "3, 4")] - pub reason: ::core::option::Option, + pub current_version: Option, + pub expected_version: Option, + pub reason: Option, } /// Nested message and enum types in `UpgradeStarted`. pub mod upgrade_started { #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - serde::Serialize, - Clone, - Copy, - PartialEq, - ::prost::Oneof, + candid::CandidType, candid::Deserialize, Debug, serde::Serialize, Clone, Copy, PartialEq, )] pub enum Reason { - #[prost(message, tag = "3")] UpgradeSnsToNextVersionProposal(super::super::ProposalId), - #[prost(message, tag = "4")] BehindTargetVersion(super::super::Empty), } } #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - serde::Serialize, - Clone, - PartialEq, - ::prost::Message, + candid::CandidType, candid::Deserialize, Debug, serde::Serialize, Clone, PartialEq, )] pub struct UpgradeOutcome { - #[prost(string, optional, tag = "1")] - pub human_readable: ::core::option::Option<::prost::alloc::string::String>, - #[prost(oneof = "upgrade_outcome::Status", tags = "2, 3, 4, 5")] - pub status: ::core::option::Option, + pub human_readable: Option, + pub status: Option, } /// Nested message and enum types in `UpgradeOutcome`. pub mod upgrade_outcome { #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - serde::Serialize, - Clone, - PartialEq, - ::prost::Message, + candid::CandidType, candid::Deserialize, Debug, serde::Serialize, Clone, PartialEq, )] pub struct InvalidState { - #[prost(message, optional, tag = "1")] - pub version: ::core::option::Option, + pub version: Option, } #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - serde::Serialize, - Clone, - PartialEq, - ::prost::Oneof, + candid::CandidType, candid::Deserialize, Debug, serde::Serialize, Clone, PartialEq, )] pub enum Status { - #[prost(message, tag = "2")] Success(super::super::Empty), - #[prost(message, tag = "3")] Timeout(super::super::Empty), /// The SNS ended up being upgraded to a version that was not the expected one. - #[prost(message, tag = "4")] InvalidState(InvalidState), - #[prost(message, tag = "5")] ExternalFailure(super::super::Empty), } } #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - serde::Serialize, - Clone, - PartialEq, - ::prost::Oneof, + candid::CandidType, candid::Deserialize, Debug, serde::Serialize, Clone, PartialEq, )] pub enum Event { - #[prost(message, tag = "1")] UpgradeStepsRefreshed(UpgradeStepsRefreshed), - #[prost(message, tag = "7")] UpgradeStepsReset(UpgradeStepsReset), - #[prost(message, tag = "2")] TargetVersionSet(TargetVersionSet), - #[prost(message, tag = "3")] TargetVersionReset(TargetVersionReset), - #[prost(message, tag = "4")] UpgradeStarted(UpgradeStarted), - #[prost(message, tag = "5")] UpgradeOutcome(UpgradeOutcome), } } /// Needed to cause prost to generate a type isomorphic to Option>. #[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - serde::Serialize, - Clone, - PartialEq, - ::prost::Message, + Default, candid::CandidType, candid::Deserialize, Debug, serde::Serialize, Clone, PartialEq, )] pub struct UpgradeJournal { /// The entries in the upgrade journal. - #[prost(message, repeated, tag = "1")] - pub entries: ::prost::alloc::vec::Vec, + pub entries: Vec, } /// The upgrade journal contains all the information neede to audit previous SNS upgrades and understand its current state. /// It is being implemented as part of the "effortless SNS upgrade" feature. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct GetUpgradeJournalRequest { /// Maximum number of journal entries to return. /// If not specified, defaults to 100. Values larger than 100 will be capped at 100. - #[prost(uint64, optional, tag = "1")] - pub limit: ::core::option::Option, + pub limit: Option, /// The starting index from which to return entries, counting from the oldest entry (0). /// If not specified, return the most recent entries. - #[prost(uint64, optional, tag = "2")] - pub offset: ::core::option::Option, + pub offset: Option, } -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct GetUpgradeJournalResponse { - #[prost(message, optional, tag = "1")] - pub upgrade_steps: ::core::option::Option, - #[prost(uint64, optional, tag = "2")] - pub response_timestamp_seconds: ::core::option::Option, + pub upgrade_steps: Option, + pub response_timestamp_seconds: Option, /// The target version that the SNS will be upgraded to. /// Currently, this field is always None, but in the "effortless SNS upgrade" /// feature, it reflect the version of the SNS that the community has decided to upgrade to. - #[prost(message, optional, tag = "3")] - pub target_version: ::core::option::Option, - #[prost(message, optional, tag = "5")] - pub deployed_version: ::core::option::Option, - #[prost(message, optional, tag = "4")] - pub upgrade_journal: ::core::option::Option, - #[prost(uint64, optional, tag = "6")] - pub upgrade_journal_entry_count: ::core::option::Option, + pub target_version: Option, + pub deployed_version: Option, + pub upgrade_journal: Option, + pub upgrade_journal_entry_count: Option, } /// A request to mint tokens for a particular principal. The associated endpoint /// is only available on SNS governance, and only then when SNS governance is /// compiled with the `test` feature enabled. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct MintTokensRequest { - #[prost(message, optional, tag = "1")] - pub recipient: ::core::option::Option, - #[prost(uint64, optional, tag = "2")] - pub amount_e8s: ::core::option::Option, + pub recipient: Option, + pub amount_e8s: Option, } /// The response to a request to mint tokens for a particular principal. The /// associated endpoint is only available on SNS governance, and only then when /// SNS governance is compiled with the `test` feature enabled. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - Copy, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, Copy, PartialEq)] pub struct MintTokensResponse {} /// A Ledger subaccount. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct Subaccount { - #[prost(bytes = "vec", tag = "1")] #[serde(with = "serde_bytes")] - pub subaccount: ::prost::alloc::vec::Vec, + pub subaccount: Vec, } /// A Ledger account identified by the owner of the account `of` and /// the `subaccount`. If the `subaccount` is not specified then the default /// one is used. -#[derive( - candid::CandidType, - candid::Deserialize, - comparable::Comparable, - Clone, - PartialEq, - ::prost::Message, -)] +#[derive(Default, candid::CandidType, candid::Deserialize, Debug, Clone, PartialEq)] pub struct Account { /// The owner of the account. - #[prost(message, optional, tag = "1")] - pub owner: ::core::option::Option<::ic_base_types::PrincipalId>, + pub owner: Option<::ic_base_types::PrincipalId>, /// The subaccount of the account. If not set then the default /// subaccount (all bytes set to 0) is used. - #[prost(message, optional, tag = "2")] - pub subaccount: ::core::option::Option, + pub subaccount: Option, } /// The different types of neuron permissions, i.e., privileges to modify a neuron, /// that principals can have. #[derive( candid::CandidType, candid::Deserialize, - comparable::Comparable, + Debug, clap::ValueEnum, - strum_macros::EnumIter, Clone, Copy, - Debug, PartialEq, Eq, Hash, @@ -3877,7 +2447,7 @@ impl NeuronPermissionType { } } /// Creates an enum from field names used in the ProtoBuf definition. - pub fn from_str_name(value: &str) -> ::core::option::Option { + pub fn from_str_name(value: &str) -> Option { match value { "NEURON_PERMISSION_TYPE_UNSPECIFIED" => Some(Self::Unspecified), "NEURON_PERMISSION_TYPE_CONFIGURE_DISSOLVE_STATE" => Some(Self::ConfigureDissolveState), @@ -3898,16 +2468,14 @@ impl NeuronPermissionType { #[derive( candid::CandidType, candid::Deserialize, - comparable::Comparable, + Debug, Clone, Copy, - Debug, PartialEq, Eq, Hash, PartialOrd, Ord, - ::prost::Enumeration, )] #[repr(i32)] pub enum Vote { @@ -3933,7 +2501,7 @@ impl Vote { } } /// Creates an enum from field names used in the ProtoBuf definition. - pub fn from_str_name(value: &str) -> ::core::option::Option { + pub fn from_str_name(value: &str) -> Option { match value { "VOTE_UNSPECIFIED" => Some(Self::Unspecified), "VOTE_YES" => Some(Self::Yes), @@ -3945,16 +2513,14 @@ impl Vote { #[derive( candid::CandidType, candid::Deserialize, - comparable::Comparable, + Debug, Clone, Copy, - Debug, PartialEq, Eq, Hash, PartialOrd, Ord, - ::prost::Enumeration, )] #[repr(i32)] pub enum LogVisibility { @@ -3977,7 +2543,7 @@ impl LogVisibility { } } /// Creates an enum from field names used in the ProtoBuf definition. - pub fn from_str_name(value: &str) -> ::core::option::Option { + pub fn from_str_name(value: &str) -> Option { match value { "LOG_VISIBILITY_UNSPECIFIED" => Some(Self::Unspecified), "LOG_VISIBILITY_CONTROLLERS" => Some(Self::Controllers), @@ -3989,16 +2555,14 @@ impl LogVisibility { #[derive( candid::CandidType, candid::Deserialize, - comparable::Comparable, + Debug, Clone, Copy, - Debug, PartialEq, Eq, Hash, PartialOrd, Ord, - ::prost::Enumeration, )] #[repr(i32)] pub enum ProposalDecisionStatus { @@ -4031,7 +2595,7 @@ impl ProposalDecisionStatus { } } /// Creates an enum from field names used in the ProtoBuf definition. - pub fn from_str_name(value: &str) -> ::core::option::Option { + pub fn from_str_name(value: &str) -> Option { match value { "PROPOSAL_DECISION_STATUS_UNSPECIFIED" => Some(Self::Unspecified), "PROPOSAL_DECISION_STATUS_OPEN" => Some(Self::Open), @@ -4047,16 +2611,14 @@ impl ProposalDecisionStatus { #[derive( candid::CandidType, candid::Deserialize, - comparable::Comparable, + Debug, Clone, Copy, - Debug, PartialEq, Eq, Hash, PartialOrd, Ord, - ::prost::Enumeration, )] #[repr(i32)] pub enum ProposalRewardStatus { @@ -4088,7 +2650,7 @@ impl ProposalRewardStatus { } } /// Creates an enum from field names used in the ProtoBuf definition. - pub fn from_str_name(value: &str) -> ::core::option::Option { + pub fn from_str_name(value: &str) -> Option { match value { "PROPOSAL_REWARD_STATUS_UNSPECIFIED" => Some(Self::Unspecified), "PROPOSAL_REWARD_STATUS_ACCEPT_VOTES" => Some(Self::AcceptVotes), @@ -4105,16 +2667,14 @@ impl ProposalRewardStatus { #[derive( candid::CandidType, candid::Deserialize, - comparable::Comparable, + Debug, Clone, Copy, - Debug, PartialEq, Eq, Hash, PartialOrd, Ord, - ::prost::Enumeration, )] #[repr(i32)] pub enum ClaimedSwapNeuronStatus { @@ -4154,7 +2714,7 @@ impl ClaimedSwapNeuronStatus { } } /// Creates an enum from field names used in the ProtoBuf definition. - pub fn from_str_name(value: &str) -> ::core::option::Option { + pub fn from_str_name(value: &str) -> Option { match value { "CLAIMED_SWAP_NEURON_STATUS_UNSPECIFIED" => Some(Self::Unspecified), "CLAIMED_SWAP_NEURON_STATUS_SUCCESS" => Some(Self::Success), @@ -4170,16 +2730,14 @@ impl ClaimedSwapNeuronStatus { #[derive( candid::CandidType, candid::Deserialize, - comparable::Comparable, + Debug, Clone, Copy, - Debug, PartialEq, Eq, Hash, PartialOrd, Ord, - ::prost::Enumeration, )] #[repr(i32)] pub enum ClaimSwapNeuronsError { @@ -4206,7 +2764,7 @@ impl ClaimSwapNeuronsError { } } /// Creates an enum from field names used in the ProtoBuf definition. - pub fn from_str_name(value: &str) -> ::core::option::Option { + pub fn from_str_name(value: &str) -> Option { match value { "CLAIM_SWAP_NEURONS_ERROR_UNSPECIFIED" => Some(Self::Unspecified), "CLAIM_SWAP_NEURONS_ERROR_UNAUTHORIZED" => Some(Self::Unauthorized), diff --git a/rs/sns/governance/api/src/lib.rs b/rs/sns/governance/api/src/lib.rs index 4b2634f4717..ff6e8691092 100644 --- a/rs/sns/governance/api/src/lib.rs +++ b/rs/sns/governance/api/src/lib.rs @@ -1,4 +1,5 @@ pub mod pb; +mod types; /// Formats the 32 bytes of a hash as a hexadecimal string. Corresponds to 64 ascii symbols. pub fn format_full_hash(hash: &[u8]) -> String { diff --git a/rs/sns/governance/api/src/types.rs b/rs/sns/governance/api/src/types.rs new file mode 100644 index 00000000000..c58bc3b5c9f --- /dev/null +++ b/rs/sns/governance/api/src/types.rs @@ -0,0 +1,9 @@ +use std::fmt; + +impl fmt::Display for crate::pb::v1::GovernanceError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}: {}", self.error_type, self.error_message) + } +} + +impl std::error::Error for crate::pb::v1::GovernanceError {} diff --git a/rs/sns/governance/canister/canister.rs b/rs/sns/governance/canister/canister.rs index 6b7755c17f0..6c883c86b6e 100644 --- a/rs/sns/governance/canister/canister.rs +++ b/rs/sns/governance/canister/canister.rs @@ -1,3 +1,6 @@ +// TODO: Jira ticket NNS1-3556 +#![allow(static_mut_refs)] + use async_trait::async_trait; use ic_base_types::{CanisterId, PrincipalId}; use ic_canister_log::log; @@ -11,7 +14,7 @@ use ic_nervous_system_clients::{ }; use ic_nervous_system_common::{ dfn_core_stable_mem_utils::{BufferedStableMemReader, BufferedStableMemWriter}, - serve_journal, serve_logs, serve_logs_v2, serve_metrics, + serve_logs, serve_logs_v2, serve_metrics, }; use ic_nervous_system_proto::pb::v1::{ GetTimersRequest, GetTimersResponse, ResetTimersRequest, ResetTimersResponse, Timers, @@ -25,6 +28,20 @@ use ic_sns_governance::{ logs::{ERROR, INFO}, pb::v1 as sns_gov_pb, types::{Environment, HeapGrowthPotential}, + upgrade_journal::serve_journal, +}; +use ic_sns_governance_api::pb::v1::{ + get_running_sns_version_response::UpgradeInProgress, governance::Version, + ClaimSwapNeuronsRequest, ClaimSwapNeuronsResponse, FailStuckUpgradeInProgressRequest, + FailStuckUpgradeInProgressResponse, GetMaturityModulationRequest, + GetMaturityModulationResponse, GetMetadataRequest, GetMetadataResponse, GetMode, + GetModeResponse, GetNeuron, GetNeuronResponse, GetProposal, GetProposalResponse, + GetRunningSnsVersionRequest, GetRunningSnsVersionResponse, + GetSnsInitializationParametersRequest, GetSnsInitializationParametersResponse, + GetUpgradeJournalRequest, GetUpgradeJournalResponse, Governance as GovernanceApi, + ListNervousSystemFunctionsResponse, ListNeurons, ListNeuronsResponse, ListProposals, + ListProposalsResponse, ManageNeuron, ManageNeuronResponse, NervousSystemParameters, + RewardEvent, SetMode, SetModeResponse, }; #[cfg(feature = "test")] use ic_sns_governance_api::pb::v1::{ @@ -32,22 +49,6 @@ use ic_sns_governance_api::pb::v1::{ AdvanceTargetVersionResponse, GovernanceError, MintTokensRequest, MintTokensResponse, Neuron, RefreshCachedUpgradeStepsRequest, RefreshCachedUpgradeStepsResponse, }; -use ic_sns_governance_api::pb::{ - v1 as api, - v1::{ - get_running_sns_version_response::UpgradeInProgress, governance::Version, - ClaimSwapNeuronsRequest, ClaimSwapNeuronsResponse, FailStuckUpgradeInProgressRequest, - FailStuckUpgradeInProgressResponse, GetMaturityModulationRequest, - GetMaturityModulationResponse, GetMetadataRequest, GetMetadataResponse, GetMode, - GetModeResponse, GetNeuron, GetNeuronResponse, GetProposal, GetProposalResponse, - GetRunningSnsVersionRequest, GetRunningSnsVersionResponse, - GetSnsInitializationParametersRequest, GetSnsInitializationParametersResponse, - GetUpgradeJournalRequest, GetUpgradeJournalResponse, Governance as GovernanceProto, - ListNervousSystemFunctionsResponse, ListNeurons, ListNeuronsResponse, ListProposals, - ListProposalsResponse, ManageNeuron, ManageNeuronResponse, NervousSystemParameters, - RewardEvent, SetMode, SetModeResponse, - }, -}; use prost::Message; use rand::{RngCore, SeedableRng}; use rand_chacha::ChaCha20Rng; @@ -210,11 +211,13 @@ fn caller() -> PrincipalId { PrincipalId::from(cdk_caller()) } -/// In contrast to canister_init(), this method does not do deserialization. -/// In addition to canister_init, this method is called by canister_post_upgrade. #[init] -fn canister_init_(init_payload: GovernanceProto) { +fn canister_init(init_payload: GovernanceApi) { let init_payload = sns_gov_pb::Governance::from(init_payload); + canister_init_(init_payload); +} + +fn canister_init_(init_payload: sns_gov_pb::Governance) { let init_payload = ValidGovernanceProto::try_from(init_payload).expect( "Cannot start canister, because the deserialized \ GovernanceProto is invalid in some way", @@ -279,7 +282,7 @@ fn canister_post_upgrade() { let reader = BufferedStableMemReader::new(STABLE_MEM_BUFFER_SIZE); - match GovernanceProto::decode(reader) { + match sns_gov_pb::Governance::decode(reader) { Err(err) => { log!( ERROR, @@ -306,7 +309,7 @@ fn canister_post_upgrade() { log!(INFO, "Completed post upgrade"); } -fn populate_finalize_disbursement_timestamp_seconds(governance_proto: &mut GovernanceProto) { +fn populate_finalize_disbursement_timestamp_seconds(governance_proto: &mut sns_gov_pb::Governance) { for neuron in governance_proto.neurons.values_mut() { for disbursement in neuron.disburse_maturity_in_progress.iter_mut() { disbursement.finalize_disbursement_timestamp_seconds = Some( @@ -646,9 +649,7 @@ pub fn http_request(request: HttpRequest) -> HttpResponse { .clone() .expect("The upgrade journal is not initialized for this SNS."); - let journal = api::UpgradeJournal::from(journal); - - serve_journal(&journal.entries) + serve_journal(&journal) } "/metrics" => serve_metrics(encode_metrics), "/logs" => serve_logs_v2(request, &INFO, &ERROR), diff --git a/rs/sns/governance/canister/governance.did b/rs/sns/governance/canister/governance.did index b379d8687eb..265c627ce8e 100644 --- a/rs/sns/governance/canister/governance.did +++ b/rs/sns/governance/canister/governance.did @@ -481,6 +481,7 @@ type NervousSystemParameters = record { voting_rewards_parameters : opt VotingRewardsParameters; maturity_modulation_disabled : opt bool; max_number_of_principals_per_neuron : opt nat64; + automatically_advance_target_version : opt bool; }; type Neuron = record { @@ -711,8 +712,15 @@ type PendingVersion = record { target_version : opt Version; }; +type ChunkedCanisterWasm = record { + wasm_module_hash : blob; + store_canister_id : opt principal; + chunk_hashes_list : vec blob; +}; + type UpgradeSnsControlledCanister = record { new_canister_wasm : blob; + chunked_canister_wasm : opt ChunkedCanisterWasm; mode : opt int32; canister_id : opt principal; canister_upgrade_arg : opt blob; @@ -777,6 +785,7 @@ type UpgradeStepsReset = record { type TargetVersionSet = record { new_target_version : opt Version; old_target_version : opt Version; + is_advanced_automatically : opt bool; }; type TargetVersionReset = record { diff --git a/rs/sns/governance/canister/governance_test.did b/rs/sns/governance/canister/governance_test.did index 4ba441aabda..5175adcc646 100644 --- a/rs/sns/governance/canister/governance_test.did +++ b/rs/sns/governance/canister/governance_test.did @@ -495,6 +495,7 @@ type NervousSystemParameters = record { voting_rewards_parameters : opt VotingRewardsParameters; maturity_modulation_disabled : opt bool; max_number_of_principals_per_neuron : opt nat64; + automatically_advance_target_version : opt bool; }; type Neuron = record { @@ -725,8 +726,15 @@ type PendingVersion = record { target_version : opt Version; }; +type ChunkedCanisterWasm = record { + wasm_module_hash : blob; + store_canister_id : opt principal; + chunk_hashes_list : vec blob; +}; + type UpgradeSnsControlledCanister = record { new_canister_wasm : blob; + chunked_canister_wasm : opt ChunkedCanisterWasm; mode : opt int32; canister_id : opt principal; canister_upgrade_arg : opt blob; @@ -791,6 +799,7 @@ type UpgradeStepsReset = record { type TargetVersionSet = record { new_target_version : opt Version; old_target_version : opt Version; + is_advanced_automatically : opt bool; }; type TargetVersionReset = record { diff --git a/rs/sns/governance/canister/tests.rs b/rs/sns/governance/canister/tests.rs index d0ed0b1ee29..496348898b1 100644 --- a/rs/sns/governance/canister/tests.rs +++ b/rs/sns/governance/canister/tests.rs @@ -1,7 +1,7 @@ use super::*; use assert_matches::assert_matches; use candid_parser::utils::{service_equal, CandidSource}; -use ic_sns_governance_api::pb::v1::{ +use ic_sns_governance::pb::v1::{ governance::{Version, Versions}, upgrade_journal_entry::{Event, UpgradeStepsRefreshed}, DisburseMaturityInProgress, Neuron, UpgradeJournal, UpgradeJournalEntry, @@ -65,7 +65,7 @@ fn test_set_time_warp() { fn test_populate_finalize_disbursement_timestamp_seconds() { // Step 1: prepare a neuron with 2 in progress disbursement, one with // finalize_disbursement_timestamp_seconds as None, and the other has incorrect timestamp. - let mut governance_proto = GovernanceProto { + let mut governance_proto = sns_gov_pb::Governance { neurons: btreemap! { "1".to_string() => Neuron { disburse_maturity_in_progress: vec![ @@ -90,7 +90,7 @@ fn test_populate_finalize_disbursement_timestamp_seconds() { populate_finalize_disbursement_timestamp_seconds(&mut governance_proto); // Step 3: verifies that both disbursements have the correct finalization timestamps. - let expected_governance_proto = GovernanceProto { + let expected_governance_proto = sns_gov_pb::Governance { neurons: btreemap! { "1".to_string() => Neuron { disburse_maturity_in_progress: vec![ @@ -135,7 +135,7 @@ fn test_upgrade_journal() { // Currently, the `/journal` Http endpoint serves the entries directly, rather than the whole // journal object. - let http_response = serve_journal(&journal.entries); + let http_response = serve_journal(&journal); let expected_headers: HashSet<(_, _)> = HashSet::from_iter([ ("Content-Type".to_string(), "application/json".to_string()), ("Content-Length".to_string(), "277".to_string()), diff --git a/rs/sns/governance/proto/ic_sns_governance/pb/v1/governance.proto b/rs/sns/governance/proto/ic_sns_governance/pb/v1/governance.proto index f26318098b2..b1d69965b46 100644 --- a/rs/sns/governance/proto/ic_sns_governance/pb/v1/governance.proto +++ b/rs/sns/governance/proto/ic_sns_governance/pb/v1/governance.proto @@ -312,6 +312,17 @@ message Motion { string motion_text = 1; } +// Represents a WASM split into smaller chunks, each of which can safely be sent around the ICP. +message ChunkedCanisterWasm { + // Obligatory check sum of the overall WASM to be reassembled from chunks. + bytes wasm_module_hash = 1; + // Obligatory; indicates which canister stores the WASM chunks. + ic_base_types.pb.v1.PrincipalId store_canister_id = 2; + // Specifies a list of hash values for the chunks that comprise this WASM. Must contain at least + // one chunk. + repeated bytes chunk_hashes_list = 3; +} + // A proposal function that upgrades a canister that is controlled by the // SNS governance canister. message UpgradeSnsControlledCanister { @@ -323,6 +334,9 @@ message UpgradeSnsControlledCanister { optional bytes canister_upgrade_arg = 3; // Canister install_code mode. optional types.v1.CanisterInstallMode mode = 4; + // If the entire WASM does not fit into the 2 MiB ingress limit, then `new_canister_wasm` should be + // an empty, and this field should be set instead. + optional ChunkedCanisterWasm chunked_canister_wasm = 5; } // A proposal to transfer SNS treasury funds to (optionally a Subaccount of) the @@ -1085,6 +1099,10 @@ message NervousSystemParameters { // that the PB default (bool fields are false) and our application default // (enabled) agree. optional bool maturity_modulation_disabled = 22; + + // Whether to automatically advance the SNS target version after a new upgrade is published + // by the NNS. If not specified, defaults to false for backward compatibility. + optional bool automatically_advance_target_version = 23; } message VotingRewardsParameters { @@ -2230,6 +2248,7 @@ message UpgradeJournalEntry { message TargetVersionSet { optional Governance.Version old_target_version = 1; optional Governance.Version new_target_version = 2; + optional bool is_advanced_automatically = 3; } message TargetVersionReset { diff --git a/rs/sns/governance/src/cached_upgrade_steps.rs b/rs/sns/governance/src/cached_upgrade_steps.rs index e42f7820bbd..9992f7b6583 100644 --- a/rs/sns/governance/src/cached_upgrade_steps.rs +++ b/rs/sns/governance/src/cached_upgrade_steps.rs @@ -476,7 +476,8 @@ impl Governance { true } - /// Refreshes the cached_upgrade_steps field + /// Attempts to refresh the cached_upgrade_steps field and (if this SNS wants automatic + /// deployment of upgrades), also the target_version. pub async fn refresh_cached_upgrade_steps(&mut self, deployed_version: Version) { let sns_governance_canister_id = self.env.canister_id().get(); @@ -495,6 +496,26 @@ impl Governance { } }; + if self.should_automatically_advance_target_version() + && upgrade_steps.has_pending_upgrades() + { + let new_target = upgrade_steps.last().clone(); + + { + let old_version = self.proto.target_version.clone(); + let new_target = new_target.clone(); + if old_version.as_ref() != Some(&new_target) { + self.push_to_upgrade_journal(upgrade_journal_entry::TargetVersionSet::new( + old_version, + new_target, + true, + )); + } + } + + self.proto.target_version.replace(new_target); + } + // This copy of the data would go to the upgrade journal for auditability. let versions = upgrade_steps.clone().into_iter().collect(); diff --git a/rs/sns/governance/src/gen/ic_sns_governance.pb.v1.rs b/rs/sns/governance/src/gen/ic_sns_governance.pb.v1.rs index 543f636048e..6e4e9a77dbd 100644 --- a/rs/sns/governance/src/gen/ic_sns_governance.pb.v1.rs +++ b/rs/sns/governance/src/gen/ic_sns_governance.pb.v1.rs @@ -366,6 +366,27 @@ pub struct Motion { #[prost(string, tag = "1")] pub motion_text: ::prost::alloc::string::String, } +/// Represents a WASM split into smaller chunks, each of which can safely be sent around the ICP. +#[derive( + candid::CandidType, + candid::Deserialize, + comparable::Comparable, + Clone, + PartialEq, + ::prost::Message, +)] +pub struct ChunkedCanisterWasm { + /// Obligatory check sum of the overall WASM to be reassembled from chunks. + #[prost(bytes = "vec", tag = "1")] + pub wasm_module_hash: ::prost::alloc::vec::Vec, + /// Obligatory; indicates which canister stores the WASM chunks. + #[prost(message, optional, tag = "2")] + pub store_canister_id: ::core::option::Option<::ic_base_types::PrincipalId>, + /// Specifies a list of hash values for the chunks that comprise this WASM. Must contain at least + /// one chunk. + #[prost(bytes = "vec", repeated, tag = "3")] + pub chunk_hashes_list: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, +} /// A proposal function that upgrades a canister that is controlled by the /// SNS governance canister. #[derive( @@ -395,6 +416,10 @@ pub struct UpgradeSnsControlledCanister { tag = "4" )] pub mode: ::core::option::Option, + /// If the entire WASM does not fit into the 2 MiB ingress limit, then `new_canister_wasm` should be + /// an empty, and this field should be set instead. + #[prost(message, optional, tag = "5")] + pub chunked_canister_wasm: ::core::option::Option, } /// A proposal to transfer SNS treasury funds to (optionally a Subaccount of) the /// target principal. @@ -1448,6 +1473,10 @@ pub struct NervousSystemParameters { /// (enabled) agree. #[prost(bool, optional, tag = "22")] pub maturity_modulation_disabled: ::core::option::Option, + /// Whether to automatically advance the SNS target version after a new upgrade is published + /// by the NNS. If not specified, defaults to false for backward compatibility. + #[prost(bool, optional, tag = "23")] + pub automatically_advance_target_version: ::core::option::Option, } #[derive( candid::CandidType, @@ -3522,6 +3551,8 @@ pub mod upgrade_journal_entry { pub old_target_version: ::core::option::Option, #[prost(message, optional, tag = "2")] pub new_target_version: ::core::option::Option, + #[prost(bool, optional, tag = "3")] + pub is_advanced_automatically: ::core::option::Option, } #[derive( candid::CandidType, diff --git a/rs/sns/governance/src/governance.rs b/rs/sns/governance/src/governance.rs index e0da74d7dd9..e9b730ca7ca 100644 --- a/rs/sns/governance/src/governance.rs +++ b/rs/sns/governance/src/governance.rs @@ -68,7 +68,7 @@ use crate::{ }, types::{ function_id_to_proposal_criticality, is_registered_function_id, Environment, - HeapGrowthPotential, LedgerUpdateLock, + HeapGrowthPotential, LedgerUpdateLock, Wasm, }, }; use candid::{Decode, Encode}; @@ -166,6 +166,10 @@ pub const UPGRADE_STEPS_INTERVAL_REFRESH_BACKOFF_SECONDS: u64 = 60 * 60; // 1 ho /// Past this duration, the lock will be automatically released. const UPGRADE_PERIODIC_TASK_LOCK_TIMEOUT_SECONDS: u64 = 600; +/// Adopted-but-not-yet-executed upgrade proposals block other upgrade proposals from executing. +/// But this is only true for proposals that are less than 1 day old, to prevent a stuck proposal from blocking all upgrades forever. +const UPGRADE_PROPOSAL_BLOCK_EXPIRY_SECONDS: u64 = 60 * 60 * 24; // 1 day + /// Converts bytes to a subaccountpub fn bytes_to_subaccount(bytes: &[u8]) -> Result { pub fn bytes_to_subaccount( bytes: &[u8], @@ -2455,8 +2459,14 @@ impl Governance { .proposals .iter() .filter_map(|(id, proposal_data)| { + let proposal_expiry_time = proposal_data + .decided_timestamp_seconds + .checked_add(UPGRADE_PROPOSAL_BLOCK_EXPIRY_SECONDS) + .unwrap_or_default(); + let proposal_recent_enough = proposal_expiry_time > self.env.now(); if proposal_data.status() == ProposalDecisionStatus::Adopted && proposal_data.is_upgrade_proposal() + && proposal_recent_enough { Some(*id) } else { @@ -2507,9 +2517,12 @@ impl Governance { let mode = upgrade.mode_or_upgrade() as i32; + let wasm = Wasm::try_from(&upgrade) + .map_err(|err| GovernanceError::new_with_message(ErrorType::InvalidCommand, err))?; + self.upgrade_non_root_canister( target_canister_id, - upgrade.new_canister_wasm, + wasm, upgrade .canister_upgrade_arg .unwrap_or_else(|| Encode!().unwrap()), @@ -2521,7 +2534,7 @@ impl Governance { async fn upgrade_non_root_canister( &mut self, target_canister_id: CanisterId, - wasm: Vec, + wasm: Wasm, arg: Vec, mode: CanisterInstallMode, ) -> Result<(), GovernanceError> { @@ -2535,12 +2548,28 @@ impl Governance { // stop_before_installing field in ChangeCanisterRequest. let stop_before_installing = true; - let change_canister_arg = + let mut change_canister_arg = ChangeCanisterRequest::new(stop_before_installing, mode, target_canister_id) - .with_wasm(wasm) .with_arg(arg) .with_mode(mode); + match wasm { + Wasm::Bytes(bytes) => { + change_canister_arg = change_canister_arg.with_wasm(bytes); + } + Wasm::Chunked { + wasm_module_hash, + store_canister_id, + chunk_hashes_list, + } => { + change_canister_arg = change_canister_arg.with_chunked_wasm( + wasm_module_hash, + store_canister_id, + chunk_hashes_list, + ); + } + }; + Encode!(&change_canister_arg).unwrap() }; @@ -2567,7 +2596,7 @@ impl Governance { proposal_id: Option, ) -> Result<(), GovernanceError> { let upgrade_proposals_in_progress = self.upgrade_proposals_in_progress(); - if upgrade_proposals_in_progress != proposal_id.into_iter().collect() { + if !upgrade_proposals_in_progress.is_subset(&proposal_id.into_iter().collect()) { return Err(GovernanceError::new_with_message( ErrorType::ResourceExhausted, format!( @@ -2690,7 +2719,7 @@ impl Governance { for target_canister_id in canister_ids_to_upgrade { self.upgrade_non_root_canister( target_canister_id, - target_wasm.clone(), + Wasm::Bytes(target_wasm.clone()), Encode!().unwrap(), CanisterInstallMode::Upgrade, ) @@ -2757,7 +2786,7 @@ impl Governance { for target_canister_id in canister_ids_to_upgrade { self.upgrade_non_root_canister( target_canister_id, - target_wasm.clone(), + Wasm::Bytes(target_wasm.clone()), Encode!().unwrap(), CanisterInstallMode::Upgrade, ) @@ -2954,7 +2983,7 @@ impl Governance { self.upgrade_non_root_canister( ledger_canister_id, - ledger_wasm, + Wasm::Bytes(ledger_wasm), ledger_upgrade_arg, CanisterInstallMode::Upgrade, ) @@ -3077,11 +3106,14 @@ impl Governance { let (_, target_version) = self .proto .validate_new_target_version(Some(new_target)) - .map_err(|err| GovernanceError::new_with_message(ErrorType::InvalidProposal, err))?; + .map_err(|err: String| { + GovernanceError::new_with_message(ErrorType::InvalidProposal, err) + })?; self.push_to_upgrade_journal(upgrade_journal_entry::TargetVersionSet::new( self.proto.target_version.clone(), - Some(target_version.clone()), + target_version.clone(), + false, )); self.proto.target_version = Some(target_version); @@ -3094,6 +3126,16 @@ impl Governance { self.proto.parameters.as_ref() } + pub fn should_automatically_advance_target_version(&self) -> bool { + self.nervous_system_parameters() + .map(|nervous_system_parameters| { + nervous_system_parameters + .automatically_advance_target_version + .unwrap_or_default() + }) + .unwrap_or_default() + } + /// Returns the NervousSystemParameters or panics fn nervous_system_parameters_or_panic(&self) -> &NervousSystemParameters { self.nervous_system_parameters() diff --git a/rs/sns/governance/src/governance/advance_target_sns_version_tests.rs b/rs/sns/governance/src/governance/advance_target_sns_version_tests.rs index 8536645ff08..f8b7fb80620 100644 --- a/rs/sns/governance/src/governance/advance_target_sns_version_tests.rs +++ b/rs/sns/governance/src/governance/advance_target_sns_version_tests.rs @@ -61,7 +61,7 @@ async fn test_initiate_upgrade_blocked_by_upgrade_proposal() { let proposal = ProposalData { action: (&action).into(), id: Some(proposal_id.into()), - decided_timestamp_seconds: 1, + decided_timestamp_seconds: NativeEnvironment::DEFAULT_TEST_START_TIMESTAMP_SECONDS - 10, latest_tally: Some(Tally { yes: 1, no: 0, @@ -926,6 +926,84 @@ fn get_or_reset_upgrade_steps_leads_to_should_refresh_cached_upgrade_steps() { assert!(gov.should_refresh_cached_upgrade_steps()); } +#[tokio::test] +async fn test_refresh_cached_upgrade_steps_advances_target_version_automatically_is_required() { + let version_a = Version { + root_wasm_hash: vec![1, 2, 3], + governance_wasm_hash: vec![2, 3, 4], + ledger_wasm_hash: vec![3, 4, 5], + swap_wasm_hash: vec![4, 5, 6], + archive_wasm_hash: vec![5, 6, 7], + index_wasm_hash: vec![6, 7, 8], + }; + let mut version_b = version_a.clone(); + version_b.root_wasm_hash = vec![9, 9, 9]; + + // Smoke test. + assert_ne!(version_a, version_b); + + let make_gov = || -> Governance { + let mut env = NativeEnvironment::new(Some(*TEST_GOVERNANCE_CANISTER_ID)); + add_environment_mock_list_upgrade_steps_call( + &mut env, + vec![ + SnsVersion::from(version_a.clone()), + SnsVersion::from(version_b.clone()), + ], + ); + Governance::new( + GovernanceProto { + deployed_version: Some(version_a.clone()), + cached_upgrade_steps: None, + ..basic_governance_proto() + } + .try_into() + .unwrap(), + Box::new(env), + Box::new(DoNothingLedger {}), + Box::new(DoNothingLedger {}), + Box::new(FakeCmc::new()), + ) + }; + + for (automatically_advance_target_version, expected_target_version) in [ + (None, None), + (Some(false), None), + (Some(true), Some(version_b.clone())), + ] { + let mut gov = make_gov(); + + if let Some(parameters) = gov.proto.parameters.as_mut() { + parameters.automatically_advance_target_version = automatically_advance_target_version; + }; + + // Precondition + assert_eq!(gov.proto.cached_upgrade_steps, None); + assert_eq!(gov.proto.target_version, None); + + // Run code under test and assert intermediate conditions. + let deployed_version = gov + .try_temporarily_lock_refresh_cached_upgrade_steps() + .unwrap(); + gov.refresh_cached_upgrade_steps(deployed_version).await; + + // Intermediate postcondition. + assert_eq!( + gov.proto + .cached_upgrade_steps + .clone() + .unwrap() + .upgrade_steps + .unwrap() + .versions, + vec![version_a.clone(), version_b.clone(),] + ); + + // Main postcondition. + assert_eq!(gov.proto.target_version, expected_target_version); + } +} + fn add_environment_mock_calls_for_initiate_upgrade( env: &mut NativeEnvironment, expected_wasm_hash_requested: Vec, diff --git a/rs/sns/governance/src/governance/assorted_governance_tests.rs b/rs/sns/governance/src/governance/assorted_governance_tests.rs index d4bdcfdb5da..a076bde6f13 100644 --- a/rs/sns/governance/src/governance/assorted_governance_tests.rs +++ b/rs/sns/governance/src/governance/assorted_governance_tests.rs @@ -1080,7 +1080,7 @@ fn test_disallow_concurrent_upgrade_execution( let execution_in_progress_proposal = ProposalData { action: proposal_in_progress_action_id, id: Some(1_u64.into()), - decided_timestamp_seconds: 123, + decided_timestamp_seconds: NativeEnvironment::DEFAULT_TEST_START_TIMESTAMP_SECONDS - 10, latest_tally: Some(Tally { yes: 1, no: 0, @@ -2914,6 +2914,7 @@ fn test_sns_controlled_canister_upgrade_only_upgrades_dapp_canisters() { new_canister_wasm: vec![0, 0x61, 0x73, 0x6D, 2, 0, 0, 0], canister_upgrade_arg: None, mode: Some(CanisterInstallModeProto::Upgrade.into()), + chunked_canister_wasm: None, }); // Upgrade Proposal @@ -3041,7 +3042,7 @@ fn test_allow_canister_upgrades_while_motion_proposal_execution_is_in_progress() let motion_proposal = ProposalData { action: motion_action_id, id: Some(motion_proposal_id.into()), - decided_timestamp_seconds: 1, + decided_timestamp_seconds: NativeEnvironment::DEFAULT_TEST_START_TIMESTAMP_SECONDS - 10, latest_tally: Some(Tally { yes: 1, no: 0, @@ -3056,7 +3057,7 @@ fn test_allow_canister_upgrades_while_motion_proposal_execution_is_in_progress() let upgrade_proposal = ProposalData { action: upgrade_action_id, id: Some(upgrade_proposal_id.into()), - decided_timestamp_seconds: 1, + decided_timestamp_seconds: NativeEnvironment::DEFAULT_TEST_START_TIMESTAMP_SECONDS - 10, latest_tally: Some(Tally { yes: 1, no: 0, @@ -3115,7 +3116,7 @@ fn test_allow_canister_upgrades_while_another_upgrade_proposal_is_open() { let executing_upgrade_proposal = ProposalData { action: upgrade_action_id, id: Some(executing_upgrade_proposal_id.into()), - decided_timestamp_seconds: 1, + decided_timestamp_seconds: NativeEnvironment::DEFAULT_TEST_START_TIMESTAMP_SECONDS - 10, latest_tally: Some(Tally { yes: 1, no: 0, @@ -3161,8 +3162,8 @@ fn test_allow_canister_upgrades_after_another_upgrade_proposal_has_executed() { let previous_upgrade_proposal = ProposalData { action: upgrade_action_id, id: Some(previous_upgrade_proposal_id.into()), - decided_timestamp_seconds: 1, - executed_timestamp_seconds: 1, + decided_timestamp_seconds: NativeEnvironment::DEFAULT_TEST_START_TIMESTAMP_SECONDS - 10, + executed_timestamp_seconds: NativeEnvironment::DEFAULT_TEST_START_TIMESTAMP_SECONDS - 5, latest_tally: Some(Tally { yes: 1, no: 0, @@ -3177,7 +3178,7 @@ fn test_allow_canister_upgrades_after_another_upgrade_proposal_has_executed() { let upgrade_proposal = ProposalData { action: upgrade_action_id, id: Some(upgrade_proposal_id.into()), - decided_timestamp_seconds: 1, + decided_timestamp_seconds: NativeEnvironment::DEFAULT_TEST_START_TIMESTAMP_SECONDS - 10, latest_tally: Some(Tally { yes: 1, no: 0, @@ -3222,7 +3223,7 @@ fn test_allow_canister_upgrades_proposal_does_not_block_itself_but_does_block_ot let proposal = ProposalData { action: upgrade_action_id, id: Some(proposal_id.into()), - decided_timestamp_seconds: 1, + decided_timestamp_seconds: NativeEnvironment::DEFAULT_TEST_START_TIMESTAMP_SECONDS - 10, latest_tally: Some(Tally { yes: 1, no: 0, @@ -3277,7 +3278,7 @@ fn test_upgrade_proposals_blocked_by_pending_upgrade() { let proposal = ProposalData { action: upgrade_action_id, id: Some(proposal_id.into()), - decided_timestamp_seconds: 1, + decided_timestamp_seconds: NativeEnvironment::DEFAULT_TEST_START_TIMESTAMP_SECONDS - 10, latest_tally: Some(Tally { yes: 1, no: 0, @@ -3330,6 +3331,76 @@ fn test_upgrade_proposals_blocked_by_pending_upgrade() { } } +/// Ugrade proposals (e.g. UpgradeSnsToNextVersion) block all other upgrade actions while they're adopted until they're done executing, unless they're too old. This test checks the "unless they're too old" part +#[test] +fn test_upgrade_proposals_not_blocked_by_old_upgrade_proposals() { + // Step 1: Prepare the world. + use ProposalDecisionStatus as Status; + + let upgrade_action_id: u64 = + (&Action::UpgradeSnsControlledCanister(UpgradeSnsControlledCanister::default())).into(); + + let proposal_id = 1_u64; + let some_other_proposal_id = 99_u64; + let proposal = ProposalData { + action: upgrade_action_id, + id: Some(proposal_id.into()), + decided_timestamp_seconds: NativeEnvironment::DEFAULT_TEST_START_TIMESTAMP_SECONDS + - crate::governance::UPGRADE_PROPOSAL_BLOCK_EXPIRY_SECONDS + - 1, + latest_tally: Some(Tally { + yes: 1, + no: 0, + total: 1, + timestamp_seconds: 1, + }), + ..Default::default() + }; + assert_eq!(proposal.status(), Status::Adopted); + + let mut governance = Governance::new( + GovernanceProto { + proposals: btreemap! { + proposal_id => proposal, + }, + ..basic_governance_proto() + } + .try_into() + .unwrap(), + Box::::default(), + Box::new(DoNothingLedger {}), + Box::new(DoNothingLedger {}), + Box::new(FakeCmc::new()), + ); + + // Step 2: Check that the proposal is not blocked by an old proposal. + match governance.check_no_upgrades_in_progress(Some(some_other_proposal_id)) { + Ok(_) => {}, + Err(err) => panic!("The proposal should not have gotten blocked by an old proposal. Instead, it was blocked due to: {:#?}", err), + } + + // Step 3: Make the proposal newer + governance + .proto + .proposals + .get_mut(&proposal_id) + .unwrap() + .decided_timestamp_seconds = NativeEnvironment::DEFAULT_TEST_START_TIMESTAMP_SECONDS + - crate::governance::UPGRADE_PROPOSAL_BLOCK_EXPIRY_SECONDS + + 1; + + // Step 4: Check that the proposal is now blocked by an old proposal. + match governance.check_no_upgrades_in_progress(Some(some_other_proposal_id)) { + Ok(_) => panic!("The proposal should have gotten blocked by an old proposal"), + Err(err) => assert_eq!( + err.error_type, + ErrorType::ResourceExhausted as i32, + "{:#?}", + err, + ), + } +} + #[test] fn test_add_generic_nervous_system_function_succeeds() { let root_canister_id = *TEST_ROOT_CANISTER_ID; @@ -3626,10 +3697,7 @@ fn test_move_staked_maturity_on_dissolved_neurons_works() { let neuron_id_2 = test_neuron_id(controller_2); let regular_maturity: u64 = 1000000; let staked_maturity: u64 = 424242; - let now = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_secs(); + let now = NativeEnvironment::DEFAULT_TEST_START_TIMESTAMP_SECONDS; // Dissolved neuron. let neuron_1 = Neuron { id: Some(neuron_id_1.clone()), diff --git a/rs/sns/governance/src/lib.rs b/rs/sns/governance/src/lib.rs index 837b1c00a7b..3e80fae168b 100644 --- a/rs/sns/governance/src/lib.rs +++ b/rs/sns/governance/src/lib.rs @@ -9,7 +9,6 @@ pub mod logs; pub mod neuron; pub mod pb; pub mod proposal; -mod request_impls; pub mod reward; pub mod sns_upgrade; mod treasury; diff --git a/rs/sns/governance/src/pb/conversions.rs b/rs/sns/governance/src/pb/conversions.rs index 7a14fbd1ad9..ff8c4ccbdf7 100644 --- a/rs/sns/governance/src/pb/conversions.rs +++ b/rs/sns/governance/src/pb/conversions.rs @@ -181,7 +181,9 @@ impl From for pb_api::NervousSystemFunction { id: item.id, name: item.name, description: item.description, - function_type: item.function_type.map(|x| x.into()), + function_type: item + .function_type + .map(|x: pb::nervous_system_function::FunctionType| x.into()), } } } @@ -291,9 +293,33 @@ impl From for pb_api::UpgradeSnsControlledCani new_canister_wasm: item.new_canister_wasm, canister_upgrade_arg: item.canister_upgrade_arg, mode: item.mode, + chunked_canister_wasm: item + .chunked_canister_wasm + .map(pb_api::ChunkedCanisterWasm::from), + } + } +} + +impl From for pb::ChunkedCanisterWasm { + fn from(item: pb_api::ChunkedCanisterWasm) -> Self { + Self { + wasm_module_hash: item.wasm_module_hash, + store_canister_id: item.store_canister_id, + chunk_hashes_list: item.chunk_hashes_list, } } } + +impl From for pb_api::ChunkedCanisterWasm { + fn from(item: pb::ChunkedCanisterWasm) -> Self { + Self { + wasm_module_hash: item.wasm_module_hash, + store_canister_id: item.store_canister_id, + chunk_hashes_list: item.chunk_hashes_list, + } + } +} + impl From for pb::UpgradeSnsControlledCanister { fn from(item: pb_api::UpgradeSnsControlledCanister) -> Self { Self { @@ -301,6 +327,9 @@ impl From for pb::UpgradeSnsControlledCani new_canister_wasm: item.new_canister_wasm, canister_upgrade_arg: item.canister_upgrade_arg, mode: item.mode, + chunked_canister_wasm: item + .chunked_canister_wasm + .map(pb::ChunkedCanisterWasm::from), } } } @@ -1096,6 +1125,7 @@ impl From for pb_api::NervousSystemParameters { max_dissolve_delay_bonus_percentage: item.max_dissolve_delay_bonus_percentage, max_age_bonus_percentage: item.max_age_bonus_percentage, maturity_modulation_disabled: item.maturity_modulation_disabled, + automatically_advance_target_version: item.automatically_advance_target_version, } } } @@ -1123,6 +1153,7 @@ impl From for pb::NervousSystemParameters { max_dissolve_delay_bonus_percentage: item.max_dissolve_delay_bonus_percentage, max_age_bonus_percentage: item.max_age_bonus_percentage, maturity_modulation_disabled: item.maturity_modulation_disabled, + automatically_advance_target_version: item.automatically_advance_target_version, } } } @@ -3195,6 +3226,7 @@ impl From Self { old_target_version: item.old_target_version.map(|x| x.into()), new_target_version: item.new_target_version.map(|x| x.into()), + is_advanced_automatically: item.is_advanced_automatically, } } } @@ -3205,6 +3237,7 @@ impl From Self { old_target_version: item.old_target_version.map(|x| x.into()), new_target_version: item.new_target_version.map(|x| x.into()), + is_advanced_automatically: item.is_advanced_automatically, } } } diff --git a/rs/sns/governance/src/proposal.rs b/rs/sns/governance/src/proposal.rs index 6aaae38eddc..a620fe0d804 100644 --- a/rs/sns/governance/src/proposal.rs +++ b/rs/sns/governance/src/proposal.rs @@ -1,5 +1,6 @@ use crate::cached_upgrade_steps::render_two_versions_as_markdown_table; use crate::pb::v1::AdvanceSnsTargetVersion; +use crate::types::Wasm; use crate::{ canister_control::perform_execute_generic_nervous_system_function_validate_and_render_call, governance::{ @@ -40,6 +41,7 @@ use ic_nervous_system_common::{ use ic_nervous_system_proto::pb::v1::Percentage; use ic_nervous_system_timestamp::format_timestamp_for_humans; use ic_protobuf::types::v1::CanisterInstallMode; +use ic_sns_governance_api::format_full_hash; use ic_sns_governance_proposals_amount_total_limit::{ // TODO(NNS1-2982): Uncomment. mint_sns_tokens_7_day_total_upper_bound_tokens, transfer_sns_treasury_funds_7_day_total_upper_bound_tokens, @@ -92,19 +94,13 @@ pub const EXECUTED_TRANSFER_SNS_TREASURY_FUNDS_PROPOSAL_RETENTION_DURATION_SECON /// the same, but we keep separate constants, because we consider this to be a coincidence. pub const EXECUTED_MINT_SNS_TOKENS_PROPOSAL_RETENTION_DURATION_SECONDS: u64 = 7 * ONE_DAY_SECONDS; -/// The maximum message size for inter-canister calls to a different subnet -/// is 2MiB and thus we restrict the maximum joint size of the canister WASM -/// and argument to 2MB (2,000,000B) to leave some slack for Candid overhead -/// and a few constant-size fields (e.g., compute and memory allocation). -pub const MAX_INSTALL_CODE_WASM_AND_ARG_SIZE: usize = 2_000_000; // 2MB - impl Proposal { /// Returns whether a proposal is allowed to be submitted when /// the heap growth potential is low. pub(crate) fn allowed_when_resources_are_low(&self) -> bool { self.action .as_ref() - .map_or(false, |a| a.allowed_when_resources_are_low()) + .is_some_and(|a| a.allowed_when_resources_are_low()) } /// Returns a clone of self, except that "large blob fields" are replaced @@ -411,7 +407,8 @@ pub(crate) async fn validate_and_render_action( validate_and_render_manage_nervous_system_parameters(manage, current_parameters) } proposal::Action::UpgradeSnsControlledCanister(upgrade) => { - validate_and_render_upgrade_sns_controlled_canister(upgrade) + validate_and_render_upgrade_sns_controlled_canister(upgrade, env, root_canister_id) + .await } Action::UpgradeSnsToNextVersion(upgrade_sns) => { match governance_proto.deployed_version_or_err() { @@ -1037,17 +1034,22 @@ impl TokenProposalAction for MintSnsTokens { } /// Validates and renders a proposal with action UpgradeSnsControlledCanister. -fn validate_and_render_upgrade_sns_controlled_canister( +async fn validate_and_render_upgrade_sns_controlled_canister( upgrade: &UpgradeSnsControlledCanister, + env: &dyn Environment, + root_canister_id: CanisterId, ) -> Result { let mut defects = vec![]; let UpgradeSnsControlledCanister { - canister_id: _, - new_canister_wasm, + canister_id, canister_upgrade_arg, mode, + // The WASM-related fields are extracted separately. + chunked_canister_wasm: _, + new_canister_wasm: _, } = upgrade; + // Make sure `mode` is not None, and not an invalid/unknown value. if let Some(mode) = mode { if let Err(err) = CanisterInstallMode::try_from(*mode) { @@ -1058,77 +1060,87 @@ fn validate_and_render_upgrade_sns_controlled_canister( let mode = upgrade.mode_or_upgrade(); // Inspect canister_id. - let mut canister_id = PrincipalId::new_user_test_id(0xDEADBEEF); // Initialize to garbage. This won't get used later. - match validate_required_field("canister_id", &upgrade.canister_id) { + let canister_id = match validate_required_field("canister_id", canister_id) { Err(err) => { defects.push(err); + None } - Ok(id) => { - canister_id = *id; - } - } + Ok(principal_id) => match CanisterId::try_from_principal_id(*principal_id) { + Ok(canister_id) => Some(canister_id), + Err(err) => { + let defect = format!( + "UpgradeSnsControlledCanister.canister_id is invalid: {:?}", + err + ); + defects.push(defect); + None + } + }, + }; // Inspect wasm. - const RAW_WASM_HEADER: [u8; 4] = [0, 0x61, 0x73, 0x6d]; - // see https://ic-interface-spec.netlify.app/#canister-module-format - const GZIPPED_WASM_HEADER: [u8; 3] = [0x1f, 0x8b, 0x08]; - - if new_canister_wasm.len() < 4 - || new_canister_wasm[..4] != RAW_WASM_HEADER[..] - && new_canister_wasm[..3] != GZIPPED_WASM_HEADER[..] - { - defects.push("new_canister_wasm lacks the magic value in its header.".into()); - } - - if new_canister_wasm.len().saturating_add( - canister_upgrade_arg - .as_ref() - .map(|arg| arg.len()) - .unwrap_or_default(), - ) >= MAX_INSTALL_CODE_WASM_AND_ARG_SIZE - { - defects.push(format!( - "the maximum canister WASM and argument size \ - for UpgradeSnsControlledCanister is {} bytes.", - MAX_INSTALL_CODE_WASM_AND_ARG_SIZE - )); - } + let wasm_info = match Wasm::try_from(upgrade) { + Err(err) => { + defects.push(err); + None + } + Ok(wasm) => match wasm.validate(env, canister_upgrade_arg).await { + Err(new_defects) => { + defects.extend(new_defects.into_iter()); + None + } + Ok(_) => Some(wasm.description()), + }, + }; // Generate final report. if !defects.is_empty() { + let tip = if upgrade.chunked_canister_wasm.is_some() { + format!( + "\nPlease make sure that both Governance ({}) and Root ({}) are controllers of \ + the Wasm store canister.", + env.canister_id().get(), + root_canister_id.get(), + ) + } else { + "".to_string() + }; return Err(format!( - "UpgradeSnsControlledCanister was invalid for the following reason(s):\n{}", + "UpgradeSnsControlledCanister was invalid for the following reason(s):\n{}{tip}", defects.join("\n"), )); } - let canister_wasm_sha256 = { - let mut state = Sha256::new(); - state.write(new_canister_wasm); - let sha = state.finish(); - hex::encode(sha) - }; + // If this is reached, then defects is empty. In that case, it is safe to unwrap the values + // required for rendering the proposal. + let canister_id = canister_id.unwrap().get(); + let wasm_info = wasm_info.unwrap(); - let upgrade_args_sha_256 = canister_upgrade_arg + let args_info = canister_upgrade_arg .as_ref() .map(|arg| { - let mut state = Sha256::new(); - state.write(arg); - let sha = state.finish(); - format!("Upgrade arg sha256: {}", hex::encode(sha)) + format!( + "Upgrade argument with {} bytes and SHA256 `{}`.", + arg.len(), + format_full_hash(arg), + ) }) - .unwrap_or_else(|| "No upgrade arg".to_string()); + .unwrap_or_else(|| "No upgrade argument.".to_string()); Ok(format!( - r"# Proposal to upgrade SNS controlled canister: + r"# Proposal to Upgrade an SNS Controlled Canister + +## Target canister: {canister_id:?} -## Canister id: {canister_id:?} +## Wasm info -## Canister wasm sha256: {canister_wasm_sha256} +{wasm_info} ## Mode: {mode:?} -## {upgrade_args_sha_256}", +## Argument info + +{args_info}", )) } @@ -2553,8 +2565,8 @@ mod tests { use crate::{ pb::v1::{ governance::{self, Version}, - Ballot, Empty, Governance as GovernanceProto, NeuronId, Proposal, ProposalId, - Subaccount, WaitForQuietState, + Ballot, ChunkedCanisterWasm, Empty, Governance as GovernanceProto, NeuronId, Proposal, + ProposalId, Subaccount, WaitForQuietState, }, sns_upgrade::{ CanisterSummary, GetNextSnsVersionRequest, GetNextSnsVersionResponse, @@ -2568,6 +2580,7 @@ mod tests { use futures::FutureExt; use ic_base_types::{NumBytes, PrincipalId}; use ic_crypto_sha2::Sha256; + use ic_management_canister_types::{CanisterIdRecord, ChunkHash, StoredChunksReply}; use ic_nervous_system_clients::canister_status::{CanisterStatusResultV2, CanisterStatusType}; use ic_nervous_system_common_test_keys::TEST_USER1_PRINCIPAL; use ic_nns_constants::SNS_WASM_CANISTER_ID; @@ -2651,6 +2664,10 @@ mod tests { PrincipalId::try_from(vec![42_u8]).unwrap() } + fn basic_canister_id() -> PrincipalId { + canister_test_id(42).get() + } + fn basic_motion_proposal() -> Proposal { let result = Proposal { title: "title".into(), @@ -2770,78 +2787,303 @@ mod tests { } } - #[test] - fn render_upgrade_sns_controlled_canister_proposal() { + #[tokio::test] + async fn render_upgrade_sns_controlled_canister_proposal() { let upgrade = UpgradeSnsControlledCanister { - canister_id: Some(basic_principal_id()), + canister_id: Some(basic_canister_id()), new_canister_wasm: vec![0, 0x61, 0x73, 0x6D, 1, 0, 0, 0], canister_upgrade_arg: None, mode: Some(CanisterInstallModeProto::Upgrade.into()), + chunked_canister_wasm: None, }; - let text = validate_and_render_upgrade_sns_controlled_canister(&upgrade).unwrap(); + let env = setup_for_upgrade_sns_controlled_canister_tests(&upgrade); + let text = validate_and_render_upgrade_sns_controlled_canister( + &upgrade, + &env, + canister_test_id(55), + ) + .await + .unwrap(); assert_eq!( text, - r#"# Proposal to upgrade SNS controlled canister: + r#"# Proposal to Upgrade an SNS Controlled Canister -## Canister id: bg4sm-wzk +## Target canister: xbgkv-fyaaa-aaaaa-aaava-cai -## Canister wasm sha256: 93a44bbb96c751218e4c00d479e4c14358122a389acca16205b1e4d0dc5f9476 +## Wasm info + +Embedded module with 8 bytes and SHA256 `93a44bbb96c751218e4c00d479e4c14358122a389acca16205b1e4d0dc5f9476`. ## Mode: Upgrade -## No upgrade arg"# +## Argument info + +No upgrade argument."# .to_string() ); } - #[test] - fn render_upgrade_sns_controlled_canister_proposal_with_upgrade_args() { + #[tokio::test] + async fn render_upgrade_sns_controlled_canister_proposal_with_chunked_wasm() { let upgrade = UpgradeSnsControlledCanister { - canister_id: Some(basic_principal_id()), + canister_id: Some(basic_canister_id()), + new_canister_wasm: vec![], + canister_upgrade_arg: None, + mode: Some(CanisterInstallModeProto::Upgrade.into()), + chunked_canister_wasm: Some(ChunkedCanisterWasm { + wasm_module_hash: vec![1, 2, 3], + store_canister_id: Some(canister_test_id(111).get()), + chunk_hashes_list: vec![vec![1, 1, 1], vec![2, 2, 2], vec![3, 3, 3]], + }), + }; + let env = setup_for_upgrade_sns_controlled_canister_tests(&upgrade); + let text = validate_and_render_upgrade_sns_controlled_canister( + &upgrade, + &env, + canister_test_id(55), + ) + .await + .unwrap(); + + assert_eq!( + text, + r#"# Proposal to Upgrade an SNS Controlled Canister + +## Target canister: xbgkv-fyaaa-aaaaa-aaava-cai + +## Wasm info + +Remote module stored on canister zyo6l-paaaa-aaaaa-aabxq-cai with SHA256 `010203`. Split into 3 chunks: + - `010101` + - `020202` + - `030303` + +## Mode: Upgrade + +## Argument info + +No upgrade argument."# + .to_string() + ); + } + + // TODO[NNS1-3550]: Enable this test for all compilations. + #[cfg(feature = "test")] + #[tokio::test] + async fn render_upgrade_sns_controlled_canister_proposal_with_unexpected_chunk() { + let mut chunked_canister_wasm = ChunkedCanisterWasm { + wasm_module_hash: vec![1, 2, 3], + store_canister_id: Some(canister_test_id(111).get()), + chunk_hashes_list: vec![vec![1, 1, 1], vec![2, 2, 2], vec![3, 3, 3]], + }; + let mut upgrade = UpgradeSnsControlledCanister { + canister_id: Some(basic_canister_id()), + new_canister_wasm: vec![], + canister_upgrade_arg: None, + mode: Some(CanisterInstallModeProto::Upgrade.into()), + chunked_canister_wasm: Some(chunked_canister_wasm.clone()), + }; + + let env = setup_for_upgrade_sns_controlled_canister_tests(&upgrade); + + // Modify the update payload to make it invalid (unexpected chunk). + { + chunked_canister_wasm.chunk_hashes_list.push(vec![4, 4, 4]); + upgrade.chunked_canister_wasm.replace(chunked_canister_wasm); + } + + let err = validate_and_render_upgrade_sns_controlled_canister( + &upgrade, + &env, + canister_test_id(55), + ) + .await + .unwrap_err(); + + assert!(err.contains( + "1 out of 4 expected WASM chunks were not uploaded to the store canister: 040404" + )); + } + + #[tokio::test] + async fn render_upgrade_sns_controlled_canister_proposal_with_chunk_hash_mismatch() { + let mut chunked_canister_wasm = ChunkedCanisterWasm { + wasm_module_hash: vec![1, 1, 1], + store_canister_id: Some(canister_test_id(111).get()), + chunk_hashes_list: vec![vec![1, 1, 1]], + }; + let mut upgrade = UpgradeSnsControlledCanister { + canister_id: Some(basic_canister_id()), + new_canister_wasm: vec![], + canister_upgrade_arg: None, + mode: Some(CanisterInstallModeProto::Upgrade.into()), + chunked_canister_wasm: Some(chunked_canister_wasm.clone()), + }; + + let env = setup_for_upgrade_sns_controlled_canister_tests(&upgrade); + + // Modify the update payload to make it invalid (mismatch between chunk_hashes_list + // and wasm_module_hash). + { + chunked_canister_wasm.chunk_hashes_list = vec![vec![2, 2, 2]]; + upgrade.chunked_canister_wasm.replace(chunked_canister_wasm); + } + + let err = validate_and_render_upgrade_sns_controlled_canister( + &upgrade, + &env, + canister_test_id(55), + ) + .await + .unwrap_err(); + + assert!(err.contains( + "chunked_canister_wasm.chunk_hashes_list specifies only one hash (020202), \ + but it differs from chunked_canister_wasm.wasm_module_hash (010101)" + ),); + } + + #[tokio::test] + async fn render_upgrade_sns_controlled_canister_proposal_with_chunks_but_no_store_canister() { + let mut chunked_canister_wasm = ChunkedCanisterWasm { + wasm_module_hash: vec![1, 1, 1], + store_canister_id: Some(canister_test_id(111).get()), + chunk_hashes_list: vec![vec![1, 1, 1]], + }; + let mut upgrade = UpgradeSnsControlledCanister { + canister_id: Some(basic_canister_id()), + new_canister_wasm: vec![], + canister_upgrade_arg: None, + mode: Some(CanisterInstallModeProto::Upgrade.into()), + chunked_canister_wasm: Some(chunked_canister_wasm.clone()), + }; + + let env = setup_for_upgrade_sns_controlled_canister_tests(&upgrade); + + // Modify the update payload to make it invalid (store_canister_id not set). + { + chunked_canister_wasm.store_canister_id = None; + upgrade.chunked_canister_wasm.replace(chunked_canister_wasm); + } + + let err = validate_and_render_upgrade_sns_controlled_canister( + &upgrade, + &env, + canister_test_id(55), + ) + .await + .unwrap_err(); + + assert!(err.contains("chunked_canister_wasm.store_canister_id must be specified.")); + } + + #[tokio::test] + async fn render_upgrade_sns_controlled_canister_proposal_with_empty_chunks_list() { + let mut chunked_canister_wasm = ChunkedCanisterWasm { + wasm_module_hash: vec![1, 1, 1], + store_canister_id: Some(canister_test_id(111).get()), + chunk_hashes_list: vec![vec![1, 1, 1]], + }; + let mut upgrade = UpgradeSnsControlledCanister { + canister_id: Some(basic_canister_id()), + new_canister_wasm: vec![], + canister_upgrade_arg: None, + mode: Some(CanisterInstallModeProto::Upgrade.into()), + chunked_canister_wasm: Some(chunked_canister_wasm.clone()), + }; + + let env = setup_for_upgrade_sns_controlled_canister_tests(&upgrade); + + // Modify the update payload to make it invalid (empty chunk_hashes_list). + { + chunked_canister_wasm.chunk_hashes_list = vec![]; + upgrade.chunked_canister_wasm.replace(chunked_canister_wasm); + } + + let err = validate_and_render_upgrade_sns_controlled_canister( + &upgrade, + &env, + canister_test_id(55), + ) + .await + .unwrap_err(); + + assert!(err.contains("chunked_canister_wasm.chunk_hashes_list cannot be empty.")); + } + + #[tokio::test] + async fn render_upgrade_sns_controlled_canister_proposal_with_upgrade_args() { + let upgrade = UpgradeSnsControlledCanister { + canister_id: Some(basic_canister_id()), new_canister_wasm: vec![0, 0x61, 0x73, 0x6D, 1, 0, 0, 0], canister_upgrade_arg: Some(vec![10, 20, 30, 40, 50, 60, 70, 80]), mode: Some(CanisterInstallModeProto::Upgrade.into()), + chunked_canister_wasm: None, }; - let text = validate_and_render_upgrade_sns_controlled_canister(&upgrade).unwrap(); + let env = setup_for_upgrade_sns_controlled_canister_tests(&upgrade); + let text = validate_and_render_upgrade_sns_controlled_canister( + &upgrade, + &env, + canister_test_id(55), + ) + .await + .unwrap(); assert_eq!( text, - r#"# Proposal to upgrade SNS controlled canister: + r#"# Proposal to Upgrade an SNS Controlled Canister + +## Target canister: xbgkv-fyaaa-aaaaa-aaava-cai -## Canister id: bg4sm-wzk +## Wasm info -## Canister wasm sha256: 93a44bbb96c751218e4c00d479e4c14358122a389acca16205b1e4d0dc5f9476 +Embedded module with 8 bytes and SHA256 `93a44bbb96c751218e4c00d479e4c14358122a389acca16205b1e4d0dc5f9476`. ## Mode: Upgrade -## Upgrade arg sha256: 73f1171adc7e49b09423da2515a1077e3cc63e3fabcb9846cac437d044ac57ec"# +## Argument info + +Upgrade argument with 8 bytes and SHA256 `0a141e28323c4650`."# .to_string() ); } - #[test] - fn render_upgrade_sns_controlled_canister_proposal_validates_mode() { + #[tokio::test] + async fn render_upgrade_sns_controlled_canister_proposal_validates_mode() { let upgrade = UpgradeSnsControlledCanister { - canister_id: Some(basic_principal_id()), + canister_id: Some(basic_canister_id()), new_canister_wasm: vec![0, 0x61, 0x73, 0x6D, 1, 0, 0, 0], canister_upgrade_arg: None, mode: Some(100), // 100 is not a valid mode + chunked_canister_wasm: None, }; - let text = validate_and_render_upgrade_sns_controlled_canister(&upgrade).unwrap_err(); + let env = setup_for_upgrade_sns_controlled_canister_tests(&upgrade); + let text = validate_and_render_upgrade_sns_controlled_canister( + &upgrade, + &env, + canister_test_id(55), + ) + .await + .unwrap_err(); assert!(text.contains("Invalid mode")); } - fn basic_upgrade_sns_controlled_canister_proposal() -> Proposal { + async fn basic_upgrade_sns_controlled_canister_proposal() -> Proposal { let upgrade = UpgradeSnsControlledCanister { - canister_id: Some(basic_principal_id()), + canister_id: Some(basic_canister_id()), new_canister_wasm: vec![0, 0x61, 0x73, 0x6D, 1, 0, 0, 0], canister_upgrade_arg: None, mode: Some(CanisterInstallModeProto::Upgrade.into()), + chunked_canister_wasm: None, }; - assert_is_ok(validate_and_render_upgrade_sns_controlled_canister( + let result = validate_and_render_upgrade_sns_controlled_canister( &upgrade, - )); + &new_environment_that_expects_no_canister_calls(), + canister_test_id(55), + ) + .await; + assert_is_ok(result); let mut result = basic_motion_proposal(); result.action = Some(proposal::Action::UpgradeSnsControlledCanister(upgrade)); @@ -2852,82 +3094,122 @@ mod tests { result } - fn assert_validate_upgrade_sns_controlled_canister_is_err(proposal: &Proposal) { + async fn assert_validate_upgrade_sns_controlled_canister_is_err( + proposal: &Proposal, + env: &dyn Environment, + ) { assert_is_err(validate_default_proposal(proposal)); assert_is_err(validate_default_action(&proposal.action)); match proposal.action.as_ref().unwrap() { proposal::Action::UpgradeSnsControlledCanister(upgrade) => { - assert_is_err(validate_and_render_upgrade_sns_controlled_canister(upgrade)) + let result = validate_and_render_upgrade_sns_controlled_canister( + upgrade, + env, + canister_test_id(55), + ) + .await; + assert_is_err(result) } _ => panic!("Proposal.action is not an UpgradeSnsControlledCanister."), } } - #[test] - fn upgrade_must_have_canister_id() { - let mut proposal = basic_upgrade_sns_controlled_canister_proposal(); + #[tokio::test] + async fn upgrade_must_have_canister_id() { + let mut proposal = basic_upgrade_sns_controlled_canister_proposal().await; // Create a defect. - match proposal.action.as_mut().unwrap() { + let env = match proposal.action.as_mut().unwrap() { proposal::Action::UpgradeSnsControlledCanister(upgrade) => { + let env = setup_for_upgrade_sns_controlled_canister_tests(upgrade); upgrade.canister_id = None; - assert_is_err(validate_and_render_upgrade_sns_controlled_canister(upgrade)); + let result = validate_and_render_upgrade_sns_controlled_canister( + upgrade, + &env, + canister_test_id(55), + ) + .await; + assert_is_err(result); + env } _ => panic!("Proposal.action is not an UpgradeSnsControlledCanister."), - } + }; - assert_validate_upgrade_sns_controlled_canister_is_err(&proposal); + assert_validate_upgrade_sns_controlled_canister_is_err(&proposal, &env).await; } /// The minimum WASM is 8 bytes long. Therefore, we must not allow the /// new_canister_wasm field to be empty. - #[test] - fn upgrade_wasm_must_be_non_empty() { - let mut proposal = basic_upgrade_sns_controlled_canister_proposal(); + #[tokio::test] + async fn upgrade_wasm_must_be_non_empty() { + let mut proposal = basic_upgrade_sns_controlled_canister_proposal().await; // Create a defect. - match proposal.action.as_mut().unwrap() { + let env = match proposal.action.as_mut().unwrap() { proposal::Action::UpgradeSnsControlledCanister(upgrade) => { + let env = setup_for_upgrade_sns_controlled_canister_tests(upgrade); upgrade.new_canister_wasm = vec![]; - assert_is_err(validate_and_render_upgrade_sns_controlled_canister(upgrade)); + let result = validate_and_render_upgrade_sns_controlled_canister( + upgrade, + &env, + canister_test_id(55), + ) + .await; + assert_is_err(result); + env } _ => panic!("Proposal.action is not an UpgradeSnsControlledCanister."), - } + }; - assert_validate_upgrade_sns_controlled_canister_is_err(&proposal); + assert_validate_upgrade_sns_controlled_canister_is_err(&proposal, &env).await; } - #[test] - fn upgrade_wasm_must_not_be_dead_beef() { - let mut proposal = basic_upgrade_sns_controlled_canister_proposal(); + #[tokio::test] + async fn upgrade_wasm_must_not_be_dead_beef() { + let mut proposal = basic_upgrade_sns_controlled_canister_proposal().await; // Create a defect. - match proposal.action.as_mut().unwrap() { + let env = match proposal.action.as_mut().unwrap() { proposal::Action::UpgradeSnsControlledCanister(upgrade) => { + let env = setup_for_upgrade_sns_controlled_canister_tests(upgrade); // This is invalid, because it does not have the magical first // four bytes that a WASM is supposed to have. (Instead, the // first four bytes of this Vec are 0xDeadBeef.) upgrade.new_canister_wasm = vec![0xde, 0xad, 0xbe, 0xef, 1, 0, 0, 0]; assert!(upgrade.new_canister_wasm.len() == 8); // The minimum wasm len. - assert_is_err(validate_and_render_upgrade_sns_controlled_canister(upgrade)); + let result = validate_and_render_upgrade_sns_controlled_canister( + upgrade, + &env, + canister_test_id(55), + ) + .await; + assert_is_err(result); + env } _ => panic!("Proposal.action is not an UpgradeSnsControlledCanister."), - } + }; - assert_validate_upgrade_sns_controlled_canister_is_err(&proposal); + assert_validate_upgrade_sns_controlled_canister_is_err(&proposal, &env).await; } - #[test] - fn upgrade_wasm_can_be_gzipped() { - let mut proposal = basic_upgrade_sns_controlled_canister_proposal(); + #[tokio::test] + async fn upgrade_wasm_can_be_gzipped() { + let mut proposal = basic_upgrade_sns_controlled_canister_proposal().await; match proposal.action.as_mut().unwrap() { proposal::Action::UpgradeSnsControlledCanister(upgrade) => { + let env = setup_for_upgrade_sns_controlled_canister_tests(upgrade); upgrade.new_canister_wasm = vec![0x1f, 0x8b, 0x08, 0x08, 0xa3, 0x8e, 0xcf, 0x63, 0, 0x03]; assert!(upgrade.new_canister_wasm.len() >= 8); // The minimum wasm len. - assert_is_ok(validate_and_render_upgrade_sns_controlled_canister(upgrade)); + let result = validate_and_render_upgrade_sns_controlled_canister( + upgrade, + &env, + canister_test_id(55), + ) + .await; + assert_is_ok(result); } _ => panic!("Proposal.action is not an UpgradeSnsControlledCanister."), } @@ -3238,6 +3520,51 @@ mod tests { ) } + fn new_environment_that_expects_no_canister_calls() -> NativeEnvironment { + let governance_canister_id = *SNS_GOVERNANCE_CANISTER_ID; + let mut env = NativeEnvironment::new(Some(governance_canister_id)); + env.default_canister_call_response = Err((Some(1), "Unexpected call!".to_string())); + env + } + + fn setup_for_upgrade_sns_controlled_canister_tests( + upgrade: &UpgradeSnsControlledCanister, + ) -> NativeEnvironment { + let UpgradeSnsControlledCanister { + chunked_canister_wasm, + .. + } = upgrade; + + let governance_canister_id = *SNS_GOVERNANCE_CANISTER_ID; + let mut env = NativeEnvironment::new(Some(governance_canister_id)); + + env.default_canister_call_response = + Err((Some(1), "Oh no something was not covered!".to_string())); + + if let Some(ChunkedCanisterWasm { + wasm_module_hash: _, + store_canister_id, + chunk_hashes_list, + }) = chunked_canister_wasm + { + let canister_id = CanisterId::unchecked_from_principal((*store_canister_id).unwrap()); + env.set_call_canister_response( + CanisterId::ic_00(), + "stored_chunks", + Encode!(&CanisterIdRecord::from(canister_id)).unwrap(), + Ok(Encode!(&StoredChunksReply( + chunk_hashes_list + .iter() + .map(|hash| { ChunkHash { hash: hash.clone() } }) + .collect() + )) + .unwrap()), + ); + }; + + env + } + /// This assumes that the current_version is: /// SnsVersion { /// root_wasm_hash: Sha256::hash(&[1]), @@ -4835,6 +5162,7 @@ Payload rendering here"# new_canister_wasm: vec![0, 1, 2, 3], canister_upgrade_arg: Some(vec![4, 5, 6, 7]), mode: Some(1), + chunked_canister_wasm: None, }, )), ..Default::default() @@ -4855,6 +5183,7 @@ Payload rendering here"# new_canister_wasm: vec![], canister_upgrade_arg: Some(vec![4, 5, 6, 7]), mode: Some(1), + chunked_canister_wasm: None, }, )), ..Default::default() diff --git a/rs/sns/governance/src/request_impls.rs b/rs/sns/governance/src/request_impls.rs deleted file mode 100644 index 5e7c35ad2a5..00000000000 --- a/rs/sns/governance/src/request_impls.rs +++ /dev/null @@ -1,85 +0,0 @@ -use ic_nervous_system_clients::Request; - -impl Request for crate::pb::v1::ClaimSwapNeuronsRequest { - type Response = crate::pb::v1::ClaimSwapNeuronsResponse; - const METHOD: &'static str = "claim_swap_neurons"; - const UPDATE: bool = true; -} - -impl Request for crate::pb::v1::FailStuckUpgradeInProgressRequest { - type Response = crate::pb::v1::FailStuckUpgradeInProgressResponse; - const METHOD: &'static str = "fail_stuck_upgrade_in_progress"; - const UPDATE: bool = true; -} - -impl Request for crate::pb::v1::GetMaturityModulationRequest { - type Response = crate::pb::v1::GetMaturityModulationResponse; - const METHOD: &'static str = "get_maturity_modulation"; - const UPDATE: bool = false; -} - -impl Request for crate::pb::v1::GetMetadataRequest { - type Response = crate::pb::v1::GetMetadataResponse; - const METHOD: &'static str = "get_metadata"; - const UPDATE: bool = false; -} - -impl Request for crate::pb::v1::GetSnsInitializationParametersRequest { - type Response = crate::pb::v1::GetSnsInitializationParametersResponse; - const METHOD: &'static str = "get_sns_initialization_parameters"; - const UPDATE: bool = false; -} - -impl Request for crate::pb::v1::GetMode { - type Response = crate::pb::v1::GetModeResponse; - const METHOD: &'static str = "get_mode"; - const UPDATE: bool = false; -} - -impl Request for crate::pb::v1::GetNeuron { - type Response = crate::pb::v1::GetNeuronResponse; - const METHOD: &'static str = "get_neuron"; - const UPDATE: bool = false; -} - -impl Request for crate::pb::v1::GetProposal { - type Response = crate::pb::v1::GetProposalResponse; - const METHOD: &'static str = "get_proposal"; - const UPDATE: bool = false; -} - -impl Request for crate::pb::v1::ListNeurons { - type Response = crate::pb::v1::ListNeuronsResponse; - const METHOD: &'static str = "list_neurons"; - const UPDATE: bool = false; -} - -impl Request for crate::pb::v1::ListProposals { - type Response = crate::pb::v1::ListProposalsResponse; - const METHOD: &'static str = "list_proposals"; - const UPDATE: bool = false; -} - -impl Request for crate::pb::v1::ManageNeuron { - type Response = crate::pb::v1::ManageNeuronResponse; - const METHOD: &'static str = "manage_neuron"; - const UPDATE: bool = true; -} - -impl Request for crate::pb::v1::GetRunningSnsVersionRequest { - type Response = crate::pb::v1::GetRunningSnsVersionResponse; - const METHOD: &'static str = "get_running_sns_version"; - const UPDATE: bool = false; -} - -impl Request for crate::pb::v1::GetUpgradeJournalRequest { - type Response = crate::pb::v1::GetUpgradeJournalResponse; - const METHOD: &'static str = "get_upgrade_journal"; - const UPDATE: bool = false; -} - -impl Request for crate::pb::v1::AdvanceTargetVersionRequest { - type Response = crate::pb::v1::AdvanceTargetVersionResponse; - const METHOD: &'static str = "advance_target_version"; - const UPDATE: bool = true; -} diff --git a/rs/sns/governance/src/types.rs b/rs/sns/governance/src/types.rs index 4b171aa20ed..6996a4a29e6 100644 --- a/rs/sns/governance/src/types.rs +++ b/rs/sns/governance/src/types.rs @@ -26,31 +26,33 @@ use crate::{ nervous_system_function::FunctionType, neuron::Followees, proposal::Action, - ClaimSwapNeuronsError, ClaimSwapNeuronsResponse, ClaimedSwapNeuronStatus, - DefaultFollowees, DeregisterDappCanisters, Empty, ExecuteGenericNervousSystemFunction, - GovernanceError, ManageDappCanisterSettings, ManageLedgerParameters, - ManageNeuronResponse, ManageSnsMetadata, MintSnsTokens, Motion, NervousSystemFunction, - NervousSystemParameters, Neuron, NeuronId, NeuronIds, NeuronPermission, - NeuronPermissionList, NeuronPermissionType, ProposalId, RegisterDappCanisters, - RewardEvent, SnsVersion, TransferSnsTreasuryFunds, UpgradeSnsControlledCanister, - UpgradeSnsToNextVersion, Vote, VotingRewardsParameters, + ChunkedCanisterWasm, ClaimSwapNeuronsError, ClaimSwapNeuronsResponse, + ClaimedSwapNeuronStatus, DefaultFollowees, DeregisterDappCanisters, Empty, + ExecuteGenericNervousSystemFunction, GovernanceError, ManageDappCanisterSettings, + ManageLedgerParameters, ManageNeuronResponse, ManageSnsMetadata, MintSnsTokens, Motion, + NervousSystemFunction, NervousSystemParameters, Neuron, NeuronId, NeuronIds, + NeuronPermission, NeuronPermissionList, NeuronPermissionType, ProposalId, + RegisterDappCanisters, RewardEvent, SnsVersion, TransferSnsTreasuryFunds, + UpgradeSnsControlledCanister, UpgradeSnsToNextVersion, Vote, VotingRewardsParameters, }, }, proposal::ValidGenericNervousSystemFunction, }; use async_trait::async_trait; +use candid::{Decode, Encode}; use ic_base_types::CanisterId; use ic_canister_log::log; use ic_crypto_sha2::Sha256; use ic_icrc1_ledger::UpgradeArgs as LedgerUpgradeArgs; use ic_ledger_core::tokens::TOKEN_SUBDIVIDABLE_BY; -use ic_management_canister_types::CanisterInstallModeError; +use ic_management_canister_types::{CanisterIdRecord, CanisterInstallModeError, StoredChunksReply}; use ic_nervous_system_common::{ hash_to_hex_string, ledger_validation::MAX_LOGO_LENGTH, NervousSystemError, DEFAULT_TRANSFER_FEE, ONE_DAY_SECONDS, ONE_MONTH_SECONDS, ONE_YEAR_SECONDS, }; use ic_nervous_system_common_validation::validate_proposal_url; use ic_nervous_system_proto::pb::v1::{Duration as PbDuration, Percentage}; +use ic_sns_governance_api::format_full_hash; use ic_sns_governance_proposal_criticality::{ ProposalCriticality, VotingDurationParameters, VotingPowerThresholds, }; @@ -72,6 +74,12 @@ const PROPOSAL_EXECUTE_SNS_FUNCTION_PAYLOAD_BYTES_MAX: usize = 70000; /// The number of e8s per governance token; pub const E8S_PER_TOKEN: u64 = TOKEN_SUBDIVIDABLE_BY; +/// The maximum message size for inter-canister calls to a different subnet +/// is 2MiB and thus we restrict the maximum joint size of the canister WASM +/// and argument to 2MB (2,000,000B) to leave some slack for Candid overhead +/// and a few constant-size fields (e.g., compute and memory allocation). +pub const MAX_INSTALL_CODE_WASM_AND_ARG_SIZE: usize = 2_000_000; // 2MB + /// The Governance spec gives each Action a u64 equivalent identifier. This module gives /// those u64 values a human-readable const variable for use in the SNS. pub mod native_action_ids { @@ -215,7 +223,7 @@ impl governance::Mode { } } - pub fn proposal_types_disallowed_in_pre_initialization_swap() -> Vec { + pub fn functions_disallowed_in_pre_initialization_swap() -> Vec { vec![ NervousSystemFunction::manage_nervous_system_parameters(), NervousSystemFunction::transfer_sns_treasury_funds(), @@ -241,7 +249,7 @@ impl governance::Mode { ); } - let is_action_disallowed = Self::proposal_types_disallowed_in_pre_initialization_swap() + let is_action_disallowed = Self::functions_disallowed_in_pre_initialization_swap() .into_iter() .any(|t| t.id == NervousSystemFunction::from(action.clone()).id); @@ -450,6 +458,7 @@ impl NervousSystemParameters { max_dissolve_delay_bonus_percentage: Some(100), max_age_bonus_percentage: Some(25), maturity_modulation_disabled: Some(false), + automatically_advance_target_version: Some(false), } } @@ -517,6 +526,9 @@ impl NervousSystemParameters { maturity_modulation_disabled: self .maturity_modulation_disabled .or(base.maturity_modulation_disabled), + automatically_advance_target_version: self + .automatically_advance_target_version + .or(base.automatically_advance_target_version), } } @@ -961,6 +973,8 @@ impl fmt::Display for GovernanceError { } } +impl std::error::Error for crate::pb::v1::GovernanceError {} + impl From for GovernanceError { fn from(nervous_system_error: NervousSystemError) -> Self { GovernanceError { @@ -1744,6 +1758,7 @@ impl UpgradeSnsControlledCanister { .as_ref() .map(|blob| summarize_blob_field(blob)), mode: self.mode, + chunked_canister_wasm: self.chunked_canister_wasm.clone(), } } @@ -1754,6 +1769,7 @@ impl UpgradeSnsControlledCanister { canister_upgrade_arg: self.canister_upgrade_arg.clone(), mode: self.mode, new_canister_wasm: Vec::new(), + chunked_canister_wasm: self.chunked_canister_wasm.clone(), } } } @@ -2527,6 +2543,256 @@ impl From for Action { } } +pub enum Wasm { + Bytes(Vec), + Chunked { + wasm_module_hash: Vec, + store_canister_id: CanisterId, + chunk_hashes_list: Vec>, + }, +} + +/// Validates that the specified byte sequence meets the following requirements: +/// 1. `new_canister_wasm` starts with Wasm or Gzip magic bytes. +/// 2. Combined length of `new_canister_wasm` and `new_canister_wasm` is within ICP message limits. +fn validate_wasm_bytes( + new_canister_wasm: &[u8], + canister_upgrade_arg: &Option>, +) -> Result<(), Vec> { + let mut defects = vec![]; + + // https://internetcomputer.org/docs/current/references/ic-interface-spec#canister-module-format + const RAW_WASM_HEADER: [u8; 4] = [0, 0x61, 0x73, 0x6d]; + const GZIPPED_WASM_HEADER: [u8; 3] = [0x1f, 0x8b, 0x08]; + + if new_canister_wasm.len() < 4 + || new_canister_wasm[..4] != RAW_WASM_HEADER[..] + && new_canister_wasm[..3] != GZIPPED_WASM_HEADER[..] + { + defects.push("new_canister_wasm lacks the magic value in its header.".into()); + } + + if new_canister_wasm.len().saturating_add( + canister_upgrade_arg + .as_ref() + .map(|arg| arg.len()) + .unwrap_or_default(), + ) >= MAX_INSTALL_CODE_WASM_AND_ARG_SIZE + { + defects.push(format!( + "the maximum canister WASM and argument size \ + for UpgradeSnsControlledCanister is {} bytes.", + MAX_INSTALL_CODE_WASM_AND_ARG_SIZE + )); + } + + if !defects.is_empty() { + return Err(defects); + } + + Ok(()) +} + +async fn validate_chunked_wasm( + env: &dyn Environment, + wasm_module_hash: &Vec, + store_canister_id: CanisterId, + chunk_hashes_list: &[Vec], +) -> Result<(), Vec> { + let mut defects = vec![]; + + match chunk_hashes_list { + [] => { + let defect = "chunked_canister_wasm.chunk_hashes_list cannot be empty.".to_string(); + defects.push(defect); + } + [chunk_hash] if wasm_module_hash != chunk_hash => { + let defect = format!( + "chunked_canister_wasm.chunk_hashes_list specifies only one hash ({}), but \ + it differs from chunked_canister_wasm.wasm_module_hash ({}).", + format_full_hash(chunk_hash), + format_full_hash(&wasm_module_hash[..]), + ); + defects.push(defect); + } + _ => (), + } + + let arg = match Encode!(&CanisterIdRecord::from(store_canister_id)) { + Ok(arg) => arg, + Err(err) => { + let defect = format!("Cannot encode stored_chunks arg: {err}"); + defects.push(defect); + return Err(defects); + } + }; + + // TODO[NNS1-3550]: Enable stored chunks validation on mainnet. + #[cfg(feature = "test")] + let validate_stored_chunks: bool = true; + #[cfg(not(feature = "test"))] + let validate_stored_chunks: bool = false; + if validate_stored_chunks { + // TODO[NNS1-3550]: Switch this call to best-effort. + let stored_chunks_response = env + .call_canister(CanisterId::ic_00(), "stored_chunks", arg) + .await; + + let stored_chunks_response = match stored_chunks_response { + Ok(stored_chunks_response) => stored_chunks_response, + Err(err) => { + let defect = format!("Cannot call stored_chunks for {store_canister_id}: {err:?}"); + defects.push(defect); + return Err(defects); + } + }; + + let stored_chunks_response = match Decode!(&stored_chunks_response, StoredChunksReply) { + Ok(stored_chunks_response) => stored_chunks_response, + Err(err) => { + let defect = format!( + "Cannot decode response from calling stored_chunks for {store_canister_id}: {err}" + ); + defects.push(defect); + return Err(defects); + } + }; + + // Finally, check that the expected chunks were successfully uploaded to the store canister. + let available_chunks = stored_chunks_response + .0 + .iter() + .map(|chunk| format_full_hash(&chunk.hash)) + .collect::>(); + let required_chunks = chunk_hashes_list + .iter() + .map(|chunk| format_full_hash(chunk)) + .collect::>(); + + let missing_chunks = required_chunks + .difference(&available_chunks) + .cloned() + .collect::>(); + if !missing_chunks.is_empty() { + let defect = format!( + "{} out of {} expected WASM chunks were not uploaded to the store canister: {}", + missing_chunks.len(), + required_chunks.len(), + missing_chunks.join(", ") + ); + defects.push(defect); + } + } + + if !defects.is_empty() { + return Err(defects); + } + + Ok(()) +} + +impl Wasm { + /// Returns the list of defects of this Wasm in Err result. + pub async fn validate( + &self, + env: &dyn Environment, + canister_upgrade_arg: &Option>, + ) -> Result<(), Vec> { + match self { + Self::Bytes(bytes) => validate_wasm_bytes(bytes, canister_upgrade_arg), + Self::Chunked { + wasm_module_hash, + store_canister_id, + chunk_hashes_list, + } => { + validate_chunked_wasm(env, wasm_module_hash, *store_canister_id, chunk_hashes_list) + .await + } + } + } + + pub fn description(&self) -> String { + match self { + Self::Bytes(bytes) => { + let canister_wasm_sha256 = { + let mut state = Sha256::new(); + state.write(&bytes[..]); + let sha = state.finish(); + sha.to_vec() + }; + format!( + "Embedded module with {} bytes and SHA256 `{}`.", + bytes.len(), + format_full_hash(&canister_wasm_sha256) + ) + } + Self::Chunked { + wasm_module_hash, + store_canister_id, + chunk_hashes_list, + } => { + format!( + "Remote module stored on canister {} with SHA256 `{}`. \ + Split into {} chunks:\n - {}", + store_canister_id.get(), + format_full_hash(wasm_module_hash), + chunk_hashes_list.len(), + chunk_hashes_list + .iter() + .map(|chunk_hash| { format!("`{}`", format_full_hash(chunk_hash)) }) + .collect::>() + .join("\n - "), + ) + } + } + } +} + +impl TryFrom<&UpgradeSnsControlledCanister> for Wasm { + type Error = String; + + fn try_from(upgrade: &UpgradeSnsControlledCanister) -> Result { + const ERR_PREFIX: &str = "Invalid UpgradeSnsControlledCanister"; + + match ( + &upgrade.new_canister_wasm[..], + &upgrade.chunked_canister_wasm, + ) { + ( + [], + Some(ChunkedCanisterWasm { + wasm_module_hash, + store_canister_id, + chunk_hashes_list, + }), + ) => { + let Some(store_canister_id) = store_canister_id else { + return Err(format!( + "{ERR_PREFIX}.chunked_canister_wasm.store_canister_id must be \ + specified." + )); + }; + + let store_canister_id = CanisterId::try_from_principal_id(*store_canister_id) + .map_err(|err| { + format!("{ERR_PREFIX}.chunked_canister_wasm.store_canister_id: {err}") + })?; + + Ok(Self::Chunked { + wasm_module_hash: wasm_module_hash.clone(), + store_canister_id, + chunk_hashes_list: chunk_hashes_list.clone(), + }) + } + (bytes, None) => Ok(Self::Bytes(bytes.to_vec())), + _ => Err(format!( + "{ERR_PREFIX}: Either .new_canister_wasm or \ + .chunked_canister_wasm (but not both) must be specified." + )), + } + } +} + impl UpgradeSnsControlledCanister { // Gets the install mode if it is set, otherwise defaults to Upgrade. // This function is not called `mode_or_default` because `or_default` usually @@ -2677,25 +2943,21 @@ pub mod test_helpers { default_canister_call_response: Ok(vec![]), required_canister_call_invocations: Arc::new(RwLock::new(vec![])), // This needs to be non-zero - now: std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_secs(), + now: Self::DEFAULT_TEST_START_TIMESTAMP_SECONDS, } } } impl NativeEnvironment { + pub const DEFAULT_TEST_START_TIMESTAMP_SECONDS: u64 = 999_111_000_u64; + pub fn new(local_canister_id: Option) -> Self { Self { local_canister_id, canister_calls_map: Default::default(), default_canister_call_response: Ok(vec![]), required_canister_call_invocations: Arc::new(RwLock::new(vec![])), - now: std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_secs(), + now: Self::DEFAULT_TEST_START_TIMESTAMP_SECONDS, } } @@ -2837,1210 +3099,4 @@ pub mod test_helpers { } } #[cfg(test)] -pub(crate) mod tests { - use super::*; - use crate::pb::v1::{ - claim_swap_neurons_request::neuron_recipe, - governance::Mode::PreInitializationSwap, - nervous_system_function::{FunctionType, GenericNervousSystemFunction}, - neuron::Followees, - ExecuteGenericNervousSystemFunction, Proposal, ProposalData, VotingRewardsParameters, - }; - use ic_base_types::PrincipalId; - use ic_nervous_system_common_test_keys::TEST_USER1_PRINCIPAL; - use ic_nervous_system_proto::pb::v1::Principals; - use lazy_static::lazy_static; - use maplit::{btreemap, hashset}; - use std::convert::TryInto; - - #[test] - fn test_voting_period_parameters() { - let non_critical_action = Action::Motion(Default::default()); - let critical_action = Action::TransferSnsTreasuryFunds(Default::default()); - - let normal_nervous_system_parameters = NervousSystemParameters { - initial_voting_period_seconds: Some(4 * ONE_DAY_SECONDS), - wait_for_quiet_deadline_increase_seconds: Some(2 * ONE_DAY_SECONDS), - ..Default::default() - }; - assert_eq!( - non_critical_action.voting_duration_parameters(&normal_nervous_system_parameters), - VotingDurationParameters { - initial_voting_period: PbDuration { - seconds: Some(4 * ONE_DAY_SECONDS), - }, - wait_for_quiet_deadline_increase: PbDuration { - seconds: Some(2 * ONE_DAY_SECONDS), - } - }, - ); - assert_eq!( - critical_action.voting_duration_parameters(&normal_nervous_system_parameters), - VotingDurationParameters { - initial_voting_period: PbDuration { - seconds: Some(5 * ONE_DAY_SECONDS), - }, - wait_for_quiet_deadline_increase: PbDuration { - seconds: Some(2 * ONE_DAY_SECONDS + ONE_DAY_SECONDS / 2), - } - }, - ); - - // This is even slower than the hard-coded values (5 days initial and 2.5 days wait for - // quiet) for critical proposals. Therefore, these values are used for both normal and - // critical proposals. - let slow_nervous_system_parameters = NervousSystemParameters { - initial_voting_period_seconds: Some(7 * ONE_DAY_SECONDS), - wait_for_quiet_deadline_increase_seconds: Some(4 * ONE_DAY_SECONDS), - ..Default::default() - }; - assert_eq!( - non_critical_action.voting_duration_parameters(&slow_nervous_system_parameters), - VotingDurationParameters { - initial_voting_period: PbDuration { - seconds: Some(7 * ONE_DAY_SECONDS), - }, - wait_for_quiet_deadline_increase: PbDuration { - seconds: Some(4 * ONE_DAY_SECONDS), - } - }, - ); - assert_eq!( - critical_action.voting_duration_parameters(&slow_nervous_system_parameters), - VotingDurationParameters { - initial_voting_period: PbDuration { - seconds: Some(7 * ONE_DAY_SECONDS), - }, - wait_for_quiet_deadline_increase: PbDuration { - seconds: Some(4 * ONE_DAY_SECONDS), - } - }, - ); - } - - #[test] - fn test_nervous_system_parameters_validate() { - NervousSystemParameters::with_default_values() - .validate() - .unwrap(); - - let invalid_params = vec![ - NervousSystemParameters { - neuron_minimum_stake_e8s: None, - ..NervousSystemParameters::with_default_values() - }, - NervousSystemParameters { - transaction_fee_e8s: Some(100), - neuron_minimum_stake_e8s: Some(10), - ..NervousSystemParameters::with_default_values() - }, - NervousSystemParameters { - transaction_fee_e8s: None, - ..NervousSystemParameters::with_default_values() - }, - NervousSystemParameters { - max_proposals_to_keep_per_action: None, - ..NervousSystemParameters::with_default_values() - }, - NervousSystemParameters { - max_proposals_to_keep_per_action: Some(0), - ..NervousSystemParameters::with_default_values() - }, - NervousSystemParameters { - max_proposals_to_keep_per_action: Some( - NervousSystemParameters::MAX_PROPOSALS_TO_KEEP_PER_ACTION_CEILING + 1, - ), - ..NervousSystemParameters::with_default_values() - }, - NervousSystemParameters { - initial_voting_period_seconds: None, - ..NervousSystemParameters::with_default_values() - }, - NervousSystemParameters { - initial_voting_period_seconds: Some( - NervousSystemParameters::INITIAL_VOTING_PERIOD_SECONDS_FLOOR - 1, - ), - ..NervousSystemParameters::with_default_values() - }, - NervousSystemParameters { - initial_voting_period_seconds: Some( - NervousSystemParameters::INITIAL_VOTING_PERIOD_SECONDS_CEILING + 1, - ), - ..NervousSystemParameters::with_default_values() - }, - NervousSystemParameters { - default_followees: None, - ..NervousSystemParameters::with_default_values() - }, - NervousSystemParameters { - max_number_of_neurons: None, - ..NervousSystemParameters::with_default_values() - }, - NervousSystemParameters { - max_number_of_neurons: Some(0), - ..NervousSystemParameters::with_default_values() - }, - NervousSystemParameters { - max_number_of_neurons: Some( - NervousSystemParameters::MAX_NUMBER_OF_NEURONS_CEILING + 1, - ), - ..NervousSystemParameters::with_default_values() - }, - NervousSystemParameters { - neuron_minimum_dissolve_delay_to_vote_seconds: None, - ..NervousSystemParameters::with_default_values() - }, - NervousSystemParameters { - max_dissolve_delay_seconds: Some(10), - neuron_minimum_dissolve_delay_to_vote_seconds: Some(20), - ..NervousSystemParameters::with_default_values() - }, - NervousSystemParameters { - max_followees_per_function: None, - ..NervousSystemParameters::with_default_values() - }, - NervousSystemParameters { - max_followees_per_function: Some( - NervousSystemParameters::MAX_FOLLOWEES_PER_FUNCTION_CEILING + 1, - ), - ..NervousSystemParameters::with_default_values() - }, - NervousSystemParameters { - max_dissolve_delay_seconds: None, - ..NervousSystemParameters::with_default_values() - }, - NervousSystemParameters { - max_neuron_age_for_age_bonus: None, - ..NervousSystemParameters::with_default_values() - }, - NervousSystemParameters { - max_number_of_proposals_with_ballots: None, - ..NervousSystemParameters::with_default_values() - }, - NervousSystemParameters { - max_number_of_proposals_with_ballots: Some(0), - ..NervousSystemParameters::with_default_values() - }, - NervousSystemParameters { - max_number_of_proposals_with_ballots: Some( - NervousSystemParameters::MAX_NUMBER_OF_PROPOSALS_WITH_BALLOTS_CEILING + 1, - ), - ..NervousSystemParameters::with_default_values() - }, - NervousSystemParameters { - neuron_claimer_permissions: Some(NeuronPermissionList { - permissions: vec![NeuronPermissionType::Vote as i32], - }), - ..NervousSystemParameters::with_default_values() - }, - NervousSystemParameters { - neuron_claimer_permissions: None, - ..NervousSystemParameters::with_default_values() - }, - NervousSystemParameters { - neuron_grantable_permissions: None, - ..NervousSystemParameters::with_default_values() - }, - NervousSystemParameters { - max_number_of_principals_per_neuron: Some(0), - ..NervousSystemParameters::with_default_values() - }, - NervousSystemParameters { - max_number_of_principals_per_neuron: Some(1000), - ..NervousSystemParameters::with_default_values() - }, - NervousSystemParameters { - voting_rewards_parameters: Some(VotingRewardsParameters { - round_duration_seconds: None, - ..Default::default() - }), - ..NervousSystemParameters::with_default_values() - }, - NervousSystemParameters { - max_number_of_principals_per_neuron: Some(4), - ..NervousSystemParameters::with_default_values() - }, - ]; - - for params in invalid_params { - params.validate().unwrap_err(); - } - } - - #[test] - fn test_inherit_from() { - let default_params = NervousSystemParameters::with_default_values(); - - let proposed_params = NervousSystemParameters { - transaction_fee_e8s: Some(124), - max_number_of_neurons: Some(566), - max_number_of_proposals_with_ballots: Some(9801), - default_followees: Some(Default::default()), - - // Set all other fields to None. - ..Default::default() - }; - - let new_params = proposed_params.inherit_from(&default_params); - let expected_params = NervousSystemParameters { - transaction_fee_e8s: Some(124), - max_number_of_neurons: Some(566), - max_number_of_proposals_with_ballots: Some(9801), - default_followees: Some(Default::default()), - ..default_params.clone() - }; - - assert_eq!(new_params, expected_params); - - assert_eq!(new_params.maturity_modulation_disabled, Some(false)); - - let disable_maturity_modulation = NervousSystemParameters { - maturity_modulation_disabled: Some(true), - - // Set all other fields to None. - ..Default::default() - }; - - assert_eq!( - disable_maturity_modulation.inherit_from(&default_params), - NervousSystemParameters { - maturity_modulation_disabled: Some(true), - ..default_params - }, - ); - } - - lazy_static! { - static ref MANAGE_NEURON_COMMANDS: (Vec, Vec, manage_neuron::Command) = { - use manage_neuron::Command; - - #[rustfmt::skip] - let allowed_in_pre_initialization_swap = vec! [ - Command::Follow (Default::default()), - Command::MakeProposal (Default::default()), - Command::RegisterVote (Default::default()), - Command::AddNeuronPermissions (Default::default()), - Command::RemoveNeuronPermissions (Default::default()), - ]; - - #[rustfmt::skip] - let disallowed_in_pre_initialization_swap = vec! [ - Command::Configure (Default::default()), - Command::Disburse (Default::default()), - Command::Split (Default::default()), - Command::MergeMaturity (Default::default()), - Command::DisburseMaturity (Default::default()), - ]; - - // Only the swap canister is allowed to do this in PreInitializationSwap. - let claim_or_refresh = Command::ClaimOrRefresh(Default::default()); - - (allowed_in_pre_initialization_swap, disallowed_in_pre_initialization_swap, claim_or_refresh) - }; - } - - #[should_panic] - #[test] - fn test_mode_allows_manage_neuron_command_or_err_unspecified_kaboom() { - let caller_is_swap_canister = true; - let innocuous_command = &MANAGE_NEURON_COMMANDS.0[0]; - let _clippy = governance::Mode::Unspecified - .allows_manage_neuron_command_or_err(innocuous_command, caller_is_swap_canister); - } - - #[test] - fn test_mode_allows_manage_neuron_command_or_err_normal_is_generally_ok() { - let mut commands = MANAGE_NEURON_COMMANDS.0.clone(); - commands.append(&mut MANAGE_NEURON_COMMANDS.1.clone()); - commands.push(MANAGE_NEURON_COMMANDS.2.clone()); - - for command in commands { - for caller_is_swap_canister in [true, false] { - let result = governance::Mode::Normal - .allows_manage_neuron_command_or_err(&command, caller_is_swap_canister); - assert!(result.is_ok(), "{:#?}", result); - } - } - } - - #[test] - fn test_mode_allows_manage_neuron_command_or_err_pre_initialization_swap_ok() { - let allowed = &MANAGE_NEURON_COMMANDS.0; - for command in allowed { - for caller_is_swap_canister in [true, false] { - let result = PreInitializationSwap - .allows_manage_neuron_command_or_err(command, caller_is_swap_canister); - assert!(result.is_ok(), "{:#?}", result); - } - } - } - - #[test] - fn test_mode_allows_manage_neuron_command_or_err_pre_initialization_swap_verboten() { - let disallowed = &MANAGE_NEURON_COMMANDS.1; - for command in disallowed { - for caller_is_swap_canister in [true, false] { - let result = PreInitializationSwap - .allows_manage_neuron_command_or_err(command, caller_is_swap_canister); - assert!(result.is_err(), "{:#?}", result); - } - } - } - - #[test] - fn test_mode_allows_manage_neuron_command_or_err_pre_initialization_swap_claim_or_refresh() { - let claim_or_refresh = &MANAGE_NEURON_COMMANDS.2; - - let caller_is_swap_canister = false; - let result = PreInitializationSwap - .allows_manage_neuron_command_or_err(claim_or_refresh, caller_is_swap_canister); - assert!(result.is_err(), "{:#?}", result); - - let caller_is_swap_canister = true; - let result = PreInitializationSwap - .allows_manage_neuron_command_or_err(claim_or_refresh, caller_is_swap_canister); - assert!(result.is_ok(), "{:#?}", result); - } - - const ROOT_TARGETING_FUNCTION_ID: u64 = 1001; - const GOVERNANCE_TARGETING_FUNCTION_ID: u64 = 1002; - const LEDGER_TARGETING_FUNCTION_ID: u64 = 1003; - const RANDOM_CANISTER_TARGETING_FUNCTION_ID: u64 = 1004; - - #[rustfmt::skip] - lazy_static! { - static ref ROOT_CANISTER_ID: PrincipalId = [101][..].try_into().unwrap(); - static ref GOVERNANCE_CANISTER_ID: PrincipalId = [102][..].try_into().unwrap(); - static ref LEDGER_CANISTER_ID: PrincipalId = [103][..].try_into().unwrap(); - static ref RANDOM_CANISTER_ID: PrincipalId = [0xDE, 0xAD, 0xBE, 0xEF][..].try_into().unwrap(); - - static ref PROPOSAL_ACTIONS: ( - Vec, // Allowed in PreInitializationSwap. - Vec, // Disallowed in PreInitializationSwap. - Vec, // ExecuteGenericNervousSystemFunction where target is root, governance, or ledger - Action, // ExecuteGenericNervousSystemFunction, but target is not one of the distinguished canisters. - ) = { - let allowed_in_pre_initialization_swap = vec! [ - Action::Motion(Default::default()), - Action::AddGenericNervousSystemFunction(Default::default()), - Action::RemoveGenericNervousSystemFunction(Default::default()), - ]; - - let disallowed_in_pre_initialization_swap = vec! [ - Action::ManageNervousSystemParameters(Default::default()), - Action::TransferSnsTreasuryFunds(Default::default()), - Action::MintSnsTokens(Default::default()), - Action::UpgradeSnsControlledCanister(Default::default()), - Action::RegisterDappCanisters(Default::default()), - Action::DeregisterDappCanisters(Default::default()), - ]; - - // Conditionally allow: No targeting SNS canisters. - fn execute(function_id: u64) -> Action { - Action::ExecuteGenericNervousSystemFunction(ExecuteGenericNervousSystemFunction { - function_id, - ..Default::default() - }) - } - - let target_sns_canister_actions = vec! [ - execute( ROOT_TARGETING_FUNCTION_ID), - execute(GOVERNANCE_TARGETING_FUNCTION_ID), - execute( LEDGER_TARGETING_FUNCTION_ID), - ]; - - let target_random_canister_action = execute(RANDOM_CANISTER_TARGETING_FUNCTION_ID); - - ( - allowed_in_pre_initialization_swap, - disallowed_in_pre_initialization_swap, - target_sns_canister_actions, - target_random_canister_action - ) - }; - - static ref ID_TO_NERVOUS_SYSTEM_FUNCTION: BTreeMap = { - fn new_fn(function_id: u64, target_canister_id: &PrincipalId) -> NervousSystemFunction { - NervousSystemFunction { - id: function_id, - name: "Amaze".to_string(), - description: Some("Best function evar.".to_string()), - function_type: Some(FunctionType::GenericNervousSystemFunction(GenericNervousSystemFunction { - target_canister_id: Some(*target_canister_id), - target_method_name: Some("Foo".to_string()), - validator_canister_id: Some(*target_canister_id), - validator_method_name: Some("Bar".to_string()), - })), - } - } - - vec![ - new_fn( ROOT_TARGETING_FUNCTION_ID, &ROOT_CANISTER_ID), - new_fn( GOVERNANCE_TARGETING_FUNCTION_ID, &GOVERNANCE_CANISTER_ID), - new_fn( LEDGER_TARGETING_FUNCTION_ID, &LEDGER_CANISTER_ID), - new_fn(RANDOM_CANISTER_TARGETING_FUNCTION_ID, &RANDOM_CANISTER_ID), - ] - .into_iter() - .map(|f| (f.id, f)) - .collect() - }; - - static ref DISALLOWED_TARGET_CANISTER_IDS: HashSet = hashset! { - CanisterId::unchecked_from_principal(*ROOT_CANISTER_ID), - CanisterId::unchecked_from_principal(*GOVERNANCE_CANISTER_ID), - CanisterId::unchecked_from_principal(*LEDGER_CANISTER_ID), - }; - } - - #[should_panic] - #[test] - fn test_mode_allows_proposal_action_or_err_unspecified_kaboom() { - let innocuous_action = &PROPOSAL_ACTIONS.0[0]; - let _clippy = governance::Mode::Unspecified.allows_proposal_action_or_err( - innocuous_action, - &DISALLOWED_TARGET_CANISTER_IDS, - &ID_TO_NERVOUS_SYSTEM_FUNCTION, - ); - } - - #[test] - fn test_mode_allows_proposal_action_or_err_normal_is_always_ok() { - // Flatten PROPOSAL_ACTIONS into one big Vec. - let mut actions = PROPOSAL_ACTIONS.0.clone(); - actions.append(&mut PROPOSAL_ACTIONS.1.clone()); - actions.append(&mut PROPOSAL_ACTIONS.2.clone()); - actions.push(PROPOSAL_ACTIONS.3.clone()); - - for action in actions { - let result = governance::Mode::Normal.allows_proposal_action_or_err( - &action, - &DISALLOWED_TARGET_CANISTER_IDS, - &ID_TO_NERVOUS_SYSTEM_FUNCTION, - ); - assert!(result.is_ok(), "{:#?} {:#?}", result, action); - } - } - - #[test] - fn test_mode_allows_proposal_action_or_err_pre_initialization_swap_happy() { - for action in &PROPOSAL_ACTIONS.0 { - let result = PreInitializationSwap.allows_proposal_action_or_err( - action, - &DISALLOWED_TARGET_CANISTER_IDS, - &ID_TO_NERVOUS_SYSTEM_FUNCTION, - ); - assert!(result.is_ok(), "{:#?} {:#?}", result, action); - } - } - - #[test] - fn test_mode_allows_proposal_action_or_err_pre_initialization_swap_sad() { - for action in &PROPOSAL_ACTIONS.1 { - let result = PreInitializationSwap.allows_proposal_action_or_err( - action, - &DISALLOWED_TARGET_CANISTER_IDS, - &ID_TO_NERVOUS_SYSTEM_FUNCTION, - ); - assert!(result.is_err(), "{:#?}", action); - } - } - - #[test] - fn test_mode_allows_proposal_action_or_err_pre_initialization_swap_disallows_targeting_an_sns_canister( - ) { - for action in &PROPOSAL_ACTIONS.2 { - let result = PreInitializationSwap.allows_proposal_action_or_err( - action, - &DISALLOWED_TARGET_CANISTER_IDS, - &ID_TO_NERVOUS_SYSTEM_FUNCTION, - ); - assert!(result.is_err(), "{:#?}", action); - } - } - - #[test] - fn test_mode_allows_proposal_action_or_err_pre_initialization_swap_allows_targeting_a_random_canister( - ) { - let action = &PROPOSAL_ACTIONS.3; - let result = PreInitializationSwap.allows_proposal_action_or_err( - action, - &DISALLOWED_TARGET_CANISTER_IDS, - &ID_TO_NERVOUS_SYSTEM_FUNCTION, - ); - assert!(result.is_ok(), "{:#?} {:#?}", result, action); - } - - #[test] - fn test_mode_allows_proposal_action_or_err_function_not_found() { - let execute = - Action::ExecuteGenericNervousSystemFunction(ExecuteGenericNervousSystemFunction { - function_id: 0xDEADBEF, - ..Default::default() - }); - - let result = governance::Mode::PreInitializationSwap.allows_proposal_action_or_err( - &execute, - &DISALLOWED_TARGET_CANISTER_IDS, - &ID_TO_NERVOUS_SYSTEM_FUNCTION, - ); - - let err = match result { - Err(err) => err, - Ok(_) => panic!( - "Make proposal is supposed to result in NotFound when \ - it specifies an unknown function ID." - ), - }; - assert_eq!(err.error_type, ErrorType::NotFound as i32, "{:#?}", err); - } - - #[should_panic] - #[test] - fn test_mode_allows_proposal_action_or_err_panic_when_function_has_no_type() { - let function_id = 42; - - let execute = - Action::ExecuteGenericNervousSystemFunction(ExecuteGenericNervousSystemFunction { - function_id, - ..Default::default() - }); - - let mut functions = ID_TO_NERVOUS_SYSTEM_FUNCTION.clone(); - functions.insert( - function_id, - NervousSystemFunction { - id: function_id, - function_type: None, // This is evil. - name: "Toxic".to_string(), - description: None, - }, - ); - - let _unused = governance::Mode::PreInitializationSwap.allows_proposal_action_or_err( - &execute, - &DISALLOWED_TARGET_CANISTER_IDS, - &functions, - ); - } - - #[should_panic] - #[test] - fn test_mode_allows_proposal_action_or_err_panic_when_function_has_no_target_canister_id() { - let function_id = 42; - - let execute = - Action::ExecuteGenericNervousSystemFunction(ExecuteGenericNervousSystemFunction { - function_id, - ..Default::default() - }); - - let mut functions = ID_TO_NERVOUS_SYSTEM_FUNCTION.clone(); - functions.insert( - function_id, - NervousSystemFunction { - id: function_id, - name: "Toxic".to_string(), - description: None, - function_type: Some(FunctionType::GenericNervousSystemFunction( - GenericNervousSystemFunction { - target_canister_id: None, // This is evil. - ..Default::default() - }, - )), - }, - ); - - let _unused = governance::Mode::PreInitializationSwap.allows_proposal_action_or_err( - &execute, - &DISALLOWED_TARGET_CANISTER_IDS, - &functions, - ); - } - - #[test] - fn test_sns_metadata_validate() { - let default = SnsMetadata { - logo: Some("data:image/png;base64,aGVsbG8gZnJvbSBkZmluaXR5IQ==".to_string()), - url: Some("https://forum.dfinity.org".to_string()), - name: Some("X".repeat(SnsMetadata::MIN_NAME_LENGTH)), - description: Some("X".repeat(SnsMetadata::MIN_DESCRIPTION_LENGTH)), - }; - - let valid_sns_metadata = vec![ - default.clone(), - SnsMetadata { - url: Some("https://forum.dfinity.org/foo/bar/?".to_string()), - ..default.clone() - }, - SnsMetadata { - url: Some("https://forum.dfinity.org/foo/bar/?".to_string()), - ..default.clone() - }, - SnsMetadata { - url: Some("https://any-url.com/foo/bar/?".to_string()), - ..default.clone() - }, - ]; - - let invalid_sns_metadata = vec![ - SnsMetadata { - name: None, - ..default.clone() - }, - SnsMetadata { - name: Some("X".repeat(SnsMetadata::MAX_NAME_LENGTH + 1)), - ..default.clone() - }, - SnsMetadata { - name: Some("X".repeat(SnsMetadata::MIN_NAME_LENGTH - 1)), - ..default.clone() - }, - SnsMetadata { - description: None, - ..default.clone() - }, - SnsMetadata { - description: Some("X".repeat(SnsMetadata::MAX_DESCRIPTION_LENGTH + 1)), - ..default.clone() - }, - SnsMetadata { - description: Some("X".repeat(SnsMetadata::MIN_DESCRIPTION_LENGTH - 1)), - ..default.clone() - }, - SnsMetadata { - logo: Some("X".repeat(MAX_LOGO_LENGTH + 1)), - ..default.clone() - }, - SnsMetadata { - url: None, - ..default.clone() - }, - SnsMetadata { - url: Some("X".repeat(SnsMetadata::MAX_URL_LENGTH + 1)), - ..default.clone() - }, - SnsMetadata { - url: Some("X".to_string()), - ..default.clone() - }, - SnsMetadata { - url: Some("X".repeat(SnsMetadata::MIN_URL_LENGTH - 1)), - ..default.clone() - }, - SnsMetadata { - url: Some("file://forum.dfinity.org".to_string()), - ..default.clone() - }, - SnsMetadata { - url: Some("https://".to_string()), - ..default.clone() - }, - SnsMetadata { - url: Some("https://forum.dfinity.org/https://forum.dfinity.org".to_string()), - ..default.clone() - }, - SnsMetadata { - url: Some("https://example@forum.dfinity.org".to_string()), - ..default.clone() - }, - SnsMetadata { - url: Some("http://internetcomputer".to_string()), - ..default.clone() - }, - SnsMetadata { - url: Some("mailto:example@internetcomputer.org".to_string()), - ..default.clone() - }, - SnsMetadata { - url: Some("internetcomputer".to_string()), - ..default - }, - ]; - - for sns_metadata in invalid_sns_metadata { - if sns_metadata.validate().is_ok() { - panic!("Invalid metadata passed validation: {:?}", sns_metadata); - } - } - - for sns_metadata in valid_sns_metadata { - if sns_metadata.validate().is_err() { - panic!("Valid metadata failed validation: {:?}", sns_metadata); - } - } - } - - impl NeuronRecipe { - fn validate_default_direct_participant() -> Self { - Self { - controller: Some(*TEST_USER1_PRINCIPAL), - neuron_id: Some(NeuronId::new_test_neuron_id(0)), - stake_e8s: Some(E8S_PER_TOKEN), - dissolve_delay_seconds: Some(3 * ONE_MONTH_SECONDS), - followees: Some(NeuronIds::from(vec![NeuronId::new_test_neuron_id(1)])), - participant: Some(Participant::Direct(neuron_recipe::Direct {})), - } - } - - fn validate_default_neurons_fund() -> Self { - Self { - controller: Some(PrincipalId::from(ic_nns_constants::GOVERNANCE_CANISTER_ID)), - neuron_id: Some(NeuronId::new_test_neuron_id(0)), - stake_e8s: Some(E8S_PER_TOKEN), - dissolve_delay_seconds: Some(3 * ONE_MONTH_SECONDS), - followees: Some(NeuronIds::from(vec![NeuronId::new_test_neuron_id(1)])), - participant: Some(Participant::NeuronsFund(neuron_recipe::NeuronsFund { - nns_neuron_id: Some(2), - nns_neuron_controller: Some(PrincipalId::new_user_test_id(13847)), - nns_neuron_hotkeys: Some(Principals::from(vec![ - PrincipalId::new_user_test_id(13848), - PrincipalId::new_user_test_id(13849), - ])), - })), - } - } - } - - mod neuron_recipe_validate_tests { - use super::*; - - const NEURON_MINIMUM_STAKE_E8S: u64 = E8S_PER_TOKEN; - const MAX_FOLLOWEES_PER_FUNCTION: u64 = 1; - const MAX_NUMBER_OF_PRINCIPALS_PER_NEURON: u64 = 5; - - fn validate_recipe(recipe: &NeuronRecipe) -> Result<(), String> { - recipe.validate( - NEURON_MINIMUM_STAKE_E8S, - MAX_FOLLOWEES_PER_FUNCTION, - MAX_NUMBER_OF_PRINCIPALS_PER_NEURON, - ) - } - - #[test] - fn test_default_direct_participant_is_valid() { - validate_recipe(&NeuronRecipe::validate_default_direct_participant()).unwrap(); - } - - #[test] - fn test_default_neurons_fund_is_valid() { - validate_recipe(&NeuronRecipe::validate_default_neurons_fund()).unwrap(); - } - - #[test] - fn test_invalid_missing_controller() { - let recipe = NeuronRecipe { - controller: None, - ..NeuronRecipe::validate_default_direct_participant() - }; - validate_recipe(&recipe).unwrap_err(); - } - - #[test] - fn test_invalid_missing_neuron_id() { - let recipe = NeuronRecipe { - neuron_id: None, - ..NeuronRecipe::validate_default_direct_participant() - }; - validate_recipe(&recipe).unwrap_err(); - } - - #[test] - fn test_invalid_missing_stake() { - let recipe = NeuronRecipe { - stake_e8s: None, - ..NeuronRecipe::validate_default_direct_participant() - }; - validate_recipe(&recipe).unwrap_err(); - } - - #[test] - fn test_invalid_low_stake() { - let recipe = NeuronRecipe { - stake_e8s: Some(NEURON_MINIMUM_STAKE_E8S - 1), - ..NeuronRecipe::validate_default_direct_participant() - }; - validate_recipe(&recipe).unwrap_err(); - } - - #[test] - fn test_invalid_missing_dissolve_delay() { - let recipe = NeuronRecipe { - dissolve_delay_seconds: None, - ..NeuronRecipe::validate_default_direct_participant() - }; - validate_recipe(&recipe).unwrap_err(); - } - - #[test] - fn test_invalid_missing_followees() { - let recipe = NeuronRecipe { - followees: None, - ..NeuronRecipe::validate_default_direct_participant() - }; - validate_recipe(&recipe).unwrap_err(); - } - - #[test] - fn test_invalid_too_many_followees() { - let recipe = NeuronRecipe { - followees: Some(NeuronIds::from(vec![ - NeuronId::new_test_neuron_id(1), - NeuronId::new_test_neuron_id(2), - ])), - ..NeuronRecipe::validate_default_direct_participant() - }; - validate_recipe(&recipe).unwrap_err(); - } - - #[test] - fn test_invalid_missing_participant() { - let recipe = NeuronRecipe { - participant: None, - ..NeuronRecipe::validate_default_direct_participant() - }; - validate_recipe(&recipe).unwrap_err(); - } - - #[test] - fn test_invalid_neurons_fund_missing_nns_neuron_id() { - let recipe = NeuronRecipe { - participant: Some(Participant::NeuronsFund(neuron_recipe::NeuronsFund { - nns_neuron_id: None, - nns_neuron_controller: Some(PrincipalId::new_user_test_id(13847)), - nns_neuron_hotkeys: Some(Principals::from(vec![ - PrincipalId::new_user_test_id(13848), - ])), - })), - ..NeuronRecipe::validate_default_neurons_fund() - }; - validate_recipe(&recipe).unwrap_err(); - } - - #[test] - fn test_invalid_neurons_fund_missing_controller() { - let recipe = NeuronRecipe { - participant: Some(Participant::NeuronsFund(neuron_recipe::NeuronsFund { - nns_neuron_id: Some(2), - nns_neuron_controller: None, - nns_neuron_hotkeys: Some(Principals::from(vec![ - PrincipalId::new_user_test_id(13848), - ])), - })), - ..NeuronRecipe::validate_default_neurons_fund() - }; - validate_recipe(&recipe).unwrap_err(); - } - - #[test] - fn test_invalid_neurons_fund_missing_hotkeys() { - let recipe = NeuronRecipe { - participant: Some(Participant::NeuronsFund(neuron_recipe::NeuronsFund { - nns_neuron_id: Some(2), - nns_neuron_controller: Some(PrincipalId::new_user_test_id(13847)), - nns_neuron_hotkeys: None, - })), - ..NeuronRecipe::validate_default_neurons_fund() - }; - validate_recipe(&recipe).unwrap_err(); - } - - #[test] - fn test_invalid_neurons_fund_too_many_hotkeys() { - let recipe = NeuronRecipe { - participant: Some(Participant::NeuronsFund(neuron_recipe::NeuronsFund { - nns_neuron_id: Some(2), - nns_neuron_controller: Some(PrincipalId::new_user_test_id(13847)), - nns_neuron_hotkeys: Some(Principals::from(vec![ - PrincipalId::new_user_test_id(13848), - PrincipalId::new_user_test_id(13849), - PrincipalId::new_user_test_id(13810), - PrincipalId::new_user_test_id(13811), - PrincipalId::new_user_test_id(13812), - ])), - })), - ..NeuronRecipe::validate_default_neurons_fund() - }; - validate_recipe(&recipe).unwrap_err(); - } - - #[test] - fn test_valid_zero_dissolve_delay() { - let recipe = NeuronRecipe { - dissolve_delay_seconds: Some(0), - ..NeuronRecipe::validate_default_direct_participant() - }; - validate_recipe(&recipe).unwrap(); - } - - #[test] - fn test_valid_empty_followees() { - let recipe = NeuronRecipe { - followees: Some(NeuronIds::from(vec![])), - ..NeuronRecipe::validate_default_direct_participant() - }; - validate_recipe(&recipe).unwrap(); - } - - #[test] - fn test_valid_minimum_stake() { - let recipe = NeuronRecipe { - stake_e8s: Some(NEURON_MINIMUM_STAKE_E8S), - ..NeuronRecipe::validate_default_neurons_fund() - }; - validate_recipe(&recipe).unwrap(); - } - } - - #[test] - fn test_voting_rewards_parameters_set_to_zero_by_default() { - let parameters = NervousSystemParameters::with_default_values(); - parameters.validate().unwrap(); - let voting_rewards_parameters = parameters.voting_rewards_parameters.unwrap(); - assert_eq!( - voting_rewards_parameters - .initial_reward_rate_basis_points - .unwrap(), - 0 - ); - assert_eq!( - voting_rewards_parameters - .final_reward_rate_basis_points - .unwrap(), - 0 - ); - } - - #[test] - #[should_panic] - fn test_nervous_system_parameters_wont_validate_without_voting_rewards_parameters() { - let mut parameters = NervousSystemParameters::with_default_values(); - parameters.voting_rewards_parameters = None; - // This is where we expect to panic. - parameters.validate().unwrap(); - } - - #[test] - fn test_nervous_system_parameters_wont_validate_without_the_required_claimer_permissions() { - for permission_to_omit in NervousSystemParameters::REQUIRED_NEURON_CLAIMER_PERMISSIONS { - let mut parameters = NervousSystemParameters::with_default_values(); - parameters.neuron_claimer_permissions = Some( - NervousSystemParameters::REQUIRED_NEURON_CLAIMER_PERMISSIONS - .iter() - .filter(|p| *p != permission_to_omit) - .cloned() - .collect::>() - .into(), - ); - parameters.validate().unwrap_err(); - } - } - - #[test] - fn test_validate_logo_lets_base64_through() { - SnsMetadata::validate_logo("data:image/png;base64,aGVsbG8gZnJvbSBkZmluaXR5IQ==").unwrap(); - } - - #[test] - fn test_validate_logo_doesnt_let_non_base64_through() { - // `_` is not in the base64 character set we're using - // so we should panic here. - SnsMetadata::validate_logo("data:image/png;base64,aGVsbG8gZnJvbSBkZmluaXR5IQ==_") - .unwrap_err(); - } - - #[test] - fn test_neuron_permission_list_display_impl() { - let neuron_permission_list = NeuronPermissionList::all(); - assert_eq!( - format!("permissions: {neuron_permission_list}"), - format!("permissions: [Unspecified, ConfigureDissolveState, ManagePrincipals, SubmitProposal, Vote, Disburse, Split, MergeMaturity, DisburseMaturity, StakeMaturity, ManageVotingPermission]") - ); - } - - #[test] - fn test_neuron_permission_list_display_impl_doesnt_panic_unknown_permission() { - let invalid_permission = 10000; - let neuron_permission_list = { - let mut neuron_permission_list = NeuronPermissionList::all(); - neuron_permission_list.permissions.push(invalid_permission); // Add an unknown permission to the list - neuron_permission_list - }; - assert_eq!( - format!("permissions: {neuron_permission_list}"), - format!("permissions: [Unspecified, ConfigureDissolveState, ManagePrincipals, SubmitProposal, Vote, Disburse, Split, MergeMaturity, DisburseMaturity, StakeMaturity, ManageVotingPermission, ]") - ); - } - - mod neuron_recipe_construct_followees_tests { - use super::*; - - #[test] - fn test_direct_participant_empty_followees() { - let [b0] = NeuronId::test_neuron_ids(); - let recipe = NeuronRecipe { - followees: Some(NeuronIds::from(vec![])), - neuron_id: Some(b0.clone()), - ..NeuronRecipe::validate_default_direct_participant() - }; - assert_eq!(recipe.construct_followees(), btreemap! {}); - } - - #[test] - fn test_direct_participant_single_followee() { - let [b0, b1] = NeuronId::test_neuron_ids(); - let w = u64::from(&Action::Unspecified(Empty {})); - let recipe = NeuronRecipe { - followees: Some(NeuronIds::from(vec![b0.clone()])), - neuron_id: Some(b1.clone()), - ..NeuronRecipe::validate_default_direct_participant() - }; - assert_eq!( - recipe.construct_followees(), - btreemap! { w => Followees { followees: vec![b0.clone()] } } - ); - } - - #[test] - fn test_direct_participant_multiple_followees() { - let [b0, b1, b2] = NeuronId::test_neuron_ids(); - let w = u64::from(&Action::Unspecified(Empty {})); - let recipe = NeuronRecipe { - followees: Some(NeuronIds::from(vec![b0.clone(), b1.clone()])), - neuron_id: Some(b2.clone()), - ..NeuronRecipe::validate_default_direct_participant() - }; - assert_eq!( - recipe.construct_followees(), - btreemap! { w => Followees { followees: vec![b0.clone(), b1.clone()] } } - ); - } - - #[test] - fn test_neurons_fund_empty_followees() { - let [b1] = NeuronId::test_neuron_ids(); - let recipe = NeuronRecipe { - followees: Some(NeuronIds::from(vec![])), - neuron_id: Some(b1.clone()), - ..NeuronRecipe::validate_default_neurons_fund() - }; - assert_eq!(recipe.construct_followees(), btreemap! {}); - } - - #[test] - fn test_neurons_fund_single_followee() { - let [b0, b1] = NeuronId::test_neuron_ids(); - let w = u64::from(&Action::Unspecified(Empty {})); - let recipe = NeuronRecipe { - followees: Some(NeuronIds::from(vec![b0.clone()])), - neuron_id: Some(b1.clone()), - ..NeuronRecipe::validate_default_neurons_fund() - }; - assert_eq!( - recipe.construct_followees(), - btreemap! { w => Followees { followees: vec![b0.clone()] } } - ); - } - - #[test] - fn test_neurons_fund_multiple_followees() { - let [b0, b1, b2, b3] = NeuronId::test_neuron_ids(); - let w = u64::from(&Action::Unspecified(Empty {})); - let recipe = NeuronRecipe { - followees: Some(NeuronIds::from(vec![b0.clone(), b1.clone(), b2.clone()])), - neuron_id: Some(b3.clone()), - ..NeuronRecipe::validate_default_neurons_fund() - }; - assert_eq!( - recipe.construct_followees(), - btreemap! { w => Followees { followees: vec![b0.clone(), b1.clone(), b2.clone()] } } - ); - } - } - - #[test] - fn test_summarize_blob_field() { - for len in 0..=64 { - let direct_copy_input = (0..len).collect::>(); - - assert_eq!(summarize_blob_field(&direct_copy_input), direct_copy_input); - } - - let too_long = (0..65).collect::>(); - let result = summarize_blob_field(&too_long); - assert_ne!(result, too_long); - assert!(result.len() > 64, "{:X?}", result); - - let result = String::from_utf8(summarize_blob_field(&too_long)).unwrap(); - assert!( - result.contains("⚠️ NOT THE ORIGINAL CONTENTS OF THIS FIELD ⚠"), - "{:X?}", - result, - ); - assert!(result.contains("00 01 02 03"), "{:X?}", result); - assert!(result.contains("3D 3E 3F 40"), "{:X?}", result); - assert!(result.contains("Length: 65"), "{:X?}", result); - assert!( - // SHA256 - result.contains( - // Independently calculating using Python. - "4B FD 2C 8B 6F 1E EC 7A \ - 2A FE B4 8B 93 4E E4 B2 \ - 69 41 82 02 7E 6D 0F C0 \ - 75 07 4F 2F AB B3 17 81", - ), - "{:X?}", - result, - ); - } - - #[test] - fn test_limited_for_get_proposal() { - let motion_proposal = ProposalData { - proposal: Some(Proposal { - action: Some(Action::Motion(Motion { - motion_text: "Hello, world!".to_string(), - })), - ..Default::default() - }), - ..Default::default() - }; - - assert_eq!(motion_proposal.limited_for_get_proposal(), motion_proposal,); - - let upgrade_sns_controlled_canister_proposal = ProposalData { - proposal: Some(Proposal { - action: Some(Action::UpgradeSnsControlledCanister( - UpgradeSnsControlledCanister { - new_canister_wasm: (0..=255).collect(), - ..Default::default() - }, - )), - ..Default::default() - }), - ..Default::default() - }; - - assert_ne!( - upgrade_sns_controlled_canister_proposal.limited_for_get_proposal(), - upgrade_sns_controlled_canister_proposal, - ); - - let execute_generic_nervous_system_function_proposal = ProposalData { - proposal: Some(Proposal { - action: Some(Action::ExecuteGenericNervousSystemFunction( - ExecuteGenericNervousSystemFunction { - payload: (0..=255).collect(), - ..Default::default() - }, - )), - ..Default::default() - }), - ..Default::default() - }; - - assert_ne!( - execute_generic_nervous_system_function_proposal.limited_for_get_proposal(), - execute_generic_nervous_system_function_proposal, - ); - } -} +mod tests; diff --git a/rs/sns/governance/src/types/tests.rs b/rs/sns/governance/src/types/tests.rs new file mode 100644 index 00000000000..68d731cc512 --- /dev/null +++ b/rs/sns/governance/src/types/tests.rs @@ -0,0 +1,1494 @@ +use super::*; +use crate::pb::v1::{ + claim_swap_neurons_request::neuron_recipe, + governance::Mode::PreInitializationSwap, + nervous_system_function::{FunctionType, GenericNervousSystemFunction}, + neuron::Followees, + ExecuteGenericNervousSystemFunction, Proposal, ProposalData, VotingRewardsParameters, +}; +use futures::FutureExt; +use ic_base_types::PrincipalId; +use ic_management_canister_types::ChunkHash; +use ic_nervous_system_common_test_keys::TEST_USER1_PRINCIPAL; +use ic_nervous_system_proto::pb::v1::Principals; +use lazy_static::lazy_static; +use maplit::{btreemap, hashset}; +use std::convert::TryInto; +use test_helpers::NativeEnvironment; + +#[test] +fn test_voting_period_parameters() { + let non_critical_action = Action::Motion(Default::default()); + let critical_action = Action::TransferSnsTreasuryFunds(Default::default()); + + let normal_nervous_system_parameters = NervousSystemParameters { + initial_voting_period_seconds: Some(4 * ONE_DAY_SECONDS), + wait_for_quiet_deadline_increase_seconds: Some(2 * ONE_DAY_SECONDS), + ..Default::default() + }; + assert_eq!( + non_critical_action.voting_duration_parameters(&normal_nervous_system_parameters), + VotingDurationParameters { + initial_voting_period: PbDuration { + seconds: Some(4 * ONE_DAY_SECONDS), + }, + wait_for_quiet_deadline_increase: PbDuration { + seconds: Some(2 * ONE_DAY_SECONDS), + } + }, + ); + assert_eq!( + critical_action.voting_duration_parameters(&normal_nervous_system_parameters), + VotingDurationParameters { + initial_voting_period: PbDuration { + seconds: Some(5 * ONE_DAY_SECONDS), + }, + wait_for_quiet_deadline_increase: PbDuration { + seconds: Some(2 * ONE_DAY_SECONDS + ONE_DAY_SECONDS / 2), + } + }, + ); + + // This is even slower than the hard-coded values (5 days initial and 2.5 days wait for + // quiet) for critical proposals. Therefore, these values are used for both normal and + // critical proposals. + let slow_nervous_system_parameters = NervousSystemParameters { + initial_voting_period_seconds: Some(7 * ONE_DAY_SECONDS), + wait_for_quiet_deadline_increase_seconds: Some(4 * ONE_DAY_SECONDS), + ..Default::default() + }; + assert_eq!( + non_critical_action.voting_duration_parameters(&slow_nervous_system_parameters), + VotingDurationParameters { + initial_voting_period: PbDuration { + seconds: Some(7 * ONE_DAY_SECONDS), + }, + wait_for_quiet_deadline_increase: PbDuration { + seconds: Some(4 * ONE_DAY_SECONDS), + } + }, + ); + assert_eq!( + critical_action.voting_duration_parameters(&slow_nervous_system_parameters), + VotingDurationParameters { + initial_voting_period: PbDuration { + seconds: Some(7 * ONE_DAY_SECONDS), + }, + wait_for_quiet_deadline_increase: PbDuration { + seconds: Some(4 * ONE_DAY_SECONDS), + } + }, + ); +} + +#[test] +fn test_nervous_system_parameters_validate() { + NervousSystemParameters::with_default_values() + .validate() + .unwrap(); + + let invalid_params = vec![ + NervousSystemParameters { + neuron_minimum_stake_e8s: None, + ..NervousSystemParameters::with_default_values() + }, + NervousSystemParameters { + transaction_fee_e8s: Some(100), + neuron_minimum_stake_e8s: Some(10), + ..NervousSystemParameters::with_default_values() + }, + NervousSystemParameters { + transaction_fee_e8s: None, + ..NervousSystemParameters::with_default_values() + }, + NervousSystemParameters { + max_proposals_to_keep_per_action: None, + ..NervousSystemParameters::with_default_values() + }, + NervousSystemParameters { + max_proposals_to_keep_per_action: Some(0), + ..NervousSystemParameters::with_default_values() + }, + NervousSystemParameters { + max_proposals_to_keep_per_action: Some( + NervousSystemParameters::MAX_PROPOSALS_TO_KEEP_PER_ACTION_CEILING + 1, + ), + ..NervousSystemParameters::with_default_values() + }, + NervousSystemParameters { + initial_voting_period_seconds: None, + ..NervousSystemParameters::with_default_values() + }, + NervousSystemParameters { + initial_voting_period_seconds: Some( + NervousSystemParameters::INITIAL_VOTING_PERIOD_SECONDS_FLOOR - 1, + ), + ..NervousSystemParameters::with_default_values() + }, + NervousSystemParameters { + initial_voting_period_seconds: Some( + NervousSystemParameters::INITIAL_VOTING_PERIOD_SECONDS_CEILING + 1, + ), + ..NervousSystemParameters::with_default_values() + }, + NervousSystemParameters { + default_followees: None, + ..NervousSystemParameters::with_default_values() + }, + NervousSystemParameters { + max_number_of_neurons: None, + ..NervousSystemParameters::with_default_values() + }, + NervousSystemParameters { + max_number_of_neurons: Some(0), + ..NervousSystemParameters::with_default_values() + }, + NervousSystemParameters { + max_number_of_neurons: Some(NervousSystemParameters::MAX_NUMBER_OF_NEURONS_CEILING + 1), + ..NervousSystemParameters::with_default_values() + }, + NervousSystemParameters { + neuron_minimum_dissolve_delay_to_vote_seconds: None, + ..NervousSystemParameters::with_default_values() + }, + NervousSystemParameters { + max_dissolve_delay_seconds: Some(10), + neuron_minimum_dissolve_delay_to_vote_seconds: Some(20), + ..NervousSystemParameters::with_default_values() + }, + NervousSystemParameters { + max_followees_per_function: None, + ..NervousSystemParameters::with_default_values() + }, + NervousSystemParameters { + max_followees_per_function: Some( + NervousSystemParameters::MAX_FOLLOWEES_PER_FUNCTION_CEILING + 1, + ), + ..NervousSystemParameters::with_default_values() + }, + NervousSystemParameters { + max_dissolve_delay_seconds: None, + ..NervousSystemParameters::with_default_values() + }, + NervousSystemParameters { + max_neuron_age_for_age_bonus: None, + ..NervousSystemParameters::with_default_values() + }, + NervousSystemParameters { + max_number_of_proposals_with_ballots: None, + ..NervousSystemParameters::with_default_values() + }, + NervousSystemParameters { + max_number_of_proposals_with_ballots: Some(0), + ..NervousSystemParameters::with_default_values() + }, + NervousSystemParameters { + max_number_of_proposals_with_ballots: Some( + NervousSystemParameters::MAX_NUMBER_OF_PROPOSALS_WITH_BALLOTS_CEILING + 1, + ), + ..NervousSystemParameters::with_default_values() + }, + NervousSystemParameters { + neuron_claimer_permissions: Some(NeuronPermissionList { + permissions: vec![NeuronPermissionType::Vote as i32], + }), + ..NervousSystemParameters::with_default_values() + }, + NervousSystemParameters { + neuron_claimer_permissions: None, + ..NervousSystemParameters::with_default_values() + }, + NervousSystemParameters { + neuron_grantable_permissions: None, + ..NervousSystemParameters::with_default_values() + }, + NervousSystemParameters { + max_number_of_principals_per_neuron: Some(0), + ..NervousSystemParameters::with_default_values() + }, + NervousSystemParameters { + max_number_of_principals_per_neuron: Some(1000), + ..NervousSystemParameters::with_default_values() + }, + NervousSystemParameters { + voting_rewards_parameters: Some(VotingRewardsParameters { + round_duration_seconds: None, + ..Default::default() + }), + ..NervousSystemParameters::with_default_values() + }, + NervousSystemParameters { + max_number_of_principals_per_neuron: Some(4), + ..NervousSystemParameters::with_default_values() + }, + ]; + + for params in invalid_params { + params.validate().unwrap_err(); + } +} + +#[test] +fn test_inherit_from() { + let default_params = NervousSystemParameters::with_default_values(); + + let proposed_params = NervousSystemParameters { + transaction_fee_e8s: Some(124), + max_number_of_neurons: Some(566), + max_number_of_proposals_with_ballots: Some(9801), + default_followees: Some(Default::default()), + + // Set all other fields to None. + ..Default::default() + }; + + let new_params = proposed_params.inherit_from(&default_params); + let expected_params = NervousSystemParameters { + transaction_fee_e8s: Some(124), + max_number_of_neurons: Some(566), + max_number_of_proposals_with_ballots: Some(9801), + default_followees: Some(Default::default()), + ..default_params.clone() + }; + + assert_eq!(new_params, expected_params); + + assert_eq!(new_params.maturity_modulation_disabled, Some(false)); + + let disable_maturity_modulation = NervousSystemParameters { + maturity_modulation_disabled: Some(true), + + // Set all other fields to None. + ..Default::default() + }; + + assert_eq!( + disable_maturity_modulation.inherit_from(&default_params), + NervousSystemParameters { + maturity_modulation_disabled: Some(true), + ..default_params + }, + ); +} + +lazy_static! { + static ref MANAGE_NEURON_COMMANDS: (Vec, Vec, manage_neuron::Command) = { + use manage_neuron::Command; + + #[rustfmt::skip] + let allowed_in_pre_initialization_swap = vec! [ + Command::Follow (Default::default()), + Command::MakeProposal (Default::default()), + Command::RegisterVote (Default::default()), + Command::AddNeuronPermissions (Default::default()), + Command::RemoveNeuronPermissions (Default::default()), + ]; + + #[rustfmt::skip] + let disallowed_in_pre_initialization_swap = vec! [ + Command::Configure (Default::default()), + Command::Disburse (Default::default()), + Command::Split (Default::default()), + Command::MergeMaturity (Default::default()), + Command::DisburseMaturity (Default::default()), + ]; + + // Only the swap canister is allowed to do this in PreInitializationSwap. + let claim_or_refresh = Command::ClaimOrRefresh(Default::default()); + + (allowed_in_pre_initialization_swap, disallowed_in_pre_initialization_swap, claim_or_refresh) + }; +} + +#[should_panic] +#[test] +fn test_mode_allows_manage_neuron_command_or_err_unspecified_kaboom() { + let caller_is_swap_canister = true; + let innocuous_command = &MANAGE_NEURON_COMMANDS.0[0]; + let _clippy = governance::Mode::Unspecified + .allows_manage_neuron_command_or_err(innocuous_command, caller_is_swap_canister); +} + +#[test] +fn test_mode_allows_manage_neuron_command_or_err_normal_is_generally_ok() { + let mut commands = MANAGE_NEURON_COMMANDS.0.clone(); + commands.append(&mut MANAGE_NEURON_COMMANDS.1.clone()); + commands.push(MANAGE_NEURON_COMMANDS.2.clone()); + + for command in commands { + for caller_is_swap_canister in [true, false] { + let result = governance::Mode::Normal + .allows_manage_neuron_command_or_err(&command, caller_is_swap_canister); + assert!(result.is_ok(), "{:#?}", result); + } + } +} + +#[test] +fn test_mode_allows_manage_neuron_command_or_err_pre_initialization_swap_ok() { + let allowed = &MANAGE_NEURON_COMMANDS.0; + for command in allowed { + for caller_is_swap_canister in [true, false] { + let result = PreInitializationSwap + .allows_manage_neuron_command_or_err(command, caller_is_swap_canister); + assert!(result.is_ok(), "{:#?}", result); + } + } +} + +#[test] +fn test_mode_allows_manage_neuron_command_or_err_pre_initialization_swap_verboten() { + let disallowed = &MANAGE_NEURON_COMMANDS.1; + for command in disallowed { + for caller_is_swap_canister in [true, false] { + let result = PreInitializationSwap + .allows_manage_neuron_command_or_err(command, caller_is_swap_canister); + assert!(result.is_err(), "{:#?}", result); + } + } +} + +#[test] +fn test_mode_allows_manage_neuron_command_or_err_pre_initialization_swap_claim_or_refresh() { + let claim_or_refresh = &MANAGE_NEURON_COMMANDS.2; + + let caller_is_swap_canister = false; + let result = PreInitializationSwap + .allows_manage_neuron_command_or_err(claim_or_refresh, caller_is_swap_canister); + assert!(result.is_err(), "{:#?}", result); + + let caller_is_swap_canister = true; + let result = PreInitializationSwap + .allows_manage_neuron_command_or_err(claim_or_refresh, caller_is_swap_canister); + assert!(result.is_ok(), "{:#?}", result); +} + +const ROOT_TARGETING_FUNCTION_ID: u64 = 1001; +const GOVERNANCE_TARGETING_FUNCTION_ID: u64 = 1002; +const LEDGER_TARGETING_FUNCTION_ID: u64 = 1003; +const RANDOM_CANISTER_TARGETING_FUNCTION_ID: u64 = 1004; + +#[rustfmt::skip] +lazy_static! { + static ref ROOT_CANISTER_ID: PrincipalId = [101][..].try_into().unwrap(); + static ref GOVERNANCE_CANISTER_ID: PrincipalId = [102][..].try_into().unwrap(); + static ref LEDGER_CANISTER_ID: PrincipalId = [103][..].try_into().unwrap(); + static ref RANDOM_CANISTER_ID: PrincipalId = [0xDE, 0xAD, 0xBE, 0xEF][..].try_into().unwrap(); + + static ref PROPOSAL_ACTIONS: ( + Vec, // Allowed in PreInitializationSwap. + Vec, // Disallowed in PreInitializationSwap. + Vec, // ExecuteGenericNervousSystemFunction where target is root, governance, or ledger + Action, // ExecuteGenericNervousSystemFunction, but target is not one of the distinguished canisters. + ) = { + let allowed_in_pre_initialization_swap = vec! [ + Action::Motion(Default::default()), + Action::AddGenericNervousSystemFunction(Default::default()), + Action::RemoveGenericNervousSystemFunction(Default::default()), + ]; + + let disallowed_in_pre_initialization_swap = vec! [ + Action::ManageNervousSystemParameters(Default::default()), + Action::TransferSnsTreasuryFunds(Default::default()), + Action::MintSnsTokens(Default::default()), + Action::UpgradeSnsControlledCanister(Default::default()), + Action::RegisterDappCanisters(Default::default()), + Action::DeregisterDappCanisters(Default::default()), + ]; + + // Conditionally allow: No targeting SNS canisters. + fn execute(function_id: u64) -> Action { + Action::ExecuteGenericNervousSystemFunction(ExecuteGenericNervousSystemFunction { + function_id, + ..Default::default() + }) + } + + let target_sns_canister_actions = vec! [ + execute( ROOT_TARGETING_FUNCTION_ID), + execute(GOVERNANCE_TARGETING_FUNCTION_ID), + execute( LEDGER_TARGETING_FUNCTION_ID), + ]; + + let target_random_canister_action = execute(RANDOM_CANISTER_TARGETING_FUNCTION_ID); + + ( + allowed_in_pre_initialization_swap, + disallowed_in_pre_initialization_swap, + target_sns_canister_actions, + target_random_canister_action + ) + }; + + static ref ID_TO_NERVOUS_SYSTEM_FUNCTION: BTreeMap = { + fn new_fn(function_id: u64, target_canister_id: &PrincipalId) -> NervousSystemFunction { + NervousSystemFunction { + id: function_id, + name: "Amaze".to_string(), + description: Some("Best function evar.".to_string()), + function_type: Some(FunctionType::GenericNervousSystemFunction(GenericNervousSystemFunction { + target_canister_id: Some(*target_canister_id), + target_method_name: Some("Foo".to_string()), + validator_canister_id: Some(*target_canister_id), + validator_method_name: Some("Bar".to_string()), + })), + } + } + + vec![ + new_fn( ROOT_TARGETING_FUNCTION_ID, &ROOT_CANISTER_ID), + new_fn( GOVERNANCE_TARGETING_FUNCTION_ID, &GOVERNANCE_CANISTER_ID), + new_fn( LEDGER_TARGETING_FUNCTION_ID, &LEDGER_CANISTER_ID), + new_fn(RANDOM_CANISTER_TARGETING_FUNCTION_ID, &RANDOM_CANISTER_ID), + ] + .into_iter() + .map(|f| (f.id, f)) + .collect() + }; + + static ref DISALLOWED_TARGET_CANISTER_IDS: HashSet = hashset! { + CanisterId::unchecked_from_principal(*ROOT_CANISTER_ID), + CanisterId::unchecked_from_principal(*GOVERNANCE_CANISTER_ID), + CanisterId::unchecked_from_principal(*LEDGER_CANISTER_ID), + }; +} + +#[should_panic] +#[test] +fn test_mode_allows_proposal_action_or_err_unspecified_kaboom() { + let innocuous_action = &PROPOSAL_ACTIONS.0[0]; + let _clippy = governance::Mode::Unspecified.allows_proposal_action_or_err( + innocuous_action, + &DISALLOWED_TARGET_CANISTER_IDS, + &ID_TO_NERVOUS_SYSTEM_FUNCTION, + ); +} + +#[test] +fn test_mode_allows_proposal_action_or_err_normal_is_always_ok() { + // Flatten PROPOSAL_ACTIONS into one big Vec. + let mut actions = PROPOSAL_ACTIONS.0.clone(); + actions.append(&mut PROPOSAL_ACTIONS.1.clone()); + actions.append(&mut PROPOSAL_ACTIONS.2.clone()); + actions.push(PROPOSAL_ACTIONS.3.clone()); + + for action in actions { + let result = governance::Mode::Normal.allows_proposal_action_or_err( + &action, + &DISALLOWED_TARGET_CANISTER_IDS, + &ID_TO_NERVOUS_SYSTEM_FUNCTION, + ); + assert!(result.is_ok(), "{:#?} {:#?}", result, action); + } +} + +#[test] +fn test_mode_allows_proposal_action_or_err_pre_initialization_swap_happy() { + for action in &PROPOSAL_ACTIONS.0 { + let result = PreInitializationSwap.allows_proposal_action_or_err( + action, + &DISALLOWED_TARGET_CANISTER_IDS, + &ID_TO_NERVOUS_SYSTEM_FUNCTION, + ); + assert!(result.is_ok(), "{:#?} {:#?}", result, action); + } +} + +#[test] +fn test_mode_allows_proposal_action_or_err_pre_initialization_swap_sad() { + for action in &PROPOSAL_ACTIONS.1 { + let result = PreInitializationSwap.allows_proposal_action_or_err( + action, + &DISALLOWED_TARGET_CANISTER_IDS, + &ID_TO_NERVOUS_SYSTEM_FUNCTION, + ); + assert!(result.is_err(), "{:#?}", action); + } +} + +#[test] +fn test_mode_allows_proposal_action_or_err_pre_initialization_swap_disallows_targeting_an_sns_canister( +) { + for action in &PROPOSAL_ACTIONS.2 { + let result = PreInitializationSwap.allows_proposal_action_or_err( + action, + &DISALLOWED_TARGET_CANISTER_IDS, + &ID_TO_NERVOUS_SYSTEM_FUNCTION, + ); + assert!(result.is_err(), "{:#?}", action); + } +} + +#[test] +fn test_mode_allows_proposal_action_or_err_pre_initialization_swap_allows_targeting_a_random_canister( +) { + let action = &PROPOSAL_ACTIONS.3; + let result = PreInitializationSwap.allows_proposal_action_or_err( + action, + &DISALLOWED_TARGET_CANISTER_IDS, + &ID_TO_NERVOUS_SYSTEM_FUNCTION, + ); + assert!(result.is_ok(), "{:#?} {:#?}", result, action); +} + +#[test] +fn test_mode_allows_proposal_action_or_err_function_not_found() { + let execute = + Action::ExecuteGenericNervousSystemFunction(ExecuteGenericNervousSystemFunction { + function_id: 0xDEADBEF, + ..Default::default() + }); + + let result = governance::Mode::PreInitializationSwap.allows_proposal_action_or_err( + &execute, + &DISALLOWED_TARGET_CANISTER_IDS, + &ID_TO_NERVOUS_SYSTEM_FUNCTION, + ); + + let err = match result { + Err(err) => err, + Ok(_) => panic!( + "Make proposal is supposed to result in NotFound when \ + it specifies an unknown function ID." + ), + }; + assert_eq!(err.error_type, ErrorType::NotFound as i32, "{:#?}", err); +} + +#[should_panic] +#[test] +fn test_mode_allows_proposal_action_or_err_panic_when_function_has_no_type() { + let function_id = 42; + + let execute = + Action::ExecuteGenericNervousSystemFunction(ExecuteGenericNervousSystemFunction { + function_id, + ..Default::default() + }); + + let mut functions = ID_TO_NERVOUS_SYSTEM_FUNCTION.clone(); + functions.insert( + function_id, + NervousSystemFunction { + id: function_id, + function_type: None, // This is evil. + name: "Toxic".to_string(), + description: None, + }, + ); + + let _unused = governance::Mode::PreInitializationSwap.allows_proposal_action_or_err( + &execute, + &DISALLOWED_TARGET_CANISTER_IDS, + &functions, + ); +} + +#[should_panic] +#[test] +fn test_mode_allows_proposal_action_or_err_panic_when_function_has_no_target_canister_id() { + let function_id = 42; + + let execute = + Action::ExecuteGenericNervousSystemFunction(ExecuteGenericNervousSystemFunction { + function_id, + ..Default::default() + }); + + let mut functions = ID_TO_NERVOUS_SYSTEM_FUNCTION.clone(); + functions.insert( + function_id, + NervousSystemFunction { + id: function_id, + name: "Toxic".to_string(), + description: None, + function_type: Some(FunctionType::GenericNervousSystemFunction( + GenericNervousSystemFunction { + target_canister_id: None, // This is evil. + ..Default::default() + }, + )), + }, + ); + + let _unused = governance::Mode::PreInitializationSwap.allows_proposal_action_or_err( + &execute, + &DISALLOWED_TARGET_CANISTER_IDS, + &functions, + ); +} + +#[test] +fn test_sns_metadata_validate() { + let default = SnsMetadata { + logo: Some("data:image/png;base64,aGVsbG8gZnJvbSBkZmluaXR5IQ==".to_string()), + url: Some("https://forum.dfinity.org".to_string()), + name: Some("X".repeat(SnsMetadata::MIN_NAME_LENGTH)), + description: Some("X".repeat(SnsMetadata::MIN_DESCRIPTION_LENGTH)), + }; + + let valid_sns_metadata = vec![ + default.clone(), + SnsMetadata { + url: Some("https://forum.dfinity.org/foo/bar/?".to_string()), + ..default.clone() + }, + SnsMetadata { + url: Some("https://forum.dfinity.org/foo/bar/?".to_string()), + ..default.clone() + }, + SnsMetadata { + url: Some("https://any-url.com/foo/bar/?".to_string()), + ..default.clone() + }, + ]; + + let invalid_sns_metadata = vec![ + SnsMetadata { + name: None, + ..default.clone() + }, + SnsMetadata { + name: Some("X".repeat(SnsMetadata::MAX_NAME_LENGTH + 1)), + ..default.clone() + }, + SnsMetadata { + name: Some("X".repeat(SnsMetadata::MIN_NAME_LENGTH - 1)), + ..default.clone() + }, + SnsMetadata { + description: None, + ..default.clone() + }, + SnsMetadata { + description: Some("X".repeat(SnsMetadata::MAX_DESCRIPTION_LENGTH + 1)), + ..default.clone() + }, + SnsMetadata { + description: Some("X".repeat(SnsMetadata::MIN_DESCRIPTION_LENGTH - 1)), + ..default.clone() + }, + SnsMetadata { + logo: Some("X".repeat(MAX_LOGO_LENGTH + 1)), + ..default.clone() + }, + SnsMetadata { + url: None, + ..default.clone() + }, + SnsMetadata { + url: Some("X".repeat(SnsMetadata::MAX_URL_LENGTH + 1)), + ..default.clone() + }, + SnsMetadata { + url: Some("X".to_string()), + ..default.clone() + }, + SnsMetadata { + url: Some("X".repeat(SnsMetadata::MIN_URL_LENGTH - 1)), + ..default.clone() + }, + SnsMetadata { + url: Some("file://forum.dfinity.org".to_string()), + ..default.clone() + }, + SnsMetadata { + url: Some("https://".to_string()), + ..default.clone() + }, + SnsMetadata { + url: Some("https://forum.dfinity.org/https://forum.dfinity.org".to_string()), + ..default.clone() + }, + SnsMetadata { + url: Some("https://example@forum.dfinity.org".to_string()), + ..default.clone() + }, + SnsMetadata { + url: Some("http://internetcomputer".to_string()), + ..default.clone() + }, + SnsMetadata { + url: Some("mailto:example@internetcomputer.org".to_string()), + ..default.clone() + }, + SnsMetadata { + url: Some("internetcomputer".to_string()), + ..default + }, + ]; + + for sns_metadata in invalid_sns_metadata { + if sns_metadata.validate().is_ok() { + panic!("Invalid metadata passed validation: {:?}", sns_metadata); + } + } + + for sns_metadata in valid_sns_metadata { + if sns_metadata.validate().is_err() { + panic!("Valid metadata failed validation: {:?}", sns_metadata); + } + } +} + +impl NeuronRecipe { + fn validate_default_direct_participant() -> Self { + Self { + controller: Some(*TEST_USER1_PRINCIPAL), + neuron_id: Some(NeuronId::new_test_neuron_id(0)), + stake_e8s: Some(E8S_PER_TOKEN), + dissolve_delay_seconds: Some(3 * ONE_MONTH_SECONDS), + followees: Some(NeuronIds::from(vec![NeuronId::new_test_neuron_id(1)])), + participant: Some(Participant::Direct(neuron_recipe::Direct {})), + } + } + + fn validate_default_neurons_fund() -> Self { + Self { + controller: Some(PrincipalId::from(ic_nns_constants::GOVERNANCE_CANISTER_ID)), + neuron_id: Some(NeuronId::new_test_neuron_id(0)), + stake_e8s: Some(E8S_PER_TOKEN), + dissolve_delay_seconds: Some(3 * ONE_MONTH_SECONDS), + followees: Some(NeuronIds::from(vec![NeuronId::new_test_neuron_id(1)])), + participant: Some(Participant::NeuronsFund(neuron_recipe::NeuronsFund { + nns_neuron_id: Some(2), + nns_neuron_controller: Some(PrincipalId::new_user_test_id(13847)), + nns_neuron_hotkeys: Some(Principals::from(vec![ + PrincipalId::new_user_test_id(13848), + PrincipalId::new_user_test_id(13849), + ])), + })), + } + } +} + +mod neuron_recipe_validate_tests { + use super::*; + + const NEURON_MINIMUM_STAKE_E8S: u64 = E8S_PER_TOKEN; + const MAX_FOLLOWEES_PER_FUNCTION: u64 = 1; + const MAX_NUMBER_OF_PRINCIPALS_PER_NEURON: u64 = 5; + + fn validate_recipe(recipe: &NeuronRecipe) -> Result<(), String> { + recipe.validate( + NEURON_MINIMUM_STAKE_E8S, + MAX_FOLLOWEES_PER_FUNCTION, + MAX_NUMBER_OF_PRINCIPALS_PER_NEURON, + ) + } + + #[test] + fn test_default_direct_participant_is_valid() { + validate_recipe(&NeuronRecipe::validate_default_direct_participant()).unwrap(); + } + + #[test] + fn test_default_neurons_fund_is_valid() { + validate_recipe(&NeuronRecipe::validate_default_neurons_fund()).unwrap(); + } + + #[test] + fn test_invalid_missing_controller() { + let recipe = NeuronRecipe { + controller: None, + ..NeuronRecipe::validate_default_direct_participant() + }; + validate_recipe(&recipe).unwrap_err(); + } + + #[test] + fn test_invalid_missing_neuron_id() { + let recipe = NeuronRecipe { + neuron_id: None, + ..NeuronRecipe::validate_default_direct_participant() + }; + validate_recipe(&recipe).unwrap_err(); + } + + #[test] + fn test_invalid_missing_stake() { + let recipe = NeuronRecipe { + stake_e8s: None, + ..NeuronRecipe::validate_default_direct_participant() + }; + validate_recipe(&recipe).unwrap_err(); + } + + #[test] + fn test_invalid_low_stake() { + let recipe = NeuronRecipe { + stake_e8s: Some(NEURON_MINIMUM_STAKE_E8S - 1), + ..NeuronRecipe::validate_default_direct_participant() + }; + validate_recipe(&recipe).unwrap_err(); + } + + #[test] + fn test_invalid_missing_dissolve_delay() { + let recipe = NeuronRecipe { + dissolve_delay_seconds: None, + ..NeuronRecipe::validate_default_direct_participant() + }; + validate_recipe(&recipe).unwrap_err(); + } + + #[test] + fn test_invalid_missing_followees() { + let recipe = NeuronRecipe { + followees: None, + ..NeuronRecipe::validate_default_direct_participant() + }; + validate_recipe(&recipe).unwrap_err(); + } + + #[test] + fn test_invalid_too_many_followees() { + let recipe = NeuronRecipe { + followees: Some(NeuronIds::from(vec![ + NeuronId::new_test_neuron_id(1), + NeuronId::new_test_neuron_id(2), + ])), + ..NeuronRecipe::validate_default_direct_participant() + }; + validate_recipe(&recipe).unwrap_err(); + } + + #[test] + fn test_invalid_missing_participant() { + let recipe = NeuronRecipe { + participant: None, + ..NeuronRecipe::validate_default_direct_participant() + }; + validate_recipe(&recipe).unwrap_err(); + } + + #[test] + fn test_invalid_neurons_fund_missing_nns_neuron_id() { + let recipe = NeuronRecipe { + participant: Some(Participant::NeuronsFund(neuron_recipe::NeuronsFund { + nns_neuron_id: None, + nns_neuron_controller: Some(PrincipalId::new_user_test_id(13847)), + nns_neuron_hotkeys: Some(Principals::from(vec![PrincipalId::new_user_test_id( + 13848, + )])), + })), + ..NeuronRecipe::validate_default_neurons_fund() + }; + validate_recipe(&recipe).unwrap_err(); + } + + #[test] + fn test_invalid_neurons_fund_missing_controller() { + let recipe = NeuronRecipe { + participant: Some(Participant::NeuronsFund(neuron_recipe::NeuronsFund { + nns_neuron_id: Some(2), + nns_neuron_controller: None, + nns_neuron_hotkeys: Some(Principals::from(vec![PrincipalId::new_user_test_id( + 13848, + )])), + })), + ..NeuronRecipe::validate_default_neurons_fund() + }; + validate_recipe(&recipe).unwrap_err(); + } + + #[test] + fn test_invalid_neurons_fund_missing_hotkeys() { + let recipe = NeuronRecipe { + participant: Some(Participant::NeuronsFund(neuron_recipe::NeuronsFund { + nns_neuron_id: Some(2), + nns_neuron_controller: Some(PrincipalId::new_user_test_id(13847)), + nns_neuron_hotkeys: None, + })), + ..NeuronRecipe::validate_default_neurons_fund() + }; + validate_recipe(&recipe).unwrap_err(); + } + + #[test] + fn test_invalid_neurons_fund_too_many_hotkeys() { + let recipe = NeuronRecipe { + participant: Some(Participant::NeuronsFund(neuron_recipe::NeuronsFund { + nns_neuron_id: Some(2), + nns_neuron_controller: Some(PrincipalId::new_user_test_id(13847)), + nns_neuron_hotkeys: Some(Principals::from(vec![ + PrincipalId::new_user_test_id(13848), + PrincipalId::new_user_test_id(13849), + PrincipalId::new_user_test_id(13810), + PrincipalId::new_user_test_id(13811), + PrincipalId::new_user_test_id(13812), + ])), + })), + ..NeuronRecipe::validate_default_neurons_fund() + }; + validate_recipe(&recipe).unwrap_err(); + } + + #[test] + fn test_valid_zero_dissolve_delay() { + let recipe = NeuronRecipe { + dissolve_delay_seconds: Some(0), + ..NeuronRecipe::validate_default_direct_participant() + }; + validate_recipe(&recipe).unwrap(); + } + + #[test] + fn test_valid_empty_followees() { + let recipe = NeuronRecipe { + followees: Some(NeuronIds::from(vec![])), + ..NeuronRecipe::validate_default_direct_participant() + }; + validate_recipe(&recipe).unwrap(); + } + + #[test] + fn test_valid_minimum_stake() { + let recipe = NeuronRecipe { + stake_e8s: Some(NEURON_MINIMUM_STAKE_E8S), + ..NeuronRecipe::validate_default_neurons_fund() + }; + validate_recipe(&recipe).unwrap(); + } +} + +#[test] +fn test_voting_rewards_parameters_set_to_zero_by_default() { + let parameters = NervousSystemParameters::with_default_values(); + parameters.validate().unwrap(); + let voting_rewards_parameters = parameters.voting_rewards_parameters.unwrap(); + assert_eq!( + voting_rewards_parameters + .initial_reward_rate_basis_points + .unwrap(), + 0 + ); + assert_eq!( + voting_rewards_parameters + .final_reward_rate_basis_points + .unwrap(), + 0 + ); +} + +#[test] +#[should_panic] +fn test_nervous_system_parameters_wont_validate_without_voting_rewards_parameters() { + let mut parameters = NervousSystemParameters::with_default_values(); + parameters.voting_rewards_parameters = None; + // This is where we expect to panic. + parameters.validate().unwrap(); +} + +#[test] +fn test_nervous_system_parameters_wont_validate_without_the_required_claimer_permissions() { + for permission_to_omit in NervousSystemParameters::REQUIRED_NEURON_CLAIMER_PERMISSIONS { + let mut parameters = NervousSystemParameters::with_default_values(); + parameters.neuron_claimer_permissions = Some( + NervousSystemParameters::REQUIRED_NEURON_CLAIMER_PERMISSIONS + .iter() + .filter(|p| *p != permission_to_omit) + .cloned() + .collect::>() + .into(), + ); + parameters.validate().unwrap_err(); + } +} + +#[test] +fn test_validate_logo_lets_base64_through() { + SnsMetadata::validate_logo("data:image/png;base64,aGVsbG8gZnJvbSBkZmluaXR5IQ==").unwrap(); +} + +#[test] +fn test_validate_logo_doesnt_let_non_base64_through() { + // `_` is not in the base64 character set we're using + // so we should panic here. + SnsMetadata::validate_logo("data:image/png;base64,aGVsbG8gZnJvbSBkZmluaXR5IQ==_").unwrap_err(); +} + +#[test] +fn test_neuron_permission_list_display_impl() { + let neuron_permission_list = NeuronPermissionList::all(); + assert_eq!( + format!("permissions: {neuron_permission_list}"), + format!("permissions: [Unspecified, ConfigureDissolveState, ManagePrincipals, SubmitProposal, Vote, Disburse, Split, MergeMaturity, DisburseMaturity, StakeMaturity, ManageVotingPermission]") + ); +} + +#[test] +fn test_neuron_permission_list_display_impl_doesnt_panic_unknown_permission() { + let invalid_permission = 10000; + let neuron_permission_list = { + let mut neuron_permission_list = NeuronPermissionList::all(); + neuron_permission_list.permissions.push(invalid_permission); // Add an unknown permission to the list + neuron_permission_list + }; + assert_eq!( + format!("permissions: {neuron_permission_list}"), + format!("permissions: [Unspecified, ConfigureDissolveState, ManagePrincipals, SubmitProposal, Vote, Disburse, Split, MergeMaturity, DisburseMaturity, StakeMaturity, ManageVotingPermission, ]") + ); +} + +mod neuron_recipe_construct_followees_tests { + use super::*; + + #[test] + fn test_direct_participant_empty_followees() { + let [b0] = NeuronId::test_neuron_ids(); + let recipe = NeuronRecipe { + followees: Some(NeuronIds::from(vec![])), + neuron_id: Some(b0.clone()), + ..NeuronRecipe::validate_default_direct_participant() + }; + assert_eq!(recipe.construct_followees(), btreemap! {}); + } + + #[test] + fn test_direct_participant_single_followee() { + let [b0, b1] = NeuronId::test_neuron_ids(); + let w = u64::from(&Action::Unspecified(Empty {})); + let recipe = NeuronRecipe { + followees: Some(NeuronIds::from(vec![b0.clone()])), + neuron_id: Some(b1.clone()), + ..NeuronRecipe::validate_default_direct_participant() + }; + assert_eq!( + recipe.construct_followees(), + btreemap! { w => Followees { followees: vec![b0.clone()] } } + ); + } + + #[test] + fn test_direct_participant_multiple_followees() { + let [b0, b1, b2] = NeuronId::test_neuron_ids(); + let w = u64::from(&Action::Unspecified(Empty {})); + let recipe = NeuronRecipe { + followees: Some(NeuronIds::from(vec![b0.clone(), b1.clone()])), + neuron_id: Some(b2.clone()), + ..NeuronRecipe::validate_default_direct_participant() + }; + assert_eq!( + recipe.construct_followees(), + btreemap! { w => Followees { followees: vec![b0.clone(), b1.clone()] } } + ); + } + + #[test] + fn test_neurons_fund_empty_followees() { + let [b1] = NeuronId::test_neuron_ids(); + let recipe = NeuronRecipe { + followees: Some(NeuronIds::from(vec![])), + neuron_id: Some(b1.clone()), + ..NeuronRecipe::validate_default_neurons_fund() + }; + assert_eq!(recipe.construct_followees(), btreemap! {}); + } + + #[test] + fn test_neurons_fund_single_followee() { + let [b0, b1] = NeuronId::test_neuron_ids(); + let w = u64::from(&Action::Unspecified(Empty {})); + let recipe = NeuronRecipe { + followees: Some(NeuronIds::from(vec![b0.clone()])), + neuron_id: Some(b1.clone()), + ..NeuronRecipe::validate_default_neurons_fund() + }; + assert_eq!( + recipe.construct_followees(), + btreemap! { w => Followees { followees: vec![b0.clone()] } } + ); + } + + #[test] + fn test_neurons_fund_multiple_followees() { + let [b0, b1, b2, b3] = NeuronId::test_neuron_ids(); + let w = u64::from(&Action::Unspecified(Empty {})); + let recipe = NeuronRecipe { + followees: Some(NeuronIds::from(vec![b0.clone(), b1.clone(), b2.clone()])), + neuron_id: Some(b3.clone()), + ..NeuronRecipe::validate_default_neurons_fund() + }; + assert_eq!( + recipe.construct_followees(), + btreemap! { w => Followees { followees: vec![b0.clone(), b1.clone(), b2.clone()] } } + ); + } +} + +#[test] +fn test_summarize_blob_field() { + for len in 0..=64 { + let direct_copy_input = (0..len).collect::>(); + + assert_eq!(summarize_blob_field(&direct_copy_input), direct_copy_input); + } + + let too_long = (0..65).collect::>(); + let result = summarize_blob_field(&too_long); + assert_ne!(result, too_long); + assert!(result.len() > 64, "{:X?}", result); + + let result = String::from_utf8(summarize_blob_field(&too_long)).unwrap(); + assert!( + result.contains("⚠️ NOT THE ORIGINAL CONTENTS OF THIS FIELD ⚠"), + "{:X?}", + result, + ); + assert!(result.contains("00 01 02 03"), "{:X?}", result); + assert!(result.contains("3D 3E 3F 40"), "{:X?}", result); + assert!(result.contains("Length: 65"), "{:X?}", result); + assert!( + // SHA256 + result.contains( + // Independently calculating using Python. + "4B FD 2C 8B 6F 1E EC 7A \ + 2A FE B4 8B 93 4E E4 B2 \ + 69 41 82 02 7E 6D 0F C0 \ + 75 07 4F 2F AB B3 17 81", + ), + "{:X?}", + result, + ); +} + +#[test] +fn test_limited_for_get_proposal() { + let motion_proposal = ProposalData { + proposal: Some(Proposal { + action: Some(Action::Motion(Motion { + motion_text: "Hello, world!".to_string(), + })), + ..Default::default() + }), + ..Default::default() + }; + + assert_eq!(motion_proposal.limited_for_get_proposal(), motion_proposal,); + + let upgrade_sns_controlled_canister_proposal = ProposalData { + proposal: Some(Proposal { + action: Some(Action::UpgradeSnsControlledCanister( + UpgradeSnsControlledCanister { + new_canister_wasm: (0..=255).collect(), + ..Default::default() + }, + )), + ..Default::default() + }), + ..Default::default() + }; + + assert_ne!( + upgrade_sns_controlled_canister_proposal.limited_for_get_proposal(), + upgrade_sns_controlled_canister_proposal, + ); + + let execute_generic_nervous_system_function_proposal = ProposalData { + proposal: Some(Proposal { + action: Some(Action::ExecuteGenericNervousSystemFunction( + ExecuteGenericNervousSystemFunction { + payload: (0..=255).collect(), + ..Default::default() + }, + )), + ..Default::default() + }), + ..Default::default() + }; + + assert_ne!( + execute_generic_nervous_system_function_proposal.limited_for_get_proposal(), + execute_generic_nervous_system_function_proposal, + ); +} + +#[test] +fn test_validate_chunked_wasm_happy() { + let store_canister_id = CanisterId::unchecked_from_principal(PrincipalId::new_user_test_id(42)); + + let mut env = NativeEnvironment::new(None); + let arg = Encode!(&CanisterIdRecord::from(store_canister_id)).unwrap(); + let response = Ok(Encode!(&StoredChunksReply(vec![ + ChunkHash { + hash: vec![4, 4, 4] + }, + ChunkHash { + hash: vec![2, 2, 2] + }, + ChunkHash { + hash: vec![3, 3, 3] + }, + ChunkHash { + hash: vec![1, 1, 1] + }, + ])) + .unwrap()); + env.set_call_canister_response(CanisterId::ic_00(), "stored_chunks", arg, response); + + let wasm_module_hash = vec![1, 2, 3]; + + let chunk_hashes_list = vec![vec![1, 1, 1], vec![2, 2, 2], vec![3, 3, 3]]; + + assert_eq!( + validate_chunked_wasm( + &env, + &wasm_module_hash, + store_canister_id, + &chunk_hashes_list + ) + .now_or_never() + .unwrap(), + Ok(()), + ); +} + +// TODO[NNS1-3550]: Enable stored chunks validation on mainnet. +#[cfg(feature = "test")] +#[test] +fn test_validate_chunked_wasm_not_uploaded_some_chunks() { + let store_canister_id = CanisterId::unchecked_from_principal(PrincipalId::new_user_test_id(42)); + + let mut env = NativeEnvironment::new(None); + let arg = Encode!(&CanisterIdRecord::from(store_canister_id)).unwrap(); + let response = Ok(Encode!(&StoredChunksReply(vec![ + ChunkHash { + hash: vec![4, 4, 4] + }, + ChunkHash { + hash: vec![2, 2, 2] + }, + ChunkHash { + hash: vec![3, 3, 3] + }, + ChunkHash { + hash: vec![1, 1, 1] + }, + ])) + .unwrap()); + env.set_call_canister_response(CanisterId::ic_00(), "stored_chunks", arg, response); + + let wasm_module_hash = vec![1, 2, 3]; + + let chunk_hashes_list = vec![ + vec![1, 1, 1], + // The problem is here. + vec![3, 2, 1], + vec![3, 3, 3], + ]; + + assert_eq!( + validate_chunked_wasm( + &env, + &wasm_module_hash, + store_canister_id, + &chunk_hashes_list + ) + .now_or_never() + .unwrap(), + Err(vec![ + "1 out of 3 expected WASM chunks were not uploaded to the store canister: 030201" + .to_string() + ]), + ); +} + +#[test] +fn test_validate_chunked_wasm_one_chunk_happy() { + let store_canister_id = CanisterId::unchecked_from_principal(PrincipalId::new_user_test_id(42)); + + let mut env = NativeEnvironment::new(None); + let arg = Encode!(&CanisterIdRecord::from(store_canister_id)).unwrap(); + let response = Ok(Encode!(&StoredChunksReply(vec![ + ChunkHash { + hash: vec![4, 4, 4] + }, + ChunkHash { + hash: vec![1, 2, 3] + }, + ChunkHash { + hash: vec![3, 3, 3] + }, + ChunkHash { + hash: vec![1, 1, 1] + }, + ])) + .unwrap()); + env.set_call_canister_response(CanisterId::ic_00(), "stored_chunks", arg, response); + + let wasm_module_hash = vec![1, 2, 3]; + + let chunk_hashes_list = vec![vec![1, 2, 3]]; + + assert_eq!( + validate_chunked_wasm( + &env, + &wasm_module_hash, + store_canister_id, + &chunk_hashes_list + ) + .now_or_never() + .unwrap(), + Ok(()), + ); +} + +#[test] +fn test_validate_chunked_wasm_one_chunk_hash_mismatch() { + let store_canister_id = CanisterId::unchecked_from_principal(PrincipalId::new_user_test_id(42)); + + let mut env = NativeEnvironment::new(None); + let arg = Encode!(&CanisterIdRecord::from(store_canister_id)).unwrap(); + let response = Ok(Encode!(&StoredChunksReply(vec![ + ChunkHash { + hash: vec![4, 4, 4] + }, + ChunkHash { + hash: vec![2, 2, 2] + }, + ChunkHash { + hash: vec![3, 3, 3] + }, + ChunkHash { + hash: vec![1, 1, 1] + }, + ])) + .unwrap()); + env.set_call_canister_response(CanisterId::ic_00(), "stored_chunks", arg, response); + + // The issue is here. + let wasm_module_hash = vec![1, 2, 3]; + let chunk_hashes_list = vec![vec![2, 2, 2]]; + + assert_eq!( + validate_chunked_wasm( + &env, + &wasm_module_hash, + store_canister_id, + &chunk_hashes_list + ) + .now_or_never() + .unwrap(), + Err(vec![ + "chunked_canister_wasm.chunk_hashes_list specifies only one hash (020202), but it \ + differs from chunked_canister_wasm.wasm_module_hash (010203)." + .to_string() + ]), + ); +} + +#[test] +fn test_validate_chunked_wasm_chunk_hashes_list_empty() { + let store_canister_id = CanisterId::unchecked_from_principal(PrincipalId::new_user_test_id(42)); + + let mut env = NativeEnvironment::new(None); + let arg = Encode!(&CanisterIdRecord::from(store_canister_id)).unwrap(); + let response = Ok(Encode!(&StoredChunksReply(vec![ + ChunkHash { + hash: vec![4, 4, 4] + }, + ChunkHash { + hash: vec![1, 2, 3] + }, + ChunkHash { + hash: vec![3, 3, 3] + }, + ChunkHash { + hash: vec![1, 1, 1] + }, + ])) + .unwrap()); + env.set_call_canister_response(CanisterId::ic_00(), "stored_chunks", arg, response); + + let wasm_module_hash = vec![1, 2, 3]; + + // The issue is here. + let chunk_hashes_list = vec![]; + + assert_eq!( + validate_chunked_wasm( + &env, + &wasm_module_hash, + store_canister_id, + &chunk_hashes_list + ) + .now_or_never() + .unwrap(), + Err(vec![ + "chunked_canister_wasm.chunk_hashes_list cannot be empty.".to_string() + ]), + ); +} + +// TODO[NNS1-3550]: Enable stored chunks validation on mainnet. +#[cfg(feature = "test")] +#[test] +fn test_validate_chunked_wasm_management_canister_call_fails() { + let store_canister_id = CanisterId::unchecked_from_principal(PrincipalId::new_user_test_id(42)); + + let mut env = NativeEnvironment::new(None); + let arg = Encode!(&CanisterIdRecord::from(store_canister_id)).unwrap(); + + // This is the problem. + let response = Err((Some(404), "No such canister".to_string())); + env.set_call_canister_response(CanisterId::ic_00(), "stored_chunks", arg, response); + + let wasm_module_hash = vec![1, 2, 3]; + + // The issue is here. + let chunk_hashes_list = vec![vec![1, 2, 3]]; + + assert_eq!( + validate_chunked_wasm( + &env, + &wasm_module_hash, + store_canister_id, + &chunk_hashes_list + ) + .now_or_never() + .unwrap(), + Err(vec![format!( + "Cannot call stored_chunks for {}: (Some(404), \"No such canister\")", + store_canister_id + )]), + ); +} + +// TODO[NNS1-3550]: Enable stored chunks validation on mainnet. +#[cfg(feature = "test")] +#[test] +fn test_validate_chunked_wasm_management_canister_call_returns_junk() { + let store_canister_id = CanisterId::unchecked_from_principal(PrincipalId::new_user_test_id(42)); + + let mut env = NativeEnvironment::new(None); + + // This is causing the problem (incorrect response type `PrincipalId` / `CanisterIdRecord`). + let arg = Encode!(&PrincipalId::new_user_test_id(888)).unwrap(); + + let response = Ok(Encode!(&StoredChunksReply(vec![ChunkHash { + hash: vec![1, 2, 3] + },])) + .unwrap()); + env.set_call_canister_response(CanisterId::ic_00(), "stored_chunks", arg, response); + + let wasm_module_hash = vec![1, 2, 3]; + + // The issue is here. + let chunk_hashes_list = vec![vec![1, 2, 3]]; + + assert_eq!( + validate_chunked_wasm( + &env, + &wasm_module_hash, + store_canister_id, + &chunk_hashes_list + ) + .now_or_never() + .unwrap(), + Err(vec![format!( + "Cannot decode response from calling stored_chunks for {}: Cannot parse header ", + store_canister_id + )]), + ); +} diff --git a/rs/sns/governance/src/upgrade_journal.rs b/rs/sns/governance/src/upgrade_journal.rs index 1ca20da4916..865152655ed 100644 --- a/rs/sns/governance/src/upgrade_journal.rs +++ b/rs/sns/governance/src/upgrade_journal.rs @@ -27,10 +27,15 @@ impl upgrade_journal_entry::UpgradeStepsReset { impl upgrade_journal_entry::TargetVersionSet { /// Creates a new TargetVersionSet event with old and new versions - pub fn new(old_version: Option, new_version: Option) -> Self { + pub fn new( + old_version: Option, + new_version: Version, + is_advanced_automatically: bool, + ) -> Self { Self { old_target_version: old_version, - new_target_version: new_version, + new_target_version: Some(new_version), + is_advanced_automatically: Some(is_advanced_automatically), } } } @@ -211,30 +216,16 @@ impl From for upgrade_journal_entry:: } } -impl upgrade_journal_entry::Event { - /// Useful for specifying expected states of the SNS upgrade journal in a way that isn't - /// overly fragile. - pub fn redact_human_readable(self) -> Self { - match self { - Self::UpgradeOutcome(upgrade_outcome) => { - Self::UpgradeOutcome(upgrade_journal_entry::UpgradeOutcome { - human_readable: None, - ..upgrade_outcome - }) - } - Self::UpgradeStepsReset(upgrade_steps_reset) => { - Self::UpgradeStepsReset(upgrade_journal_entry::UpgradeStepsReset { - human_readable: None, - ..upgrade_steps_reset - }) - } - Self::TargetVersionReset(target_version_reset) => { - Self::TargetVersionReset(upgrade_journal_entry::TargetVersionReset { - human_readable: None, - ..target_version_reset - }) - } - event => event, +pub fn serve_journal(journal: &UpgradeJournal) -> ic_canisters_http_types::HttpResponse { + use ic_canisters_http_types::HttpResponseBuilder; + let journal = ic_sns_governance_api::pb::v1::UpgradeJournal::from(journal.clone()); + match serde_json::to_string(&journal.entries) { + Err(err) => { + HttpResponseBuilder::server_error(format!("Failed to encode journal: {}", err)).build() } + Ok(body) => HttpResponseBuilder::ok() + .header("Content-Type", "application/json") + .with_body_and_content_length(body) + .build(), } } diff --git a/rs/sns/governance/unreleased_changelog.md b/rs/sns/governance/unreleased_changelog.md new file mode 100644 index 00000000000..b5ff3f4f201 --- /dev/null +++ b/rs/sns/governance/unreleased_changelog.md @@ -0,0 +1,49 @@ +# How This File Is Used + +In general, upcoming/unreleased behavior changes are described here. For details +on the process that this file is part of, see +`rs/nervous_system/changelog_process.md`. + + +# Next Upgrade Proposal + +## Added + +* Enable SNSs to opt in for +[automatically advancing its target version](https://forum.dfinity.org/t/proposal-opt-in-mechanism-for-automatic-sns-target-version-advancement/39874) +to the newest version blessed by the NNS. To do so, please submit a `ManageNervousSystemParameters` +proposal, e.g.: + + ```bash + dfx canister --network ic call $SNS_GOVERNANCE_CANISTER_ID manage_neuron '( + record { + subaccount = blob "'${PROPOSER_SNS_NEURON_SUBACCOUNT}'"; + command = opt variant { + MakeProposal = record { + url = "https://forum.dfinity.org/t/proposal-opt-in-mechanism-for-automatic-sns-target-version-advancement/39874"; + title = "Opt for automatic advancement of SNS target versions"; + action = opt variant { + ManageNervousSystemParameters = record { + automatically_advance_target_version = opt true; + } + }; + summary = "Enable automatically advancing the target version \ + of this SNS to speed up the delivery of SNS framework \ + upgrades that were already blessed by the NNS."; + } + }; + }, + )' + ``` + +* Do not redact chunked Wasm data in `ProposalInfo` served from `SnsGov.list_proposals`. + +## Changed + +## Deprecated + +## Removed + +## Fixed + +## Security diff --git a/rs/sns/integration_tests/BUILD.bazel b/rs/sns/integration_tests/BUILD.bazel index 2eda895b531..cd37a5ec7b1 100644 --- a/rs/sns/integration_tests/BUILD.bazel +++ b/rs/sns/integration_tests/BUILD.bazel @@ -143,7 +143,6 @@ DATA_DEPS = [ "//rs/ledger_suite/icp/ledger:ledger-canister-wasm", "//rs/ledger_suite/icp/ledger:ledger-canister-wasm-notify-method", "//rs/ledger_suite/icrc1/archive:archive_canister", - "//rs/ledger_suite/icrc1/index:index_canister", "//rs/ledger_suite/icrc1/index-ng:index_ng_canister", "//rs/ledger_suite/icrc1/ledger:ledger_canister", "//rs/nervous_system/common/test_canister", @@ -166,7 +165,6 @@ ENV = { "GENESIS_TOKEN_CANISTER_WASM_PATH": "$(rootpath //rs/nns/gtc:genesis-token-canister)", "GOVERNANCE_CANISTER_TEST_WASM_PATH": "$(rootpath //rs/nns/governance:governance-canister-test)", "IC_ICRC1_ARCHIVE_WASM_PATH": "$(rootpath //rs/ledger_suite/icrc1/archive:archive_canister)", - "IC_ICRC1_INDEX_WASM_PATH": "$(rootpath //rs/ledger_suite/icrc1/index:index_canister)", "IC_ICRC1_INDEX_NG_WASM_PATH": "$(rootpath //rs/ledger_suite/icrc1/index-ng:index_ng_canister)", "IC_ICRC1_LEDGER_WASM_PATH": "$(rootpath //rs/ledger_suite/icrc1/ledger:ledger_canister)", "LEDGER_CANISTER_WASM_PATH": "$(rootpath //rs/ledger_suite/icp/ledger:ledger-canister-wasm)", diff --git a/rs/sns/integration_tests/src/neuron.rs b/rs/sns/integration_tests/src/neuron.rs index d4b6cdb8c34..5b03363dfcb 100644 --- a/rs/sns/integration_tests/src/neuron.rs +++ b/rs/sns/integration_tests/src/neuron.rs @@ -724,7 +724,10 @@ fn test_neuron_action_is_not_authorized() { // This is a bit hacky and fragile, as it depends on how `SnsCanisters` is setup. // TODO(NNS1-1892): expose SnsCanisters' current time via API. fn get_sns_canisters_now_seconds() -> i64 { - (NativeEnvironment::default().now() as i64) + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs() as i64 + (NervousSystemParameters::with_default_values() .initial_voting_period_seconds .unwrap() as i64) diff --git a/rs/sns/integration_tests/src/proposals.rs b/rs/sns/integration_tests/src/proposals.rs index daf4ac9a9c0..3ac6b042fdf 100644 --- a/rs/sns/integration_tests/src/proposals.rs +++ b/rs/sns/integration_tests/src/proposals.rs @@ -36,6 +36,8 @@ use std::{ time::{SystemTime, UNIX_EPOCH}, }; +const EXPECTED_MAX_BALLOT_AGE: f64 = 60.0; + const MOTION_PROPOSAL_ACTION_TYPE: u64 = 1; const VOTING_REWARDS_PARAMETERS: VotingRewardsParameters = VotingRewardsParameters { @@ -346,7 +348,7 @@ fn test_voting_with_three_neurons_with_the_same_stake() { ballot ); assert!( - age_seconds < 30.0, + age_seconds < EXPECTED_MAX_BALLOT_AGE, "age_seconds = {}. ballot = {:?}", age_seconds, ballot diff --git a/rs/sns/integration_tests/src/root.rs b/rs/sns/integration_tests/src/root.rs index 65ab0246be6..7d240bd854b 100644 --- a/rs/sns/integration_tests/src/root.rs +++ b/rs/sns/integration_tests/src/root.rs @@ -317,6 +317,7 @@ fn test_root_restarts_governance_on_stop_canister_timeout() { arg: vec![], compute_allocation: None, memory_allocation: None, + chunked_canister_wasm: None, }; let _: () = update_with_sender( diff --git a/rs/sns/integration_tests/src/upgrade_canister.rs b/rs/sns/integration_tests/src/upgrade_canister.rs index 7fd01998880..de01de27d3a 100644 --- a/rs/sns/integration_tests/src/upgrade_canister.rs +++ b/rs/sns/integration_tests/src/upgrade_canister.rs @@ -45,6 +45,8 @@ lazy_static! { pub static ref EMPTY_WASM: Vec = vec![0, 0x61, 0x73, 0x6D, 1, 0, 0, 0]; } +const EXPECTED_SNS_DAPP_CANISTER_UPGRADE_TIME_SECONDS: u64 = 60; + // Note: Tests for UpgradeSnsToNextVersion action is in rs/nns/sns-wasm/tests/upgrade_sns_instance.rs fn setup_sns( @@ -135,6 +137,7 @@ fn test_upgrade_canister_proposal_is_successful() { canister_upgrade_arg: Some(wasm().set_global_data(&[42]).build()), // mode: None corresponds to CanisterInstallModeProto::Upgrade mode: None, + chunked_canister_wasm: None, }, )), ..Default::default() @@ -261,6 +264,7 @@ fn test_upgrade_canister_proposal_reinstall() { new_canister_wasm: new_dapp_wasm, canister_upgrade_arg: Some(wasm().build()), mode: Some(CanisterInstallModeProto::Reinstall.into()), + chunked_canister_wasm: None, }, )), ..Default::default() @@ -418,6 +422,7 @@ fn test_upgrade_canister_proposal_execution_fail() { new_canister_wasm: new_dapp_wasm, canister_upgrade_arg: None, mode: Some(CanisterInstallModeProto::Upgrade.into()), + chunked_canister_wasm: None, }, )), ..Default::default() @@ -442,16 +447,16 @@ fn test_upgrade_canister_proposal_execution_fail() { action ), }; - fn age_s(t: u64) -> f64 { + fn age_s(t: u64) -> u64 { SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() - .as_secs_f64() - - (t as f64) + .as_secs() + .saturating_sub(t) } let decision_age_s = age_s(proposal.decided_timestamp_seconds); assert!( - decision_age_s < 30.0, + decision_age_s < EXPECTED_SNS_DAPP_CANISTER_UPGRADE_TIME_SECONDS, "decision_age_s: {}, proposal: {:?}", decision_age_s, proposal @@ -463,7 +468,7 @@ fn test_upgrade_canister_proposal_execution_fail() { ); let failure_age_s = age_s(proposal.failed_timestamp_seconds); assert!( - failure_age_s < 30.0, + failure_age_s < EXPECTED_SNS_DAPP_CANISTER_UPGRADE_TIME_SECONDS, "failure_age_s: {}, proposal: {:?}", failure_age_s, proposal @@ -520,6 +525,7 @@ fn test_upgrade_canister_proposal_too_large() { canister_upgrade_arg: Some(wasm().set_global_data(&[42; 2_000_000]).build()), // mode: None corresponds to CanisterInstallModeProto::Upgrade mode: None, + chunked_canister_wasm: None, }, )), ..Default::default() @@ -641,6 +647,7 @@ fn test_upgrade_after_state_shrink() { new_canister_wasm: governance_wasm, canister_upgrade_arg: None, mode: Some(CanisterInstallModeProto::Upgrade.into()), + chunked_canister_wasm: None, }, )), ..Default::default() diff --git a/rs/sns/integration_tests/test_canisters/sns_governance_mem_test_canister.rs b/rs/sns/integration_tests/test_canisters/sns_governance_mem_test_canister.rs index bce4a43db4b..26f7306e4a9 100644 --- a/rs/sns/integration_tests/test_canisters/sns_governance_mem_test_canister.rs +++ b/rs/sns/integration_tests/test_canisters/sns_governance_mem_test_canister.rs @@ -1,3 +1,6 @@ +// TODO: Jira ticket NNS1-3556 +#![allow(static_mut_refs)] + //! This is a special-purpose canister to create a large Governance proto and //! serialize it to stable memory in a format that is compatible with the real //! governance canister. diff --git a/rs/sns/root/CHANGELOG.md b/rs/sns/root/CHANGELOG.md new file mode 100644 index 00000000000..6062774a801 --- /dev/null +++ b/rs/sns/root/CHANGELOG.md @@ -0,0 +1,31 @@ +# Changelog + +Proposals before 2025 are NOT listed in here, because this process was +introduced later. (We could back fill those later though.) + +The process that populates this file is described in +`rs/nervous_system/changelog_process.md`. In general though, the entries you see +here were moved from the adjacent `unreleased_changelog.md` file. + + +INSERT NEW RELEASES HERE + + +# 2025-01-20: Proposal 134905 + +http://dashboard.internetcomputer.org/proposals/134905 + +## Added + +Enable upgrading SNS-controlled canisters using chunked WASMs. This is implemented as an extension +of the existing `UpgradeSnsControllerCanister` proposal type with new field `chunked_canister_wasm`. +This field can be used for specifying an upgrade of an SNS-controlled *target* canister using +a potentially large WASM module (over 2 MiB) uploaded to some *store* canister, which: +* must be installed on the same subnet as target. +* must have SNS Root as one of its controllers. +* must have enough cycles for performing the upgrade. + +You can now set wasm_memory_threshold via proposal. + + +END diff --git a/rs/sns/root/canister/root.did b/rs/sns/root/canister/root.did index bc3856b691d..352ee49fd64 100644 --- a/rs/sns/root/canister/root.did +++ b/rs/sns/root/canister/root.did @@ -43,9 +43,16 @@ type CanisterSummary = record { canister_id : opt principal; }; +type ChunkedCanisterWasm = record { + wasm_module_hash : blob; + store_canister_id : principal; + chunk_hashes_list : vec blob; +}; + type ChangeCanisterRequest = record { arg : blob; wasm_module : blob; + chunked_canister_wasm : opt ChunkedCanisterWasm; stop_before_installing : bool; mode : CanisterInstallMode; canister_id : principal; diff --git a/rs/sns/root/src/lib.rs b/rs/sns/root/src/lib.rs index e866d94c17d..4b50f134ca2 100644 --- a/rs/sns/root/src/lib.rs +++ b/rs/sns/root/src/lib.rs @@ -32,7 +32,6 @@ use types::SnsCanisterType; pub use icrc_ledger_types::icrc3::archive::ArchiveInfo; pub mod logs; pub mod pb; -mod request_impls; pub mod types; // The number of dapp canisters that can be registered with the SNS Root diff --git a/rs/sns/root/src/request_impls.rs b/rs/sns/root/src/request_impls.rs deleted file mode 100644 index 388297f9de8..00000000000 --- a/rs/sns/root/src/request_impls.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::{ - pb::v1::{ListSnsCanistersRequest, ListSnsCanistersResponse}, - GetSnsCanistersSummaryRequest, GetSnsCanistersSummaryResponse, -}; -use ic_nervous_system_clients::Request; - -impl Request for GetSnsCanistersSummaryRequest { - type Response = GetSnsCanistersSummaryResponse; - const METHOD: &'static str = "get_sns_canisters_summary"; - const UPDATE: bool = true; -} - -impl Request for ListSnsCanistersRequest { - type Response = ListSnsCanistersResponse; - const METHOD: &'static str = "list_sns_canisters"; - const UPDATE: bool = false; -} diff --git a/rs/sns/root/unreleased_changelog.md b/rs/sns/root/unreleased_changelog.md new file mode 100644 index 00000000000..94126a0ff42 --- /dev/null +++ b/rs/sns/root/unreleased_changelog.md @@ -0,0 +1,20 @@ +# How This File Is Used + +In general, upcoming/unreleased behavior changes are described here. For details +on the process that this file is part of, see +`rs/nervous_system/changelog_process.md`. + + +# Next Upgrade Proposal + +## Added + +## Changed + +## Deprecated + +## Removed + +## Fixed + +## Security diff --git a/rs/sns/sns.bzl b/rs/sns/sns.bzl new file mode 100644 index 00000000000..264328502ff --- /dev/null +++ b/rs/sns/sns.bzl @@ -0,0 +1,21 @@ +""" +For now, this just defines how large compressed SNS canister WASMs are allowed to be, +but other things could be added here later. +""" + +# How these limits were chosen: +# +# 1. Temporarily set value to "0". +# 2. Run the test: bazel test //publish/canisters:all +# 3. Fail message reports size it sees. +# 4. Pick a number that gives at least 20% headroom. +# +# This way, there is some room to grow, but an alarm eventually gets triggered after a "significant" +# amount of growth happens. + +CANISTER_NAME_TO_MAX_COMPRESSED_WASM_SIZE_E5_BYTES = { + "sns-governance-canister.wasm.gz": "13", + "sns-governance-canister_test.wasm.gz": "13", + "sns-root-canister.wasm.gz": "4", + "sns-swap-canister.wasm.gz": "7", +} diff --git a/rs/sns/swap/CHANGELOG.md b/rs/sns/swap/CHANGELOG.md new file mode 100644 index 00000000000..db9378aabbb --- /dev/null +++ b/rs/sns/swap/CHANGELOG.md @@ -0,0 +1,21 @@ +# Changelog + +Proposals before 2025 are NOT listed in here, because this process was +introduced later. (We could back fill those later though.) + +The process that populates this file is described in +`rs/nervous_system/changelog_process.md`. In general though, the entries you see +here were moved from the adjacent `unreleased_changelog.md` file. + + +INSERT NEW RELEASES HERE + + +# 2025-01-20: Proposal 134907 + +http://dashboard.internetcomputer.org/proposals/134907 + +No behavior changes. This was just a maintenance release. + + +END diff --git a/rs/sns/swap/canister/canister.rs b/rs/sns/swap/canister/canister.rs index 1e9b1484caf..4c047678fc6 100644 --- a/rs/sns/swap/canister/canister.rs +++ b/rs/sns/swap/canister/canister.rs @@ -1,3 +1,6 @@ +// TODO: Jira ticket NNS1-3556 +#![allow(static_mut_refs)] + use ic_base_types::{CanisterId, PrincipalId}; use ic_canister_log::log; use ic_canisters_http_types::{HttpRequest, HttpResponse, HttpResponseBuilder}; diff --git a/rs/sns/swap/src/lib.rs b/rs/sns/swap/src/lib.rs index aa342e0bcb9..a5531dec6e5 100644 --- a/rs/sns/swap/src/lib.rs +++ b/rs/sns/swap/src/lib.rs @@ -4,7 +4,6 @@ pub mod logs; pub mod memory; pub mod neurons_fund; pub mod pb; -mod request_impls; pub mod swap; pub mod swap_builder; pub mod types; diff --git a/rs/sns/swap/src/request_impls.rs b/rs/sns/swap/src/request_impls.rs deleted file mode 100644 index be943eeeb08..00000000000 --- a/rs/sns/swap/src/request_impls.rs +++ /dev/null @@ -1,19 +0,0 @@ -use ic_nervous_system_clients::Request; - -impl Request for crate::pb::v1::GetDerivedStateRequest { - type Response = crate::pb::v1::GetDerivedStateResponse; - const METHOD: &'static str = "get_derived_state"; - const UPDATE: bool = false; -} - -impl Request for crate::pb::v1::GetInitRequest { - type Response = crate::pb::v1::GetInitResponse; - const METHOD: &'static str = "get_init"; - const UPDATE: bool = false; -} - -impl Request for crate::pb::v1::ListSnsNeuronRecipesRequest { - type Response = crate::pb::v1::ListSnsNeuronRecipesResponse; - const METHOD: &'static str = "list_sns_neuron_recipes"; - const UPDATE: bool = false; -} diff --git a/rs/sns/swap/src/swap.rs b/rs/sns/swap/src/swap.rs index e4f58e45e0c..324424faa2e 100644 --- a/rs/sns/swap/src/swap.rs +++ b/rs/sns/swap/src/swap.rs @@ -2535,7 +2535,7 @@ impl Swap { if request .subaccount .as_ref() - .map_or(false, |subaccount| subaccount.len() != 32) + .is_some_and(|subaccount| subaccount.len() != 32) { return NewSaleTicketResponse::err_invalid_subaccount(); } @@ -3768,7 +3768,7 @@ impl<'a> SwapDigest<'a> { } } -impl<'a> fmt::Debug for SwapDigest<'a> { +impl fmt::Debug for SwapDigest<'_> { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { let Swap { lifecycle, diff --git a/rs/sns/swap/unreleased_changelog.md b/rs/sns/swap/unreleased_changelog.md new file mode 100644 index 00000000000..94126a0ff42 --- /dev/null +++ b/rs/sns/swap/unreleased_changelog.md @@ -0,0 +1,20 @@ +# How This File Is Used + +In general, upcoming/unreleased behavior changes are described here. For details +on the process that this file is part of, see +`rs/nervous_system/changelog_process.md`. + + +# Next Upgrade Proposal + +## Added + +## Changed + +## Deprecated + +## Removed + +## Fixed + +## Security diff --git a/rs/sns/test_utils/src/icrc1.rs b/rs/sns/test_utils/src/icrc1.rs index 5076c3d8f5c..e50faeb55f4 100644 --- a/rs/sns/test_utils/src/icrc1.rs +++ b/rs/sns/test_utils/src/icrc1.rs @@ -9,15 +9,15 @@ use icrc_ledger_types::icrc1::{ }; use num_traits::ToPrimitive; -pub async fn balance_of<'a>(canister: &Canister<'a>, account: Account) -> Result { +pub async fn balance_of(canister: &Canister<'_>, account: Account) -> Result { canister .query_("icrc1_balance_of", candid_one, account) .await .map(|n: Nat| n.0.to_u64().unwrap()) } -pub async fn transfer<'a>( - canister: &Canister<'a>, +pub async fn transfer( + canister: &Canister<'_>, sender: &Sender, args: TransferArg, ) -> Result { diff --git a/rs/sns/test_utils/src/itest_helpers.rs b/rs/sns/test_utils/src/itest_helpers.rs index 800ca59c133..f21aee316ac 100644 --- a/rs/sns/test_utils/src/itest_helpers.rs +++ b/rs/sns/test_utils/src/itest_helpers.rs @@ -1281,7 +1281,7 @@ impl SnsCanisters<'_> { ); return; } - tokio::time::sleep(Duration::from_millis(100)).await; + self.governance.runtime().tick().await; } panic!( @@ -1395,10 +1395,7 @@ pub async fn set_up_governance_canister( } /// Compiles the ledger canister, builds it's initial payload and installs it -pub async fn install_ledger_canister<'runtime, 'a>( - canister: &mut Canister<'runtime>, - args: LedgerArgument, -) { +pub async fn install_ledger_canister(canister: &mut Canister<'_>, args: LedgerArgument) { install_rust_canister_with_memory_allocation( canister, "ic-icrc1-ledger", @@ -1417,10 +1414,7 @@ pub async fn set_up_ledger_canister(runtime: &'_ Runtime, args: LedgerInitArgs) } /// Compiles the ledger index canister, builds it's initial payload and installs it -pub async fn install_index_ng_canister<'runtime, 'a>( - canister: &mut Canister<'runtime>, - args: Option, -) { +pub async fn install_index_ng_canister(canister: &mut Canister<'_>, args: Option) { install_rust_canister_with_memory_allocation( canister, "ic-icrc1-index-ng", diff --git a/rs/starter/src/main.rs b/rs/starter/src/main.rs index faf97c53b7e..970b03ae284 100644 --- a/rs/starter/src/main.rs +++ b/rs/starter/src/main.rs @@ -101,6 +101,7 @@ fn main() -> Result<()> { node_operator_principal_id: None, secret_key_store: None, domain: None, + node_reward_type: None, }, ); @@ -204,7 +205,7 @@ fn main() -> Result<()> { .arg("--config-file") .args([config_path.to_str().unwrap()]); info!(log, "Executing {:?}", cmd); - cmd.exec(); + let _ = cmd.exec(); Ok(()) } diff --git a/rs/state_layout/src/state_layout.rs b/rs/state_layout/src/state_layout.rs index 029082110bc..a84b8814350 100644 --- a/rs/state_layout/src/state_layout.rs +++ b/rs/state_layout/src/state_layout.rs @@ -112,14 +112,14 @@ impl AccessPolicy for WriteOnly { impl WritePolicy for WriteOnly {} -impl<'a, T> AccessPolicy for RwPolicy<'a, T> { +impl AccessPolicy for RwPolicy<'_, T> { fn check_dir(p: &Path) -> Result<(), LayoutError> { WriteOnly::check_dir(p) } } -impl<'a, T> ReadPolicy for RwPolicy<'a, T> {} -impl<'a, T> WritePolicy for RwPolicy<'a, T> {} +impl ReadPolicy for RwPolicy<'_, T> {} +impl WritePolicy for RwPolicy<'_, T> {} pub type CompleteCheckpointLayout = CheckpointLayout; @@ -473,21 +473,36 @@ impl TipHandler { } impl StateLayout { - /// Needs to be pub for criterion performance regression tests. + /// Create a new StateLayout and initialize it by creating all necessary + /// directories if they do not exist already and clear all tmp directories + /// that are expected to be empty when the replica starts. + /// Needs to be pub for tests. pub fn try_new( log: ReplicaLogger, root: PathBuf, metrics_registry: &MetricsRegistry, ) -> Result { - let state_layout = Self { + let state_layout = Self::new_no_init(log, root, metrics_registry); + state_layout.init()?; + Ok(state_layout) + } + + /// Create a new StateLayout without initializing it. Useful for tests and + /// tools that want to create a StateLayout without interferring with + /// replicas / state managers that are already running using the same state + /// directory. + pub fn new_no_init( + log: ReplicaLogger, + root: PathBuf, + metrics_registry: &MetricsRegistry, + ) -> Self { + Self { root, log, metrics: StateLayoutMetrics::new(metrics_registry), tip_handler_captured: Arc::new(false.into()), checkpoint_ref_registry: Arc::new(Mutex::new(BTreeMap::new())), - }; - state_layout.init()?; - Ok(state_layout) + } } fn init(&self) -> Result<(), LayoutError> { @@ -1171,7 +1186,7 @@ impl StateLayout { /// path into the specified dst path. /// /// If a thread-pool is provided then files are copied in parallel. - fn copy_and_sync_checkpoint( + pub fn copy_and_sync_checkpoint( &self, name: &str, src: &Path, diff --git a/rs/state_machine_tests/src/lib.rs b/rs/state_machine_tests/src/lib.rs index 0db742a6902..769aca41d13 100644 --- a/rs/state_machine_tests/src/lib.rs +++ b/rs/state_machine_tests/src/lib.rs @@ -10,8 +10,8 @@ use ic_config::{ subnet_config::SubnetConfig, }; use ic_consensus::{ - consensus::payload_builder::PayloadBuilderImpl, - dkg::{make_registry_cup, make_registry_cup_from_cup_contents}, + consensus::payload_builder::PayloadBuilderImpl, make_registry_cup, + make_registry_cup_from_cup_contents, }; use ic_consensus_utils::crypto::SignVerify; use ic_crypto_test_utils_ni_dkg::{ @@ -153,9 +153,8 @@ use ic_types::{ CanisterId, CryptoHashOfState, Cycles, NumBytes, PrincipalId, SubnetId, UserId, }; use ic_xnet_payload_builder::{ - certified_slice_pool::{certified_slice_count_bytes, CertifiedSliceError}, - ExpectedIndices, RefillTaskHandle, XNetPayloadBuilderImpl, XNetPayloadBuilderMetrics, - XNetSlicePool, + certified_slice_pool::CertifiedSlicePool, refill_stream_slice_indices, RefillTaskHandle, + XNetPayloadBuilderImpl, XNetPayloadBuilderMetrics, XNetSlicePoolImpl, }; use rcgen::{CertificateParams, KeyPair}; use serde::Deserialize; @@ -423,9 +422,13 @@ fn make_nodes_registry( .build(); // Insert initial DKG transcripts + let mut high_threshold_transcript = ni_dkg_transcript.clone(); + high_threshold_transcript.dkg_id.dkg_tag = NiDkgTag::HighThreshold; + let mut low_threshold_transcript = ni_dkg_transcript; + low_threshold_transcript.dkg_id.dkg_tag = NiDkgTag::LowThreshold; let cup_contents = CatchUpPackageContents { - initial_ni_dkg_transcript_high_threshold: Some(ni_dkg_transcript.clone().into()), - initial_ni_dkg_transcript_low_threshold: Some(ni_dkg_transcript.into()), + initial_ni_dkg_transcript_high_threshold: Some(high_threshold_transcript.into()), + initial_ni_dkg_transcript_low_threshold: Some(low_threshold_transcript.into()), ..Default::default() }; registry_data_provider @@ -599,72 +602,64 @@ pub trait Subnets: Send + Sync { fn get(&self, subnet_id: SubnetId) -> Option>; } -/// Struct mocking the pool of XNet messages required for -/// instantiating `XNetPayloadBuilderImpl` in `StateMachine`. -struct PocketXNetSlicePoolImpl { - /// Pool of `StateMachine`s from which the XNet messages are fetched. +/// Struct mocking the XNet layer. +struct PocketXNetImpl { + /// Pool of `StateMachine`s from which XNet messages are fetched. subnets: Arc, - /// Subnet ID of the `StateMachine` containing the pool. + /// The certified slice pool of the `StateMachine` for which the XNet layer is mocked. + pool: Arc>, + /// The subnet ID of the `StateMachine` for which the XNet layer is mocked. own_subnet_id: SubnetId, } -impl PocketXNetSlicePoolImpl { - fn new(subnets: Arc, own_subnet_id: SubnetId) -> Self { +impl PocketXNetImpl { + fn new( + subnets: Arc, + pool: Arc>, + own_subnet_id: SubnetId, + ) -> Self { Self { subnets, + pool, own_subnet_id, } } -} -impl XNetSlicePool for PocketXNetSlicePoolImpl { - /// Obtains a certified slice of a stream from a `StateMachine` - /// corresponding to a given subnet ID. - fn take_slice( - &self, - subnet_id: SubnetId, - begin: Option<&ExpectedIndices>, - msg_limit: Option, - byte_limit: Option, - ) -> Result, CertifiedSliceError> { - let sm = self.subnets.get(subnet_id).unwrap(); - let msg_begin = begin.map(|idx| idx.message_index); - // We set `witness_begin` equal to `msg_begin` since all states are certified. - let certified_stream = sm.generate_certified_stream_slice( - self.own_subnet_id, - msg_begin, - msg_begin, - msg_limit, - byte_limit, - ); - Ok(certified_stream - .map(|certified_stream| { - let mut num_bytes = certified_slice_count_bytes(&certified_stream).unwrap(); - // Because `StateMachine::generate_certified_stream_slice` only uses a size estimate - // when constructing a slice (this estimate can be off by at most a few KB), - // we fake the reported slice size if it exceeds the specified size limit to make sure the payload builder will accept the slice as valid and include it into the block. - // This is fine since we don't actually validate the payload in the context of Pocket IC, and so blocks containing - // a XNet slice exceeding the byte limit won't be rejected as invalid. - if let Some(byte_limit) = byte_limit { - if num_bytes > byte_limit { - num_bytes = byte_limit; + fn refill(&self, registry_version: RegistryVersion, log: ReplicaLogger) { + let refill_stream_slice_indices = + refill_stream_slice_indices(self.pool.clone(), self.own_subnet_id); + + for (subnet_id, indices) in refill_stream_slice_indices { + let sm = self.subnets.get(subnet_id).unwrap(); + match sm.generate_certified_stream_slice( + self.own_subnet_id, + Some(indices.witness_begin), + Some(indices.msg_begin), + None, + Some(indices.byte_limit), + ) { + Ok(slice) => { + if indices.witness_begin != indices.msg_begin { + // Pulled a stream suffix, append to pooled slice. + self.pool + .lock() + .unwrap() + .append(subnet_id, slice, registry_version, log.clone()) + .unwrap(); + } else { + // Pulled a complete stream, replace pooled slice (if any). + self.pool + .lock() + .unwrap() + .put(subnet_id, slice, registry_version, log.clone()) + .unwrap(); } } - (certified_stream, num_bytes) - }) - .ok()) + Err(EncodeStreamError::NoStreamForSubnet(_)) => (), + Err(err) => panic!("Unexpected XNetClient error: {}", err), + } + } } - - /// We do not collect any metrics here. - fn observe_pool_size_bytes(&self) {} - - /// We do not cache XNet messages in this mock implementation - /// and thus there is no need for garbage collection. - fn garbage_collect(&self, _new_stream_positions: BTreeMap) {} - - /// We do not cache XNet messages in this mock implementation - /// and thus there is no need for garbage collection. - fn garbage_collect_slice(&self, _subnet_id: SubnetId, _stream_position: ExpectedIndices) {} } /// A custom `QueryStatsPayloadBuilderImpl` that uses a single @@ -825,6 +820,7 @@ pub struct StateMachine { ingress_pool: Arc>, ingress_manager: Arc, pub ingress_filter: Arc>, + pocket_xnet: Arc>>, payload_builder: Arc>>, message_routing: SyncMessageRouting, pub metrics_registry: MetricsRegistry, @@ -856,6 +852,7 @@ pub struct StateMachine { /// A drop guard to gracefully cancel the ingress watcher task. _ingress_watcher_drop_guard: tokio_util::sync::DropGuard, query_stats_payload_builder: Arc, + remove_old_states: bool, // This field must be the last one so that the temporary directory is deleted at the very end. state_dir: Box, // DO NOT PUT ANY FIELDS AFTER `state_dir`!!! @@ -928,6 +925,7 @@ pub struct StateMachineBuilder { with_extra_canister_range: Option>, log_level: Option, bitcoin_testnet_uds_path: Option, + remove_old_states: bool, } impl StateMachineBuilder { @@ -960,6 +958,7 @@ impl StateMachineBuilder { with_extra_canister_range: None, log_level: Some(Level::Warning), bitcoin_testnet_uds_path: None, + remove_old_states: true, } } @@ -1140,6 +1139,13 @@ impl StateMachineBuilder { } } + pub fn with_remove_old_states(self, remove_old_states: bool) -> Self { + Self { + remove_old_states, + ..self + } + } + pub fn build_internal(self) -> StateMachine { StateMachine::setup_from_dir( self.state_dir, @@ -1167,6 +1173,7 @@ impl StateMachineBuilder { self.is_root_subnet, self.seed, self.log_level, + self.remove_old_states, ) } @@ -1224,7 +1231,12 @@ impl StateMachineBuilder { // Instantiate a `XNetPayloadBuilderImpl`. // We need to use a deterministic PRNG - so we use an arbitrary fixed seed, e.g., 42. let rng = Arc::new(Some(Mutex::new(StdRng::seed_from_u64(42)))); - let xnet_slice_pool_impl = Box::new(PocketXNetSlicePoolImpl::new(subnets, subnet_id)); + let certified_stream_store: Arc = sm.state_manager.clone(); + let certified_slice_pool = Arc::new(Mutex::new(CertifiedSlicePool::new( + certified_stream_store, + &sm.metrics_registry, + ))); + let xnet_slice_pool_impl = Box::new(XNetSlicePoolImpl::new(certified_slice_pool.clone())); let metrics = Arc::new(XNetPayloadBuilderMetrics::new(&sm.metrics_registry)); let xnet_payload_builder = Arc::new(XNetPayloadBuilderImpl::new_from_components( sm.state_manager.clone(), @@ -1262,6 +1274,10 @@ impl StateMachineBuilder { sm.replica_logger.clone(), )); + // Put `PocketXNetImpl` into `StateMachine` + // which contains no `PocketXNetImpl` after creation. + let pocket_xnet_impl = PocketXNetImpl::new(subnets, certified_slice_pool, subnet_id); + *sm.pocket_xnet.write().unwrap() = Some(pocket_xnet_impl); // Instantiate a `PayloadBuilderImpl` and put it into `StateMachine` // which contains no `PayloadBuilderImpl` after creation. *sm.payload_builder.write().unwrap() = Some(PayloadBuilderImpl::new( @@ -1305,6 +1321,7 @@ impl StateMachine { /// because the payload builder contains an `Arc` of this `StateMachine` /// which creates a circular dependency preventing this `StateMachine`s from being dropped. pub fn drop_payload_builder(&self) { + self.pocket_xnet.write().unwrap().take(); self.payload_builder.write().unwrap().take(); } @@ -1349,6 +1366,12 @@ impl StateMachine { membership_version: subnet_record.clone(), context_version: subnet_record, }; + self.pocket_xnet + .read() + .unwrap() + .as_ref() + .unwrap() + .refill(registry_version, self.replica_logger.clone()); let payload_builder = self.payload_builder.read().unwrap(); let payload_builder = payload_builder.as_ref().unwrap(); let batch_payload = payload_builder.get_payload( @@ -1362,9 +1385,7 @@ impl StateMachine { // used by the function `Self::execute_payload` of the `StateMachine`. let xnet_payload = batch_payload.xnet.clone(); let ingress = &batch_payload.ingress; - let ingress_messages = (0..ingress.message_count()) - .map(|i| ingress.get(i).unwrap().1) - .collect(); + let ingress_messages = ingress.clone().try_into().unwrap(); let (http_responses, _) = CanisterHttpPayloadBuilderImpl::into_messages(&batch_payload.canister_http); let inducted: Vec<_> = http_responses @@ -1462,6 +1483,7 @@ impl StateMachine { is_root_subnet: bool, seed: [u8; 32], log_level: Option, + remove_old_states: bool, ) -> Self { let checkpoint_interval_length = checkpoint_interval_length.unwrap_or(match subnet_type { SubnetType::Application | SubnetType::VerifiedApplication => 499, @@ -1596,6 +1618,7 @@ impl StateMachine { Arc::clone(&state_manager) as Arc<_>, Arc::clone(&state_manager.get_fd_factory()), completed_execution_messages_tx, + &state_manager.state_layout().tmp(), ) }); @@ -1774,6 +1797,7 @@ impl StateMachine { ingress_pool, ingress_manager: ingress_manager.clone(), ingress_filter: Arc::new(Mutex::new(execution_services.ingress_filter)), + pocket_xnet: Arc::new(RwLock::new(None)), // set by `StateMachineBuilder::build_with_subnets` payload_builder: Arc::new(RwLock::new(None)), // set by `StateMachineBuilder::build_with_subnets` ingress_history_reader: execution_services.ingress_history_reader, message_routing, @@ -1801,10 +1825,11 @@ impl StateMachine { canister_http_pool, canister_http_payload_builder, query_stats_payload_builder: pocket_query_stats_payload_builder, + remove_old_states, } } - fn into_components(self) -> (Box, u64, Time, u64) { + fn into_components_inner(self) -> (Box, u64, Time, u64) { ( self.state_dir, self.nonce.into_inner(), @@ -1813,6 +1838,29 @@ impl StateMachine { ) } + fn into_components(self) -> (Box, u64, Time, u64) { + let state_manager = Arc::downgrade(&self.state_manager); + let result = self.into_components_inner(); + // StateManager is owned by an Arc, that is cloned into multiple components and different + // threads. If we return before all the asynchronous components release the Arc, we may + // end up with to StateManagers writing to the same directory, resulting in a crash. + let start = std::time::Instant::now(); + while state_manager.upgrade().is_some() { + std::thread::sleep(std::time::Duration::from_millis(50)); + if start.elapsed() > std::time::Duration::from_secs(5 * 60) { + panic!("Timed out while dropping StateMachine."); + } + } + result + } + + /// Safely drops this `StateMachine`. We cannot achieve this functionality by implementing `Drop` + /// since we have to wait until there are no more `Arc`s for the state manager and + /// this is infeasible in a `Drop` implementation. + pub fn drop(self) { + let _ = self.into_components(); + } + /// Emulates a node restart, including checkpoint recovery. pub fn restart_node(self) -> Self { // We must drop self before setup_form_dir so that we don't have two StateManagers pointing @@ -2365,7 +2413,9 @@ impl StateMachine { .process_batch(batch) .expect("Could not process batch"); - self.state_manager.remove_states_below(batch_number); + if self.remove_old_states { + self.state_manager.remove_states_below(batch_number); + } assert_eq!( self.state_manager .latest_state_certification_hash() @@ -2463,6 +2513,26 @@ impl StateMachine { .unwrap_or_else(|_| error!(self.replica_logger, "Time went backwards.")); } + /// Certifies the specified time by modifying the time in the replicated state + /// and certifying that new state. + pub fn set_certified_time(&self, time: SystemTime) { + let t = time + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_nanos() as u64; + let time = Time::from_nanos_since_unix_epoch(t); + let (height, mut replicated_state) = self.state_manager.take_tip(); + replicated_state.metadata.batch_time = time; + self.state_manager.commit_and_certify( + replicated_state, + height.increment(), + CertificationScope::Metadata, + None, + ); + self.set_time(time.into()); + *self.time_of_last_round.write().unwrap() = time; + } + /// Returns the current state machine time. /// The time of a round executed by this state machine equals its current time /// if its current time increased since the last round. @@ -2568,8 +2638,7 @@ impl StateMachine { /// After you import the canister, you can execute methods on it and upgrade it. /// The original directory is not modified. /// - /// The function is currently not used in code, but it is useful for local - /// testing and debugging. Do not remove it. + /// This function is useful for local testing and debugging. Do not remove it. /// /// # Panics /// @@ -2579,6 +2648,8 @@ impl StateMachine { canister_directory: P, canister_id: CanisterId, ) { + use ic_replicated_state::testing::SystemStateTesting; + let canister_directory = canister_directory.as_ref(); assert!( canister_directory.is_dir(), @@ -2631,7 +2702,7 @@ impl StateMachine { } } - let canister_state = ic_state_manager::checkpoint::load_canister_state( + let mut canister_state = ic_state_manager::checkpoint::load_canister_state( &tip_canister_layout, &canister_id, ic_types::Height::new(0), @@ -2648,7 +2719,14 @@ impl StateMachine { .0; let (h, mut state) = self.state_manager.take_tip(); + + // Repartition input schedules; Required step for migrating canisters. + canister_state + .system_state + .split_input_schedules(&canister_id, &state.canister_states); + state.put_canister_state(canister_state); + self.state_manager.commit_and_certify( state, h.increment(), @@ -3237,6 +3315,11 @@ impl StateMachine { (self.ingress_history_reader.get_latest_status())(msg_id) } + /// Returns the caller of the ingress message with the specified ID if available. + pub fn ingress_caller(&self, msg_id: &MessageId) -> Option { + self.get_latest_state().get_ingress_status(msg_id).user_id() + } + /// Starts the canister with the specified ID. pub fn start_canister(&self, canister_id: CanisterId) -> Result { self.start_canister_as(PrincipalId::new_anonymous(), canister_id) @@ -3532,7 +3615,7 @@ impl StateMachine { let canister_state = replicated_state .canister_state_mut(&canister_id) .unwrap_or_else(|| panic!("Canister {} does not exist", canister_id)); - let size = (data.len() + WASM_PAGE_SIZE_IN_BYTES - 1) / WASM_PAGE_SIZE_IN_BYTES; + let size = data.len().div_ceil(WASM_PAGE_SIZE_IN_BYTES); let memory = Memory::new(PageMap::from(data), NumWasmPages::new(size)); canister_state .execution_state diff --git a/rs/state_machine_tests/tests/tests.rs b/rs/state_machine_tests/tests/tests.rs index fadf407ef62..6501f493fda 100644 --- a/rs/state_machine_tests/tests/tests.rs +++ b/rs/state_machine_tests/tests/tests.rs @@ -40,6 +40,10 @@ fn test() { time.duration_since(SystemTime::UNIX_EPOCH).unwrap(), Duration::from_nanos(1_620_329_630_000_000_000) ); + + // Kill the task to avoid zombie process. + child.kill().unwrap(); + child.wait().unwrap(); } fn call_state_machine( diff --git a/rs/state_manager/src/checkpoint.rs b/rs/state_manager/src/checkpoint.rs index ba76d6f4130..d76aaad6be1 100644 --- a/rs/state_manager/src/checkpoint.rs +++ b/rs/state_manager/src/checkpoint.rs @@ -7,10 +7,11 @@ use ic_replicated_state::canister_snapshots::{ CanisterSnapshot, CanisterSnapshots, ExecutionStateSnapshot, PageMemory, }; use ic_replicated_state::canister_state::system_state::wasm_chunk_store::WasmChunkStore; -use ic_replicated_state::page_map::{storage::verify, PageAllocatorFileDescriptor}; +use ic_replicated_state::page_map::{storage::validate, PageAllocatorFileDescriptor}; use ic_replicated_state::{ - canister_state::execution_state::WasmBinary, page_map::PageMap, CanisterMetrics, CanisterState, - ExecutionState, ReplicatedState, SchedulerState, SystemState, + canister_state::execution_state::WasmBinary, + canister_state::execution_state::WasmExecutionMode, page_map::PageMap, CanisterMetrics, + CanisterState, ExecutionState, ReplicatedState, SchedulerState, SystemState, }; use ic_replicated_state::{CheckpointLoadingMetrics, Memory}; use ic_state_layout::{ @@ -20,6 +21,7 @@ use ic_state_layout::{ use ic_types::batch::RawQueryStats; use ic_types::{CanisterTimer, Height, Time}; use ic_utils::thread::maybe_parallel_map; +use ic_validate_eq::ValidateEq; use std::collections::BTreeMap; use std::convert::{identity, TryFrom}; use std::sync::Arc; @@ -27,7 +29,8 @@ use std::time::{Duration, Instant}; use crate::{ CheckpointError, CheckpointMetrics, HasDowngrade, PageMapType, TipRequest, - CRITICAL_ERROR_CHECKPOINT_SOFT_INVARIANT_BROKEN, NUMBER_OF_CHECKPOINT_THREADS, + CRITICAL_ERROR_CHECKPOINT_SOFT_INVARIANT_BROKEN, + CRITICAL_ERROR_REPLICATED_STATE_ALTERED_AFTER_CHECKPOINT, NUMBER_OF_CHECKPOINT_THREADS, }; #[cfg(test)] @@ -47,24 +50,15 @@ impl CheckpointLoadingMetrics for CheckpointMetrics { } /// Creates a checkpoint of the node state using specified directory -/// layout. Returns a new state that is equivalent to the given one -/// and a result of the operation. -/// -/// This function uses the provided thread-pool to parallelize expensive -/// operations. -/// -/// If the result is `Ok`, the returned state is "rebased" to use -/// files from the newly created checkpoint. If the result is `Err`, -/// the returned state is exactly the one that was passed as argument. -pub(crate) fn make_checkpoint( +/// layout. Returns a layout of the new state that is equivalent to the +/// given one and a result of the operation. +pub(crate) fn make_unvalidated_checkpoint( state: &ReplicatedState, height: Height, tip_channel: &Sender, metrics: &CheckpointMetrics, - thread_pool: &mut scoped_threadpool::Pool, - fd_factory: Arc, lsmt_storage: FlagStatus, -) -> Result<(CheckpointLayout, ReplicatedState, HasDowngrade), CheckpointError> { +) -> Result<(CheckpointLayout, HasDowngrade), CheckpointError> { { let _timer = metrics .make_checkpoint_step_duration @@ -124,34 +118,34 @@ pub(crate) fn make_checkpoint( recv.recv().unwrap(); } - let state = { - let _timer = metrics - .make_checkpoint_step_duration - .with_label_values(&["load"]) - .start_timer(); - load_checkpoint( - &cp, - state.metadata.own_subnet_type, - metrics, - Some(thread_pool), - Arc::clone(&fd_factory), - )? - }; - - Ok((cp, state, has_downgrade)) + Ok((cp, has_downgrade)) } pub(crate) fn validate_checkpoint_and_remove_unverified_marker( checkpoint_layout: &CheckpointLayout, + reference_state: Option<&ReplicatedState>, + own_subnet_type: SubnetType, + fd_factory: Arc, + metrics: &CheckpointMetrics, mut thread_pool: Option<&mut scoped_threadpool::Pool>, ) -> Result<(), CheckpointError> { maybe_parallel_map( &mut thread_pool, checkpoint_layout.all_existing_pagemaps()?.into_iter(), - |pm| verify(pm), + |pm| validate(pm), ) .into_iter() .try_for_each(identity)?; + if let Some(reference_state) = reference_state { + validate_eq_checkpoint( + checkpoint_layout, + reference_state, + own_subnet_type, + thread_pool, + fd_factory, + metrics, + ); + } checkpoint_layout .remove_unverified_checkpoint_marker() .map_err(CheckpointError::from)?; @@ -176,7 +170,14 @@ pub fn load_checkpoint_and_validate_parallel( Some(&mut thread_pool), Arc::clone(&fd_factory), )?; - validate_checkpoint_and_remove_unverified_marker(checkpoint_layout, Some(&mut thread_pool))?; + validate_checkpoint_and_remove_unverified_marker( + checkpoint_layout, + None, + own_subnet_type, + fd_factory, + metrics, + Some(&mut thread_pool), + )?; Ok(state) } @@ -249,7 +250,7 @@ impl CheckpointLoader { .map_err(|err| self.map_to_checkpoint_error("CanisterQueues".into(), err)) } - fn load_query_stats(&self) -> Result { + fn load_epoch_query_stats(&self) -> Result { let stats = self.checkpoint_layout.stats().deserialize()?; if let Some(query_stats) = stats.query_stats { Ok(RawQueryStats::try_from(query_stats) @@ -290,6 +291,45 @@ impl CheckpointLoader { Ok(canister_states) } + fn validate_eq_canister_states( + &self, + thread_pool: &mut Option<&mut scoped_threadpool::Pool>, + ref_canister_states: &BTreeMap, + ) -> Result<(), String> { + let on_disk_canister_ids = self + .checkpoint_layout + .canister_ids() + .map_err(|err| format!("Canister Validation: failed to load canister ids: {}", err))?; + let ref_canister_ids: Vec<_> = ref_canister_states.keys().copied().collect(); + debug_assert!(on_disk_canister_ids.is_sorted()); + debug_assert!(ref_canister_ids.is_sorted()); + if on_disk_canister_ids != ref_canister_ids { + return Err("Canister ids mismatch".to_string()); + } + maybe_parallel_map(thread_pool, ref_canister_ids.iter(), |canister_id| { + load_canister_state_from_checkpoint( + &self.checkpoint_layout, + canister_id, + Arc::clone(&self.fd_factory), + &self.metrics, + ) + .map_err(|err| { + format!( + "Failed to load canister state for validation for key #{}: {}", + canister_id, err + ) + })? + .0 + .validate_eq( + ref_canister_states + .get(canister_id) + .expect("Failed to get canister from canister_states"), + ) + }) + .into_iter() + .try_for_each(identity) + } + fn load_canister_snapshots( &self, thread_pool: &mut Option<&mut scoped_threadpool::Pool>, @@ -322,6 +362,49 @@ impl CheckpointLoader { Ok(CanisterSnapshots::new(canister_snapshots)) } + + fn validate_eq_canister_snapshots( + &self, + thread_pool: &mut Option<&mut scoped_threadpool::Pool>, + ref_canister_snapshots: &CanisterSnapshots, + ) -> Result<(), String> { + let mut on_disk_snapshot_ids = self.checkpoint_layout.snapshot_ids().map_err(|err| { + format!( + "Snapshot validation: failed to load list of snapshot ids: {}", + err + ) + })?; + let mut ref_snapshot_ids: Vec<_> = ref_canister_snapshots.iter().map(|x| *x.0).collect(); + on_disk_snapshot_ids.sort(); + ref_snapshot_ids.sort(); + if on_disk_snapshot_ids != ref_snapshot_ids { + return Err("Snapshot ids mismatch".to_string()); + } + if !ref_canister_snapshots.is_unflushed_changes_empty() { + return Err("Snapshots have unflushed changes after checkpoint".to_string()); + } + maybe_parallel_map(thread_pool, ref_snapshot_ids.iter(), |snapshot_id| { + load_snapshot_from_checkpoint( + &self.checkpoint_layout, + snapshot_id, + Arc::clone(&self.fd_factory), + ) + .map_err(|err| { + format!( + "Failed to load canister snapshot {} for validation: {}", + snapshot_id, err + ) + })? + .0 + .validate_eq( + ref_canister_snapshots + .get(**snapshot_id) + .expect("Failed to lookup snapshot in ref state"), + ) + }) + .into_iter() + .try_for_each(identity) + } } /// Loads the node state heighted with `height` using the specified @@ -343,11 +426,80 @@ pub fn load_checkpoint( checkpoint_loader.load_canister_states(&mut thread_pool)?, checkpoint_loader.load_system_metadata()?, checkpoint_loader.load_subnet_queues()?, - checkpoint_loader.load_query_stats()?, + checkpoint_loader.load_epoch_query_stats()?, checkpoint_loader.load_canister_snapshots(&mut thread_pool)?, )) } +pub fn validate_eq_checkpoint( + checkpoint_layout: &CheckpointLayout, + reference_state: &ReplicatedState, + own_subnet_type: SubnetType, + thread_pool: Option<&mut scoped_threadpool::Pool>, + fd_factory: Arc, // + metrics: &CheckpointMetrics, // Make optional in the loader & don't provide? +) { + validate_eq_checkpoint_internal( + checkpoint_layout, + reference_state, + own_subnet_type, + thread_pool, + fd_factory, + metrics, + ) + .unwrap_or_else(|err: String| { + error!( + &metrics.log, + "{}: Replicated state altered: {}", + CRITICAL_ERROR_REPLICATED_STATE_ALTERED_AFTER_CHECKPOINT, + err + ); + metrics.replicated_state_altered_after_checkpoint.inc(); + }); +} + +fn validate_eq_checkpoint_internal( + checkpoint_layout: &CheckpointLayout, + reference_state: &ReplicatedState, + own_subnet_type: SubnetType, + mut thread_pool: Option<&mut scoped_threadpool::Pool>, + fd_factory: Arc, // + metrics: &CheckpointMetrics, // Make optional in the loader & don't provide? +) -> Result<(), String> { + let ( + canister_states, + metadata, + subnet_queues, + consensus_queue, + epoch_query_stats, + canister_snapshots, + ) = reference_state.component_refs(); + + let checkpoint_loader = CheckpointLoader { + checkpoint_layout: checkpoint_layout.clone(), + own_subnet_type, + metrics: metrics.clone(), + fd_factory, + }; + + checkpoint_loader.validate_eq_canister_states(&mut thread_pool, canister_states)?; + checkpoint_loader + .load_system_metadata() + .map_err(|err| format!("Failed to load system metadata: {}", err))? + .validate_eq(metadata)?; + checkpoint_loader + .load_subnet_queues() + .unwrap() + .validate_eq(subnet_queues)?; + if checkpoint_loader.load_epoch_query_stats().unwrap() != *epoch_query_stats { + return Err("query_stats has diverged.".to_string()); + } + if !consensus_queue.is_empty() { + return Err("consensus_queue is not empty".to_string()); + } + checkpoint_loader.validate_eq_canister_snapshots(&mut thread_pool, canister_snapshots) +} + #[derive(Default)] pub struct LoadCanisterMetrics { durations: BTreeMap<&'static str, Duration>, @@ -438,7 +590,9 @@ pub fn load_canister_state( metadata: execution_state_bits.metadata, last_executed_round: execution_state_bits.last_executed_round, next_scheduled_method: execution_state_bits.next_scheduled_method, - is_wasm64: execution_state_bits.is_wasm64, + wasm_execution_mode: WasmExecutionMode::from_is_wasm64( + execution_state_bits.is_wasm64, + ), }) } None => None, diff --git a/rs/state_manager/src/checkpoint/tests.rs b/rs/state_manager/src/checkpoint/tests.rs index fd74ad59800..41b02225459 100644 --- a/rs/state_manager/src/checkpoint/tests.rs +++ b/rs/state_manager/src/checkpoint/tests.rs @@ -69,19 +69,26 @@ fn make_checkpoint_and_get_state_impl( tip_channel: &Sender, log: &ReplicaLogger, ) -> ReplicatedState { - let mut thread_pool = thread_pool(); - let (cp_layout, state, _has_downgrade) = make_checkpoint( + let (cp_layout, _has_downgrade) = make_unvalidated_checkpoint( state, height, tip_channel, &state_manager_metrics(log).checkpoint_metrics, - &mut thread_pool, - Arc::new(TestPageAllocatorFileDescriptorImpl::new()), ic_config::state_manager::lsmt_config_default().lsmt_status, ) - .unwrap_or_else(|err| panic!("Expected make_checkpoint to succeed, got {:?}", err)); - validate_checkpoint_and_remove_unverified_marker(&cp_layout, Some(&mut thread_pool)).unwrap(); - state + .unwrap_or_else(|err| { + panic!( + "Expected make_unvalidated_checkpoint to succeed, got {:?}", + err + ) + }); + load_checkpoint_and_validate_parallel( + &cp_layout, + state.metadata.own_subnet_type, + &state_manager_metrics(log).checkpoint_metrics, + Arc::new(TestPageAllocatorFileDescriptorImpl::new()), + ) + .unwrap() } fn make_checkpoint_and_get_state( @@ -190,13 +197,11 @@ fn scratchpad_dir_is_deleted_if_checkpointing_failed() { // Scratchpad directory is "tmp/scatchpad_{hex(height)}" let expected_scratchpad_dir = root.join("tmp").join("scratchpad_000000000000002a"); - let replicated_state = make_checkpoint( + let replicated_state = make_unvalidated_checkpoint( &state, HEIGHT, &tip_channel, &state_manager_metrics.checkpoint_metrics, - &mut thread_pool(), - Arc::new(TestPageAllocatorFileDescriptorImpl::new()), ic_config::state_manager::lsmt_config_default().lsmt_status, ); diff --git a/rs/state_manager/src/lib.rs b/rs/state_manager/src/lib.rs index 5785c22bb0d..2db08326fd5 100644 --- a/rs/state_manager/src/lib.rs +++ b/rs/state_manager/src/lib.rs @@ -18,7 +18,7 @@ use crate::{ }, tip::{spawn_tip_thread, HasDowngrade, PageMapToFlush, TipRequest}, }; -use crossbeam_channel::{bounded, unbounded, Sender}; +use crossbeam_channel::{unbounded, Sender}; use ic_canonical_state::lazy_tree_conversion::replicated_state_as_lazy_tree; use ic_canonical_state_tree_hash::{ hash_tree::{hash_lazy_tree, HashTree, HashTreeError}, @@ -62,8 +62,7 @@ use ic_types::{ CanisterId, CryptoHashOfPartialState, CryptoHashOfState, Height, RegistryVersion, SnapshotId, SubnetId, }; -use ic_utils_thread::JoinOnDrop; -use ic_validate_eq::ValidateEq; +use ic_utils_thread::{deallocator_thread::DeallocatorThread, JoinOnDrop}; use prometheus::{Histogram, HistogramVec, IntCounter, IntCounterVec, IntGauge, IntGaugeVec}; use prost::Message; use std::convert::{From, TryFrom}; @@ -329,7 +328,7 @@ impl MergeMetrics { // very first metric update should be visible to Prometheus. impl StateManagerMetrics { - fn new(metrics_registry: &MetricsRegistry, log: ReplicaLogger) -> Self { + pub fn new(metrics_registry: &MetricsRegistry, log: ReplicaLogger) -> Self { let checkpoint_op_duration = metrics_registry.histogram_vec( "state_manager_checkpoint_op_duration_seconds", "Duration of checkpoint operations in seconds.", @@ -794,24 +793,6 @@ impl SharedState { } } -// We send complex objects to a different thread to free them. This will spread -// the cost of deallocation over a longer period of time, and avoid long pauses. -type Deallocation = Box; - -struct NotifyWhenDeallocated { - channel: Sender<()>, -} - -impl Drop for NotifyWhenDeallocated { - fn drop(&mut self) { - self.channel.send(()).expect("Failed to notify dellocation"); - } -} - -// We will not use the deallocation thread when the number of pending -// deallocation objects goes above the threshold. -const DEALLOCATION_BACKLOG_THRESHOLD: usize = 500; - /// The number of archived and diverged states to keep before we start deleting the old ones. const MAX_ARCHIVED_DIVERGED_CHECKPOINTS_TO_KEEP: usize = 1; @@ -833,12 +814,11 @@ pub struct StateManagerImpl { verifier: Arc, own_subnet_id: SubnetId, own_subnet_type: SubnetType, - deallocation_sender: Sender, + deallocator_thread: DeallocatorThread, // Cached latest state height. We cache it separately because it's // requested quite often and this causes high contention on the lock. latest_state_height: AtomicU64, latest_certified_height: AtomicU64, - _deallocation_handle: JoinOnDrop<()>, persist_metadata_guard: Arc>, tip_channel: Sender, _tip_thread_handle: JoinOnDrop<()>, @@ -1259,109 +1239,110 @@ fn strip_page_map_deltas( } } -/// Switches `tip` to the most recent checkpoint file provided by `src`. +/// Switches `tip` to the most recent checkpoint file provided by `layout`. /// /// Preconditions: -/// 1) `tip` and `src` mut have exactly the same set of canisters. -/// 2) The page deltas must be empty in both states. +/// 1) `tip` and `layout` mut have exactly the same set of canisters. +/// 2) The page deltas must be empty in `tip` /// 3) The memory sizes must match. -fn switch_to_checkpoint(tip: &mut ReplicatedState, src: &ReplicatedState) { - for ((tip_id, tip_canister), (src_id, src_canister)) in tip - .canister_states - .iter_mut() - .zip(src.canister_states.iter()) - { - assert_eq!(tip_id, src_id); - +fn switch_to_checkpoint( + tip: &mut ReplicatedState, + layout: &CheckpointLayout, + fd_factory: &Arc, +) -> Result<(), Box> { + for (tip_id, tip_canister) in tip.canister_states.iter_mut() { + let canister_layout = layout.canister(tip_id).unwrap(); tip_canister .system_state .wasm_chunk_store .page_map_mut() - .switch_to_checkpoint(src_canister.system_state.wasm_chunk_store.page_map()); - - assert_eq!( - src_canister.execution_state.is_some(), - tip_canister.execution_state.is_some() - ); + .switch_to_checkpoint(&PageMap::open( + Box::new(canister_layout.wasm_chunk_store()), + layout.height(), + Arc::clone(fd_factory), + )?); - if let (Some(src_execution), Some(tip_execution)) = ( - src_canister.execution_state.as_ref(), - tip_canister.execution_state.as_mut(), - ) { + if let Some(tip_execution) = tip_canister.execution_state.as_mut() { tip_execution .wasm_memory .page_map - .switch_to_checkpoint(&src_execution.wasm_memory.page_map); + .switch_to_checkpoint(&PageMap::open( + Box::new(canister_layout.vmemory_0()), + layout.height(), + Arc::clone(fd_factory), + )?); tip_execution .stable_memory .page_map - .switch_to_checkpoint(&src_execution.stable_memory.page_map); + .switch_to_checkpoint(&PageMap::open( + Box::new(canister_layout.stable_memory()), + layout.height(), + Arc::clone(fd_factory), + )?); } } - for ((tip_id, tip_snapshot), (src_id, src_snapshot)) in tip - .canister_snapshots - .iter_mut() - .zip(src.canister_snapshots.iter()) - { + for (tip_id, tip_snapshot) in tip.canister_snapshots.iter_mut() { let new_snapshot = Arc::make_mut(tip_snapshot); - - assert_eq!(tip_id, src_id); + let snapshot_layout = layout.snapshot(tip_id).unwrap(); new_snapshot .chunk_store_mut() .page_map_mut() - .switch_to_checkpoint(src_snapshot.chunk_store().page_map()); + .switch_to_checkpoint(&PageMap::open( + Box::new(snapshot_layout.wasm_chunk_store()), + layout.height(), + Arc::clone(fd_factory), + )?); new_snapshot .execution_snapshot_mut() .wasm_memory .page_map - .switch_to_checkpoint(&src_snapshot.execution_snapshot().wasm_memory.page_map); + .switch_to_checkpoint(&PageMap::open( + Box::new(snapshot_layout.vmemory_0()), + layout.height(), + Arc::clone(fd_factory), + )?); new_snapshot .execution_snapshot_mut() .stable_memory .page_map - .switch_to_checkpoint(&src_snapshot.execution_snapshot().stable_memory.page_map); + .switch_to_checkpoint(&PageMap::open( + Box::new(snapshot_layout.stable_memory()), + layout.height(), + Arc::clone(fd_factory), + )?); } - for (tip_canister, src_canister) in tip.canisters_iter_mut().zip(src.canisters_iter()) { - assert_eq!( - tip_canister.system_state.canister_id, - src_canister.system_state.canister_id - ); - assert_eq!( - tip_canister.execution_state.is_some(), - src_canister.execution_state.is_some(), - "execution state of canister {} unexpectedly (dis)appeared after creating a checkpoint", - tip_canister.system_state.canister_id - ); - if let (Some(tip_state), Some(src_state)) = ( - &mut tip_canister.execution_state, - &src_canister.execution_state, - ) { - debug_assert_eq!( - tip_state.wasm_binary.binary.as_slice(), - src_state.wasm_binary.binary.as_slice() - ); + for (tip_id, tip_canister) in tip.canister_states.iter_mut() { + if let Some(tip_state) = &mut tip_canister.execution_state { + let canister_layout = layout.canister(tip_id).unwrap(); // We can reuse the cache because the Wasm binary has the same // contents, only the storage of that binary changed. let embedder_cache = Arc::clone(&tip_state.wasm_binary.embedder_cache); + let wasm_binary = canister_layout + .wasm() + .deserialize(Some(tip_state.wasm_binary.binary.module_hash().into()))?; + debug_assert_eq!( + tip_state.wasm_binary.binary.as_slice(), + wasm_binary.as_slice() + ); tip_state.wasm_binary = Arc::new( ic_replicated_state::canister_state::execution_state::WasmBinary { - binary: src_state.wasm_binary.binary.clone(), + binary: wasm_binary, embedder_cache, }, ); - assert_eq!(tip_state.wasm_memory.size, src_state.wasm_memory.size); // Reset the sandbox state to force full synchronization on the next message // execution because the checkpoint file of `tip` has changed. tip_state.wasm_memory.sandbox_memory = SandboxMemory::new(); tip_state.stable_memory.sandbox_memory = SandboxMemory::new(); } } + Ok(()) } /// Persists metadata after releasing the write lock @@ -1718,22 +1699,8 @@ impl StateManagerImpl { let persist_metadata_guard = Arc::new(Mutex::new(())); - #[allow(clippy::disallowed_methods)] - let (deallocation_sender, deallocation_receiver) = unbounded(); - let _deallocation_handle = JoinOnDrop::new( - std::thread::Builder::new() - .name("StateDeallocation".to_string()) - .spawn({ - move || { - while let Ok(object) = deallocation_receiver.recv() { - std::mem::drop(object); - // The sleep below is to spread out the load on memory allocator - std::thread::sleep(std::time::Duration::from_millis(1)); - } - } - }) - .expect("failed to spawn background deallocation thread"), - ); + let deallocator_thread = + DeallocatorThread::new("StateDeallocator", Duration::from_millis(1)); for checkpoint_layout in checkpoint_layouts_to_compute_manifest { tip_channel @@ -1756,10 +1723,9 @@ impl StateManagerImpl { verifier, own_subnet_id, own_subnet_type, - deallocation_sender, + deallocator_thread, latest_state_height, latest_certified_height, - _deallocation_handle, persist_metadata_guard, tip_channel, _tip_thread_handle, @@ -1849,7 +1815,12 @@ impl StateManagerImpl { match StateMetadata::try_from(pb) { Ok(meta) => { if let Some(root_hash) = meta.root_hash() { - info!(log, "Recomputed root hash {:?} when loading state metadata at height {}", root_hash, h); + info!( + log, + "Root hash {:?} when loading state metadata at height {}", + root_hash, + h + ); } map.insert(Height::new(h), meta); } @@ -2226,12 +2197,7 @@ impl StateManagerImpl { /// Wait till deallocation queue is empty. pub fn flush_deallocation_channel(&self) { - let (send, recv) = bounded(1); - self.deallocation_sender - .send(Box::new(NotifyWhenDeallocated { channel: send })) - .expect("Failed to send deallocation request"); - recv.recv() - .expect("Failed to receive deallocation notification"); + self.deallocator_thread.flush_deallocation_channel(); } /// Remove any inmemory state at height h with h < last_height_to_keep @@ -2309,17 +2275,6 @@ impl StateManagerImpl { .chain(extra_inmemory_heights_to_keep.iter().copied()) .collect::>(); - // Send object to deallocation thread if it has capacity. - let deallocate = |x| { - if self.deallocation_sender.len() < DEALLOCATION_BACKLOG_THRESHOLD { - self.deallocation_sender - .send(x) - .expect("failed to send object to deallocation thread"); - } else { - std::mem::drop(x); - } - }; - let (removed, retained) = states.snapshots.drain(0..).partition(|snapshot| { heights_to_remove.contains(&snapshot.height) && !inmemory_heights_to_keep.contains(&snapshot.height) @@ -2355,7 +2310,7 @@ impl StateManagerImpl { .set(latest_height.get() as i64); // Send removed snapshot to deallocator thread - deallocate(Box::new(removed)); + self.deallocator_thread.send(Box::new(removed)); for (height, metadata) in states.states_metadata.range(heights_to_remove) { if checkpoint_heights_to_keep.contains(height) { @@ -2382,8 +2337,9 @@ impl StateManagerImpl { &mut states.certifications_metadata, ); - // Send removed certification metadata to deallocator thread - deallocate(Box::new(certifications_metadata)); + // Send removed certification metadata to deallocator thread. + self.deallocator_thread + .send(Box::new(certifications_metadata)); let latest_certified_height = states .certifications_metadata @@ -2416,7 +2372,10 @@ impl StateManagerImpl { // // NOTE: we rely on deallocations happening sequentially, adding more // deallocation threads might break the desired behavior. - deallocate(Box::new(metadata_to_keep)); + // + // FIXME: Objects are not necessarily deleted in order: if the backlog is too + // large, we drop them synchronously. + self.deallocator_thread.send(Box::new(metadata_to_keep)); } if number_of_checkpoints != states.states_metadata.len() { @@ -2606,17 +2565,15 @@ impl StateManagerImpl { strip_page_map_deltas(&mut state, self.get_fd_factory()); } let result = { - checkpoint::make_checkpoint( + checkpoint::make_unvalidated_checkpoint( &state, height, &self.tip_channel, &self.metrics.checkpoint_metrics, - &mut scoped_threadpool::Pool::new(NUMBER_OF_CHECKPOINT_THREADS), - self.get_fd_factory(), self.lsmt_status, ) }; - let (cp_layout, checkpointed_state, has_downgrade) = match result { + let (cp_layout, has_downgrade) = match result { Ok(response) => response, Err(CheckpointError::AlreadyExists(_)) => { warn!( @@ -2626,35 +2583,19 @@ impl StateManagerImpl { height ); - let (cp_verified, checkpointed_state) = self + let cp_layout = self .state_layout .checkpoint_in_verification(height) - .map_err(CheckpointError::from) - .and_then(|layout| { - let _timer = self - .metrics - .checkpoint_op_duration - .with_label_values(&["recover"]) - .start_timer(); - let state = checkpoint::load_checkpoint_and_validate_parallel( - &layout, - self.own_subnet_type, - &self.metrics.checkpoint_metrics, - self.get_fd_factory(), - )?; - Ok((layout, state)) - }) .unwrap_or_else(|err| { fatal!( self.log, - "Failed to load existing checkpoint @{}: {}", + "Failed to open checkpoint layout #{}: {}", height, err - ) + ); }); ( - cp_verified, - checkpointed_state, + cp_layout, // HasDowngrade::Yes is the conservative choice, opting for full Manifest computation. HasDowngrade::Yes, ) @@ -2678,26 +2619,6 @@ impl StateManagerImpl { let canisters = std::mem::take(&mut state.canister_states); state.canister_states = canisters.into_iter().collect(); } - { - let _timer = self - .metrics - .checkpoint_metrics - .make_checkpoint_step_duration - .with_label_values(&["validate_eq"]) - .start_timer(); - if let Err(err) = checkpointed_state.validate_eq(&state) { - error!( - self.log, - "{}: Replicated state altered: {}", - CRITICAL_ERROR_REPLICATED_STATE_ALTERED_AFTER_CHECKPOINT, - err - ); - self.metrics - .checkpoint_metrics - .replicated_state_altered_after_checkpoint - .inc(); - } - } { let _timer = self .metrics @@ -2705,13 +2626,26 @@ impl StateManagerImpl { .make_checkpoint_step_duration .with_label_values(&["switch_to_checkpoint"]) .start_timer(); - switch_to_checkpoint(&mut state, &checkpointed_state); - self.tip_channel - .send(TipRequest::ValidateReplicatedState { - checkpoint_layout: cp_layout.clone(), - }) - .expect("Failed to send Validate request"); + switch_to_checkpoint(&mut state, &cp_layout, &self.get_fd_factory()).unwrap_or_else( + |err| { + fatal!( + self.log, + "Failed to switch to checkpoint for height #{}: {}", + height, + err + ); + }, + ); } + let state = Arc::new(state); + self.tip_channel + .send(TipRequest::ValidateReplicatedState { + checkpoint_layout: cp_layout.clone(), + reference_state: Arc::clone(&state), + own_subnet_type: self.own_subnet_type, + fd_factory: self.fd_factory.clone(), + }) + .expect("Failed to send Validate request"); // On the NNS subnet we never allow incremental manifest computation let is_nns = self.own_subnet_id == state.metadata.network_topology.nns_subnet_id; @@ -2751,7 +2685,7 @@ impl StateManagerImpl { .with_label_values(&["create_checkpoint_result"]) .start_timer(); // With lsmt, we do not need the defrag. - // Without lsmt, the ResetTipAndMerge happens earlier in make_checkpoint. + // Without lsmt, the ResetTipAndMerge happens earlier in make_unvalidated_checkpoint. let tip_requests = if self.lsmt_status == FlagStatus::Enabled { vec![TipRequest::ResetTipAndMerge { checkpoint_layout: cp_layout.clone(), @@ -2767,7 +2701,7 @@ impl StateManagerImpl { CreateCheckpointResult { tip_requests, - state: Arc::new(state), + state, state_metadata: StateMetadata { checkpoint_layout: Some(cp_layout.clone()), bundled_manifest: None, @@ -3285,9 +3219,7 @@ impl StateManager for StateManagerImpl { .range_mut(Self::INITIAL_STATE_HEIGHT..certification_height) { if let Some(tree) = certification_metadata.hash_tree.take() { - self.deallocation_sender - .send(Box::new(tree)) - .expect("failed to send object to deallocation thread"); + self.deallocator_thread.send(Box::new(tree)); } } } diff --git a/rs/state_manager/src/manifest.rs b/rs/state_manager/src/manifest.rs index 4530c25e47b..bc53ef5786f 100644 --- a/rs/state_manager/src/manifest.rs +++ b/rs/state_manager/src/manifest.rs @@ -286,7 +286,7 @@ fn write_chunk_hash(hasher: &mut Sha256, chunk_info: &ChunkInfo, version: StateS /// Returns the number of chunks of size `max_chunk_size` required to cover a /// file of size `size_bytes`. fn count_chunks(size_bytes: u64, max_chunk_size: u32) -> usize { - (size_bytes as usize + max_chunk_size as usize - 1) / max_chunk_size as usize + (size_bytes as usize).div_ceil(max_chunk_size as usize) } /// Checks if the manifest was computed using specified max_chunk_size. diff --git a/rs/state_manager/src/manifest/tests/computation.rs b/rs/state_manager/src/manifest/tests/computation.rs index 53b7856a836..ab706b8c9fe 100644 --- a/rs/state_manager/src/manifest/tests/computation.rs +++ b/rs/state_manager/src/manifest/tests/computation.rs @@ -401,8 +401,7 @@ fn test_validate_sub_manifest() { let meta_manifest = build_meta_manifest(&manifest); let encoded_manifest = encode_manifest(&manifest); - let num = - (encoded_manifest.len() + DEFAULT_CHUNK_SIZE as usize - 1) / DEFAULT_CHUNK_SIZE as usize; + let num = encoded_manifest.len().div_ceil(DEFAULT_CHUNK_SIZE as usize); assert!( num > 1, "This test does not cover the case where the encoded manifest is divided into multiple pieces." diff --git a/rs/state_manager/src/split.rs b/rs/state_manager/src/split.rs index bc7d4d51d1b..d5119c323f2 100644 --- a/rs/state_manager/src/split.rs +++ b/rs/state_manager/src/split.rs @@ -1,7 +1,8 @@ //! Prunes a replicated state, as part of a subnet split. use crate::{ checkpoint::{ - load_checkpoint, make_checkpoint, validate_checkpoint_and_remove_unverified_marker, + load_checkpoint, make_unvalidated_checkpoint, + validate_checkpoint_and_remove_unverified_marker, }, flush_canister_snapshots_and_page_maps, tip::spawn_tip_thread, @@ -80,7 +81,7 @@ pub fn split( let (cp, state) = read_checkpoint( &state_layout, &mut thread_pool, - fd_factory.clone(), + Arc::clone(&fd_factory), &metrics, )?; @@ -100,8 +101,8 @@ pub fn split( state_layout, &cp, &mut thread_pool, - fd_factory, &config, + Arc::clone(&fd_factory), &metrics, log, ) @@ -179,8 +180,8 @@ fn write_checkpoint( state_layout: StateLayout, old_cp: &CheckpointLayout, thread_pool: &mut Pool, - fd_factory: Arc, config: &Config, + fd_factory: Arc, metrics: &StateManagerMetrics, log: ReplicaLogger, ) -> Result<(), String> { @@ -214,18 +215,23 @@ fn write_checkpoint( &metrics.checkpoint_metrics, ); - let (cp_layout, _state, _has_downgrade) = make_checkpoint( + let (cp_layout, _has_downgrade) = make_unvalidated_checkpoint( state, new_height, &tip_channel, &metrics.checkpoint_metrics, - thread_pool, - fd_factory, config.lsmt_config.lsmt_status, ) .map_err(|e| format!("Failed to write checkpoint: {}", e))?; - validate_checkpoint_and_remove_unverified_marker(&cp_layout, Some(thread_pool)) - .map_err(|e| format!("Failed to validate checkpoint: {}", e))?; + validate_checkpoint_and_remove_unverified_marker( + &cp_layout, + None, + SubnetType::Application, + fd_factory.clone(), + &metrics.checkpoint_metrics, + Some(thread_pool), + ) + .map_err(|e| format!("Failed to validate checkpoint: {}", e))?; Ok(()) } diff --git a/rs/state_manager/src/split/tests.rs b/rs/state_manager/src/split/tests.rs index fec85b551f5..f680be93cfc 100644 --- a/rs/state_manager/src/split/tests.rs +++ b/rs/state_manager/src/split/tests.rs @@ -1,6 +1,6 @@ use super::*; use crate::{ - checkpoint::make_checkpoint, + checkpoint::make_unvalidated_checkpoint, flush_canister_snapshots_and_page_maps, state_sync::types::{FileInfo, Manifest}, tip::spawn_tip_thread, @@ -204,8 +204,8 @@ fn read_write_roundtrip() { layout.clone(), &cp, &mut thread_pool, - fd_factory, &Config::new(root), + fd_factory.clone(), &metrics, log.clone(), ) @@ -454,17 +454,29 @@ fn new_state_layout(log: ReplicaLogger) -> (TempDir, Time) { ); let mut thread_pool = thread_pool(); - let (cp_layout, _state, _has_downgrade) = make_checkpoint( + let (cp_layout, _has_downgrade) = make_unvalidated_checkpoint( &state, HEIGHT, &tip_channel, &state_manager_metrics.checkpoint_metrics, - &mut thread_pool, - Arc::new(TestPageAllocatorFileDescriptorImpl::new()), lsmt_config_default().lsmt_status, ) - .unwrap_or_else(|err| panic!("Expected make_checkpoint to succeed, got {:?}", err)); - validate_checkpoint_and_remove_unverified_marker(&cp_layout, Some(&mut thread_pool)).unwrap(); + .unwrap_or_else(|err| { + panic!( + "Expected make_unvalidated_checkpoint to succeed, got {:?}", + err + ) + }); + let fd_factory = Arc::new(TestPageAllocatorFileDescriptorImpl::new()); + validate_checkpoint_and_remove_unverified_marker( + &cp_layout, + None, + SubnetType::Application, + fd_factory.clone(), + &state_manager_metrics.checkpoint_metrics, + Some(&mut thread_pool), + ) + .unwrap(); // Sanity checks. assert_eq!(layout.checkpoint_heights().unwrap(), vec![HEIGHT]); diff --git a/rs/state_manager/src/tip.rs b/rs/state_manager/src/tip.rs index 0ae8a522a1b..243fed18bce 100644 --- a/rs/state_manager/src/tip.rs +++ b/rs/state_manager/src/tip.rs @@ -16,6 +16,8 @@ use ic_protobuf::state::{ stats::v1::Stats, system_metadata::v1::{SplitFrom, SystemMetadata}, }; +use ic_registry_subnet_type::SubnetType; +use ic_replicated_state::page_map::PageAllocatorFileDescriptor; use ic_replicated_state::{ canister_snapshots::{CanisterSnapshot, SnapshotOperation}, page_map::{MergeCandidate, StorageMetrics, StorageResult, MAX_NUMBER_OF_FILES}, @@ -38,6 +40,7 @@ use rand::prelude::SliceRandom; use rand::{seq::IteratorRandom, Rng, SeedableRng}; use rand_chacha::ChaChaRng; use std::collections::BTreeSet; +use std::ops::Deref; use std::os::unix::prelude::MetadataExt; use std::path::PathBuf; use std::sync::{Arc, Mutex}; @@ -125,6 +128,9 @@ pub(crate) enum TipRequest { /// Crash if diverges. ValidateReplicatedState { checkpoint_layout: CheckpointLayout, + reference_state: Arc, + own_subnet_type: SubnetType, + fd_factory: Arc, }, /// Wait for the message to be executed and notify back via sender. /// State: * @@ -448,9 +454,19 @@ pub(crate) fn spawn_tip_thread( have_latest_manifest = true; } - TipRequest::ValidateReplicatedState { checkpoint_layout } => { + TipRequest::ValidateReplicatedState { + checkpoint_layout, + reference_state, + own_subnet_type, + fd_factory, + } => { + let _timer = request_timer(&metrics, "validate_replicated_state"); if let Err(err) = validate_checkpoint_and_remove_unverified_marker( &checkpoint_layout, + Some(reference_state.deref()), + own_subnet_type, + Arc::clone(&fd_factory), + &metrics.checkpoint_metrics, Some(&mut thread_pool), ) { fatal!( @@ -847,7 +863,7 @@ fn merge_to_base( merge_candidate.is_some() }); - return rewritten.iter().any(|b| *b); + rewritten.iter().any(|b| *b) } fn serialize_to_tip( @@ -982,7 +998,7 @@ fn serialize_canister_to_tip( metadata: execution_state.metadata.clone(), binary_hash: Some(execution_state.wasm_binary.binary.module_hash().into()), next_scheduled_method: execution_state.next_scheduled_method, - is_wasm64: execution_state.is_wasm64, + is_wasm64: execution_state.wasm_execution_mode.is_wasm64(), }) } None => { diff --git a/rs/state_manager/tests/state_manager.rs b/rs/state_manager/tests/state_manager.rs index 078e82a6d7f..c5b5e026b7c 100644 --- a/rs/state_manager/tests/state_manager.rs +++ b/rs/state_manager/tests/state_manager.rs @@ -46,7 +46,9 @@ use ic_sys::PAGE_SIZE; use ic_test_utilities_consensus::fake::FakeVerifier; use ic_test_utilities_io::{make_mutable, make_readonly, write_all_at}; use ic_test_utilities_logger::with_test_replica_logger; -use ic_test_utilities_metrics::{fetch_int_counter_vec, fetch_int_gauge, Labels}; +use ic_test_utilities_metrics::{ + fetch_histogram_vec_stats, fetch_int_counter_vec, fetch_int_gauge, Labels, +}; use ic_test_utilities_state::{arb_stream, arb_stream_slice, canister_ids}; use ic_test_utilities_tmpdir::tmpdir; use ic_test_utilities_types::{ @@ -1277,6 +1279,30 @@ fn missing_manifests_are_recomputed() { }); } +#[test] +fn validate_replicated_state_is_called() { + fn validate_was_called(metrics: &MetricsRegistry) -> bool { + let request_duration = fetch_histogram_vec_stats( + metrics, + "state_manager_tip_handler_request_duration_seconds", + ); + for (label, _stats) in request_duration.iter() { + if label.get("request") == Some(&"validate_replicated_state".to_string()) { + return true; + } + } + false + } + + state_manager_test(|metrics, state_manager| { + assert!(!validate_was_called(metrics)); + let (_, tip) = state_manager.take_tip(); + state_manager.commit_and_certify(tip, height(1), CertificationScope::Full, None); + state_manager.flush_tip_channel(); + assert!(validate_was_called(metrics)); + }); +} + fn any_manifest_was_incremental(metrics: &MetricsRegistry) -> bool { // We detect that the manifest computation was incremental by checking that at least some bytes // are either "reused" or "hashed_and_compared" @@ -6201,6 +6227,7 @@ fn can_merge_unexpected_number_of_files() { .vmemory_0(); let existing_overlays = pm_layout.existing_overlays().unwrap(); assert_eq!(existing_overlays.len(), NUM_PAGES); // single page per shard + state_manager.flush_tip_channel(); // Copy each shard for heights 1..HEIGHT; now each file is beyond the hard limit, // triggering forced merge for all shards back to one overlay. @@ -7519,8 +7546,12 @@ fn arbitrary_test_canister_op() -> impl Strategy { } proptest! { -// We go for fewer, but longer runs -#![proptest_config(ProptestConfig::with_cases(5))] +#![proptest_config(ProptestConfig { + // Fork to prevent flaky timeouts due to closed sandbox fds + fork: true, + // We go for fewer, but longer runs + ..ProptestConfig::with_cases(5) +})] #[test] fn random_canister_input_lsmt(ops in proptest::collection::vec(arbitrary_test_canister_op(), 1..50)) { diff --git a/rs/state_tool/BUILD.bazel b/rs/state_tool/BUILD.bazel index 6e2d7bb6d86..7e69bf77def 100644 --- a/rs/state_tool/BUILD.bazel +++ b/rs/state_tool/BUILD.bazel @@ -1,4 +1,5 @@ -load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library", "rust_test") +load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library") +load("//bazel:defs.bzl", "rust_ic_test") DEPENDENCIES = [ # Keep sorted. @@ -25,6 +26,7 @@ MACRO_DEPENDENCIES = [] DEV_DEPENDENCIES = [ # Keep sorted. + "//rs/state_machine_tests", "@crate_index//:tempfile", ] @@ -58,10 +60,18 @@ rust_binary( deps = DEPENDENCIES + [":state_tool_lib"], ) -rust_test( +rust_ic_test( name = "state_tool_test", aliases = ALIASES, crate = ":state_tool_lib", proc_macro_deps = MACRO_DEPENDENCIES + MACRO_DEV_DEPENDENCIES, deps = DEPENDENCIES + DEV_DEPENDENCIES, ) + +rust_ic_test( + name = "state_tool_integration_test", + srcs = ["src/main.rs"], + aliases = ALIASES, + proc_macro_deps = MACRO_DEPENDENCIES + MACRO_DEV_DEPENDENCIES, + deps = DEPENDENCIES + DEV_DEPENDENCIES + [":state_tool_lib"], +) diff --git a/rs/state_tool/Cargo.toml b/rs/state_tool/Cargo.toml index 3fd5cf6527b..0370906eb17 100644 --- a/rs/state_tool/Cargo.toml +++ b/rs/state_tool/Cargo.toml @@ -32,4 +32,5 @@ slog = { workspace = true } slog-term = { workspace = true } [dev-dependencies] +ic-state-machine-tests = { path = "../state_machine_tests" } tempfile = { workspace = true } diff --git a/rs/state_tool/src/commands.rs b/rs/state_tool/src/commands.rs index 85bfb45061d..5c69bb50fba 100644 --- a/rs/state_tool/src/commands.rs +++ b/rs/state_tool/src/commands.rs @@ -2,6 +2,7 @@ pub mod cdiff; pub mod chash; pub mod convert_ids; +pub mod copy; pub mod decode; pub mod import_state; pub mod list; diff --git a/rs/state_tool/src/commands/copy.rs b/rs/state_tool/src/commands/copy.rs new file mode 100644 index 00000000000..5adfda1592e --- /dev/null +++ b/rs/state_tool/src/commands/copy.rs @@ -0,0 +1,305 @@ +use ic_logger::no_op_logger; +use ic_metrics::MetricsRegistry; +use ic_protobuf::state::v1 as pb; +use ic_state_layout::StateLayout; +use ic_types::Height; +use prost::Message; +use std::io::{Read, Write}; +use std::path::{Path, PathBuf}; + +pub enum Heights { + All, + Latest, + Explicit(Vec<(Height, Option)>), +} + +/// Copy checkpoints from the state directory at `source` to the state directory at `destination`. +/// +/// If `heights` is `Heights::All`, all checkpoints from the source are copied. If it is `Heights::Latest` only the maximum height +/// from `src` is copied (no-op if there are no checkpoints). +/// Otherwise, only listed checkpoints are copied. +/// Optionally, if the heights are listed explicitly then the copied checkpoints can be given a different height in the destination. +/// +/// Apart from copying the checkpoints, the relevant entries are also copied from the `states_metadata.pbuf` file, containing the manifest. +pub fn do_copy(source: PathBuf, destination: PathBuf, heights: Heights) -> Result<(), String> { + let src_layout = StateLayout::new_no_init(no_op_logger(), source, &MetricsRegistry::new()); + let dst_layout = + StateLayout::try_new(no_op_logger(), destination, &MetricsRegistry::new()).unwrap(); + + let heights = match heights { + Heights::All => src_layout + .checkpoint_heights() + .unwrap() + .into_iter() + .map(|h| (h, None)) + .collect(), + Heights::Latest => src_layout + .checkpoint_heights() + .unwrap() + .last() + .map(|h| vec![(*h, None)]) + .unwrap_or_default(), + Heights::Explicit(heights) => heights, + }; + + // Replace (h, None) with (h, h) and (h, Some(x)) with (h, x) + let heights = heights + .into_iter() + .map(|(src, dst)| { + let dst = dst.unwrap_or(src); + (src, dst) + }) + .collect(); + + do_copy_with_state_layouts(&src_layout, &dst_layout, heights) +} + +fn do_copy_with_state_layouts( + src_layout: &StateLayout, + dst_layout: &StateLayout, + heights: Vec<(Height, Height)>, +) -> Result<(), String> { + if heights.is_empty() { + return Ok(()); + } + + // Check if all checkpoints exist at the source and none exist at the destination. + // We do this before copying anything to avoid partially copying checkpoints. + for (src_height, dst_height) in heights.iter() { + if let Ok(cp_layout) = dst_layout.checkpoint_verified(*dst_height) { + return Err(format!( + "Checkpoint {} already exists at {}", + dst_height, + cp_layout.raw_path().display() + )); + } + + if src_layout.checkpoint_verified(*src_height).is_err() { + return Err(format!("Checkpoint {} does not exist at src", src_height)); + } + } + + let src_metadata = load_metadata_proto(&src_layout.states_metadata()) + .map_err(|e| format!("Failed to read metadata: {}", e))?; + let mut dst_metadata = load_metadata_proto(&dst_layout.states_metadata()) + .map_err(|e| format!("Failed to read metadata: {}", e))?; + + for (src_height, dst_height) in heights { + dst_layout + .copy_and_sync_checkpoint( + &format!("import_{}", dst_height), + &src_layout + .checkpoints() + .join(StateLayout::checkpoint_name(src_height)), + &dst_layout + .checkpoints() + .join(StateLayout::checkpoint_name(dst_height)), + None, + ) + .map_err(|e| format!("Failed to copy checkpoint. Not all states might have been copied and some metadata might be missing: {}", e))?; + + if let Some(src_metadata_entry) = src_metadata.by_height.get(&src_height.get()) { + dst_metadata + .by_height + .insert(dst_height.get(), src_metadata_entry.clone()); + } + } + + write_metadata_proto(&dst_layout.states_metadata(), &dst_metadata).map_err(|e| { + format!( + "Failed to write metadata. Metadata might be missing or corrupted in destination: {}", + e + ) + })?; + + Ok(()) +} + +fn write_metadata_proto(path: &Path, metadata: &pb::StatesMetadata) -> Result<(), std::io::Error> { + let mut w = std::fs::File::create(path)?; + let mut buf = Vec::new(); + metadata.encode(&mut buf)?; + w.write_all(&buf[..])?; + Ok(()) +} + +fn load_metadata_proto(path: &Path) -> Result { + if path.exists() { + let mut file = std::fs::File::open(path)?; + let mut buf = Vec::new(); + file.read_to_end(&mut buf)?; + Ok(pb::StatesMetadata::decode(&buf[..]).unwrap_or_default()) + } else { + Ok(pb::StatesMetadata::default()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ic_state_machine_tests::StateMachineBuilder; + use tempfile::TempDir; + + #[test] + fn copy_test() { + let env = StateMachineBuilder::new().build(); + env.checkpointed_tick(); + env.state_manager.flush_tip_channel(); + + let tmp_dir = TempDir::new().unwrap(); + let dst_layout = StateLayout::try_new( + no_op_logger(), + tmp_dir.path().to_path_buf(), + &MetricsRegistry::new(), + ) + .unwrap(); + + assert!(dst_layout.checkpoint_heights().unwrap().is_empty()); + assert!(load_metadata_proto(&dst_layout.states_metadata()) + .unwrap() + .by_height + .is_empty()); + + do_copy_with_state_layouts( + env.state_manager.state_layout(), + &dst_layout, + vec![(Height::new(1), Height::new(1))], + ) + .unwrap(); + + assert_eq!( + dst_layout.checkpoint_heights().unwrap(), + vec![Height::new(1)] + ); + assert_eq!( + load_metadata_proto(&dst_layout.states_metadata()) + .unwrap() + .by_height + .len(), + 1 + ); + assert!(load_metadata_proto(&dst_layout.states_metadata()) + .unwrap() + .by_height[&1] + .manifest + .is_some()); + + env.checkpointed_tick(); + env.checkpointed_tick(); + env.state_manager.flush_tip_channel(); + + do_copy_with_state_layouts( + env.state_manager.state_layout(), + &dst_layout, + vec![(Height::new(3), Height::new(3))], + ) + .unwrap(); + + assert_eq!( + dst_layout.checkpoint_heights().unwrap(), + vec![Height::new(1), Height::new(3)] + ); + } + + #[test] + fn multiple_copy_test() { + let env = StateMachineBuilder::new() + .with_remove_old_states(false) + .build(); + env.checkpointed_tick(); + env.checkpointed_tick(); + env.checkpointed_tick(); + env.state_manager.flush_tip_channel(); + + let tmp_dir = TempDir::new().unwrap(); + let dst_layout = StateLayout::try_new( + no_op_logger(), + tmp_dir.path().to_path_buf(), + &MetricsRegistry::new(), + ) + .unwrap(); + + assert!(dst_layout.checkpoint_heights().unwrap().is_empty()); + assert!(load_metadata_proto(&dst_layout.states_metadata()) + .unwrap() + .by_height + .is_empty()); + + do_copy_with_state_layouts( + env.state_manager.state_layout(), + &dst_layout, + vec![ + (Height::new(1), Height::new(1)), + (Height::new(3), Height::new(3)), + ], + ) + .unwrap(); + + assert_eq!( + dst_layout.checkpoint_heights().unwrap(), + vec![Height::new(1), Height::new(3)] + ); + assert_eq!( + load_metadata_proto(&dst_layout.states_metadata()) + .unwrap() + .by_height + .len(), + 2 + ); + assert!(load_metadata_proto(&dst_layout.states_metadata()) + .unwrap() + .by_height[&1] + .manifest + .is_some()); + assert!(load_metadata_proto(&dst_layout.states_metadata()) + .unwrap() + .by_height[&3] + .manifest + .is_some()); + } + + #[test] + fn rename_copy_test() { + let env = StateMachineBuilder::new().build(); + env.checkpointed_tick(); + env.state_manager.flush_tip_channel(); + + let tmp_dir = TempDir::new().unwrap(); + let dst_layout = StateLayout::try_new( + no_op_logger(), + tmp_dir.path().to_path_buf(), + &MetricsRegistry::new(), + ) + .unwrap(); + + assert!(dst_layout.checkpoint_heights().unwrap().is_empty()); + assert!(load_metadata_proto(&dst_layout.states_metadata()) + .unwrap() + .by_height + .is_empty()); + + do_copy_with_state_layouts( + env.state_manager.state_layout(), + &dst_layout, + vec![(Height::new(1), Height::new(4))], + ) + .unwrap(); + + assert_eq!( + dst_layout.checkpoint_heights().unwrap(), + vec![Height::new(4)] + ); + assert_eq!( + load_metadata_proto(&dst_layout.states_metadata()) + .unwrap() + .by_height + .len(), + 1 + ); + assert!(load_metadata_proto(&dst_layout.states_metadata()) + .unwrap() + .by_height[&4] + .manifest + .is_some()); + } +} diff --git a/rs/state_tool/src/commands/import_state.rs b/rs/state_tool/src/commands/import_state.rs index a5befcb0886..a52f67e3ea1 100644 --- a/rs/state_tool/src/commands/import_state.rs +++ b/rs/state_tool/src/commands/import_state.rs @@ -1,71 +1,9 @@ //! Imports replicated state from an external location. use crate::commands::utils; -use ic_state_layout::{CheckpointLayout, RwPolicy}; -use ic_sys::fs::{clone_file, copy_file_sparse}; +use ic_state_layout::StateLayout; use ic_types::Height; -use std::fs; -use std::path::{Path, PathBuf}; -use std::string::ToString; - -/// Copies SRC into DST recursively. -/// -/// Function is not crash-safe. Caller is responsible to follow guidelines -/// regarding crash-safe I/O. -fn copy_recursively(src: &Path, dst: &Path) -> Result<(), String> { - enum CanCloneFiles { - Yes, - No, - } - fn go(src: &Path, dst: &Path, can_clone: &mut CanCloneFiles) -> Result<(), String> { - let src_metadata = src - .metadata() - .map_err(|e| format!("failed to get metadata of path {}: {}", src.display(), e))?; - - if src_metadata.is_dir() { - let entries = src - .read_dir() - .map_err(|e| format!("failed to read directory {}: {}", src.display(), e))?; - - fs::create_dir_all(dst) - .map_err(|e| format!("failed to create directory {}: {}", dst.display(), e))?; - - for entry_result in entries { - let entry = entry_result.map_err(|e| { - format!("failed to read entry of directory {}: {}", src.display(), e) - })?; - let dst_entry = dst.join(entry.file_name()); - - go(&entry.path(), &dst_entry, can_clone)?; - } - } else { - if let CanCloneFiles::Yes = can_clone { - match clone_file(src, dst) { - Ok(_) => return Ok(()), - Err(_) => { - *can_clone = CanCloneFiles::No; - } - } - } - - copy_file_sparse(src, dst).map_err(|e| { - format!( - "Failed to copy {} -> {}: {}", - src.display(), - dst.display(), - e - ) - })?; - } - - Ok(()) - } - // We try to clone files first because it's much faster for big files. - // If cloning fails (most likely, because SRC and DST are on different file - // systems), we fall back to usual copying. - let mut can_clone = CanCloneFiles::Yes; - go(src, dst, &mut can_clone) -} +use std::path::PathBuf; /// Imports a checkpoint of replicated state into the replica state directory. /// @@ -83,24 +21,16 @@ pub fn do_import(state_path: PathBuf, config_path: PathBuf, height: u64) -> Resu )); } - let scratchpad_dir = state_layout - .state_sync_scratchpad(height) - .map_err(|e| format!("Failed to get a scratchpad directory: {}", e))?; - - copy_recursively(&state_path, &scratchpad_dir)?; - - let cp_layout = CheckpointLayout::>::new_untracked(scratchpad_dir, height) - .map_err(|e| format!("Failed to create scratchpad checkpoint layout: {}", e))?; - state_layout - .scratchpad_to_checkpoint(cp_layout, height, None) - .map_err(|e| e.to_string())?; - - println!( - "Successfully created checkpoint {} in state root {}", - height, - state_layout.raw_path().display() - ); + .copy_and_sync_checkpoint( + &format!("import_{}", height), + &state_path, + &state_layout + .checkpoints() + .join(StateLayout::checkpoint_name(height)), + None, + ) + .map_err(|e| format!("Failed to import checkpoint: {}", e))?; Ok(()) } diff --git a/rs/state_tool/src/main.rs b/rs/state_tool/src/main.rs index d0b0d69774d..0529654deea 100644 --- a/rs/state_tool/src/main.rs +++ b/rs/state_tool/src/main.rs @@ -8,8 +8,8 @@ use clap::Parser; use ic_registry_routing_table::CanisterIdRange; use ic_registry_subnet_type::SubnetType; use ic_state_tool::commands; -use ic_types::{PrincipalId, Time}; -use std::path::PathBuf; +use ic_types::{Height, PrincipalId, Time}; +use std::{error::Error, path::PathBuf}; /// Supported `state_tool` commands and their arguments. #[derive(Debug, Parser)] @@ -28,6 +28,7 @@ enum Opt { }, /// Imports replicated state from an external location. + /// Deprecated: use `copy` instead. #[clap(name = "import")] ImportState { /// Path to the state to import. @@ -43,6 +44,18 @@ enum Opt { height: u64, }, + /// Copies states from one ic_state directory to another including their metadata. + #[clap(name = "copy")] + CopyStates { + /// Path to the source ic_state directory. + source: PathBuf, + /// Path to the destination ic_state directory. + destination: PathBuf, + /// Heights to copy. + #[command(flatten)] + heights: HeightsArgs, + }, + /// Computes manifest of a checkpoint. #[clap(name = "manifest")] Manifest { @@ -149,8 +162,61 @@ enum Opt { }, } +/// Command line arguments for the `copy` command with eith +#[derive(Debug, Clone, clap::Args)] +#[group(multiple = false)] +struct HeightsArgs { + /// Copy the latest state only, or none if there are no states in the source. + /// + /// Mutually exclusive with `--heights`. If neither is specified, all heights are copied. + #[clap(long = "latest")] + latest: bool, + /// List of heights to copy. + /// + /// Heights can be specified as a comma separated list of heights. Optionally, a state can be renamed by specifiying the source and destination height separated by '->'. + /// + /// Examples: + /// - `--heights 1,2,3` copies states at heights 1, 2, and 3. + /// - `--heights 1->2` copies the state at height 1 and renames it to height 2. + /// + /// Mutually exclusive with `--latest`. If neither is specified, all heights are copied. + #[clap(long = "heights", value_parser = parse_height_pair, value_delimiter = ',', verbatim_doc_comment)] + heights: Option)>>, +} + +impl From for commands::copy::Heights { + fn from(val: HeightsArgs) -> Self { + if val.latest { + Self::Latest + } else if val.heights.is_some() { + Self::Explicit(val.heights.unwrap().into_iter().collect()) + } else { + Self::All + } + } +} + +/// Parser for either a single height or a pair of heights separated by '->'. +/// Used to parse the `--heights` argument of the `copy` command. +fn parse_height_pair( + s: &str, +) -> Result<(Height, Option), Box> { + match s.find("->") { + Some(pos) => Ok(( + Height::new(s[..pos].parse()?), + Some(Height::new(s[pos + 2..].parse()?)), + )), + None => Ok((Height::new(s.parse()?), None)), + } +} + fn main() { - let opt = Parser::parse(); + let args = std::env::args().collect(); + main_inner(args); +} + +pub(crate) fn main_inner(args: Vec) { + let opt = Parser::parse_from(args); let result = match opt { Opt::CDiff { path_a, path_b } => commands::cdiff::do_diff(path_a, path_b), Opt::CHash { path } => commands::chash::do_hash(path), @@ -159,6 +225,11 @@ fn main() { config, height, } => commands::import_state::do_import(state, config, height), + Opt::CopyStates { + source, + destination, + heights, + } => commands::copy::do_copy(source, destination, heights.into()), Opt::Manifest { path } => commands::manifest::do_compute_manifest(path), Opt::VerifyManifest { file } => commands::verify_manifest::do_verify_manifest(&file), Opt::ListStates { config } => commands::list::do_list(config), @@ -207,3 +278,154 @@ fn main() { std::process::exit(1); } } + +#[cfg(test)] +mod tests { + use super::*; + use ic_logger::no_op_logger; + use ic_metrics::MetricsRegistry; + use ic_state_layout::StateLayout; + use ic_state_machine_tests::StateMachineBuilder; + use tempfile::TempDir; + + #[test] + fn copy_command_line_test() { + let env = StateMachineBuilder::new().build(); + env.checkpointed_tick(); + env.state_manager.flush_tip_channel(); + + let dst_dir = TempDir::new().unwrap(); + + main_inner(vec![ + "state-tool".to_string(), + "copy".to_string(), + env.state_manager + .state_layout() + .raw_path() + .display() + .to_string(), + dst_dir.path().display().to_string(), + ]); + + let dst_layout = StateLayout::try_new( + no_op_logger(), + dst_dir.path().to_path_buf(), + &MetricsRegistry::new(), + ) + .unwrap(); + + assert_eq!( + dst_layout.checkpoint_heights().unwrap(), + vec![Height::new(1)] + ); + } + + #[test] + fn copy_command_line_latest_test() { + let env = StateMachineBuilder::new() + .with_remove_old_states(false) + .build(); + env.checkpointed_tick(); + env.checkpointed_tick(); + env.checkpointed_tick(); + env.state_manager.flush_tip_channel(); + + let dst_dir = TempDir::new().unwrap(); + + main_inner(vec![ + "state-tool".to_string(), + "copy".to_string(), + env.state_manager + .state_layout() + .raw_path() + .display() + .to_string(), + dst_dir.path().display().to_string(), + "--latest".to_string(), + ]); + + let dst_layout = StateLayout::try_new( + no_op_logger(), + dst_dir.path().to_path_buf(), + &MetricsRegistry::new(), + ) + .unwrap(); + + assert_eq!( + dst_layout.checkpoint_heights().unwrap(), + vec![Height::new(3)] + ); + } + + #[test] + fn copy_command_line_rename_test() { + let env = StateMachineBuilder::new().build(); + env.checkpointed_tick(); + env.state_manager.flush_tip_channel(); + + let dst_dir = TempDir::new().unwrap(); + + main_inner(vec![ + "state-tool".to_string(), + "copy".to_string(), + env.state_manager + .state_layout() + .raw_path() + .display() + .to_string(), + dst_dir.path().display().to_string(), + "--heights".to_string(), + "1->2".to_string(), + ]); + + let dst_layout = StateLayout::try_new( + no_op_logger(), + dst_dir.path().to_path_buf(), + &MetricsRegistry::new(), + ) + .unwrap(); + + assert_eq!( + dst_layout.checkpoint_heights().unwrap(), + vec![Height::new(2)] + ); + } + + #[test] + fn copy_command_line_filter_test() { + let env = StateMachineBuilder::new() + .with_remove_old_states(false) + .build(); + env.checkpointed_tick(); + env.checkpointed_tick(); + env.checkpointed_tick(); + env.state_manager.flush_tip_channel(); + + let dst_dir = TempDir::new().unwrap(); + + main_inner(vec![ + "state-tool".to_string(), + "copy".to_string(), + env.state_manager + .state_layout() + .raw_path() + .display() + .to_string(), + dst_dir.path().display().to_string(), + "--heights".to_string(), + "1,3".to_string(), + ]); + + let dst_layout = StateLayout::try_new( + no_op_logger(), + dst_dir.path().to_path_buf(), + &MetricsRegistry::new(), + ) + .unwrap(); + + assert_eq!( + dst_layout.checkpoint_heights().unwrap(), + vec![Height::new(1), Height::new(3)] + ); + } +} diff --git a/rs/system_api/src/lib.rs b/rs/system_api/src/lib.rs index 4ba99460078..a85e90e829c 100644 --- a/rs/system_api/src/lib.rs +++ b/rs/system_api/src/lib.rs @@ -27,15 +27,19 @@ use ic_types::{ use ic_utils::deterministic_operations::deterministic_copy_from_slice; use ic_wasm_types::doc_ref; use request_in_prep::{into_request, RequestInPrep}; -use sandbox_safe_system_state::{CanisterStatusView, SandboxSafeSystemState, SystemStateChanges}; +use sandbox_safe_system_state::{ + CanisterStatusView, SandboxSafeSystemState, SystemStateModifications, +}; use serde::{Deserialize, Serialize}; use stable_memory::StableMemory; use std::{ + collections::BTreeMap, convert::{From, TryFrom}, rc::Rc, }; pub mod cycles_balance_change; +use cycles_balance_change::CyclesBalanceChange; mod request_in_prep; mod routing; pub mod sandbox_safe_system_state; @@ -266,6 +270,7 @@ pub enum ApiType { #[serde(with = "serde_bytes")] incoming_payload: Vec, caller: PrincipalId, + call_context_id: CallContextId, #[serde(with = "serde_bytes")] response_data: Vec, response_status: ResponseStatus, @@ -422,11 +427,17 @@ impl ApiType { } } - pub fn replicated_query(time: Time, incoming_payload: Vec, caller: PrincipalId) -> Self { + pub fn replicated_query( + time: Time, + incoming_payload: Vec, + caller: PrincipalId, + call_context_id: CallContextId, + ) -> Self { Self::ReplicatedQuery { time, incoming_payload, caller, + call_context_id, response_data: vec![], response_status: ResponseStatus::NotRepliedYet, max_reply_size: MAX_INTER_CANISTER_PAYLOAD_IN_BYTES, @@ -582,7 +593,6 @@ impl ApiType { | ApiType::PreUpgrade { .. } | ApiType::Cleanup { .. } | ApiType::InspectMessage { .. } - | ApiType::ReplicatedQuery { .. } | ApiType::NonReplicatedQuery { query_kind: NonReplicatedQueryKind::Pure, .. @@ -590,6 +600,9 @@ impl ApiType { ApiType::Update { call_context_id, .. } + | ApiType::ReplicatedQuery { + call_context_id, .. + } | ApiType::NonReplicatedQuery { query_kind: NonReplicatedQueryKind::Stateful { @@ -873,7 +886,7 @@ impl MemoryUsage { self.add_execution_memory(execution_bytes, execution_memory_type)?; - sandbox_safe_system_state.check_on_low_wasm_memory_hook_condition( + sandbox_safe_system_state.update_status_of_low_wasm_memory_hook_condition( None, self.wasm_memory_limit, self.current_usage, @@ -900,7 +913,7 @@ impl MemoryUsage { self.current_usage = NumBytes::from(new_usage); self.add_execution_memory(execution_bytes, execution_memory_type)?; - sandbox_safe_system_state.check_on_low_wasm_memory_hook_condition( + sandbox_safe_system_state.update_status_of_low_wasm_memory_hook_condition( Some(reserved_bytes), self.wasm_memory_limit, self.current_usage, @@ -1399,11 +1412,11 @@ impl SystemApiImpl { | ApiType::Init { .. } | ApiType::SystemTask { .. } | ApiType::Cleanup { .. } - | ApiType::ReplicatedQuery { .. } | ApiType::PreUpgrade { .. } | ApiType::NonReplicatedQuery { .. } | ApiType::InspectMessage { .. } => Err(self.error_for(method_name)), ApiType::Update { .. } + | ApiType::ReplicatedQuery { .. } | ApiType::ReplyCallback { .. } | ApiType::RejectCallback { .. } => { if self.execution_parameters.execution_mode == ExecutionMode::NonReplicated { @@ -1464,10 +1477,10 @@ impl SystemApiImpl { | ApiType::SystemTask { .. } | ApiType::PreUpgrade { .. } | ApiType::Cleanup { .. } - | ApiType::ReplicatedQuery { .. } | ApiType::NonReplicatedQuery { .. } | ApiType::InspectMessage { .. } => Err(self.error_for(method_name)), ApiType::Update { .. } + | ApiType::ReplicatedQuery { .. } | ApiType::ReplyCallback { .. } | ApiType::RejectCallback { .. } => { if self.execution_parameters.execution_mode == ExecutionMode::NonReplicated { @@ -1481,12 +1494,60 @@ impl SystemApiImpl { } } - pub fn into_system_state_changes(self) -> SystemStateChanges { - self.sandbox_safe_system_state.system_state_changes - } - - pub fn take_system_state_changes(&mut self) -> SystemStateChanges { - self.sandbox_safe_system_state.take_changes() + pub fn take_system_state_modifications(&mut self) -> SystemStateModifications { + let system_state_modifications = self.sandbox_safe_system_state.take_changes(); + match self.api_type { + // List all fields of `SystemStateModifications` so that + // there's an explicit decision that needs to be made + // for each context when a new field is added. + ApiType::InspectMessage { .. } => SystemStateModifications { + new_certified_data: None, + callback_updates: vec![], + cycles_balance_change: CyclesBalanceChange::zero(), + reserved_cycles: Cycles::zero(), + consumed_cycles_by_use_case: BTreeMap::new(), + call_context_balance_taken: None, + request_slots_used: BTreeMap::new(), + requests: vec![], + new_global_timer: None, + canister_log: Default::default(), + on_low_wasm_memory_hook_condition_check_result: None, + }, + ApiType::NonReplicatedQuery { .. } => SystemStateModifications { + new_certified_data: None, + callback_updates: system_state_modifications.callback_updates, + cycles_balance_change: CyclesBalanceChange::zero(), + reserved_cycles: Cycles::zero(), + consumed_cycles_by_use_case: BTreeMap::new(), + call_context_balance_taken: None, + request_slots_used: system_state_modifications.request_slots_used, + requests: system_state_modifications.requests, + new_global_timer: None, + canister_log: Default::default(), + on_low_wasm_memory_hook_condition_check_result: None, + }, + ApiType::ReplicatedQuery { .. } => SystemStateModifications { + new_certified_data: None, + callback_updates: vec![], + cycles_balance_change: system_state_modifications.cycles_balance_change, + reserved_cycles: Cycles::zero(), + consumed_cycles_by_use_case: system_state_modifications.consumed_cycles_by_use_case, + call_context_balance_taken: system_state_modifications.call_context_balance_taken, + request_slots_used: BTreeMap::new(), + requests: vec![], + new_global_timer: None, + canister_log: system_state_modifications.canister_log, + on_low_wasm_memory_hook_condition_check_result: None, + }, + ApiType::Start { .. } + | ApiType::Init { .. } + | ApiType::SystemTask { .. } + | ApiType::PreUpgrade { .. } + | ApiType::Update { .. } + | ApiType::Cleanup { .. } + | ApiType::ReplyCallback { .. } + | ApiType::RejectCallback { .. } => system_state_modifications, + } } pub fn stable_memory_size(&self) -> NumWasmPages { @@ -1639,12 +1700,7 @@ impl SystemApi for SystemApiImpl { } fn get_num_instructions_from_bytes(&self, num_bytes: NumBytes) -> NumInstructions { - match self.sandbox_safe_system_state.subnet_type { - SubnetType::System => NumInstructions::from(0), - SubnetType::VerifiedApplication | SubnetType::Application => { - NumInstructions::from(num_bytes.get()) - } - } + NumInstructions::from(num_bytes.get()) } fn stable_memory_dirty_pages(&self) -> Vec<(PageIndex, &PageBytes)> { @@ -3131,7 +3187,7 @@ impl SystemApi for SystemApiImpl { // Update the certified data. self.sandbox_safe_system_state - .system_state_changes + .system_state_modifications .new_certified_data = Some(heap[src..src + size].to_vec()); Ok(()) } @@ -3401,15 +3457,18 @@ impl SystemApi for SystemApiImpl { let result = match &self.api_type { ApiType::Start { .. } | ApiType::Init { .. } - | ApiType::ReplyCallback { .. } - | ApiType::RejectCallback { .. } | ApiType::Cleanup { .. } | ApiType::PreUpgrade { .. } - | ApiType::InspectMessage { .. } | ApiType::Update { .. } | ApiType::SystemTask { .. } | ApiType::ReplicatedQuery { .. } => Ok(1), - ApiType::NonReplicatedQuery { .. } => Ok(0), + ApiType::ReplyCallback { .. } | ApiType::RejectCallback { .. } => { + match self.execution_parameters.execution_mode { + ExecutionMode::NonReplicated => Ok(0), + ExecutionMode::Replicated => Ok(1), + } + } + ApiType::InspectMessage { .. } | ApiType::NonReplicatedQuery { .. } => Ok(0), }; trace_syscall!(self, ic0_in_replicated_execution, result); result @@ -3424,10 +3483,10 @@ impl SystemApi for SystemApiImpl { let method_name = "ic0_cycles_burn128"; let result = match self.api_type { ApiType::Start { .. } - | ApiType::ReplicatedQuery { .. } | ApiType::NonReplicatedQuery { .. } | ApiType::InspectMessage { .. } => Err(self.error_for(method_name)), ApiType::Init { .. } + | ApiType::ReplicatedQuery { .. } | ApiType::PreUpgrade { .. } | ApiType::Cleanup { .. } | ApiType::Update { .. } @@ -3452,6 +3511,68 @@ impl SystemApi for SystemApiImpl { trace_syscall!(self, CyclesBurn128, result, amount); result } + + fn ic0_subnet_self_size(&self) -> HypervisorResult { + let result = match &self.api_type { + ApiType::Start { .. } => Err(self.error_for("ic0_subnet_self_size")), + ApiType::Init { .. } + | ApiType::SystemTask { .. } + | ApiType::Cleanup { .. } + | ApiType::Update { .. } + | ApiType::ReplicatedQuery { .. } + | ApiType::NonReplicatedQuery { .. } + | ApiType::ReplyCallback { .. } + | ApiType::RejectCallback { .. } + | ApiType::PreUpgrade { .. } + | ApiType::InspectMessage { .. } => { + let subnet_id = self.sandbox_safe_system_state.get_subnet_id(); + Ok(subnet_id.get_ref().as_slice().len()) + } + }; + + trace_syscall!(self, SubnetSelfSize, result); + result + } + + fn ic0_subnet_self_copy( + &self, + dst: usize, + offset: usize, + size: usize, + heap: &mut [u8], + ) -> HypervisorResult<()> { + let result = match &self.api_type { + ApiType::Start { .. } => Err(self.error_for("ic0.subnet_self_copy")), + ApiType::Init { .. } + | ApiType::SystemTask { .. } + | ApiType::Cleanup { .. } + | ApiType::Update { .. } + | ApiType::ReplicatedQuery { .. } + | ApiType::NonReplicatedQuery { .. } + | ApiType::PreUpgrade { .. } + | ApiType::ReplyCallback { .. } + | ApiType::RejectCallback { .. } + | ApiType::InspectMessage { .. } => { + valid_subslice("ic0.subnet_self_copy heap", dst, size, heap)?; + let subnet_id = self.sandbox_safe_system_state.get_subnet_id(); + let id_bytes = subnet_id.get_ref().as_slice(); + let slice = valid_subslice("ic0.subnet_self_copy id", offset, size, id_bytes)?; + deterministic_copy_from_slice(&mut heap[dst..dst + size], slice); + + Ok(()) + } + }; + trace_syscall!( + self, + SubnetSelfCopy, + dst, + offset, + size, + summarize(heap, dst, size) + ); + + result + } } /// The default implementation of the `OutOfInstructionHandler` trait. diff --git a/rs/system_api/src/sandbox_safe_system_state.rs b/rs/system_api/src/sandbox_safe_system_state.rs index 19dd37d7015..3a4d87a7ea7 100644 --- a/rs/system_api/src/sandbox_safe_system_state.rs +++ b/rs/system_api/src/sandbox_safe_system_state.rs @@ -16,9 +16,13 @@ use ic_management_canister_types::{ }; use ic_nns_constants::CYCLES_MINTING_CANISTER_ID; use ic_registry_subnet_type::SubnetType; -use ic_replicated_state::canister_state::system_state::CyclesUseCase; -use ic_replicated_state::canister_state::DEFAULT_QUEUE_CAPACITY; -use ic_replicated_state::{CallOrigin, ExecutionTask, NetworkTopology, SystemState}; +use ic_replicated_state::canister_state::system_state::{ + is_low_wasm_memory_hook_condition_satisfied, CyclesUseCase, +}; +use ic_replicated_state::{ + canister_state::execution_state::WasmExecutionMode, canister_state::DEFAULT_QUEUE_CAPACITY, + CallOrigin, ExecutionTask, NetworkTopology, SystemState, +}; use ic_types::{ messages::{CallContextId, CallbackId, RejectContext, Request, RequestMetadata, NO_DEADLINE}, methods::Callback, @@ -57,25 +61,25 @@ pub enum CallbackUpdate { /// Tracks changes to the system state that the canister has requested. #[derive(Clone, PartialEq, Debug, Deserialize, Serialize)] -pub struct SystemStateChanges { +pub struct SystemStateModifications { pub(super) new_certified_data: Option>, // pub for testing pub callback_updates: Vec, - cycles_balance_change: CyclesBalanceChange, + pub(super) cycles_balance_change: CyclesBalanceChange, // The cycles that move from the main balance to the reserved balance. // Invariant: `cycles_balance_change` contains // `CyclesBalanceChange::Removed(reserved_cycles)`. - reserved_cycles: Cycles, - consumed_cycles_by_use_case: BTreeMap, - call_context_balance_taken: Option<(CallContextId, Cycles)>, - request_slots_used: BTreeMap, - requests: Vec, + pub(super) reserved_cycles: Cycles, + pub(super) consumed_cycles_by_use_case: BTreeMap, + pub(super) call_context_balance_taken: Option<(CallContextId, Cycles)>, + pub(super) request_slots_used: BTreeMap, + pub(super) requests: Vec, pub(super) new_global_timer: Option, - canister_log: CanisterLog, + pub(super) canister_log: CanisterLog, pub on_low_wasm_memory_hook_condition_check_result: Option, } -impl Default for SystemStateChanges { +impl Default for SystemStateModifications { fn default() -> Self { Self { new_certified_data: None, @@ -93,7 +97,7 @@ impl Default for SystemStateChanges { } } -impl SystemStateChanges { +impl SystemStateModifications { /// Checks that no cycles were created during the execution of this message /// (unless the canister is the cycles minting canister). fn validate_cycle_change(&self, is_cmc_canister: bool) -> HypervisorResult<()> { @@ -187,7 +191,7 @@ impl SystemStateChanges { err ); - let reject_context = RejectContext::new(RejectCode::CanisterError, err.to_string()); + let reject_context = RejectContext::new(err.code().into(), err.to_string()); system_state .reject_subnet_output_request(msg, reject_context, subnet_ids) .map_err(|e| Self::error(format!("Failed to push IC00 reject response: {:?}", e)))?; @@ -543,8 +547,8 @@ impl SystemStateChanges { fn default_with_cycles_changes( cycles_balance_change: CyclesBalanceChange, consumed_cycles_by_use_case: BTreeMap, - ) -> SystemStateChanges { - SystemStateChanges { + ) -> SystemStateModifications { + SystemStateModifications { cycles_balance_change, consumed_cycles_by_use_case, ..Default::default() @@ -559,7 +563,7 @@ impl SystemStateChanges { pub struct SandboxSafeSystemState { /// Only public for tests #[doc(hidden)] - pub system_state_changes: SystemStateChanges, + pub system_state_modifications: SystemStateModifications, pub(super) canister_id: CanisterId, pub(super) status: CanisterStatusView, pub(super) subnet_type: SubnetType, @@ -636,12 +640,12 @@ impl SandboxSafeSystemState { memory_allocation, wasm_memory_threshold, compute_allocation, - system_state_changes: SystemStateChanges { + system_state_modifications: SystemStateModifications { // Start indexing new batch of canister log records from the given index. canister_log: CanisterLog::new_with_next_index(next_canister_log_record_idx), call_context_balance_taken: call_context_id .map(|call_context_id| (call_context_id, Cycles::zero())), - ..SystemStateChanges::default() + ..SystemStateModifications::default() }, initial_cycles_balance, initial_reserved_balance, @@ -791,12 +795,12 @@ impl SandboxSafeSystemState { pub fn set_global_timer(&mut self, timer: CanisterTimer) { // Update both sandbox global timer and the changes. - self.system_state_changes.new_global_timer = Some(timer); + self.system_state_modifications.new_global_timer = Some(timer); self.global_timer = timer; } - pub fn take_changes(&mut self) -> SystemStateChanges { - std::mem::take(&mut self.system_state_changes) + pub fn take_changes(&mut self) -> SystemStateModifications { + std::mem::take(&mut self.system_state_modifications) } /// Only public for use in tests. @@ -806,7 +810,7 @@ impl SandboxSafeSystemState { Some(next_callback_id) => { *next_callback_id += 1; let id = CallbackId::from(*next_callback_id); - self.system_state_changes + self.system_state_modifications .callback_updates .push(CallbackUpdate::Register(id, callback)); Ok(id) @@ -821,7 +825,7 @@ impl SandboxSafeSystemState { /// Only public for use in tests. #[doc(hidden)] pub fn unregister_callback(&mut self, id: CallbackId) { - self.system_state_changes + self.system_state_modifications .callback_updates .push(CallbackUpdate::Unregister(id)) } @@ -829,21 +833,21 @@ impl SandboxSafeSystemState { /// Computes the current main balance of the canister based /// on the initial value and the changes during the execution. pub(super) fn cycles_balance(&self) -> Cycles { - let cycles_change = self.system_state_changes.cycles_balance_change; + let cycles_change = self.system_state_modifications.cycles_balance_change; cycles_change.apply(self.initial_cycles_balance) } /// Computes the current reserved balance of the canister based /// on the initial value and the changes during the execution. pub(super) fn reserved_balance(&self) -> Cycles { - self.initial_reserved_balance + self.system_state_changes.reserved_cycles + self.initial_reserved_balance + self.system_state_modifications.reserved_cycles } pub(super) fn msg_cycles_available(&self) -> Cycles { let initial_available = self.call_context_balance.unwrap_or(Cycles::zero()); let already_taken = self - .system_state_changes + .system_state_modifications .call_context_balance_taken .map_or(Cycles::zero(), |(_, balance_taken)| balance_taken); @@ -856,7 +860,7 @@ impl SandboxSafeSystemState { } fn update_balance_change(&mut self, new_balance: Cycles) { - self.system_state_changes.cycles_balance_change = + self.system_state_modifications.cycles_balance_change = CyclesBalanceChange::new(self.initial_cycles_balance, new_balance); } @@ -876,7 +880,7 @@ impl SandboxSafeSystemState { new_balance ); - self.system_state_changes + self.system_state_modifications .add_consumed_cycles(consumed_cycles); self.update_balance_change(new_balance); } @@ -930,16 +934,16 @@ impl SandboxSafeSystemState { let mut new_balance = self.cycles_balance(); // It is safe to unwrap since msg_cycles_accept and msg_cycles_accept128 are - // available only for ApiType::{Update, ReplyCallback, RejectCallback} and all of - // them have CallContextId, hence SystemStateChanges::call_context_balance_taken - // will never be `None`. + // available only forApiType::{Update, ReplicatedQuery, ReplyCallback, + // RejectCallBack} and all of them have CallContextId, hence + // SystemStateModifications::call_context_balance_taken will never be `None`. debug_assert!(self - .system_state_changes + .system_state_modifications .call_context_balance_taken .is_some()); let balance_taken = &mut self - .system_state_changes + .system_state_modifications .call_context_balance_taken .as_mut() .unwrap() @@ -968,7 +972,10 @@ impl SandboxSafeSystemState { pub fn prepayment_for_response_execution(&self) -> Cycles { self.cycles_account_manager - .prepayment_for_response_execution(self.subnet_size, self.is_wasm64_execution.into()) + .prepayment_for_response_execution( + self.subnet_size, + WasmExecutionMode::from_is_wasm64(self.is_wasm64_execution), + ) } pub fn prepayment_for_response_transmission(&self) -> Cycles { @@ -1055,14 +1062,14 @@ impl SandboxSafeSystemState { .get(&msg.receiver) .unwrap_or(&DEFAULT_QUEUE_CAPACITY); let used_slots = self - .system_state_changes + .system_state_modifications .request_slots_used .entry(msg.receiver) .or_insert(0); if *used_slots >= *initial_available_slots { return Err(msg); } - self.system_state_changes.requests.push(msg); + self.system_state_modifications.requests.push(msg); *used_slots += 1; self.update_balance_change_consuming(new_balance, &consumed_cycles); Ok(()) @@ -1215,8 +1222,8 @@ impl SandboxSafeSystemState { /// /// The reserved cycles are removed from the main balance and added to the /// reserved balance. In pseudocode, reserving means: - /// - `self.system_state_changes.cycles_balance_change -= reserved_cycles` - /// - `self.system_state_changes.reserved_cycles += reserved_cycles` + /// - `self.system_state_modifications.cycles_balance_change -= reserved_cycles` + /// - `self.system_state_modifications.reserved_cycles += reserved_cycles` pub(super) fn reserve_storage_cycles( &mut self, allocated_bytes: NumBytes, @@ -1256,7 +1263,7 @@ impl SandboxSafeSystemState { } let new_balance = old_balance - cycles_to_reserve; self.update_balance_change(new_balance); - self.system_state_changes.reserved_cycles += cycles_to_reserve; + self.system_state_modifications.reserved_cycles += cycles_to_reserve; Ok(()) } } @@ -1270,56 +1277,22 @@ impl SandboxSafeSystemState { /// `wasm_memory_threshold >= wasm_memory_limit - wasm_memory_usage` /// /// Note: if `wasm_memory_limit` is not set, its default value is 4 GiB. - pub fn check_on_low_wasm_memory_hook_condition( + pub fn update_status_of_low_wasm_memory_hook_condition( &mut self, memory_allocation: Option, wasm_memory_limit: Option, memory_usage: NumBytes, wasm_memory_usage: NumBytes, ) { - // If wasm memory limit is not set, the default is 4 GiB. Wasm memory - // limit is ignored for query methods, response callback handlers, - // global timers, heartbeats, and canister pre_upgrade. - let wasm_memory_limit = - wasm_memory_limit.unwrap_or_else(|| NumBytes::new(4 * 1024 * 1024 * 1024)); - - debug_assert!( - wasm_memory_usage <= memory_usage, - "Wasm memory usage {} is greater that memory usage {}.", + let is_condition_satisfied = is_low_wasm_memory_hook_condition_satisfied( + memory_usage, wasm_memory_usage, - memory_usage - ); - - let memory_usage_without_wasm_memory = - NumBytes::new(memory_usage.get().saturating_sub(wasm_memory_usage.get())); - - // If the canister has memory allocation, then it maximum allowed Wasm memory can be calculated - // as min(memory_allocation - memory_usage_without_wasm_memory, wasm_memory_limit). - let wasm_capacity = memory_allocation.map_or_else( - || wasm_memory_limit, - |memory_allocation| { - debug_assert!( - memory_usage_without_wasm_memory <= memory_allocation, - "Used stable memory: {:?} is larger than memory allocation: {:?}.", - memory_usage_without_wasm_memory, - memory_allocation - ); - std::cmp::min( - memory_allocation - memory_usage_without_wasm_memory, - wasm_memory_limit, - ) - }, + memory_allocation, + wasm_memory_limit, + self.wasm_memory_threshold, ); - // Conceptually we can think that the remaining Wasm memory is - // equal to `wasm_capacity - wasm_memory_usage` and that should - // be compared with `wasm_memory_threshold` when checking for - // the condition for the hook. However, since `wasm_memory_limit` - // is ignored in some executions as stated above it is possible - // that `wasm_memory_usage` is greater than `wasm_capacity` to - // avoid overflowing subtraction we adopted inequality. - let is_condition_satisfied = wasm_capacity < wasm_memory_usage + self.wasm_memory_threshold; - self.system_state_changes + self.system_state_modifications .on_low_wasm_memory_hook_condition_check_result = Some(is_condition_satisfied); } @@ -1351,19 +1324,19 @@ impl SandboxSafeSystemState { /// Appends a log record to the system state changes. pub fn append_canister_log(&mut self, time: &Time, content: Vec) { - self.system_state_changes + self.system_state_modifications .canister_log .add_record(time.as_nanos_since_unix_epoch(), content); } /// Takes collected canister log records. pub fn take_canister_log(&mut self) -> CanisterLog { - std::mem::take(&mut self.system_state_changes.canister_log) + std::mem::take(&mut self.system_state_modifications.canister_log) } /// Returns collected canister log records. pub fn canister_log(&self) -> &CanisterLog { - &self.system_state_changes.canister_log + &self.system_state_modifications.canister_log } fn caller_is_controller(&self) -> bool { @@ -1373,6 +1346,10 @@ impl SandboxSafeSystemState { false } } + + pub fn get_subnet_id(&self) -> SubnetId { + self.cycles_account_manager.get_subnet_id() + } } /// Holds the metadata and the number of downstream requests. Requests created during the same @@ -1406,7 +1383,7 @@ mod tests { use crate::{ cycles_balance_change::CyclesBalanceChange, sandbox_safe_system_state::{ - CanisterStatusView, SandboxSafeSystemState, SystemStateChanges, + CanisterStatusView, SandboxSafeSystemState, SystemStateModifications, }, }; @@ -1423,12 +1400,12 @@ mod tests { let removed = Cycles::new(500_000); let consumed = Cycles::new(100_000); - let system_state_changes = SystemStateChanges::default_with_cycles_changes( + let system_state_modifications = SystemStateModifications::default_with_cycles_changes( CyclesBalanceChange::Removed(removed), BTreeMap::from([(CyclesUseCase::RequestAndResponseTransmission, consumed)]), ); - system_state_changes.apply_balance_changes(&mut system_state); + system_state_modifications.apply_balance_changes(&mut system_state); assert_eq!(initial_cycles_balance - removed, system_state.balance()); @@ -1436,12 +1413,12 @@ mod tests { let removed = Cycles::new(500_000); let consumed = Cycles::new(600_000); - let system_state_changes = SystemStateChanges::default_with_cycles_changes( + let system_state_modifications = SystemStateModifications::default_with_cycles_changes( CyclesBalanceChange::Removed(removed), BTreeMap::from([(CyclesUseCase::RequestAndResponseTransmission, consumed)]), ); - system_state_changes.apply_balance_changes(&mut system_state); + system_state_modifications.apply_balance_changes(&mut system_state); assert_eq!(initial_cycles_balance - removed, system_state.balance()); @@ -1449,12 +1426,12 @@ mod tests { let added = Cycles::new(500_000); let consumed = Cycles::new(100_000); - let system_state_changes = SystemStateChanges::default_with_cycles_changes( + let system_state_modifications = SystemStateModifications::default_with_cycles_changes( CyclesBalanceChange::Added(added), BTreeMap::from([(CyclesUseCase::RequestAndResponseTransmission, consumed)]), ); - system_state_changes.apply_balance_changes(&mut system_state); + system_state_modifications.apply_balance_changes(&mut system_state); assert_eq!(initial_cycles_balance + added, system_state.balance()); @@ -1462,12 +1439,12 @@ mod tests { let added = Cycles::new(500_000); let consumed = Cycles::new(600_000); - let system_state_changes = SystemStateChanges::default_with_cycles_changes( + let system_state_modifications = SystemStateModifications::default_with_cycles_changes( CyclesBalanceChange::Added(added), BTreeMap::from([(CyclesUseCase::RequestAndResponseTransmission, consumed)]), ); - system_state_changes.apply_balance_changes(&mut system_state); + system_state_modifications.apply_balance_changes(&mut system_state); assert_eq!(initial_cycles_balance + added, system_state.balance()); } @@ -1595,14 +1572,14 @@ mod tests { assert_eq!( state - .system_state_changes + .system_state_modifications .on_low_wasm_memory_hook_condition_check_result, None ); let memory_usage = wasm_memory_usage + memory_usage_without_wasm_memory; - state.check_on_low_wasm_memory_hook_condition( + state.update_status_of_low_wasm_memory_hook_condition( memory_allocation.map(|m| m.into()), wasm_memory_limit.map(|m| m.into()), memory_usage.into(), @@ -1611,7 +1588,7 @@ mod tests { assert_eq!( state - .system_state_changes + .system_state_modifications .on_low_wasm_memory_hook_condition_check_result .unwrap(), helper_is_condition_satisfied_for_on_low_wasm_memory_hook( diff --git a/rs/system_api/tests/common/mod.rs b/rs/system_api/tests/common/mod.rs index 84511a5dc29..cf0101f87f1 100644 --- a/rs/system_api/tests/common/mod.rs +++ b/rs/system_api/tests/common/mod.rs @@ -99,6 +99,15 @@ impl ApiTypeBuilder { ) } + pub fn build_replicated_query_api() -> ApiType { + ApiType::replicated_query( + UNIX_EPOCH, + vec![], + user_test_id(1).get(), + CallContextId::new(1), + ) + } + pub fn build_non_replicated_query_api() -> ApiType { ApiType::non_replicated_query( UNIX_EPOCH, @@ -175,6 +184,27 @@ impl ApiTypeBuilder { 0.into(), ) } + + pub fn build_inspect_message_api() -> ApiType { + ApiType::inspect_message( + PrincipalId::new_anonymous(), + "test".to_string(), + vec![], + UNIX_EPOCH, + ) + } + + pub fn build_start_api() -> ApiType { + ApiType::start(UNIX_EPOCH) + } + + pub fn build_init_api() -> ApiType { + ApiType::init(UNIX_EPOCH, vec![], user_test_id(1).get()) + } + + pub fn build_pre_upgrade_api() -> ApiType { + ApiType::pre_upgrade(UNIX_EPOCH, user_test_id(1).get()) + } } pub fn get_system_api( diff --git a/rs/system_api/tests/sandbox_safe_system_state.rs b/rs/system_api/tests/sandbox_safe_system_state.rs index 2307aa6285f..4ad62595d7f 100644 --- a/rs/system_api/tests/sandbox_safe_system_state.rs +++ b/rs/system_api/tests/sandbox_safe_system_state.rs @@ -34,7 +34,7 @@ use std::sync::Arc; mod common; use common::*; -use ic_cycles_account_manager::WasmExecutionMode; +use ic_replicated_state::canister_state::execution_state::WasmExecutionMode; const MAX_NUM_INSTRUCTIONS: NumInstructions = NumInstructions::new(1 << 30); const INITIAL_CYCLES: Cycles = Cycles::new(5_000_000_000_000); @@ -253,7 +253,7 @@ fn correct_charging_source_canister_for_a_request() { // => Mock the response_cycles_refund() invocation from the // execute_canister_response() sandbox_safe_system_state - .system_state_changes + .system_state_modifications .apply_changes( UNIX_EPOCH, &mut system_state, @@ -443,8 +443,8 @@ fn call_increases_cycles_consumed_metric() { api.ic0_call_new(0, 0, 0, 0, 0, 0, 0, 0, &[]).unwrap(); api.ic0_call_perform().unwrap(); - let system_state_changes = api.into_system_state_changes(); - system_state_changes + let system_state_modifications = api.take_system_state_modifications(); + system_state_modifications .apply_changes( UNIX_EPOCH, &mut system_state, @@ -526,7 +526,7 @@ fn test_inter_canister_call( .unwrap(); sandbox_safe_system_state - .system_state_changes + .system_state_modifications .apply_changes( UNIX_EPOCH, &mut system_state, diff --git a/rs/system_api/tests/system_api.rs b/rs/system_api/tests/system_api.rs index 5cb55e539c1..f6634ef5fa5 100644 --- a/rs/system_api/tests/system_api.rs +++ b/rs/system_api/tests/system_api.rs @@ -171,7 +171,12 @@ fn update_api() -> ApiType { } fn replicated_query_api() -> ApiType { - ApiType::replicated_query(UNIX_EPOCH, vec![], user_test_id(1).get()) + ApiType::replicated_query( + UNIX_EPOCH, + vec![], + user_test_id(1).get(), + call_context_test_id(1), + ) } fn non_replicated_query_api() -> ApiType { @@ -251,13 +256,13 @@ fn is_supported(api_type: SystemApiCallId, context: &str) -> bool { SystemApiCallId::MsgReply => vec!["U", "RQ", "NRQ", "CQ", "Ry", "Rt", "CRy", "CRt"], SystemApiCallId::MsgReject => vec!["U", "RQ", "NRQ", "CQ", "Ry", "Rt", "CRy", "CRt"], SystemApiCallId::MsgDeadline => vec!["U", "RQ", "NRQ", "CQ", "Ry", "Rt", "CRy", "CRt"], - SystemApiCallId::MsgCyclesAvailable => vec!["U", "Rt", "Ry"], - SystemApiCallId::MsgCyclesAvailable128 => vec!["U", "Rt", "Ry"], + SystemApiCallId::MsgCyclesAvailable => vec!["U", "RQ", "Rt", "Ry"], + SystemApiCallId::MsgCyclesAvailable128 => vec!["U", "RQ", "Rt", "Ry"], SystemApiCallId::MsgCyclesRefunded => vec!["Rt", "Ry"], SystemApiCallId::MsgCyclesRefunded128 => vec!["Rt", "Ry"], - SystemApiCallId::MsgCyclesAccept => vec!["U", "Rt", "Ry"], - SystemApiCallId::MsgCyclesAccept128 => vec!["U", "Rt", "Ry"], - SystemApiCallId::CyclesBurn128 => vec!["I", "G", "U", "Ry", "Rt", "C", "T"], + SystemApiCallId::MsgCyclesAccept => vec!["U", "RQ", "Rt", "Ry"], + SystemApiCallId::MsgCyclesAccept128 => vec!["U", "RQ", "Rt", "Ry"], + SystemApiCallId::CyclesBurn128 => vec!["I", "G", "U", "RQ", "Ry", "Rt", "C", "T"], SystemApiCallId::CanisterSelfSize => vec!["*"], SystemApiCallId::CanisterSelfCopy => vec!["*"], SystemApiCallId::CanisterCycleBalance => vec!["*"], @@ -294,7 +299,9 @@ fn is_supported(api_type: SystemApiCallId, context: &str) -> bool { SystemApiCallId::DebugPrint => vec!["*", "s"], SystemApiCallId::Trap => vec!["*", "s"], SystemApiCallId::MintCycles => vec!["U", "Ry", "Rt", "T"], - SystemApiCallId::MintCycles128 => vec!["U", "Ry", "Rt", "T"] + SystemApiCallId::MintCycles128 => vec!["U", "Ry", "Rt", "T"], + SystemApiCallId::SubnetSelfSize => vec!["*"], + SystemApiCallId::SubnetSelfCopy => vec!["*"], }; // the semantics of "*" is to cover all modes except for "s" matrix.get(&api_type).unwrap().contains(&context) @@ -781,6 +788,26 @@ fn api_availability_test( context, ); } + SystemApiCallId::SubnetSelfSize => { + assert_api_availability( + |api| api.ic0_subnet_self_size(), + api_type, + &system_state, + cycles_account_manager, + api_type_enum, + context, + ); + } + SystemApiCallId::SubnetSelfCopy => { + assert_api_availability( + |api| api.ic0_subnet_self_copy(0, 0, 0, &mut [42; 128]), + api_type, + &system_state, + cycles_account_manager, + api_type_enum, + context, + ); + } // stable API is tested separately SystemApiCallId::StableGrow | SystemApiCallId::StableRead @@ -1137,8 +1164,8 @@ fn certified_data_set() { // Copy the certified data into the system state. api.ic0_certified_data_set(0, 32, &heap).unwrap(); - let system_state_changes = api.into_system_state_changes(); - system_state_changes + let system_state_modifications = api.take_system_state_modifications(); + system_state_modifications .apply_changes( UNIX_EPOCH, &mut system_state, @@ -1310,8 +1337,8 @@ fn call_perform_not_enough_cycles_does_not_trap() { res ), } - let system_state_changes = api.into_system_state_changes(); - system_state_changes + let system_state_modifications = api.take_system_state_modifications(); + system_state_modifications .apply_changes( UNIX_EPOCH, &mut system_state, @@ -1454,8 +1481,8 @@ fn helper_test_on_low_wasm_memory( .unwrap(); } - let system_state_changes = api.into_system_state_changes(); - system_state_changes + let system_state_modifications = api.take_system_state_modifications(); + system_state_modifications .apply_changes( UNIX_EPOCH, &mut system_state, @@ -1723,8 +1750,8 @@ fn push_output_request_respects_memory_limits() { ); // Ensure that exactly one output request was pushed. - let system_state_changes = api.into_system_state_changes(); - system_state_changes + let system_state_modifications = api.take_system_state_modifications(); + system_state_modifications .apply_changes( UNIX_EPOCH, &mut system_state, @@ -1839,8 +1866,8 @@ fn push_output_request_oversized_request_memory_limits() { ); // Ensure that exactly one output request was pushed. - let system_state_changes = api.into_system_state_changes(); - system_state_changes + let system_state_modifications = api.take_system_state_modifications(); + system_state_modifications .apply_changes( UNIX_EPOCH, &mut system_state, @@ -1875,8 +1902,8 @@ fn ic0_global_timer_set_is_propagated_from_sandbox() { // Propagate system state changes assert_eq!(system_state.global_timer, CanisterTimer::Inactive); - let system_state_changes = api.into_system_state_changes(); - system_state_changes + let system_state_modifications = api.take_system_state_modifications(); + system_state_modifications .apply_changes( UNIX_EPOCH, &mut system_state, @@ -2069,3 +2096,40 @@ fn test_save_log_message_keeps_total_log_size_limited() { assert_eq!(log.records().len(), initial_records_number + 1); assert_le!(log.used_space(), MAX_ALLOWED_CANISTER_LOG_BUFFER_SIZE); } + +#[test] +fn in_replicated_execution_works_correctly() { + // The following should execute in replicated mode. + for api_type in &[ + ApiTypeBuilder::build_update_api(), + ApiTypeBuilder::build_system_task_api(), + ApiTypeBuilder::build_start_api(), + ApiTypeBuilder::build_init_api(), + ApiTypeBuilder::build_pre_upgrade_api(), + ApiTypeBuilder::build_replicated_query_api(), + ApiTypeBuilder::build_reply_api(Cycles::new(0)), + ApiTypeBuilder::build_reject_api(RejectContext::new(RejectCode::CanisterReject, "error")), + ] { + let cycles_account_manager = CyclesAccountManagerBuilder::new().build(); + let system_state = SystemStateBuilder::default().build(); + let api = get_system_api(api_type.clone(), &system_state, cycles_account_manager); + assert_eq!(api.ic0_in_replicated_execution(), Ok(1)); + } + + // The following should execute in non-replicated mode. + for api_type in &[ + ApiTypeBuilder::build_non_replicated_query_api(), + ApiTypeBuilder::build_composite_query_api(), + ApiTypeBuilder::build_composite_reply_api(Cycles::new(0)), + ApiTypeBuilder::build_composite_reject_api(RejectContext::new( + RejectCode::CanisterReject, + "error", + )), + ApiTypeBuilder::build_inspect_message_api(), + ] { + let cycles_account_manager = CyclesAccountManagerBuilder::new().build(); + let system_state = SystemStateBuilder::default().build(); + let api = get_system_api(api_type.clone(), &system_state, cycles_account_manager); + assert_eq!(api.ic0_in_replicated_execution(), Ok(0)); + } +} diff --git a/rs/test_utilities/artifact_pool/BUILD.bazel b/rs/test_utilities/artifact_pool/BUILD.bazel index d3c1eb1026a..7ff50877d1b 100644 --- a/rs/test_utilities/artifact_pool/BUILD.bazel +++ b/rs/test_utilities/artifact_pool/BUILD.bazel @@ -7,6 +7,7 @@ DEPENDENCIES = [ "//rs/artifact_pool", "//rs/config", "//rs/consensus", + "//rs/consensus/dkg", "//rs/consensus/utils", "//rs/interfaces", "//rs/interfaces/registry", diff --git a/rs/test_utilities/artifact_pool/Cargo.toml b/rs/test_utilities/artifact_pool/Cargo.toml index 76d3675c20e..bb8bbd6f955 100644 --- a/rs/test_utilities/artifact_pool/Cargo.toml +++ b/rs/test_utilities/artifact_pool/Cargo.toml @@ -10,6 +10,7 @@ documentation.workspace = true ic-artifact-pool = { path = "../../artifact_pool" } ic-config = { path = "../../config" } ic-consensus = { path = "../../consensus" } +ic-consensus-dkg = { path = "../../consensus/dkg" } ic-consensus-utils = { path = "../../consensus/utils" } ic-interfaces = { path = "../../interfaces" } ic-interfaces-registry = { path = "../../interfaces/registry" } diff --git a/rs/test_utilities/artifact_pool/src/consensus_pool.rs b/rs/test_utilities/artifact_pool/src/consensus_pool.rs index 2fe4c1f5b5b..8e206edc9df 100644 --- a/rs/test_utilities/artifact_pool/src/consensus_pool.rs +++ b/rs/test_utilities/artifact_pool/src/consensus_pool.rs @@ -145,7 +145,7 @@ fn dkg_payload_builder_fn( dkg_pool: Arc>, ) -> Box consensus::dkg::Payload> { Box::new(move |cons_pool, parent, validation_context| { - ic_consensus::dkg::create_payload( + ic_consensus_dkg::create_payload( subnet_id, &*registry_client, &*crypto, @@ -188,7 +188,7 @@ impl TestConsensusPool { )) }), )); - let summary = ic_consensus::dkg::make_genesis_summary(&*registry_client, subnet_id, None); + let summary = ic_consensus_dkg::make_genesis_summary(&*registry_client, subnet_id, None); let pool = ConsensusPoolImpl::new( node_id, subnet_id, diff --git a/rs/test_utilities/consensus/src/batch.rs b/rs/test_utilities/consensus/src/batch.rs index 5a315c1875b..0b9de6fa43f 100644 --- a/rs/test_utilities/consensus/src/batch.rs +++ b/rs/test_utilities/consensus/src/batch.rs @@ -1,10 +1,13 @@ use ic_base_types::NumBytes; use ic_interfaces::{ - batch_payload::{iterator_to_bytes, BatchPayloadBuilder, PastPayload, ProposalContext}, + batch_payload::{BatchPayloadBuilder, PastPayload, ProposalContext}, consensus::PayloadValidationError, validation::ValidationResult, }; -use ic_types::{batch::ValidationContext, Height}; +use ic_types::{ + batch::{iterator_to_bytes, ValidationContext}, + Height, +}; use mockall::*; mock! { diff --git a/rs/test_utilities/consensus/src/lib.rs b/rs/test_utilities/consensus/src/lib.rs index 6888ff6205d..09baebb16a4 100755 --- a/rs/test_utilities/consensus/src/lib.rs +++ b/rs/test_utilities/consensus/src/lib.rs @@ -140,10 +140,12 @@ pub fn make_genesis(summary: dkg::Summary) -> CatchUpPackage { let height = summary.height; let low_dkg_id = summary .current_transcript(&NiDkgTag::LowThreshold) + .unwrap() .dkg_id .clone(); let high_dkg_id = summary .current_transcript(&NiDkgTag::HighThreshold) + .unwrap() .dkg_id .clone(); let block = Block::new( diff --git a/rs/test_utilities/execution_environment/src/lib.rs b/rs/test_utilities/execution_environment/src/lib.rs index dda93c74257..5948e9cb2cc 100644 --- a/rs/test_utilities/execution_environment/src/lib.rs +++ b/rs/test_utilities/execution_environment/src/lib.rs @@ -8,7 +8,6 @@ use ic_config::{ subnet_config::SubnetConfig, }; use ic_cycles_account_manager::CyclesAccountManager; -use ic_cycles_account_manager::WasmExecutionMode; use ic_embedders::{ wasm_utils::{compile, decoding::decode_wasm}, WasmtimeEmbedder, @@ -41,7 +40,9 @@ use ic_registry_routing_table::{ use ic_registry_subnet_features::SubnetFeatures; use ic_registry_subnet_type::SubnetType; use ic_replicated_state::{ - canister_state::{execution_state::SandboxMemory, NextExecution}, + canister_state::{ + execution_state::SandboxMemory, execution_state::WasmExecutionMode, NextExecution, + }, page_map::{ test_utils::base_only_storage_layout, PageMap, TestPageAllocatorFileDescriptorImpl, PAGE_SIZE, @@ -69,13 +70,15 @@ use ic_types_test_utils::ids::{node_test_id, subnet_test_id, user_test_id}; use ic_universal_canister::{UNIVERSAL_CANISTER_SERIALIZED_MODULE, UNIVERSAL_CANISTER_WASM}; use ic_wasm_types::BinaryEncodedWasm; use maplit::{btreemap, btreeset}; -use std::convert::TryFrom; -use std::sync::Arc; use std::{ collections::{BTreeMap, BTreeSet, HashMap}, + convert::TryFrom, + os::unix::prelude::FileExt, + path::Path, + str::FromStr, + sync::Arc, time::Duration, }; -use std::{os::unix::prelude::FileExt, str::FromStr}; use tempfile::NamedTempFile; mod wat_canister; @@ -285,7 +288,7 @@ impl ExecutionTest { if let Some(state) = self.state.as_ref() { if let Some(canister) = state.canister_state(&canister_id).as_ref() { if let Some(execution_state) = canister.execution_state.as_ref() { - return execution_state.is_wasm64.into(); + return execution_state.wasm_execution_mode; } } } @@ -1709,6 +1712,10 @@ impl ExecutionTest { pub fn query_stats_set_epoch_for_testing(&mut self, epoch: QueryStatsEpoch) { self.query_handler.query_stats_set_epoch_for_testing(epoch); } + + pub fn get_own_subnet_id(&self) -> SubnetId { + self.cycles_account_manager.get_subnet_id() + } } /// A builder for `ExecutionTest`. @@ -2113,6 +2120,13 @@ impl ExecutionTestBuilder { self } + pub fn with_max_wasm64_memory_size(mut self, wasm_memory_size: NumBytes) -> Self { + self.execution_config + .embedders_config + .max_wasm64_memory_size = wasm_memory_size; + self + } + pub fn with_metering_type(mut self, metering_type: MeteringType) -> Self { self.execution_config.embedders_config.metering_type = metering_type; self @@ -2313,6 +2327,7 @@ impl ExecutionTestBuilder { dirty_page_overhead, Arc::new(TestPageAllocatorFileDescriptorImpl::new()), Arc::new(FakeStateManager::new()), + Path::new("/tmp"), ); if self.precompiled_universal_canister { hypervisor.compilation_cache_insert_for_testing( diff --git a/rs/test_utilities/types/BUILD.bazel b/rs/test_utilities/types/BUILD.bazel index 7a9748adc93..f5d2a16e788 100644 --- a/rs/test_utilities/types/BUILD.bazel +++ b/rs/test_utilities/types/BUILD.bazel @@ -25,7 +25,6 @@ rust_test( crate = ":types", deps = [ # Keep sorted. - "@crate_index//:assert_matches", "@crate_index//:bincode", "@crate_index//:serde_cbor", ], diff --git a/rs/test_utilities/types/Cargo.toml b/rs/test_utilities/types/Cargo.toml index 300abf61004..bbac97ba3da 100644 --- a/rs/test_utilities/types/Cargo.toml +++ b/rs/test_utilities/types/Cargo.toml @@ -14,6 +14,5 @@ ic-types-test-utils = { path = "../../types/types_test_utils" } rand = { workspace = true } [dev-dependencies] -assert_matches = { workspace = true } bincode = { workspace = true } serde_cbor = { workspace = true } diff --git a/rs/test_utilities/types/src/batch/ingress_payload.rs b/rs/test_utilities/types/src/batch/ingress_payload.rs index f858ae6542e..bef65d6638a 100644 --- a/rs/test_utilities/types/src/batch/ingress_payload.rs +++ b/rs/test_utilities/types/src/batch/ingress_payload.rs @@ -36,77 +36,3 @@ impl IngressPayloadBuilder { self.ingress_payload.into() } } - -#[cfg(test)] -mod tests { - use super::*; - use crate::messages::SignedIngressBuilder; - use assert_matches::assert_matches; - use ic_types::{batch::IngressPayloadError, time::expiry_time_from_now}; - use std::convert::TryFrom; - use std::time::Duration; - - #[test] - fn test_ingress_payload_deserialization() { - // serialization/deserialization of empty payload. - let payload = IngressPayload::default(); - let bytes = bincode::serialize(&payload).unwrap(); - assert_eq!( - bincode::deserialize::(&bytes).unwrap(), - payload - ); - let time = expiry_time_from_now(); - - // Some test messages. - let m1 = SignedIngressBuilder::new() - .method_name("m1".to_string()) - .expiry_time(time + Duration::from_secs(1)) - .build(); - let m1_id = m1.id(); - let m2 = SignedIngressBuilder::new() - .method_name("m2".to_string()) - .expiry_time(time + Duration::from_secs(2)) - .build(); - let m3 = SignedIngressBuilder::new() - .method_name("m3".to_string()) - .expiry_time(time + Duration::from_secs(3)) - .build(); - - let msgs = vec![m1, m2, m3]; - let payload = IngressPayload::from(msgs.clone()); - // Serialization/deserialization works. - let mut bytes = bincode::serialize(&payload).unwrap(); - assert_eq!( - bincode::deserialize::(&bytes).unwrap(), - payload - ); - // Individual lookup works. - assert_matches!(payload.get(0).unwrap(), (_, msg) if msg == msgs[0]); - assert_matches!(payload.get(1).unwrap(), (_, msg) if msg == msgs[1]); - assert_matches!(payload.get(2).unwrap(), (_, msg) if msg == msgs[2]); - // Test IndexOutOfBound. - assert_matches!(payload.get(3), Err(IngressPayloadError::IndexOutOfBound(3))); - // Converting back to messages should match original - assert_eq!(msgs, >::try_from(payload).unwrap()); - - // A sub-sequence search function - fn find(array: &[u8], subseq: &[u8]) -> Option { - (0..array.len() - subseq.len() + 1).find(|&i| array[i..i + subseq.len()] == subseq[..]) - } - - // Mutate some byte, deserialization works, but casting back to messages fail. - let pos = find(&bytes, m1_id.as_bytes()).unwrap(); - // `+= 1` may overflow in debug mode. - bytes[pos] ^= 1; - let payload = bincode::deserialize::(&bytes); - assert!(payload.is_ok()); - let payload = payload.unwrap(); - // get(0) should return error. - assert_matches!( - payload.get(0), - Err(IngressPayloadError::MismatchedMessageIdAtIndex(0)) - ); - // Conversion should also fail. - assert!(>::try_from(payload).is_err()); - } -} diff --git a/rs/tests/BUILD.bazel b/rs/tests/BUILD.bazel index 6a2a583cc3f..47c31dae65a 100644 --- a/rs/tests/BUILD.bazel +++ b/rs/tests/BUILD.bazel @@ -239,18 +239,6 @@ symlink_dir( }, ) -symlink_dir( - name = "backup/binaries", - testonly = True, - targets = { - "//rs/backup:ic-backup": "ic-backup", - "//rs/replay:ic-replay": "ic-replay", - "//rs/canister_sandbox:compiler_sandbox": "compiler_sandbox", - "//rs/canister_sandbox:sandbox_launcher": "sandbox_launcher", - "//rs/canister_sandbox:canister_sandbox": "canister_sandbox", - }, -) - symlink_dir_test( name = "cup_compatibility/binaries", targets = { diff --git a/rs/tests/boundary_nodes/BUILD.bazel b/rs/tests/boundary_nodes/BUILD.bazel index 15b50f7c765..058bb5ed0a2 100644 --- a/rs/tests/boundary_nodes/BUILD.bazel +++ b/rs/tests/boundary_nodes/BUILD.bazel @@ -10,6 +10,7 @@ TEST_CANISTERS_RUNTIME_DEPS = [ "//rs/tests/test_canisters/http_counter", "//rs/tests/test_canisters/kv_store", "@asset_canister//file", + "@long_asset_canister//file", ] system_test_nns( @@ -18,6 +19,7 @@ system_test_nns( "KV_STORE_WASM_PATH": "$(rootpath //rs/tests/test_canisters/kv_store)", "HTTP_COUNTER_WASM_PATH": "$(rootpath //rs/tests/test_canisters/http_counter)", "ASSET_CANISTER_WASM_PATH": "$(rootpath @asset_canister//file)", + "LONG_ASSET_CANISTER_WASM_PATH": "$(rootpath @long_asset_canister//file)", }, extra_head_nns_tags = [], # don't run the head_nns variant on nightly since it aleady runs on long_test. tags = [ @@ -59,6 +61,7 @@ system_test_nns( "KV_STORE_WASM_PATH": "$(rootpath //rs/tests/test_canisters/kv_store)", "HTTP_COUNTER_WASM_PATH": "$(rootpath //rs/tests/test_canisters/http_counter)", "ASSET_CANISTER_WASM_PATH": "$(rootpath @asset_canister//file)", + "LONG_ASSET_CANISTER_WASM_PATH": "$(rootpath @long_asset_canister//file)", }, flaky = True, tags = [ @@ -80,7 +83,7 @@ system_test_nns( name = "bn_update_workload_test", flaky = True, tags = [ - "manual", # TODO(BOUN-1039): remove "manual" and add back "system_test_hourly" and "system_test_nightly" when the test is not flaky anymore + "manual", # TODO(BOUN-1039): remove "manual" and add back "long_test" when the test is not flaky anymore ], target_compatible_with = ["@platforms//os:linux"], # requires libssh that does not build on Mac OS test_timeout = "long", @@ -127,6 +130,7 @@ system_test_nns( env = { "RATE_LIMIT_CANISTER_WASM_PATH": "$(rootpath //rs/boundary_node/rate_limits:rate_limit_canister)", }, + proc_macro_deps = ["@crate_index//:async-trait"], tags = [ "k8s", ], @@ -137,8 +141,13 @@ system_test_nns( deps = [ # Keep sorted. "//rs/boundary_node/rate_limits/api:rate_limits_api", + "//rs/nns/constants", + "//rs/nns/test_utils", "//rs/registry/subnet_type", + "//rs/rust_canisters/canister_test", + "//rs/tests/boundary_nodes/utils", "//rs/tests/driver:ic-system-test-driver", + "//rs/types/base_types", "//rs/types/types", "@crate_index//:anyhow", "@crate_index//:candid", @@ -149,6 +158,7 @@ system_test_nns( "@crate_index//:regex", "@crate_index//:slog", "@crate_index//:tokio", + "@crate_index//:wat", ], ) diff --git a/rs/tests/boundary_nodes/Cargo.toml b/rs/tests/boundary_nodes/Cargo.toml index da97cc1a076..354485874b3 100644 --- a/rs/tests/boundary_nodes/Cargo.toml +++ b/rs/tests/boundary_nodes/Cargo.toml @@ -8,7 +8,9 @@ documentation.workspace = true [dependencies] anyhow = { workspace = true } +async-trait = { workspace = true } candid = { workspace = true } +canister-test = { path = "../../rust_canisters/canister_test" } certificate_orchestrator_interface = { path = "../../boundary_node/certificate_issuance/certificate_orchestrator_interface" } ic-agent = { workspace = true } ic-base-types = { path = "../../types/base_types" } diff --git a/rs/tests/boundary_nodes/api_bn_decentralization_test.rs b/rs/tests/boundary_nodes/api_bn_decentralization_test.rs index 4d0fb59a9ef..4b98b9d8fdb 100644 --- a/rs/tests/boundary_nodes/api_bn_decentralization_test.rs +++ b/rs/tests/boundary_nodes/api_bn_decentralization_test.rs @@ -36,11 +36,8 @@ use registry_canister::mutations::{ use ic_agent::{ agent::{ - http_transport::{ - reqwest_transport::reqwest::{redirect::Policy, Client, ClientBuilder}, - route_provider::RouteProvider, - ReqwestTransport, - }, + http_transport::reqwest_transport::reqwest::{redirect::Policy, Client, ClientBuilder}, + route_provider::RouteProvider, ApiBoundaryNode, }, export::Principal, @@ -185,11 +182,9 @@ async fn test(env: TestEnv) { assert!(rules.contains("ct state new add @connection_limit")); } - let transport = - ReqwestTransport::create_with_client("https://api1.com", http_client.clone()).unwrap(); - let bn_agent = Agent::builder() - .with_transport(transport) + .with_url("https://api1.com") + .with_http_client(http_client.clone()) .with_identity(AnonymousIdentity {}) .build() .unwrap(); @@ -302,10 +297,9 @@ async fn test(env: TestEnv) { "Incrementing counters on canisters for the second time" ); - let transport = ReqwestTransport::create_with_client("https://api3.com", http_client).unwrap(); - let bn_agent = Agent::builder() - .with_transport(transport) + .with_url("https://api3.com") + .with_http_client(http_client) .with_identity(AnonymousIdentity {}) .build() .unwrap(); @@ -517,14 +511,10 @@ async fn assert_api_bns_present_in_state_tree( .sorted_by(|a, b| Ord::cmp(&a.domain, &b.domain)) .collect::>(); - let are_expected_bns = api_bns_sorted - .iter() - .enumerate() - .map(|(idx, bn)| { - bn.domain == expected_api_bns[idx].domain - && bn.ipv6_address == expected_api_bns[idx].ipv6_address - }) - .all(|is_match| is_match); + let are_expected_bns = api_bns_sorted.iter().enumerate().all(|(idx, bn)| { + bn.domain == expected_api_bns[idx].domain + && bn.ipv6_address == expected_api_bns[idx].ipv6_address + }); if !are_expected_bns { bail!("Expected API BNs haven't yet appeared in the state tree ..."); diff --git a/rs/tests/boundary_nodes/bn_integration_on_playnet_test.rs b/rs/tests/boundary_nodes/bn_integration_on_playnet_test.rs index 5e33e17da73..32c8dde0658 100644 --- a/rs/tests/boundary_nodes/bn_integration_on_playnet_test.rs +++ b/rs/tests/boundary_nodes/bn_integration_on_playnet_test.rs @@ -4,9 +4,10 @@ use anyhow::Result; use ic_boundary_nodes_integration_test_common::{ api_call_test, api_canister_read_state_test, api_query_test, api_status_test, - api_subnet_read_state_test, api_sync_call_test, asset_canister_test, canister_denylist_test, - content_type_headers_test, cors_headers_test, http_endpoints_test, proxy_http_canister_test, - redirect_http_to_https_test, redirect_to_dashboard_test, + api_subnet_read_state_test, api_sync_call_test, canister_denylist_test, + content_type_headers_test, cors_headers_test, http_endpoints_test, legacy_asset_canister_test, + long_asset_canister_test, proxy_http_canister_test, redirect_http_to_https_test, + redirect_to_dashboard_test, }; use ic_boundary_nodes_system_test_utils::{ constants::BOUNDARY_NODE_NAME, helpers::BoundaryNodeHttpsConfig, setup::setup_ic_with_bn, @@ -34,7 +35,8 @@ fn main() -> Result<()> { .add_test(systest!(api_sync_call_test)) .add_test(systest!(api_canister_read_state_test)) .add_test(systest!(api_subnet_read_state_test)) - .add_test(systest!(asset_canister_test)) + .add_test(systest!(legacy_asset_canister_test)) + .add_test(systest!(long_asset_canister_test)) .add_test(systest!(content_type_headers_test)) .add_test(systest!(cors_headers_test)) .add_test(systest!(proxy_http_canister_test)) diff --git a/rs/tests/boundary_nodes/bn_integration_test.rs b/rs/tests/boundary_nodes/bn_integration_test.rs index 7c858087557..8a3100593b6 100644 --- a/rs/tests/boundary_nodes/bn_integration_test.rs +++ b/rs/tests/boundary_nodes/bn_integration_test.rs @@ -4,9 +4,10 @@ use anyhow::Result; use ic_boundary_nodes_integration_test_common::{ api_call_test, api_canister_read_state_test, api_query_test, api_status_test, - api_subnet_read_state_test, api_sync_call_test, asset_canister_test, canister_denylist_test, - content_type_headers_test, cors_headers_test, http_endpoints_test, proxy_http_canister_test, - redirect_http_to_https_test, redirect_to_dashboard_test, + api_subnet_read_state_test, api_sync_call_test, canister_denylist_test, + content_type_headers_test, cors_headers_test, http_endpoints_test, legacy_asset_canister_test, + long_asset_canister_test, proxy_http_canister_test, redirect_http_to_https_test, + redirect_to_dashboard_test, }; use ic_boundary_nodes_system_test_utils::{ constants::BOUNDARY_NODE_NAME, helpers::BoundaryNodeHttpsConfig, setup::setup_ic_with_bn, @@ -34,9 +35,10 @@ fn main() -> Result<()> { .add_test(systest!(api_sync_call_test)) .add_test(systest!(api_canister_read_state_test)) .add_test(systest!(api_subnet_read_state_test)) - .add_test(systest!(asset_canister_test)) .add_test(systest!(content_type_headers_test)) .add_test(systest!(cors_headers_test)) + .add_test(systest!(legacy_asset_canister_test)) + .add_test(systest!(long_asset_canister_test)) .add_test(systest!(proxy_http_canister_test)) .add_test(systest!(redirect_http_to_https_test)) .add_test(systest!(redirect_to_dashboard_test)) diff --git a/rs/tests/boundary_nodes/custom_domains/setup.rs b/rs/tests/boundary_nodes/custom_domains/setup.rs index ddd2ff996ee..c614b003412 100644 --- a/rs/tests/boundary_nodes/custom_domains/setup.rs +++ b/rs/tests/boundary_nodes/custom_domains/setup.rs @@ -883,7 +883,7 @@ pub async fn setup_asset_canister( index_content: Option<&str>, ) -> Result { let asset_canister = env - .deploy_asset_canister() + .deploy_legacy_asset_canister() .await .expect("Could not install asset canister"); diff --git a/rs/tests/boundary_nodes/integration_test_common/src/lib.rs b/rs/tests/boundary_nodes/integration_test_common/src/lib.rs index 916658e785b..0553cfef2ed 100644 --- a/rs/tests/boundary_nodes/integration_test_common/src/lib.rs +++ b/rs/tests/boundary_nodes/integration_test_common/src/lib.rs @@ -38,15 +38,8 @@ use std::{env, iter, net::SocketAddrV6, time::Duration}; use anyhow::{anyhow, bail, Context, Error}; use futures::stream::FuturesUnordered; -use ic_agent::{ - agent::http_transport::{ - hyper_transport::hyper::StatusCode, - reqwest_transport::{reqwest, ReqwestTransport}, - }, - export::Principal, - Agent, -}; -use reqwest::{redirect::Policy, ClientBuilder, Method}; +use ic_agent::{export::Principal, Agent}; +use reqwest::{redirect::Policy, ClientBuilder, Method, StatusCode}; use serde::Deserialize; use slog::{error, info, Logger}; use tokio::{runtime::Runtime, time::sleep}; @@ -180,9 +173,10 @@ pub fn api_query_test(env: TestEnv) { block_on(async move { let cid = install_counter_canister(env, logger.clone()).await?; - let transport = ReqwestTransport::create_with_client(format!("https://{host}/"), client)?; - - let agent = Agent::builder().with_transport(transport).build()?; + let agent = Agent::builder() + .with_url(format!("https://{host}/")) + .with_http_client(client) + .build()?; agent.fetch_root_key().await?; let out = agent.query(&cid, "read").call().await?; @@ -220,8 +214,10 @@ pub fn api_call_test(env: TestEnv) { .unwrap(); // check that the update call went through - let transport = ReqwestTransport::create_with_client(format!("https://{host}/"), client)?; - let agent = Agent::builder().with_transport(transport).build()?; + let agent = Agent::builder() + .with_url(format!("https://{host}/")) + .with_http_client(client) + .build()?; agent.fetch_root_key().await?; let out = agent.query(&cid, "read").call().await?; @@ -250,10 +246,10 @@ pub fn api_sync_call_test(env: TestEnv) { block_on(async move { let cid = install_counter_canister(env.clone(), logger.clone()).await?; - let transport = ReqwestTransport::create_with_client(format!("https://{host}/"), client)? - .with_use_call_v3_endpoint(); - - let agent = Agent::builder().with_transport(transport).build()?; + let agent = Agent::builder() + .with_url(format!("https://{host}/")) + .with_http_client(client) + .build()?; agent.fetch_root_key().await?; // update call @@ -285,9 +281,10 @@ pub fn api_canister_read_state_test(env: TestEnv) { block_on(async move { let cid = install_counter_canister(env.clone(), logger.clone()).await?; - let transport = ReqwestTransport::create_with_client(format!("https://{host}/"), client)?; - - let agent = Agent::builder().with_transport(transport).build()?; + let agent = Agent::builder() + .with_url(format!("https://{host}/")) + .with_http_client(client) + .build()?; agent.fetch_root_key().await?; let _ = agent.read_state_canister_info(cid, "module_hash").await?; @@ -311,9 +308,10 @@ pub fn api_subnet_read_state_test(env: TestEnv) { let (client, host) = setup_client(env.clone()).expect("failed to setup client"); block_on(async move { - let transport = ReqwestTransport::create_with_client(format!("https://{host}/"), client)?; - - let agent = Agent::builder().with_transport(transport).build()?; + let agent = Agent::builder() + .with_url(format!("https://{host}/")) + .with_http_client(client) + .build()?; agent.fetch_root_key().await?; let subnet_id: Principal = env @@ -347,7 +345,7 @@ Coverage:: asset Canisters behave as expected end::catalog[] */ -pub fn asset_canister_test(env: TestEnv) { +pub fn legacy_asset_canister_test(env: TestEnv) { let logger_orig = env.logger(); let boundary_node = env .get_deployed_boundary_node(BOUNDARY_NODE_NAME) @@ -359,7 +357,7 @@ pub fn asset_canister_test(env: TestEnv) { info!(&logger_orig, "Creating asset canister"); let asset_canister_orig = rt - .block_on(env.deploy_asset_canister()) + .block_on(env.deploy_legacy_asset_canister()) .expect("Could not install asset canister"); let http_client_builder = ClientBuilder::new(); @@ -756,6 +754,150 @@ pub fn asset_canister_test(env: TestEnv) { .expect("test suite failed"); } +// Constants copied from long asset canister: +const ASSET_CHUNK_SIZE: usize = 2_000_000; + +const ONE_CHUNK_ASSET_LEN: usize = ASSET_CHUNK_SIZE; +const TWO_CHUNKS_ASSET_LEN: usize = ASSET_CHUNK_SIZE + 1; +const SIX_CHUNKS_ASSET_LEN: usize = 5 * ASSET_CHUNK_SIZE + 12; + +pub fn long_asset_canister_test(env: TestEnv) { + let logger_orig = env.logger(); + let boundary_node = env + .get_deployed_boundary_node(BOUNDARY_NODE_NAME) + .unwrap() + .get_snapshot() + .unwrap(); + + let rt = runtime(); + + info!(&logger_orig, "Creating asset canister"); + let asset_canister_orig = rt + .block_on(env.deploy_long_asset_canister()) + .expect("Could not install asset canister"); + + let http_client_builder = ClientBuilder::new(); + let (client_builder, host_orig) = if let Some(playnet) = boundary_node.get_playnet() { + ( + http_client_builder, + format!("{0}.{playnet}", asset_canister_orig.canister_id), + ) + } else { + let host = format!("{0}.ic0.app", asset_canister_orig.canister_id); + let bn_addr = SocketAddrV6::new(boundary_node.ipv6(), 0, 0, 0).into(); + let client_builder = http_client_builder + .danger_accept_invalid_certs(true) + .resolve(&host, bn_addr); + (client_builder, host) + }; + let http_client = client_builder.build().unwrap(); + + let futs = FuturesUnordered::new(); + futs.push(rt.spawn({ + let host = host_orig.clone(); + let logger = logger_orig.clone(); + let http_client = http_client.clone(); + let name = "Requesting a single chunk asset"; + info!(&logger, "Starting subtest {}", name); + + async move { + info!(&logger, "Requesting /long_asset_one_chunk ..."); + let res = http_client + .get(format!("https://{host}/long_asset_one_chunk")) + .header("accept-encoding", "gzip") + .send() + .await? + .bytes() + .await? + .to_vec(); + + if res.len() != ONE_CHUNK_ASSET_LEN { + bail!("/long_asset_one_chunk response did not match uploaded content") + } + + Ok(()) + } + })); + + futs.push(rt.spawn({ + let host = host_orig.clone(); + let logger = logger_orig.clone(); + let http_client = http_client.clone(); + let name = "Requesting a two chunk asset"; + info!(&logger, "Starting subtest {}", name); + + async move { + info!(&logger, "Requesting /long_asset_two_chunks ..."); + let res = http_client + .get(format!("https://{host}/long_asset_two_chunks")) + .header("accept-encoding", "gzip") + .send() + .await? + .bytes() + .await? + .to_vec(); + + if res.len() != TWO_CHUNKS_ASSET_LEN { + bail!("/long_asset_two_chunks response did not match uploaded content") + } + + Ok(()) + } + })); + + futs.push(rt.spawn({ + let host = host_orig.clone(); + let logger = logger_orig.clone(); + let http_client = http_client.clone(); + let name = "Requesting a six chunk asset"; + info!(&logger, "Starting subtest {}", name); + + async move { + info!(&logger, "Requesting /long_asset_six_chunks ..."); + let res = http_client + .get(format!("https://{host}/long_asset_six_chunks")) + .header("accept-encoding", "gzip") + .send() + .await? + .bytes() + .await? + .to_vec(); + + if res.len() != SIX_CHUNKS_ASSET_LEN { + bail!("/long_asset_six_chunks response did not match uploaded content") + } + + Ok(()) + } + })); + + let logger = logger_orig.clone(); + rt.block_on(async move { + let mut cnt_err = 0; + info!(&logger, "Waiting for subtests"); + + for fut in futs { + match fut.await { + Ok(Err(err)) => { + error!(logger, "test failed: {}", err); + cnt_err += 1; + } + Err(err) => { + error!(logger, "test panicked: {}", err); + cnt_err += 1; + } + _ => {} + } + } + + match cnt_err { + 0 => Ok(()), + _ => bail!("failed with {cnt_err} errors"), + } + }) + .expect("test suite failed"); +} + /* tag::catalog[] Title:: Boundary nodes HTTP canister test @@ -1327,7 +1469,7 @@ pub fn http_endpoints_test(env: TestEnv) { let rt = runtime(); info!(&logger_orig, "Creating asset canister"); - let asset_canister_orig = rt.block_on(env.deploy_asset_canister()).unwrap(); + let asset_canister_orig = rt.block_on(env.deploy_legacy_asset_canister()).unwrap(); info!(&logger_orig, "Uploading static assets"); #[derive(Clone)] diff --git a/rs/tests/boundary_nodes/rate_limit_canister_test.rs b/rs/tests/boundary_nodes/rate_limit_canister_test.rs index c3b3606a4e8..af557884146 100644 --- a/rs/tests/boundary_nodes/rate_limit_canister_test.rs +++ b/rs/tests/boundary_nodes/rate_limit_canister_test.rs @@ -1,62 +1,97 @@ /* tag::catalog[] -Title:: Setting rate-limits on the API boundary nodes via rate-limit canister (status WIP) +Title:: Rate-limit canister integration with API boundary nodes -Goal:: NOTE: this is a WIP system-test. Verify that API boundary nodes can dynamically fetch rate-limit configurations from the canister and enforce them for ingress messages. +Goal:: Ensure rate-limit rules can be added to the canister and are properly enforced by API Boundary Nodes. Runbook: -. Set up an rate-limit canister. -. Test that the rate-limit canister API works. -. TODO: test that API boundary node can successfully fetch/apply rate-limit configurations. - -Success:: The rate-limit canister is installed and the API works. - -Coverage:: The rate-limit canister interface works as expected. +1. Set up an Internet Computer (IC) with a system-subnet and an API boundary node. +2. Install the rate-limit canister at a specified mainnet ID, without specifying an authorized_principal in the payload argument. +3. Install the counter canister, which is used for testing enforced rate-limits. +4. Create an `ic-agent` instance associated with an API boundary node. +5. Verify that initially the agent can successfully interact with the counter canister by sending e.g. an update call. +6. Try to add two rate-limit rules to the rate-limit canister, which completely block requests to two canisters: counter and rate-limit (self blocking). +7. Assert canister call fails (rejected), as authorized_principal is unset for the rate-limit canister. +8. Upgrade rate-limit canister code via proposal, specifying authorized_principal in the payload. +9. Retry step 6 and assert it succeeds. +10. Verify that the agent can no longer send requests to the counter canister after API boundary node enforces the new rule. +11. Add a rate-limit rule, which explicitly unblocks requests to the counter canister. + Setting this rule should still be possible despite the rate-limit canister being blocked itself (as there is an explicit allow-rule in the ic-boundary). +12. Verify that the agent can send requests to the counter canister again, ensuring that updated rate-limit rules are enforced correctly by API boundary nodes. end::catalog[] */ use anyhow::{bail, Result}; +use async_trait::async_trait; use candid::{Decode, Encode, Principal}; +use canister_test::{Canister, Wasm}; +use ic_base_types::PrincipalId; +use ic_boundary_nodes_system_test_utils::{ + constants::COUNTER_CANISTER_WAT, helpers::install_canisters, +}; +use ic_nns_constants::{GOVERNANCE_CANISTER_ID, ROOT_CANISTER_ID}; +use ic_nns_test_utils::{ + common::modify_wasm_bytes, governance::upgrade_nns_canister_by_proposal, + itest_helpers::install_rust_canister_from_path, +}; use k256::elliptic_curve::SecretKey; use rand::{rngs::OsRng, SeedableRng}; use rand_chacha::ChaChaRng; -use slog::{info, Logger}; -use std::env; -use std::time::Duration; +use slog::info; +use std::{env, net::SocketAddr, sync::Arc, time::Duration}; use tokio::runtime::Runtime; use ic_agent::{ - identity::{AnonymousIdentity, Secp256k1Identity}, - Agent, Identity, + agent::{ + http_transport::reqwest_transport::reqwest::{Client, Request, Response}, + HttpService, + }, + identity::Secp256k1Identity, + Agent, AgentError, Identity, }; use ic_registry_subnet_type::SubnetType; use ic_system_test_driver::{ + driver::test_env_api::NnsInstallationBuilder, driver::{ - group::{SystemTestGroup, SystemTestSubGroup}, + group::SystemTestGroup, ic::InternetComputer, test_env::TestEnv, test_env_api::{ - GetFirstHealthyNodeSnapshot, HasPublicApiUrl, HasTopologySnapshot, IcNodeContainer, - READY_WAIT_TIMEOUT, RETRY_BACKOFF, + get_dependency_path, GetFirstHealthyNodeSnapshot, HasPublicApiUrl, HasTopologySnapshot, + IcNodeContainer, }, }, retry_with_msg_async, systest, - util::agent_observes_canister_module, + util::runtime_from_url, }; -use regex::Regex; - use rate_limits_api::{ - v1::{Action, RateLimitRule, RequestType}, - AddConfigResponse, DiscloseRulesArg, DiscloseRulesResponse, GetConfigResponse, - GetRuleByIdResponse, IncidentId, InitArg, InputConfig, InputRule, OutputRuleMetadata, RuleId, - Version, + v1::{Action, RateLimitRule}, + AddConfigResponse, InitArg, InputConfig, InputRule, }; +const RATE_LIMIT_CANISTER_ID: &str = "u637p-5aaaa-aaaaq-qaaca-cai"; + pub fn setup(env: TestEnv) { + info!( + &env.logger(), + "Step 1. Set up an Internet Computer (IC) with a system-subnet and an API boundary node" + ); InternetComputer::new() .add_fast_single_node_subnet(SubnetType::System) - .add_fast_single_node_subnet(SubnetType::Application) + .use_specified_ids_allocation_range() + .with_api_boundary_nodes(1) .setup_and_start(&env) .expect("failed to setup IC under test"); + let nns_node = env + .topology_snapshot() + .root_subnet() + .nodes() + .next() + .unwrap(); + NnsInstallationBuilder::new() + .at_ids() + .install(&nns_node, &env) + .expect("could not install NNS canisters"); + info!(&env.logger(), "Checking readiness of all replica nodes ..."); env.topology_snapshot().subnets().for_each(|subnet| { subnet .nodes() @@ -64,340 +99,347 @@ pub fn setup(env: TestEnv) { }); } -pub fn complete_flow_test(env: TestEnv) { +async fn test_async(env: TestEnv) { let logger = env.logger(); - info!(&logger, "installing canister"); - let mut rng = ChaChaRng::from_rng(OsRng).unwrap(); let full_access_identity = Secp256k1Identity::from_private_key(SecretKey::random(&mut rng)); let full_access_principal = full_access_identity.sender().unwrap(); - let args = Encode!(&InitArg { - registry_polling_period_secs: 60, - authorized_principal: Some(full_access_principal), - }) - .unwrap(); + let nns_node = env.get_first_healthy_system_node_snapshot(); + let nns = runtime_from_url(nns_node.get_public_url(), nns_node.effective_canister_id()); - let app_node = env.get_first_healthy_application_node_snapshot(); - let canister_id = app_node.create_and_install_canister_with_arg( - &env::var("RATE_LIMIT_CANISTER_WASM_PATH").expect("RATE_LIMIT_CANISTER_WASM_PATH not set"), - Some(args), + let rate_limit_id = Principal::from_text(RATE_LIMIT_CANISTER_ID).unwrap(); + + info!( + &logger, + "Step 2. Install the rate-limit canister at a specified mainnet ID {rate_limit_id} (do not set any authorized_principal)" ); - let rt = Runtime::new().expect("Could not create tokio runtime."); - let agent_node = app_node.build_default_agent(); - - rt.block_on(async move { - // wait for canister to finish installing - retry_with_msg_async!( - format!( - "agent of {} observes canister module {}", - app_node.get_public_url().to_string(), - canister_id.to_string() - ), - &logger, - READY_WAIT_TIMEOUT, - RETRY_BACKOFF, - || async { - match agent_observes_canister_module(&agent_node, &canister_id).await { - true => Ok(()), - false => bail!("Canister module not available yet"), - } - } - ) + let mut rate_limit_canister = nns + .create_canister_at_id(PrincipalId(rate_limit_id)) .await .unwrap(); - info!(&logger, "installed rate-limit canister ({canister_id})"); - info!(&logger, "creating two agents with different access rights"); - let app_node_url = app_node.get_public_url().to_string(); - let agent_full_access = create_agent(full_access_identity, app_node_url.clone()).await; - let agent_restricted_read = create_agent(AnonymousIdentity {}, app_node_url).await; + let path_to_wasm = get_dependency_path( + env::var("RATE_LIMIT_CANISTER_WASM_PATH").expect("RATE_LIMIT_CANISTER_WASM_PATH not set"), + ); - info!(&logger, "Call 1. Add a new config (version = 2) containing some rules (FullAccess level of the caller is required)"); - add_config_1(logger.clone(), &agent_full_access, canister_id).await; + let wasm: Wasm = Wasm::from_file(path_to_wasm.clone()); - info!(&logger, "Call 2. Read config by privileged user (FullAccess or FullRead caller level). Response will expose rules/descriptions in their full form"); - let version = 2; - read_config(logger.clone(), &agent_full_access, version, canister_id).await; + let args = Encode!(&InitArg { + registry_polling_period_secs: 5, + authorized_principal: None, + }) + .unwrap(); - info!(&logger, "Call 3. Read config by non-privileged user (RestrictedRead level). Rules and descriptions are hidden in the response"); - let rule_ids = read_config(logger.clone(), &agent_restricted_read, version, canister_id).await; + info!( + &logger, + "Installing rate-limit canister wasm (with unset authorized_principal)..." + ); - info!(&logger, "Call 4. Inspect the metadata of a rule before its disclosure. Some metadata fields should be hidden"); - let rule_metadata = read_rule(logger.clone(), &agent_restricted_read, &rule_ids[2], canister_id).await; - assert!(rule_metadata.rule_raw.is_none()); - assert!(rule_metadata.description.is_none()); + install_rust_canister_from_path(&mut rate_limit_canister, path_to_wasm, Some(args)).await; - info!(&logger, "Call 5. Disclose two rules linked to one incident"); - let incident_id = "b97730ac-4879-47f2-9fea-daf20b8d4b64".to_string(); - disclose_incident(logger.clone(), &agent_full_access, incident_id, canister_id).await; + info!( + &logger, + "Rate-limit canister with id={rate_limit_id} installed successfully" + ); - info!(&logger, "Call 6. Read config by non-privileged user again. Now rules related to the disclosed incident are fully visible"); - let _ = read_config(logger.clone(), &agent_restricted_read, version, canister_id).await; + let root = Canister::new(&nns, ROOT_CANISTER_ID); - info!(&logger, "Call 7. Add another config (version = 3) with one newly added rule, and one remove rule"); - add_config_2(logger.clone(), &agent_full_access, canister_id).await; + // set the root principal as the controller of the canister + rate_limit_canister + .set_controller(ROOT_CANISTER_ID.into()) + .await + .unwrap(); - info!(&logger, "Call 8. Read config by privileged user (FullAccess or FullRead caller level). Response will expose rules/descriptions in their full form"); - let version = 3; - let _ = read_config(logger.clone(), &agent_full_access, version, canister_id).await; + info!(&logger, "Step 3. Installing counter canister ..."); - info!(&logger, "Call 9. Inspect the metadata of the removed rule. All metadata fields should be visible, including versions when the rule was added/removed"); - let rule_metadata = read_rule(logger.clone(), &agent_restricted_read, &rule_ids[2], canister_id).await; - assert!(rule_metadata.rule_raw.is_some()); - assert!(rule_metadata.description.is_some()); - assert_eq!(rule_metadata.added_in_version, 2); - assert_eq!(rule_metadata.removed_in_version, Some(3)); - }); -} + let counter_canister_id = install_canisters( + env.topology_snapshot(), + wat::parse_str(COUNTER_CANISTER_WAT).unwrap().as_slice(), + 1, + ) + .await[0]; -async fn create_agent(identity: I, domain: String) -> Agent { - let agent = Agent::builder() - .with_url(domain) - .with_identity(identity) - .build() - .expect("failed to build the agent"); - agent.fetch_root_key().await.unwrap(); - agent -} + info!( + &logger, + "Step 4. Create an `ic-agent` instance associated with an API boundary node" + ); -async fn add_config_1(logger: Logger, agent: &Agent, canister_id: Principal) { - // Note two rules (indices = [0, 2]) are linked to the same incident_id_1 - // RuleIds are generated randomly on the canister side - let rule_1 = RateLimitRule { - canister_id: Some(canister_id), - subnet_id: None, - methods_regex: Some(Regex::new(r"^(method_1)$").unwrap()), - request_types: Some(vec![RequestType::Call]), - limit: Action::Limit(1, Duration::from_secs(60)), + let api_bn_agent = { + let api_bn = env.topology_snapshot().api_boundary_nodes().next().unwrap(); + let api_bn_ipv6 = SocketAddr::new(api_bn.get_ip_addr(), 0); + let api_bn_domain = api_bn.get_domain().unwrap(); + let client = Client::builder() + .danger_accept_invalid_certs(true) + .resolve(&api_bn_domain, api_bn_ipv6) + .build() + .expect("Could not create HTTP client."); + let agent = Agent::builder() + .with_url(format!("https://{api_bn_domain}")) + .with_identity(full_access_identity.clone()) + .with_arc_http_middleware(Arc::new(HttpServiceNoRetry { client })) // do not use inbuilt retry logic for 429 responses + .build() + .unwrap(); + agent.fetch_root_key().await.unwrap(); + agent }; - let rule_2 = RateLimitRule { - canister_id: Some(canister_id), - subnet_id: None, - methods_regex: Some(Regex::new(r"^(method_2)$").unwrap()), - request_types: Some(vec![RequestType::Query]), - limit: Action::Limit(2, Duration::from_secs(60)), - }; + info!( + &logger, + "Step 5. Verify that agent can successfully interact with counter canister" + ); - let rule_3 = RateLimitRule { - canister_id: Some(canister_id), - subnet_id: None, - methods_regex: Some(Regex::new(r"^(method_3)$").unwrap()), - request_types: None, - limit: Action::Limit(3, Duration::from_secs(60)), - }; + api_bn_agent + .update(&counter_canister_id, "write") + .call_and_wait() + .await + .expect("failed to increment the counter on canister"); - let rule_4 = RateLimitRule { - canister_id: Some(canister_id), - subnet_id: None, - methods_regex: Some(Regex::new(r"^(method_4)$").unwrap()), - request_types: Some(vec![RequestType::ReadState]), - limit: Action::Limit(4, Duration::from_secs(60)), - }; + info!( + &logger, + "Step 6. Try to add two rate-limit rules that block requests to counter and rate-limit canisters" + ); - let args = Encode!(&InputConfig { - schema_version: 1, - rules: vec![ - InputRule { - incident_id: "b97730ac-4879-47f2-9fea-daf20b8d4b64".to_string(), - rule_raw: rule_1.to_bytes_json().unwrap(), - description: - "Some vulnerability #1 discovered, temporarily rate-limiting the canister calls" - .to_string(), - }, - InputRule { - incident_id: "f63c821c-9320-476a-bc89-94cb99d04639".to_string(), - rule_raw: rule_2.to_bytes_json().unwrap(), - description: "Some vulnerability #2 discovered".to_string(), - }, + let result = set_rate_limit_rules( + &api_bn_agent, + rate_limit_id, + vec![ InputRule { incident_id: "b97730ac-4879-47f2-9fea-daf20b8d4b64".to_string(), - rule_raw: rule_3.to_bytes_json().unwrap(), - description: - "Some vulnerability #1 discovered, temporarily rate-limiting the canister calls" - .to_string(), + rule_raw: RateLimitRule { + canister_id: Some(counter_canister_id), + limit: Action::Block, + ..Default::default() + } + .to_bytes_json() + .unwrap(), + description: "Block requests to counter canister".to_string(), }, InputRule { - incident_id: "389bbff8-bffa-4430-bb70-8ce1ea399c07".to_string(), - rule_raw: rule_4.to_bytes_json().unwrap(), - description: "Some vulnerability #3 discovered".to_string(), + incident_id: "34bb6dee-9646-4543-ba62-af546ea5565b".to_string(), + rule_raw: RateLimitRule { + canister_id: Some(rate_limit_id), + limit: Action::Block, + ..Default::default() + } + .to_bytes_json() + .unwrap(), + description: "Block requests to rate-limit canister".to_string(), }, ], - }) - .unwrap(); + ) + .await; - let result = agent - .update(&canister_id, "add_config") - .with_arg(args) - .call_and_wait() - .await - .unwrap(); + info!( + &logger, + "Step 7. Assert canister call fails (rejected), as authorized_principal is unset for the rate-limit canister", + ); - let decoded = Decode!(&result, AddConfigResponse).unwrap(); + assert!(result.unwrap_err().contains("reject")); - info!(&logger, "Response to add_config() call: {decoded:#?}"); -} + info!( + &logger, + "Step 8. Upgrade rate-limit canister code via proposal, specifying authorized_principal in the payload", + ); -async fn add_config_2(logger: Logger, agent: &Agent, canister_id: Principal) { - // This config differs from config 1 by rule_3 at index = 2, see comment below. - let rule_1 = RateLimitRule { - canister_id: Some(canister_id), - subnet_id: None, - methods_regex: Some(Regex::new(r"^(method_1)$").unwrap()), - request_types: Some(vec![RequestType::Call]), - limit: Action::Limit(1, Duration::from_secs(60)), - }; + let args = Encode!(&InitArg { + registry_polling_period_secs: 5, + authorized_principal: Some(full_access_principal), + }) + .unwrap(); - let rule_2 = RateLimitRule { - canister_id: Some(canister_id), - subnet_id: None, - methods_regex: Some(Regex::new(r"^(method_2)$").unwrap()), - request_types: Some(vec![RequestType::Query]), - limit: Action::Limit(2, Duration::from_secs(60)), - }; + // apply a no-impact WASM modification and reinstall the canister + let new_wasm = modify_wasm_bytes(wasm.bytes().as_slice(), 42); - // only this rule is different from config_1 - let rule_3 = RateLimitRule { - canister_id: Some(canister_id), - subnet_id: None, - methods_regex: Some(Regex::new(r"^(method_33)$").unwrap()), - request_types: None, - limit: Action::Limit(33, Duration::from_secs(60)), - }; + upgrade_nns_canister_by_proposal( + &rate_limit_canister, + &Canister::new(&nns, GOVERNANCE_CANISTER_ID), + &root, + true, + Wasm::from_bytes(new_wasm), + Some(args), + ) + .await; - let rule_4 = RateLimitRule { - canister_id: Some(canister_id), - subnet_id: None, - methods_regex: Some(Regex::new(r"^(method_4)$").unwrap()), - request_types: Some(vec![RequestType::ReadState]), - limit: Action::Limit(4, Duration::from_secs(60)), - }; + info!( + &logger, + "Step 9. Assert adding two rate-limit rules to the rate-limit canister now succeeds" + ); - let args = Encode!(&InputConfig { - schema_version: 1, - rules: vec![ + set_rate_limit_rules( + &api_bn_agent, + rate_limit_id, + vec![ InputRule { incident_id: "b97730ac-4879-47f2-9fea-daf20b8d4b64".to_string(), - rule_raw: rule_1.to_bytes_json().unwrap(), - description: - "Some vulnerability #1 discovered, temporarily rate-limiting the canister calls" - .to_string(), - }, - InputRule { - incident_id: "f63c821c-9320-476a-bc89-94cb99d04639".to_string(), - rule_raw: rule_2.to_bytes_json().unwrap(), - description: "Some vulnerability #2 discovered".to_string(), - }, - // Only this rule is different from config 1, it also has another incident_id and description - // It means that the old rule is removed (not mutated) and this new rule is applied instead. - InputRule { - incident_id: "ebe7dbb1-63c9-420e-980d-eb0f8c20a9fb".to_string(), - rule_raw: rule_3.to_bytes_json().unwrap(), - description: "Some vulnerability #4 discovered".to_string(), + rule_raw: RateLimitRule { + canister_id: Some(counter_canister_id), + limit: Action::Block, + ..Default::default() + } + .to_bytes_json() + .unwrap(), + description: "Block requests to counter canister".to_string(), }, InputRule { - incident_id: "389bbff8-bffa-4430-bb70-8ce1ea399c07".to_string(), - rule_raw: rule_4.to_bytes_json().unwrap(), - description: "Some vulnerability #3 discovered".to_string(), + incident_id: "34bb6dee-9646-4543-ba62-af546ea5565b".to_string(), + rule_raw: RateLimitRule { + canister_id: Some(rate_limit_id), + limit: Action::Block, + ..Default::default() + } + .to_bytes_json() + .unwrap(), + description: "Block requests to rate-limit canister".to_string(), }, ], - }) + ) + .await .unwrap(); - let result = agent - .update(&canister_id, "add_config") - .with_arg(args) - .call_and_wait() - .await - .unwrap(); - - let decoded = Decode!(&result, AddConfigResponse).unwrap(); - - info!(&logger, "Response to add_config() call: {decoded:#?}"); -} + info!( + &logger, + "Step 10. Verify that the api_bn_agent can no longer send requests to the counter canister" + ); -async fn read_config( - logger: Logger, - agent: &Agent, - version: Version, - canister_id: Principal, -) -> Vec { - let args = Encode!(&Some(version)).unwrap(); + retry_with_msg_async!( + "check_counter_canister_becomes_unreachable".to_string(), + &logger, + Duration::from_secs(180), + Duration::from_secs(5), + || async { + match api_bn_agent + .update(&counter_canister_id, "write") + .call() + .await + { + Ok(_) => { + bail!("counter canister is still reachable, retrying"); + } + Err(error) => { + // We should observe 403 http error, as all requests are blocked + if let AgentError::HttpError(ref payload) = error { + if payload.status == 403 { + return Ok(()); + } + } + bail!("update call failed with unexpected error: {error:?}"); + } + } + } + ) + .await + .expect("failed to check that canister becomes unreachable"); - let response = agent - .query(&canister_id, "get_config") - .with_arg(args) - .call() - .await - .expect("update call failed"); + info!( + &logger, + "Step 11. Add a rate-limit rule, which unblocks requests to the counter canister" + ); - let decoded = Decode!(&response, GetConfigResponse) - .expect("failed to decode candid response") - .unwrap(); + // api_bn_agent can't communicate with canister after blocking, hence we use nns_agent + let mut nns_agent = nns_node.build_default_agent_async().await; + nns_agent.set_identity(full_access_identity); + + set_rate_limit_rules( + &nns_agent, + rate_limit_id, + vec![InputRule { + incident_id: "e6a27788-01a5-444a-9035-ab3af3ad84f3".to_string(), + rule_raw: RateLimitRule { + canister_id: Some(counter_canister_id), + limit: Action::Limit(300, Duration::from_secs(60)), + ..Default::default() + } + .to_bytes_json() + .unwrap(), + description: "Unblock requests to the counter canister".to_string(), + }], + ) + .await + .unwrap(); - info!(&logger, "Response to get_config() call: {}", decoded); + info!( + &logger, + "Step 12. Verify that agent can send requests to the counter canister again" + ); - decoded - .config - .rules - .into_iter() - .map(|rule| rule.id) - .collect() + retry_with_msg_async!( + "check_counter_canister_becomes_reachable".to_string(), + &logger, + Duration::from_secs(180), + Duration::from_secs(5), + || async { + match api_bn_agent + .update(&counter_canister_id, "write") + .call() + .await + { + Ok(response) => { + info!(&logger, "update call succeeded with response: {response:?}"); + Ok(()) + } + Err(error) => { + info!(&logger, "update call failed with error: {error:?}"); + bail!("counter canister is still unreachable, retrying"); + } + } + } + ) + .await + .expect("failed to check that canister becomes reachable"); } -async fn disclose_incident( - logger: Logger, +async fn set_rate_limit_rules( agent: &Agent, - incident_id: IncidentId, - canister_id: Principal, -) { - let disclose_arg = DiscloseRulesArg::IncidentIds(vec![incident_id]); - let args = Encode!(&disclose_arg).unwrap(); - - let response = agent - .update(&canister_id, "disclose_rules") + rate_limit_canister_id: Principal, + rules: Vec, +) -> Result<(), String> { + let args = Encode!(&InputConfig { + schema_version: 1, + rules, + }) + .unwrap(); + + let result = agent + .update(&rate_limit_canister_id, "add_config") .with_arg(args) .call_and_wait() .await - .expect("update call failed"); + .map_err(|err| err.to_string())?; - let decoded = Decode!(&response, DiscloseRulesResponse).unwrap(); - - info!(&logger, "Response to disclose_rules() call: {decoded:#?}"); + Decode!(&result, AddConfigResponse) + .expect("failed to deserialize response") + .map_err(|err| format!("{err:?}")) } -async fn read_rule( - logger: Logger, - agent: &Agent, - rule_id: &RuleId, - canister_id: Principal, -) -> OutputRuleMetadata { - let args = Encode!(rule_id).unwrap(); - - let response = agent - .query(&canister_id, "get_rule_by_id") - .with_arg(args) - .call() - .await - .expect("update call failed"); - - let rule_metadata = Decode!(&response, GetRuleByIdResponse).unwrap().unwrap(); - - info!( - &logger, - "Response to get_rule_by_id() call: {rule_metadata}" - ); - - rule_metadata +fn test(env: TestEnv) { + let rt = Runtime::new().expect("Could not create tokio runtime"); + rt.block_on(test_async(env)); } fn main() -> Result<()> { SystemTestGroup::new() .with_setup(setup) - .add_parallel(SystemTestSubGroup::new().add_test(systest!(complete_flow_test))) + .add_test(systest!(test)) .execute_from_args()?; Ok(()) } + +// The default HttpService in ic-agent retries on 429 errors, but we expect these and don't want retries. +#[derive(Debug)] +struct HttpServiceNoRetry { + client: Client, +} + +#[async_trait] +impl HttpService for HttpServiceNoRetry { + async fn call<'a>( + &'a self, + req: &'a (dyn Fn() -> Result + Send + Sync), + _max_tcp_retries: usize, + ) -> Result { + Ok(self.client.call(req, _max_tcp_retries).await?) + } +} diff --git a/rs/tests/ckbtc/BUILD.bazel b/rs/tests/ckbtc/BUILD.bazel index 2c1878d5190..dd9ae2b656d 100644 --- a/rs/tests/ckbtc/BUILD.bazel +++ b/rs/tests/ckbtc/BUILD.bazel @@ -98,7 +98,6 @@ system_test_nns( }, flaky = True, tags = [ - "k8s", "long_test", # since it takes longer than 5 minutes. ], target_compatible_with = ["@platforms//os:linux"], # requires libssh that does not build on Mac OS @@ -129,7 +128,6 @@ system_test_nns( extra_head_nns_tags = [], # don't run the head_nns variant on nightly since it aleady runs on long_test. flaky = True, tags = [ - "k8s", "long_test", # since it takes longer than 5 minutes. ], target_compatible_with = ["@platforms//os:linux"], # requires libssh that does not build on Mac OS @@ -160,7 +158,6 @@ system_test_nns( extra_head_nns_tags = [], # don't run the head_nns variant on nightly since it aleady runs on long_test. flaky = True, tags = [ - "k8s", "long_test", # since it takes longer than 5 minutes. ], target_compatible_with = ["@platforms//os:linux"], # requires libssh that does not build on Mac OS @@ -191,7 +188,6 @@ system_test_nns( extra_head_nns_tags = [], # don't run the head_nns variant on nightly since it aleady runs on long_test. flaky = True, tags = [ - "k8s", "long_test", # since it takes longer than 5 minutes. ], target_compatible_with = ["@platforms//os:linux"], # requires libssh that does not build on Mac OS @@ -222,7 +218,6 @@ system_test_nns( extra_head_nns_tags = [], # don't run the head_nns variant on nightly since it aleady runs on long_test. flaky = True, tags = [ - "k8s", "long_test", # since it takes longer than 5 minutes. ], target_compatible_with = ["@platforms//os:linux"], # requires libssh that does not build on Mac OS diff --git a/rs/tests/ckbtc/Cargo.toml b/rs/tests/ckbtc/Cargo.toml index d7156f4b708..e23131167b0 100644 --- a/rs/tests/ckbtc/Cargo.toml +++ b/rs/tests/ckbtc/Cargo.toml @@ -9,7 +9,7 @@ documentation.workspace = true [dependencies] anyhow = { workspace = true } assert_matches = { workspace = true } -bitcoincore-rpc = "0.15.0" +bitcoincore-rpc = { workspace = true } candid = { workspace = true } canister-test = { path = "../../rust_canisters/canister_test" } dfn_candid = { path = "../../rust_canisters/dfn_candid" } diff --git a/rs/tests/ckbtc/ckbtc_minter_batching.rs b/rs/tests/ckbtc/ckbtc_minter_batching.rs index a8f23e13fd3..9f439bf2358 100644 --- a/rs/tests/ckbtc/ckbtc_minter_batching.rs +++ b/rs/tests/ckbtc/ckbtc_minter_batching.rs @@ -61,7 +61,10 @@ pub fn test_batching(env: TestEnv) { let btc_rpc = get_btc_client(&env); ensure_wallet(&btc_rpc, &logger); - let default_btc_address = btc_rpc.get_new_address(None, None).unwrap(); + let default_btc_address = btc_rpc + .get_new_address(None, None) + .unwrap() + .assume_checked(); // Creating the 101 first block to reach the min confirmations to spend a coinbase utxo. debug!( &logger, @@ -163,7 +166,10 @@ pub fn test_batching(env: TestEnv) { "Transfer to the minter account occurred at block {}", transfer_result ); - let destination_btc_address = btc_rpc.get_new_address(None, None).unwrap(); + let destination_btc_address = btc_rpc + .get_new_address(None, None) + .unwrap() + .assume_checked(); info!(&logger, "Call retrieve_btc"); @@ -239,7 +245,7 @@ pub fn test_batching(env: TestEnv) { // Let's wait for the transaction to appear on the mempool let mempool_txids = wait_for_mempool_change(&btc_rpc, &logger).await; let txid = mempool_txids[0]; - let btc_txid = Txid::from_hash(Hash::from_slice(&txid).unwrap()); + let btc_txid = Txid::from_raw_hash(Hash::from_slice(&txid[..]).unwrap()); // Check if we have the txid in the bitcoind mempool assert!( mempool_txids.contains(&btc_txid), @@ -262,7 +268,7 @@ pub fn test_batching(env: TestEnv) { // Hence, we expect the fee to be 3650 // By checking the fee we know that we have the right amount of inputs and outputs const EXPECTED_FEE: u64 = 3650; - assert_eq!(get_tx_infos.fees.base.as_sat(), EXPECTED_FEE); + assert_eq!(get_tx_infos.fees.base.to_sat(), EXPECTED_FEE); // Check that we can modify the fee assert!(get_tx_infos.bip125_replaceable); @@ -278,7 +284,7 @@ pub fn test_batching(env: TestEnv) { let finalized_txid = wait_for_finalization_no_new_blocks(&minter_agent, block_indexes[0]).await; // We don't need to check which input has been used as there is only one input in the possession of the minter - let txid_array: [u8; 32] = txid.as_hash().to_vec().try_into().unwrap(); + let txid_array: [u8; 32] = txid[..].to_vec().try_into().unwrap(); assert_eq!(ic_btc_interface::Txid::from(txid_array), finalized_txid); // We can now check that the destination_btc_address received some utxos @@ -293,7 +299,7 @@ pub fn test_batching(env: TestEnv) { .expect("failed to get tx infos"); let destination_balance = unspent_result .iter() - .map(|entry| entry.amount.as_sat()) + .map(|entry| entry.amount.to_sat()) .sum::(); // We have 1 input and 21 outputs (20 requests and the minter's address) diff --git a/rs/tests/ckbtc/ckbtc_minter_checker.rs b/rs/tests/ckbtc/ckbtc_minter_checker.rs index 1dc74182214..c3c8a096953 100644 --- a/rs/tests/ckbtc/ckbtc_minter_checker.rs +++ b/rs/tests/ckbtc/ckbtc_minter_checker.rs @@ -49,7 +49,10 @@ pub fn test_btc_checker(env: TestEnv) { // Create wallet if required. ensure_wallet(&btc_rpc, &logger); - let default_btc_address = btc_rpc.get_new_address(None, None).unwrap(); + let default_btc_address = btc_rpc + .get_new_address(None, None) + .unwrap() + .assume_checked(); // Creating the 101 first block to reach the min confirmations to spend a coinbase utxo. debug!( &logger, diff --git a/rs/tests/ckbtc/ckbtc_minter_deposit_and_withdrawal.rs b/rs/tests/ckbtc/ckbtc_minter_deposit_and_withdrawal.rs index 64b0f6a77ee..f103058d6e9 100644 --- a/rs/tests/ckbtc/ckbtc_minter_deposit_and_withdrawal.rs +++ b/rs/tests/ckbtc/ckbtc_minter_deposit_and_withdrawal.rs @@ -41,7 +41,10 @@ pub fn test_deposit_and_withdrawal(env: TestEnv) { let btc_rpc = get_btc_client(&env); ensure_wallet(&btc_rpc, &logger); - let default_btc_address = btc_rpc.get_new_address(None, None).unwrap(); + let default_btc_address = btc_rpc + .get_new_address(None, None) + .unwrap() + .assume_checked(); // Creating the 101 first block to reach the min confirmations to spend a coinbase utxo. debug!( &logger, @@ -128,7 +131,10 @@ pub fn test_deposit_and_withdrawal(env: TestEnv) { "Transfer to the minter account occurred at block {}", transfer_result ); - let destination_btc_address = btc_rpc.get_new_address(None, None).unwrap(); + let destination_btc_address = btc_rpc + .get_new_address(None, None) + .unwrap() + .assume_checked(); info!(&logger, "Call retrieve_btc"); @@ -165,7 +171,7 @@ pub fn test_deposit_and_withdrawal(env: TestEnv) { // We wait for the heartbeat to send the transaction to the mempool info!(&logger, "Waiting for tx to appear in mempool"); let mempool_txids = wait_for_mempool_change(&btc_rpc, &logger).await; - let btc_txid = Txid::from_hash(Hash::from_slice(&txid_bytes).unwrap()); + let btc_txid = Txid::from_raw_hash(Hash::from_slice(&txid_bytes).unwrap()); // Check if we have the txid in the bitcoind mempool assert!( mempool_txids.contains(&btc_txid), @@ -187,7 +193,7 @@ pub fn test_deposit_and_withdrawal(env: TestEnv) { // - a fee of 5 satoshis/vbytes // Hence a total fee of 705 satoshis const EXPECTED_FEE: u64 = 705; - assert_eq!(get_tx_infos.fees.base.as_sat(), EXPECTED_FEE); + assert_eq!(get_tx_infos.fees.base.to_sat(), EXPECTED_FEE); // Check that we can modify the fee assert!(get_tx_infos.bip125_replaceable); diff --git a/rs/tests/ckbtc/ckbtc_minter_retrieve_btc.rs b/rs/tests/ckbtc/ckbtc_minter_retrieve_btc.rs index 58f32badf37..5879cd9ec75 100644 --- a/rs/tests/ckbtc/ckbtc_minter_retrieve_btc.rs +++ b/rs/tests/ckbtc/ckbtc_minter_retrieve_btc.rs @@ -45,11 +45,15 @@ pub fn test_retrieve_btc(env: TestEnv) { let btc_rpc = get_btc_client(&env); ensure_wallet(&btc_rpc, &logger); - let default_btc_address = btc_rpc.get_new_address(None, None).unwrap(); + let default_btc_address = btc_rpc + .get_new_address(None, None) + .unwrap() + .assume_checked(); // Creating the 10 first block to reach the min confirmations of the minter canister. debug!( &logger, - "Generating 10 blocks to default address: {}", &default_btc_address + "Generating 10 blocks to default address: {}", + &default_btc_address.to_string() ); btc_rpc .generate_to_address(10, &default_btc_address) diff --git a/rs/tests/ckbtc/ckbtc_minter_update_balance.rs b/rs/tests/ckbtc/ckbtc_minter_update_balance.rs index e0419c7b2cf..d00fbf11f01 100644 --- a/rs/tests/ckbtc/ckbtc_minter_update_balance.rs +++ b/rs/tests/ckbtc/ckbtc_minter_update_balance.rs @@ -55,7 +55,10 @@ pub fn test_update_balance(env: TestEnv) { // Create wallet if required. ensure_wallet(&btc_rpc, &logger); - let default_btc_address = btc_rpc.get_new_address(None, None).unwrap(); + let default_btc_address = btc_rpc + .get_new_address(None, None) + .unwrap() + .assume_checked(); // Creating the 10 first block to reach the min confirmations of the minter canister. debug!( &logger, diff --git a/rs/tests/ckbtc/src/lib.rs b/rs/tests/ckbtc/src/lib.rs index 93746c04726..2208f1603d7 100644 --- a/rs/tests/ckbtc/src/lib.rs +++ b/rs/tests/ckbtc/src/lib.rs @@ -284,7 +284,7 @@ async fn execute_update_subnet_proposal( ) .await; let proposal_result = vote_and_execute_proposal(governance, proposal_id).await; - assert_eq!(proposal_result.status(), ProposalStatus::Executed); + assert_eq!(proposal_result.status, ProposalStatus::Executed as i32); } fn empty_subnet_update() -> UpdateSubnetPayload { @@ -524,7 +524,7 @@ pub async fn install_bitcoin_canister_with_network( bitcoin_canister.canister_id() } -pub async fn install_icrc1_ledger<'a>(canister: &mut Canister<'a>, args: &LedgerArgument) { +pub async fn install_icrc1_ledger(canister: &mut Canister<'_>, args: &LedgerArgument) { install_rust_canister_from_path( canister, get_dependency_path(env::var("LEDGER_WASM_PATH").expect("LEDGER_WASM_PATH not set")), diff --git a/rs/tests/ckbtc/src/utils.rs b/rs/tests/ckbtc/src/utils.rs index 25b2674f871..abcb645bc29 100644 --- a/rs/tests/ckbtc/src/utils.rs +++ b/rs/tests/ckbtc/src/utils.rs @@ -85,8 +85,8 @@ pub fn generate_blocks(btc_client: &Client, logger: &Logger, nb_blocks: u64, add /// Wait for the expected balance to be available at the given btc address. /// Timeout after SHORT_TIMEOUT if the expected balance is not reached. -pub async fn wait_for_bitcoin_balance<'a>( - canister: &UniversalCanister<'a>, +pub async fn wait_for_bitcoin_balance( + canister: &UniversalCanister<'_>, logger: &Logger, expected_balance_in_satoshis: u64, btc_address: &Address, @@ -107,7 +107,7 @@ pub async fn wait_for_bitcoin_balance<'a>( /// Wait for the expected balance to be available at the given account. /// Timeout after SHORT_TIMEOUT if the expected balance is not reached. -pub async fn wait_for_ledger_balance<'a>( +pub async fn wait_for_ledger_balance( ledger_agent: &Icrc1Agent, logger: &Logger, expected_balance: Nat, @@ -382,7 +382,7 @@ pub async fn get_btc_address( debug!(logger, "Btc address for subaccount is: {}", address); // Checking only proper format of address since ECDSA signature is non-deterministic. assert_eq!(ADDRESS_LENGTH, address.len()); - address.parse().unwrap() + address.parse::>().unwrap().assume_checked() } pub async fn send_to_btc_address(btc_rpc: &Client, logger: &Logger, dst: &Address, amount: u64) { @@ -422,10 +422,7 @@ pub fn get_btc_client(env: &TestEnv) -> Client { .unwrap() } -pub async fn get_bitcoin_balance<'a>( - canister: &UniversalCanister<'a>, - btc_address: &Address, -) -> u64 { +pub async fn get_bitcoin_balance(canister: &UniversalCanister<'_>, btc_address: &Address) -> u64 { canister .update(wasm().call(management::bitcoin_get_balance( btc_address.to_string(), diff --git a/rs/tests/consensus/BUILD.bazel b/rs/tests/consensus/BUILD.bazel index fa75039ba48..60400c6c4e5 100644 --- a/rs/tests/consensus/BUILD.bazel +++ b/rs/tests/consensus/BUILD.bazel @@ -27,7 +27,7 @@ system_test( # TODO(NET-1683): Adjust test for faster p2p tags = [ "k8s", - "system_test_hourly", + "long_test", ], target_compatible_with = ["@platforms//os:linux"], # requires libssh that does not build on Mac OS test_timeout = "eternal", @@ -49,7 +49,7 @@ system_test( # TODO(NET-1683): Adjust test for faster p2p tags = [ "k8s", - "system_test_hourly", + "long_test", ], target_compatible_with = ["@platforms//os:linux"], # requires libssh that does not build on Mac OS uses_guestos_dev = True, @@ -328,7 +328,9 @@ system_test_nns( ], target_compatible_with = ["@platforms//os:linux"], # requires libssh that does not build on Mac OS test_timeout = "eternal", - runtime_deps = GUESTOS_RUNTIME_DEPS + GRAFANA_RUNTIME_DEPS + COUNTER_CANISTER_RUNTIME_DEPS, + runtime_deps = GUESTOS_RUNTIME_DEPS + GRAFANA_RUNTIME_DEPS + COUNTER_CANISTER_RUNTIME_DEPS + [ + "//rs/tests:jaeger_uvm_config_image", + ], deps = [ # Keep sorted. "//rs/registry/subnet_type", diff --git a/rs/tests/consensus/backup/BUILD.bazel b/rs/tests/consensus/backup/BUILD.bazel index 31b6987208e..53720a8d63b 100644 --- a/rs/tests/consensus/backup/BUILD.bazel +++ b/rs/tests/consensus/backup/BUILD.bazel @@ -29,10 +29,20 @@ rust_library( BACKUP_RUNTIME_DEPS = UNIVERSAL_CANISTER_RUNTIME_DEPS + [ # Keep sorted. - "//rs/tests:backup/binaries", + "//rs/backup:ic-backup", + "//rs/canister_sandbox:canister_sandbox", + "//rs/canister_sandbox:compiler_sandbox", + "//rs/canister_sandbox:sandbox_launcher", + "//rs/replay:ic-replay", ] -BACKUP_ENV = UNIVERSAL_CANISTER_ENV +BACKUP_ENV = UNIVERSAL_CANISTER_ENV | { + "IC_BACKUP_PATH": "$(rootpath //rs/backup:ic-backup)", + "IC_REPLAY_PATH": "$(rootpath //rs/replay:ic-replay)", + "COMPILER_SANDBOX_PATH": "$(rootpath //rs/canister_sandbox:compiler_sandbox)", + "SANDBOX_LAUNCHER_PATH": "$(rootpath //rs/canister_sandbox:sandbox_launcher)", + "CANISTER_SANDBOX_PATH": "$(rootpath //rs/canister_sandbox:canister_sandbox)", +} system_test_nns( name = "backup_manager_downgrade_test", @@ -44,6 +54,7 @@ system_test_nns( "long_test", # since it takes longer than 5 minutes. ], target_compatible_with = ["@platforms//os:linux"], # requires libssh that does not build on Mac OS + test_timeout = "eternal", # this test often times out with the default 15 minute timeout so we allow more time uses_guestos_dev_test = True, runtime_deps = GUESTOS_RUNTIME_DEPS + @@ -68,6 +79,7 @@ system_test_nns( "long_test", # since it takes longer than 5 minutes. ], target_compatible_with = ["@platforms//os:linux"], # requires libssh that does not build on Mac OS + test_timeout = "eternal", # this test often times out with the default 15 minute timeout so we allow more time uses_guestos_dev_test = True, runtime_deps = GUESTOS_RUNTIME_DEPS + diff --git a/rs/tests/consensus/backup/backup_manager_downgrade_test.rs b/rs/tests/consensus/backup/backup_manager_downgrade_test.rs index 7cabb0ee90c..9af6ee77fbd 100644 --- a/rs/tests/consensus/backup/backup_manager_downgrade_test.rs +++ b/rs/tests/consensus/backup/backup_manager_downgrade_test.rs @@ -8,7 +8,7 @@ use std::time::Duration; fn main() -> Result<()> { SystemTestGroup::new() .with_setup(setup_downgrade) - .with_timeout_per_test(Duration::from_secs(15 * 60)) + .with_timeout_per_test(Duration::from_secs(25 * 60)) .add_test(systest!(test_downgrade)) .execute_from_args()?; diff --git a/rs/tests/consensus/backup/backup_manager_upgrade_test.rs b/rs/tests/consensus/backup/backup_manager_upgrade_test.rs index d2b440138a8..bde1b83dbb5 100644 --- a/rs/tests/consensus/backup/backup_manager_upgrade_test.rs +++ b/rs/tests/consensus/backup/backup_manager_upgrade_test.rs @@ -8,7 +8,7 @@ use std::time::Duration; fn main() -> Result<()> { SystemTestGroup::new() .with_setup(setup_upgrade) - .with_timeout_per_test(Duration::from_secs(15 * 60)) + .with_timeout_per_test(Duration::from_secs(25 * 60)) .add_test(systest!(test_upgrade)) .execute_from_args()?; diff --git a/rs/tests/consensus/backup/common.rs b/rs/tests/consensus/backup/common.rs index 79192665cda..5ad31158aef 100644 --- a/rs/tests/consensus/backup/common.rs +++ b/rs/tests/consensus/backup/common.rs @@ -181,12 +181,32 @@ fn test(env: TestEnv, binary_version: String, target_version: String) { fs::create_dir_all(&backup_binaries_dir).expect("failure creating backup binaries directory"); // Copy all the binaries needed for the replay of the current version in order to avoid downloading them - let testing_dir = get_dependency_path("rs/tests"); - let binaries_path = testing_dir.join("backup/binaries"); - copy_file(&binaries_path, &backup_binaries_dir, "ic-replay"); - copy_file(&binaries_path, &backup_binaries_dir, "sandbox_launcher"); - copy_file(&binaries_path, &backup_binaries_dir, "canister_sandbox"); - copy_file(&binaries_path, &backup_binaries_dir, "compiler_sandbox"); + copy_file( + &get_dependency_path(std::env::var("IC_REPLAY_PATH").expect("IC_REPLAY_PATH not set")), + &backup_binaries_dir, + "ic-replay", + ); + copy_file( + &get_dependency_path( + std::env::var("SANDBOX_LAUNCHER_PATH").expect("SANDBOX_LAUNCHER_PATH not set"), + ), + &backup_binaries_dir, + "sandbox_launcher", + ); + copy_file( + &get_dependency_path( + std::env::var("CANISTER_SANDBOX_PATH").expect("CANISTER_SANDBOX_PATH not set"), + ), + &backup_binaries_dir, + "canister_sandbox", + ); + copy_file( + &get_dependency_path( + std::env::var("COMPILER_SANDBOX_PATH").expect("COMPILER_SANDBOX_PATH not set"), + ), + &backup_binaries_dir, + "compiler_sandbox", + ); // Generate keypair and store the private key info!(log, "Create backup user credentials"); @@ -282,8 +302,9 @@ fn test(env: TestEnv, binary_version: String, target_version: String) { write!(f, "{}", config_str).expect("Should be able to write the config file"); info!(log, "Start the backup process in a separate thread"); - let ic_backup_path = binaries_path.join("ic-backup"); - let mut command = Command::new(&ic_backup_path); + let ic_backup_path = + &get_dependency_path(std::env::var("IC_BACKUP_PATH").expect("IC_BACKUP_PATH not set")); + let mut command = Command::new(ic_backup_path); command .arg("--config-file") .arg(&config_file) @@ -362,6 +383,7 @@ fn test(env: TestEnv, binary_version: String, target_version: String) { "Restart and wait for cold storage and divergence to happen" ); child.kill().expect("Error killing backup process"); + child.wait().expect("Error waiting for backup process"); let checkpoint = some_checkpoint_dir(&backup_dir, &subnet_id).expect("Checkpoint doesn't exist"); @@ -379,7 +401,7 @@ fn test(env: TestEnv, binary_version: String, target_version: String) { modify_byte_in_file(memory_artifact_path).expect("Modifying a byte failed"); info!(log, "Start again the backup process in a separate thread"); - let mut command = Command::new(&ic_backup_path); + let mut command = Command::new(ic_backup_path); command .arg("--config-file") .arg(&config_file) @@ -432,6 +454,7 @@ fn test(env: TestEnv, binary_version: String, target_version: String) { info!(log, "Kill child process"); child.kill().expect("Error killing backup process"); + child.wait().expect("Error waiting for backup process"); assert!(hash_mismatch); info!(log, "There was a divergence of the state"); @@ -498,12 +521,8 @@ fn dir_exists_and_have_file(log: &Logger, dir: &PathBuf) -> bool { have_file } -fn copy_file(binaries_path: &Path, backup_binaries_dir: &Path, file_name: &str) { - fs::copy( - binaries_path.join(file_name), - backup_binaries_dir.join(file_name), - ) - .expect("failed to copy file"); +fn copy_file(binary_path: &Path, backup_binaries_dir: &Path, file_name: &str) { + fs::copy(binary_path, backup_binaries_dir.join(file_name)).expect("failed to copy file"); } fn highest_dir_entry(dir: &PathBuf, radix: u32) -> u64 { diff --git a/rs/tests/consensus/catch_up_test_common/src/lib.rs b/rs/tests/consensus/catch_up_test_common/src/lib.rs index 8470db27b08..55e0dfb101f 100644 --- a/rs/tests/consensus/catch_up_test_common/src/lib.rs +++ b/rs/tests/consensus/catch_up_test_common/src/lib.rs @@ -1,4 +1,4 @@ -/// Common test function for a couple of catch up tests. +//! Common test function for a couple of catch up tests. const DKG_INTERVAL: u64 = 150; const CATCH_UP_RETRIES: u64 = 40; diff --git a/rs/tests/consensus/consensus_performance.rs b/rs/tests/consensus/consensus_performance.rs index 23e786f258b..255448b07f0 100644 --- a/rs/tests/consensus/consensus_performance.rs +++ b/rs/tests/consensus/consensus_performance.rs @@ -45,7 +45,7 @@ // // Happy testing! -use ic_consensus_system_test_utils::performance::persist_metrics; +use ic_consensus_system_test_utils::performance::{persist_metrics, setup_jaeger_vm}; use ic_consensus_system_test_utils::rw_message::install_nns_with_customizations_and_check_progress; use ic_registry_subnet_type::SubnetType; use ic_system_test_driver::driver::group::SystemTestGroup; @@ -74,17 +74,33 @@ const NODES_COUNT: usize = 13; const DKG_INTERVAL: u64 = 999; // Network parameters const BANDWIDTH_MBITS: u32 = 300; // artificial cap on bandwidth -const LATENCY: Duration = Duration::from_millis(200); // artificial added latency +const LATENCY: Duration = Duration::from_millis(150); // artificial added latency const NETWORK_SIMULATION: FixedNetworkSimulation = FixedNetworkSimulation::new() .with_latency(LATENCY) .with_bandwidth(BANDWIDTH_MBITS); +/// When set to `true` a [Jaeger](https://www.jaegertracing.io/) instance will be spawned. +/// Look for "Jaeger frontend available at: $URL" in the logs and follow the link to visualize & +/// analyze traces. +const SHOULD_SPAWN_JAEGER_VM: bool = false; + fn setup(env: TestEnv) { PrometheusVm::default() .with_required_host_features(vec![HostFeature::Performance]) .start(&env) .expect("Failed to start prometheus VM"); - InternetComputer::new() + + let mut ic_builder = InternetComputer::new(); + + if SHOULD_SPAWN_JAEGER_VM { + let jaeger_ipv6 = setup_jaeger_vm(&env); + ic_builder = ic_builder.with_jaeger_addr(std::net::SocketAddr::new( + std::net::IpAddr::V6(jaeger_ipv6), + 4317, + )); + } + + ic_builder .with_required_host_features(vec![HostFeature::Performance]) .add_subnet( Subnet::new(SubnetType::System) @@ -153,6 +169,9 @@ fn test(env: TestEnv, message_size: usize, rps: f64) { test_metrics, message_size, rps, + LATENCY, + BANDWIDTH_MBITS * 1_000_000, + NODES_COUNT, &logger, )); } @@ -166,6 +185,10 @@ fn test_small_messages(env: TestEnv) { test(env, 4_000, 500.0) } +fn test_few_large_messages(env: TestEnv) { + test(env, 1_999_000, 1.0) +} + fn test_large_messages(env: TestEnv) { test(env, 950_000, 4.0) } @@ -178,6 +201,7 @@ fn main() -> Result<()> { .with_setup(setup) .add_test(systest!(test_few_small_messages)) .add_test(systest!(test_small_messages)) + .add_test(systest!(test_few_large_messages)) .add_test(systest!(test_large_messages)) .execute_from_args()?; Ok(()) diff --git a/rs/tests/consensus/orchestrator/BUILD.bazel b/rs/tests/consensus/orchestrator/BUILD.bazel index 44f4383e074..2c1e8a9b7f0 100644 --- a/rs/tests/consensus/orchestrator/BUILD.bazel +++ b/rs/tests/consensus/orchestrator/BUILD.bazel @@ -120,7 +120,7 @@ system_test_nns( flaky = True, tags = [ "k8s", - "system_test_hourly", + "long_test", ], target_compatible_with = ["@platforms//os:linux"], uses_guestos_dev_test = True, diff --git a/rs/tests/consensus/orchestrator/rotate_ecdsa_idkg_key_test.rs b/rs/tests/consensus/orchestrator/rotate_ecdsa_idkg_key_test.rs index 69bf064dea9..86a5eb0aa8c 100644 --- a/rs/tests/consensus/orchestrator/rotate_ecdsa_idkg_key_test.rs +++ b/rs/tests/consensus/orchestrator/rotate_ecdsa_idkg_key_test.rs @@ -242,7 +242,7 @@ fn test(env: TestEnv) { assert!(last_rotation .duration_since(first_rotation) - .map_or(false, |d| d + gamma <= delta)); + .is_ok_and(|d| d + gamma <= delta)); // Ensure signing still works for (key_id, public_key) in public_keys { diff --git a/rs/tests/consensus/orchestrator/unstuck_subnet_test.rs b/rs/tests/consensus/orchestrator/unstuck_subnet_test.rs index 97cb79cd63b..6759722b0c9 100644 --- a/rs/tests/consensus/orchestrator/unstuck_subnet_test.rs +++ b/rs/tests/consensus/orchestrator/unstuck_subnet_test.rs @@ -205,7 +205,7 @@ fn test(test_env: TestEnv) { fn have_sha_errors(session: &Session) -> bool { let cmd = "journalctl | grep -c 'FileHashMismatchError'".to_string(); - execute_bash_command(session, cmd).map_or(false, |res| res.trim().parse::().unwrap() > 0) + execute_bash_command(session, cmd).is_ok_and(|res| res.trim().parse::().unwrap() > 0) } fn main() -> Result<()> { diff --git a/rs/tests/consensus/subnet_recovery/BUILD.bazel b/rs/tests/consensus/subnet_recovery/BUILD.bazel index 48a0782b351..47307008d88 100644 --- a/rs/tests/consensus/subnet_recovery/BUILD.bazel +++ b/rs/tests/consensus/subnet_recovery/BUILD.bazel @@ -71,8 +71,8 @@ system_test_nns( tags = [ "experimental_system_test_colocation", "k8s", + "long_test", "subnet_recovery", - "system_test_hourly", ], target_compatible_with = ["@platforms//os:linux"], # requires libssh that does not build on Mac OS uses_guestos_dev_test = True, @@ -129,8 +129,8 @@ system_test_nns( tags = [ "experimental_system_test_colocation", "k8s", + "long_test", "subnet_recovery", - "system_test_hourly", ], target_compatible_with = ["@platforms//os:linux"], # requires libssh that does not build on Mac OS uses_guestos_dev = True, @@ -149,8 +149,8 @@ system_test_nns( tags = [ "experimental_system_test_colocation", "k8s", + "long_test", "subnet_recovery", - "system_test_hourly", ], target_compatible_with = ["@platforms//os:linux"], # requires libssh that does not build on Mac OS uses_guestos_dev = True, @@ -189,8 +189,8 @@ system_test_nns( tags = [ "experimental_system_test_colocation", "k8s", + "long_test", "subnet_recovery", - "system_test_hourly", ], target_compatible_with = ["@platforms//os:linux"], # requires libssh that does not build on Mac OS uses_guestos_dev = True, @@ -209,8 +209,8 @@ system_test_nns( tags = [ "experimental_system_test_colocation", "k8s", + "long_test", "subnet_recovery", - "system_test_hourly", ], target_compatible_with = ["@platforms//os:linux"], # requires libssh that does not build on Mac OS uses_guestos_dev = True, diff --git a/rs/tests/consensus/subnet_recovery/common.rs b/rs/tests/consensus/subnet_recovery/common.rs index 9fd85e32a38..4d824815b05 100644 --- a/rs/tests/consensus/subnet_recovery/common.rs +++ b/rs/tests/consensus/subnet_recovery/common.rs @@ -339,6 +339,7 @@ fn app_subnet_recovery_test(env: TestEnv, cfg: Config) { key_file: Some(ssh_authorized_priv_keys_dir.join(SSH_USERNAME)), test_mode: true, skip_prompts: true, + use_local_binaries: false, }; let mut unassigned_nodes = env.topology_snapshot().unassigned_nodes(); @@ -572,7 +573,7 @@ fn halt_subnet( message.cursor ), ); - if res.map_or(false, |r| r.trim().parse::().unwrap() > 0) { + if res.is_ok_and(|r| r.trim().parse::().unwrap() > 0) { Ok(()) } else { bail!("Did not find log entry that consensus is halted.") @@ -659,7 +660,7 @@ fn corrupt_latest_cup(subnet: &SubnetSnapshot, recovery: &Recovery, logger: &Log message.cursor ), ); - if res.map_or(false, |r| r.trim().parse::().unwrap() > 0) { + if res.is_ok_and( |r| r.trim().parse::().unwrap() > 0) { Ok(()) } else { bail!("Did not find log entry that cup is corrupted.") diff --git a/rs/tests/consensus/subnet_recovery/sr_nns_failover_nodes_test.rs b/rs/tests/consensus/subnet_recovery/sr_nns_failover_nodes_test.rs index 5c0e54e8c59..1ef315b9bb2 100644 --- a/rs/tests/consensus/subnet_recovery/sr_nns_failover_nodes_test.rs +++ b/rs/tests/consensus/subnet_recovery/sr_nns_failover_nodes_test.rs @@ -175,6 +175,7 @@ pub fn test(env: TestEnv) { key_file: Some(ssh_authorized_priv_keys_dir.join(SSH_USERNAME)), test_mode: true, skip_prompts: true, + use_local_binaries: false, }; let subnet_args = NNSRecoveryFailoverNodesArgs { subnet_id: topo_broken_ic.root_subnet_id(), diff --git a/rs/tests/consensus/subnet_recovery/sr_nns_same_nodes_test.rs b/rs/tests/consensus/subnet_recovery/sr_nns_same_nodes_test.rs index 80adb66dba1..c0cb0e44c94 100644 --- a/rs/tests/consensus/subnet_recovery/sr_nns_same_nodes_test.rs +++ b/rs/tests/consensus/subnet_recovery/sr_nns_same_nodes_test.rs @@ -116,6 +116,7 @@ pub fn test(env: TestEnv) { key_file: Some(ssh_authorized_priv_keys_dir.join(SSH_USERNAME)), test_mode: true, skip_prompts: true, + use_local_binaries: false, }; // unlike during a production recovery using the CLI, here we already know all of parameters diff --git a/rs/tests/consensus/subnet_splitting_test.rs b/rs/tests/consensus/subnet_splitting_test.rs index 730d46c53d5..6ed0b74a661 100644 --- a/rs/tests/consensus/subnet_splitting_test.rs +++ b/rs/tests/consensus/subnet_splitting_test.rs @@ -139,6 +139,7 @@ fn subnet_splitting_test(env: TestEnv) { ), test_mode: true, skip_prompts: true, + use_local_binaries: false, }; let subnet_splitting_args = SubnetSplittingArgs { diff --git a/rs/tests/consensus/tecdsa/BUILD.bazel b/rs/tests/consensus/tecdsa/BUILD.bazel index 5f1c8f3f549..a7b8250779f 100644 --- a/rs/tests/consensus/tecdsa/BUILD.bazel +++ b/rs/tests/consensus/tecdsa/BUILD.bazel @@ -133,7 +133,7 @@ system_test_nns( flaky = True, tags = [ "k8s", - "system_test_hourly", + "long_test", ], target_compatible_with = ["@platforms//os:linux"], # requires libssh that does not build on Mac OS runtime_deps = GUESTOS_RUNTIME_DEPS, @@ -157,7 +157,7 @@ system_test_nns( flaky = True, tags = [ "k8s", - "system_test_hourly", + "long_test", ], target_compatible_with = ["@platforms//os:linux"], # requires libssh that does not build on Mac OS runtime_deps = GUESTOS_RUNTIME_DEPS, @@ -177,7 +177,7 @@ system_test_nns( flaky = True, tags = [ "k8s", - "system_test_hourly", + "long_test", ], target_compatible_with = ["@platforms//os:linux"], # requires libssh that does not build on Mac OS runtime_deps = GUESTOS_RUNTIME_DEPS, @@ -198,7 +198,7 @@ system_test_nns( flaky = True, tags = [ "k8s", - "system_test_hourly", + "long_test", ], target_compatible_with = ["@platforms//os:linux"], # requires libssh that does not build on Mac OS runtime_deps = GUESTOS_RUNTIME_DEPS, @@ -254,7 +254,7 @@ system_test_nns( flaky = True, tags = [ "k8s", - "system_test_hourly", + "long_test", ], target_compatible_with = ["@platforms//os:linux"], # requires libssh that does not build on Mac OS runtime_deps = GUESTOS_RUNTIME_DEPS, diff --git a/rs/tests/consensus/tecdsa/tecdsa_signature_fails_without_cycles_test.rs b/rs/tests/consensus/tecdsa/tecdsa_signature_fails_without_cycles_test.rs index 9869d7d3626..2e335230498 100644 --- a/rs/tests/consensus/tecdsa/tecdsa_signature_fails_without_cycles_test.rs +++ b/rs/tests/consensus/tecdsa/tecdsa_signature_fails_without_cycles_test.rs @@ -79,7 +79,7 @@ fn test(env: TestEnv) { scale_cycles(ECDSA_SIGNATURE_FEE) - Cycles::from(1u64), scale_cycles(ECDSA_SIGNATURE_FEE), ), - error_code: None + error_code: Some("IC0406".to_string()) }) ) } diff --git a/rs/tests/consensus/tecdsa/tecdsa_signature_life_cycle_test.rs b/rs/tests/consensus/tecdsa/tecdsa_signature_life_cycle_test.rs index 96f29ca668c..830a459e0a0 100644 --- a/rs/tests/consensus/tecdsa/tecdsa_signature_life_cycle_test.rs +++ b/rs/tests/consensus/tecdsa/tecdsa_signature_life_cycle_test.rs @@ -100,7 +100,7 @@ fn test(env: TestEnv) { ChainKeyError(\"Requested unknown threshold key: {}, existing keys: {}\")", key_id3, initial_key_ids_as_string, ), - error_code: None, + error_code: Some("IC0406".to_string()), }) ); assert_eq!( @@ -121,7 +121,7 @@ fn test(env: TestEnv) { existing enabled keys: {}\")", key_id3, initial_key_ids_as_string, ), - error_code: None, + error_code: Some("IC0406".to_string()), }) ); @@ -228,7 +228,7 @@ fn test(env: TestEnv) { existing enabled keys: []\")", method_name, key_id ), - error_code: None + error_code: Some("IC0406".to_string()) }) ); break; diff --git a/rs/tests/consensus/tecdsa/tecdsa_signature_timeout_test.rs b/rs/tests/consensus/tecdsa/tecdsa_signature_timeout_test.rs index 81285874790..eb2de9fed5e 100644 --- a/rs/tests/consensus/tecdsa/tecdsa_signature_timeout_test.rs +++ b/rs/tests/consensus/tecdsa/tecdsa_signature_timeout_test.rs @@ -67,7 +67,7 @@ fn test(env: TestEnv) { AgentError::CertifiedReject(RejectResponse { reject_code: RejectCode::CanisterReject, reject_message: "Signature request expired".to_string(), - error_code: None + error_code: Some("IC0406".to_string()) }) ) } diff --git a/rs/tests/consensus/tecdsa/utils/src/lib.rs b/rs/tests/consensus/tecdsa/utils/src/lib.rs index 97dae4589cf..6b8071ae0da 100644 --- a/rs/tests/consensus/tecdsa/utils/src/lib.rs +++ b/rs/tests/consensus/tecdsa/utils/src/lib.rs @@ -460,7 +460,7 @@ pub async fn execute_update_subnet_proposal( logger, "Subnet Update proposal result: {:?}", proposal_result ); - assert_eq!(proposal_result.status(), ProposalStatus::Executed); + assert_eq!(proposal_result.status, ProposalStatus::Executed as i32); } pub async fn execute_create_subnet_proposal( @@ -489,7 +489,7 @@ pub async fn execute_create_subnet_proposal( logger, "Subnet Creation proposal result: {:?}", proposal_result ); - assert_eq!(proposal_result.status(), ProposalStatus::Executed); + assert_eq!(proposal_result.status, ProposalStatus::Executed as i32); } pub async fn get_signature_with_logger( diff --git a/rs/tests/consensus/upgrade/BUILD.bazel b/rs/tests/consensus/upgrade/BUILD.bazel index d6fb7cc6ee9..e4f68d10ef7 100644 --- a/rs/tests/consensus/upgrade/BUILD.bazel +++ b/rs/tests/consensus/upgrade/BUILD.bazel @@ -137,7 +137,7 @@ system_test_nns( flaky = True, tags = [ "k8s", - "system_test_hourly", + "long_test", ], target_compatible_with = ["@platforms//os:linux"], uses_guestos_dev = True, @@ -160,7 +160,7 @@ system_test_nns( flaky = True, tags = [ "k8s", - "system_test_hourly", + "long_test", ], target_compatible_with = ["@platforms//os:linux"], uses_guestos_dev = True, diff --git a/rs/tests/consensus/utils/src/performance.rs b/rs/tests/consensus/utils/src/performance.rs index 38b0c9f8f0b..de2dae5179c 100644 --- a/rs/tests/consensus/utils/src/performance.rs +++ b/rs/tests/consensus/utils/src/performance.rs @@ -2,7 +2,10 @@ use ic_registry_subnet_type::SubnetType; use ic_system_test_driver::canister_agent::HasCanisterAgentCapability; use ic_system_test_driver::canister_api::{CallMode, GenericRequest}; use ic_system_test_driver::canister_requests; -use ic_system_test_driver::driver::test_env_api::IcNodeSnapshot; +use ic_system_test_driver::driver::farm::HostFeature; +use ic_system_test_driver::driver::ic::{AmountOfMemoryKiB, ImageSizeGiB, NrOfVCPUs, VmResources}; +use ic_system_test_driver::driver::test_env_api::{get_dependency_path, IcNodeSnapshot}; +use ic_system_test_driver::driver::universal_vm::{UniversalVm, UniversalVms}; use ic_system_test_driver::driver::{ test_env::TestEnv, test_env_api::{HasTopologySnapshot, IcNodeContainer}, @@ -78,6 +81,9 @@ pub fn test_with_rt_handle( } info!(log, "{} canisters installed successfully.", canisters.len()); + info!(log, "Sleeping for 60 seconds"); + std::thread::sleep(Duration::from_secs(60)); + info!(log, "Step 2: Instantiate and start the workload.."); let payload: Vec = vec![0; message_size]; let generator = { @@ -212,12 +218,12 @@ impl std::fmt::Display for TestMetrics { )?; writeln!( f, - "Average time to receive a rank 0 block: {:.1}s", + "Average time to receive a rank 0 block: {:.2}s", self.average_time_to_receive_block )?; write!( f, - "Avarage E2E ingress message latency: {:.1}s", + "Avarage E2E ingress message latency: {:.2}s", self.average_e2e_latency ) } @@ -338,6 +344,9 @@ pub async fn persist_metrics( metrics: TestMetrics, message_size: usize, rps: f64, + latency: Duration, + bandwidth_bits_per_seconds: u32, + subnet_size: usize, log: &Logger, ) { // elastic search url @@ -355,6 +364,9 @@ pub async fn persist_metrics( "benchmark_settings": { "message_size": message_size, "rps": rps, + "latency_seconds": latency.as_secs_f64(), + "bandwith_bits_per_second": bandwidth_bits_per_seconds, + "subnet_size": subnet_size, }, "benchmark_results": { "success_rate": metrics.success_rate, @@ -416,3 +428,30 @@ fn average_f64(nums: &[f64]) -> f64 { nums.iter().sum::() / (nums.len() as f64) } + +pub fn setup_jaeger_vm(env: &TestEnv) -> std::net::Ipv6Addr { + const JAEGER_VM_NAME: &str = "jaeger-vm"; + + let path = get_dependency_path("rs/tests/jaeger_uvm_config_image.zst"); + UniversalVm::new(JAEGER_VM_NAME.to_string()) + .with_required_host_features(vec![HostFeature::Performance]) + .with_vm_resources(VmResources { + vcpus: Some(NrOfVCPUs::new(16)), + memory_kibibytes: Some(AmountOfMemoryKiB::new(33560000)), // 32GiB + boot_image_minimal_size_gibibytes: Some(ImageSizeGiB::new(1024)), + }) + .with_config_img(path) + .start(env) + .expect("failed to setup Jaeger Universal VM"); + + let deployed_jaeger_vm = env.get_deployed_universal_vm(JAEGER_VM_NAME).unwrap(); + let jaeger_vm = deployed_jaeger_vm.get_vm().unwrap(); + let jaeger_ipv6 = jaeger_vm.ipv6; + + info!( + env.logger(), + "Jaeger frontend available at: http://[{}]:16686", jaeger_ipv6 + ); + + jaeger_ipv6 +} diff --git a/rs/tests/consensus/vetkd/BUILD.bazel b/rs/tests/consensus/vetkd/BUILD.bazel new file mode 100644 index 00000000000..5d496974163 --- /dev/null +++ b/rs/tests/consensus/vetkd/BUILD.bazel @@ -0,0 +1,31 @@ +load("//rs/tests:common.bzl", "GUESTOS_RUNTIME_DEPS") +load("//rs/tests:system_tests.bzl", "system_test_nns") + +package(default_visibility = ["//rs:system-tests-pkg"]) + +system_test_nns( + name = "vetkd_key_life_cycle_test", + extra_head_nns_tags = [], # don't run the head_nns variant on nightly since it aleady runs on long_test. + flaky = True, + tags = [ + "k8s", + "long_test", # since it takes longer than 5 minutes. + ], + target_compatible_with = ["@platforms//os:linux"], # requires libssh that does not build on Mac OS + runtime_deps = GUESTOS_RUNTIME_DEPS, + deps = [ + # Keep sorted. + "//rs/config", + "//rs/nns/constants", + "//rs/registry/subnet_type", + "//rs/rust_canisters/canister_test", + "//rs/tests/consensus/tecdsa/utils", + "//rs/tests/consensus/utils", + "//rs/tests/driver:ic-system-test-driver", + "//rs/types/management_canister_types", + "//rs/types/types", + "@crate_index//:anyhow", + "@crate_index//:ic-agent", + "@crate_index//:slog", + ], +) diff --git a/rs/tests/consensus/vetkd/Cargo.toml b/rs/tests/consensus/vetkd/Cargo.toml new file mode 100644 index 00000000000..dec10d17542 --- /dev/null +++ b/rs/tests/consensus/vetkd/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "consensus-vetkd-system-tests" +version.workspace = true +authors.workspace = true +edition.workspace = true +description.workspace = true +documentation.workspace = true + +[dependencies] +anyhow = { workspace = true } +canister-test = { path = "../../../rust_canisters/canister_test" } +ic-management-canister-types = { path = "../../../types/management_canister_types" } +ic-nns-constants = { path = "../../../nns/constants" } +ic-registry-subnet-type = { path = "../../../registry/subnet_type" } +ic-system-test-driver = { path = "../../driver" } +ic-types = { path = "../../../types/types" } +ic_consensus_system_test_utils = { path = "../utils" } +ic_consensus_threshold_sig_system_test_utils = { path = "../tecdsa/utils" } +slog = { workspace = true } +tokio = { workspace = true } + +[[bin]] +name = "ic-systest-vetkd-key-life-cycle-test" +path = "vetkd_key_life_cycle_test.rs" diff --git a/rs/tests/consensus/vetkd/vetkd_key_life_cycle_test.rs b/rs/tests/consensus/vetkd/vetkd_key_life_cycle_test.rs new file mode 100644 index 00000000000..9ea6e9fb7ce --- /dev/null +++ b/rs/tests/consensus/vetkd/vetkd_key_life_cycle_test.rs @@ -0,0 +1,113 @@ +/* tag::catalog[] +Title:: Creating, fetching and deleting a vetkey on a subnet + +Goal:: Test whether the local DKG mechanism for vetkeys works + + +Runbook:: +. Setup:: + . System subnet comprising N nodes, necessary NNS canisters +. Wait one DKG interval +. Enable vetkey on subnet +. Wait two DKG intervals, check subnet health +. TODO(CON-1420): Fetch the public key from a canister + +end::catalog[] */ + +use anyhow::Result; +use canister_test::Canister; +use ic_consensus_system_test_utils::node::await_node_certified_height; +use ic_consensus_threshold_sig_system_test_utils::{ + add_chain_keys_with_timeout_and_rotation_period, DKG_INTERVAL, +}; +use ic_management_canister_types::{MasterPublicKeyId, VetKdCurve, VetKdKeyId}; +use ic_nns_constants::GOVERNANCE_CANISTER_ID; +use ic_registry_subnet_type::SubnetType; +use ic_system_test_driver::{ + driver::{ + group::SystemTestGroup, + ic::{InternetComputer, Subnet}, + test_env::TestEnv, + test_env_api::{ + HasPublicApiUrl, HasTopologySnapshot, IcNodeContainer, NnsInstallationBuilder, + }, + }, + systest, + util::{block_on, runtime_from_url}, +}; +use ic_types::Height; +use slog::info; + +const NODES_COUNT: usize = 4; + +fn setup(env: TestEnv) { + InternetComputer::new() + .add_subnet( + Subnet::new(SubnetType::System) + .with_dkg_interval_length(Height::from(DKG_INTERVAL)) + .add_nodes(NODES_COUNT), + ) + .setup_and_start(&env) + .expect("failed to setup IC under test"); + // Check all subnet nodes are healthy. + env.topology_snapshot().subnets().for_each(|subnet| { + subnet + .nodes() + .for_each(|node| node.await_status_is_healthy().unwrap()) + }); +} + +fn test(env: TestEnv) { + let log = env.logger(); + let topology_snapshot = env.topology_snapshot(); + + let nns_subnet = topology_snapshot.root_subnet(); + let nns_node = nns_subnet.nodes().next().unwrap(); + + info!(log, "Installing nns canisters."); + NnsInstallationBuilder::new() + .install(&nns_node, &env) + .expect("Could not install NNS canisters."); + + // TODO(CON-1420): Install message canister to fetch keys + // TODO: Since installing these canisters takes some time, we should actually + // take the hight at this moment as the base for the test and then wait relative to that. + + // Wait one DKG + await_node_certified_height(&nns_node, Height::from(DKG_INTERVAL + 1), log.clone()); + + let nns_runtime = runtime_from_url(nns_node.get_public_url(), nns_node.effective_canister_id()); + let governance = Canister::new(&nns_runtime, GOVERNANCE_CANISTER_ID); + let key_ids = vec![MasterPublicKeyId::VetKd(VetKdKeyId { + curve: VetKdCurve::Bls12_381_G2, + name: String::from("some_vetkd_key"), + })]; + + block_on(async { + //enable_chain_key_signing(&governance, nns_subnet.subnet_id, key_ids.clone(), &log).await; + add_chain_keys_with_timeout_and_rotation_period( + &governance, + nns_subnet.subnet_id, + key_ids.clone(), + None, + None, + &log, + ) + .await; + }); + + // Wait two DKGs + await_node_certified_height(&nns_node, Height::from((DKG_INTERVAL + 1) * 3), log.clone()); + // TODO(CON-1420): Fetch public key from subnet + + // Wait two more DKGs + await_node_certified_height(&nns_node, Height::from((DKG_INTERVAL + 1) * 5), log.clone()); +} + +fn main() -> Result<()> { + SystemTestGroup::new() + .with_setup(setup) + .add_test(systest!(test)) + .execute_from_args()?; + Ok(()) +} diff --git a/rs/tests/cross_chain/BUILD.bazel b/rs/tests/cross_chain/BUILD.bazel index f5bf56f3af7..7aa0b3ee321 100644 --- a/rs/tests/cross_chain/BUILD.bazel +++ b/rs/tests/cross_chain/BUILD.bazel @@ -1,5 +1,5 @@ load("//rs/tests:common.bzl", "BOUNDARY_NODE_GUESTOS_RUNTIME_DEPS", "GUESTOS_RUNTIME_DEPS", "UNIVERSAL_VM_RUNTIME_DEPS") -load("//rs/tests:system_tests.bzl", "system_test_nns") +load("//rs/tests:system_tests.bzl", "oci_tar", "system_test_nns", "uvm_config_image") package(default_visibility = ["//rs:system-tests-pkg"]) @@ -44,3 +44,72 @@ system_test_nns( "@crate_index//:slog", ], ) + +system_test_nns( + name = "ic_xc_cketh_test", + env = { + "CKETH_UVM_CONFIG_PATH": "$(rootpath :cketh_uvm_config_image)", + "CKETH_MINTER_WASM_PATH": "$(rootpath //rs/ethereum/cketh/minter:cketh_minter.wasm.gz)", + "LEDGER_WASM_PATH": "$(rootpath //rs/ledger_suite/icrc1/ledger:ledger_canister_u256.wasm.gz)", + "LEDGER_SUITE_ORCHESTRATOR_WASM_PATH": "$(rootpath //rs/ethereum/ledger-suite-orchestrator:ledger_suite_orchestrator_canister.wasm.gz)", + }, + tags = ["long_test"], + target_compatible_with = ["@platforms//os:linux"], # requires libssh that does not build on Mac OS + runtime_deps = + BOUNDARY_NODE_GUESTOS_RUNTIME_DEPS + + GUESTOS_RUNTIME_DEPS + + UNIVERSAL_VM_RUNTIME_DEPS + [ + # Keep sorted. + ":cketh_uvm_config_image", + "//rs/ethereum/cketh/minter:cketh_minter.wasm.gz", + "//rs/ethereum/ledger-suite-orchestrator:ledger_suite_orchestrator_canister.wasm.gz", + "//rs/ledger_suite/icrc1/ledger:ledger_canister_u256.wasm.gz", + ], + deps = [ + # Keep sorted. + "//packages/ic-ethereum-types", + "//packages/icrc-ledger-types:icrc_ledger_types", + "//rs/ethereum/cketh/minter", + "//rs/ethereum/ledger-suite-orchestrator:ledger_suite_orchestrator", + "//rs/ledger_suite/icrc1/ledger", + "//rs/nns/constants", + "//rs/registry/subnet_type", + "//rs/rust_canisters/canister_test", + "//rs/rust_canisters/dfn_candid", + "//rs/tests/consensus/tecdsa/utils", + "//rs/tests/consensus/utils", + "//rs/tests/driver:ic-system-test-driver", + "//rs/types/management_canister_types", + "//rs/types/types", + "@crate_index//:anyhow", + "@crate_index//:candid", + "@crate_index//:futures", + "@crate_index//:hex-literal", + "@crate_index//:reqwest", + "@crate_index//:serde_json", + "@crate_index//:slog", + ], +) + +filegroup( + name = "erc20_contract", + srcs = [ + "ERC20.sol", + ], +) + +oci_tar( + name = "foundry.tar", + image = "@foundry", + repo_tags = ["foundry:latest"], +) + +uvm_config_image( + name = "cketh_uvm_config_image", + srcs = [ + ":erc20_contract", + ":foundry.tar", + "//rs/ethereum/cketh/minter:helper_contracts", + ], + tags = ["manual"], # this target will be built if required as a dependency of another target +) diff --git a/rs/tests/cross_chain/Cargo.toml b/rs/tests/cross_chain/Cargo.toml new file mode 100644 index 00000000000..25155229474 --- /dev/null +++ b/rs/tests/cross_chain/Cargo.toml @@ -0,0 +1,48 @@ +[package] +name = "ic-tests-cross-chain" +version.workspace = true +authors.workspace = true +edition.workspace = true +description.workspace = true +documentation.workspace = true + +[dependencies] +anyhow = { workspace = true } +candid = { workspace = true } +canister-test = { path = "../../rust_canisters/canister_test" } +dfn_candid = { path = "../../rust_canisters/dfn_candid" } +futures = {workspace = true} +hex-literal = "0.4.1" +ic-base-types = { path = "../../types/base_types" } +ic-canister-client = { path = "../../canister_client" } +ic_consensus_system_test_utils = { path = "../consensus/utils" } +ic_consensus_threshold_sig_system_test_utils = { path = "../consensus/tecdsa/utils"} +ic-ethereum-types = { path = "../../../packages/ic-ethereum-types"} +ic-icrc1-index-ng = { path = "../../ledger_suite/icrc1/index-ng" } +ic-icrc1-ledger = { path = "../../ledger_suite/icrc1/ledger" } +ic-ledger-suite-orchestrator = { path = "../../ethereum/ledger-suite-orchestrator" } +ic-cketh-minter = { path = "../../ethereum/cketh/minter" } +ic-management-canister-types = { path = "../../types/management_canister_types" } +ic-nervous-system-clients = { path = "../../nervous_system/clients" } +ic-nervous-system-common-test-keys = { path = "../../nervous_system/common/test_keys" } +ic-nervous-system-root = { path = "../../nervous_system/root" } +ic-nns-common = { path = "../../nns/common" } +ic-nns-constants = { path = "../../nns/constants" } +ic-nns-governance-api = { path = "../../nns/governance/api" } +ic-nns-test-utils = { path = "../../nns/test_utils" } +ic-registry-subnet-type = { path = "../../registry/subnet_type" } +ic-system-test-driver = { path = "../driver" } +ic-types = { path = "../../types/types" } +ic-wasm-types = { path = "../../types/wasm_types" } +icrc-ledger-types = { path = "../../../packages/icrc-ledger-types" } +reqwest = { workspace = true } +serde_json = { workspace = true } +slog = { workspace = true } + +[[bin]] +name = "ic-xc-ledger-suite-orchestrator" +path = "ic_xc_ledger_suite_orchestrator_test.rs" + +[[bin]] +name = "ic_xc_cketh" +path = "ic_xc_cketh_test.rs" diff --git a/rs/tests/cross_chain/ERC20.sol b/rs/tests/cross_chain/ERC20.sol new file mode 100644 index 00000000000..b7a4bc130e4 --- /dev/null +++ b/rs/tests/cross_chain/ERC20.sol @@ -0,0 +1,633 @@ +// Flattening of a standard ERC-20 contract that uses OpenZeppelin contract library +// See https://docs.openzeppelin.com/contracts/4.x/erc20. +// File: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v5.1.0/contracts/token/ERC20/IERC20.sol + + +// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol) + +pragma solidity ^0.8.20; + +/** + * @dev Interface of the ERC-20 standard as defined in the ERC. + */ +interface IERC20 { + /** + * @dev Emitted when `value` tokens are moved from one account (`from`) to + * another (`to`). + * + * Note that `value` may be zero. + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev Emitted when the allowance of a `spender` for an `owner` is set by + * a call to {approve}. `value` is the new allowance. + */ + event Approval(address indexed owner, address indexed spender, uint256 value); + + /** + * @dev Returns the value of tokens in existence. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the value of tokens owned by `account`. + */ + function balanceOf(address account) external view returns (uint256); + + /** + * @dev Moves a `value` amount of tokens from the caller's account to `to`. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address to, uint256 value) external returns (bool); + + /** + * @dev Returns the remaining number of tokens that `spender` will be + * allowed to spend on behalf of `owner` through {transferFrom}. This is + * zero by default. + * + * This value changes when {approve} or {transferFrom} are called. + */ + function allowance(address owner, address spender) external view returns (uint256); + + /** + * @dev Sets a `value` amount of tokens as the allowance of `spender` over the + * caller's tokens. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * IMPORTANT: Beware that changing an allowance with this method brings the risk + * that someone may use both the old and the new allowance by unfortunate + * transaction ordering. One possible solution to mitigate this race + * condition is to first reduce the spender's allowance to 0 and set the + * desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * Emits an {Approval} event. + */ + function approve(address spender, uint256 value) external returns (bool); + + /** + * @dev Moves a `value` amount of tokens from `from` to `to` using the + * allowance mechanism. `value` is then deducted from the caller's + * allowance. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transferFrom(address from, address to, uint256 value) external returns (bool); +} + +// File: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v5.1.0/contracts/token/ERC20/extensions/IERC20Metadata.sol + + +// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Metadata.sol) + +pragma solidity ^0.8.20; + + +/** + * @dev Interface for the optional metadata functions from the ERC-20 standard. + */ +interface IERC20Metadata is IERC20 { + /** + * @dev Returns the name of the token. + */ + function name() external view returns (string memory); + + /** + * @dev Returns the symbol of the token. + */ + function symbol() external view returns (string memory); + + /** + * @dev Returns the decimals places of the token. + */ + function decimals() external view returns (uint8); +} + +// File: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v5.1.0/contracts/utils/Context.sol + + +// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol) + +pragma solidity ^0.8.20; + +/** + * @dev Provides information about the current execution context, including the + * sender of the transaction and its data. While these are generally available + * via msg.sender and msg.data, they should not be accessed in such a direct + * manner, since when dealing with meta-transactions the account sending and + * paying for execution may not be the actual sender (as far as an application + * is concerned). + * + * This contract is only required for intermediate, library-like contracts. + */ +abstract contract Context { + function _msgSender() internal view virtual returns (address) { + return msg.sender; + } + + function _msgData() internal view virtual returns (bytes calldata) { + return msg.data; + } + + function _contextSuffixLength() internal view virtual returns (uint256) { + return 0; + } +} + +// File: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v5.1.0/contracts/interfaces/draft-IERC6093.sol + + +// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/draft-IERC6093.sol) +pragma solidity ^0.8.20; + +/** + * @dev Standard ERC-20 Errors + * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 tokens. + */ +interface IERC20Errors { + /** + * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers. + * @param sender Address whose tokens are being transferred. + * @param balance Current balance for the interacting account. + * @param needed Minimum amount required to perform a transfer. + */ + error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed); + + /** + * @dev Indicates a failure with the token `sender`. Used in transfers. + * @param sender Address whose tokens are being transferred. + */ + error ERC20InvalidSender(address sender); + + /** + * @dev Indicates a failure with the token `receiver`. Used in transfers. + * @param receiver Address to which tokens are being transferred. + */ + error ERC20InvalidReceiver(address receiver); + + /** + * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers. + * @param spender Address that may be allowed to operate on tokens without being their owner. + * @param allowance Amount of tokens a `spender` is allowed to operate with. + * @param needed Minimum amount required to perform a transfer. + */ + error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed); + + /** + * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. + * @param approver Address initiating an approval operation. + */ + error ERC20InvalidApprover(address approver); + + /** + * @dev Indicates a failure with the `spender` to be approved. Used in approvals. + * @param spender Address that may be allowed to operate on tokens without being their owner. + */ + error ERC20InvalidSpender(address spender); +} + +/** + * @dev Standard ERC-721 Errors + * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens. + */ +interface IERC721Errors { + /** + * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-20. + * Used in balance queries. + * @param owner Address of the current owner of a token. + */ + error ERC721InvalidOwner(address owner); + + /** + * @dev Indicates a `tokenId` whose `owner` is the zero address. + * @param tokenId Identifier number of a token. + */ + error ERC721NonexistentToken(uint256 tokenId); + + /** + * @dev Indicates an error related to the ownership over a particular token. Used in transfers. + * @param sender Address whose tokens are being transferred. + * @param tokenId Identifier number of a token. + * @param owner Address of the current owner of a token. + */ + error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner); + + /** + * @dev Indicates a failure with the token `sender`. Used in transfers. + * @param sender Address whose tokens are being transferred. + */ + error ERC721InvalidSender(address sender); + + /** + * @dev Indicates a failure with the token `receiver`. Used in transfers. + * @param receiver Address to which tokens are being transferred. + */ + error ERC721InvalidReceiver(address receiver); + + /** + * @dev Indicates a failure with the `operator`’s approval. Used in transfers. + * @param operator Address that may be allowed to operate on tokens without being their owner. + * @param tokenId Identifier number of a token. + */ + error ERC721InsufficientApproval(address operator, uint256 tokenId); + + /** + * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. + * @param approver Address initiating an approval operation. + */ + error ERC721InvalidApprover(address approver); + + /** + * @dev Indicates a failure with the `operator` to be approved. Used in approvals. + * @param operator Address that may be allowed to operate on tokens without being their owner. + */ + error ERC721InvalidOperator(address operator); +} + +/** + * @dev Standard ERC-1155 Errors + * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 tokens. + */ +interface IERC1155Errors { + /** + * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers. + * @param sender Address whose tokens are being transferred. + * @param balance Current balance for the interacting account. + * @param needed Minimum amount required to perform a transfer. + * @param tokenId Identifier number of a token. + */ + error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId); + + /** + * @dev Indicates a failure with the token `sender`. Used in transfers. + * @param sender Address whose tokens are being transferred. + */ + error ERC1155InvalidSender(address sender); + + /** + * @dev Indicates a failure with the token `receiver`. Used in transfers. + * @param receiver Address to which tokens are being transferred. + */ + error ERC1155InvalidReceiver(address receiver); + + /** + * @dev Indicates a failure with the `operator`’s approval. Used in transfers. + * @param operator Address that may be allowed to operate on tokens without being their owner. + * @param owner Address of the current owner of a token. + */ + error ERC1155MissingApprovalForAll(address operator, address owner); + + /** + * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. + * @param approver Address initiating an approval operation. + */ + error ERC1155InvalidApprover(address approver); + + /** + * @dev Indicates a failure with the `operator` to be approved. Used in approvals. + * @param operator Address that may be allowed to operate on tokens without being their owner. + */ + error ERC1155InvalidOperator(address operator); + + /** + * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation. + * Used in batch transfers. + * @param idsLength Length of the array of token identifiers + * @param valuesLength Length of the array of token amounts + */ + error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength); +} + +// File: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v5.1.0/contracts/token/ERC20/ERC20.sol + + +// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/ERC20.sol) + +pragma solidity ^0.8.20; + + + + + +/** + * @dev Implementation of the {IERC20} interface. + * + * This implementation is agnostic to the way tokens are created. This means + * that a supply mechanism has to be added in a derived contract using {_mint}. + * + * TIP: For a detailed writeup see our guide + * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How + * to implement supply mechanisms]. + * + * The default value of {decimals} is 18. To change this, you should override + * this function so it returns a different value. + * + * We have followed general OpenZeppelin Contracts guidelines: functions revert + * instead returning `false` on failure. This behavior is nonetheless + * conventional and does not conflict with the expectations of ERC-20 + * applications. + */ +abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors { + mapping(address account => uint256) private _balances; + + mapping(address account => mapping(address spender => uint256)) private _allowances; + + uint256 private _totalSupply; + + string private _name; + string private _symbol; + + /** + * @dev Sets the values for {name} and {symbol}. + * + * All two of these values are immutable: they can only be set once during + * construction. + */ + constructor(string memory name_, string memory symbol_) { + _name = name_; + _symbol = symbol_; + } + + /** + * @dev Returns the name of the token. + */ + function name() public view virtual returns (string memory) { + return _name; + } + + /** + * @dev Returns the symbol of the token, usually a shorter version of the + * name. + */ + function symbol() public view virtual returns (string memory) { + return _symbol; + } + + /** + * @dev Returns the number of decimals used to get its user representation. + * For example, if `decimals` equals `2`, a balance of `505` tokens should + * be displayed to a user as `5.05` (`505 / 10 ** 2`). + * + * Tokens usually opt for a value of 18, imitating the relationship between + * Ether and Wei. This is the default value returned by this function, unless + * it's overridden. + * + * NOTE: This information is only used for _display_ purposes: it in + * no way affects any of the arithmetic of the contract, including + * {IERC20-balanceOf} and {IERC20-transfer}. + */ + function decimals() public view virtual returns (uint8) { + return 18; + } + + /** + * @dev See {IERC20-totalSupply}. + */ + function totalSupply() public view virtual returns (uint256) { + return _totalSupply; + } + + /** + * @dev See {IERC20-balanceOf}. + */ + function balanceOf(address account) public view virtual returns (uint256) { + return _balances[account]; + } + + /** + * @dev See {IERC20-transfer}. + * + * Requirements: + * + * - `to` cannot be the zero address. + * - the caller must have a balance of at least `value`. + */ + function transfer(address to, uint256 value) public virtual returns (bool) { + address owner = _msgSender(); + _transfer(owner, to, value); + return true; + } + + /** + * @dev See {IERC20-allowance}. + */ + function allowance(address owner, address spender) public view virtual returns (uint256) { + return _allowances[owner][spender]; + } + + /** + * @dev See {IERC20-approve}. + * + * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on + * `transferFrom`. This is semantically equivalent to an infinite approval. + * + * Requirements: + * + * - `spender` cannot be the zero address. + */ + function approve(address spender, uint256 value) public virtual returns (bool) { + address owner = _msgSender(); + _approve(owner, spender, value); + return true; + } + + /** + * @dev See {IERC20-transferFrom}. + * + * Skips emitting an {Approval} event indicating an allowance update. This is not + * required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve]. + * + * NOTE: Does not update the allowance if the current allowance + * is the maximum `uint256`. + * + * Requirements: + * + * - `from` and `to` cannot be the zero address. + * - `from` must have a balance of at least `value`. + * - the caller must have allowance for ``from``'s tokens of at least + * `value`. + */ + function transferFrom(address from, address to, uint256 value) public virtual returns (bool) { + address spender = _msgSender(); + _spendAllowance(from, spender, value); + _transfer(from, to, value); + return true; + } + + /** + * @dev Moves a `value` amount of tokens from `from` to `to`. + * + * This internal function is equivalent to {transfer}, and can be used to + * e.g. implement automatic token fees, slashing mechanisms, etc. + * + * Emits a {Transfer} event. + * + * NOTE: This function is not virtual, {_update} should be overridden instead. + */ + function _transfer(address from, address to, uint256 value) internal { + if (from == address(0)) { + revert ERC20InvalidSender(address(0)); + } + if (to == address(0)) { + revert ERC20InvalidReceiver(address(0)); + } + _update(from, to, value); + } + + /** + * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from` + * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding + * this function. + * + * Emits a {Transfer} event. + */ + function _update(address from, address to, uint256 value) internal virtual { + if (from == address(0)) { + // Overflow check required: The rest of the code assumes that totalSupply never overflows + _totalSupply += value; + } else { + uint256 fromBalance = _balances[from]; + if (fromBalance < value) { + revert ERC20InsufficientBalance(from, fromBalance, value); + } + unchecked { + // Overflow not possible: value <= fromBalance <= totalSupply. + _balances[from] = fromBalance - value; + } + } + + if (to == address(0)) { + unchecked { + // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply. + _totalSupply -= value; + } + } else { + unchecked { + // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256. + _balances[to] += value; + } + } + + emit Transfer(from, to, value); + } + + /** + * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0). + * Relies on the `_update` mechanism + * + * Emits a {Transfer} event with `from` set to the zero address. + * + * NOTE: This function is not virtual, {_update} should be overridden instead. + */ + function _mint(address account, uint256 value) internal { + if (account == address(0)) { + revert ERC20InvalidReceiver(address(0)); + } + _update(address(0), account, value); + } + + /** + * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply. + * Relies on the `_update` mechanism. + * + * Emits a {Transfer} event with `to` set to the zero address. + * + * NOTE: This function is not virtual, {_update} should be overridden instead + */ + function _burn(address account, uint256 value) internal { + if (account == address(0)) { + revert ERC20InvalidSender(address(0)); + } + _update(account, address(0), value); + } + + /** + * @dev Sets `value` as the allowance of `spender` over the `owner` s tokens. + * + * This internal function is equivalent to `approve`, and can be used to + * e.g. set automatic allowances for certain subsystems, etc. + * + * Emits an {Approval} event. + * + * Requirements: + * + * - `owner` cannot be the zero address. + * - `spender` cannot be the zero address. + * + * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument. + */ + function _approve(address owner, address spender, uint256 value) internal { + _approve(owner, spender, value, true); + } + + /** + * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event. + * + * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by + * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any + * `Approval` event during `transferFrom` operations. + * + * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to + * true using the following override: + * + * ```solidity + * function _approve(address owner, address spender, uint256 value, bool) internal virtual override { + * super._approve(owner, spender, value, true); + * } + * ``` + * + * Requirements are the same as {_approve}. + */ + function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual { + if (owner == address(0)) { + revert ERC20InvalidApprover(address(0)); + } + if (spender == address(0)) { + revert ERC20InvalidSpender(address(0)); + } + _allowances[owner][spender] = value; + if (emitEvent) { + emit Approval(owner, spender, value); + } + } + + /** + * @dev Updates `owner` s allowance for `spender` based on spent `value`. + * + * Does not update the allowance value in case of infinite allowance. + * Revert if not enough allowance is available. + * + * Does not emit an {Approval} event. + */ + function _spendAllowance(address owner, address spender, uint256 value) internal virtual { + uint256 currentAllowance = allowance(owner, spender); + if (currentAllowance != type(uint256).max) { + if (currentAllowance < value) { + revert ERC20InsufficientAllowance(spender, currentAllowance, value); + } + unchecked { + _approve(owner, spender, currentAllowance - value, false); + } + } + } +} + +// File: erc20.sol + +// contracts/GLDToken.sol + +pragma solidity ^0.8.0; + + +contract EXLToken is ERC20 { + constructor(uint256 initialSupply) ERC20("Example", "EXL") { + _mint(msg.sender, initialSupply); + } +} diff --git a/rs/tests/cross_chain/ic_xc_cketh_test.rs b/rs/tests/cross_chain/ic_xc_cketh_test.rs new file mode 100644 index 00000000000..4f0d3ee2436 --- /dev/null +++ b/rs/tests/cross_chain/ic_xc_cketh_test.rs @@ -0,0 +1,1030 @@ +use anyhow::{anyhow, bail, Result}; +use candid::{Encode, Nat, Principal}; +use canister_test::{Canister, Runtime, Wasm}; +use dfn_candid::candid; +use futures::future::FutureExt; +use hex_literal::hex; +use ic_cketh_minter::endpoints::{CandidBlockTag, MinterInfo}; +use ic_cketh_minter::lifecycle::upgrade::UpgradeArg as MinterUpgradeArg; +use ic_cketh_minter::lifecycle::{init::InitArg as MinterInitArgs, EthereumNetwork, MinterArg}; +use ic_cketh_minter::numeric::BlockNumber; +use ic_consensus_system_test_utils::rw_message::install_nns_with_customizations_and_check_progress; +use ic_consensus_threshold_sig_system_test_utils::{ + enable_chain_key_signing, get_public_key_and_test_signature, make_key, +}; +use ic_ethereum_types::Address; +use ic_icrc1_ledger::{ArchiveOptions, FeatureFlags, InitArgsBuilder, LedgerArgument}; +use ic_ledger_suite_orchestrator::candid::{ + AddErc20Arg, Erc20Contract, InitArg, LedgerInitArg, ManagedCanisterIds, OrchestratorArg, + UpgradeArg as LedgerSuiteOrchestratorUpgradeArg, +}; +use ic_management_canister_types::{EcdsaKeyId, MasterPublicKeyId}; +use ic_nns_constants::{GOVERNANCE_CANISTER_ID, ROOT_CANISTER_ID}; +use ic_registry_subnet_type::SubnetType; +use ic_system_test_driver::driver::test_env_api::SubnetSnapshot; +use ic_system_test_driver::driver::universal_vm::DeployedUniversalVm; +use ic_system_test_driver::util::MessageCanister; +use ic_system_test_driver::{ + driver::{ + group::SystemTestGroup, + ic::{InternetComputer, Subnet}, + test_env::TestEnv, + test_env_api::{ + get_dependency_path, HasPublicApiUrl, HasTopologySnapshot, IcNodeContainer, + NnsCustomizations, SshSession, + }, + universal_vm::{UniversalVm, UniversalVms}, + }, + systest, + util::{block_on, runtime_from_url}, +}; +use ic_types::Height; +use icrc_ledger_types::icrc1::account::Account; +use reqwest::Client; +use slog::{info, Logger}; +use std::env; +use std::future::Future; +use std::time::Duration; + +const FOUNDRY_VM_NAME: &str = "foundry"; +const DOCKER_NETWORK_NAME: &str = "ethereum"; +const FOUNDRY_PORT: u16 = 8545; +const ENCODED_PRINCIPAL: &str = + "0x1d9facb184cbe453de4841b6b9d9cc95bfc065344e485789b550544529020000"; + +fn main() -> Result<()> { + SystemTestGroup::new() + .with_setup(setup_with_system_and_application_subnets) + .add_test(systest!(ic_xc_cketh_test)) + .execute_from_args()?; + Ok(()) +} + +fn setup_with_system_and_application_subnets(env: TestEnv) { + std::thread::scope(|s| { + s.spawn(|| { + setup_anvil(&env); + }); + s.spawn(|| { + InternetComputer::new() + .add_subnet(Subnet::new(SubnetType::System).add_nodes(1)) + .add_subnet( + Subnet::new(SubnetType::Application) + .add_nodes(1) + .with_dkg_interval_length(Height::from(10)), + ) + .setup_and_start(&env) + .expect("Failed to setup IC under test"); + install_nns_with_customizations_and_check_progress( + env.topology_snapshot(), + NnsCustomizations::default(), + ); + }); + }); +} + +fn setup_anvil(env: &TestEnv) { + UniversalVm::new(String::from(FOUNDRY_VM_NAME)) + .with_config_img(get_dependency_path( + env::var("CKETH_UVM_CONFIG_PATH").expect("CKETH_UVM_CONFIG_PATH not set"), + )) + .enable_ipv4() //forge needs to download the version of the solidity compiler indicated in the smart contracts that are being deployed + .start(env) + .expect("failed to setup universal VM"); + + let deployed_universal_vm = env.get_deployed_universal_vm(FOUNDRY_VM_NAME).unwrap(); + + deployed_universal_vm + .block_on_bash_script(&format!( + r#" +docker load -i /config/foundry.tar +docker network create {DOCKER_NETWORK_NAME} +docker run --net {DOCKER_NETWORK_NAME} --detach --rm --name anvil -p {FOUNDRY_PORT}:{FOUNDRY_PORT} foundry:latest "anvil --host 0.0.0.0" +"# + )) + .unwrap(); +} + +fn ic_xc_cketh_test(env: TestEnv) { + let logger = env.logger(); + let topology_snapshot = env.topology_snapshot(); + let nns_subnet = topology_snapshot.root_subnet(); + let application_subnet = topology_snapshot + .subnets() + .find(|s| s.subnet_type() == SubnetType::Application) + .expect("missing application subnet"); + + let system_subnet_runtime = { + let nns_node = nns_subnet.nodes().next().unwrap(); + runtime_from_url(nns_node.get_public_url(), nns_node.effective_canister_id()) + }; + let governance_canister = Canister::new(&system_subnet_runtime, GOVERNANCE_CANISTER_ID); + + let ecdsa_key_id = block_on(async { + activate_threshold_ecdsa(&governance_canister, &application_subnet, &logger).await + }); + + let application_subnet_runtime = { + let application_node = application_subnet.nodes().next().unwrap(); + runtime_from_url( + application_node.get_public_url(), + application_node.effective_canister_id(), + ) + }; + + let foundry = env.get_deployed_universal_vm(FOUNDRY_VM_NAME).unwrap(); + assert_eq!( + block_on(async { eth_block_number(&foundry).await }), + BlockNumber::ZERO + ); + + let (cketh_ledger, mut minter) = block_on(async { + let minter_canister = create_canister(&application_subnet_runtime).await; + let minter = minter_canister.canister_id().get().0; + let cketh_ledger_canister = install_cketh_ledger(&application_subnet_runtime, minter).await; + info!( + logger, + "Installed ckETH ledger at {}", + cketh_ledger_canister.principal() + ); + + let minter_canister = install_cketh_minter( + minter_canister, + &ecdsa_key_id, + cketh_ledger_canister.principal(), + ) + .await; + info!( + logger, + "Installed ckETH minter at {}", + minter_canister.principal() + ); + (cketh_ledger_canister, minter_canister) + }); + + info!( + logger, + "Supporting deposit of ETH and testing deposit flows." + ); + block_on(async { + support_eth_deposit( + &foundry, + &mut minter, + &ecdsa_key_id, + cketh_ledger.principal(), + &logger, + ) + .await; + + test_cketh_deposit(&foundry, &minter, &logger).await + }); + + let (erc20_contract_address, _contract_creation_block_number) = + deploy_erc20_contract(&foundry, &logger); + info!( + logger, + "Deployed ERC20 contract EXL at {}", erc20_contract_address + ); + let mut ledger_orchestrator = block_on(async { + install_ledger_suite_orchestrator(&application_subnet_runtime, minter.principal()).await + }); + info!( + logger, + "Ledger suite orchestrator installed at {}", + ledger_orchestrator.principal() + ); + + info!( + logger, + "Supporting deposit of ERC-20 and testing deposit flows." + ); + block_on(async { + support_erc20_deposit( + &foundry, + &mut minter, + &mut ledger_orchestrator, + &erc20_contract_address, + &logger, + ) + .await; + + test_cketh_deposit(&foundry, &minter, &logger).await; + test_ckerc20_deposit(&foundry, &minter, &erc20_contract_address, &logger).await; + }); + + info!( + logger, + "Supporting deposit with subaccounts and testing deposit flows." + ); + block_on(async { + support_deposit_with_subaccount(&foundry, &mut minter, &logger).await; + + test_cketh_deposit(&foundry, &minter, &logger).await; + test_ckerc20_deposit(&foundry, &minter, &erc20_contract_address, &logger).await; + test_deposit_with_subaccount(&foundry, &minter, &erc20_contract_address, &logger).await; + }); +} + +async fn activate_threshold_ecdsa( + governance: &Canister<'_>, + subnet: &SubnetSnapshot, + logger: &Logger, +) -> EcdsaKeyId { + let ecdsa_key_id = make_key("some_key"); + let key_id = MasterPublicKeyId::Ecdsa(ecdsa_key_id.clone()); + enable_chain_key_signing(governance, subnet.subnet_id, vec![key_id.clone()], logger).await; + let app_node = subnet.nodes().next().unwrap(); + let app_agent = app_node.build_default_agent_async().await; + let msg_can = MessageCanister::new(&app_agent, app_node.effective_canister_id()).await; + get_public_key_and_test_signature(&key_id, &msg_can, false, logger) + .await + .expect("Should successfully create and verify the signature"); + ecdsa_key_id +} + +// TODO: XC-258: setup EVM RPC canister to target anvil via IPv6 +async fn eth_block_number(foundry: &DeployedUniversalVm) -> BlockNumber { + let foundry_ip = foundry.get_vm().unwrap().ipv6; + let client = Client::new(); + + let url = format!("http://[{:?}]:{:?}", foundry_ip, FOUNDRY_PORT); + + let response = client + .post(&url) + .body(r#"{"method":"eth_blockNumber","params":[],"id":1,"jsonrpc":"2.0"}"#) + .header("Content-Type", "application/json") + .send() + .await + .expect("failed to get the zone"); + + let response_json: serde_json::Value = response + .json() + .await + .expect("failed to decode the response"); + + serde_json::from_value(response_json["result"].clone()).unwrap() +} + +async fn install_cketh_ledger(runtime: &Runtime, minter: Principal) -> LedgerCanister { + let mut cketh_ledger_canister = create_canister(runtime).await; + let ledger_init_args = LedgerArgument::Init( + // See proposal 126309 + InitArgsBuilder::with_symbol_and_name("ckETH", "ckETH") + .with_minting_account(minter) + .with_transfer_fee(2_000_000_000_000_u64) + .with_feature_flags(FeatureFlags { icrc2: true }) + .with_fee_collector_account(Account { + owner: minter, + subaccount: Some([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0x0f, 0xee, + ]), + }) + .with_decimals(18) + .with_max_memo_length(80) + .with_archive_options(ArchiveOptions { + trigger_threshold: 2_000, + num_blocks_to_archive: 1_0000, + node_max_memory_size_bytes: None, + max_message_size_bytes: Some(3_221_225_472), + controller_id: minter.into(), + more_controller_ids: None, + cycles_for_archive_creation: Some(100_000_000_000_000), + max_transactions_per_response: None, + }) + .build(), + ); + let ledger_wasm = + Wasm::from_file(env::var("LEDGER_WASM_PATH").expect("LEDGER_WASM_PATH not set")); + ledger_wasm + .install_with_retries_onto_canister( + &mut cketh_ledger_canister, + Some(Encode!(&ledger_init_args).unwrap()), + None, + ) + .await + .unwrap(); + LedgerCanister { + canister: cketh_ledger_canister, + } +} + +async fn install_cketh_minter<'a>( + mut minter_canister: Canister<'a>, + ecdsa_key_id: &EcdsaKeyId, + cketh_ledger: Principal, +) -> CkEthMinterCanister<'a> { + let minter_init_args = MinterArg::InitArg(minter_init_args(ecdsa_key_id, cketh_ledger)); + let minter_wasm = Wasm::from_file( + env::var("CKETH_MINTER_WASM_PATH").expect("CKETH_MINTER_WASM_PATH not set"), + ); + minter_wasm + .install_with_retries_onto_canister( + &mut minter_canister, + Some(Encode!(&minter_init_args).unwrap()), + None, + ) + .await + .unwrap(); + + CkEthMinterCanister { + canister: minter_canister, + } +} + +fn minter_init_args(ecdsa_key_id: &EcdsaKeyId, cketh_ledger: Principal) -> MinterInitArgs { + MinterInitArgs { + ethereum_network: EthereumNetwork::Mainnet, + ecdsa_key_name: ecdsa_key_id.name.clone(), + ethereum_contract_address: None, + ledger_id: cketh_ledger, + ethereum_block_height: CandidBlockTag::Finalized, + minimum_withdrawal_amount: Nat::from(30_000_000_000_000_000_u64), + next_transaction_nonce: Nat::from(0_u8), + last_scraped_block_number: Nat::from(0_u8), + } +} + +async fn install_ledger_suite_orchestrator( + runtime: &Runtime, + minter: Principal, +) -> LedgerSuiteOrchestratorCanister { + let mut lso_canister = create_canister(runtime).await; + let lso_init_args = OrchestratorArg::InitArg(InitArg { + more_controller_ids: vec![ROOT_CANISTER_ID.get().0], + minter_id: Some(minter), + cycles_management: None, + }); + let lso_wasm = Wasm::from_file( + env::var("LEDGER_SUITE_ORCHESTRATOR_WASM_PATH") + .expect("LEDGER_SUITE_ORCHESTRATOR_WASM_PATH not set"), + ); + lso_wasm + .install_with_retries_onto_canister( + &mut lso_canister, + Some(Encode!(&lso_init_args).unwrap()), + None, + ) + .await + .unwrap(); + + LedgerSuiteOrchestratorCanister { + canister: lso_canister, + } +} + +fn deploy_eth_deposit_helper_contract( + docker_host: &DeployedUniversalVm, + minter_address: &Address, + logger: &slog::Logger, +) -> (Address, BlockNumber) { + let (contract_address, block_number) = deploy_smart_contract( + docker_host, + &EthereumAccount::HelperContractDeployer, + "EthDepositHelper.sol", + "CkEthDeposit", + &minter_address.to_string(), + logger, + ); + assert_eq!( + call_smart_contract( + docker_host, + &contract_address, + "getMinterAddress()(address)", + &[] + ), + minter_address.to_string() + ); + (contract_address, block_number) +} + +async fn support_eth_deposit( + foundry: &DeployedUniversalVm, + minter: &mut CkEthMinterCanister<'_>, + ecdsa_key_id: &EcdsaKeyId, + cketh_ledger: Principal, + logger: &slog::Logger, +) { + let minter_address = minter.minter_address().await.parse().unwrap(); + info!(logger, "Retrieved ckETH minter address {minter_address}"); + + let (eth_deposit_helper_contract_address, eth_deposit_contract_creation_block_number) = + deploy_eth_deposit_helper_contract(foundry, &minter_address, logger); + minter + .reinstall(MinterInitArgs { + ethereum_contract_address: Some(eth_deposit_helper_contract_address.to_string()), + last_scraped_block_number: Nat::from(eth_deposit_contract_creation_block_number), + ..minter_init_args(ecdsa_key_id, cketh_ledger) + }) + .await; + info!( + logger, + "ckETH minter re-installed to set `last_scraped_block_number`" + ); +} + +async fn test_cketh_deposit( + foundry: &DeployedUniversalVm, + minter: &CkEthMinterCanister<'_>, + logger: &slog::Logger, +) { + let minter_info = minter.get_minter_info().await; + let minter_address: Address = minter_info.minter_address.unwrap().parse().unwrap(); + // retrieve helper contract address from minter to ensure ABI does not change + let eth_deposit_helper_contract_address: Address = minter_info + .eth_helper_contract_address + .unwrap() + .parse() + .unwrap(); + let deposit_amount: u128 = 42_000; + assert!(eth_balance_of(foundry, &EthereumAccount::User.address()) > deposit_amount); + + test_eth_deposit( + foundry, + &minter_address, + ð_deposit_helper_contract_address, + "deposit(bytes32)", + &[ENCODED_PRINCIPAL], + logger, + ); +} + +fn test_eth_deposit( + foundry: &DeployedUniversalVm, + minter_address: &Address, + helper_contract_address: &Address, + helper_contract_method: &str, + helper_contract_args: &[&str], + logger: &slog::Logger, +) { + let deposit_amount: u128 = 42_000; + assert!(eth_balance_of(foundry, &EthereumAccount::User.address()) > deposit_amount); + + let minter_balance_before = eth_balance_of(foundry, minter_address); + info!( + logger, + "Depositing {} wei to helper contract {}", deposit_amount, helper_contract_address, + ); + let _cketh_deposit_tx_hash = send_smart_contract( + foundry, + &EthereumAccount::User, + helper_contract_address, + helper_contract_method, + helper_contract_args, + Some(&deposit_amount.to_string()), + ); + let minter_balance_after = eth_balance_of(foundry, minter_address); + + assert_eq!(minter_balance_after - minter_balance_before, deposit_amount); +} + +async fn support_erc20_deposit( + foundry: &DeployedUniversalVm, + minter: &mut CkEthMinterCanister<'_>, + ledger_orchestrator: &mut LedgerSuiteOrchestratorCanister<'_>, + erc20_contract_address: &Address, + logger: &slog::Logger, +) { + let minter_address = minter.minter_address().await.parse().unwrap(); + let (erc20_deposit_helper_contract_address, erc20_contract_creation_block_number) = + deploy_erc20_helper_contract(foundry, &minter_address, logger); + + ledger_orchestrator.register_embedded_wasms().await; + minter + .upgrade(MinterUpgradeArg { + ledger_suite_orchestrator_id: Some(ledger_orchestrator.principal()), + erc20_helper_contract_address: Some(erc20_deposit_helper_contract_address.to_string()), + last_erc20_scraped_block_number: Some(Nat::from(erc20_contract_creation_block_number)), + ..Default::default() + }) + .await; + + ledger_orchestrator + .add_erc20( + AddErc20Arg { + contract: Erc20Contract { + chain_id: Nat::from(1_u8), + address: erc20_contract_address.to_string(), + }, + ledger_init_arg: LedgerInitArg { + transfer_fee: 1_u8.into(), + decimals: 18, + token_name: "ckEXL".to_string(), + token_symbol: "ckEXL".to_string(), + token_logo: "".to_string(), + }, + }, + logger, + ) + .await; + let ckexl_token = try_async("minter supports ckEXL", logger, || { + minter.get_minter_info().map(|info| { + info.supported_ckerc20_tokens + .clone() + .into_iter() + .flatten() + .find(|t| t.ckerc20_token_symbol == "ckEXL") + .ok_or(format!( + "ckEXL not found. Supported ckERC20: {:?}", + info.supported_ckerc20_tokens + )) + }) + }) + .await; + assert_eq!( + &ckexl_token + .erc20_contract_address + .parse::
() + .unwrap(), + erc20_contract_address + ); +} + +async fn test_ckerc20_deposit( + foundry: &DeployedUniversalVm, + minter: &CkEthMinterCanister<'_>, + erc20_contract_address: &Address, + logger: &slog::Logger, +) { + let minter_info = minter.get_minter_info().await; + let minter_address: Address = minter_info.minter_address.unwrap().parse().unwrap(); + // retrieve helper contract address from minter to ensure ABI does not change + let erc20_deposit_helper_contract_address: Address = minter_info + .erc20_helper_contract_address + .unwrap() + .parse() + .unwrap(); + let deposit_amount: u128 = 1000; + + test_erc20_deposit( + foundry, + &minter_address, + erc20_contract_address, + &erc20_deposit_helper_contract_address, + "deposit(address,uint256,bytes32)", + &[ + &erc20_contract_address.to_string(), + &deposit_amount.to_string(), + ENCODED_PRINCIPAL, + ], + deposit_amount, + logger, + ); +} + +fn test_erc20_deposit( + foundry: &DeployedUniversalVm, + minter_address: &Address, + erc20_contract_address: &Address, + helper_contract_address: &Address, + helper_contract_method: &str, + helper_contract_args: &[&str], + deposit_amount: u128, + logger: &slog::Logger, +) { + assert!( + erc20_balance_of( + foundry, + erc20_contract_address, + &EthereumAccount::User.address() + ) > deposit_amount + ); + + let minter_balance_before = erc20_balance_of(foundry, erc20_contract_address, minter_address); + info!( + logger, + "Approving helper smart contract {} to use {} ckEXL", + helper_contract_address, + deposit_amount + ); + send_smart_contract( + foundry, + &EthereumAccount::User, + erc20_contract_address, + "approve(address,uint256)", + &[ + &helper_contract_address.to_string(), + &deposit_amount.to_string(), + ], + None, + ); + info!( + logger, + "Depositing {} ckEXL to helper contract {}", deposit_amount, helper_contract_address, + ); + send_smart_contract( + foundry, + &EthereumAccount::User, + helper_contract_address, + helper_contract_method, + helper_contract_args, + None, + ); + + let minter_balance_after = erc20_balance_of(foundry, erc20_contract_address, minter_address); + assert_eq!(minter_balance_after - minter_balance_before, deposit_amount); +} + +async fn support_deposit_with_subaccount( + foundry: &DeployedUniversalVm, + minter: &mut CkEthMinterCanister<'_>, + logger: &slog::Logger, +) { + let minter_address = minter.minter_address().await.parse().unwrap(); + let ( + deposit_with_subaccount_helper_contract_address, + deposit_with_subaccount_contract_creation_block_number, + ) = deploy_deposit_with_subaccount_helper_contract(foundry, &minter_address, logger); + info!( + logger, + "Deposit with subaccount helper smart contract deployed at {} in block {}", + deposit_with_subaccount_helper_contract_address, + deposit_with_subaccount_contract_creation_block_number + ); + minter + .upgrade(MinterUpgradeArg { + deposit_with_subaccount_helper_contract_address: Some( + deposit_with_subaccount_helper_contract_address.to_string(), + ), + last_deposit_with_subaccount_scraped_block_number: Some(Nat::from( + deposit_with_subaccount_contract_creation_block_number, + )), + ..Default::default() + }) + .await +} + +async fn test_deposit_with_subaccount( + foundry: &DeployedUniversalVm, + minter: &CkEthMinterCanister<'_>, + erc20_contract_address: &Address, + logger: &slog::Logger, +) { + const ENCODED_NO_SUBACCOUNT: &str = + "0x0000000000000000000000000000000000000000000000000000000000000000"; + const ENCODED_SUBACCOUNT: &str = + "0xff00000000000000000000000000000000000000000000000000000000000000"; + + let minter_info = minter.get_minter_info().await; + let minter_address: Address = minter_info.minter_address.unwrap().parse().unwrap(); + // retrieve helper contract address from minter to ensure ABI does not change + let deposit_with_subaccount_helper_contract_address: Address = minter_info + .deposit_with_subaccount_helper_contract_address + .unwrap() + .parse() + .unwrap(); + let erc20_deposit_amount: u128 = 1000; + + for subaccount in [ENCODED_NO_SUBACCOUNT, ENCODED_SUBACCOUNT] { + test_eth_deposit( + foundry, + &minter_address, + &deposit_with_subaccount_helper_contract_address, + "depositEth(bytes32,bytes32)", + &[ENCODED_PRINCIPAL, subaccount], + logger, + ); + + test_erc20_deposit( + foundry, + &minter_address, + erc20_contract_address, + &deposit_with_subaccount_helper_contract_address, + "depositErc20(address,uint256,bytes32,bytes32)", + &[ + &erc20_contract_address.to_string(), + &erc20_deposit_amount.to_string(), + ENCODED_PRINCIPAL, + subaccount, + ], + erc20_deposit_amount, + logger, + ); + } +} + +fn deploy_erc20_helper_contract( + docker_host: &DeployedUniversalVm, + minter_address: &Address, + logger: &slog::Logger, +) -> (Address, BlockNumber) { + let (erc20_deposit_helper_contract_address, block_number) = deploy_smart_contract( + docker_host, + &EthereumAccount::HelperContractDeployer, + "ERC20DepositHelper.sol", + "CkErc20Deposit", + &minter_address.to_string(), + logger, + ); + assert_eq!( + call_smart_contract( + docker_host, + &erc20_deposit_helper_contract_address, + "getMinterAddress()(address)", + &[] + ), + minter_address.to_string() + ); + (erc20_deposit_helper_contract_address, block_number) +} + +fn deploy_erc20_contract( + foundry: &DeployedUniversalVm, + logger: &slog::Logger, +) -> (Address, BlockNumber) { + let initial_supply: u128 = 1_000_000_000_000_000_000_000; + let (erc20_address, block_number) = deploy_smart_contract( + foundry, + &EthereumAccount::Erc20Deployer, + "ERC20.sol", + "EXLToken", + &format!("0x{:x}", initial_supply), + logger, + ); + //deployer has initial supply, transfer some ERC-20 tokens to user to play with + let user_initial_balance = initial_supply / 1_000; + let user_address = EthereumAccount::User.address(); + let _transfer_tx = send_smart_contract( + foundry, + &EthereumAccount::Erc20Deployer, + &erc20_address, + "transfer(address,uint256)", + &[&user_address.to_string(), &user_initial_balance.to_string()], + None, + ); + assert_eq!( + erc20_balance_of(foundry, &erc20_address, &user_address), + user_initial_balance + ); + (erc20_address, block_number) +} + +fn deploy_deposit_with_subaccount_helper_contract( + docker_host: &DeployedUniversalVm, + minter_address: &Address, + logger: &slog::Logger, +) -> (Address, BlockNumber) { + let (deposit_helper_contract_with_subaccount_address, block_number) = deploy_smart_contract( + docker_host, + &EthereumAccount::HelperContractDeployer, + "DepositHelperWithSubaccount.sol", + "CkDeposit", + &minter_address.to_string(), + logger, + ); + assert_eq!( + call_smart_contract( + docker_host, + &deposit_helper_contract_with_subaccount_address, + "getMinterAddress()(address)", + &[] + ), + minter_address.to_string() + ); + ( + deposit_helper_contract_with_subaccount_address, + block_number, + ) +} + +fn erc20_balance_of( + foundry: &DeployedUniversalVm, + contract_address: &Address, + user_address: &Address, +) -> u128 { + let user_balance = call_smart_contract( + foundry, + contract_address, + "balanceOf(address)(uint256)", + &[&user_address.to_string()], + ); //Output is formatted as "1000000000000000000 [1e18]" + let user_balance = user_balance.split_ascii_whitespace().next().unwrap(); + user_balance.parse::().unwrap() +} + +fn eth_balance_of(foundry: &DeployedUniversalVm, user_address: &Address) -> u128 { + foundry.block_on_bash_script(&format!(r#"docker run --net {DOCKER_NETWORK_NAME} --rm foundry "cast balance {user_address} --rpc-url http://anvil:{FOUNDRY_PORT}""#)).unwrap().trim().to_string().parse::().unwrap() +} + +fn deploy_smart_contract( + foundry: &DeployedUniversalVm, + sender: &EthereumAccount, + filename: &str, + contract_name: &str, + constructor_args: &str, + logger: &slog::Logger, +) -> (Address, BlockNumber) { + let sender_private_key = sender.private_key(); + let json_output = foundry.block_on_bash_script(&format!(r#"docker run --net {DOCKER_NETWORK_NAME} --rm -v /config/{filename}:/contracts/{filename} foundry "forge create --json --rpc-url http://anvil:{FOUNDRY_PORT} --private-key {sender_private_key} /contracts/{filename}:{contract_name} --constructor-args {constructor_args}""#)).unwrap(); + info!( + logger, + "Deployed {filename} with constructor args {constructor_args}: {}", json_output + ); + let parsed_output: serde_json::Value = serde_json::from_str(&json_output).unwrap(); + let tx_hash = parsed_output["transactionHash"].as_str().unwrap(); + let tx_receipt_json = foundry.block_on_bash_script(&format!(r#"docker run --net {DOCKER_NETWORK_NAME} --rm foundry "cast receipt --json {tx_hash} --rpc-url http://anvil:{FOUNDRY_PORT}""#)).unwrap(); + let parsed_tx_receipt: serde_json::Value = serde_json::from_str(&tx_receipt_json).unwrap(); + let contract_address = + serde_json::from_value(parsed_tx_receipt["contractAddress"].clone()).unwrap(); + let block_number = serde_json::from_value(parsed_tx_receipt["blockNumber"].clone()).unwrap(); + (contract_address, block_number) +} + +fn call_smart_contract( + foundry: &DeployedUniversalVm, + contract_address: &Address, + method: &str, + args: &[&str], +) -> String { + let arg = args.join(" "); + foundry.block_on_bash_script(&format!(r#"docker run --net {DOCKER_NETWORK_NAME} --rm foundry "cast call {contract_address} '{method}' {arg} --rpc-url http://anvil:{FOUNDRY_PORT}""#)).unwrap().trim().to_string() +} + +fn send_smart_contract( + foundry: &DeployedUniversalVm, + sender: &EthereumAccount, + contract_address: &Address, + method: &str, + args: &[&str], + eth: Option<&str>, +) -> String { + let value = eth.unwrap_or("0"); + let sender_private_key = sender.private_key(); + let arg = args.join(" "); + let json_output = foundry.block_on_bash_script(&format!(r#"docker run --net {DOCKER_NETWORK_NAME} --rm foundry "cast send --json {contract_address} '{method}' {arg} --value {value} --private-key {sender_private_key} --rpc-url http://anvil:{FOUNDRY_PORT}""#)).unwrap().trim().to_string(); + let parsed_output: serde_json::Value = serde_json::from_str(&json_output).unwrap(); + assert_eq!(parsed_output["status"].as_str().unwrap(), "0x1"); + parsed_output["transactionHash"] + .as_str() + .unwrap() + .to_string() +} + +pub async fn create_canister(runtime: &Runtime) -> Canister<'_> { + runtime + .create_canister(Some(u128::MAX)) + .await + .expect("Unable to create canister") +} + +struct LedgerCanister<'a> { + canister: Canister<'a>, +} + +impl LedgerCanister<'_> { + fn principal(&self) -> Principal { + self.canister.canister_id().get().0 + } +} + +struct CkEthMinterCanister<'a> { + canister: Canister<'a>, +} + +impl CkEthMinterCanister<'_> { + async fn minter_address(&self) -> String { + self.canister + .update_("minter_address", candid, ()) + .await + .unwrap() + } + + async fn get_minter_info(&self) -> MinterInfo { + self.canister + .update_("get_minter_info", candid, ()) + .await + .unwrap() + } + + async fn reinstall(&mut self, arg: MinterInitArgs) { + self.canister + .reinstall_with_self_binary(Encode!(&MinterArg::InitArg(arg)).unwrap()) + .await + .unwrap(); + } + + async fn upgrade(&mut self, arg: MinterUpgradeArg) { + self.canister + .upgrade_to_self_binary(Encode!(&MinterArg::UpgradeArg(arg)).unwrap()) + .await + .unwrap(); + } + + fn principal(&self) -> Principal { + self.canister.canister_id().get().0 + } +} + +struct LedgerSuiteOrchestratorCanister<'a> { + canister: Canister<'a>, +} + +impl LedgerSuiteOrchestratorCanister<'_> { + async fn upgrade(&mut self, arg: LedgerSuiteOrchestratorUpgradeArg) { + self.canister + .upgrade_to_self_binary(Encode!(&OrchestratorArg::UpgradeArg(arg)).unwrap()) + .await + .unwrap(); + } + + async fn register_embedded_wasms(&mut self) { + self.upgrade(LedgerSuiteOrchestratorUpgradeArg { + git_commit_hash: Some("6a8e5fca2c6b4e12966638c444e994e204b42989".to_string()), + ..Default::default() + }) + .await; + } + + async fn add_erc20(&mut self, arg: AddErc20Arg, logger: &slog::Logger) { + self.canister + .upgrade_to_self_binary(Encode!(&OrchestratorArg::AddErc20Arg(arg.clone())).unwrap()) + .await + .unwrap(); + let created_canister_ids = ic_system_test_driver::retry_with_msg_async!( + "checking if all canisters are created", + logger, + Duration::from_secs(100), + Duration::from_secs(1), + || async { + let managed_canister_ids = self.canister_ids(arg.contract.clone()).await; + match managed_canister_ids { + None => bail!("No managed canister IDs yet"), + Some(x) if x.ledger.is_some() && x.index.is_some() => Ok(x), + _ => bail!( + "Not all canisters were created yet: {:?}", + managed_canister_ids + ), + } + } + ) + .await + .unwrap_or_else(|e| panic!("Canisters for ERC-20 {:?} were not created: {}", arg, e)); + info!( + &logger, + "Created canister IDs: {} for ERC-20 {:?}", created_canister_ids, arg + ); + } + + async fn canister_ids(&self, contract: Erc20Contract) -> Option { + self.canister + .query_("canister_ids", candid, (contract,)) + .await + .expect("Error while calling canister_ids endpoint") + } + + fn principal(&self) -> Principal { + self.canister.canister_id().get().0 + } +} + +/// Accounts created by Anvil on startup +enum EthereumAccount { + Erc20Deployer, + HelperContractDeployer, + User, +} + +impl EthereumAccount { + const ACCOUNT_0: (Address, &str) = ( + Address::new(hex!("f39Fd6e51aad88F6F4ce6aB8827279cffFb92266")), + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", + ); + const ACCOUNT_1: (Address, &str) = ( + Address::new(hex!("70997970C51812dc3A010C7d01b50e0d17dc79C8")), + "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d", + ); + const ACCOUNT_2: (Address, &str) = ( + Address::new(hex!("3C44CdDdB6a900fa2b585dd299e03d12FA4293BC")), + "0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a", + ); + fn account(&self) -> (Address, &str) { + match self { + EthereumAccount::Erc20Deployer => Self::ACCOUNT_0, + EthereumAccount::HelperContractDeployer => Self::ACCOUNT_1, + EthereumAccount::User => Self::ACCOUNT_2, + } + } + pub fn address(&self) -> Address { + self.account().0 + } + + pub fn private_key(&self) -> &str { + self.account().1 + } +} + +async fn try_async, F, Fut, R>(msg: S, logger: &slog::Logger, f: F) -> R +where + Fut: Future>, + F: Fn() -> Fut, +{ + ic_system_test_driver::retry_with_msg_async!( + msg.as_ref(), + logger, + Duration::from_secs(100), + Duration::from_secs(1), + || async { f().await.map_err(|e| anyhow!(e)) } + ) + .await + .expect("failed despite retries") +} diff --git a/rs/tests/cross_chain/ic_xc_ledger_suite_orchestrator_test.rs b/rs/tests/cross_chain/ic_xc_ledger_suite_orchestrator_test.rs index 646a312c603..3f70b6c63bb 100644 --- a/rs/tests/cross_chain/ic_xc_ledger_suite_orchestrator_test.rs +++ b/rs/tests/cross_chain/ic_xc_ledger_suite_orchestrator_test.rs @@ -114,11 +114,7 @@ fn ic_xc_ledger_suite_orchestrator_test(env: TestEnv) { &ledger_orchestrator, OrchestratorArg::UpgradeArg(UpgradeArg { git_commit_hash: Some("6a8e5fca2c6b4e12966638c444e994e204b42989".to_string()), - ledger_compressed_wasm_hash: None, - index_compressed_wasm_hash: None, - archive_compressed_wasm_hash: None, - cycles_management: None, - manage_ledger_suites: None, + ..Default::default() }), ) .await @@ -237,7 +233,7 @@ async fn install_nns_controlled_canister<'a>( .await; let proposal_result = vote_and_execute_proposal(governance_canister, proposal_id).await; - assert_eq!(proposal_result.status(), ProposalStatus::Executed); + assert_eq!(proposal_result.status, ProposalStatus::Executed as i32); info!( logger, "Installed WASM to {} via NNS proposal", @@ -255,12 +251,12 @@ async fn install_nns_controlled_canister<'a>( canister } -async fn upgrade_ledger_suite_orchestrator_by_nns_proposal<'a>( +async fn upgrade_ledger_suite_orchestrator_by_nns_proposal( logger: &slog::Logger, governance_canister: &Canister<'_>, root_canister: &Canister<'_>, canister_wasm: CanisterModule, - orchestrator: &LedgerOrchestratorCanister<'a>, + orchestrator: &LedgerOrchestratorCanister<'_>, upgrade_arg: OrchestratorArg, ) { use ic_canister_client::Sender; @@ -289,7 +285,7 @@ async fn upgrade_ledger_suite_orchestrator_by_nns_proposal<'a>( .await; let proposal_result = vote_and_execute_proposal(governance_canister, proposal_id).await; - assert_eq!(proposal_result.status(), ProposalStatus::Executed); + assert_eq!(proposal_result.status, ProposalStatus::Executed as i32); info!( logger, "Upgrade ledger suite orchestrator {:?} via NNS proposal", upgrade_arg @@ -435,7 +431,7 @@ struct LedgerOrchestratorCanister<'a> { canister: Canister<'a>, } -impl<'a> LedgerOrchestratorCanister<'a> { +impl LedgerOrchestratorCanister<'_> { async fn call_canister_ids(&self, contract: Erc20Contract) -> Option { self.canister .query_("canister_ids", dfn_candid::candid, (contract,)) diff --git a/rs/tests/crypto/canister_sig_verification_cache_test.rs b/rs/tests/crypto/canister_sig_verification_cache_test.rs index e925fac44b6..1c960a0b0cc 100644 --- a/rs/tests/crypto/canister_sig_verification_cache_test.rs +++ b/rs/tests/crypto/canister_sig_verification_cache_test.rs @@ -58,9 +58,9 @@ const RETRY_DELAY: Duration = Duration::from_secs(1); const NUM_RETRIES: usize = 100; /// Range for the random initialization of the number of users in this test -const NUM_USERS_RANGE: RangeInclusive = 5..=15; +const NUM_USERS_RANGE: RangeInclusive = 5..=10; /// Range for the random initialization of the number of calls per user in this test -const NUM_CALLS_PER_USER_RANGE: RangeInclusive = 5..=15; +const NUM_CALLS_PER_USER_RANGE: RangeInclusive = 5..=10; fn main() -> Result<()> { SystemTestGroup::new() diff --git a/rs/tests/driver/src/canister_api.rs b/rs/tests/driver/src/canister_api.rs index 1dd732f65ea..d575641426a 100644 --- a/rs/tests/driver/src/canister_api.rs +++ b/rs/tests/driver/src/canister_api.rs @@ -738,6 +738,8 @@ impl ListNnsNeuronsRequest { include_neurons_readable_by_caller, include_empty_neurons_readable_by_caller: None, include_public_neurons_in_full_neurons: None, + page_number: None, + page_size: None, }, } } diff --git a/rs/tests/driver/src/driver/asset_canister.rs b/rs/tests/driver/src/driver/asset_canister.rs index ea41ffd3188..a343b74d2e3 100644 --- a/rs/tests/driver/src/driver/asset_canister.rs +++ b/rs/tests/driver/src/driver/asset_canister.rs @@ -16,7 +16,9 @@ use tokio::task; #[async_trait] pub trait DeployAssetCanister { - async fn deploy_asset_canister(&self) -> Result; + async fn deploy_legacy_asset_canister(&self) -> Result; + async fn deploy_long_asset_canister(&self) -> Result; + async fn deploy_asset_canister(&self, wasm_env_var_name: &str) -> Result; } #[async_trait] @@ -24,17 +26,25 @@ impl DeployAssetCanister for T where T: HasTestEnv + Send + Sync, { - async fn deploy_asset_canister(&self) -> Result { + async fn deploy_legacy_asset_canister(&self) -> Result { + self.deploy_asset_canister("ASSET_CANISTER_WASM_PATH").await + } + async fn deploy_long_asset_canister(&self) -> Result { + self.deploy_asset_canister("LONG_ASSET_CANISTER_WASM_PATH") + .await + } + async fn deploy_asset_canister(&self, wasm_env_var_name: &str) -> Result { let env = self.test_env(); let logger = env.logger(); let app_node = env.get_first_healthy_application_node_snapshot(); let canister_id = task::spawn_blocking({ let app_node = app_node.clone(); + let wasm_env_var_name = wasm_env_var_name.to_string(); move || { app_node.create_and_install_canister_with_arg( - &env::var("ASSET_CANISTER_WASM_PATH") - .expect("ASSET_CANISTER_WASM_PATH not set"), + &env::var(wasm_env_var_name.clone()) + .unwrap_or_else(|_| panic!("{} not set", wasm_env_var_name)), None, ) } diff --git a/rs/tests/driver/src/driver/bootstrap.rs b/rs/tests/driver/src/driver/bootstrap.rs index e634bb73fd3..145d85388c8 100644 --- a/rs/tests/driver/src/driver/bootstrap.rs +++ b/rs/tests/driver/src/driver/bootstrap.rs @@ -17,8 +17,7 @@ use crate::driver::{ }, test_setup::InfraProvider, }; -use crate::k8s::datavolume::DataVolumeContentType; -use crate::k8s::images::*; +use crate::k8s::config::LOGS_URL; use crate::k8s::tnet::{TNet, TNode}; use crate::util::block_on; use anyhow::{bail, Result}; @@ -286,26 +285,22 @@ pub fn setup_and_start_vms( let conf_img_path = PathBuf::from(&node.node_path).join(CONF_IMG_FNAME); match InfraProvider::read_attribute(&t_env) { InfraProvider::K8s => { - let url = format!( - "{}/{}", - tnet_node.config_url.clone().expect("missing config_url"), - CONF_IMG_FNAME - ); - info!( - t_env.logger(), - "Uploading image {} to {}", - conf_img_path.clone().display().to_string(), - url.clone() - ); - block_on(upload_image(conf_img_path.as_path(), &url)) - .expect("Failed to upload config image"); - block_on(tnet_node.deploy_config_image( - CONF_IMG_FNAME, - "config", - DataVolumeContentType::Kubevirt, - )) + block_on( + tnet_node.build_oci_config_image( + &conf_img_path, + &tnet_node.name.clone().unwrap(), + ), + ) .expect("deploying config image failed"); block_on(tnet_node.start()).expect("starting vm failed"); + let node_name = tnet_node.name.unwrap(); + info!(t_farm.logger, "starting k8s vm: {}", node_name); + info!( + t_farm.logger, + "vm {} console logs: {}", + node_name.clone(), + LOGS_URL.replace("{job}", &node_name) + ); } InfraProvider::Farm => { let image_spec = AttachImageSpec::new(upload_config_disk_image( @@ -582,6 +577,7 @@ fn node_to_config(node: &Node) -> NodeConfiguration { node_operator_principal_id: None, secret_key_store: node.secret_key_store.clone(), domain: node.domain.clone(), + node_reward_type: None, } } diff --git a/rs/tests/driver/src/driver/boundary_node.rs b/rs/tests/driver/src/driver/boundary_node.rs index 9c639dae589..681e75c62d5 100644 --- a/rs/tests/driver/src/driver/boundary_node.rs +++ b/rs/tests/driver/src/driver/boundary_node.rs @@ -27,8 +27,6 @@ use crate::{ }, test_setup::{GroupSetup, InfraProvider}, }, - k8s::datavolume::DataVolumeContentType, - k8s::images::upload_image, k8s::tnet::TNet, retry_with_msg, util::{block_on, create_agent, create_agent_mapping}, @@ -345,19 +343,10 @@ impl BoundaryNodeWithVm { } else { let tnet = TNet::read_attribute(env); let tnet_node = tnet.nodes.last().expect("no nodes"); - block_on(upload_image( - compressed_img_path, - &format!( - "{}/{}", - tnet_node.config_url.clone().expect("missing config url"), - &mk_compressed_img_path() - ), - ))?; - block_on(tnet_node.deploy_config_image( - &mk_compressed_img_path(), - "config", - DataVolumeContentType::Kubevirt, - )) + block_on( + tnet_node + .build_oci_config_image(&compressed_img_path, &tnet_node.name.clone().unwrap()), + ) .expect("deploying config image failed"); block_on(tnet_node.start()).expect("starting vm failed"); } diff --git a/rs/tests/driver/src/driver/prometheus_vm.rs b/rs/tests/driver/src/driver/prometheus_vm.rs index 4d77f95e04c..fa2dd4c2933 100644 --- a/rs/tests/driver/src/driver/prometheus_vm.rs +++ b/rs/tests/driver/src/driver/prometheus_vm.rs @@ -253,11 +253,12 @@ for name in replica orchestrator node_exporter; do echo '[]' > "{PROMETHEUS_SCRAPING_TARGETS_DIR}/$name.json" done +mkdir -p /config/grafana/dashboards + if uname -a | grep -q Ubuntu; then # k8s chmod g+s /etc/prometheus cp -f /config/prometheus/prometheus.yml /etc/prometheus/prometheus.yml - mkdir -p /config/grafana/dashboards cp -R /config/grafana/dashboards /var/lib/grafana/ chown -R grafana:grafana /var/lib/grafana/dashboards chown -R {SSH_USERNAME}:prometheus /etc/prometheus @@ -271,6 +272,8 @@ fi ) .unwrap(); + let grafana_dashboards_dst = config_dir.join("grafana").join("dashboards"); + std::fs::create_dir_all(&grafana_dashboards_dst).unwrap(); let grafana_dashboards_src = env.get_path(GRAFANA_DASHBOARDS); if let Err(e) = Self::transform_dashboards_root_dir(log.clone(), &grafana_dashboards_src) { warn!( @@ -279,7 +282,6 @@ fi e.to_string() ) } else { - let grafana_dashboards_dst = config_dir.join("grafana").join("dashboards"); debug!(log, "Copying Grafana dashboards from {grafana_dashboards_src:?} to {grafana_dashboards_dst:?} ..."); TestEnv::shell_copy_with_deref(grafana_dashboards_src, grafana_dashboards_dst).unwrap(); } diff --git a/rs/tests/driver/src/driver/simulate_network.rs b/rs/tests/driver/src/driver/simulate_network.rs index 006961bc3af..2947e35578f 100644 --- a/rs/tests/driver/src/driver/simulate_network.rs +++ b/rs/tests/driver/src/driver/simulate_network.rs @@ -192,7 +192,7 @@ impl ProductionSubnetTopology { /// Query: /// https://victoria.ch1-obs1.dfinity.network/select/0/vmui/#/?g0.expr=sum+by+%28ic_node%2Cpeer%29+%28quic_transport_quinn_path_rtt_seconds%7Bic_subnet%3D%22uzr34-akd3s-xrdag-3ql62-ocgoh-ld2ao-tamcv-54e7j-krwgb-2gm4z-oqe%22%7D%29&g0.range_input=21h24m51s870ms&g0.end_input=2024-08-20T08%3A22%3A07&g0.relative_time=none&g0.tenantID=0 -pub const UZR_34_RTT: [(u64, u64, f64); 756] = [ +pub static UZR_34_RTT: [(u64, u64, f64); 756] = [ (1, 2, 0.15598), (1, 3, 0.37478), (1, 4, 0.13583), @@ -952,7 +952,7 @@ pub const UZR_34_RTT: [(u64, u64, f64); 756] = [ ]; /// Query: /// https://victoria.ch1-obs1.dfinity.network/select/0/vmui/#/?g0.expr=sum+by+%28ic_node%2Cpeer%29+%28%0A++rate%28quic_transport_quinn_path_lost_packets%7Bic_subnet%3D%22uzr34-akd3s-xrdag-3ql62-ocgoh-ld2ao-tamcv-54e7j-krwgb-2gm4z-oqe%22%7D%5B7d%5D%29+%2F%0A++rate%28quic_transport_quinn_path_sent_packets%7Bic_subnet%3D%22uzr34-akd3s-xrdag-3ql62-ocgoh-ld2ao-tamcv-54e7j-krwgb-2gm4z-oqe%22%7D%5B7d%5D%29%0A%29&g0.range_input=13d14h36m59s549ms&g0.end_input=2024-06-23T20%3A59%3A19&g0.tab=1&g0.relative_time=none&g0.tenantID=0 -pub const UZR_34_PACKET_LOSS: [(u64, u64, f64); 756] = [ +pub static UZR_34_PACKET_LOSS: [(u64, u64, f64); 756] = [ (1, 2, 0.0015710290841178466), (1, 3, 0.000604650481546998), (1, 4, 0.0014703598418393934), diff --git a/rs/tests/driver/src/k8s/config.rs b/rs/tests/driver/src/k8s/config.rs index 3cfcbc70af1..a5af7cda1b6 100644 --- a/rs/tests/driver/src/k8s/config.rs +++ b/rs/tests/driver/src/k8s/config.rs @@ -15,6 +15,10 @@ pub static TNET_CONFIG_URL: Lazy = Lazy::new(|| { pub static TNET_DNS_SUFFIX: Lazy = Lazy::new(|| var("TNET_DNS_SUFFIX").unwrap_or("tnets.ln1-idx1.dfinity.network".to_string())); +pub static LOGS_URL: Lazy = Lazy::new(|| { + var("TNET_LOGS_URL").unwrap_or("https://grafana.ln1-idx1.dfinity.network/explore?schemaVersion=1&panes=%7B%22z2x%22:%7B%22datasource%22:%22P8E80F9AEF21F6940%22,%22queries%22:%5B%7B%22refId%22:%22A%22,%22expr%22:%22%7Bnamespace%3D%5C%22tnets%5C%22,%20container%3D%5C%22guest-console-log%5C%22,%20job%3D%5C%22tnets%2F{job}%5C%22%7D%20%7C%3D%20%60%60%22,%22queryType%22:%22range%22,%22datasource%22:%7B%22type%22:%22loki%22,%22uid%22:%22P8E80F9AEF21F6940%22%7D,%22editorMode%22:%22builder%22%7D%5D,%22range%22:%7B%22from%22:%22now-2h%22,%22to%22:%22now%22%7D%7D%7D&orgId=1".to_string()) +}); + pub static TNET_BUCKET: Lazy = Lazy::new(|| { var("TNET_BUCKET").unwrap_or("tnet-config-8edb8de3-6057-49e4-9fdb-66a29ee9aeda".to_string()) }); diff --git a/rs/tests/driver/src/k8s/tnet.rs b/rs/tests/driver/src/k8s/tnet.rs index 7c6b1c22a20..39695b93ace 100644 --- a/rs/tests/driver/src/k8s/tnet.rs +++ b/rs/tests/driver/src/k8s/tnet.rs @@ -3,10 +3,12 @@ use regex::Regex; use slog::Logger; use std::collections::BTreeMap; use std::net::{Ipv4Addr, Ipv6Addr}; +use std::path::Path; +use std::process::Command; use std::str::FromStr; use url::Url; -use anyhow::Result; +use anyhow::{bail, Result}; use backon::Retryable; use backon::{ConstantBuilder, ExponentialBuilder}; use k8s_openapi::api::core::v1::{ @@ -115,6 +117,42 @@ impl TNode { } } + pub async fn build_oci_config_image(&self, file_path: &Path, tag: &str) -> Result<()> { + // https://kubevirt.io/user-guide/storage/disks_and_volumes/#containerdisk + // build ctr disk that holds config fat disk for guestos & push it to local ctr registry + // uncompress zst disk (the case with boundary node image) + let command = format!( + "set -xe; \ + mkdir -p /var/sysimage/tnet; \ + if echo {0} | grep -q '.zst'; then \ + uncompressed_file=$(echo {0} | sed 's/.zst$//'); \ + rm -f $uncompressed_file; \ + unzstd -o $uncompressed_file {0}; \ + file_to_copy=$uncompressed_file; \ + else \ + file_to_copy={0}; \ + fi; \ + ctr=$(sudo buildah --root /var/sysimage/tnet from scratch); \ + sudo buildah --root /var/sysimage/tnet copy --chown=107:107 $ctr $file_to_copy /disk/; \ + sudo buildah --root /var/sysimage/tnet commit $ctr harbor-core.harbor.svc.cluster.local/tnet/config:{1}; \ + sudo buildah --root /var/sysimage/tnet push --tls-verify=false --creds 'robot$tnet+tnet:TestingPOC1' harbor-core.harbor.svc.cluster.local/tnet/config:{1}", + file_path.display(), tag + ); + let output = Command::new("bash") + .arg("-c") + .arg(command) + .output() + .expect("Failed to execute command"); + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + bail!( + "Error building and pushing config container config image: {}", + stderr + ); + } + Ok(()) + } + pub async fn deploy_config_image( &self, image_name: &str, diff --git a/rs/tests/driver/src/k8s/virtualmachine.rs b/rs/tests/driver/src/k8s/virtualmachine.rs index 78e4d31cfb7..790e713f9d6 100644 --- a/rs/tests/driver/src/k8s/virtualmachine.rs +++ b/rs/tests/driver/src/k8s/virtualmachine.rs @@ -31,11 +31,14 @@ spec: domain: cpu: cores: {cpus} + model: host-passthrough firmware: bootloader: efi: secureBoot: false devices: + autoattachSerialConsole: true + logSerialConsole: true disks: - name: disk0 disk: @@ -47,23 +50,6 @@ spec: - name: default binding: name: passt - ports: - - port: 22 - - port: 8100 - - port: 8101 - - port: 8102 - - port: 8103 - - port: 8104 - - port: 8105 - - port: 8106 - - port: 8107 - - port: 8108 - - port: 8109 - - port: 8110 - - port: 8111 - - port: 8332 - - port: 18444 - - port: 20443 resources: overcommitGuestOverhead: true requests: @@ -133,11 +119,14 @@ spec: domain: cpu: cores: {cpus} + model: host-passthrough firmware: bootloader: efi: secureBoot: false devices: + autoattachSerialConsole: true + logSerialConsole: true disks: - name: disk0 disk: @@ -150,23 +139,6 @@ spec: - name: default binding: name: passt - ports: - - port: 22 - - port: 80 - - port: 443 - - port: 2497 - - port: 4100 - protocol: UDP - - port: 4444 - - port: 7070 - - port: 8080 - - port: 8332 - - port: 9090 - - port: 9091 - - port: 9100 - - port: 18444 - - port: 19100 - - port: 19531 resources: overcommitGuestOverhead: true requests: @@ -180,8 +152,8 @@ spec: - dataVolume: name: "{name}-guestos" name: disk0 - - dataVolume: - name: "{name}-config" + - containerDisk: + image: "harbor.ln1-idx1.dfinity.network/tnet/config:{name}" name: disk1 "#; diff --git a/rs/tests/driver/src/nns.rs b/rs/tests/driver/src/nns.rs index fe220d34348..1b265a68373 100644 --- a/rs/tests/driver/src/nns.rs +++ b/rs/tests/driver/src/nns.rs @@ -364,8 +364,8 @@ pub async fn vote_execute_proposal_assert_executed( // Wait for the proposal to be accepted and executed. let proposal_info = vote_and_execute_proposal(governance_canister, proposal_id).await; assert_eq!( - proposal_info.status(), - ProposalStatus::Executed, + proposal_info.status, + ProposalStatus::Executed as i32, "proposal {proposal_id} did not execute: {proposal_info:?}" ); } @@ -384,7 +384,7 @@ pub async fn vote_execute_proposal_assert_failed( let expected_message_substring = expected_message_substring.to_string(); // Wait for the proposal to be accepted and executed. let proposal_info = vote_and_execute_proposal(governance_canister, proposal_id).await; - assert_eq!(proposal_info.status(), ProposalStatus::Failed); + assert_eq!(proposal_info.status, ProposalStatus::Failed as i32); let reason = proposal_info.failure_reason.unwrap_or_default(); assert!( reason diff --git a/rs/tests/driver/src/util.rs b/rs/tests/driver/src/util.rs index 363f86d1687..5df338cf51e 100644 --- a/rs/tests/driver/src/util.rs +++ b/rs/tests/driver/src/util.rs @@ -19,8 +19,8 @@ use futures::{ }; use ic_agent::{ agent::{ - http_transport::reqwest_transport::{reqwest, ReqwestTransport}, - CallResponse, EnvelopeContent, RejectCode, RejectResponse, + http_transport::reqwest_transport::reqwest, CallResponse, EnvelopeContent, RejectCode, + RejectResponse, }, export::Principal, identity::BasicIdentity, @@ -29,7 +29,7 @@ use ic_agent::{ use ic_canister_client::{Agent as DeprecatedAgent, Sender}; use ic_config::ConfigOptional; use ic_limits::MAX_INGRESS_TTL; -use ic_management_canister_types::{CanisterStatusResult, EmptyBlob, Payload}; +use ic_management_canister_types::{CanisterStatusResultV2, EmptyBlob, Payload}; use ic_message::ForwardParams; use ic_nervous_system_proto::pb::v1::GlobalTimeOfDay; use ic_nns_constants::{GOVERNANCE_CANISTER_ID, ROOT_CANISTER_ID}; @@ -813,9 +813,7 @@ pub async fn agent_with_identity_mapping( (Some(addr_mapping), Ok(Some(domain))) => builder.resolve(domain, (addr_mapping, 0).into()), _ => builder, }; - let client = builder - .build() - .map_err(|err| AgentError::TransportError(Box::new(err)))?; + let client = builder.build().map_err(AgentError::TransportError)?; agent_with_client_identity(url, client, identity).await } @@ -824,11 +822,9 @@ pub async fn agent_with_client_identity( client: reqwest::Client, identity: impl Identity + 'static, ) -> Result { - let transport = ReqwestTransport::create_with_client(url, client)? - .with_use_call_v3_endpoint() - .with_max_tcp_errors_retries(MAX_TCP_ERROR_RETRIES); let a = Agent::builder() - .with_transport(transport) + .with_url(url) + .with_http_client(client) .with_identity(identity) .with_max_concurrent_requests(MAX_CONCURRENT_REQUESTS) // Ingresses are created with the system time but are checked against the consensus time. @@ -843,7 +839,7 @@ pub async fn agent_with_client_identity( // too further in the future, i.e. greater than x+MAX_INGRESS_TTL in this case. To tolerate // the delays in the progress of consensus, we reduce 30sn from MAX_INGRESS_TTL and set the // expiry_time of ingresses accordingly. - .with_ingress_expiry(Some(MAX_INGRESS_TTL - std::time::Duration::from_secs(30))) + .with_ingress_expiry(MAX_INGRESS_TTL - std::time::Duration::from_secs(30)) .build() .unwrap(); a.fetch_root_key().await?; @@ -1241,7 +1237,7 @@ pub async fn get_balance_via_canister( ) .await .map(|res| { - Decode!(res.as_slice(), CanisterStatusResult) + Decode!(res.as_slice(), CanisterStatusResultV2) .unwrap() .cycles() .into() diff --git a/rs/tests/driver/src/util/delegations.rs b/rs/tests/driver/src/util/delegations.rs index a8a779a5552..e6e0e003959 100644 --- a/rs/tests/driver/src/util/delegations.rs +++ b/rs/tests/driver/src/util/delegations.rs @@ -124,7 +124,7 @@ pub struct AgentWithDelegation<'a> { pub polling_timeout: Duration, } -impl<'a> AgentWithDelegation<'a> { +impl AgentWithDelegation<'_> { async fn send_http_request( &self, method: &str, diff --git a/rs/tests/execution/Cargo.toml b/rs/tests/execution/Cargo.toml index a50cb647351..5c3ede82769 100644 --- a/rs/tests/execution/Cargo.toml +++ b/rs/tests/execution/Cargo.toml @@ -8,7 +8,7 @@ documentation.workspace = true [dependencies] anyhow = { workspace = true } -bitcoincore-rpc = "0.15.0" +bitcoincore-rpc = { workspace = true } candid = { workspace = true } futures = { workspace = true } ic-agent = { workspace = true } diff --git a/rs/tests/execution/btc_get_balance_test.rs b/rs/tests/execution/btc_get_balance_test.rs index 6200c1be7c9..859d0a81d16 100644 --- a/rs/tests/execution/btc_get_balance_test.rs +++ b/rs/tests/execution/btc_get_balance_test.rs @@ -149,8 +149,15 @@ pub fn get_balance(env: TestEnv) { .unwrap(); // Generate an address. - let btc_address = btc_rpc.get_new_address(None, None).unwrap(); - info!(&logger, "Created temporary btc address: {}", btc_address); + let btc_address = btc_rpc + .get_new_address(None, None) + .unwrap() + .assume_checked(); + info!( + &logger, + "Created temporary btc address: {}", + btc_address.to_string() + ); // Mint some blocks for the address we generated. let block = btc_rpc.generate_to_address(101, &btc_address).unwrap(); diff --git a/rs/tests/execution/general_execution_test.rs b/rs/tests/execution/general_execution_test.rs index a317c029208..63702ad15e9 100644 --- a/rs/tests/execution/general_execution_test.rs +++ b/rs/tests/execution/general_execution_test.rs @@ -84,13 +84,7 @@ fn main() -> Result<()> { .add_test(systest!(canister_only_accepts_ingress_with_payload)) .add_test(systest!(canister_rejects_ingress_only_from_one_caller)) .add_test(systest!(query_reply_sizes)) - .add_test(systest!( - mint_cycles_supported_only_on_cycles_minting_canister - )) .add_test(systest!(mint_cycles_not_supported_on_application_subnet)) - .add_test(systest!( - mint_cycles128_supported_only_on_cycles_minting_canister - )) .add_test(systest!(mint_cycles128_not_supported_on_application_subnet)) .add_test(systest!(no_cycle_balance_limit_on_nns_subnet)) .add_test(systest!(app_canister_attempt_initiating_dkg_fails)) diff --git a/rs/tests/execution/general_execution_tests/nns_shielding.rs b/rs/tests/execution/general_execution_tests/nns_shielding.rs index 1c1c69422be..f2c1cb01719 100644 --- a/rs/tests/execution/general_execution_tests/nns_shielding.rs +++ b/rs/tests/execution/general_execution_tests/nns_shielding.rs @@ -47,53 +47,6 @@ const MINT_CYCLES: &str = r#"(module (export "memory" (memory $memory)) )"#; -pub fn mint_cycles_supported_only_on_cycles_minting_canister(env: TestEnv) { - let nns_node = env.get_first_healthy_nns_node_snapshot(); - let specified_id = nns_node.get_last_canister_id_in_allocation_ranges(); - // Check that 'specified_id' is not 'CYCLES_MINTING_CANISTER_ID'. - assert_ne!(specified_id, CYCLES_MINTING_CANISTER_ID.into()); - let nns_agent = nns_node.build_default_agent(); - block_on(async move { - let wasm = wat::parse_str(MINT_CYCLES).unwrap(); - let nns_canister_id: Principal = create_and_install_with_cycles_and_specified_id( - &nns_agent, - specified_id, - wasm.as_slice(), - *INITIAL_CYCLES, - ) - .await; - - let before_balance = get_balance(&nns_canister_id, &nns_agent).await; - assert_eq!(INITIAL_CYCLES.get(), before_balance); - - let res = nns_agent - .update(&nns_canister_id, "test") - .call_and_wait() - .await - .expect_err("should not succeed"); - - assert_eq!( - res, - AgentError::CertifiedReject( - RejectResponse { - reject_code: RejectCode::CanisterError, - reject_message: format!( - "Error from Canister {}: Canister violated contract: ic0.mint_cycles cannot be executed on non Cycles Minting Canister: {} != {}.\nThis is likely an error with the compiler/CDK toolchain being used to build the canister. Please report the error to IC devs on the forum: https://forum.dfinity.org and include which language/CDK was used to create the canister.", - nns_canister_id, nns_canister_id, - CYCLES_MINTING_CANISTER_ID), - error_code: None}) - ); - - let after_balance = get_balance(&nns_canister_id, &nns_agent).await; - assert!( - after_balance == before_balance, - "expected {} == {}", - after_balance, - before_balance - ); - }); -} - pub fn mint_cycles_not_supported_on_application_subnet(env: TestEnv) { let initial_cycles = CANISTER_FREEZE_BALANCE_RESERVE + Cycles::new(5_000_000_000_000); let app_node = env.get_first_healthy_application_node_snapshot(); @@ -141,13 +94,20 @@ fn setup_ucan_and_try_mint128(node: IcNodeSnapshot) -> (AgentError, u128, u128, let agent = node.build_default_agent(); let effective_canister_id = node.get_last_canister_id_in_allocation_ranges(); block_on(async move { - let canister_id = + let mut canister_id = UniversalCanister::new_with_cycles(&agent, effective_canister_id, *INITIAL_CYCLES) .await .unwrap() .canister_id(); - // Check that 'canister_id' is not 'CYCLES_MINTING_CANISTER_ID'. - assert_ne!(canister_id, CYCLES_MINTING_CANISTER_ID.into()); + // Make sure that 'canister_id' is not 'CYCLES_MINTING_CANISTER_ID'. + if canister_id == CYCLES_MINTING_CANISTER_ID.into() { + let effective_canister_id = node.get_last_canister_id_in_allocation_ranges(); + canister_id = + UniversalCanister::new_with_cycles(&agent, effective_canister_id, *INITIAL_CYCLES) + .await + .unwrap() + .canister_id(); + } let before_balance = get_balance(&canister_id, &agent).await; let res = agent .update(&canister_id, "update") @@ -166,28 +126,6 @@ fn setup_ucan_and_try_mint128(node: IcNodeSnapshot) -> (AgentError, u128, u128, }) } -pub fn mint_cycles128_supported_only_on_cycles_minting_canister(env: TestEnv) { - let nns_node = env.get_first_healthy_nns_node_snapshot(); - let (res, before_balance, after_balance, canister_id) = setup_ucan_and_try_mint128(nns_node); - assert_eq!( - res, - AgentError::CertifiedReject( - RejectResponse { - reject_code: RejectCode::CanisterError, - reject_message: format!( - "Error from Canister {}: Canister violated contract: ic0.mint_cycles cannot be executed on non Cycles Minting Canister: {} != {}.\nThis is likely an error with the compiler/CDK toolchain being used to build the canister. Please report the error to IC devs on the forum: https://forum.dfinity.org and include which language/CDK was used to create the canister.", - canister_id, canister_id, - CYCLES_MINTING_CANISTER_ID), - error_code: None}) - ); - assert!( - after_balance == before_balance, - "expected {} == {}", - after_balance, - before_balance - ); -} - pub fn mint_cycles128_not_supported_on_application_subnet(env: TestEnv) { let app_node = env.get_first_healthy_application_node_snapshot(); let (res, before_balance, after_balance, canister_id) = setup_ucan_and_try_mint128(app_node); @@ -200,7 +138,7 @@ pub fn mint_cycles128_not_supported_on_application_subnet(env: TestEnv) { "Error from Canister {}: Canister violated contract: ic0.mint_cycles cannot be executed on non Cycles Minting Canister: {} != {}.\nThis is likely an error with the compiler/CDK toolchain being used to build the canister. Please report the error to IC devs on the forum: https://forum.dfinity.org and include which language/CDK was used to create the canister.", canister_id, canister_id, CYCLES_MINTING_CANISTER_ID), - error_code: None}) + error_code: Some("IC0504".to_string())}) ); assert!( after_balance <= before_balance, diff --git a/rs/tests/execution/general_execution_tests/wasm_chunk_store.rs b/rs/tests/execution/general_execution_tests/wasm_chunk_store.rs index ccee4717e65..a4ee00f238a 100644 --- a/rs/tests/execution/general_execution_tests/wasm_chunk_store.rs +++ b/rs/tests/execution/general_execution_tests/wasm_chunk_store.rs @@ -310,7 +310,7 @@ pub fn install_large_wasm_with_other_store_fails_cross_subnet(env: TestEnv) { let expected_err = AgentError::CertifiedReject(RejectResponse { reject_code: RejectCode::CanisterReject, reject_message: format!("InstallChunkedCode Error: Store canister {} was not found on subnet {} of target canister {}", store_canister_id, app_subnet_id, target_canister_id), - error_code: None, + error_code: Some("IC0406".to_string()), }); assert_eq!(err, expected_err); } diff --git a/rs/tests/financial_integrations/icrc1_agent_test.rs b/rs/tests/financial_integrations/icrc1_agent_test.rs index 8eda3c764e4..7dc06bb5aaf 100644 --- a/rs/tests/financial_integrations/icrc1_agent_test.rs +++ b/rs/tests/financial_integrations/icrc1_agent_test.rs @@ -400,7 +400,7 @@ fn mleaf>(blob: B) -> MixedHashTree { MixedHashTree::Leaf(blob.as_ref().to_vec()) } -pub async fn install_icrc1_ledger<'a>(canister: &mut Canister<'a>, args: &LedgerArgument) { +pub async fn install_icrc1_ledger(canister: &mut Canister<'_>, args: &LedgerArgument) { install_rust_canister_from_path( canister, get_dependency_path(env::var("LEDGER_WASM_PATH").expect("LEDGER_WASM_PATH not set")), diff --git a/rs/tests/financial_integrations/rosetta/BUILD.bazel b/rs/tests/financial_integrations/rosetta/BUILD.bazel index 753ffce0ba6..028ad667b93 100644 --- a/rs/tests/financial_integrations/rosetta/BUILD.bazel +++ b/rs/tests/financial_integrations/rosetta/BUILD.bazel @@ -67,34 +67,3 @@ system_test_nns( "@crate_index//:serde_json", ], ) - -system_test_nns( - name = "rosetta_neuron_voting_test", - extra_head_nns_tags = [], # don't run the head_nns variant on nightly since it aleady runs on long_test. - flaky = True, - tags = [ - "k8s", - "long_test", # since it takes longer than 5 minutes. - ], - target_compatible_with = ["@platforms//os:linux"], # requires libssh that does not build on Mac OS - runtime_deps = - GUESTOS_RUNTIME_DEPS + - UNIVERSAL_VM_RUNTIME_DEPS + [ - "//rs/rosetta-api/icp:ic-rosetta-api", - "//rs/rosetta-api/icp:rosetta_image.tar", - "//rs/tests:rosetta_workspace", - "@rosetta-cli//:rosetta-cli", - ], - deps = [ - "//rs/nns/common", - "//rs/nns/governance/api", - "//rs/rosetta-api/icp:rosetta-api", - "//rs/rosetta-api/icp/test_utils", - "//rs/tests/driver:ic-system-test-driver", - "//rs/tests/financial_integrations/rosetta/rosetta_test_lib", - "@crate_index//:anyhow", - "@crate_index//:ic-agent", - "@crate_index//:rand", - "@crate_index//:slog", - ], -) diff --git a/rs/tests/financial_integrations/rosetta/Cargo.toml b/rs/tests/financial_integrations/rosetta/Cargo.toml index fe173409dcd..9d48f358860 100644 --- a/rs/tests/financial_integrations/rosetta/Cargo.toml +++ b/rs/tests/financial_integrations/rosetta/Cargo.toml @@ -37,10 +37,6 @@ ic-agent = { workspace = true } name = "rosetta_neuron_follow_test" path = "rosetta_neuron_follow_test.rs" -[[bin]] -name = "rosetta_neuron_voting_test" -path = "rosetta_neuron_voting_test.rs" - [[bin]] name = "rosetta_test" path = "rosetta_test.rs" diff --git a/rs/tests/financial_integrations/rosetta/rosetta_neuron_voting_test.rs b/rs/tests/financial_integrations/rosetta/rosetta_neuron_voting_test.rs deleted file mode 100644 index bb335f4abfb..00000000000 --- a/rs/tests/financial_integrations/rosetta/rosetta_neuron_voting_test.rs +++ /dev/null @@ -1,202 +0,0 @@ -use anyhow::Result; -use ic_agent::Identity; -use ic_nns_common::pb::v1::ProposalId; -use ic_nns_governance_api::pb::v1::{ - neuron::DissolveState, proposal::Action, MakeProposalRequest, Motion, Neuron, Proposal, - ProposalActionRequest, -}; -use ic_rosetta_api::{ - convert::neuron_subaccount_bytes_from_public_key, - ledger_client::proposal_info_response::ProposalInfoResponse, - models::{CallResponse, EdKeypair}, - request::{request_result::RequestResult, Request}, - request_types::{RegisterVote, Status}, -}; -use ic_rosetta_test_utils::RequestInfo; -use ic_system_test_driver::driver::group::SystemTestGroup; -use ic_system_test_driver::systest; -use ic_system_test_driver::{ - driver::test_env::TestEnv, - util::{block_on, get_identity, IDENTITY_PEM}, -}; -use rosetta_test_lib::{ - rosetta_client::RosettaApiClient, - setup::{setup, ROSETTA_TESTS_OVERALL_TIMEOUT, ROSETTA_TESTS_PER_TEST_TIMEOUT}, - test_neurons::TestNeurons, - utils::{ - create_governance_client, do_multiple_txn, one_day_from_now_nanos, to_public_key, - NeuronDetails, - }, -}; -use slog::info; -use std::{collections::HashMap, sync::Arc, time::UNIX_EPOCH}; - -const PORT: u32 = 8111; -const VM_NAME: &str = "neuron-voting"; - -fn main() -> Result<()> { - SystemTestGroup::new() - .with_setup(group_setup) - .with_overall_timeout(ROSETTA_TESTS_OVERALL_TIMEOUT) - .with_timeout_per_test(ROSETTA_TESTS_PER_TEST_TIMEOUT) - .add_test(systest!(test)) - .execute_from_args()?; - Ok(()) -} - -fn group_setup(_env: TestEnv) {} - -pub fn test(env: TestEnv) { - let _logger = env.logger(); - - let mut ledger_balances = HashMap::new(); - let now = std::time::SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_secs(); - let one_year_from_now = 60 * 60 * 24 * 365 + now; - - //We need to know the identity of the agent before we create the neurons. - //The controller of the neuron has to be the agent principal otherwise we cannot make proposals and vote on them. - let agent_identity = get_identity(); - let agent_principal = agent_identity.sender().unwrap(); - let agent_keypair = EdKeypair::deserialize_pkcs8_pem(IDENTITY_PEM).unwrap(); - - // Create neurons and set the controller account to be the agent who makes the proposal with that neuron - let mut neurons = TestNeurons::new(2000, &mut ledger_balances); - let neuron_setup = |neuron: &mut Neuron| { - neuron.dissolve_state = Some(DissolveState::DissolveDelaySeconds(one_year_from_now)); - neuron.aging_since_timestamp_seconds = now; - neuron.maturity_e8s_equivalent = 420_000_000; - neuron.controller = Some(agent_principal.into()); - neuron.account = - neuron_subaccount_bytes_from_public_key(&to_public_key(&agent_keypair), rand::random()) - .unwrap() - .to_vec(); - }; - let neuron1 = neurons.create_custom(neuron_setup, 100, &agent_keypair); - - //Setup for non proposal making entities - let neuron_setup = |neuron: &mut Neuron| { - neuron.dissolve_state = Some(DissolveState::DissolveDelaySeconds(one_year_from_now)); - neuron.aging_since_timestamp_seconds = now; - neuron.maturity_e8s_equivalent = 420_000_000; - }; - let neuron2 = neurons.create(neuron_setup); - let neuron3 = neurons.create(neuron_setup); - let neurons = neurons.get_neurons(); - - let proposal = MakeProposalRequest { - title: Some("dummy title".to_string()), - summary: "test".to_string(), - action: Some(ProposalActionRequest::Motion(Motion { - motion_text: "dummy text".to_string(), - })), - ..Default::default() - }; - // Create Rosetta and ledger clients. - let client = setup(&env, PORT, VM_NAME, Some(ledger_balances), Some(neurons)); - let governance_client = create_governance_client(&env, &client); - block_on(async { - let first_proposal = governance_client.make_proposal(&neuron1, &proposal).await; - - //Test the endpoint get_proposal_info of rosetta - let proposal_info_response: CallResponse = client - .get_proposal_info(first_proposal.id) - .await - .unwrap() - .unwrap(); - info!( - _logger, - "Test if received proposal matches the proposal created" - ); - let proposal_info = - ProposalInfoResponse::try_from(Some(proposal_info_response.result)).unwrap(); - - let expected_proposal = Proposal { - title: Some("dummy title".to_string()), - summary: "test".to_string(), - action: Some(Action::Motion(Motion { - motion_text: "dummy text".to_string(), - })), - ..Default::default() - }; - assert_eq!(proposal_info.0.proposal.unwrap(), expected_proposal); - info!(_logger, "Test Register Vote with Vote: Yes"); - test_register_proposal(&client, &neuron2, &first_proposal, &1).await; - info!(_logger, "Test Register Vote with Vote: No"); - test_register_proposal(&client, &neuron3, &first_proposal, &2).await; - - //Test the endpoint get_pending_proposals of rosetta - //Create a couple more proposals so there is something to query - let second_proposal = governance_client.make_proposal(&neuron1, &proposal).await; - let third_proposal = governance_client.make_proposal(&neuron1, &proposal).await; - let pending_proposals = client.get_pending_proposals().await.unwrap(); - info!( - _logger, - "Test if get pending proposal matches the proposals created" - ); - - // Number of pending proposals should be 2 since the first proposal was already voted for - assert_eq!(pending_proposals, vec![expected_proposal.clone(); 2]); - - // Vote on one the second proposal - test_register_proposal(&client, &neuron2, &second_proposal, &1).await; - test_register_proposal(&client, &neuron3, &second_proposal, &2).await; - - // Now it should be 1 proposal - let pending_proposals = client.get_pending_proposals().await.unwrap(); - assert_eq!(pending_proposals, vec![expected_proposal.clone(); 1]); - - // Vote on third and last proposal - test_register_proposal(&client, &neuron2, &third_proposal, &1).await; - test_register_proposal(&client, &neuron3, &third_proposal, &2).await; - - // Now there should be no proposal left to vote for - let pending_proposals = client.get_pending_proposals().await.unwrap(); - assert!(pending_proposals.is_empty()); - }); -} - -async fn test_register_proposal( - ros: &RosettaApiClient, - neuron_info: &NeuronDetails, - proposal_id: &ProposalId, - vote: &i32, -) { - let acc = neuron_info.account_id; - let neuron_index = neuron_info.neuron_subaccount_identifier; - //The caller of the register vote command has to be the same as the controller of the neuron - //let key_pair: Arc = Arc::new(EdKeypair::from_pem(IDENTITY_PEM).unwrap()); - let key_pair: Arc = Arc::new(neuron_info.key_pair.clone()); - - do_multiple_txn( - ros, - &[RequestInfo { - request: Request::RegisterVote(RegisterVote { - account: acc, - proposal: Some(proposal_id.id), - vote: *vote, - neuron_index, - }), - sender_keypair: Arc::clone(&key_pair), - }], - true, - Some(one_day_from_now_nanos()), - None, - ) - .await - .map(|(tx_id, results, _)| { - assert!(!tx_id.is_transfer()); - let request_result = results.operations.first().unwrap(); - assert!(matches!( - request_result, - RequestResult { - _type: Request::RegisterVote(RegisterVote { .. }), - status: Status::Completed, - .. - } - )); - }) - .expect("failed to register vote"); -} diff --git a/rs/tests/idx/BUILD.bazel b/rs/tests/idx/BUILD.bazel index 6021f89c9b5..4cf2f71f7df 100644 --- a/rs/tests/idx/BUILD.bazel +++ b/rs/tests/idx/BUILD.bazel @@ -30,7 +30,7 @@ system_test( flaky = True, tags = [ "k8s", - "system_test_hourly", + "long_test", ], target_compatible_with = ["@platforms//os:linux"], # requires libssh that does not build on Mac OS runtime_deps = GUESTOS_RUNTIME_DEPS + GRAFANA_RUNTIME_DEPS + UNIVERSAL_CANISTER_RUNTIME_DEPS, @@ -50,7 +50,7 @@ system_test( }, flaky = True, tags = [ - "system_test_hourly", + "long_test", ], target_compatible_with = ["@platforms//os:linux"], # requires libssh that does not build on Mac OS runtime_deps = GUESTOS_RUNTIME_DEPS + COUNTER_CANISTER_RUNTIME_DEPS + UNIVERSAL_CANISTER_RUNTIME_DEPS + [ diff --git a/rs/tests/message_routing/BUILD.bazel b/rs/tests/message_routing/BUILD.bazel index ce2bffae45b..ba42cad9e36 100644 --- a/rs/tests/message_routing/BUILD.bazel +++ b/rs/tests/message_routing/BUILD.bazel @@ -40,7 +40,7 @@ system_test( ], tags = [ "k8s", - "system_test_hourly", + "long_test", ], target_compatible_with = ["@platforms//os:linux"], # requires libssh that does not build on Mac OS runtime_deps = GUESTOS_RUNTIME_DEPS, @@ -65,7 +65,7 @@ system_test_nns( env = UNIVERSAL_CANISTER_ENV, tags = [ "k8s", - "system_test_hourly", + "long_test", ], target_compatible_with = ["@platforms//os:linux"], # requires libssh that does not build on Mac OS runtime_deps = @@ -88,7 +88,6 @@ system_test_nns( }, extra_head_nns_tags = ["manual"], # only run this test with the mainnet NNS canisters. tags = [ - "k8s", "system_test_hotfix", ], target_compatible_with = ["@platforms//os:linux"], # requires libssh that does not build on Mac OS diff --git a/rs/tests/message_routing/queues_compatibility_test.rs b/rs/tests/message_routing/queues_compatibility_test.rs index 5078a963acf..f7ecba984de 100644 --- a/rs/tests/message_routing/queues_compatibility_test.rs +++ b/rs/tests/message_routing/queues_compatibility_test.rs @@ -262,6 +262,17 @@ fn test(env: TestEnv) { "_main/rs/replicated_state/replicated_state_test_binary/replicated_state_test_binary", "canister_state::queues::tests::mainnet_compatibility_tests::basic_test", ), + TestCase::new( + // TODO(MR-638): Switch this to a bi-directional test once a version including + // `best_effort_test` has been deployed to mainnet. + TestType::SelfTestOnly, + // TestType::Bidirectional { + // published_binary: "replicated-state-test".to_string(), + // mainnet_version: v.clone(), + // }, + "_main/rs/replicated_state/replicated_state_test_binary/replicated_state_test_binary", + "canister_state::queues::tests::mainnet_compatibility_tests::best_effort_test", + ), TestCase::new( TestType::Bidirectional { published_binary: "replicated-state-test".to_string(), diff --git a/rs/tests/message_routing/rejoin_test_lib/rejoin_test_lib.rs b/rs/tests/message_routing/rejoin_test_lib/rejoin_test_lib.rs index c7e4ac665b8..9846b965fcd 100644 --- a/rs/tests/message_routing/rejoin_test_lib/rejoin_test_lib.rs +++ b/rs/tests/message_routing/rejoin_test_lib/rejoin_test_lib.rs @@ -87,29 +87,7 @@ pub async fn rejoin_test( let message = b"This beautiful prose should be persisted for future generations"; store_and_read_stable(&logger, message, &universal_canister).await; - info!( - logger, - "Checking for the state sync count metrics indicating that a successful state sync has happened" - ); - let res = fetch_metrics::( - &logger, - rejoin_node.clone(), - vec![SUCCESSFUL_STATE_SYNC_DURATION_SECONDS_COUNT], - ) - .await; - assert!(res[SUCCESSFUL_STATE_SYNC_DURATION_SECONDS_COUNT][0] > base_count); - - let res = fetch_metrics::( - &logger, - rejoin_node.clone(), - vec![SUCCESSFUL_STATE_SYNC_DURATION_SECONDS_SUM], - ) - .await; - info!( - logger, - "State sync finishes successfully in {} seconds", - res[SUCCESSFUL_STATE_SYNC_DURATION_SECONDS_SUM][0], - ); + assert_state_sync_has_happened(&logger, rejoin_node, base_count).await; } pub async fn rejoin_test_large_state( @@ -223,29 +201,53 @@ pub async fn rejoin_test_large_state( let message = b"This beautiful prose should be persisted for future generations"; store_and_read_stable(&logger, message, &universal_canister).await; + assert_state_sync_has_happened(&logger, rejoin_node, base_count).await; +} + +async fn assert_state_sync_has_happened( + logger: &slog::Logger, + rejoin_node: IcNodeSnapshot, + base_count: u64, +) { + const NUM_RETRIES: u32 = 200; + const BACKOFF_TIME_MILLIS: u64 = 500; + info!( logger, "Checking for the state sync count metrics indicating that a successful state sync has happened" ); - let res = fetch_metrics::( - &logger, - rejoin_node.clone(), - vec![SUCCESSFUL_STATE_SYNC_DURATION_SECONDS_COUNT], - ) - .await; - assert!(res[SUCCESSFUL_STATE_SYNC_DURATION_SECONDS_COUNT][0] > base_count); - let res = fetch_metrics::( - &logger, - rejoin_node.clone(), - vec![SUCCESSFUL_STATE_SYNC_DURATION_SECONDS_SUM], - ) - .await; - info!( - logger, - "State sync finishes successfully in {} seconds", - res[SUCCESSFUL_STATE_SYNC_DURATION_SECONDS_SUM][0], - ); + // We retry a few times as we observed a pontential race condition where it + // still reads a slightly older value from the metrics, even though the + // state sync has already happened. This is a workaround to make the test + // more robust. + for i in 0..NUM_RETRIES { + let res = fetch_metrics::( + logger, + rejoin_node.clone(), + vec![SUCCESSFUL_STATE_SYNC_DURATION_SECONDS_COUNT], + ) + .await; + if res[SUCCESSFUL_STATE_SYNC_DURATION_SECONDS_COUNT][0] > base_count { + let res = fetch_metrics::( + logger, + rejoin_node.clone(), + vec![SUCCESSFUL_STATE_SYNC_DURATION_SECONDS_SUM], + ) + .await; + info!( + logger, + "State sync finishes successfully in {} seconds", + res[SUCCESSFUL_STATE_SYNC_DURATION_SECONDS_SUM][0], + ); + + return; + } + + info!(logger, "No state sync detected yet, attempt {i}."); + tokio::time::sleep(Duration::from_millis(BACKOFF_TIME_MILLIS)).await; + } + panic!("Couldn't verify that a state sync has happened after {NUM_RETRIES} attempts."); } pub async fn fetch_metrics( diff --git a/rs/tests/nested/BUILD.bazel b/rs/tests/nested/BUILD.bazel index cef3d211389..989db521a93 100644 --- a/rs/tests/nested/BUILD.bazel +++ b/rs/tests/nested/BUILD.bazel @@ -17,6 +17,7 @@ DEPENDENCIES = [ "//rs/types/types", "@crate_index//:anyhow", "@crate_index//:slog", + "@crate_index//:url", ] RUNNER_DEPENDENCIES = [ diff --git a/rs/tests/nested/Cargo.toml b/rs/tests/nested/Cargo.toml index f2e13afbffb..537961faf58 100644 --- a/rs/tests/nested/Cargo.toml +++ b/rs/tests/nested/Cargo.toml @@ -18,6 +18,7 @@ ic-system-test-driver = { path = "../driver" } ic-types = { path = "../../types/types" } ic_consensus_system_test_utils = { path = "../consensus/utils" } slog = { workspace = true } +url = { workspace = true } [[bin]] name = "registration" diff --git a/rs/tests/nested/src/lib.rs b/rs/tests/nested/src/lib.rs index 7bb7eac158d..11d392502f5 100644 --- a/rs/tests/nested/src/lib.rs +++ b/rs/tests/nested/src/lib.rs @@ -12,13 +12,14 @@ use ic_system_test_driver::driver::test_env::{HasIcPrepDir, TestEnv, TestEnvAttr use ic_system_test_driver::driver::test_env_api::*; use ic_system_test_driver::driver::test_setup::GroupSetup; use ic_system_test_driver::nns::add_nodes_to_subnet; -use ic_system_test_driver::util::{block_on, get_nns_node}; +use ic_system_test_driver::util::block_on; use ic_types::hostos_version::HostosVersion; use slog::info; use std::str::FromStr; use std::time::Duration; mod util; +use url::Url; use util::{check_hostos_version, elect_hostos_version, update_nodes_hostos_version}; const HOST_VM_NAME: &str = "host-1"; @@ -36,6 +37,7 @@ pub fn config(env: TestEnv) { // Setup "testnet" InternetComputer::new() .add_fast_single_node_subnet(SubnetType::System) + .with_api_boundary_nodes(1) .with_node_provider(principal) .with_node_operator(principal) .setup_and_start(&env) @@ -65,13 +67,26 @@ fn setup_nested_vms(env: TestEnv) { .expect("Unable to write nested VM."); } - let nns_node = get_nns_node(&env.topology_snapshot()); - let nns_url = nns_node.get_public_url(); + let api_boundary_node = env + .topology_snapshot() + .api_boundary_nodes() + .next() + .expect("No API BN present"); + let api_bn_url = Url::parse(&format!("https://[{}]/", api_boundary_node.get_ip_addr())) + .expect("Could not parse Url"); + let nns_public_key = std::fs::read_to_string(env.prep_dir("").unwrap().root_public_key_path()).unwrap(); - setup_and_start_nested_vms(&nodes, &env, &farm, &group_name, &nns_url, &nns_public_key) - .expect("Unable to start nested VMs."); + setup_and_start_nested_vms( + &nodes, + &env, + &farm, + &group_name, + &api_bn_url, + &nns_public_key, + ) + .expect("Unable to start nested VMs."); } /// Allow the nested GuestOS to install and launch, and check that it can diff --git a/rs/tests/networking/BUILD.bazel b/rs/tests/networking/BUILD.bazel index 468fdda5f63..5d80a448b87 100644 --- a/rs/tests/networking/BUILD.bazel +++ b/rs/tests/networking/BUILD.bazel @@ -59,7 +59,7 @@ system_test_nns( flaky = True, tags = [ # TODO(NET-1710): enable on CI again when the problematic firewall rule in the IC node has been removed. - #"system_test_hourly", + #"long_test", #"system_test_nightly", "manual", ], @@ -81,9 +81,6 @@ system_test_nns( "PROXY_WASM_PATH": "$(rootpath //rs/rust_canisters/proxy_canister:proxy_canister)", }, flaky = True, - tags = [ - "k8s", - ], target_compatible_with = ["@platforms//os:linux"], # requires libssh that does not build on Mac OS runtime_deps = GUESTOS_RUNTIME_DEPS + @@ -133,7 +130,7 @@ system_test_nns( flaky = True, tags = [ "k8s", - "system_test_hourly", + "long_test", ], target_compatible_with = ["@platforms//os:linux"], # requires libssh that does not build on Mac OS runtime_deps = @@ -147,7 +144,7 @@ system_test_nns( name = "firewall_max_connections_test", flaky = True, tags = [ - "system_test_hourly", + "long_test", ], target_compatible_with = ["@platforms//os:linux"], runtime_deps = GUESTOS_RUNTIME_DEPS + UNIVERSAL_VM_RUNTIME_DEPS, @@ -161,7 +158,7 @@ system_test_nns( name = "firewall_priority_test", flaky = True, tags = [ - "system_test_hourly", + "long_test", ], target_compatible_with = ["@platforms//os:linux"], runtime_deps = GUESTOS_RUNTIME_DEPS + UNIVERSAL_VM_RUNTIME_DEPS, diff --git a/rs/tests/networking/canister_http_socks_test.rs b/rs/tests/networking/canister_http_socks_test.rs index ed926ac4078..5e3af452cbc 100644 --- a/rs/tests/networking/canister_http_socks_test.rs +++ b/rs/tests/networking/canister_http_socks_test.rs @@ -94,6 +94,7 @@ pub fn setup(env: TestEnv) { }) .add_nodes(4), ) + .with_api_boundary_nodes(1) .setup_and_start(&env) .expect("failed to setup IC under test"); diff --git a/rs/tests/nns/BUILD.bazel b/rs/tests/nns/BUILD.bazel index c80715e7a96..4cc704d0caf 100644 --- a/rs/tests/nns/BUILD.bazel +++ b/rs/tests/nns/BUILD.bazel @@ -47,7 +47,7 @@ system_test_nns( }, flaky = False, tags = [ - "system_test_hourly", + "long_test", ], target_compatible_with = ["@platforms//os:linux"], # requires libssh that does not build on Mac OS runtime_deps = GUESTOS_RUNTIME_DEPS + BOUNDARY_NODE_GUESTOS_RUNTIME_DEPS + [ diff --git a/rs/tests/nns/ic_mainnet_nns_recovery/src/lib.rs b/rs/tests/nns/ic_mainnet_nns_recovery/src/lib.rs index ead5a7d838f..90af797ee35 100644 --- a/rs/tests/nns/ic_mainnet_nns_recovery/src/lib.rs +++ b/rs/tests/nns/ic_mainnet_nns_recovery/src/lib.rs @@ -312,7 +312,9 @@ fn support_snses( recovered_nns_public_key.clone(), ); - wait_until_ready_for_interaction(env.logger(), new_subnet_node.clone()); + new_subnet_node + .await_status_is_healthy() + .expect("New subnet node should become healthy."); let new_subnet_id = get_app_subnet_id( env.clone(), @@ -851,27 +853,9 @@ fn recover_nns_subnet( if !exit_status.success() { panic!("{cmd:?} failed!"); } - wait_until_ready_for_interaction(logger.clone(), recovered_nns_node); -} - -fn wait_until_ready_for_interaction(logger: Logger, node: IcNodeSnapshot) { - let node_ip = node.get_ip_addr(); - info!( - logger.clone(), - "Waiting until node {node_ip:?} is ready for interaction ..." - ); - ic_system_test_driver::retry_with_msg!( - format!("Check if node {node_ip:?} is ready for interaction"), - logger.clone(), - Duration::from_secs(500), - Duration::from_secs(5), - || node.block_on_bash_script("journalctl | grep -q 'Ready for interaction'") - ) - .unwrap_or_else(|e| { - panic!("Node {node_ip:?} didn't become ready for interaction in time because {e:?}") - }); - - info!(logger, "Node {node_ip:?} is ready for interaction."); + recovered_nns_node + .await_status_is_healthy() + .expect("Recovered NNS node should become healthy."); } fn test_recovered_nns(env: TestEnv, neuron_id: NeuronId, nns_node: IcNodeSnapshot) { diff --git a/rs/tests/nns/nns_cycles_minting_multi_app_subnets_test.rs b/rs/tests/nns/nns_cycles_minting_multi_app_subnets_test.rs index 0de8c3ce99f..997a094e8fd 100644 --- a/rs/tests/nns/nns_cycles_minting_multi_app_subnets_test.rs +++ b/rs/tests/nns/nns_cycles_minting_multi_app_subnets_test.rs @@ -3,7 +3,7 @@ use cycles_minting::{make_user_ed25519, TestAgent, UserHandle}; use cycles_minting_canister::{SubnetFilter, SubnetSelection, CREATE_CANISTER_REFUND_FEE}; use dfn_candid::candid_one; use ic_canister_client::{HttpClient, Sender}; -use ic_management_canister_types::{CanisterIdRecord, CanisterStatusResult}; +use ic_management_canister_types::{CanisterIdRecord, CanisterStatusResultV2}; use ic_nervous_system_common_test_keys::{TEST_USER1_KEYPAIR, TEST_USER1_PRINCIPAL}; use ic_nns_constants::{CYCLES_MINTING_CANISTER_ID, LEDGER_CANISTER_ID}; use ic_registry_subnet_type::SubnetType; @@ -242,7 +242,7 @@ pub fn create_canister_on_specific_subnet_type(env: TestEnv) { node_on_type1_subnet.effective_canister_id(), ); - let _status: CanisterStatusResult = authorized_runtime + let _status: CanisterStatusResultV2 = authorized_runtime .get_management_canister_with_effective_canister_id( canister_on_authorized_subnet.into(), ) @@ -255,7 +255,7 @@ pub fn create_canister_on_specific_subnet_type(env: TestEnv) { .await .unwrap(); - let _status: CanisterStatusResult = type1_runtime + let _status: CanisterStatusResultV2 = type1_runtime .get_management_canister_with_effective_canister_id(canister_on_type1_subnet.into()) .update_from_sender( "canister_status", @@ -266,7 +266,7 @@ pub fn create_canister_on_specific_subnet_type(env: TestEnv) { .await .unwrap(); - let _status: CanisterStatusResult = type1_runtime + let _status: CanisterStatusResultV2 = type1_runtime .get_management_canister_with_effective_canister_id(canister_on_type1_subnet_2.into()) .update_from_sender( "canister_status", @@ -277,7 +277,7 @@ pub fn create_canister_on_specific_subnet_type(env: TestEnv) { .await .unwrap(); - let _status: CanisterStatusResult = authorized_runtime + let _status: CanisterStatusResultV2 = authorized_runtime .get_management_canister_with_effective_canister_id( canister_on_specific_subnet_authorized.into(), ) @@ -290,7 +290,7 @@ pub fn create_canister_on_specific_subnet_type(env: TestEnv) { .await .unwrap(); - let _status: CanisterStatusResult = type1_runtime + let _status: CanisterStatusResultV2 = type1_runtime .get_management_canister_with_effective_canister_id( canister_on_specific_subnet_type1.into(), ) diff --git a/rs/tests/nns/nns_cycles_minting_test.rs b/rs/tests/nns/nns_cycles_minting_test.rs index 0345aec2be9..a09b507be27 100644 --- a/rs/tests/nns/nns_cycles_minting_test.rs +++ b/rs/tests/nns/nns_cycles_minting_test.rs @@ -13,7 +13,7 @@ use ic_crypto_tree_hash::MixedHashTree; use ic_crypto_utils_threshold_sig_der::threshold_sig_public_key_from_der; use ic_ledger_core::tokens::CheckedAdd; use ic_limits::SMALL_APP_SUBNET_MAX_SIZE; -use ic_management_canister_types::{CanisterIdRecord, CanisterStatusResult}; +use ic_management_canister_types::{CanisterIdRecord, CanisterStatusResultV2}; use ic_nervous_system_clients::canister_status::CanisterStatusResult as RootCanisterStatusResult; use ic_nervous_system_common_test_keys::{ TEST_NEURON_1_ID, TEST_NEURON_1_OWNER_KEYPAIR, TEST_USER1_KEYPAIR, TEST_USER1_PRINCIPAL, @@ -545,7 +545,7 @@ pub fn test(env: TestEnv) { let nonce_size = 8; // see RemoteTestRuntime::get_nonce_vec - let new_canister_status: CanisterStatusResult = + let new_canister_status: CanisterStatusResultV2 = runtime_from_url(app_node.get_public_url(), app_node.effective_canister_id()) .get_management_canister_with_effective_canister_id(new_canister_id.into()) .update_from_sender( @@ -634,7 +634,7 @@ pub fn test(env: TestEnv) { let nonce_size = 8; // see RemoteTestRuntime::get_nonce_vec - let new_canister_status: CanisterStatusResult = + let new_canister_status: CanisterStatusResultV2 = runtime_from_url(app_node.get_public_url(), app_node.effective_canister_id()) .get_management_canister_with_effective_canister_id(new_canister_id.into()) .update_from_sender( @@ -698,7 +698,7 @@ pub fn test(env: TestEnv) { .unwrap(); /* Check the controller / cycles balance. */ - let new_canister_status: CanisterStatusResult = nns + let new_canister_status: CanisterStatusResultV2 = nns .get_management_canister_with_effective_canister_id(new_canister_id.into()) .update_from_sender( "canister_status", @@ -730,7 +730,7 @@ pub fn test(env: TestEnv) { .unwrap(); /* Check the controller / cycles balance. */ - let new_canister_status: CanisterStatusResult = nns + let new_canister_status: CanisterStatusResultV2 = nns .get_management_canister_with_effective_canister_id(new_canister_id.into()) .update_from_sender( "canister_status", diff --git a/rs/tests/nns/node_removal_from_registry_test.rs b/rs/tests/nns/node_removal_from_registry_test.rs index 5ac3653d214..a68d7b3333c 100644 --- a/rs/tests/nns/node_removal_from_registry_test.rs +++ b/rs/tests/nns/node_removal_from_registry_test.rs @@ -116,23 +116,20 @@ pub fn test(env: TestEnv) { ) .await; vote_execute_proposal_assert_executed(&governance_canister, proposal_id).await; - // Confirm that the node was indeed removed by sending the proposal again and asserting failure. - let proposal_id = submit_external_proposal_with_test_id( - &governance_canister, - NnsFunction::RemoveNodes, - RemoveNodesPayload { - node_ids: vec![unassigned_node_id], - }, - ) - .await; - vote_execute_proposal_assert_failed( - &governance_canister, - proposal_id, - format!( - "Aborting node removal: Node Id {} not found in the registry", - unassigned_node_id - ), - ) - .await; + + // Confirm that the node was indeed removed by checking the unassigned node list in the registry. + topology + .block_for_newer_registry_version() + .await + .expect("Could not obtain updated registry."); + let topology = env.topology_snapshot(); + assert_eq!( + topology + .unassigned_nodes() + .filter(|node| node.node_id == unassigned_node_id) + .map(|node| node.node_id) + .collect::>(), + vec![] + ); }); } diff --git a/rs/tests/nns/sns/lib/src/sns_aggregator.rs b/rs/tests/nns/sns/lib/src/sns_aggregator.rs index 8e1e1bd5397..bafb8e76c3b 100644 --- a/rs/tests/nns/sns/lib/src/sns_aggregator.rs +++ b/rs/tests/nns/sns/lib/src/sns_aggregator.rs @@ -143,9 +143,9 @@ impl AggregatorClient { format!("/{AGGREGATOR_CANISTER_VERSION}{AGGREGATOR_CANISTER_PATH}") } - async fn http_get_request<'agent>( + async fn http_get_request( log: &Logger, - canister: &HttpRequestCanister<'agent>, + canister: &HttpRequestCanister<'_>, relative_url: String, ) -> Result { let (response,) = canister @@ -171,9 +171,9 @@ impl AggregatorClient { } } - pub async fn http_get_favicon<'agent>( + pub async fn http_get_favicon( log: &Logger, - canister: &HttpRequestCanister<'agent>, + canister: &HttpRequestCanister<'_>, ) -> Result> { let url = "/favicon.ico".to_string(); Self::http_get_request(log, canister, url) @@ -181,9 +181,9 @@ impl AggregatorClient { .map(|res| res.body) } - pub async fn http_get_asset<'agent>( + pub async fn http_get_asset( log: &Logger, - canister: &HttpRequestCanister<'agent>, + canister: &HttpRequestCanister<'_>, ) -> Result> { let url = Self::aggregator_http_endpoint(); Self::http_get_request(log, canister, url) @@ -191,9 +191,9 @@ impl AggregatorClient { .map(|res| res.body) } - pub async fn extract_first_sns_sale_config<'agent>( + pub async fn extract_first_sns_sale_config( log: &Logger, - canister: &HttpRequestCanister<'agent>, + canister: &HttpRequestCanister<'_>, ) -> Result { let asset_bytes = Self::http_get_asset(log, canister).await.unwrap(); let asset: Value = serde_json::from_slice(asset_bytes.as_slice())?; @@ -219,9 +219,9 @@ impl AggregatorClient { } } - async fn sub_asset<'agent, P>( + async fn sub_asset

( log: &Logger, - canister: &HttpRequestCanister<'agent>, + canister: &HttpRequestCanister<'_>, extract_sub_asset: &P, timeout: Duration, ) -> RequestOutcome @@ -263,9 +263,9 @@ impl AggregatorClient { ) } - pub async fn first_sns_asset<'agent>( + pub async fn first_sns_asset( log: &Logger, - canister: &HttpRequestCanister<'agent>, + canister: &HttpRequestCanister<'_>, timeout: Duration, ) -> RequestOutcome { let extract_sub_asset = move |asset| { @@ -275,9 +275,9 @@ impl AggregatorClient { Self::sub_asset(log, canister, &extract_sub_asset, timeout).await } - pub async fn first_swap_params<'agent>( + pub async fn first_swap_params( log: &Logger, - canister: &HttpRequestCanister<'agent>, + canister: &HttpRequestCanister<'_>, timeout: Duration, ) -> RequestOutcome { let extract_sub_asset = move |asset| { diff --git a/rs/tests/src/rosetta_tests/tests.rs b/rs/tests/src/rosetta_tests/tests.rs new file mode 100644 index 00000000000..e69de29bb2d diff --git a/rs/tests/system_tests.bzl b/rs/tests/system_tests.bzl index e5a9806b866..d37e6fe7c0c 100644 --- a/rs/tests/system_tests.bzl +++ b/rs/tests/system_tests.bzl @@ -276,6 +276,12 @@ def system_test( if uses_boundary_guestos: icos_images["ENV_DEPS__BOUNDARY_GUESTOS_DISK_IMG_TAR_ZST_CAS_URL"] = "//ic-os/boundary-guestos/envs/dev:disk-img.tar.zst" + # set "local" tag for k8s system tests due to rootful container image builds + is_k8s = select({ + "//rs/tests:k8s": True, + "//conditions:default": False, + }) + run_system_test( name = name, src = test_driver_target, @@ -284,7 +290,7 @@ def system_test( env = env, icos_images = icos_images, env_inherit = env_inherit, - tags = tags + ["requires-network", "system_test"] + + tags = tags + ["requires-network", "system_test"] + (["local"] if is_k8s else []) + (["manual"] if "experimental_system_test_colocation" in tags else []), target_compatible_with = ["@platforms//os:linux"], timeout = test_timeout, diff --git a/rs/tla_instrumentation/BUILD.bazel b/rs/tla_instrumentation/BUILD.bazel index 372be6df33f..82649186435 100644 --- a/rs/tla_instrumentation/BUILD.bazel +++ b/rs/tla_instrumentation/BUILD.bazel @@ -73,7 +73,10 @@ rust_test( "TLA_APALACHE_BIN": "$(rootpath @tla_apalache//:bin/apalache-mc)", "TLA_MODULES": "$(locations :tla_models)", }, - proc_macro_deps = [":proc_macros"], + proc_macro_deps = [ + ":proc_macros", + "@crate_index//:async-trait", + ], toolchains = ["@bazel_tools//tools/jdk:current_java_runtime"], deps = [ ":local_key", diff --git a/rs/tla_instrumentation/local_key/src/lib.rs b/rs/tla_instrumentation/local_key/src/lib.rs index 803bb734775..c63de2a48c7 100644 --- a/rs/tla_instrumentation/local_key/src/lib.rs +++ b/rs/tla_instrumentation/local_key/src/lib.rs @@ -185,7 +185,7 @@ impl LocalKey { slot: &'a mut Option, } - impl<'a, T: 'static> Drop for Guard<'a, T> { + impl Drop for Guard<'_, T> { fn drop(&mut self) { // This should not panic. // @@ -414,7 +414,7 @@ where struct TransparentOption<'a, T> { value: &'a Option, } - impl<'a, T: fmt::Debug> fmt::Debug for TransparentOption<'a, T> { + impl fmt::Debug for TransparentOption<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.value.as_ref() { Some(value) => value.fmt(f), diff --git a/rs/tla_instrumentation/tla_instrumentation/Cargo.toml b/rs/tla_instrumentation/tla_instrumentation/Cargo.toml index 88fd1e7f227..601256e2254 100644 --- a/rs/tla_instrumentation/tla_instrumentation/Cargo.toml +++ b/rs/tla_instrumentation/tla_instrumentation/Cargo.toml @@ -14,6 +14,7 @@ sha2 = { workspace = true } uuid = { workspace = true } [dev-dependencies] +async-trait = { workspace = true } tokio-test = { workspace = true } local_key = { path = "../local_key" } tla_instrumentation_proc_macros = { path = "../tla_instrumentation_proc_macros" } diff --git a/rs/tla_instrumentation/tla_instrumentation/tests/multiple_calls.rs b/rs/tla_instrumentation/tla_instrumentation/tests/multiple_calls.rs index d97215e7f82..ed2b4fabe5c 100644 --- a/rs/tla_instrumentation/tla_instrumentation/tests/multiple_calls.rs +++ b/rs/tla_instrumentation/tla_instrumentation/tests/multiple_calls.rs @@ -12,6 +12,8 @@ use tla_instrumentation::{ }; use tla_instrumentation_proc_macros::{tla_function, tla_update_method}; +use async_trait::async_trait; + mod common; use common::check_tla_trace; @@ -121,36 +123,47 @@ struct StructCanister { static mut GLOBAL: StructCanister = StructCanister { counter: 0 }; -#[tla_function] -async fn call_maker() { - tla_log_request!( - "WaitForResponse", - Destination::new("othercan"), - "Target_Method", - 2_u64 - ); - tla_log_response!( - Destination::new("othercan"), - TlaValue::Variant { - tag: "Ok".to_string(), - value: Box::new(3_u64.to_tla_value()) - } - ); +struct CallMaker {} + +#[async_trait] +trait CallMakerTrait { + async fn call_maker(&self); +} + +#[async_trait] +impl CallMakerTrait for CallMaker { + #[tla_function(async_trait_fn = true)] + async fn call_maker(&self) { + tla_log_request!( + "WaitForResponse", + Destination::new("othercan"), + "Target_Method", + 2_u64 + ); + tla_log_response!( + Destination::new("othercan"), + TlaValue::Variant { + tag: "Ok".to_string(), + value: Box::new(3_u64.to_tla_value()) + } + ); + } } impl StructCanister { #[tla_update_method(my_f_desc())] pub async fn my_method(&mut self) { self.counter += 1; + let call_maker = CallMaker {}; let mut my_local: u64 = self.counter; tla_log_locals! {my_local: my_local}; tla_log_label!("Phase1"); - call_maker().await; + call_maker.call_maker().await; self.counter += 1; my_local = self.counter; tla_log_locals! {my_local: my_local}; tla_log_label!("Phase2"); - call_maker().await; + call_maker.call_maker().await; self.counter += 1; my_local = self.counter; // Note that this would not be necessary (and would be an error) if diff --git a/rs/tla_instrumentation/tla_instrumentation_proc_macros/src/lib.rs b/rs/tla_instrumentation/tla_instrumentation_proc_macros/src/lib.rs index 0935869facf..a729c93cf47 100644 --- a/rs/tla_instrumentation/tla_instrumentation_proc_macros/src/lib.rs +++ b/rs/tla_instrumentation/tla_instrumentation_proc_macros/src/lib.rs @@ -1,7 +1,7 @@ use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use syn::{parse_macro_input, ItemFn}; +use syn::{parse_macro_input, AttributeArgs, ItemFn, Lit, Meta, NestedMeta}; /// Used to annotate top-level methods (which de-facto start an update call) #[proc_macro_attribute] @@ -199,11 +199,18 @@ pub fn tla_update_method(attr: TokenStream, item: TokenStream) -> TokenStream { output.into() } +/// Instructs the TLA instrumentation to "stack" PlusCal labels when entering a function. +/// This is useful when a Rust function makes inter-canister calls and is called from multiple +/// locations in the same update method. In this case, we want the labels in the TLA trace to +/// reflect the different call sites. We do this by "stacking" the labels; for example, if an +/// update method `upd` calls a function `foo` from two different locations, where the first location +/// has the label `A` and the second location has the label `B`, and `foo` adds a label `Call` when +/// it performs the call, then the labels in the TLA trace will be `A_Call` and `B_Call`. #[proc_macro_attribute] -pub fn tla_function(_attr: TokenStream, item: TokenStream) -> TokenStream { +pub fn tla_function(attr: TokenStream, item: TokenStream) -> TokenStream { // Parse the input tokens of the attribute and the function let input_fn = parse_macro_input!(item as ItemFn); - + let args = parse_macro_input!(attr as AttributeArgs); let mut modified_fn = input_fn.clone(); // Deconstruct the function elements @@ -211,61 +218,86 @@ pub fn tla_function(_attr: TokenStream, item: TokenStream) -> TokenStream { attrs, vis, sig, - block: _, + block: body, } = input_fn; let mangled_name = syn::Ident::new(&format!("_tla_impl_{}", sig.ident), sig.ident.span()); modified_fn.sig.ident = mangled_name.clone(); - let has_receiver = sig.inputs.iter().any(|arg| match arg { - syn::FnArg::Receiver(_) => true, - syn::FnArg::Typed(_) => false, - }); - // Creating the modified original function which calls f_impl - let args: Vec<_> = sig - .inputs - .iter() - .filter_map(|arg| match arg { - syn::FnArg::Receiver(_) => None, - syn::FnArg::Typed(pat_type) => Some(&*pat_type.pat), - }) - .collect(); - let asyncness = sig.asyncness; - - let call = match (asyncness.is_some(), has_receiver) { - (true, true) => quote! { self.#mangled_name(#(#args),*).await }, - (true, false) => quote! { #mangled_name(#(#args),*).await }, - (false, true) => quote! { self.#mangled_name(#(#args),*) }, - (false, false) => quote! { #mangled_name(#(#args),*) }, - }; - - let output = quote! { - #modified_fn - - #(#attrs)* #vis #sig { - TLA_INSTRUMENTATION_STATE.try_with(|state| { - { - let mut handler_state = state.handler_state.borrow_mut(); - handler_state.context.call_function(); + let mut async_trait_fn = false; + + // Examine each attribute argument + for arg in args { + if let NestedMeta::Meta(Meta::NameValue(name_value)) = arg { + if name_value.path.is_ident("async_trait_fn") { + if let Lit::Bool(lit_bool) = name_value.lit { + async_trait_fn = lit_bool.value(); } - }).unwrap_or_else(|e| - // TODO(RES-152): fail if there's an error and if we're in some kind of strict mode? - () - ); + } + } + } + + // We need three different ways to invoke the wrapped function. + // One is when the function is in an async_trait, as this will get desugared + // into a Pin>. There, we will want to await the result even though + // the function itself is not async. The other is when the function is async, + // in which case we want to await the result. The last is when the function is + // synchronous, in which case we just want to call it. + let call = if async_trait_fn { + quote! { + #body.await + } + } else if asyncness.is_some() { + quote! { + (|| async move { + #body + })().await + } + } else { + quote! { + (move || { + #body + })() + } + }; + let with_instrumentation = quote! { + TLA_INSTRUMENTATION_STATE.try_with(|state| { + { + let mut handler_state = state.handler_state.borrow_mut(); + handler_state.context.call_function(); + } + }).unwrap_or_else(|e| { + // TODO(RES-152): fail if there's an error and if we're in some kind of strict mode? + println!("Couldn't find TLA_INSTRUMENTATION_STATE when calling a tla_function; ignoring for the moment"); + }); + let res = #call; + TLA_INSTRUMENTATION_STATE.try_with(|state| { + { + let mut handler_state = state.handler_state.borrow_mut(); + handler_state.context.return_from_function(); + } + }).unwrap_or_else(|e| + // TODO(RES-152): fail if there's an error and if we're in some kind of strict mode? + () + ); + res + }; - let res = #call; - TLA_INSTRUMENTATION_STATE.try_with(|state| { - { - let mut handler_state = state.handler_state.borrow_mut(); - handler_state.context.return_from_function(); - } - }).unwrap_or_else(|e| - // TODO(RES-152): fail if there's an error and if we're in some kind of strict mode? - () - ); - res + let output = if async_trait_fn { + quote! { + #(#attrs)* #vis #sig { + Box::pin(async move { + #with_instrumentation + }) + } + } + } else { + quote! { + #(#attrs)* #vis #sig { + #with_instrumentation + } } }; diff --git a/rs/tree_deserializer/src/types.rs b/rs/tree_deserializer/src/types.rs index bcd8b5a1c69..80dbd207a10 100644 --- a/rs/tree_deserializer/src/types.rs +++ b/rs/tree_deserializer/src/types.rs @@ -52,7 +52,7 @@ impl<'de> serde::Deserialize<'de> for Leb128EncodedU64 { { struct LebU64Visitor; - impl<'de> serde::de::Visitor<'de> for LebU64Visitor { + impl serde::de::Visitor<'_> for LebU64Visitor { type Value = Leb128EncodedU64; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/rs/types/management_canister_types/BUILD.bazel b/rs/types/management_canister_types/BUILD.bazel index a415f88fecc..39e3c90bd16 100644 --- a/rs/types/management_canister_types/BUILD.bazel +++ b/rs/types/management_canister_types/BUILD.bazel @@ -1,7 +1,63 @@ load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test") load("//bazel:fuzz_testing.bzl", "DEFAULT_RUSTC_FLAGS_FOR_FUZZING") -package(default_visibility = ["//visibility:public"]) +# This library is private to the execution environment. +# These crates are considered direct users of the management +# types and may always depend on this crate directly. +permanent_whitelist = [ + "//rs/execution_environment:__subpackages__", + "//rs/cycles_account_manager:__subpackages__", + "//rs/messaging:__subpackages__", + "//rs/management_canister_types/fuzz:__subpackages__", +] + +# These crates depend on this library directly for historical reasons +# and must move to the published version of this library on crates.io. +temporary_whitelist = [ + "//rs/artifact_pool:__subpackages__", + "//rs/bitcoin:__subpackages__", + "//rs/canister_client:__subpackages__", + "//rs/canister_sandbox:__subpackages__", # permanent? + "//rs/canonical_state:__subpackages__", # permanent? + "//rs/consensus:__subpackages__", + "//rs/crypto:__subpackages__", + "//rs/determinism_test:__subpackages__", # permanent? + "//rs/drun:__subpackages__", + "//rs/ethereum:__subpackages__", + "//rs/https_outcalls:__subpackages__", # permanent? + "//rs/ingress_manager:__subpackages__", # permanent? + "//rs/interfaces:__subpackages__", + "//rs/ledger_suite:__subpackages__", + "//rs/nervous_system:__subpackages__", + "//rs/nns/cmc:__subpackages__", + "//rs/nns/governance:__subpackages__", + "//rs/nns/handlers:__subpackages__", + "//rs/nns/integration_tests:__subpackages__", + "//rs/nns/sns-wasm:__subpackages__", + "//rs/nns/test_utils:__subpackages__", + "//rs/orchestrator:__subpackages__", + "//rs/pocket_ic_server:__subpackages__", + "//rs/prep:__subpackages__", + "//rs/recovery:__subpackages__", + "//rs/registry:__subpackages__", + "//rs/replica:__subpackages__", + "//rs/replica_tests:__subpackages__", + "//rs/replicated_state:__subpackages__", # permanent? + "//rs/rust_canisters:__subpackages__", + "//rs/artifact_pool:__subpackages__", + "//rs/sns:__subpackages__", + "//rs/starter:__subpackages__", + "//rs/state_layout:__subpackages__", # permanent? + "//rs/state_machine_tests:__subpackages__", # permanent? + "//rs/state_manager:__subpackages__", # permanent? + "//rs/system_api:__subpackages__", # permanent? + "//rs/test_utilities:__subpackages__", # some subpackages permanent? + "//rs/tests:__subpackages__", # some subpackages permanent? + "//rs/types:__subpackages__", + "//rs/workload_generator:__subpackages__", +] + +package(default_visibility = permanent_whitelist + temporary_whitelist) rust_library( name = "management_canister_types", diff --git a/rs/types/management_canister_types/src/lib.rs b/rs/types/management_canister_types/src/lib.rs index e55a3d88925..67acd41a50b 100644 --- a/rs/types/management_canister_types/src/lib.rs +++ b/rs/types/management_canister_types/src/lib.rs @@ -956,62 +956,6 @@ impl DefiniteCanisterSettingsArgs { impl Payload<'_> for DefiniteCanisterSettingsArgs {} -/// The deprecated version of CanisterStatusResult that is being -/// used by NNS canisters. -#[derive(Eq, PartialEq, Debug, CandidType, Deserialize)] -pub struct CanisterStatusResult { - status: CanisterStatusType, - module_hash: Option>, - controller: candid::Principal, - memory_size: candid::Nat, - cycles: candid::Nat, - // this is for compat with Spec 0.12/0.13 - balance: Vec<(Vec, candid::Nat)>, -} - -impl CanisterStatusResult { - pub fn new( - status: CanisterStatusType, - module_hash: Option>, - controller: PrincipalId, - memory_size: NumBytes, - cycles: u128, - ) -> Self { - Self { - status, - module_hash, - controller: candid::Principal::from_text(controller.to_string()).unwrap(), - memory_size: candid::Nat::from(memory_size.get()), - cycles: candid::Nat::from(cycles), - // the following is spec 0.12/0.13 compat; - // "\x00" denotes cycles - balance: vec![(vec![0], candid::Nat::from(cycles))], - } - } - - pub fn status(&self) -> CanisterStatusType { - self.status.clone() - } - - pub fn module_hash(&self) -> Option> { - self.module_hash.clone() - } - - pub fn controller(&self) -> PrincipalId { - PrincipalId::try_from(self.controller.as_slice()).unwrap() - } - - pub fn memory_size(&self) -> NumBytes { - NumBytes::from(self.memory_size.0.to_u64().unwrap()) - } - - pub fn cycles(&self) -> u128 { - self.cycles.0.to_u128().unwrap() - } -} - -impl Payload<'_> for CanisterStatusResult {} - #[derive(Eq, PartialEq, Debug, CandidType, Deserialize)] pub struct QueryStats { num_calls_total: candid::Nat, @@ -1220,9 +1164,11 @@ pub enum CanisterInstallMode { pub enum WasmMemoryPersistence { /// Retain the main memory across upgrades. /// Used for enhanced orthogonal persistence, as implemented in Motoko + #[serde(rename = "keep")] Keep, /// Reinitialize the main memory on upgrade. /// Default behavior without enhanced orthogonal persistence. + #[serde(rename = "replace")] Replace, } @@ -1473,9 +1419,19 @@ impl From for CanisterInstallMode { /// The function is lossy, hence it should be avoided when possible. fn from(item: CanisterInstallModeV2) -> Self { match item { - CanisterInstallModeV2::Install => CanisterInstallMode::Install, - CanisterInstallModeV2::Reinstall => CanisterInstallMode::Reinstall, - CanisterInstallModeV2::Upgrade(_) => CanisterInstallMode::Upgrade, + CanisterInstallModeV2::Install => Self::Install, + CanisterInstallModeV2::Reinstall => Self::Reinstall, + CanisterInstallModeV2::Upgrade(_) => Self::Upgrade, + } + } +} + +impl From for CanisterInstallModeV2 { + fn from(item: CanisterInstallMode) -> Self { + match item { + CanisterInstallMode::Install => Self::Install, + CanisterInstallMode::Reinstall => Self::Reinstall, + CanisterInstallMode::Upgrade => Self::Upgrade(None), } } } diff --git a/rs/types/types/src/batch.rs b/rs/types/types/src/batch.rs index 7676e5d084e..f3d36f20637 100644 --- a/rs/types/types/src/batch.rs +++ b/rs/types/types/src/batch.rs @@ -5,6 +5,7 @@ mod canister_http; mod execution_environment; mod ingress; mod self_validating; +mod vetkd; mod xnet; pub use self::{ @@ -15,6 +16,10 @@ pub use self::{ }, ingress::{IngressPayload, IngressPayloadError}, self_validating::{SelfValidatingPayload, MAX_BITCOIN_PAYLOAD_IN_BYTES}, + vetkd::{ + bytes_to_vetkd_payload, vetkd_payload_to_bytes, VetKdAgreement, VetKdErrorCode, + VetKdPayload, + }, xnet::XNetPayload, }; use crate::{ @@ -24,12 +29,13 @@ use crate::{ xnet::CertifiedStreamSlice, Height, Randomness, RegistryVersion, ReplicaVersion, SubnetId, Time, }; -use ic_base_types::NodeId; +use ic_base_types::{NodeId, NumBytes}; use ic_btc_replica_types::BitcoinAdapterResponse; #[cfg(test)] use ic_exhaustive_derive::ExhaustiveSet; use ic_management_canister_types::MasterPublicKeyId; use ic_protobuf::{proxy::ProxyDecodeError, types::v1 as pb}; +use prost::{bytes::BufMut, DecodeError, Message}; use serde::{Deserialize, Serialize}; use std::{ collections::{BTreeMap, BTreeSet}, @@ -165,10 +171,19 @@ impl BatchPayload { } pub fn is_empty(&self) -> bool { - self.ingress.is_empty() - && self.xnet.stream_slices.is_empty() - && self.self_validating.is_empty() - && self.canister_http.is_empty() + let BatchPayload { + ingress, + xnet, + self_validating, + canister_http, + query_stats, + } = &self; + + ingress.is_empty() + && xnet.is_empty() + && self_validating.is_empty() + && canister_http.is_empty() + && query_stats.is_empty() } } @@ -187,6 +202,44 @@ impl BlockmakerMetrics { } } +/// Given an iterator of [`Message`]s, this function will deserialize the messages +/// into a byte vector. +/// +/// The function is given a `max_size` limit, and guarantees that the buffer will be +/// smaller or equal than the byte limit. +/// It may drop messages from the iterator, if they don't fit. +pub fn iterator_to_bytes(iter: I, max_size: NumBytes) -> Vec +where + M: Message, + I: Iterator, +{ + let mut buffer = vec![].limit(max_size.get() as usize); + + for val in iter { + // NOTE: This call may fail due to the encoding hitting the + // byte limit. We continue trying the rest of the messages + // nonetheless, to give smaller messages a chance as well + let _ = val.encode_length_delimited(&mut buffer); + } + + buffer.into_inner() +} + +/// Parse a slice filled with protobuf encoded [`Message`]s into a vector +pub fn slice_to_messages(mut data: &[u8]) -> Result, DecodeError> +where + M: Message + Default, +{ + let mut msgs = vec![]; + + while !data.is_empty() { + let msg = M::decode_length_delimited(&mut data)?; + msgs.push(msg) + } + + Ok(msgs) +} + /// Response to a subnet call that requires Consensus' involvement. /// /// Only holds the payload and callback ID, Execution populates other fields @@ -241,12 +294,44 @@ mod tests { use super::*; use crate::CountBytes; + /// This is a quick test to check the invariant, that the [`Default`] implementation + /// of a payload section actually produces the empty payload, + #[test] + fn default_batch_payload_is_zero_bytes() { + let BatchPayload { + ingress, + xnet: _, // No implementation of `CountBytes` for xnet. + self_validating, + canister_http, + query_stats, + } = BatchPayload::default(); + + assert_eq!(ingress.count_bytes(), 0); + assert_eq!(self_validating.count_bytes(), 0); + assert_eq!(canister_http.len(), 0); + assert_eq!(query_stats.len(), 0); + } + /// This is a quick test to check the invariant, that the [`Default`] implementation /// of a payload section actually produces the empty payload, #[test] fn default_batch_payload_is_empty() { - assert_eq!(IngressPayload::default().count_bytes(), 0); - assert_eq!(SelfValidatingPayload::default().count_bytes(), 0); + let payload = BatchPayload::default(); + assert!(payload.is_empty()); + + let BatchPayload { + ingress, + xnet, + self_validating, + canister_http, + query_stats, + } = &payload; + + assert!(ingress.is_empty()); + assert!(xnet.is_empty()); + assert!(self_validating.is_empty()); + assert!(canister_http.is_empty()); + assert!(query_stats.is_empty()); } #[test] diff --git a/rs/types/types/src/batch/ingress.rs b/rs/types/types/src/batch/ingress.rs index 3bfcdafadaf..f0973aac665 100644 --- a/rs/types/types/src/batch/ingress.rs +++ b/rs/types/types/src/batch/ingress.rs @@ -1,45 +1,42 @@ use crate::{ artifact::IngressMessageId, - messages::{MessageId, SignedIngress, SignedRequestBytes, EXPECTED_MESSAGE_ID_LENGTH}, + messages::{ + HttpRequestError, MessageId, SignedIngress, SignedRequestBytes, EXPECTED_MESSAGE_ID_LENGTH, + }, CountBytes, Time, }; #[cfg(test)] use ic_exhaustive_derive::ExhaustiveSet; use ic_protobuf::{proxy::ProxyDecodeError, types::v1 as pb}; use serde::{Deserialize, Serialize}; -use std::{ - convert::TryFrom, - io::{Cursor, Write}, -}; +use std::{collections::BTreeMap, convert::TryFrom, fmt::Display}; /// Payload that contains Ingress messages #[derive(Clone, Eq, PartialEq, Hash, Debug, Default, Deserialize, Serialize)] #[cfg_attr(test, derive(ExhaustiveSet))] pub struct IngressPayload { - /// Pairs of MessageId and its serialized byte position in the buffer. - id_and_pos: Vec<(IngressMessageId, u64)>, - /// All messages are serialized in a single byte buffer, so individual + /// Keep ingress messages in a serialized form, so individual /// deserialization is delayed. This allows faster deserialization of /// IngressPayload when individual message is not needed (e.g. in /// ingress payload deduplication). - #[serde(with = "serde_bytes")] - buffer: Vec, + serialized_ingress_messages: BTreeMap, } impl From<&IngressPayload> for pb::IngressPayload { fn from(ingress_payload: &IngressPayload) -> Self { - Self { - id_and_pos: ingress_payload - .id_and_pos - .iter() - .map(|(msg_id, offset)| pb::IngressIdOffset { - expiry: msg_id.expiry().as_nanos_since_unix_epoch(), - message_id: msg_id.message_id.as_bytes().to_vec(), - offset: *offset, - }) - .collect(), - buffer: ingress_payload.buffer.clone(), - } + let ingress_messages = ingress_payload + .serialized_ingress_messages + .iter() + .map( + |(ingress_message_id, serialized_ingress_message)| pb::IngressMessage { + expiry: ingress_message_id.expiry().as_nanos_since_unix_epoch(), + message_id: ingress_message_id.message_id.as_bytes().to_vec(), + signed_request_bytes: serialized_ingress_message.as_ref().to_vec(), + }, + ) + .collect(); + + pb::IngressPayload { ingress_messages } } } @@ -47,131 +44,107 @@ impl TryFrom for IngressPayload { type Error = ProxyDecodeError; fn try_from(payload: pb::IngressPayload) -> Result { + let mut serialized_ingress_messages = BTreeMap::new(); + + for ingress_message_proto in payload.ingress_messages { + let ingress_message_id = IngressMessageId::new( + Time::from_nanos_since_unix_epoch(ingress_message_proto.expiry), + MessageId::try_from(ingress_message_proto.message_id.as_slice())?, + ); + + serialized_ingress_messages.insert( + ingress_message_id, + SignedRequestBytes::from(ingress_message_proto.signed_request_bytes), + ); + } + Ok(Self { - id_and_pos: payload - .id_and_pos - .iter() - .map(|ingress_offset| { - Ok(( - IngressMessageId::new( - Time::from_nanos_since_unix_epoch(ingress_offset.expiry), - MessageId::try_from(ingress_offset.message_id.as_slice())?, - ), - ingress_offset.offset, - )) - }) - .collect::, ProxyDecodeError>>()?, - buffer: payload.buffer, + serialized_ingress_messages, }) } } -/// Index of an ingress message in the IngressPayload. -type IngressIndex = usize; - -/// Position of serialized ingress message in the payload buffer. -type BufferPosition = u64; +#[derive(Debug, PartialEq)] +pub struct IngressPayloadError(HttpRequestError); -#[derive(Eq, PartialEq, Debug)] -/// Possible errors when accessing messages in an [`IngressPayload`]. -pub enum IngressPayloadError { - IndexOutOfBound(IngressIndex), - IngressPositionOutOfBound(IngressIndex, BufferPosition), - DeserializationFailure(String), - MismatchedMessageIdAtIndex(IngressIndex), +impl Display for IngressPayloadError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } } impl IngressPayload { /// Return the number of ingress messages contained in this payload pub fn message_count(&self) -> usize { - self.id_and_pos.len() + self.serialized_ingress_messages.len() } - /// Return all MessageIds in the payload. - pub fn message_ids(&self) -> Vec { - self.id_and_pos - .iter() - .map(|(id, _)| id.clone()) - .collect::>() + /// Return all [`IngressMessageId`]s in the payload. + pub fn message_ids(&self) -> impl Iterator { + self.serialized_ingress_messages.keys() } /// Return true if the payload is empty. pub fn is_empty(&self) -> bool { - self.id_and_pos.is_empty() + let IngressPayload { + serialized_ingress_messages, + } = &self; + serialized_ingress_messages.is_empty() } - // TODO(kpop): run some benchmarks and see if it makes sense to change the type of - // `[IngressPayload::id_and_pos]` - pub fn get_by_id(&self, ingress_message_id: &IngressMessageId) -> Option { - let (index, _) = self - .id_and_pos - .iter() - .enumerate() - .find(|(_, (id, _))| id == ingress_message_id)?; + /// Return the [`SignedRequestBytes`] referenced by the [`IngressMessageId`]. + pub fn get_serialized_by_id( + &self, + ingress_message_id: &IngressMessageId, + ) -> Option<&SignedRequestBytes> { + self.serialized_ingress_messages.get(ingress_message_id) + } - self.get(index) - .map(|(_, ingress_message)| ingress_message) - .ok() + /// Iterates over the ingress messages in their deserialized form. + pub fn iter( + &self, + ) -> impl Iterator< + Item = ( + &IngressMessageId, + Result, + ), + > { + self.serialized_ingress_messages.iter().map(|(id, bytes)| { + ( + id, + SignedIngress::try_from(bytes.clone()).map_err(IngressPayloadError), + ) + }) } - /// Return the ingress message at a given index, which is expected to be - /// less than `message_count`. - pub fn get( + pub fn iter_serialized( &self, - index: usize, - ) -> Result<(IngressMessageId, SignedIngress), IngressPayloadError> { - self.id_and_pos - .get(index) - .ok_or(IngressPayloadError::IndexOutOfBound(index)) - .and_then(|(id, pos)| { - // Return error if pos is out of bound. - if *pos > self.buffer.len() as u64 { - Err(IngressPayloadError::IngressPositionOutOfBound(index, *pos)) - } else { - let end = { - if index == self.id_and_pos.len() - 1 { - self.buffer.len() - } else { - self.id_and_pos[index + 1].1 as usize - } - }; - let ingress = SignedIngress::try_from(SignedRequestBytes::from(Vec::from( - &self.buffer[*pos as usize..end], - ))) - .map_err(|e| IngressPayloadError::DeserializationFailure(e.to_string()))?; - let ingress_id = IngressMessageId::from(&ingress); - if *id == ingress_id { - Ok((ingress_id, ingress)) - } else { - Err(IngressPayloadError::MismatchedMessageIdAtIndex(index)) - } - } - }) + ) -> impl Iterator { + self.serialized_ingress_messages.iter() } } impl CountBytes for IngressPayload { fn count_bytes(&self) -> usize { - self.buffer.len() + self.id_and_pos.len() * EXPECTED_MESSAGE_ID_LENGTH + let IngressPayload { + serialized_ingress_messages, + } = &self; + serialized_ingress_messages + .values() + .map(|message| EXPECTED_MESSAGE_ID_LENGTH + message.len()) + .sum() } } impl<'a> FromIterator<&'a SignedIngress> for IngressPayload { fn from_iter>(msgs: I) -> Self { - let mut buf = Cursor::new(Vec::new()); - let mut id_and_pos = Vec::new(); - for ingress in msgs { - let id = IngressMessageId::from(ingress); - let pos = buf.position(); - // This panic will only happen when we run out of memory. - buf.write_all(ingress.binary().as_ref()) - .unwrap_or_else(|err| panic!("SignedIngress serialization error: {:?}", err)); - - id_and_pos.push((id, pos)); - } + let serialized_ingress_messages = msgs + .into_iter() + .map(|ingress| (IngressMessageId::from(ingress), ingress.binary().clone())) + .collect(); + Self { - id_and_pos, - buffer: buf.into_inner(), + serialized_ingress_messages, } } } @@ -186,11 +159,11 @@ impl TryFrom for Vec { type Error = IngressPayloadError; fn try_from(payload: IngressPayload) -> Result, Self::Error> { payload - .id_and_pos - .iter() - .enumerate() - .map(|(i, _)| payload.get(i).map(|m| m.1)) - .collect::>() + .serialized_ingress_messages + .into_values() + .map(SignedIngress::try_from) + .collect::, _>>() + .map_err(IngressPayloadError) } } @@ -204,51 +177,108 @@ mod tests { }, time::expiry_time_from_now, }; + use std::convert::TryFrom; - /// Build a Vec. Convert to IngressPayload and then back to - /// Vec. Ensure that the two vectors are identical. - #[test] - fn into_ingress_payload_and_back() { - let ingress_expiry = expiry_time_from_now(); - let content = HttpCallContent::Call { + fn fake_http_call_content(method_name: &str) -> HttpCallContent { + HttpCallContent::Call { update: HttpCanisterUpdate { canister_id: Blob(vec![42; 8]), - method_name: "some_method".to_string(), + method_name: method_name.to_string(), arg: Blob(b"".to_vec()), sender: Blob(vec![0x05]), nonce: Some(Blob(vec![1, 2, 3, 4])), - ingress_expiry: ingress_expiry.as_nanos_since_unix_epoch(), + ingress_expiry: expiry_time_from_now().as_nanos_since_unix_epoch(), }, - }; + } + } + + /// Build a Vec. Convert to IngressPayload and then back to + /// Vec. Ensure that the two vectors are identical. + #[test] + fn into_ingress_payload_and_back() { let update_messages = vec![ HttpRequestEnvelope:: { - content: content.clone(), + content: fake_http_call_content("1"), sender_pubkey: Some(Blob(vec![2; 32])), sender_sig: Some(Blob(vec![1; 32])), sender_delegation: None, }, HttpRequestEnvelope:: { - content: content.clone(), + content: fake_http_call_content("2"), sender_pubkey: None, sender_sig: None, sender_delegation: None, }, HttpRequestEnvelope:: { - content, + content: fake_http_call_content("3"), sender_pubkey: Some(Blob(vec![2; 32])), sender_sig: Some(Blob(vec![1; 32])), sender_delegation: Some(vec![SignedDelegation::new( - Delegation::new(vec![1, 2], ingress_expiry), + Delegation::new(vec![1, 2], expiry_time_from_now()), vec![3, 4], )]), }, ]; - let signed_ingresses: Vec = update_messages + let mut signed_ingresses: Vec = update_messages .into_iter() .map(|msg| SignedIngress::try_from(msg).unwrap()) .collect(); + let ingress_payload = IngressPayload::from(signed_ingresses.clone()); let signed_ingresses1 = Vec::::try_from(ingress_payload).unwrap(); + // ingress messages are sorted by id in the ingress payload, hence the sort below + signed_ingresses.sort_by(|msg_1, msg_2| { + IngressMessageId::from(msg_1) + .partial_cmp(&IngressMessageId::from(msg_2)) + .unwrap() + }); assert_eq!(signed_ingresses, signed_ingresses1); } + + #[test] + fn test_ingress_payload_deserialization() { + // serialization/deserialization of empty payload. + let payload = IngressPayload::default(); + let bytes = bincode::serialize(&payload).unwrap(); + assert_eq!( + bincode::deserialize::(&bytes).unwrap(), + payload + ); + + let fake_ingress_message = |method_name| { + let message = HttpRequestEnvelope:: { + content: fake_http_call_content(method_name), + sender_pubkey: None, + sender_sig: None, + sender_delegation: None, + }; + + let ingress = SignedIngress::try_from(message).unwrap(); + let id = IngressMessageId::from(&ingress); + + (ingress, id) + }; + + // Some test messages. + let (m1, id1) = fake_ingress_message("m1"); + let (m2, id2) = fake_ingress_message("m2"); + let (m3, id3) = fake_ingress_message("m3"); + let (_m4, id4) = fake_ingress_message("m4"); + + let msgs = vec![m1.clone(), m2.clone(), m3.clone()]; + let payload = IngressPayload::from(msgs.clone()); + // Serialization/deserialization works. + let bytes = bincode::serialize(&payload).unwrap(); + assert_eq!( + bincode::deserialize::(&bytes).unwrap(), + payload + ); + // Individual lookup works. + assert_eq!(payload.get_serialized_by_id(&id1), Some(m1.binary())); + assert_eq!(payload.get_serialized_by_id(&id2), Some(m2.binary())); + assert_eq!(payload.get_serialized_by_id(&id3), Some(m3.binary())); + assert_eq!(payload.get_serialized_by_id(&id4), None); + // Converting back to messages should match original + assert_eq!(msgs, >::try_from(payload).unwrap()); + } } diff --git a/rs/types/types/src/batch/self_validating.rs b/rs/types/types/src/batch/self_validating.rs index ca97598690f..30f25975a84 100644 --- a/rs/types/types/src/batch/self_validating.rs +++ b/rs/types/types/src/batch/self_validating.rs @@ -35,7 +35,8 @@ impl SelfValidatingPayload { /// Returns true if the payload is empty pub fn is_empty(&self) -> bool { - self.0.is_empty() + let SelfValidatingPayload(responses) = &self; + responses.is_empty() } } @@ -61,6 +62,7 @@ impl TryFrom for SelfValidatingPayload { impl CountBytes for SelfValidatingPayload { fn count_bytes(&self) -> usize { - self.0.iter().map(|x| x.count_bytes()).sum() + let SelfValidatingPayload(responses) = &self; + responses.iter().map(|x| x.count_bytes()).sum() } } diff --git a/rs/types/types/src/batch/vetkd.rs b/rs/types/types/src/batch/vetkd.rs new file mode 100644 index 00000000000..21987ae6eae --- /dev/null +++ b/rs/types/types/src/batch/vetkd.rs @@ -0,0 +1,221 @@ +use ic_base_types::NumBytes; +#[cfg(test)] +use ic_exhaustive_derive::ExhaustiveSet; +use ic_protobuf::{ + proxy::{try_from_option_field, ProxyDecodeError}, + types::v1::{ + vet_kd_agreement::Agreement as VetKdInternalAgreementProto, + VetKdAgreement as VetKdAgreementProto, VetKdErrorCode as VetKdErrorCodeProto, + }, +}; +use serde::{Deserialize, Serialize}; +use std::{collections::BTreeMap, convert::TryFrom}; + +use crate::{messages::CallbackId, CountBytes}; + +use super::{iterator_to_bytes, slice_to_messages}; + +/// Errors that may occur when handling a VetKd request. +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Deserialize, Serialize)] +#[cfg_attr(test, derive(ExhaustiveSet))] +pub enum VetKdErrorCode { + TimedOut = 1, + InvalidKey = 2, +} + +/// Consensus may either agree on a successful response, or reject the request. +#[derive(Clone, Eq, PartialEq, Hash, Debug, Deserialize, Serialize)] +#[cfg_attr(test, derive(ExhaustiveSet))] +pub enum VetKdAgreement { + Success(Vec), + Reject(VetKdErrorCode), +} + +impl CountBytes for VetKdAgreement { + fn count_bytes(&self) -> usize { + match self { + VetKdAgreement::Success(data) => data.len(), + VetKdAgreement::Reject(_) => size_of::(), + } + } +} + +/// Payload that contains completed VetKey agreements. +#[derive(Clone, Eq, PartialEq, Hash, Debug, Default, Deserialize, Serialize)] +#[cfg_attr(test, derive(ExhaustiveSet))] +pub struct VetKdPayload { + pub vetkd_agreements: BTreeMap, +} + +impl VetKdPayload { + pub fn is_empty(&self) -> bool { + self.vetkd_agreements.is_empty() + } +} + +impl From for VetKdInternalAgreementProto { + fn from(agreement: VetKdAgreement) -> Self { + match agreement { + VetKdAgreement::Success(data) => VetKdInternalAgreementProto::Data(data), + VetKdAgreement::Reject(error_code) => { + VetKdInternalAgreementProto::Reject(VetKdErrorCodeProto::from(error_code).into()) + } + } + } +} + +impl TryFrom for VetKdAgreement { + type Error = ProxyDecodeError; + fn try_from(proto: VetKdInternalAgreementProto) -> Result { + let res = match proto { + VetKdInternalAgreementProto::Data(data) => VetKdAgreement::Success(data), + VetKdInternalAgreementProto::Reject(error_code) => VetKdAgreement::Reject( + VetKdErrorCode::try_from(VetKdErrorCodeProto::try_from(error_code).map_err( + |_| ProxyDecodeError::ValueOutOfRange { + typ: "VetKdErrorCode", + err: format!("Unexpected value for VetKd error code {}", error_code), + }, + )?)?, + ), + }; + Ok(res) + } +} + +impl From for VetKdErrorCodeProto { + fn from(value: VetKdErrorCode) -> Self { + match value { + VetKdErrorCode::TimedOut => VetKdErrorCodeProto::TimedOut, + VetKdErrorCode::InvalidKey => VetKdErrorCodeProto::InvalidKey, + } + } +} + +impl TryFrom for VetKdErrorCode { + type Error = ProxyDecodeError; + + fn try_from(value: VetKdErrorCodeProto) -> Result { + match value { + VetKdErrorCodeProto::Unspecified => Err(ProxyDecodeError::ValueOutOfRange { + typ: "VetKdErrorCode", + err: format!("Unexpected value for VetKd error code {:?}", value), + }), + VetKdErrorCodeProto::TimedOut => Ok(VetKdErrorCode::TimedOut), + VetKdErrorCodeProto::InvalidKey => Ok(VetKdErrorCode::InvalidKey), + } + } +} + +pub fn vetkd_payload_to_bytes(payload: VetKdPayload, max_size: NumBytes) -> Vec { + let message_iterator = payload + .vetkd_agreements + .into_iter() + .map(|(callback_id, agreement)| VetKdAgreementProto { + callback_id: callback_id.get(), + agreement: Some(agreement.into()), + }); + + iterator_to_bytes(message_iterator, max_size) +} + +pub fn bytes_to_vetkd_payload(data: &[u8]) -> Result { + let messages: Vec = + slice_to_messages(data).map_err(ProxyDecodeError::DecodeError)?; + let mut payload = VetKdPayload::default(); + + for message in messages { + let callback_id = CallbackId::from(message.callback_id); + let response = try_from_option_field(message.agreement, "VetKdAgreement::agreement")?; + payload.vetkd_agreements.insert(callback_id, response); + } + + Ok(payload) +} + +#[cfg(test)] +mod tests { + use ic_crypto_test_utils_reproducible_rng::reproducible_rng; + use rand::RngCore; + + use crate::exhaustive::ExhaustiveSet; + + use super::*; + + #[test] + fn test_vetkd_payload_conversion() { + let set = VetKdPayload::exhaustive_set(&mut reproducible_rng()); + println!("Number of VetKdPayload variants: {}", set.len()); + let max_size = NumBytes::new(2 * 1024 * 1024); + for element in set { + // serialize -> deserialize round-trip + let bytes = vetkd_payload_to_bytes(element.clone(), max_size); + let new_element = bytes_to_vetkd_payload(&bytes).unwrap(); + + assert_eq!( + element, new_element, + "deserialized VetKdPayload is different from original" + ); + } + } + + #[test] + fn test_large_vetkd_payload_conversion() { + let mut rng = reproducible_rng(); + // Max size is 10_000 bytes + let max_size = NumBytes::new(10 * 1000); + + // Each agreement has 1000 bytes (in deserialized form) + let mut make_agreement = || { + let mut data = [0; 1000]; + rng.fill_bytes(&mut data); + VetKdAgreement::Success(data.to_vec()) + }; + + // 8 Agreements should still fit in the payload + let payload_fits = VetKdPayload { + vetkd_agreements: (0..9) + .map(|i| (CallbackId::new(i), make_agreement())) + .collect(), + }; + + let bytes = vetkd_payload_to_bytes(payload_fits.clone(), max_size); + assert!(bytes.len() as u64 <= max_size.get()); + let new_payload = bytes_to_vetkd_payload(&bytes).unwrap(); + + assert_eq!( + new_payload, payload_fits, + "deserialized VetKdPayload is different from original" + ); + + // The 9th agreement should be truncated + let mut payload_too_large = payload_fits.clone(); + payload_too_large + .vetkd_agreements + .insert(CallbackId::new(9), make_agreement()); + + let bytes = vetkd_payload_to_bytes(payload_too_large, max_size); + assert!(bytes.len() as u64 <= max_size.get()); + let new_payload = bytes_to_vetkd_payload(&bytes).unwrap(); + + assert_eq!( + new_payload, payload_fits, + "deserialized VetKdPayload is different from original" + ); + + // But there should still be space for a reject + let mut payload_reject = payload_fits.clone(); + payload_reject.vetkd_agreements.insert( + CallbackId::new(9), + VetKdAgreement::Reject(VetKdErrorCode::TimedOut), + ); + + let bytes = vetkd_payload_to_bytes(payload_reject.clone(), max_size); + assert!(bytes.len() as u64 <= max_size.get()); + let new_payload = bytes_to_vetkd_payload(&bytes).unwrap(); + + assert_eq!( + new_payload, payload_reject, + "deserialized VetKdPayload is different from original" + ); + } +} diff --git a/rs/types/types/src/batch/xnet.rs b/rs/types/types/src/batch/xnet.rs index 317de7f135a..f435a7c64d0 100644 --- a/rs/types/types/src/batch/xnet.rs +++ b/rs/types/types/src/batch/xnet.rs @@ -72,4 +72,10 @@ impl XNetPayload { }) .sum() } + + /// Returns true if the payload is empty + pub fn is_empty(&self) -> bool { + let XNetPayload { stream_slices } = &self; + stream_slices.is_empty() + } } diff --git a/rs/types/types/src/canister_http.rs b/rs/types/types/src/canister_http.rs index ac019d51355..0b2eeb52b63 100644 --- a/rs/types/types/src/canister_http.rs +++ b/rs/types/types/src/canister_http.rs @@ -53,7 +53,7 @@ use ic_error_types::{ErrorCode, RejectCode, UserError}; #[cfg(test)] use ic_exhaustive_derive::ExhaustiveSet; use ic_management_canister_types::{ - CanisterHttpRequestArgs, HttpHeader, HttpMethod, TransformContext, + CanisterHttpRequestArgs, DataSize, HttpHeader, HttpMethod, TransformContext, }; use ic_protobuf::{ proxy::{try_from_option_field, ProxyDecodeError}, @@ -457,6 +457,10 @@ pub struct CanisterHttpRequest { pub id: CanisterHttpRequestId, /// The context of the request which captures all the metadata about this request pub context: CanisterHttpRequestContext, + /// The most up to date api boundary nodes address that should be used as a socks proxy in the case of a request to an IPv4 address. + /// The addresses should be sent in the following format: "socks5://[]:", for example: + /// "socks5://[2602:fb2b:110:10:506f:cff:feff:fe69]:1080" + pub socks_proxy_addrs: Vec, } /// The content of a response after the transformation @@ -471,7 +475,16 @@ pub struct CanisterHttpResponse { impl CountBytes for CanisterHttpResponse { fn count_bytes(&self) -> usize { - size_of::() + size_of::