diff --git a/.github/workflows/build-publish-images.yml b/.github/workflows/build-publish-images.yml index 874b5d37469cde33f2b92f8cec0a4d61c57b7ffb..deb3b3df5ff2f6d0c13e038cb6291e958a9c879a 100644 --- a/.github/workflows/build-publish-images.yml +++ b/.github/workflows/build-publish-images.yml @@ -53,7 +53,7 @@ jobs: - name: pack artifacts run: | mkdir -p ./artifacts - VERSION="${{ needs.preflight.outputs.SOURCE_REF_NAME }}" # will be tag or branch name + VERSION="${{ needs.preflight.outputs.SOURCE_REF_SLUG }}" # will be tag or branch name mv ./target/testnet/polkadot ./artifacts/. mv ./target/testnet/polkadot-prepare-worker ./artifacts/. mv ./target/testnet/polkadot-execute-worker ./artifacts/. @@ -62,7 +62,7 @@ jobs: sha256sum polkadot | tee polkadot.sha256 shasum -c polkadot.sha256 cd ../ - EXTRATAG="${{ needs.preflight.outputs.SOURCE_REF_NAME }}-${COMMIT_SHA}" + EXTRATAG="${{ needs.preflight.outputs.SOURCE_REF_SLUG }}-${COMMIT_SHA}" echo "Polkadot version = ${VERSION} (EXTRATAG = ${EXTRATAG})" echo -n ${VERSION} > ./artifacts/VERSION echo -n ${EXTRATAG} > ./artifacts/EXTRATAG @@ -77,7 +77,7 @@ jobs: - name: upload artifacts uses: actions/upload-artifact@v4 with: - name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_NAME }} + name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_SLUG }} path: artifacts.tar retention-days: 1 @@ -103,7 +103,7 @@ jobs: mkdir -p ./artifacts mv ./target/release/polkadot-parachain ./artifacts/. echo "___The VERSION is either a tag name or the curent branch if triggered not by a tag___" - echo ${{ needs.preflight.outputs.SOURCE_REF_NAME }} | tee ./artifacts/VERSION + echo ${{ needs.preflight.outputs.SOURCE_REF_SLUG }} | tee ./artifacts/VERSION - name: tar run: tar -cvf artifacts.tar artifacts @@ -111,7 +111,7 @@ jobs: - name: upload artifacts uses: actions/upload-artifact@v4 with: - name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_NAME }} + name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_SLUG }} path: artifacts.tar retention-days: 1 @@ -147,7 +147,7 @@ jobs: - name: upload artifacts uses: actions/upload-artifact@v4 with: - name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_NAME }} + name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_SLUG }} path: artifacts.tar retention-days: 1 @@ -172,8 +172,8 @@ jobs: mkdir -p ./artifacts mv ./target/testnet/adder-collator ./artifacts/. mv ./target/testnet/undying-collator ./artifacts/. - echo -n "${{ needs.preflight.outputs.SOURCE_REF_NAME }}" > ./artifacts/VERSION - echo -n "${{ needs.preflight.outputs.SOURCE_REF_NAME }}-${COMMIT_SHA}" > ./artifacts/EXTRATAG + echo -n "${{ needs.preflight.outputs.SOURCE_REF_SLUG }}" > ./artifacts/VERSION + echo -n "${{ needs.preflight.outputs.SOURCE_REF_SLUG }}-${COMMIT_SHA}" > ./artifacts/EXTRATAG echo "adder-collator version = $(cat ./artifacts/VERSION) (EXTRATAG = $(cat ./artifacts/EXTRATAG))" echo "undying-collator version = $(cat ./artifacts/VERSION) (EXTRATAG = $(cat ./artifacts/EXTRATAG))" cp -r ./docker/* ./artifacts @@ -184,7 +184,7 @@ jobs: - name: upload artifacts uses: actions/upload-artifact@v4 with: - name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_NAME }} + name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_SLUG }} path: artifacts.tar retention-days: 1 @@ -209,8 +209,8 @@ jobs: mv ./target/testnet/malus ./artifacts/. mv ./target/testnet/polkadot-execute-worker ./artifacts/. mv ./target/testnet/polkadot-prepare-worker ./artifacts/. - echo -n "${{ needs.preflight.outputs.SOURCE_REF_NAME }}" > ./artifacts/VERSION - echo -n "${{ needs.preflight.outputs.SOURCE_REF_NAME }}-${COMMIT_SHA}" > ./artifacts/EXTRATAG + echo -n "${{ needs.preflight.outputs.SOURCE_REF_SLUG }}" > ./artifacts/VERSION + echo -n "${{ needs.preflight.outputs.SOURCE_REF_SLUG }}-${COMMIT_SHA}" > ./artifacts/EXTRATAG echo "polkadot-test-malus = $(cat ./artifacts/VERSION) (EXTRATAG = $(cat ./artifacts/EXTRATAG))" cp -r ./docker/* ./artifacts @@ -220,7 +220,7 @@ jobs: - name: upload artifacts uses: actions/upload-artifact@v4 with: - name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_NAME }} + name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_SLUG }} path: artifacts.tar retention-days: 1 @@ -246,6 +246,7 @@ jobs: WASM_BUILD_NO_COLOR=1 forklift cargo build --locked --release -p staging-node-cli ls -la target/release/ - name: pack artifacts + shell: bash run: | mv target/release/substrate-node ./artifacts/substrate/substrate echo -n "Substrate version = " @@ -264,7 +265,7 @@ jobs: - name: upload artifacts uses: actions/upload-artifact@v4 with: - name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_NAME }} + name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_SLUG }} path: artifacts.tar retention-days: 1 @@ -294,7 +295,7 @@ jobs: - name: upload artifacts uses: actions/upload-artifact@v4 with: - name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_NAME }} + name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_SLUG }} path: artifacts.tar retention-days: 1 @@ -313,7 +314,7 @@ jobs: - uses: actions/download-artifact@v4.1.8 with: - name: build-test-parachain-${{ needs.preflight.outputs.SOURCE_REF_NAME }} + name: build-test-parachain-${{ needs.preflight.outputs.SOURCE_REF_SLUG }} - name: tar run: tar -xvf artifacts.tar @@ -337,7 +338,7 @@ jobs: - uses: actions/download-artifact@v4.1.8 with: - name: build-linux-stable-${{ needs.preflight.outputs.SOURCE_REF_NAME }} + name: build-linux-stable-${{ needs.preflight.outputs.SOURCE_REF_SLUG }} - name: tar run: tar -xvf artifacts.tar @@ -361,7 +362,7 @@ jobs: - uses: actions/download-artifact@v4.1.8 with: - name: build-test-collators-${{ needs.preflight.outputs.SOURCE_REF_NAME }} + name: build-test-collators-${{ needs.preflight.outputs.SOURCE_REF_SLUG }} - name: tar run: tar -xvf artifacts.tar @@ -385,7 +386,7 @@ jobs: - uses: actions/download-artifact@v4.1.8 with: - name: build-malus-${{ needs.preflight.outputs.SOURCE_REF_NAME }} + name: build-malus-${{ needs.preflight.outputs.SOURCE_REF_SLUG }} - name: tar run: tar -xvf artifacts.tar @@ -409,7 +410,7 @@ jobs: - uses: actions/download-artifact@v4.1.8 with: - name: build-linux-substrate-${{ needs.preflight.outputs.SOURCE_REF_NAME }} + name: build-linux-substrate-${{ needs.preflight.outputs.SOURCE_REF_SLUG }} - name: tar run: tar -xvf artifacts.tar @@ -441,7 +442,7 @@ jobs: - uses: actions/download-artifact@v4.1.8 with: - name: build-linux-stable-${{ needs.preflight.outputs.SOURCE_REF_NAME }} + name: build-linux-stable-${{ needs.preflight.outputs.SOURCE_REF_SLUG }} - name: tar run: | tar -xvf artifacts.tar @@ -449,7 +450,7 @@ jobs: - uses: actions/download-artifact@v4.1.8 with: - name: build-linux-stable-cumulus-${{ needs.preflight.outputs.SOURCE_REF_NAME }} + name: build-linux-stable-cumulus-${{ needs.preflight.outputs.SOURCE_REF_SLUG }} - name: tar run: | tar -xvf artifacts.tar @@ -457,7 +458,7 @@ jobs: - uses: actions/download-artifact@v4.1.8 with: - name: prepare-bridges-zombienet-artifacts-${{ needs.preflight.outputs.SOURCE_REF_NAME }} + name: prepare-bridges-zombienet-artifacts-${{ needs.preflight.outputs.SOURCE_REF_SLUG }} - name: tar run: | tar -xvf artifacts.tar @@ -482,7 +483,7 @@ jobs: - uses: actions/download-artifact@v4.1.8 with: - name: build-linux-stable-cumulus-${{ needs.preflight.outputs.SOURCE_REF_NAME }} + name: build-linux-stable-cumulus-${{ needs.preflight.outputs.SOURCE_REF_SLUG }} - name: tar run: tar -xvf artifacts.tar diff --git a/.github/workflows/check-semver.yml b/.github/workflows/check-semver.yml index 0da3e54ef60b88420a0bb9762bc9fff2e88be4af..43c70d6abc78b0aca6fe0738617632a9b33b809a 100644 --- a/.github/workflows/check-semver.yml +++ b/.github/workflows/check-semver.yml @@ -81,7 +81,7 @@ jobs: - name: install parity-publish if: ${{ !contains(github.event.pull_request.labels.*.name, 'R0-silent') }} # Set the target dir to cache the build. - run: CARGO_TARGET_DIR=./target/ cargo install parity-publish@0.10.3 --locked -q + run: CARGO_TARGET_DIR=./target/ cargo install parity-publish@0.10.4 --locked -q - name: check semver if: ${{ !contains(github.event.pull_request.labels.*.name, 'R0-silent') }} diff --git a/.github/workflows/command-inform.yml b/.github/workflows/command-inform.yml index 97346395319362b0455bcbcfbb490fa23e6b3b07..3431eadf70608d2a7465f57718689f4acfe96952 100644 --- a/.github/workflows/command-inform.yml +++ b/.github/workflows/command-inform.yml @@ -8,7 +8,7 @@ jobs: comment: runs-on: ubuntu-latest # Temporary disable the bot until the new command bot works properly - if: github.event.issue.pull_request && startsWith(github.event.comment.body, 'bot ') && false # disabled for now, until tested + if: github.event.issue.pull_request && startsWith(github.event.comment.body, 'bot ') steps: - name: Inform that the new command exist uses: actions/github-script@v7 @@ -18,5 +18,5 @@ jobs: issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, - body: 'We have migrated the command bot to GHA<br/><br/>Please, see the new usage instructions <a href="https://github.com/paritytech/polkadot-sdk/blob/master/docs/contributor/commands-readme.md">here</a>. Soon the old commands will be disabled.' - }) \ No newline at end of file + body: 'We have migrated the command bot to GHA<br/><br/>Please, see the new usage instructions <a href="https://github.com/paritytech/polkadot-sdk/blob/master/docs/contributor/commands-readme.md">here</a> or <a href="https://forum.parity.io/t/streamlining-weight-generation-and-more-the-new-cmd-bot/2411">here</a>. Soon the old commands will be disabled.' + }) diff --git a/.github/workflows/publish-check-compile.yml b/.github/workflows/publish-check-compile.yml index ce1b2cb231d0bbfa9497559a1c0dbe8a2dedb783..f20909106a82098d79ab8e47b03bf5b4076b9380 100644 --- a/.github/workflows/publish-check-compile.yml +++ b/.github/workflows/publish-check-compile.yml @@ -26,12 +26,14 @@ jobs: - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: Rust Cache - uses: Swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab # v2.7.5 + uses: Swatinem/rust-cache@f0deed1e0edfc6a9be95417288c0e1099b1eeec3 # v2.7.7 with: cache-on-failure: true - name: install parity-publish - run: cargo install parity-publish@0.10.3 --locked -q + run: | + rustup override set 1.82.0 + cargo install parity-publish@0.10.4 --locked -q - name: parity-publish update plan run: parity-publish --color always plan --skip-check --prdoc prdoc/ diff --git a/.github/workflows/publish-check-crates.yml b/.github/workflows/publish-check-crates.yml index 3150cb9dd40508a214280f716ba161c5420bfc92..c1b13243ba193c7c141782dc263c577c5f0ec159 100644 --- a/.github/workflows/publish-check-crates.yml +++ b/.github/workflows/publish-check-crates.yml @@ -19,12 +19,12 @@ jobs: - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: Rust Cache - uses: Swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab # v2.7.5 + uses: Swatinem/rust-cache@f0deed1e0edfc6a9be95417288c0e1099b1eeec3 # v2.7.7 with: cache-on-failure: true - name: install parity-publish - run: cargo install parity-publish@0.10.3 --locked -q + run: cargo install parity-publish@0.10.4 --locked -q - name: parity-publish check run: parity-publish --color always check --allow-unpublished diff --git a/.github/workflows/publish-claim-crates.yml b/.github/workflows/publish-claim-crates.yml index a6efc8a5599e4171cd93c5bdb287d2b5a8514787..804baf9ff06cfc4c32c19fbe3a70be81c8f07b46 100644 --- a/.github/workflows/publish-claim-crates.yml +++ b/.github/workflows/publish-claim-crates.yml @@ -13,12 +13,12 @@ jobs: - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: Rust Cache - uses: Swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab # v2.7.5 + uses: Swatinem/rust-cache@f0deed1e0edfc6a9be95417288c0e1099b1eeec3 # v2.7.7 with: cache-on-failure: true - name: install parity-publish - run: cargo install parity-publish@0.10.3 --locked -q + run: cargo install parity-publish@0.10.4 --locked -q - name: parity-publish claim env: diff --git a/.github/workflows/review-bot.yml b/.github/workflows/review-bot.yml index 3dd5b1114813dbb2e151319293ade7ce44f4aae9..27c6162a0fc20cddbde29324a9626f3f1e8b7973 100644 --- a/.github/workflows/review-bot.yml +++ b/.github/workflows/review-bot.yml @@ -29,7 +29,7 @@ jobs: with: artifact-name: pr_number - name: "Evaluates PR reviews and assigns reviewers" - uses: paritytech/review-bot@v2.6.0 + uses: paritytech/review-bot@v2.7.0 with: repo-token: ${{ steps.app_token.outputs.token }} team-token: ${{ steps.app_token.outputs.token }} diff --git a/.github/workflows/zombienet-reusable-preflight.yml b/.github/workflows/zombienet-reusable-preflight.yml new file mode 100644 index 0000000000000000000000000000000000000000..8e938567d8118ad6d68eb91108281fd20e72b26f --- /dev/null +++ b/.github/workflows/zombienet-reusable-preflight.yml @@ -0,0 +1,145 @@ +# Reusable workflow to set various useful variables +# and to perform checks and generate conditions for other workflows. +# Currently it checks if any Rust (build-related) file is changed +# and if the current (caller) workflow file is changed. +# Example: +# +# jobs: +# preflight: +# uses: ./.github/workflows/reusable-preflight.yml +# some-job: +# needs: changes +# if: ${{ needs.preflight.outputs.changes_rust }} +# ....... + +name: Zombienet Preflight + +on: + workflow_call: + # Map the workflow outputs to job outputs + outputs: + changes_substrate: + value: ${{ jobs.preflight.outputs.changes_substrate }} + + ZOMBIENET_IMAGE: + value: ${{ jobs.preflight.outputs.ZOMBIENET_IMAGE }} + description: "ZOMBIENET CI image" + + ZOMBIENET_RUNNER: + value: ${{ jobs.preflight.outputs.ZOMBIENET_RUNNER }} + description: | + Main runner for zombienet tests. + + DOCKER_IMAGES_VERSION: + value: ${{ jobs.preflight.outputs.DOCKER_IMAGES_VERSION }} + description: | + Version for temp docker images. + + # Global vars (from global preflight) + SOURCE_REF_SLUG: + value: ${{ jobs.global_preflight.outputs.SOURCE_REF_SLUG }} + + # Zombie vars + PUSHGATEWAY_URL: + value: ${{ jobs.preflight.outputs.PUSHGATEWAY_URL }} + description: "Gateway (url) to push metrics related to test." + DEBUG: + value: ${{ jobs.preflight.outputs.DEBUG }} + description: "Debug value to zombienet v1 tests." + ZOMBIE_PROVIDER: + value: ${{ jobs.preflight.outputs.ZOMBIE_PROVIDER }} + description: "Provider to use in zombienet-sdk tests." + RUST_LOG: + value: ${{ jobs.preflight.outputs.RUST_LOG }} + description: "Log value to use in zombinet-sdk tests." + RUN_IN_CI: + value: ${{ jobs.preflight.outputs.RUN_IN_CI }} + description: "Internal flag to make zombienet aware of the env." + + KUBERNETES_CPU_REQUEST: + value: ${{ jobs.preflight.outputs.KUBERNETES_CPU_REQUEST }} + description: "Base cpu (request) for pod runner." + + KUBERNETES_MEMORY_REQUEST: + value: ${{ jobs.preflight.outputs.KUBERNETES_MEMORY_REQUEST }} + description: "Base memory (request) for pod runner." + +jobs: + global_preflight: + uses: ./.github/workflows/reusable-preflight.yml + + # + # + # + preflight: + runs-on: ubuntu-latest + outputs: + changes_substrate: ${{ steps.set_changes.outputs.substrate_any_changed || steps.set_changes.outputs.currentWorkflow_any_changed }} + + ZOMBIENET_IMAGE: ${{ steps.set_vars.outputs.ZOMBIENET_IMAGE }} + ZOMBIENET_RUNNER: ${{ steps.set_vars.outputs.ZOMBIENET_RUNNER }} + + DOCKER_IMAGES_VERSION: ${{ steps.set_images_version.outputs.ZOMBIENET_RUNNER }} + + # common vars + PUSHGATEWAY_URL: ${{ steps.set_vars.outputs.PUSHGATEWAY_URL }} + DEBUG: ${{ steps.set_vars.outputs.DEBUG }} + ZOMBIE_PROVIDER: ${{ steps.set_vars.outputs.ZOMBIE_PROVIDER }} + RUST_LOG: ${{ steps.set_vars.outputs.RUST_LOG }} + RUN_IN_CI: ${{ steps.set_vars.outputs.RUN_IN_CI }} + KUBERNETES_CPU_REQUEST: ${{ steps.set_vars.outputs.KUBERNETES_CPU_REQUEST }} + KUBERNETES_MEMORY_REQUEST: ${{ steps.set_vars.outputs.KUBERNETES_MEMORY_REQUEST }} + + steps: + + - uses: actions/checkout@v4 + + # + # Set changes + # + - name: Current file + id: current_file + shell: bash + run: | + echo "currentWorkflowFile=$(echo ${{ github.workflow_ref }} | sed -nE "s/.*(\.github\/workflows\/[a-zA-Z0-9_-]*\.y[a]?ml)@refs.*/\1/p")" >> $GITHUB_OUTPUT + echo "currentActionDir=$(echo ${{ github.action_path }} | sed -nE "s/.*(\.github\/actions\/[a-zA-Z0-9_-]*)/\1/p")" >> $GITHUB_OUTPUT + + - name: Set changes + id: set_changes + uses: tj-actions/changed-files@v45 + with: + files_yaml: | + substrate: + - 'substrate/**/*' + currentWorkflow: + - '${{ steps.current_file.outputs.currentWorkflowFile }}' + - '.github/workflows/zombienet-reusable-preflight.yml' + - '.github/zombienet-env' + + + # + # Set environment vars (including runner/image) + # + - name: Set vars + id: set_vars + shell: bash + run: cat .github/env >> $GITHUB_OUTPUT + + + # + # + # + - name: Set docker images version + id: set_images_version + shell: bash + run: | + export BRANCH_NAME=${{ github.head_ref || github.ref_name }} + export DOCKER_IMAGES_VERSION=${BRANCH_NAME/\//-} + if [[ ${{ github.event_name }} == "merge_group" ]]; then export DOCKER_IMAGES_VERSION="${GITHUB_SHA::8}"; fi + echo "DOCKER_IMAGES_VERSION=${DOCKER_IMAGES_VERSION}" >> $GITHUB_OUTPUT + + - name: log + shell: bash + run: | + echo "workflow file: ${{ steps.current_file.outputs.currentWorkflowFile }}" + echo "Modified: ${{ steps.set_changes.outputs.modified_keys }}" \ No newline at end of file diff --git a/.github/workflows/zombienet_substrate.yml b/.github/workflows/zombienet_substrate.yml new file mode 100644 index 0000000000000000000000000000000000000000..823679d67d5c0adc204a2fc15360fdab561a4c17 --- /dev/null +++ b/.github/workflows/zombienet_substrate.yml @@ -0,0 +1,45 @@ +name: Zombienet Substrate + +on: + workflow_run: + workflows: [Build and push images] + types: [completed] + merge_group: + workflow_dispatch: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + preflight: + uses: ./.github/workflows/zombienet-reusable-preflight.yml + + zombienet-substrate-0000-block-building: + needs: [preflight] + # only run if we have changes in ./substrate directory and the build workflow already finish with success status. + if: ${{ needs.preflight.outputs.changes_substrate && github.event.workflow_run.conclusion == 'success' }} + runs-on: ${{ needs.preflight.outputs.ZOMBIENET_RUNNER }} + timeout-minutes: 60 + container: + image: ${{ needs.preflight.outputs.ZOMBIENET_IMAGE }} + env: + FF_DISABLE_UMASK_FOR_DOCKER_EXECUTOR: 1 + LOCAL_DIR: "./substrate/zombienet" + steps: + - name: Checkout + uses: actions/checkout@v4 + + - uses: actions/download-artifact@v4.1.8 + with: + name: build-linux-substrate-${{ needs.preflight.outputs.SOURCE_REF_NAME }} + + - name: script + run: | + DEBUG=${{ needs.preflight.outputs.DEBUG }} zombie -p native ${LOCAL_DIR}/0000-block-building/block-building.zndsl + + - name: upload logs + uses: actions/upload-artifact@v4 + with: + name: zombienet-logs-scale-net + path: | + /tmp/zombie*/logs/* diff --git a/.github/zombienet-env b/.github/zombienet-env new file mode 100644 index 0000000000000000000000000000000000000000..e6da1a49c4bb38602bdc9d7a21623ed136dee61b --- /dev/null +++ b/.github/zombienet-env @@ -0,0 +1,9 @@ + ZOMBIENET_IMAGE="docker.io/paritytech/zombienet:v1.3.116" + ZOMBIE_RUNNER="zombienet-arc-runner" + PUSHGATEWAY_URL="http://zombienet-prometheus-pushgateway.managed-monitoring:9091/metrics/job/zombie-metrics" + DEBUG="zombie,zombie::network-node,zombie::kube::client::logs" + ZOMBIE_PROVIDER="k8s" + RUST_LOG="info,zombienet_orchestrator=debug" + RUN_IN_CI="1" + KUBERNETES_CPU_REQUEST="512m" + KUBERNETES_MEMORY_REQUEST="1Gi" diff --git a/Cargo.lock b/Cargo.lock index 37c65b1652517a0fd88b636786a027dbd96b411e..79cd837c6d4d0b7f334eff4a7a88d5d5763b765d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -910,6 +910,7 @@ dependencies = [ "cumulus-primitives-core 0.7.0", "emulated-integration-tests-common", "frame-support 28.0.0", + "pallet-asset-rewards", "parachains-common 7.0.0", "rococo-emulated-chain", "sp-core 28.0.0", @@ -928,6 +929,7 @@ dependencies = [ "emulated-integration-tests-common", "frame-support 28.0.0", "pallet-asset-conversion 10.0.0", + "pallet-asset-rewards", "pallet-assets 29.1.0", "pallet-balances 28.0.0", "pallet-message-queue 31.0.0", @@ -978,6 +980,7 @@ dependencies = [ "pallet-asset-conversion 10.0.0", "pallet-asset-conversion-ops 0.1.0", "pallet-asset-conversion-tx-payment 10.0.0", + "pallet-asset-rewards", "pallet-assets 29.1.0", "pallet-assets-freezer 0.1.0", "pallet-aura 27.0.0", @@ -1063,6 +1066,7 @@ dependencies = [ "frame-support 28.0.0", "frame-system 28.0.0", "pallet-asset-conversion 10.0.0", + "pallet-asset-rewards", "pallet-asset-tx-payment 28.0.0", "pallet-assets 29.1.0", "pallet-balances 28.0.0", @@ -1114,6 +1118,7 @@ dependencies = [ "pallet-asset-conversion 10.0.0", "pallet-asset-conversion-ops 0.1.0", "pallet-asset-conversion-tx-payment 10.0.0", + "pallet-asset-rewards", "pallet-assets 29.1.0", "pallet-assets-freezer 0.1.0", "pallet-aura 27.0.0", @@ -10446,9 +10451,9 @@ dependencies = [ [[package]] name = "litep2p" -version = "0.8.4" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b0fef34af8847e816003bf7fdeac5ea50b9a7a88441ac927a6166b5e812ab79" +checksum = "6ca6ee50a125dc4fc4e9a3ae3640010796d1d07bc517a0ac715fdf0b24a0b6ac" dependencies = [ "async-trait", "bs58", @@ -12036,6 +12041,27 @@ dependencies = [ "sp-runtime 39.0.2", ] +[[package]] +name = "pallet-asset-rewards" +version = "0.1.0" +dependencies = [ + "frame-benchmarking 28.0.0", + "frame-support 28.0.0", + "frame-system 28.0.0", + "pallet-assets 29.1.0", + "pallet-assets-freezer 0.1.0", + "pallet-balances 28.0.0", + "parity-scale-codec", + "primitive-types 0.13.1", + "scale-info", + "sp-api 26.0.0", + "sp-arithmetic 23.0.0", + "sp-core 28.0.0", + "sp-io 30.0.0", + "sp-runtime 31.0.1", + "sp-std 14.0.0", +] + [[package]] name = "pallet-asset-tx-payment" version = "28.0.0" @@ -13343,6 +13369,7 @@ dependencies = [ "log", "pallet-bags-list 27.0.0", "pallet-balances 28.0.0", + "pallet-delegated-staking 1.0.0", "pallet-election-provider-multi-phase 27.0.0", "pallet-nomination-pools 25.0.0", "pallet-session 28.0.0", @@ -14428,29 +14455,6 @@ dependencies = [ "sp-tracing 16.0.0", ] -[[package]] -name = "pallet-nomination-pools-test-transfer-stake" -version = "1.0.0" -dependencies = [ - "frame-election-provider-support 28.0.0", - "frame-support 28.0.0", - "frame-system 28.0.0", - "log", - "pallet-bags-list 27.0.0", - "pallet-balances 28.0.0", - "pallet-nomination-pools 25.0.0", - "pallet-staking 28.0.0", - "pallet-staking-reward-curve", - "pallet-timestamp 27.0.0", - "parity-scale-codec", - "scale-info", - "sp-core 28.0.0", - "sp-io 30.0.0", - "sp-runtime 31.0.1", - "sp-staking 26.0.0", - "sp-tracing 16.0.0", -] - [[package]] name = "pallet-offences" version = "27.0.0" @@ -14830,6 +14834,7 @@ dependencies = [ "assert_matches", "derive_more 0.99.17", "environmental", + "ethabi-decode 2.0.0", "ethereum-types 0.15.1", "frame-benchmarking 28.0.0", "frame-support 28.0.0", @@ -14848,7 +14853,7 @@ dependencies = [ "pallet-utility 28.0.0", "parity-scale-codec", "paste", - "polkavm 0.18.0", + "polkavm 0.19.0", "pretty_assertions", "rlp 0.6.1", "scale-info", @@ -14937,7 +14942,7 @@ name = "pallet-revive-fixtures" version = "0.1.0" dependencies = [ "anyhow", - "polkavm-linker 0.18.0", + "polkavm-linker 0.19.0", "sp-core 28.0.0", "sp-io 30.0.0", "toml 0.8.19", @@ -15052,7 +15057,7 @@ dependencies = [ "pallet-revive-proc-macro 0.1.0", "parity-scale-codec", "paste", - "polkavm-derive 0.18.0", + "polkavm-derive 0.19.0", "scale-info", ] @@ -18710,6 +18715,7 @@ dependencies = [ "pallet-asset-conversion-ops 0.1.0", "pallet-asset-conversion-tx-payment 10.0.0", "pallet-asset-rate 7.0.0", + "pallet-asset-rewards", "pallet-asset-tx-payment 28.0.0", "pallet-assets 29.1.0", "pallet-assets-freezer 0.1.0", @@ -19923,6 +19929,19 @@ dependencies = [ "polkavm-linux-raw 0.18.0", ] +[[package]] +name = "polkavm" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8379bb48ff026aa8ae0645ea45f27920bfd21c82b2e82ed914224bb233d59f83" +dependencies = [ + "libc", + "log", + "polkavm-assembler 0.19.0", + "polkavm-common 0.19.0", + "polkavm-linux-raw 0.19.0", +] + [[package]] name = "polkavm-assembler" version = "0.9.0" @@ -19950,6 +19969,15 @@ dependencies = [ "log", ] +[[package]] +name = "polkavm-assembler" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57513b596cf0bafb052dab48e9c168f473c35f7522e17f70cc9f96603012d9b7" +dependencies = [ + "log", +] + [[package]] name = "polkavm-common" version = "0.9.0" @@ -19979,6 +20007,16 @@ dependencies = [ "polkavm-assembler 0.18.0", ] +[[package]] +name = "polkavm-common" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a972bd305ba8cbf0de79951d6d49d2abfad47c277596be5a2c6a0924a163abbd" +dependencies = [ + "log", + "polkavm-assembler 0.19.0", +] + [[package]] name = "polkavm-derive" version = "0.9.1" @@ -20006,6 +20044,15 @@ dependencies = [ "polkavm-derive-impl-macro 0.18.0", ] +[[package]] +name = "polkavm-derive" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8d866972a7532d82d05c26b4516563660dd6676d7ab9e64e681d8ef0e29255c" +dependencies = [ + "polkavm-derive-impl-macro 0.19.0", +] + [[package]] name = "polkavm-derive-impl" version = "0.9.0" @@ -20042,6 +20089,18 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "polkavm-derive-impl" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cffca9d51b21153395a192b65698457687bc51daa41026629895542ccaa65c2" +dependencies = [ + "polkavm-common 0.19.0", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.87", +] + [[package]] name = "polkavm-derive-impl-macro" version = "0.9.0" @@ -20072,6 +20131,16 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "polkavm-derive-impl-macro" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc0dc0cf2e8f4d30874131eccfa36bdabd4a52cfb79c15f8630508abaf06a2a6" +dependencies = [ + "polkavm-derive-impl 0.19.0", + "syn 2.0.87", +] + [[package]] name = "polkavm-linker" version = "0.9.2" @@ -20118,6 +20187,22 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "polkavm-linker" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caec2308f1328b5a667da45322c04fad7ff97ad8b36817d18c7635ea4dd6c6f4" +dependencies = [ + "dirs", + "gimli 0.31.1", + "hashbrown 0.14.5", + "log", + "object 0.36.1", + "polkavm-common 0.19.0", + "regalloc2 0.9.3", + "rustc-demangle", +] + [[package]] name = "polkavm-linux-raw" version = "0.9.0" @@ -20136,6 +20221,12 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23eff02c070c70f31878a3d915e88a914ecf3e153741e2fb572dde28cce20fde" +[[package]] +name = "polkavm-linux-raw" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136ae072ab6fa38e584a06d12b1b216cff19f54d5cd202a8f8c5ec2e92e7e4bb" + [[package]] name = "polling" version = "2.8.0" diff --git a/Cargo.toml b/Cargo.toml index c30a9949e85e4212a9ead1f895889ee8bf69a53c..e17f08148b163206bc0073e3610cb5c64586cf69 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -315,6 +315,7 @@ members = [ "substrate/frame/asset-conversion", "substrate/frame/asset-conversion/ops", "substrate/frame/asset-rate", + "substrate/frame/asset-rewards", "substrate/frame/assets", "substrate/frame/assets-freezer", "substrate/frame/atomic-swap", @@ -388,7 +389,6 @@ members = [ "substrate/frame/nomination-pools/fuzzer", "substrate/frame/nomination-pools/runtime-api", "substrate/frame/nomination-pools/test-delegate-stake", - "substrate/frame/nomination-pools/test-transfer-stake", "substrate/frame/offences", "substrate/frame/offences/benchmarking", "substrate/frame/paged-list", @@ -850,7 +850,7 @@ linked-hash-map = { version = "0.5.4" } linked_hash_set = { version = "0.1.4" } linregress = { version = "0.5.1" } lite-json = { version = "0.2.0", default-features = false } -litep2p = { version = "0.8.4", features = ["websocket"] } +litep2p = { version = "0.9.0", features = ["websocket"] } log = { version = "0.4.22", default-features = false } macro_magic = { version = "0.5.1" } maplit = { version = "1.0.2" } @@ -893,6 +893,7 @@ pallet-asset-conversion = { path = "substrate/frame/asset-conversion", default-f pallet-asset-conversion-ops = { path = "substrate/frame/asset-conversion/ops", default-features = false } pallet-asset-conversion-tx-payment = { path = "substrate/frame/transaction-payment/asset-conversion-tx-payment", default-features = false } pallet-asset-rate = { path = "substrate/frame/asset-rate", default-features = false } +pallet-asset-rewards = { path = "substrate/frame/asset-rewards", default-features = false } pallet-asset-tx-payment = { path = "substrate/frame/transaction-payment/asset-tx-payment", default-features = false } pallet-assets = { path = "substrate/frame/assets", default-features = false } pallet-assets-freezer = { path = "substrate/frame/assets-freezer", default-features = false } diff --git a/bridges/relays/lib-substrate-relay/src/error.rs b/bridges/relays/lib-substrate-relay/src/error.rs index 2ebd9130f3912ba4c0552860a2259b222220e8f8..3a62f30838c7588317234409a6107b7d1d6cacf1 100644 --- a/bridges/relays/lib-substrate-relay/src/error.rs +++ b/bridges/relays/lib-substrate-relay/src/error.rs @@ -47,7 +47,7 @@ pub enum Error<Hash: Debug + MaybeDisplay, HeaderNumber: Debug + MaybeDisplay> { #[error("Failed to guess initial {0} GRANDPA authorities set id: checked all possible ids in range [0; {1}]")] GuessInitialAuthorities(&'static str, HeaderNumber), /// Failed to retrieve GRANDPA authorities at the given header from the source chain. - #[error("Failed to retrive {0} GRANDPA authorities set at header {1}: {2:?}")] + #[error("Failed to retrieve {0} GRANDPA authorities set at header {1}: {2:?}")] RetrieveAuthorities(&'static str, Hash, client::Error), /// Failed to decode GRANDPA authorities at the given header of the source chain. #[error("Failed to decode {0} GRANDPA authorities set at header {1}: {2:?}")] diff --git a/bridges/relays/messages/src/message_lane_loop.rs b/bridges/relays/messages/src/message_lane_loop.rs index 36de637f04c437450e9054dd5088c3da5eb329cd..cdc94b9fae493cc9701449a1485308476d5c758f 100644 --- a/bridges/relays/messages/src/message_lane_loop.rs +++ b/bridges/relays/messages/src/message_lane_loop.rs @@ -1041,7 +1041,7 @@ pub(crate) mod tests { #[test] fn message_lane_loop_is_able_to_recover_from_unsuccessful_transaction() { // with this configuration, both source and target clients will mine their transactions, but - // their corresponding nonce won't be udpated => reconnect will happen + // their corresponding nonce won't be updated => reconnect will happen let (exit_sender, exit_receiver) = unbounded(); let result = run_loop_test( Arc::new(Mutex::new(TestClientData { diff --git a/bridges/snowbridge/primitives/core/src/location.rs b/bridges/snowbridge/primitives/core/src/location.rs index f49a245c4126f410994390908a81b6d55e613b15..eb5ac66d46db7192d249141dbe7104fce8d619b0 100644 --- a/bridges/snowbridge/primitives/core/src/location.rs +++ b/bridges/snowbridge/primitives/core/src/location.rs @@ -206,7 +206,7 @@ mod tests { for token in token_locations { assert!( TokenIdOf::convert_location(&token).is_some(), - "Valid token = {token:?} yeilds no TokenId." + "Valid token = {token:?} yields no TokenId." ); } @@ -220,7 +220,7 @@ mod tests { for token in non_token_locations { assert!( TokenIdOf::convert_location(&token).is_none(), - "Invalid token = {token:?} yeilds a TokenId." + "Invalid token = {token:?} yields a TokenId." ); } } diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/Cargo.toml b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/Cargo.toml index a164a8197f72e6e22bf9b4a1e3a49815583992dc..c6a8baeff3b37b147c82b21bfcf1668a90d48e1d 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/Cargo.toml @@ -14,6 +14,7 @@ workspace = true # Substrate frame-support = { workspace = true } +pallet-asset-rewards = { workspace = true } sp-core = { workspace = true } sp-keyring = { workspace = true } diff --git a/cumulus/parachains/integration-tests/emulated/common/src/lib.rs b/cumulus/parachains/integration-tests/emulated/common/src/lib.rs index e2757f8b9a35b7ba07067ac708fe37babdd16af4..f5466a63f1f5b9b7018465268c15e2175d976a3c 100644 --- a/cumulus/parachains/integration-tests/emulated/common/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/common/src/lib.rs @@ -58,6 +58,8 @@ pub const USDT_ID: u32 = 1984; pub const PENPAL_A_ID: u32 = 2000; pub const PENPAL_B_ID: u32 = 2001; +pub const ASSET_HUB_ROCOCO_ID: u32 = 1000; +pub const ASSET_HUB_WESTEND_ID: u32 = 1000; pub const ASSETS_PALLET_ID: u8 = 50; parameter_types! { diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/Cargo.toml index 9e8b8f2a52d7819e3af0422f9a4f9f534a407a1d..b53edb39c73b59555994a279f122af6c234a4ce0 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/Cargo.toml @@ -17,6 +17,7 @@ codec = { workspace = true } # Substrate frame-support = { workspace = true } pallet-asset-conversion = { workspace = true } +pallet-asset-rewards = { workspace = true } pallet-assets = { workspace = true } pallet-balances = { workspace = true } pallet-message-queue = { workspace = true } diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs index f3a1b3f5bfa282b21aba49bbe79a81780508f975..513ca278a319e49e326ae0725d62cbb1324f48bf 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs @@ -76,10 +76,11 @@ mod imports { genesis::ED as ROCOCO_ED, rococo_runtime::{ governance as rococo_governance, + governance::pallet_custom_origins::Origin::Treasurer, xcm_config::{ UniversalLocation as RococoUniversalLocation, XcmConfig as RococoXcmConfig, }, - OriginCaller as RococoOriginCaller, + Dmp, OriginCaller as RococoOriginCaller, }, RococoRelayPallet as RococoPallet, }, diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/mod.rs index 88fa379c4072b97e867f3eccb68bb23b0eeedff2..75714acb07cd9e2b126bd4238e6c0377c8561e8f 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/mod.rs @@ -16,6 +16,7 @@ mod claim_assets; mod hybrid_transfers; mod reserve_transfer; +mod reward_pool; mod send; mod set_xcm_versions; mod swap; diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reward_pool.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reward_pool.rs new file mode 100644 index 0000000000000000000000000000000000000000..2f3ee536a7b9948e17d6be9ccf2033cce625b580 --- /dev/null +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reward_pool.rs @@ -0,0 +1,114 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::imports::*; +use codec::Encode; +use frame_support::{assert_ok, sp_runtime::traits::Dispatchable, traits::schedule::DispatchTime}; +use xcm_executor::traits::ConvertLocation; + +#[test] +fn treasury_creates_asset_reward_pool() { + AssetHubRococo::execute_with(|| { + type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent; + type Balances = <AssetHubRococo as AssetHubRococoPallet>::Balances; + + let treasurer = + Location::new(1, [Plurality { id: BodyId::Treasury, part: BodyPart::Voice }]); + let treasurer_account = + ahr_xcm_config::LocationToAccountId::convert_location(&treasurer).unwrap(); + + assert_ok!(Balances::force_set_balance( + <AssetHubRococo as Chain>::RuntimeOrigin::root(), + treasurer_account.clone().into(), + ASSET_HUB_ROCOCO_ED * 100_000, + )); + + let events = AssetHubRococo::events(); + match events.iter().last() { + Some(RuntimeEvent::Balances(pallet_balances::Event::BalanceSet { who, .. })) => + assert_eq!(*who, treasurer_account), + _ => panic!("Expected Balances::BalanceSet event"), + } + }); + + Rococo::execute_with(|| { + type AssetHubRococoRuntimeCall = <AssetHubRococo as Chain>::RuntimeCall; + type AssetHubRococoRuntime = <AssetHubRococo as Chain>::Runtime; + type RococoRuntimeCall = <Rococo as Chain>::RuntimeCall; + type RococoRuntime = <Rococo as Chain>::Runtime; + type RococoRuntimeEvent = <Rococo as Chain>::RuntimeEvent; + type RococoRuntimeOrigin = <Rococo as Chain>::RuntimeOrigin; + + Dmp::make_parachain_reachable(AssetHubRococo::para_id()); + + let staked_asset_id = bx!(RelayLocation::get()); + let reward_asset_id = bx!(RelayLocation::get()); + + let reward_rate_per_block = 1_000_000_000; + let lifetime = 1_000_000_000; + let admin = None; + + let create_pool_call = + RococoRuntimeCall::XcmPallet(pallet_xcm::Call::<RococoRuntime>::send { + dest: bx!(VersionedLocation::V4( + xcm::v4::Junction::Parachain(AssetHubRococo::para_id().into()).into() + )), + message: bx!(VersionedXcm::V5(Xcm(vec![ + UnpaidExecution { weight_limit: Unlimited, check_origin: None }, + Transact { + origin_kind: OriginKind::SovereignAccount, + fallback_max_weight: None, + call: AssetHubRococoRuntimeCall::AssetRewards( + pallet_asset_rewards::Call::<AssetHubRococoRuntime>::create_pool { + staked_asset_id, + reward_asset_id, + reward_rate_per_block, + expiry: DispatchTime::After(lifetime), + admin + } + ) + .encode() + .into(), + } + ]))), + }); + + let treasury_origin: RococoRuntimeOrigin = Treasurer.into(); + assert_ok!(create_pool_call.dispatch(treasury_origin)); + + assert_expected_events!( + Rococo, + vec![ + RococoRuntimeEvent::XcmPallet(pallet_xcm::Event::Sent { .. }) => {}, + ] + ); + }); + + AssetHubRococo::execute_with(|| { + type Runtime = <AssetHubRococo as Chain>::Runtime; + type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent; + + assert_eq!(1, pallet_asset_rewards::Pools::<Runtime>::iter().count()); + + let events = AssetHubRococo::events(); + match events.iter().last() { + Some(RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed { + success: true, + .. + })) => (), + _ => panic!("Expected MessageQueue::Processed event"), + } + }); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/Cargo.toml index 5cd00c239e60d24df018bccb5937fe4ed4277659..ef68a53c3b18bd629ca2feda51bb7b76defb081e 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/Cargo.toml @@ -19,6 +19,7 @@ frame-metadata-hash-extension = { workspace = true, default-features = true } frame-support = { workspace = true } frame-system = { workspace = true } pallet-asset-conversion = { workspace = true } +pallet-asset-rewards = { workspace = true } pallet-asset-tx-payment = { workspace = true } pallet-assets = { workspace = true } pallet-balances = { workspace = true } diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs index 36630e2d22217a73f691d6ed780f0471a7ba2b05..68dc87250f76bdcd458166069cf4e4b78f61e94f 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs @@ -79,8 +79,12 @@ mod imports { }, westend_emulated_chain::{ genesis::ED as WESTEND_ED, - westend_runtime::xcm_config::{ - UniversalLocation as WestendUniversalLocation, XcmConfig as WestendXcmConfig, + westend_runtime::{ + governance::pallet_custom_origins::Origin::Treasurer, + xcm_config::{ + UniversalLocation as WestendUniversalLocation, XcmConfig as WestendXcmConfig, + }, + Dmp, }, WestendRelayPallet as WestendPallet, }, diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/mod.rs index 0dfe7a85f4c2a08ecb7d7701cabe64d0045a13f2..576c44fc542fdc5fd26365078d96ac7ee4796515 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/mod.rs @@ -17,6 +17,7 @@ mod claim_assets; mod fellowship_treasury; mod hybrid_transfers; mod reserve_transfer; +mod reward_pool; mod send; mod set_asset_claimer; mod set_xcm_versions; diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reward_pool.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reward_pool.rs new file mode 100644 index 0000000000000000000000000000000000000000..4df51abcacebf4ff0102b5d50271ca9cad7e14e6 --- /dev/null +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reward_pool.rs @@ -0,0 +1,113 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::imports::*; +use codec::Encode; +use frame_support::{assert_ok, sp_runtime::traits::Dispatchable, traits::schedule::DispatchTime}; +use xcm_executor::traits::ConvertLocation; + +#[test] +fn treasury_creates_asset_reward_pool() { + AssetHubWestend::execute_with(|| { + type RuntimeEvent = <AssetHubWestend as Chain>::RuntimeEvent; + type Balances = <AssetHubWestend as AssetHubWestendPallet>::Balances; + + let treasurer = + Location::new(1, [Plurality { id: BodyId::Treasury, part: BodyPart::Voice }]); + let treasurer_account = + ahw_xcm_config::LocationToAccountId::convert_location(&treasurer).unwrap(); + + assert_ok!(Balances::force_set_balance( + <AssetHubWestend as Chain>::RuntimeOrigin::root(), + treasurer_account.clone().into(), + ASSET_HUB_WESTEND_ED * 100_000, + )); + + let events = AssetHubWestend::events(); + match events.iter().last() { + Some(RuntimeEvent::Balances(pallet_balances::Event::BalanceSet { who, .. })) => + assert_eq!(*who, treasurer_account), + _ => panic!("Expected Balances::BalanceSet event"), + } + }); + Westend::execute_with(|| { + type AssetHubWestendRuntimeCall = <AssetHubWestend as Chain>::RuntimeCall; + type AssetHubWestendRuntime = <AssetHubWestend as Chain>::Runtime; + type WestendRuntimeCall = <Westend as Chain>::RuntimeCall; + type WestendRuntime = <Westend as Chain>::Runtime; + type WestendRuntimeEvent = <Westend as Chain>::RuntimeEvent; + type WestendRuntimeOrigin = <Westend as Chain>::RuntimeOrigin; + + Dmp::make_parachain_reachable(AssetHubWestend::para_id()); + + let staked_asset_id = bx!(RelayLocation::get()); + let reward_asset_id = bx!(RelayLocation::get()); + + let reward_rate_per_block = 1_000_000_000; + let lifetime = 1_000_000_000; + let admin = None; + + let create_pool_call = + WestendRuntimeCall::XcmPallet(pallet_xcm::Call::<WestendRuntime>::send { + dest: bx!(VersionedLocation::V4( + xcm::v4::Junction::Parachain(AssetHubWestend::para_id().into()).into() + )), + message: bx!(VersionedXcm::V5(Xcm(vec![ + UnpaidExecution { weight_limit: Unlimited, check_origin: None }, + Transact { + origin_kind: OriginKind::SovereignAccount, + fallback_max_weight: None, + call: AssetHubWestendRuntimeCall::AssetRewards( + pallet_asset_rewards::Call::<AssetHubWestendRuntime>::create_pool { + staked_asset_id, + reward_asset_id, + reward_rate_per_block, + expiry: DispatchTime::After(lifetime), + admin + } + ) + .encode() + .into(), + } + ]))), + }); + + let treasury_origin: WestendRuntimeOrigin = Treasurer.into(); + assert_ok!(create_pool_call.dispatch(treasury_origin)); + + assert_expected_events!( + Westend, + vec![ + WestendRuntimeEvent::XcmPallet(pallet_xcm::Event::Sent { .. }) => {}, + ] + ); + }); + + AssetHubWestend::execute_with(|| { + type Runtime = <AssetHubWestend as Chain>::Runtime; + type RuntimeEvent = <AssetHubWestend as Chain>::RuntimeEvent; + + assert_eq!(1, pallet_asset_rewards::Pools::<Runtime>::iter().count()); + + let events = AssetHubWestend::events(); + match events.iter().last() { + Some(RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed { + success: true, + .. + })) => (), + _ => panic!("Expected MessageQueue::Processed event"), + } + }); +} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml index abe59a8439a8af9f064dd179f07e629a9e7a5038..d612dd03c247a101049de02c4bf7822c73302783 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml @@ -30,6 +30,7 @@ frame-try-runtime = { optional = true, workspace = true } pallet-asset-conversion = { workspace = true } pallet-asset-conversion-ops = { workspace = true } pallet-asset-conversion-tx-payment = { workspace = true } +pallet-asset-rewards = { workspace = true } pallet-assets = { workspace = true } pallet-assets-freezer = { workspace = true } pallet-aura = { workspace = true } @@ -61,6 +62,7 @@ sp-storage = { workspace = true } sp-transaction-pool = { workspace = true } sp-version = { workspace = true } sp-weights = { workspace = true } + # num-traits feature needed for dex integer sq root: primitive-types = { features = ["codec", "num-traits", "scale-info"], workspace = true } @@ -123,6 +125,7 @@ runtime-benchmarks = [ "pallet-asset-conversion-ops/runtime-benchmarks", "pallet-asset-conversion-tx-payment/runtime-benchmarks", "pallet-asset-conversion/runtime-benchmarks", + "pallet-asset-rewards/runtime-benchmarks", "pallet-assets-freezer/runtime-benchmarks", "pallet-assets/runtime-benchmarks", "pallet-balances/runtime-benchmarks", @@ -162,6 +165,7 @@ try-runtime = [ "pallet-asset-conversion-ops/try-runtime", "pallet-asset-conversion-tx-payment/try-runtime", "pallet-asset-conversion/try-runtime", + "pallet-asset-rewards/try-runtime", "pallet-assets-freezer/try-runtime", "pallet-assets/try-runtime", "pallet-aura/try-runtime", @@ -212,6 +216,7 @@ std = [ "pallet-asset-conversion-ops/std", "pallet-asset-conversion-tx-payment/std", "pallet-asset-conversion/std", + "pallet-asset-rewards/std", "pallet-assets-freezer/std", "pallet-assets/std", "pallet-aura/std", diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs index db9a8201ebbe0517dceb1e7ba187171e32292721..43b7bf0ba1184e095c2f53eeefceb2b88b154a9a 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -35,7 +35,7 @@ use assets_common::{ foreign_creators::ForeignCreators, local_and_foreign_assets::{LocalFromLeft, TargetFromLeft}, matching::{FromNetwork, FromSiblingParachain}, - AssetIdForTrustBackedAssetsConvert, + AssetIdForPoolAssets, AssetIdForPoolAssetsConvert, AssetIdForTrustBackedAssetsConvert, }; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; use cumulus_primitives_core::{AggregateMessageOrigin, ClaimQueueOffset, CoreSelector}; @@ -61,9 +61,9 @@ use frame_support::{ genesis_builder_helper::{build_state, get_preset}, ord_parameter_types, parameter_types, traits::{ - fungible, fungibles, tokens::imbalance::ResolveAssetTo, AsEnsureOriginWithArg, ConstBool, - ConstU128, ConstU32, ConstU64, ConstU8, EitherOfDiverse, Equals, InstanceFilter, - TransformOrigin, + fungible, fungible::HoldConsideration, fungibles, tokens::imbalance::ResolveAssetTo, + AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU32, ConstU64, ConstU8, + ConstantStoragePrice, EitherOfDiverse, Equals, InstanceFilter, TransformOrigin, }, weights::{ConstantMultiplier, Weight, WeightToFee as _}, BoundedVec, PalletId, @@ -84,8 +84,8 @@ use sp_runtime::{Perbill, RuntimeDebug}; use testnet_parachains_constants::rococo::{consensus::*, currency::*, fee::WeightToFee, time::*}; use xcm_config::{ ForeignAssetsConvertedConcreteId, GovernanceLocation, LocationToAccountId, - PoolAssetsConvertedConcreteId, TokenLocation, TrustBackedAssetsConvertedConcreteId, - TrustBackedAssetsPalletLocation, + PoolAssetsConvertedConcreteId, PoolAssetsPalletLocation, TokenLocation, + TrustBackedAssetsConvertedConcreteId, TrustBackedAssetsPalletLocation, }; #[cfg(test)] @@ -111,6 +111,9 @@ use xcm_runtime_apis::{ fees::Error as XcmPaymentApiError, }; +#[cfg(feature = "runtime-benchmarks")] +use frame_support::traits::PalletInfoAccess; + use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; impl_opaque_keys! { @@ -217,8 +220,8 @@ impl pallet_balances::Config for Runtime { type ReserveIdentifier = [u8; 8]; type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; - type FreezeIdentifier = (); - type MaxFreezes = ConstU32<0>; + type FreezeIdentifier = RuntimeFreezeReason; + type MaxFreezes = ConstU32<50>; type DoneSlashHandler = (); } @@ -302,7 +305,7 @@ impl pallet_assets::Config<PoolAssetsInstance> for Runtime { type RuntimeEvent = RuntimeEvent; type Balance = Balance; type RemoveItemsLimit = ConstU32<1000>; - type AssetId = u32; + type AssetId = AssetIdForPoolAssets; type AssetIdParameter = u32; type Currency = Balances; type CreateOrigin = @@ -343,8 +346,21 @@ pub type LocalAndForeignAssets = fungibles::UnionOf< AccountId, >; -/// Union fungibles implementation for [`LocalAndForeignAssets`] and `Balances`. -pub type NativeAndAssets = fungible::UnionOf< +/// Union fungibles implementation for `AssetsFreezer` and `ForeignAssetsFreezer`. +pub type LocalAndForeignAssetsFreezer = fungibles::UnionOf< + AssetsFreezer, + ForeignAssetsFreezer, + LocalFromLeft< + AssetIdForTrustBackedAssetsConvert<TrustBackedAssetsPalletLocation, xcm::v5::Location>, + AssetIdForTrustBackedAssets, + xcm::v5::Location, + >, + xcm::v5::Location, + AccountId, +>; + +/// Union fungibles implementation for [`LocalAndForeignAssets`] and [`Balances`]. +pub type NativeAndNonPoolAssets = fungible::UnionOf< Balances, LocalAndForeignAssets, TargetFromLeft<TokenLocation, xcm::v5::Location>, @@ -352,6 +368,45 @@ pub type NativeAndAssets = fungible::UnionOf< AccountId, >; +/// Union fungibles implementation for [`LocalAndForeignAssetsFreezer`] and [`Balances`]. +pub type NativeAndNonPoolAssetsFreezer = fungible::UnionOf< + Balances, + LocalAndForeignAssetsFreezer, + TargetFromLeft<TokenLocation, xcm::v5::Location>, + xcm::v5::Location, + AccountId, +>; + +/// Union fungibles implementation for [`PoolAssets`] and [`NativeAndNonPoolAssets`]. +/// +/// NOTE: Should be kept updated to include ALL balances and assets in the runtime. +pub type NativeAndAllAssets = fungibles::UnionOf< + PoolAssets, + NativeAndNonPoolAssets, + LocalFromLeft< + AssetIdForPoolAssetsConvert<PoolAssetsPalletLocation, xcm::v5::Location>, + AssetIdForPoolAssets, + xcm::v5::Location, + >, + xcm::v5::Location, + AccountId, +>; + +/// Union fungibles implementation for [`PoolAssetsFreezer`] and [`NativeAndNonPoolAssetsFreezer`]. +/// +/// NOTE: Should be kept updated to include ALL balances and assets in the runtime. +pub type NativeAndAllAssetsFreezer = fungibles::UnionOf< + PoolAssetsFreezer, + NativeAndNonPoolAssetsFreezer, + LocalFromLeft< + AssetIdForPoolAssetsConvert<PoolAssetsPalletLocation, xcm::v5::Location>, + AssetIdForPoolAssets, + xcm::v5::Location, + >, + xcm::v5::Location, + AccountId, +>; + pub type PoolIdToAccountId = pallet_asset_conversion::AccountIdConverter< AssetConversionPalletId, (xcm::v5::Location, xcm::v5::Location), @@ -362,7 +417,7 @@ impl pallet_asset_conversion::Config for Runtime { type Balance = Balance; type HigherPrecisionBalance = sp_core::U256; type AssetKind = xcm::v5::Location; - type Assets = NativeAndAssets; + type Assets = NativeAndNonPoolAssets; type PoolId = (Self::AssetKind, Self::AssetKind); type PoolLocator = pallet_asset_conversion::WithFirstAsset< TokenLocation, @@ -823,9 +878,9 @@ impl pallet_asset_conversion_tx_payment::Config for Runtime { type AssetId = xcm::v5::Location; type OnChargeAssetTransaction = SwapAssetAdapter< TokenLocation, - NativeAndAssets, + NativeAndNonPoolAssets, AssetConversion, - ResolveAssetTo<StakingPot, NativeAndAssets>, + ResolveAssetTo<StakingPot, NativeAndNonPoolAssets>, >; type WeightInfo = weights::pallet_asset_conversion_tx_payment::WeightInfo<Runtime>; #[cfg(feature = "runtime-benchmarks")] @@ -953,6 +1008,55 @@ impl pallet_xcm_bridge_hub_router::Config<ToWestendXcmRouterInstance> for Runtim type FeeAsset = xcm_config::bridging::XcmBridgeHubRouterFeeAssetId; } +#[cfg(feature = "runtime-benchmarks")] +pub struct PalletAssetRewardsBenchmarkHelper; + +#[cfg(feature = "runtime-benchmarks")] +impl pallet_asset_rewards::benchmarking::BenchmarkHelper<xcm::v5::Location> + for PalletAssetRewardsBenchmarkHelper +{ + fn staked_asset() -> Location { + Location::new( + 0, + [PalletInstance(<Assets as PalletInfoAccess>::index() as u8), GeneralIndex(100)], + ) + } + fn reward_asset() -> Location { + Location::new( + 0, + [PalletInstance(<Assets as PalletInfoAccess>::index() as u8), GeneralIndex(101)], + ) + } +} + +parameter_types! { + pub const AssetRewardsPalletId: PalletId = PalletId(*b"py/astrd"); + pub const RewardsPoolCreationHoldReason: RuntimeHoldReason = + RuntimeHoldReason::AssetRewards(pallet_asset_rewards::HoldReason::PoolCreation); + // 1 item, 135 bytes into the storage on pool creation. + pub const StakePoolCreationDeposit: Balance = deposit(1, 135); +} + +impl pallet_asset_rewards::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type PalletId = AssetRewardsPalletId; + type Balance = Balance; + type Assets = NativeAndAllAssets; + type AssetsFreezer = NativeAndAllAssetsFreezer; + type AssetId = xcm::v5::Location; + type CreatePoolOrigin = EnsureSigned<AccountId>; + type RuntimeFreezeReason = RuntimeFreezeReason; + type Consideration = HoldConsideration< + AccountId, + Balances, + RewardsPoolCreationHoldReason, + ConstantStoragePrice<StakePoolCreationDeposit, Balance>, + >; + type WeightInfo = weights::pallet_asset_rewards::WeightInfo<Runtime>; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = PalletAssetRewardsBenchmarkHelper; +} + // Create the runtime by composing the FRAME pallets that were previously configured. construct_runtime!( pub enum Runtime @@ -998,10 +1102,13 @@ construct_runtime!( NftFractionalization: pallet_nft_fractionalization = 54, PoolAssets: pallet_assets::<Instance3> = 55, AssetConversion: pallet_asset_conversion = 56, + AssetsFreezer: pallet_assets_freezer::<Instance1> = 57, ForeignAssetsFreezer: pallet_assets_freezer::<Instance2> = 58, PoolAssetsFreezer: pallet_assets_freezer::<Instance3> = 59, + AssetRewards: pallet_asset_rewards = 60, + // TODO: the pallet instance should be removed once all pools have migrated // to the new account IDs. AssetConversionMigration: pallet_asset_conversion_ops = 200, @@ -1193,6 +1300,7 @@ mod benches { [pallet_assets, Foreign] [pallet_assets, Pool] [pallet_asset_conversion, AssetConversion] + [pallet_asset_rewards, AssetRewards] [pallet_asset_conversion_tx_payment, AssetTxPayment] [pallet_balances, Balances] [pallet_message_queue, MessageQueue] @@ -1503,6 +1611,12 @@ impl_runtime_apis! { } } + impl pallet_asset_rewards::AssetRewards<Block, Balance> for Runtime { + fn pool_creation_cost() -> Balance { + StakePoolCreationDeposit::get() + } + } + impl cumulus_primitives_core::GetCoreSelectorApi<Block> for Runtime { fn core_selector() -> (CoreSelector, ClaimQueueOffset) { ParachainSystem::core_selector() diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/mod.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/mod.rs index ae78a56d8b3c1fef27e54e37b940098396426883..6893766ac72d2113f993e54d3b28e5e136acac3d 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/mod.rs @@ -24,6 +24,7 @@ pub mod frame_system_extensions; pub mod pallet_asset_conversion; pub mod pallet_asset_conversion_ops; pub mod pallet_asset_conversion_tx_payment; +pub mod pallet_asset_rewards; pub mod pallet_assets_foreign; pub mod pallet_assets_local; pub mod pallet_assets_pool; diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_asset_rewards.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_asset_rewards.rs new file mode 100644 index 0000000000000000000000000000000000000000..218c93c51035037e5d325dc5b3d31897e164f0ec --- /dev/null +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_asset_rewards.rs @@ -0,0 +1,217 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see <http://www.gnu.org/licenses/>. + +//! Autogenerated weights for `pallet_asset_rewards` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2025-01-14, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-ys-ssygq-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-rococo-dev")`, DB CACHE: 1024 + +// Executed Command: +// target/production/polkadot-parachain +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_asset_rewards +// --chain=asset-hub-rococo-dev +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_asset_rewards`. +pub struct WeightInfo<T>(PhantomData<T>); +impl<T: frame_system::Config> pallet_asset_rewards::WeightInfo for WeightInfo<T> { + /// Storage: `Assets::Asset` (r:2 w:0) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `AssetRewards::NextPoolId` (r:1 w:1) + /// Proof: `AssetRewards::NextPoolId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + /// Storage: `AssetRewards::PoolCost` (r:0 w:1) + /// Proof: `AssetRewards::PoolCost` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + /// Storage: `AssetRewards::Pools` (r:0 w:1) + /// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(1344), added: 3819, mode: `MaxEncodedLen`) + fn create_pool() -> Weight { + // Proof Size summary in bytes: + // Measured: `358` + // Estimated: `6360` + // Minimum execution time: 65_882_000 picoseconds. + Weight::from_parts(67_073_000, 0) + .saturating_add(Weight::from_parts(0, 6360)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(5)) + } + /// Storage: `AssetRewards::Pools` (r:1 w:1) + /// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(1344), added: 3819, mode: `MaxEncodedLen`) + /// Storage: `AssetRewards::PoolStakers` (r:1 w:1) + /// Proof: `AssetRewards::PoolStakers` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `AssetsFreezer::Freezes` (r:1 w:1) + /// Proof: `AssetsFreezer::Freezes` (`max_values`: None, `max_size`: Some(87), added: 2562, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1 w:0) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `AssetsFreezer::FrozenBalances` (r:1 w:1) + /// Proof: `AssetsFreezer::FrozenBalances` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + fn stake() -> Weight { + // Proof Size summary in bytes: + // Measured: `872` + // Estimated: `4809` + // Minimum execution time: 56_950_000 picoseconds. + Weight::from_parts(58_088_000, 0) + .saturating_add(Weight::from_parts(0, 4809)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(4)) + } + /// Storage: `AssetRewards::Pools` (r:1 w:1) + /// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(1344), added: 3819, mode: `MaxEncodedLen`) + /// Storage: `AssetRewards::PoolStakers` (r:1 w:1) + /// Proof: `AssetRewards::PoolStakers` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `AssetsFreezer::Freezes` (r:1 w:1) + /// Proof: `AssetsFreezer::Freezes` (`max_values`: None, `max_size`: Some(87), added: 2562, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1 w:0) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `AssetsFreezer::FrozenBalances` (r:1 w:1) + /// Proof: `AssetsFreezer::FrozenBalances` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + fn unstake() -> Weight { + // Proof Size summary in bytes: + // Measured: `872` + // Estimated: `4809` + // Minimum execution time: 59_509_000 picoseconds. + Weight::from_parts(61_064_000, 0) + .saturating_add(Weight::from_parts(0, 4809)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(4)) + } + /// Storage: `AssetRewards::Pools` (r:1 w:0) + /// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(1344), added: 3819, mode: `MaxEncodedLen`) + /// Storage: `AssetRewards::PoolStakers` (r:1 w:1) + /// Proof: `AssetRewards::PoolStakers` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:2 w:2) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `AssetsFreezer::FrozenBalances` (r:1 w:0) + /// Proof: `AssetsFreezer::FrozenBalances` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + fn harvest_rewards() -> Weight { + // Proof Size summary in bytes: + // Measured: `1072` + // Estimated: `6208` + // Minimum execution time: 80_685_000 picoseconds. + Weight::from_parts(83_505_000, 0) + .saturating_add(Weight::from_parts(0, 6208)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(4)) + } + /// Storage: `AssetRewards::Pools` (r:1 w:1) + /// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(1344), added: 3819, mode: `MaxEncodedLen`) + fn set_pool_reward_rate_per_block() -> Weight { + // Proof Size summary in bytes: + // Measured: `318` + // Estimated: `4809` + // Minimum execution time: 17_032_000 picoseconds. + Weight::from_parts(17_628_000, 0) + .saturating_add(Weight::from_parts(0, 4809)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `AssetRewards::Pools` (r:1 w:1) + /// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(1344), added: 3819, mode: `MaxEncodedLen`) + fn set_pool_admin() -> Weight { + // Proof Size summary in bytes: + // Measured: `318` + // Estimated: `4809` + // Minimum execution time: 15_290_000 picoseconds. + Weight::from_parts(16_212_000, 0) + .saturating_add(Weight::from_parts(0, 4809)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `AssetRewards::Pools` (r:1 w:1) + /// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(1344), added: 3819, mode: `MaxEncodedLen`) + fn set_pool_expiry_block() -> Weight { + // Proof Size summary in bytes: + // Measured: `318` + // Estimated: `4809` + // Minimum execution time: 17_721_000 picoseconds. + Weight::from_parts(18_603_000, 0) + .saturating_add(Weight::from_parts(0, 4809)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `AssetRewards::Pools` (r:1 w:0) + /// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(1344), added: 3819, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:2 w:2) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `AssetsFreezer::FrozenBalances` (r:1 w:0) + /// Proof: `AssetsFreezer::FrozenBalances` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn deposit_reward_tokens() -> Weight { + // Proof Size summary in bytes: + // Measured: `747` + // Estimated: `6208` + // Minimum execution time: 67_754_000 picoseconds. + Weight::from_parts(69_428_000, 0) + .saturating_add(Weight::from_parts(0, 6208)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(4)) + } + /// Storage: `AssetRewards::Pools` (r:1 w:1) + /// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(1344), added: 3819, mode: `MaxEncodedLen`) + /// Storage: `AssetRewards::PoolStakers` (r:1 w:0) + /// Proof: `AssetRewards::PoolStakers` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:2 w:2) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `AssetsFreezer::FrozenBalances` (r:1 w:1) + /// Proof: `AssetsFreezer::FrozenBalances` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `AssetRewards::PoolCost` (r:1 w:1) + /// Proof: `AssetRewards::PoolCost` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + /// Storage: `AssetsFreezer::Freezes` (r:0 w:1) + /// Proof: `AssetsFreezer::Freezes` (`max_values`: None, `max_size`: Some(87), added: 2562, mode: `MaxEncodedLen`) + fn cleanup_pool() -> Weight { + // Proof Size summary in bytes: + // Measured: `1105` + // Estimated: `6208` + // Minimum execution time: 127_524_000 picoseconds. + Weight::from_parts(130_238_000, 0) + .saturating_add(Weight::from_parts(0, 6208)) + .saturating_add(T::DbWeight::get().reads(10)) + .saturating_add(T::DbWeight::get().writes(10)) + } +} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs index 08b2f520c4b9ac1abb39f9d185c821dec7241d0f..0c6ff5e4bfddc6c236fe1ec4a5a5f425027f41f5 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs @@ -76,6 +76,10 @@ parameter_types! { pub TrustBackedAssetsPalletLocation: Location = PalletInstance(TrustBackedAssetsPalletIndex::get()).into(); pub TrustBackedAssetsPalletIndex: u8 = <Assets as PalletInfoAccess>::index() as u8; + pub TrustBackedAssetsPalletLocationV3: xcm::v3::Location = + xcm::v3::Junction::PalletInstance(<Assets as PalletInfoAccess>::index() as u8).into(); + pub PoolAssetsPalletLocationV3: xcm::v3::Location = + xcm::v3::Junction::PalletInstance(<PoolAssets as PalletInfoAccess>::index() as u8).into(); pub ForeignAssetsPalletLocation: Location = PalletInstance(<ForeignAssets as PalletInfoAccess>::index() as u8).into(); pub PoolAssetsPalletLocation: Location = @@ -336,7 +340,7 @@ pub type TrustedTeleporters = ( /// asset and the asset required for fee payment. pub type PoolAssetsExchanger = SingleAssetExchangeAdapter< crate::AssetConversion, - crate::NativeAndAssets, + crate::NativeAndNonPoolAssets, ( TrustBackedAssetsAsLocation<TrustBackedAssetsPalletLocation, Balance, xcm::v5::Location>, ForeignAssetsConvertedConcreteId, @@ -387,7 +391,7 @@ impl xcm_executor::Config for XcmConfig { TokenLocation, crate::AssetConversion, WeightToFee, - crate::NativeAndAssets, + crate::NativeAndNonPoolAssets, ( TrustBackedAssetsAsLocation< TrustBackedAssetsPalletLocation, @@ -396,7 +400,7 @@ impl xcm_executor::Config for XcmConfig { >, ForeignAssetsConvertedConcreteId, ), - ResolveAssetTo<StakingPot, crate::NativeAndAssets>, + ResolveAssetTo<StakingPot, crate::NativeAndNonPoolAssets>, AccountId, >, // This trader allows to pay with `is_sufficient=true` "Trust Backed" assets from dedicated diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml index cb10ae9a480034b3067e436a4760692357789645..65ef63a7fb356c1b2c20a9fcddbc4bdc3d3b8bff 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml @@ -30,6 +30,7 @@ frame-try-runtime = { optional = true, workspace = true } pallet-asset-conversion = { workspace = true } pallet-asset-conversion-ops = { workspace = true } pallet-asset-conversion-tx-payment = { workspace = true } +pallet-asset-rewards = { workspace = true } pallet-assets = { workspace = true } pallet-assets-freezer = { workspace = true } pallet-aura = { workspace = true } @@ -62,6 +63,7 @@ sp-std = { workspace = true } sp-storage = { workspace = true } sp-transaction-pool = { workspace = true } sp-version = { workspace = true } + # num-traits feature needed for dex integer sq root: primitive-types = { features = ["codec", "num-traits", "scale-info"], workspace = true } @@ -125,6 +127,7 @@ runtime-benchmarks = [ "pallet-asset-conversion-ops/runtime-benchmarks", "pallet-asset-conversion-tx-payment/runtime-benchmarks", "pallet-asset-conversion/runtime-benchmarks", + "pallet-asset-rewards/runtime-benchmarks", "pallet-assets-freezer/runtime-benchmarks", "pallet-assets/runtime-benchmarks", "pallet-balances/runtime-benchmarks", @@ -166,6 +169,7 @@ try-runtime = [ "pallet-asset-conversion-ops/try-runtime", "pallet-asset-conversion-tx-payment/try-runtime", "pallet-asset-conversion/try-runtime", + "pallet-asset-rewards/try-runtime", "pallet-assets-freezer/try-runtime", "pallet-assets/try-runtime", "pallet-aura/try-runtime", @@ -218,6 +222,7 @@ std = [ "pallet-asset-conversion-ops/std", "pallet-asset-conversion-tx-payment/std", "pallet-asset-conversion/std", + "pallet-asset-rewards/std", "pallet-assets-freezer/std", "pallet-assets/std", "pallet-aura/std", diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index cfc150ce5d6f7614c1d23cc6cb0bfb408a11f722..41f29fe2c56a098dd64adfaa39f2c28bfb968e01 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -33,7 +33,7 @@ extern crate alloc; use alloc::{vec, vec::Vec}; use assets_common::{ local_and_foreign_assets::{LocalFromLeft, TargetFromLeft}, - AssetIdForTrustBackedAssetsConvert, + AssetIdForPoolAssets, AssetIdForPoolAssetsConvert, AssetIdForTrustBackedAssetsConvert, }; use codec::{Decode, Encode, MaxEncodedLen}; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; @@ -44,10 +44,12 @@ use frame_support::{ genesis_builder_helper::{build_state, get_preset}, ord_parameter_types, parameter_types, traits::{ - fungible, fungibles, + fungible, + fungible::HoldConsideration, + fungibles, tokens::{imbalance::ResolveAssetTo, nonfungibles_v2::Inspect}, - AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU32, ConstU64, ConstU8, Equals, - InstanceFilter, Nothing, TransformOrigin, + AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU32, ConstU64, ConstU8, + ConstantStoragePrice, Equals, InstanceFilter, Nothing, TransformOrigin, }, weights::{ConstantMultiplier, Weight, WeightToFee as _}, BoundedVec, PalletId, @@ -81,8 +83,8 @@ use testnet_parachains_constants::westend::{ }; use xcm_config::{ ForeignAssetsConvertedConcreteId, LocationToAccountId, PoolAssetsConvertedConcreteId, - TrustBackedAssetsConvertedConcreteId, TrustBackedAssetsPalletLocation, WestendLocation, - XcmOriginToTransactDispatchOrigin, + PoolAssetsPalletLocation, TrustBackedAssetsConvertedConcreteId, + TrustBackedAssetsPalletLocation, WestendLocation, XcmOriginToTransactDispatchOrigin, }; #[cfg(any(feature = "std", test))] @@ -93,11 +95,15 @@ use assets_common::{ matching::{FromNetwork, FromSiblingParachain}, }; use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; +use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; use xcm::{ latest::prelude::AssetId, prelude::{VersionedAsset, VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm}, }; +#[cfg(feature = "runtime-benchmarks")] +use frame_support::traits::PalletInfoAccess; + #[cfg(feature = "runtime-benchmarks")] use xcm::latest::prelude::{ Asset, Assets as XcmAssets, Fungible, Here, InteriorLocation, Junction, Junction::*, Location, @@ -109,8 +115,6 @@ use xcm_runtime_apis::{ fees::Error as XcmPaymentApiError, }; -use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; - impl_opaque_keys! { pub struct SessionKeys { pub aura: Aura, @@ -125,7 +129,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: alloc::borrow::Cow::Borrowed("westmint"), impl_name: alloc::borrow::Cow::Borrowed("westmint"), authoring_version: 1, - spec_version: 1_017_003, + spec_version: 1_017_004, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 16, @@ -218,8 +222,8 @@ impl pallet_balances::Config for Runtime { type ReserveIdentifier = [u8; 8]; type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; - type FreezeIdentifier = (); - type MaxFreezes = ConstU32<0>; + type FreezeIdentifier = RuntimeFreezeReason; + type MaxFreezes = ConstU32<50>; type DoneSlashHandler = (); } @@ -341,8 +345,22 @@ pub type LocalAndForeignAssets = fungibles::UnionOf< xcm::v5::Location, AccountId, >; + +/// Union fungibles implementation for `AssetsFreezer` and `ForeignAssetsFreezer`. +pub type LocalAndForeignAssetsFreezer = fungibles::UnionOf< + AssetsFreezer, + ForeignAssetsFreezer, + LocalFromLeft< + AssetIdForTrustBackedAssetsConvert<TrustBackedAssetsPalletLocation, xcm::v5::Location>, + AssetIdForTrustBackedAssets, + xcm::v5::Location, + >, + xcm::v5::Location, + AccountId, +>; + /// Union fungibles implementation for [`LocalAndForeignAssets`] and `Balances`. -pub type NativeAndAssets = fungible::UnionOf< +pub type NativeAndNonPoolAssets = fungible::UnionOf< Balances, LocalAndForeignAssets, TargetFromLeft<WestendLocation, xcm::v5::Location>, @@ -350,6 +368,45 @@ pub type NativeAndAssets = fungible::UnionOf< AccountId, >; +/// Union fungibles implementation for [`LocalAndForeignAssetsFreezer`] and [`Balances`]. +pub type NativeAndNonPoolAssetsFreezer = fungible::UnionOf< + Balances, + LocalAndForeignAssetsFreezer, + TargetFromLeft<WestendLocation, xcm::v5::Location>, + xcm::v5::Location, + AccountId, +>; + +/// Union fungibles implementation for [`PoolAssets`] and [`NativeAndNonPoolAssets`]. +/// +/// NOTE: Should be kept updated to include ALL balances and assets in the runtime. +pub type NativeAndAllAssets = fungibles::UnionOf< + PoolAssets, + NativeAndNonPoolAssets, + LocalFromLeft< + AssetIdForPoolAssetsConvert<PoolAssetsPalletLocation, xcm::v5::Location>, + AssetIdForPoolAssets, + xcm::v5::Location, + >, + xcm::v5::Location, + AccountId, +>; + +/// Union fungibles implementation for [`PoolAssetsFreezer`] and [`NativeAndNonPoolAssetsFreezer`]. +/// +/// NOTE: Should be kept updated to include ALL balances and assets in the runtime. +pub type NativeAndAllAssetsFreezer = fungibles::UnionOf< + PoolAssetsFreezer, + NativeAndNonPoolAssetsFreezer, + LocalFromLeft< + AssetIdForPoolAssetsConvert<PoolAssetsPalletLocation, xcm::v5::Location>, + AssetIdForPoolAssets, + xcm::v5::Location, + >, + xcm::v5::Location, + AccountId, +>; + pub type PoolIdToAccountId = pallet_asset_conversion::AccountIdConverter< AssetConversionPalletId, (xcm::v5::Location, xcm::v5::Location), @@ -360,7 +417,7 @@ impl pallet_asset_conversion::Config for Runtime { type Balance = Balance; type HigherPrecisionBalance = sp_core::U256; type AssetKind = xcm::v5::Location; - type Assets = NativeAndAssets; + type Assets = NativeAndNonPoolAssets; type PoolId = (Self::AssetKind, Self::AssetKind); type PoolLocator = pallet_asset_conversion::WithFirstAsset< WestendLocation, @@ -388,6 +445,55 @@ impl pallet_asset_conversion::Config for Runtime { >; } +#[cfg(feature = "runtime-benchmarks")] +pub struct PalletAssetRewardsBenchmarkHelper; + +#[cfg(feature = "runtime-benchmarks")] +impl pallet_asset_rewards::benchmarking::BenchmarkHelper<xcm::v5::Location> + for PalletAssetRewardsBenchmarkHelper +{ + fn staked_asset() -> Location { + Location::new( + 0, + [PalletInstance(<Assets as PalletInfoAccess>::index() as u8), GeneralIndex(100)], + ) + } + fn reward_asset() -> Location { + Location::new( + 0, + [PalletInstance(<Assets as PalletInfoAccess>::index() as u8), GeneralIndex(101)], + ) + } +} + +parameter_types! { + pub const AssetRewardsPalletId: PalletId = PalletId(*b"py/astrd"); + pub const RewardsPoolCreationHoldReason: RuntimeHoldReason = + RuntimeHoldReason::AssetRewards(pallet_asset_rewards::HoldReason::PoolCreation); + // 1 item, 135 bytes into the storage on pool creation. + pub const StakePoolCreationDeposit: Balance = deposit(1, 135); +} + +impl pallet_asset_rewards::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type PalletId = AssetRewardsPalletId; + type Balance = Balance; + type Assets = NativeAndAllAssets; + type AssetsFreezer = NativeAndAllAssetsFreezer; + type AssetId = xcm::v5::Location; + type CreatePoolOrigin = EnsureSigned<AccountId>; + type RuntimeFreezeReason = RuntimeFreezeReason; + type Consideration = HoldConsideration< + AccountId, + Balances, + RewardsPoolCreationHoldReason, + ConstantStoragePrice<StakePoolCreationDeposit, Balance>, + >; + type WeightInfo = weights::pallet_asset_rewards::WeightInfo<Runtime>; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = PalletAssetRewardsBenchmarkHelper; +} + impl pallet_asset_conversion_ops::Config for Runtime { type RuntimeEvent = RuntimeEvent; type PriorAccountIdConverter = pallet_asset_conversion::AccountIdConverterNoSeed< @@ -816,9 +922,9 @@ impl pallet_asset_conversion_tx_payment::Config for Runtime { type AssetId = xcm::v5::Location; type OnChargeAssetTransaction = SwapAssetAdapter< WestendLocation, - NativeAndAssets, + NativeAndNonPoolAssets, AssetConversion, - ResolveAssetTo<StakingPot, NativeAndAssets>, + ResolveAssetTo<StakingPot, NativeAndNonPoolAssets>, >; type WeightInfo = weights::pallet_asset_conversion_tx_payment::WeightInfo<Runtime>; #[cfg(feature = "runtime-benchmarks")] @@ -952,11 +1058,6 @@ parameter_types! { pub CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(30); } -type EventRecord = frame_system::EventRecord< - <Runtime as frame_system::Config>::RuntimeEvent, - <Runtime as frame_system::Config>::Hash, ->; - impl pallet_revive::Config for Runtime { type Time = Timestamp; type Currency = Balances; @@ -976,7 +1077,6 @@ impl pallet_revive::Config for Runtime { type InstantiateOrigin = EnsureSigned<Self::AccountId>; type RuntimeHoldReason = RuntimeHoldReason; type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; - type Debug = (); type Xcm = pallet_xcm::Pallet<Self>; type ChainId = ConstU64<420_420_421>; type NativeToEthRatio = ConstU32<1_000_000>; // 10^(18 - 12) Eth is 10^18, Native is 10^12. @@ -1040,11 +1140,14 @@ construct_runtime!( NftFractionalization: pallet_nft_fractionalization = 54, PoolAssets: pallet_assets::<Instance3> = 55, AssetConversion: pallet_asset_conversion = 56, + AssetsFreezer: pallet_assets_freezer::<Instance1> = 57, ForeignAssetsFreezer: pallet_assets_freezer::<Instance2> = 58, PoolAssetsFreezer: pallet_assets_freezer::<Instance3> = 59, Revive: pallet_revive = 60, + AssetRewards: pallet_asset_rewards = 61, + StateTrieMigration: pallet_state_trie_migration = 70, // TODO: the pallet instance should be removed once all pools have migrated @@ -1322,6 +1425,7 @@ mod benches { [pallet_assets, Foreign] [pallet_assets, Pool] [pallet_asset_conversion, AssetConversion] + [pallet_asset_rewards, AssetRewards] [pallet_asset_conversion_tx_payment, AssetTxPayment] [pallet_balances, Balances] [pallet_message_queue, MessageQueue] @@ -1679,6 +1783,12 @@ impl_runtime_apis! { } } + impl pallet_asset_rewards::AssetRewards<Block, Balance> for Runtime { + fn pool_creation_cost() -> Balance { + StakePoolCreationDeposit::get() + } + } + impl cumulus_primitives_core::GetCoreSelectorApi<Block> for Runtime { fn core_selector() -> (CoreSelector, ClaimQueueOffset) { ParachainSystem::core_selector() @@ -2073,7 +2183,7 @@ impl_runtime_apis! { } } - impl pallet_revive::ReviveApi<Block, AccountId, Balance, Nonce, BlockNumber, EventRecord> for Runtime + impl pallet_revive::ReviveApi<Block, AccountId, Balance, Nonce, BlockNumber> for Runtime { fn balance(address: H160) -> U256 { Revive::evm_balance(&address) @@ -2108,7 +2218,7 @@ impl_runtime_apis! { gas_limit: Option<Weight>, storage_deposit_limit: Option<Balance>, input_data: Vec<u8>, - ) -> pallet_revive::ContractResult<pallet_revive::ExecReturnValue, Balance, EventRecord> { + ) -> pallet_revive::ContractResult<pallet_revive::ExecReturnValue, Balance> { let blockweights= <Runtime as frame_system::Config>::BlockWeights::get(); Revive::bare_call( RuntimeOrigin::signed(origin), @@ -2117,8 +2227,6 @@ impl_runtime_apis! { gas_limit.unwrap_or(blockweights.max_block), pallet_revive::DepositLimit::Balance(storage_deposit_limit.unwrap_or(u128::MAX)), input_data, - pallet_revive::DebugInfo::UnsafeDebug, - pallet_revive::CollectEvents::UnsafeCollect, ) } @@ -2130,7 +2238,7 @@ impl_runtime_apis! { code: pallet_revive::Code, data: Vec<u8>, salt: Option<[u8; 32]>, - ) -> pallet_revive::ContractResult<pallet_revive::InstantiateReturnValue, Balance, EventRecord> + ) -> pallet_revive::ContractResult<pallet_revive::InstantiateReturnValue, Balance> { let blockweights= <Runtime as frame_system::Config>::BlockWeights::get(); Revive::bare_instantiate( @@ -2141,8 +2249,6 @@ impl_runtime_apis! { code, data, salt, - pallet_revive::DebugInfo::UnsafeDebug, - pallet_revive::CollectEvents::UnsafeCollect, ) } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/mod.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/mod.rs index 442b58635f48ad5ae910cc3612d45cab512e4a51..d653838ad80e61d9dd96a705a1871fe4c4858fa8 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/mod.rs @@ -23,6 +23,7 @@ pub mod frame_system_extensions; pub mod pallet_asset_conversion; pub mod pallet_asset_conversion_ops; pub mod pallet_asset_conversion_tx_payment; +pub mod pallet_asset_rewards; pub mod pallet_assets_foreign; pub mod pallet_assets_local; pub mod pallet_assets_pool; diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_asset_rewards.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_asset_rewards.rs new file mode 100644 index 0000000000000000000000000000000000000000..3bbc289fec7b61433041c8007cc1d579a74ea7e5 --- /dev/null +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_asset_rewards.rs @@ -0,0 +1,217 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see <http://www.gnu.org/licenses/>. + +//! Autogenerated weights for `pallet_asset_rewards` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2025-01-14, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-ys-ssygq-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-westend-dev")`, DB CACHE: 1024 + +// Executed Command: +// target/production/polkadot-parachain +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_asset_rewards +// --chain=asset-hub-westend-dev +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_asset_rewards`. +pub struct WeightInfo<T>(PhantomData<T>); +impl<T: frame_system::Config> pallet_asset_rewards::WeightInfo for WeightInfo<T> { + /// Storage: `Assets::Asset` (r:2 w:0) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `AssetRewards::NextPoolId` (r:1 w:1) + /// Proof: `AssetRewards::NextPoolId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(157), added: 2632, mode: `MaxEncodedLen`) + /// Storage: `AssetRewards::PoolCost` (r:0 w:1) + /// Proof: `AssetRewards::PoolCost` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + /// Storage: `AssetRewards::Pools` (r:0 w:1) + /// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(1344), added: 3819, mode: `MaxEncodedLen`) + fn create_pool() -> Weight { + // Proof Size summary in bytes: + // Measured: `392` + // Estimated: `6360` + // Minimum execution time: 60_734_000 picoseconds. + Weight::from_parts(61_828_000, 0) + .saturating_add(Weight::from_parts(0, 6360)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(5)) + } + /// Storage: `AssetRewards::Pools` (r:1 w:1) + /// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(1344), added: 3819, mode: `MaxEncodedLen`) + /// Storage: `AssetRewards::PoolStakers` (r:1 w:1) + /// Proof: `AssetRewards::PoolStakers` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `AssetsFreezer::Freezes` (r:1 w:1) + /// Proof: `AssetsFreezer::Freezes` (`max_values`: None, `max_size`: Some(87), added: 2562, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1 w:0) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `AssetsFreezer::FrozenBalances` (r:1 w:1) + /// Proof: `AssetsFreezer::FrozenBalances` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + fn stake() -> Weight { + // Proof Size summary in bytes: + // Measured: `906` + // Estimated: `4809` + // Minimum execution time: 56_014_000 picoseconds. + Weight::from_parts(58_487_000, 0) + .saturating_add(Weight::from_parts(0, 4809)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(4)) + } + /// Storage: `AssetRewards::Pools` (r:1 w:1) + /// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(1344), added: 3819, mode: `MaxEncodedLen`) + /// Storage: `AssetRewards::PoolStakers` (r:1 w:1) + /// Proof: `AssetRewards::PoolStakers` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `AssetsFreezer::Freezes` (r:1 w:1) + /// Proof: `AssetsFreezer::Freezes` (`max_values`: None, `max_size`: Some(87), added: 2562, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1 w:0) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `AssetsFreezer::FrozenBalances` (r:1 w:1) + /// Proof: `AssetsFreezer::FrozenBalances` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + fn unstake() -> Weight { + // Proof Size summary in bytes: + // Measured: `906` + // Estimated: `4809` + // Minimum execution time: 59_071_000 picoseconds. + Weight::from_parts(60_631_000, 0) + .saturating_add(Weight::from_parts(0, 4809)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(4)) + } + /// Storage: `AssetRewards::Pools` (r:1 w:0) + /// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(1344), added: 3819, mode: `MaxEncodedLen`) + /// Storage: `AssetRewards::PoolStakers` (r:1 w:1) + /// Proof: `AssetRewards::PoolStakers` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:2 w:2) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `AssetsFreezer::FrozenBalances` (r:1 w:0) + /// Proof: `AssetsFreezer::FrozenBalances` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + fn harvest_rewards() -> Weight { + // Proof Size summary in bytes: + // Measured: `1106` + // Estimated: `6208` + // Minimum execution time: 80_585_000 picoseconds. + Weight::from_parts(82_186_000, 0) + .saturating_add(Weight::from_parts(0, 6208)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(4)) + } + /// Storage: `AssetRewards::Pools` (r:1 w:1) + /// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(1344), added: 3819, mode: `MaxEncodedLen`) + fn set_pool_reward_rate_per_block() -> Weight { + // Proof Size summary in bytes: + // Measured: `318` + // Estimated: `4809` + // Minimum execution time: 17_083_000 picoseconds. + Weight::from_parts(17_816_000, 0) + .saturating_add(Weight::from_parts(0, 4809)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `AssetRewards::Pools` (r:1 w:1) + /// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(1344), added: 3819, mode: `MaxEncodedLen`) + fn set_pool_admin() -> Weight { + // Proof Size summary in bytes: + // Measured: `318` + // Estimated: `4809` + // Minimum execution time: 15_269_000 picoseconds. + Weight::from_parts(15_881_000, 0) + .saturating_add(Weight::from_parts(0, 4809)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `AssetRewards::Pools` (r:1 w:1) + /// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(1344), added: 3819, mode: `MaxEncodedLen`) + fn set_pool_expiry_block() -> Weight { + // Proof Size summary in bytes: + // Measured: `318` + // Estimated: `4809` + // Minimum execution time: 17_482_000 picoseconds. + Weight::from_parts(18_124_000, 0) + .saturating_add(Weight::from_parts(0, 4809)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `AssetRewards::Pools` (r:1 w:0) + /// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(1344), added: 3819, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:2 w:2) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `AssetsFreezer::FrozenBalances` (r:1 w:0) + /// Proof: `AssetsFreezer::FrozenBalances` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn deposit_reward_tokens() -> Weight { + // Proof Size summary in bytes: + // Measured: `781` + // Estimated: `6208` + // Minimum execution time: 66_644_000 picoseconds. + Weight::from_parts(67_950_000, 0) + .saturating_add(Weight::from_parts(0, 6208)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(4)) + } + /// Storage: `AssetRewards::Pools` (r:1 w:1) + /// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(1344), added: 3819, mode: `MaxEncodedLen`) + /// Storage: `AssetRewards::PoolStakers` (r:1 w:0) + /// Proof: `AssetRewards::PoolStakers` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:2 w:2) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `AssetsFreezer::FrozenBalances` (r:1 w:1) + /// Proof: `AssetsFreezer::FrozenBalances` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `AssetRewards::PoolCost` (r:1 w:1) + /// Proof: `AssetRewards::PoolCost` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(157), added: 2632, mode: `MaxEncodedLen`) + /// Storage: `AssetsFreezer::Freezes` (r:0 w:1) + /// Proof: `AssetsFreezer::Freezes` (`max_values`: None, `max_size`: Some(87), added: 2562, mode: `MaxEncodedLen`) + fn cleanup_pool() -> Weight { + // Proof Size summary in bytes: + // Measured: `1139` + // Estimated: `6208` + // Minimum execution time: 124_136_000 picoseconds. + Weight::from_parts(128_642_000, 0) + .saturating_add(Weight::from_parts(0, 6208)) + .saturating_add(T::DbWeight::get().reads(10)) + .saturating_add(T::DbWeight::get().writes(10)) + } +} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs index b4e938f1f8b5709515a855302d1e26b03afb968e..1ea2ce5136abd09348e8843b48b7a356b00c57f0 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs @@ -65,6 +65,7 @@ use xcm_executor::XcmExecutor; parameter_types! { pub const RootLocation: Location = Location::here(); pub const WestendLocation: Location = Location::parent(); + pub const GovernanceLocation: Location = Location::parent(); pub const RelayNetwork: Option<NetworkId> = Some(NetworkId::ByGenesis(WESTEND_GENESIS_HASH)); pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorLocation = @@ -359,7 +360,7 @@ pub type TrustedTeleporters = ( /// asset and the asset required for fee payment. pub type PoolAssetsExchanger = SingleAssetExchangeAdapter< crate::AssetConversion, - crate::NativeAndAssets, + crate::NativeAndNonPoolAssets, ( TrustBackedAssetsAsLocation<TrustBackedAssetsPalletLocation, Balance, xcm::v5::Location>, ForeignAssetsConvertedConcreteId, @@ -409,7 +410,7 @@ impl xcm_executor::Config for XcmConfig { WestendLocation, crate::AssetConversion, WeightToFee, - crate::NativeAndAssets, + crate::NativeAndNonPoolAssets, ( TrustBackedAssetsAsLocation< TrustBackedAssetsPalletLocation, @@ -418,7 +419,7 @@ impl xcm_executor::Config for XcmConfig { >, ForeignAssetsConvertedConcreteId, ), - ResolveAssetTo<StakingPot, crate::NativeAndAssets>, + ResolveAssetTo<StakingPot, crate::NativeAndNonPoolAssets>, AccountId, >, // This trader allows to pay with `is_sufficient=true` "Trust Backed" assets from dedicated diff --git a/cumulus/parachains/runtimes/assets/common/src/lib.rs b/cumulus/parachains/runtimes/assets/common/src/lib.rs index 25c2df6b68d16c9eb282b564e8b7a4acf23f9e3a..50b1b63146bc863945aa9602797db4478d08bd53 100644 --- a/cumulus/parachains/runtimes/assets/common/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/common/src/lib.rs @@ -123,10 +123,11 @@ pub type ForeignAssetsConvertedConcreteId< BalanceConverter, >; -type AssetIdForPoolAssets = u32; +pub type AssetIdForPoolAssets = u32; + /// `Location` vs `AssetIdForPoolAssets` converter for `PoolAssets`. -pub type AssetIdForPoolAssetsConvert<PoolAssetsPalletLocation> = - AsPrefixedGeneralIndex<PoolAssetsPalletLocation, AssetIdForPoolAssets, TryConvertInto>; +pub type AssetIdForPoolAssetsConvert<PoolAssetsPalletLocation, L = Location> = + AsPrefixedGeneralIndex<PoolAssetsPalletLocation, AssetIdForPoolAssets, TryConvertInto, L>; /// [`MatchedConvertedConcreteId`] converter dedicated for `PoolAssets` pub type PoolAssetsConvertedConcreteId<PoolAssetsPalletLocation, Balance> = MatchedConvertedConcreteId< diff --git a/docs/sdk/packages/guides/first-pallet/Cargo.toml b/docs/sdk/packages/guides/first-pallet/Cargo.toml index a1411580119da456e389715c0f47bc2df8691c8f..e6325c31781a6571055983607879f0c920a13f0f 100644 --- a/docs/sdk/packages/guides/first-pallet/Cargo.toml +++ b/docs/sdk/packages/guides/first-pallet/Cargo.toml @@ -18,7 +18,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { workspace = true } docify = { workspace = true } -frame = { workspace = true, features = ["experimental", "runtime"] } +frame = { workspace = true, features = ["runtime"] } scale-info = { workspace = true } [features] diff --git a/docs/sdk/packages/guides/first-runtime/Cargo.toml b/docs/sdk/packages/guides/first-runtime/Cargo.toml index 303d5c5e7f5fc8a11c52d48d0105f4872e77bceb..8ed17dea1b71ec90295a2a75d6fa064e289ed6ca 100644 --- a/docs/sdk/packages/guides/first-runtime/Cargo.toml +++ b/docs/sdk/packages/guides/first-runtime/Cargo.toml @@ -18,7 +18,7 @@ scale-info = { workspace = true } serde_json = { workspace = true } # this is a frame-based runtime, thus importing `frame` with runtime feature enabled. -frame = { workspace = true, features = ["experimental", "runtime"] } +frame = { workspace = true, features = ["runtime"] } # pallets that we want to use pallet-balances = { workspace = true } diff --git a/polkadot/runtime/rococo/src/xcm_config.rs b/polkadot/runtime/rococo/src/xcm_config.rs index bb77ec0000e56888f2bb5068a4dfba919bc527e0..10c3f6c0cbfcf2a25efc02524e46f6c4d256674d 100644 --- a/polkadot/runtime/rococo/src/xcm_config.rs +++ b/polkadot/runtime/rococo/src/xcm_config.rs @@ -18,7 +18,8 @@ use super::{ parachains_origin, AccountId, AllPalletsWithSystem, Balances, Dmp, Fellows, ParaId, Runtime, - RuntimeCall, RuntimeEvent, RuntimeOrigin, TransactionByteFee, Treasury, WeightToFee, XcmPallet, + RuntimeCall, RuntimeEvent, RuntimeOrigin, TransactionByteFee, Treasurer, Treasury, WeightToFee, + XcmPallet, }; use crate::governance::StakingAdmin; @@ -228,11 +229,14 @@ impl xcm_executor::Config for XcmConfig { } parameter_types! { + /// Collective pluralistic body. pub const CollectiveBodyId: BodyId = BodyId::Unit; - // StakingAdmin pluralistic body. + /// StakingAdmin pluralistic body. pub const StakingAdminBodyId: BodyId = BodyId::Defense; - // Fellows pluralistic body. + /// Fellows pluralistic body. pub const FellowsBodyId: BodyId = BodyId::Technical; + /// Treasury pluralistic body. + pub const TreasuryBodyId: BodyId = BodyId::Treasury; } /// Type to convert an `Origin` type value into a `Location` value which represents an interior @@ -249,6 +253,9 @@ pub type StakingAdminToPlurality = /// Type to convert the Fellows origin to a Plurality `Location` value. pub type FellowsToPlurality = OriginToPluralityVoice<RuntimeOrigin, Fellows, FellowsBodyId>; +/// Type to convert the Treasury origin to a Plurality `Location` value. +pub type TreasurerToPlurality = OriginToPluralityVoice<RuntimeOrigin, Treasurer, TreasuryBodyId>; + /// Type to convert a pallet `Origin` type value into a `Location` value which represents an /// interior location of this chain for a destination chain. pub type LocalPalletOriginToLocation = ( @@ -256,13 +263,18 @@ pub type LocalPalletOriginToLocation = ( StakingAdminToPlurality, // Fellows origin to be used in XCM as a corresponding Plurality `Location` value. FellowsToPlurality, + // Treasurer origin to be used in XCM as a corresponding Plurality `Location` value. + TreasurerToPlurality, ); impl pallet_xcm::Config for Runtime { type RuntimeEvent = RuntimeEvent; // Note that this configuration of `SendXcmOrigin` is different from the one present in // production. - type SendXcmOrigin = xcm_builder::EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>; + type SendXcmOrigin = xcm_builder::EnsureXcmOrigin< + RuntimeOrigin, + (LocalPalletOriginToLocation, LocalOriginToLocation), + >; type XcmRouter = XcmRouter; // Anyone can execute XCM messages locally. type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>; diff --git a/polkadot/runtime/test-runtime/src/lib.rs b/polkadot/runtime/test-runtime/src/lib.rs index 4f9ba8d8508cdecc904efefcde280e386fddc1d3..cdf6fa92da2f5d5f09c6f48b21e2e4f13035d4ef 100644 --- a/polkadot/runtime/test-runtime/src/lib.rs +++ b/polkadot/runtime/test-runtime/src/lib.rs @@ -366,11 +366,13 @@ impl onchain::Config for OnChainSeqPhragmen { const MAX_QUOTA_NOMINATIONS: u32 = 16; impl pallet_staking::Config for Runtime { + type OldCurrency = Balances; type Currency = Balances; type CurrencyBalance = Balance; type UnixTime = Timestamp; type CurrencyToVote = polkadot_runtime_common::CurrencyToVote; type RewardRemainder = (); + type RuntimeHoldReason = RuntimeHoldReason; type RuntimeEvent = RuntimeEvent; type Slash = (); type Reward = (); diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 58d2bdcb7c7db76e8625aff189a18acbde44b60e..a9ba0778fe0eff7b4801df2b045f038752390113 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -728,8 +728,10 @@ parameter_types! { } impl pallet_staking::Config for Runtime { + type OldCurrency = Balances; type Currency = Balances; type CurrencyBalance = Balance; + type RuntimeHoldReason = RuntimeHoldReason; type UnixTime = Timestamp; type CurrencyToVote = CurrencyToVote; type RewardRemainder = (); @@ -1085,6 +1087,7 @@ pub enum ProxyType { CancelProxy, Auction, NominationPools, + ParaRegistration, } impl Default for ProxyType { fn default() -> Self { @@ -1181,6 +1184,15 @@ impl InstanceFilter<RuntimeCall> for ProxyType { RuntimeCall::Registrar(..) | RuntimeCall::Slots(..) ), + ProxyType::ParaRegistration => matches!( + c, + RuntimeCall::Registrar(paras_registrar::Call::reserve { .. }) | + RuntimeCall::Registrar(paras_registrar::Call::register { .. }) | + RuntimeCall::Utility(pallet_utility::Call::batch { .. }) | + RuntimeCall::Utility(pallet_utility::Call::batch_all { .. }) | + RuntimeCall::Utility(pallet_utility::Call::force_batch { .. }) | + RuntimeCall::Proxy(pallet_proxy::Call::remove_proxy { .. }) + ), } } fn is_superset(&self, o: &Self) -> bool { diff --git a/polkadot/runtime/westend/src/tests.rs b/polkadot/runtime/westend/src/tests.rs index fcdaf7ff2de6d3c95207291bd6b2f47043877860..65b81cc00f06975e7be235cb7d7700824b356853 100644 --- a/polkadot/runtime/westend/src/tests.rs +++ b/polkadot/runtime/westend/src/tests.rs @@ -155,25 +155,27 @@ mod remote_tests { let transport: Transport = var("WS").unwrap_or("ws://127.0.0.1:9900".to_string()).into(); let maybe_state_snapshot: Option<SnapshotConfig> = var("SNAP").map(|s| s.into()).ok(); + let online_config = OnlineConfig { + transport, + state_snapshot: maybe_state_snapshot.clone(), + child_trie: false, + pallets: vec![ + "Staking".into(), + "System".into(), + "Balances".into(), + "NominationPools".into(), + "DelegatedStaking".into(), + ], + ..Default::default() + }; let mut ext = Builder::<Block>::default() .mode(if let Some(state_snapshot) = maybe_state_snapshot { Mode::OfflineOrElseOnline( OfflineConfig { state_snapshot: state_snapshot.clone() }, - OnlineConfig { - transport, - state_snapshot: Some(state_snapshot), - pallets: vec![ - "staking".into(), - "system".into(), - "balances".into(), - "nomination-pools".into(), - "delegated-staking".into(), - ], - ..Default::default() - }, + online_config, ) } else { - Mode::Online(OnlineConfig { transport, ..Default::default() }) + Mode::Online(online_config) }) .build() .await @@ -241,6 +243,77 @@ mod remote_tests { ); }); } + + #[tokio::test] + async fn staking_curr_fun_migrate() { + // Intended to be run only manually. + if var("RUN_MIGRATION_TESTS").is_err() { + return; + } + sp_tracing::try_init_simple(); + + let transport: Transport = var("WS").unwrap_or("ws://127.0.0.1:9944".to_string()).into(); + let maybe_state_snapshot: Option<SnapshotConfig> = var("SNAP").map(|s| s.into()).ok(); + let online_config = OnlineConfig { + transport, + state_snapshot: maybe_state_snapshot.clone(), + child_trie: false, + pallets: vec!["Staking".into(), "System".into(), "Balances".into()], + ..Default::default() + }; + let mut ext = Builder::<Block>::default() + .mode(if let Some(state_snapshot) = maybe_state_snapshot { + Mode::OfflineOrElseOnline( + OfflineConfig { state_snapshot: state_snapshot.clone() }, + online_config, + ) + } else { + Mode::Online(online_config) + }) + .build() + .await + .unwrap(); + ext.execute_with(|| { + // create an account with some balance + let alice = AccountId::from([1u8; 32]); + use frame_support::traits::Currency; + let _ = Balances::deposit_creating(&alice, 100_000 * UNITS); + + let mut success = 0; + let mut err = 0; + let mut force_withdraw_acc = 0; + // iterate over all pools + pallet_staking::Ledger::<Runtime>::iter().for_each(|(ctrl, ledger)| { + match pallet_staking::Pallet::<Runtime>::migrate_currency( + RuntimeOrigin::signed(alice.clone()).into(), + ledger.stash.clone(), + ) { + Ok(_) => { + let updated_ledger = + pallet_staking::Ledger::<Runtime>::get(&ctrl).expect("ledger exists"); + let force_withdraw = ledger.total - updated_ledger.total; + if force_withdraw > 0 { + force_withdraw_acc += force_withdraw; + log::info!(target: "remote_test", "Force withdraw from stash {:?}: value {:?}", ledger.stash, force_withdraw); + } + success += 1; + }, + Err(e) => { + log::error!(target: "remote_test", "Error migrating {:?}: {:?}", ledger.stash, e); + err += 1; + }, + } + }); + + log::info!( + target: "remote_test", + "Migration stats: success: {}, err: {}, total force withdrawn stake: {}", + success, + err, + force_withdraw_acc + ); + }); + } } #[test] diff --git a/polkadot/runtime/westend/src/weights/pallet_staking.rs b/polkadot/runtime/westend/src/weights/pallet_staking.rs index 393fa0b37176a0ca5ce8160eec7e8e3ac56c3458..f1e7f5ba1576eafbd768f51cf98faa41cbe02b5c 100644 --- a/polkadot/runtime/westend/src/weights/pallet_staking.rs +++ b/polkadot/runtime/westend/src/weights/pallet_staking.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_staking` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-03-27, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-09-17, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-obbyq9g6-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("westend-dev")`, DB CACHE: 1024 // Executed Command: @@ -52,19 +52,19 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> { /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::Ledger` (r:1 w:1) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`) + /// Storage: `Staking::VirtualStakers` (r:1 w:0) + /// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) /// Storage: `Staking::Payee` (r:0 w:1) /// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) fn bond() -> Weight { // Proof Size summary in bytes: - // Measured: `1009` - // Estimated: `4764` - // Minimum execution time: 40_585_000 picoseconds. - Weight::from_parts(41_800_000, 0) - .saturating_add(Weight::from_parts(0, 4764)) + // Measured: `1035` + // Estimated: `4556` + // Minimum execution time: 70_147_000 picoseconds. + Weight::from_parts(71_795_000, 0) + .saturating_add(Weight::from_parts(0, 4556)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -72,20 +72,20 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> { /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::Ledger` (r:1 w:1) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `Staking::VirtualStakers` (r:1 w:0) + /// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`) /// Storage: `VoterList::ListNodes` (r:3 w:3) /// Proof: `VoterList::ListNodes` (`max_values`: None, `max_size`: Some(154), added: 2629, mode: `MaxEncodedLen`) /// Storage: `VoterList::ListBags` (r:2 w:2) /// Proof: `VoterList::ListBags` (`max_values`: None, `max_size`: Some(82), added: 2557, mode: `MaxEncodedLen`) fn bond_extra() -> Weight { // Proof Size summary in bytes: - // Measured: `1921` + // Measured: `1947` // Estimated: `8877` - // Minimum execution time: 81_809_000 picoseconds. - Weight::from_parts(84_387_000, 0) + // Minimum execution time: 125_203_000 picoseconds. + Weight::from_parts(128_088_000, 0) .saturating_add(Weight::from_parts(0, 8877)) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(7)) @@ -100,23 +100,23 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> { /// Proof: `Staking::MinNominatorBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Staking::CurrentEra` (r:1 w:0) /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `Staking::VirtualStakers` (r:1 w:0) + /// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:0) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`) /// Storage: `VoterList::ListNodes` (r:3 w:3) /// Proof: `VoterList::ListNodes` (`max_values`: None, `max_size`: Some(154), added: 2629, mode: `MaxEncodedLen`) /// Storage: `VoterList::ListBags` (r:2 w:2) /// Proof: `VoterList::ListBags` (`max_values`: None, `max_size`: Some(82), added: 2557, mode: `MaxEncodedLen`) fn unbond() -> Weight { // Proof Size summary in bytes: - // Measured: `2128` + // Measured: `2051` // Estimated: `8877` - // Minimum execution time: 89_419_000 picoseconds. - Weight::from_parts(91_237_000, 0) + // Minimum execution time: 101_991_000 picoseconds. + Weight::from_parts(104_567_000, 0) .saturating_add(Weight::from_parts(0, 8877)) .saturating_add(T::DbWeight::get().reads(12)) - .saturating_add(T::DbWeight::get().writes(7)) + .saturating_add(T::DbWeight::get().writes(6)) } /// Storage: `Staking::Ledger` (r:1 w:1) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) @@ -124,23 +124,25 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> { /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::CurrentEra` (r:1 w:0) /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `Staking::VirtualStakers` (r:1 w:0) + /// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`) /// Storage: `NominationPools::ReversePoolIdLookup` (r:1 w:0) /// Proof: `NominationPools::ReversePoolIdLookup` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + /// Storage: `DelegatedStaking::Agents` (r:1 w:0) + /// Proof: `DelegatedStaking::Agents` (`max_values`: None, `max_size`: Some(120), added: 2595, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1223` - // Estimated: `4764` - // Minimum execution time: 45_152_000 picoseconds. - Weight::from_parts(46_460_819, 0) - .saturating_add(Weight::from_parts(0, 4764)) - // Standard Error: 972 - .saturating_add(Weight::from_parts(55_473, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(6)) + // Measured: `1253` + // Estimated: `4556` + // Minimum execution time: 76_450_000 picoseconds. + Weight::from_parts(78_836_594, 0) + .saturating_add(Weight::from_parts(0, 4556)) + // Standard Error: 1_529 + .saturating_add(Weight::from_parts(66_662, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(2)) } /// Storage: `Staking::Ledger` (r:1 w:1) @@ -151,10 +153,10 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> { /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `Staking::SlashingSpans` (r:1 w:1) /// Proof: `Staking::SlashingSpans` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `Staking::VirtualStakers` (r:1 w:1) + /// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`) /// Storage: `Staking::Validators` (r:1 w:0) /// Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) /// Storage: `Staking::Nominators` (r:1 w:1) @@ -174,15 +176,15 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> { /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_kill(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2127 + s * (4 ±0)` + // Measured: `2153 + s * (4 ±0)` // Estimated: `6248 + s * (4 ±0)` - // Minimum execution time: 82_762_000 picoseconds. - Weight::from_parts(91_035_077, 0) + // Minimum execution time: 121_962_000 picoseconds. + Weight::from_parts(131_000_151, 0) .saturating_add(Weight::from_parts(0, 6248)) - // Standard Error: 3_771 - .saturating_add(Weight::from_parts(1_217_871, 0).saturating_mul(s.into())) + // Standard Error: 3_846 + .saturating_add(Weight::from_parts(1_277_843, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(13)) - .saturating_add(T::DbWeight::get().writes(11)) + .saturating_add(T::DbWeight::get().writes(12)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) .saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into())) } @@ -210,10 +212,10 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> { /// Proof: `Staking::CounterForValidators` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn validate() -> Weight { // Proof Size summary in bytes: - // Measured: `1301` + // Measured: `1334` // Estimated: `4556` - // Minimum execution time: 50_555_000 picoseconds. - Weight::from_parts(52_052_000, 0) + // Minimum execution time: 66_450_000 picoseconds. + Weight::from_parts(68_302_000, 0) .saturating_add(Weight::from_parts(0, 4556)) .saturating_add(T::DbWeight::get().reads(11)) .saturating_add(T::DbWeight::get().writes(5)) @@ -227,13 +229,13 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> { /// The range of component `k` is `[1, 128]`. fn kick(k: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1778 + k * (572 ±0)` + // Measured: `1811 + k * (572 ±0)` // Estimated: `4556 + k * (3033 ±0)` - // Minimum execution time: 35_037_000 picoseconds. - Weight::from_parts(35_081_878, 0) + // Minimum execution time: 43_875_000 picoseconds. + Weight::from_parts(47_332_240, 0) .saturating_add(Weight::from_parts(0, 4556)) - // Standard Error: 5_473 - .saturating_add(Weight::from_parts(6_667_924, 0).saturating_mul(k.into())) + // Standard Error: 6_530 + .saturating_add(Weight::from_parts(7_398_001, 0).saturating_mul(k.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(k.into()))) @@ -264,13 +266,13 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> { /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1797 + n * (102 ±0)` + // Measured: `1830 + n * (102 ±0)` // Estimated: `6248 + n * (2520 ±0)` - // Minimum execution time: 62_098_000 picoseconds. - Weight::from_parts(60_154_061, 0) + // Minimum execution time: 80_640_000 picoseconds. + Weight::from_parts(78_801_092, 0) .saturating_add(Weight::from_parts(0, 6248)) - // Standard Error: 19_257 - .saturating_add(Weight::from_parts(3_839_855, 0).saturating_mul(n.into())) + // Standard Error: 22_249 + .saturating_add(Weight::from_parts(4_996_344, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(6)) @@ -294,10 +296,10 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> { /// Proof: `VoterList::CounterForListNodes` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn chill() -> Weight { // Proof Size summary in bytes: - // Measured: `1747` + // Measured: `1780` // Estimated: `6248` - // Minimum execution time: 54_993_000 picoseconds. - Weight::from_parts(56_698_000, 0) + // Minimum execution time: 71_494_000 picoseconds. + Weight::from_parts(73_487_000, 0) .saturating_add(Weight::from_parts(0, 6248)) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(6)) @@ -310,10 +312,10 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> { /// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) fn set_payee() -> Weight { // Proof Size summary in bytes: - // Measured: `865` + // Measured: `898` // Estimated: `4556` - // Minimum execution time: 18_100_000 picoseconds. - Weight::from_parts(18_547_000, 0) + // Minimum execution time: 24_310_000 picoseconds. + Weight::from_parts(24_676_000, 0) .saturating_add(Weight::from_parts(0, 4556)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) @@ -326,10 +328,10 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> { /// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) fn update_payee() -> Weight { // Proof Size summary in bytes: - // Measured: `932` + // Measured: `965` // Estimated: `4556` - // Minimum execution time: 23_428_000 picoseconds. - Weight::from_parts(24_080_000, 0) + // Minimum execution time: 31_348_000 picoseconds. + Weight::from_parts(32_384_000, 0) .saturating_add(Weight::from_parts(0, 4556)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -340,10 +342,10 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> { /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) fn set_controller() -> Weight { // Proof Size summary in bytes: - // Measured: `865` + // Measured: `898` // Estimated: `8122` - // Minimum execution time: 21_159_000 picoseconds. - Weight::from_parts(21_706_000, 0) + // Minimum execution time: 27_537_000 picoseconds. + Weight::from_parts(28_714_000, 0) .saturating_add(Weight::from_parts(0, 8122)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) @@ -354,8 +356,8 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_910_000 picoseconds. - Weight::from_parts(2_003_000, 0) + // Minimum execution time: 2_362_000 picoseconds. + Weight::from_parts(2_518_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -365,8 +367,8 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_076_000 picoseconds. - Weight::from_parts(7_349_000, 0) + // Minimum execution time: 7_752_000 picoseconds. + Weight::from_parts(8_105_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -376,8 +378,8 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_067_000 picoseconds. - Weight::from_parts(7_389_000, 0) + // Minimum execution time: 7_868_000 picoseconds. + Weight::from_parts(8_175_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -387,8 +389,8 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_148_000 picoseconds. - Weight::from_parts(7_446_000, 0) + // Minimum execution time: 7_945_000 picoseconds. + Weight::from_parts(8_203_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -399,11 +401,11 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_025_000 picoseconds. - Weight::from_parts(2_229_953, 0) + // Minimum execution time: 2_458_000 picoseconds. + Weight::from_parts(2_815_664, 0) .saturating_add(Weight::from_parts(0, 0)) // Standard Error: 67 - .saturating_add(Weight::from_parts(11_785, 0).saturating_mul(v.into())) + .saturating_add(Weight::from_parts(12_287, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `Staking::Ledger` (r:1502 w:1502) @@ -415,13 +417,13 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> { /// The range of component `i` is `[0, 751]`. fn deprecate_controller_batch(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `680 + i * (227 ±0)` + // Measured: `713 + i * (227 ±0)` // Estimated: `990 + i * (7132 ±0)` - // Minimum execution time: 4_321_000 picoseconds. - Weight::from_parts(4_407_000, 0) + // Minimum execution time: 4_976_000 picoseconds. + Weight::from_parts(5_102_000, 0) .saturating_add(Weight::from_parts(0, 990)) - // Standard Error: 37_239 - .saturating_add(Weight::from_parts(21_300_598, 0).saturating_mul(i.into())) + // Standard Error: 36_458 + .saturating_add(Weight::from_parts(25_359_275, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(i.into()))) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(i.into()))) .saturating_add(Weight::from_parts(0, 7132).saturating_mul(i.into())) @@ -432,10 +434,10 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> { /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::Ledger` (r:1 w:1) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `Staking::VirtualStakers` (r:1 w:1) + /// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `Staking::Validators` (r:1 w:0) @@ -457,15 +459,15 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> { /// The range of component `s` is `[0, 100]`. fn force_unstake(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2127 + s * (4 ±0)` + // Measured: `2153 + s * (4 ±0)` // Estimated: `6248 + s * (4 ±0)` - // Minimum execution time: 78_908_000 picoseconds. - Weight::from_parts(84_886_373, 0) + // Minimum execution time: 116_776_000 picoseconds. + Weight::from_parts(125_460_389, 0) .saturating_add(Weight::from_parts(0, 6248)) - // Standard Error: 3_376 - .saturating_add(Weight::from_parts(1_217_850, 0).saturating_mul(s.into())) + // Standard Error: 3_095 + .saturating_add(Weight::from_parts(1_300_502, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(13)) - .saturating_add(T::DbWeight::get().writes(12)) + .saturating_add(T::DbWeight::get().writes(13)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) .saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into())) } @@ -474,13 +476,13 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> { /// The range of component `s` is `[1, 1000]`. fn cancel_deferred_slash(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `66639` - // Estimated: `70104` - // Minimum execution time: 136_389_000 picoseconds. - Weight::from_parts(1_207_241_524, 0) - .saturating_add(Weight::from_parts(0, 70104)) - // Standard Error: 77_138 - .saturating_add(Weight::from_parts(6_443_948, 0).saturating_mul(s.into())) + // Measured: `66672` + // Estimated: `70137` + // Minimum execution time: 135_135_000 picoseconds. + Weight::from_parts(937_565_332, 0) + .saturating_add(Weight::from_parts(0, 70137)) + // Standard Error: 57_675 + .saturating_add(Weight::from_parts(4_828_080, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -498,12 +500,10 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> { /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `Staking::ErasValidatorReward` (r:1 w:0) /// Proof: `Staking::ErasValidatorReward` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) - /// Storage: `Balances::Locks` (r:65 w:65) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:65 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:65 w:65) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Staking::VirtualStakers` (r:65 w:0) + /// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:65 w:65) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`) /// Storage: `Staking::ErasStakersPaged` (r:1 w:0) /// Proof: `Staking::ErasStakersPaged` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Staking::ErasRewardPoints` (r:1 w:0) @@ -512,30 +512,32 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> { /// Proof: `Staking::ErasValidatorPrefs` (`max_values`: None, `max_size`: Some(57), added: 2532, mode: `MaxEncodedLen`) /// Storage: `Staking::Payee` (r:65 w:0) /// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:65 w:65) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 64]`. fn payout_stakers_alive_staked(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `8249 + n * (396 ±0)` - // Estimated: `10779 + n * (3774 ±0)` - // Minimum execution time: 130_222_000 picoseconds. - Weight::from_parts(167_236_150, 0) - .saturating_add(Weight::from_parts(0, 10779)) - // Standard Error: 34_051 - .saturating_add(Weight::from_parts(39_899_917, 0).saturating_mul(n.into())) + // Measured: `8275 + n * (389 ±0)` + // Estimated: `10805 + n * (3566 ±0)` + // Minimum execution time: 180_144_000 picoseconds. + Weight::from_parts(237_134_733, 0) + .saturating_add(Weight::from_parts(0, 10805)) + // Standard Error: 52_498 + .saturating_add(Weight::from_parts(73_633_326, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(14)) .saturating_add(T::DbWeight::get().reads((6_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(4)) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 3774).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 3566).saturating_mul(n.into())) } /// Storage: `Staking::Ledger` (r:1 w:1) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) /// Storage: `Staking::Bonded` (r:1 w:0) /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `Staking::VirtualStakers` (r:1 w:0) + /// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:0) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`) /// Storage: `VoterList::ListNodes` (r:3 w:3) /// Proof: `VoterList::ListNodes` (`max_values`: None, `max_size`: Some(154), added: 2629, mode: `MaxEncodedLen`) /// Storage: `VoterList::ListBags` (r:2 w:2) @@ -543,26 +545,26 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> { /// The range of component `l` is `[1, 32]`. fn rebond(l: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1922 + l * (5 ±0)` + // Measured: `1845 + l * (5 ±0)` // Estimated: `8877` - // Minimum execution time: 79_136_000 picoseconds. - Weight::from_parts(82_129_497, 0) + // Minimum execution time: 89_307_000 picoseconds. + Weight::from_parts(92_902_634, 0) .saturating_add(Weight::from_parts(0, 8877)) - // Standard Error: 3_867 - .saturating_add(Weight::from_parts(75_156, 0).saturating_mul(l.into())) + // Standard Error: 4_446 + .saturating_add(Weight::from_parts(73_546, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(9)) - .saturating_add(T::DbWeight::get().writes(7)) + .saturating_add(T::DbWeight::get().writes(6)) } + /// Storage: `Staking::VirtualStakers` (r:1 w:1) + /// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) /// Storage: `Staking::Bonded` (r:1 w:1) /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::Ledger` (r:1 w:1) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) /// Storage: `Staking::SlashingSpans` (r:1 w:1) /// Proof: `Staking::SlashingSpans` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`) /// Storage: `Staking::Validators` (r:1 w:0) /// Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) /// Storage: `Staking::Nominators` (r:1 w:1) @@ -582,15 +584,15 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> { /// The range of component `s` is `[1, 100]`. fn reap_stash(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2127 + s * (4 ±0)` + // Measured: `2153 + s * (4 ±0)` // Estimated: `6248 + s * (4 ±0)` - // Minimum execution time: 89_375_000 picoseconds. - Weight::from_parts(91_224_907, 0) + // Minimum execution time: 130_544_000 picoseconds. + Weight::from_parts(133_260_598, 0) .saturating_add(Weight::from_parts(0, 6248)) - // Standard Error: 3_424 - .saturating_add(Weight::from_parts(1_219_542, 0).saturating_mul(s.into())) + // Standard Error: 3_545 + .saturating_add(Weight::from_parts(1_313_348, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(12)) - .saturating_add(T::DbWeight::get().writes(11)) + .saturating_add(T::DbWeight::get().writes(12)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) .saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into())) } @@ -633,14 +635,14 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> { fn new_era(v: u32, n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + n * (716 ±0) + v * (3594 ±0)` - // Estimated: `456136 + n * (3566 ±4) + v * (3566 ±0)` - // Minimum execution time: 520_905_000 picoseconds. - Weight::from_parts(523_771_000, 0) + // Estimated: `456136 + n * (3566 ±4) + v * (3566 ±40)` + // Minimum execution time: 654_756_000 picoseconds. + Weight::from_parts(658_861_000, 0) .saturating_add(Weight::from_parts(0, 456136)) - // Standard Error: 2_142_714 - .saturating_add(Weight::from_parts(68_631_588, 0).saturating_mul(v.into())) - // Standard Error: 213_509 - .saturating_add(Weight::from_parts(19_343_025, 0).saturating_mul(n.into())) + // Standard Error: 2_078_102 + .saturating_add(Weight::from_parts(67_775_668, 0).saturating_mul(v.into())) + // Standard Error: 207_071 + .saturating_add(Weight::from_parts(22_624_711, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(184)) .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(n.into()))) @@ -669,15 +671,15 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> { /// The range of component `n` is `[500, 1000]`. fn get_npos_voters(v: u32, n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `3108 + n * (907 ±0) + v * (391 ±0)` + // Measured: `3141 + n * (907 ±0) + v * (391 ±0)` // Estimated: `456136 + n * (3566 ±0) + v * (3566 ±0)` - // Minimum execution time: 36_848_619_000 picoseconds. - Weight::from_parts(37_362_442_000, 0) + // Minimum execution time: 42_790_195_000 picoseconds. + Weight::from_parts(42_954_437_000, 0) .saturating_add(Weight::from_parts(0, 456136)) - // Standard Error: 415_031 - .saturating_add(Weight::from_parts(5_204_987, 0).saturating_mul(v.into())) - // Standard Error: 415_031 - .saturating_add(Weight::from_parts(4_132_636, 0).saturating_mul(n.into())) + // Standard Error: 478_107 + .saturating_add(Weight::from_parts(6_744_044, 0).saturating_mul(v.into())) + // Standard Error: 478_107 + .saturating_add(Weight::from_parts(4_837_739, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(179)) .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(n.into()))) @@ -692,13 +694,13 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> { /// The range of component `v` is `[500, 1000]`. fn get_npos_targets(v: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `946 + v * (50 ±0)` + // Measured: `979 + v * (50 ±0)` // Estimated: `3510 + v * (2520 ±0)` - // Minimum execution time: 2_512_817_000 picoseconds. - Weight::from_parts(119_401_374, 0) + // Minimum execution time: 2_851_801_000 picoseconds. + Weight::from_parts(4_477_533, 0) .saturating_add(Weight::from_parts(0, 3510)) - // Standard Error: 8_463 - .saturating_add(Weight::from_parts(4_860_364, 0).saturating_mul(v.into())) + // Standard Error: 8_644 + .saturating_add(Weight::from_parts(5_811_682, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(v.into()))) .saturating_add(Weight::from_parts(0, 2520).saturating_mul(v.into())) @@ -721,8 +723,8 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_686_000 picoseconds. - Weight::from_parts(3_881_000, 0) + // Minimum execution time: 4_250_000 picoseconds. + Weight::from_parts(4_472_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(7)) } @@ -744,8 +746,8 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_143_000 picoseconds. - Weight::from_parts(3_424_000, 0) + // Minimum execution time: 3_986_000 picoseconds. + Weight::from_parts(4_144_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(7)) } @@ -773,10 +775,10 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> { /// Proof: `VoterList::CounterForListNodes` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn chill_other() -> Weight { // Proof Size summary in bytes: - // Measured: `1870` + // Measured: `1903` // Estimated: `6248` - // Minimum execution time: 66_946_000 picoseconds. - Weight::from_parts(69_382_000, 0) + // Minimum execution time: 87_291_000 picoseconds. + Weight::from_parts(89_344_000, 0) .saturating_add(Weight::from_parts(0, 6248)) .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().writes(6)) @@ -787,10 +789,10 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> { /// Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) fn force_apply_min_commission() -> Weight { // Proof Size summary in bytes: - // Measured: `658` + // Measured: `691` // Estimated: `3510` - // Minimum execution time: 11_278_000 picoseconds. - Weight::from_parts(11_603_000, 0) + // Minimum execution time: 16_113_000 picoseconds. + Weight::from_parts(16_593_000, 0) .saturating_add(Weight::from_parts(0, 3510)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) @@ -801,29 +803,53 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_963_000 picoseconds. - Weight::from_parts(2_077_000, 0) + // Minimum execution time: 2_433_000 picoseconds. + Weight::from_parts(2_561_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) + /// Storage: `Staking::VirtualStakers` (r:1 w:0) + /// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:0) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:0) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) /// Storage: `Staking::Bonded` (r:1 w:1) /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::Ledger` (r:1 w:1) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + fn restore_ledger() -> Weight { + // Proof Size summary in bytes: + // Measured: `1040` + // Estimated: `4764` + // Minimum execution time: 50_167_000 picoseconds. + Weight::from_parts(51_108_000, 0) + .saturating_add(Weight::from_parts(0, 4764)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Staking::VirtualStakers` (r:1 w:0) + /// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Staking::Bonded` (r:1 w:0) + /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `Staking::Ledger` (r:1 w:0) + /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`) /// Storage: `Balances::Freezes` (r:1 w:0) /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) - fn restore_ledger() -> Weight { + fn migrate_currency() -> Weight { // Proof Size summary in bytes: - // Measured: `1014` + // Measured: `1209` // Estimated: `4764` - // Minimum execution time: 40_258_000 picoseconds. - Weight::from_parts(41_210_000, 0) + // Minimum execution time: 91_790_000 picoseconds. + Weight::from_parts(92_991_000, 0) .saturating_add(Weight::from_parts(0, 4764)) - .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(4)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(2)) } } diff --git a/polkadot/runtime/westend/src/xcm_config.rs b/polkadot/runtime/westend/src/xcm_config.rs index 3f6a7304c8a94b8b502cb19f0016e57b69ddee2d..4235edf82b24de71f82c21dda1bd69c231048ac9 100644 --- a/polkadot/runtime/westend/src/xcm_config.rs +++ b/polkadot/runtime/westend/src/xcm_config.rs @@ -280,7 +280,10 @@ impl pallet_xcm::Config for Runtime { type RuntimeEvent = RuntimeEvent; // Note that this configuration of `SendXcmOrigin` is different from the one present in // production. - type SendXcmOrigin = xcm_builder::EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>; + type SendXcmOrigin = xcm_builder::EnsureXcmOrigin< + RuntimeOrigin, + (LocalPalletOriginToLocation, LocalOriginToLocation), + >; type XcmRouter = XcmRouter; // Anyone can execute XCM messages locally. type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>; diff --git a/polkadot/xcm/docs/Cargo.toml b/polkadot/xcm/docs/Cargo.toml index 6fa7ea9a23a92c2e3f86082a630ca3994bfce1d1..c3bda50619c1572202db2a3334bc14d4d11c6b4b 100644 --- a/polkadot/xcm/docs/Cargo.toml +++ b/polkadot/xcm/docs/Cargo.toml @@ -18,7 +18,7 @@ xcm-simulator = { workspace = true, default-features = true } # For building FRAME runtimes codec = { workspace = true, default-features = true } -frame = { features = ["experimental", "runtime"], workspace = true, default-features = true } +frame = { features = ["runtime"], workspace = true, default-features = true } polkadot-parachain-primitives = { workspace = true, default-features = true } polkadot-primitives = { workspace = true, default-features = true } polkadot-runtime-parachains = { workspace = true, default-features = true } diff --git a/prdoc/pr_3926.prdoc b/prdoc/pr_3926.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..7f352f7a45fb39876d7d2d0771d3e5cee496ead3 --- /dev/null +++ b/prdoc/pr_3926.prdoc @@ -0,0 +1,30 @@ +title: Introduce pallet-asset-rewards + +doc: + - audience: Runtime Dev + description: | + Introduce pallet-asset-rewards, which allows accounts to be rewarded for freezing fungible + tokens. The motivation for creating this pallet is to allow incentivising LPs. + See the pallet docs for more info about the pallet. + +crates: + - name: pallet-asset-rewards + bump: major + - name: polkadot-sdk + bump: minor + - name: kitchensink-runtime + bump: major + - name: asset-hub-rococo-runtime + bump: major + - name: asset-hub-westend-runtime + bump: major + - name: assets-common + bump: minor + - name: rococo-runtime + bump: minor + - name: westend-runtime + bump: patch + - name: frame-support + bump: minor + - name: emulated-integration-tests-common + bump: minor diff --git a/prdoc/pr_5501.prdoc b/prdoc/pr_5501.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..f2a5aa9a4667980452d4ac4dc728752da3591634 --- /dev/null +++ b/prdoc/pr_5501.prdoc @@ -0,0 +1,47 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Currency to Fungible migration for pallet-staking + +doc: + - audience: Runtime User + description: | + Lazy migration of staking balance from `Currency::locks` to `Fungible::holds`. New extrinsic + `staking::migrate_currency` removes the old lock along with other housekeeping. Additionally, any ledger mutation + creates hold if it does not exist. + + The pallet-staking configuration item `Currency` is updated to use `fungible::hold::Mutate` type while still + requiring `LockableCurrency` type to be passed as `OldCurrency` for migration purposes. + + +crates: + - name: westend-runtime + bump: major + - name: kitchensink-runtime + bump: minor + - name: pallet-delegated-staking + bump: patch + - name: pallet-nomination-pools + bump: minor + - name: pallet-nomination-pools-runtime-api + bump: patch + - name: sp-staking + bump: patch + - name: pallet-beefy + bump: patch + - name: pallet-fast-unstake + bump: patch + - name: pallet-staking + bump: major + - name: pallet-grandpa + bump: patch + - name: pallet-babe + bump: patch + - name: pallet-nomination-pools-benchmarking + bump: patch + - name: pallet-session-benchmarking + bump: patch + - name: pallet-root-offences + bump: patch + - name: pallet-offences-benchmarking + bump: patch diff --git a/prdoc/pr_6995.prdoc b/prdoc/pr_6995.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..ffdb4738a6fd5b1841d07ddd8e5e462c42abd704 --- /dev/null +++ b/prdoc/pr_6995.prdoc @@ -0,0 +1,14 @@ +title: added new proxy ParaRegistration to Westend +doc: +- audience: Runtime User + description: |- + This adds a new Proxy type to Westend Runtime called ParaRegistration. This is related to: https://github.com/polkadot-fellows/runtimes/pull/520. + + This new proxy allows: + 1. Reserve paraID + 2. Register Parachain + 3. Leverage Utilites pallet + 4. Remove proxy. +crates: +- name: westend-runtime + bump: major diff --git a/prdoc/pr_7091.prdoc b/prdoc/pr_7091.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..badea4e82fdbec9a48eb8948bd762b90cd49275a --- /dev/null +++ b/prdoc/pr_7091.prdoc @@ -0,0 +1,12 @@ +title: '[pallet-revive] Add new host function `to_account_id`' +doc: +- audience: Runtime Dev + description: A new host function `to_account_id` is added. It allows retrieving + the account id for a `H160` address. +crates: +- name: pallet-revive-fixtures + bump: minor +- name: pallet-revive + bump: minor +- name: pallet-revive-uapi + bump: minor diff --git a/prdoc/pr_7099.prdoc b/prdoc/pr_7099.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..58d809f3c0909b9236381aa0ab34a2f17ba84a40 --- /dev/null +++ b/prdoc/pr_7099.prdoc @@ -0,0 +1,16 @@ +title: Provide partial results to speedup GetRecord queries + +doc: + - audience: Node Dev + description: | + This PR provides the partial results of the GetRecord kademlia query. + + This significantly improves the authority discovery records, from ~37 minutes to ~2/3 minutes. + In contrast, libp2p discovers authority records in around ~10 minutes. + + The authority discovery was slow because litep2p provided the records only after the Kademlia query was completed. A normal Kademlia query completes in around 40 seconds to a few minutes. + In this PR, partial records are provided as soon as they are discovered from the network. + +crates: + - name: sc-network + bump: patch diff --git a/prdoc/pr_7163.prdoc b/prdoc/pr_7163.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..669c480b835bd982699f6a0eec21fbb343d8b6e3 --- /dev/null +++ b/prdoc/pr_7163.prdoc @@ -0,0 +1,13 @@ +title: '[pallet-revive] Remove debug buffer' +doc: +- audience: Runtime Dev + description: Remove the `debug_buffer` feature +crates: +- name: asset-hub-westend-runtime + bump: minor +- name: pallet-revive + bump: major +- name: pallet-revive-proc-macro + bump: minor +- name: pallet-revive-uapi + bump: minor diff --git a/prdoc/pr_7164.prdoc b/prdoc/pr_7164.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..cb0410a9de79954bab775de09629610ba420b2b4 --- /dev/null +++ b/prdoc/pr_7164.prdoc @@ -0,0 +1,8 @@ +title: '[pallet-revive] Remove revive events' +doc: +- audience: Runtime Dev + description: Remove all pallet::events except for the `ContractEmitted` event that + is emitted by contracts +crates: +- name: pallet-revive + bump: major diff --git a/prdoc/pr_7176.prdoc b/prdoc/pr_7176.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..b78f42014afea5e04d6c3057e91c62d9f9928db5 --- /dev/null +++ b/prdoc/pr_7176.prdoc @@ -0,0 +1,9 @@ +title: '[pallet-revive] Bump asset-hub westend spec version' +doc: +- audience: Runtime Dev + description: Bump asset-hub westend spec version +crates: +- name: asset-hub-westend-runtime + bump: minor +- name: pallet-revive-eth-rpc + bump: minor diff --git a/prdoc/pr_7177.prdoc b/prdoc/pr_7177.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..9ab0be1f20a936efe9feadf81c79a5f74f3531f6 --- /dev/null +++ b/prdoc/pr_7177.prdoc @@ -0,0 +1,20 @@ +title: Make frame crate not experimental +doc: +- audience: Runtime Dev + description: |- + Frame crate may still be unstable, but it is no longer feature gated by the feature `experimental`. +crates: +- name: polkadot-sdk-frame + bump: minor +- name: pallet-salary + bump: patch +- name: pallet-multisig + bump: patch +- name: pallet-proxy + bump: patch +- name: pallet-atomic-swap + bump: patch +- name: pallet-mixnet + bump: patch +- name: pallet-node-authorization + bump: patch diff --git a/prdoc/pr_7194.prdoc b/prdoc/pr_7194.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..3a9db46ceae96fdbb9f352a4558b1ab95ffd1450 --- /dev/null +++ b/prdoc/pr_7194.prdoc @@ -0,0 +1,15 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: '[FRAME] `pallet_asset_tx_payment`: replace `AssetId` bound from `Copy` to `Clone`' + +doc: + - audience: Runtime Dev + description: | + `OnChargeAssetTransaction`'s associated type `AssetId` is bounded by `Copy` which makes it impossible + to use `staging_xcm::v4::Location` as `AssetId`. This PR bounds `AssetId` to `Clone` instead, which is + more lenient. + +crates: + - name: pallet-asset-tx-payment + bump: minor diff --git a/prdoc/pr_7203.prdoc b/prdoc/pr_7203.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..96a3d19472e9f77189eaaf0f9ad3c829aa27e8a4 --- /dev/null +++ b/prdoc/pr_7203.prdoc @@ -0,0 +1,13 @@ +title: 'pallet_revive: Bump PolkaVM' +doc: +- audience: Runtime Dev + description: Update to PolkaVM `0.19`. This version renumbers the opcodes in order + to be in-line with the grey paper. Hopefully, for the last time. This means that + it breaks existing contracts. +crates: +- name: pallet-revive + bump: patch +- name: pallet-revive-fixtures + bump: patch +- name: pallet-revive-uapi + bump: patch diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index e11a009c1c3f5d4081f9bffc6b6a73d24800f17e..26f4dacf9a1e3039d3cd8e8d0f79415e4727be07 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -53,13 +53,15 @@ use frame_support::{ Balanced, Credit, HoldConsideration, ItemOf, NativeFromLeft, NativeOrWithId, UnionOf, }, tokens::{ - imbalance::ResolveAssetTo, nonfungibles_v2::Inspect, pay::PayAssetFromAccount, + imbalance::{ResolveAssetTo, ResolveTo}, + nonfungibles_v2::Inspect, + pay::PayAssetFromAccount, GetSalary, PayFromAccount, }, - AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU16, ConstU32, ConstU64, Contains, - Currency, EitherOfDiverse, EnsureOriginWithArg, EqualPrivilegeOnly, Imbalance, InsideBoth, - InstanceFilter, KeyOwnerProofSystem, LinearStoragePrice, LockIdentifier, Nothing, - OnUnbalanced, VariantCountOf, WithdrawReasons, + AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU16, ConstU32, ConstU64, + ConstantStoragePrice, Contains, Currency, EitherOfDiverse, EnsureOriginWithArg, + EqualPrivilegeOnly, Imbalance, InsideBoth, InstanceFilter, KeyOwnerProofSystem, + LinearStoragePrice, LockIdentifier, Nothing, OnUnbalanced, VariantCountOf, WithdrawReasons, }, weights::{ constants::{ @@ -511,7 +513,8 @@ impl pallet_glutton::Config for Runtime { } parameter_types! { - pub const PreimageHoldReason: RuntimeHoldReason = RuntimeHoldReason::Preimage(pallet_preimage::HoldReason::Preimage); + pub const PreimageHoldReason: RuntimeHoldReason = + RuntimeHoldReason::Preimage(pallet_preimage::HoldReason::Preimage); } impl pallet_preimage::Config for Runtime { @@ -618,6 +621,12 @@ impl pallet_transaction_payment::Config for Runtime { type WeightInfo = pallet_transaction_payment::weights::SubstrateWeight<Runtime>; } +pub type AssetsFreezerInstance = pallet_assets_freezer::Instance1; +impl pallet_assets_freezer::Config<AssetsFreezerInstance> for Runtime { + type RuntimeFreezeReason = RuntimeFreezeReason; + type RuntimeEvent = RuntimeEvent; +} + impl pallet_asset_conversion_tx_payment::Config for Runtime { type RuntimeEvent = RuntimeEvent; type AssetId = NativeOrWithId<u32>; @@ -712,13 +721,15 @@ impl pallet_staking::BenchmarkingConfig for StakingBenchmarkingConfig { } impl pallet_staking::Config for Runtime { + type OldCurrency = Balances; type Currency = Balances; type CurrencyBalance = Balance; type UnixTime = Timestamp; type CurrencyToVote = sp_staking::currency_to_vote::U128CurrencyToVote; - type RewardRemainder = Treasury; + type RewardRemainder = ResolveTo<TreasuryAccount, Balances>; type RuntimeEvent = RuntimeEvent; - type Slash = Treasury; // send the slashed funds to the treasury. + type RuntimeHoldReason = RuntimeHoldReason; + type Slash = ResolveTo<TreasuryAccount, Balances>; // send the slashed funds to the treasury. type Reward = (); // rewards are minted from the void type SessionsPerEra = SessionsPerEra; type BondingDuration = BondingDuration; @@ -741,7 +752,7 @@ impl pallet_staking::Config for Runtime { type MaxUnlockingChunks = ConstU32<32>; type MaxControllersInDeprecationBatch = MaxControllersInDeprecationBatch; type HistoryDepth = HistoryDepth; - type EventListeners = NominationPools; + type EventListeners = (NominationPools, DelegatedStaking); type WeightInfo = pallet_staking::weights::SubstrateWeight<Runtime>; type BenchmarkingConfig = StakingBenchmarkingConfig; type DisablingStrategy = pallet_staking::UpToLimitWithReEnablingDisablingStrategy; @@ -925,6 +936,21 @@ impl pallet_bags_list::Config<VoterBagsListInstance> for Runtime { type WeightInfo = pallet_bags_list::weights::SubstrateWeight<Runtime>; } +parameter_types! { + pub const DelegatedStakingPalletId: PalletId = PalletId(*b"py/dlstk"); + pub const SlashRewardFraction: Perbill = Perbill::from_percent(1); +} + +impl pallet_delegated_staking::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type PalletId = DelegatedStakingPalletId; + type Currency = Balances; + type OnSlash = (); + type SlashRewardFraction = SlashRewardFraction; + type RuntimeHoldReason = RuntimeHoldReason; + type CoreStaking = Staking; +} + parameter_types! { pub const PostUnbondPoolsWindow: u32 = 4; pub const NominationPoolsPalletId: PalletId = PalletId(*b"py/nopls"); @@ -953,7 +979,8 @@ impl pallet_nomination_pools::Config for Runtime { type RewardCounter = FixedU128; type BalanceToU256 = BalanceToU256; type U256ToBalance = U256ToBalance; - type StakeAdapter = pallet_nomination_pools::adapter::TransferStake<Self, Staking>; + type StakeAdapter = + pallet_nomination_pools::adapter::DelegateStake<Self, Staking, DelegatedStaking>; type PostUnbondingPoolsWindow = PostUnbondPoolsWindow; type MaxMetadataLen = ConstU32<256>; type MaxUnbonding = ConstU32<8>; @@ -1464,7 +1491,6 @@ impl pallet_revive::Config for Runtime { type InstantiateOrigin = EnsureSigned<Self::AccountId>; type RuntimeHoldReason = RuntimeHoldReason; type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; - type Debug = (); type Xcm = (); type ChainId = ConstU64<420_420_420>; type NativeToEthRatio = ConstU32<1_000_000>; // 10^(18 - 12) Eth is 10^18, Native is 10^12. @@ -1858,6 +1884,53 @@ impl pallet_asset_conversion::Config for Runtime { type BenchmarkHelper = (); } +pub type NativeAndAssetsFreezer = + UnionOf<Balances, AssetsFreezer, NativeFromLeft, NativeOrWithId<u32>, AccountId>; + +/// Benchmark Helper +#[cfg(feature = "runtime-benchmarks")] +pub struct AssetRewardsBenchmarkHelper; + +#[cfg(feature = "runtime-benchmarks")] +impl pallet_asset_rewards::benchmarking::BenchmarkHelper<NativeOrWithId<u32>> + for AssetRewardsBenchmarkHelper +{ + fn staked_asset() -> NativeOrWithId<u32> { + NativeOrWithId::<u32>::WithId(100) + } + fn reward_asset() -> NativeOrWithId<u32> { + NativeOrWithId::<u32>::WithId(101) + } +} + +parameter_types! { + pub const StakingRewardsPalletId: PalletId = PalletId(*b"py/stkrd"); + pub const CreationHoldReason: RuntimeHoldReason = + RuntimeHoldReason::AssetRewards(pallet_asset_rewards::HoldReason::PoolCreation); + // 1 item, 135 bytes into the storage on pool creation. + pub const StakePoolCreationDeposit: Balance = deposit(1, 135); +} + +impl pallet_asset_rewards::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeFreezeReason = RuntimeFreezeReason; + type AssetId = NativeOrWithId<u32>; + type Balance = Balance; + type Assets = NativeAndAssets; + type PalletId = StakingRewardsPalletId; + type CreatePoolOrigin = EnsureSigned<AccountId>; + type WeightInfo = (); + type AssetsFreezer = NativeAndAssetsFreezer; + type Consideration = HoldConsideration< + AccountId, + Balances, + CreationHoldReason, + ConstantStoragePrice<StakePoolCreationDeposit, Balance>, + >; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = AssetRewardsBenchmarkHelper; +} + impl pallet_asset_conversion_ops::Config for Runtime { type RuntimeEvent = RuntimeEvent; type PriorAccountIdConverter = pallet_asset_conversion::AccountIdConverterNoSeed<( @@ -2636,6 +2709,15 @@ mod runtime { #[runtime::pallet_index(81)] pub type VerifySignature = pallet_verify_signature::Pallet<Runtime>; + + #[runtime::pallet_index(82)] + pub type DelegatedStaking = pallet_delegated_staking::Pallet<Runtime>; + + #[runtime::pallet_index(83)] + pub type AssetRewards = pallet_asset_rewards::Pallet<Runtime>; + + #[runtime::pallet_index(84)] + pub type AssetsFreezer = pallet_assets_freezer::Pallet<Runtime, Instance1>; } impl TryFrom<RuntimeCall> for pallet_revive::Call<Runtime> { @@ -2846,6 +2928,7 @@ mod benches { [pallet_example_tasks, TasksExample] [pallet_democracy, Democracy] [pallet_asset_conversion, AssetConversion] + [pallet_asset_rewards, AssetRewards] [pallet_asset_conversion_tx_payment, AssetConversionTxPayment] [pallet_transaction_payment, TransactionPayment] [pallet_election_provider_multi_phase, ElectionProviderMultiPhase] @@ -3212,7 +3295,7 @@ impl_runtime_apis! { } } - impl pallet_revive::ReviveApi<Block, AccountId, Balance, Nonce, BlockNumber, EventRecord> for Runtime + impl pallet_revive::ReviveApi<Block, AccountId, Balance, Nonce, BlockNumber> for Runtime { fn balance(address: H160) -> U256 { Revive::evm_balance(&address) @@ -3247,7 +3330,7 @@ impl_runtime_apis! { gas_limit: Option<Weight>, storage_deposit_limit: Option<Balance>, input_data: Vec<u8>, - ) -> pallet_revive::ContractResult<pallet_revive::ExecReturnValue, Balance, EventRecord> { + ) -> pallet_revive::ContractResult<pallet_revive::ExecReturnValue, Balance> { Revive::bare_call( RuntimeOrigin::signed(origin), dest, @@ -3255,8 +3338,6 @@ impl_runtime_apis! { gas_limit.unwrap_or(RuntimeBlockWeights::get().max_block), pallet_revive::DepositLimit::Balance(storage_deposit_limit.unwrap_or(u128::MAX)), input_data, - pallet_revive::DebugInfo::UnsafeDebug, - pallet_revive::CollectEvents::UnsafeCollect, ) } @@ -3268,7 +3349,7 @@ impl_runtime_apis! { code: pallet_revive::Code, data: Vec<u8>, salt: Option<[u8; 32]>, - ) -> pallet_revive::ContractResult<pallet_revive::InstantiateReturnValue, Balance, EventRecord> + ) -> pallet_revive::ContractResult<pallet_revive::InstantiateReturnValue, Balance> { Revive::bare_instantiate( RuntimeOrigin::signed(origin), @@ -3278,8 +3359,6 @@ impl_runtime_apis! { code, data, salt, - pallet_revive::DebugInfo::UnsafeDebug, - pallet_revive::CollectEvents::UnsafeCollect, ) } @@ -3557,6 +3636,12 @@ impl_runtime_apis! { } } + impl pallet_asset_rewards::AssetRewards<Block, Balance> for Runtime { + fn pool_creation_cost() -> Balance { + StakePoolCreationDeposit::get() + } + } + #[cfg(feature = "try-runtime")] impl frame_try_runtime::TryRuntime<Block> for Runtime { fn on_runtime_upgrade(checks: frame_try_runtime::UpgradeCheckSelect) -> (Weight, Weight) { diff --git a/substrate/bin/node/testing/src/genesis.rs b/substrate/bin/node/testing/src/genesis.rs index 7f5364744c667f711a5e2c32e20636e904ee02c2..0394f6cd7394d3715c268229ba254053d6de5f53 100644 --- a/substrate/bin/node/testing/src/genesis.rs +++ b/substrate/bin/node/testing/src/genesis.rs @@ -38,9 +38,9 @@ pub fn config_endowed(extra_endowed: Vec<AccountId>) -> RuntimeGenesisConfig { (alice(), 111 * DOLLARS), (bob(), 100 * DOLLARS), (charlie(), 100_000_000 * DOLLARS), - (dave(), 111 * DOLLARS), + (dave(), 112 * DOLLARS), (eve(), 101 * DOLLARS), - (ferdie(), 100 * DOLLARS), + (ferdie(), 101 * DOLLARS), ]; endowed.extend(extra_endowed.into_iter().map(|endowed| (endowed, 100 * DOLLARS))); diff --git a/substrate/client/network/src/litep2p/discovery.rs b/substrate/client/network/src/litep2p/discovery.rs index b55df374f60ecc71959327c063c86b21e960f0b3..eb571804f30e61841ace4f985fe7a439bd9a43b7 100644 --- a/substrate/client/network/src/litep2p/discovery.rs +++ b/substrate/client/network/src/litep2p/discovery.rs @@ -33,8 +33,8 @@ use litep2p::{ identify::{Config as IdentifyConfig, IdentifyEvent}, kademlia::{ Config as KademliaConfig, ConfigBuilder as KademliaConfigBuilder, ContentProvider, - IncomingRecordValidationMode, KademliaEvent, KademliaHandle, QueryId, Quorum, - Record, RecordKey, RecordsType, + IncomingRecordValidationMode, KademliaEvent, KademliaHandle, PeerRecord, QueryId, + Quorum, Record, RecordKey, }, ping::{Config as PingConfig, PingEvent}, }, @@ -129,13 +129,19 @@ pub enum DiscoveryEvent { address: Multiaddr, }, - /// Record was found from the DHT. + /// `GetRecord` query succeeded. GetRecordSuccess { /// Query ID. query_id: QueryId, + }, - /// Records. - records: RecordsType, + /// Record was found from the DHT. + GetRecordPartialResult { + /// Query ID. + query_id: QueryId, + + /// Record. + record: PeerRecord, }, /// Record was successfully stored on the DHT. @@ -573,13 +579,24 @@ impl Stream for Discovery { peers: peers.into_iter().collect(), })) }, - Poll::Ready(Some(KademliaEvent::GetRecordSuccess { query_id, records })) => { + Poll::Ready(Some(KademliaEvent::GetRecordSuccess { query_id })) => { log::trace!( target: LOG_TARGET, - "`GET_RECORD` succeeded for {query_id:?}: {records:?}", + "`GET_RECORD` succeeded for {query_id:?}", ); - return Poll::Ready(Some(DiscoveryEvent::GetRecordSuccess { query_id, records })); + return Poll::Ready(Some(DiscoveryEvent::GetRecordSuccess { query_id })); + }, + Poll::Ready(Some(KademliaEvent::GetRecordPartialResult { query_id, record })) => { + log::trace!( + target: LOG_TARGET, + "`GET_RECORD` intermediary succeeded for {query_id:?}: {record:?}", + ); + + return Poll::Ready(Some(DiscoveryEvent::GetRecordPartialResult { + query_id, + record, + })); }, Poll::Ready(Some(KademliaEvent::PutRecordSuccess { query_id, key: _ })) => return Poll::Ready(Some(DiscoveryEvent::PutRecordSuccess { query_id })), diff --git a/substrate/client/network/src/litep2p/mod.rs b/substrate/client/network/src/litep2p/mod.rs index 52b2970525df1e0a58c9daf409a2e65355fdea45..fc4cce47628396bd22c8785edd18227c27a7326a 100644 --- a/substrate/client/network/src/litep2p/mod.rs +++ b/substrate/client/network/src/litep2p/mod.rs @@ -58,7 +58,7 @@ use litep2p::{ protocol::{ libp2p::{ bitswap::Config as BitswapConfig, - kademlia::{QueryId, Record, RecordsType}, + kademlia::{QueryId, Record}, }, request_response::ConfigBuilder as RequestResponseConfigBuilder, }, @@ -836,23 +836,45 @@ impl<B: BlockT + 'static, H: ExHashT> NetworkBackend<B, H> for Litep2pNetworkBac self.peerstore_handle.add_known_peer(peer.into()); } } - Some(DiscoveryEvent::GetRecordSuccess { query_id, records }) => { + Some(DiscoveryEvent::GetRecordPartialResult { query_id, record }) => { + if !self.pending_queries.contains_key(&query_id) { + log::error!( + target: LOG_TARGET, + "Missing/invalid pending query for `GET_VALUE` partial result: {query_id:?}" + ); + + continue + } + + let peer_id: sc_network_types::PeerId = record.peer.into(); + let record = PeerRecord { + record: P2PRecord { + key: record.record.key.to_vec().into(), + value: record.record.value, + publisher: record.record.publisher.map(|peer_id| { + let peer_id: sc_network_types::PeerId = peer_id.into(); + peer_id.into() + }), + expires: record.record.expires, + }, + peer: Some(peer_id.into()), + }; + + self.event_streams.send( + Event::Dht( + DhtEvent::ValueFound( + record.into() + ) + ) + ); + } + Some(DiscoveryEvent::GetRecordSuccess { query_id }) => { match self.pending_queries.remove(&query_id) { Some(KadQuery::GetValue(key, started)) => { log::trace!( target: LOG_TARGET, - "`GET_VALUE` for {:?} ({query_id:?}) succeeded", - key, + "`GET_VALUE` for {key:?} ({query_id:?}) succeeded", ); - for record in litep2p_to_libp2p_peer_record(records) { - self.event_streams.send( - Event::Dht( - DhtEvent::ValueFound( - record.into() - ) - ) - ); - } if let Some(ref metrics) = self.metrics { metrics @@ -1165,42 +1187,3 @@ impl<B: BlockT + 'static, H: ExHashT> NetworkBackend<B, H> for Litep2pNetworkBac } } } - -// Glue code to convert from a litep2p records type to a libp2p2 PeerRecord. -fn litep2p_to_libp2p_peer_record(records: RecordsType) -> Vec<PeerRecord> { - match records { - litep2p::protocol::libp2p::kademlia::RecordsType::LocalStore(record) => { - vec![PeerRecord { - record: P2PRecord { - key: record.key.to_vec().into(), - value: record.value, - publisher: record.publisher.map(|peer_id| { - let peer_id: sc_network_types::PeerId = peer_id.into(); - peer_id.into() - }), - expires: record.expires, - }, - peer: None, - }] - }, - litep2p::protocol::libp2p::kademlia::RecordsType::Network(records) => records - .into_iter() - .map(|record| { - let peer_id: sc_network_types::PeerId = record.peer.into(); - - PeerRecord { - record: P2PRecord { - key: record.record.key.to_vec().into(), - value: record.record.value, - publisher: record.record.publisher.map(|peer_id| { - let peer_id: sc_network_types::PeerId = peer_id.into(); - peer_id.into() - }), - expires: record.record.expires, - }, - peer: Some(peer_id.into()), - } - }) - .collect::<Vec<_>>(), - } -} diff --git a/substrate/frame/asset-rewards/Cargo.toml b/substrate/frame/asset-rewards/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..a03fa17cf0dc0e1911dadab39a4dbadbb542a372 --- /dev/null +++ b/substrate/frame/asset-rewards/Cargo.toml @@ -0,0 +1,71 @@ +[package] +name = "pallet-asset-rewards" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +license = "Apache-2.0" +homepage.workspace = true +repository.workspace = true +description = "FRAME asset rewards pallet" + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } +frame-support = { workspace = true, features = ["experimental"] } +frame-system = { workspace = true } +scale-info = { workspace = true, features = ["derive"] } +sp-api = { workspace = true } +sp-arithmetic = { workspace = true } +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } + +[dev-dependencies] +pallet-assets = { workspace = true } +pallet-assets-freezer = { workspace = true } +pallet-balances = { workspace = true } +primitive-types = { workspace = true, features = ["codec", "num-traits", "scale-info"] } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "pallet-assets-freezer/std", + "pallet-assets/std", + "pallet-balances/std", + "primitive-types/std", + "scale-info/std", + "sp-api/std", + "sp-arithmetic/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-assets-freezer/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "pallet-assets-freezer/try-runtime", + "pallet-assets/try-runtime", + "pallet-balances/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/substrate/frame/asset-rewards/src/benchmarking.rs b/substrate/frame/asset-rewards/src/benchmarking.rs new file mode 100644 index 0000000000000000000000000000000000000000..5605804dd20ece8eb27509de9bb4fe44d0037ab3 --- /dev/null +++ b/substrate/frame/asset-rewards/src/benchmarking.rs @@ -0,0 +1,355 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Asset Rewards pallet benchmarking. + +use super::*; +use crate::Pallet as AssetRewards; +use frame_benchmarking::{v2::*, whitelisted_caller, BenchmarkError}; +use frame_support::{ + assert_ok, + traits::{ + fungibles::{Create, Inspect, Mutate}, + Consideration, EnsureOrigin, Footprint, + }, +}; +use frame_system::{pallet_prelude::BlockNumberFor, Pallet as System, RawOrigin}; +use sp_runtime::{traits::One, Saturating}; +use sp_std::prelude::*; + +/// Benchmark Helper +pub trait BenchmarkHelper<AssetId> { + /// Returns the staked asset id. + /// + /// If the asset does not exist, it will be created by the benchmark. + fn staked_asset() -> AssetId; + /// Returns the reward asset id. + /// + /// If the asset does not exist, it will be created by the benchmark. + fn reward_asset() -> AssetId; +} + +fn pool_expire<T: Config>() -> DispatchTime<BlockNumberFor<T>> { + DispatchTime::At(BlockNumberFor::<T>::from(100u32)) +} + +fn create_reward_pool<T: Config>() -> Result<T::RuntimeOrigin, BenchmarkError> +where + T::Assets: Create<T::AccountId> + Mutate<T::AccountId>, +{ + let caller_origin = + T::CreatePoolOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + let caller = T::CreatePoolOrigin::ensure_origin(caller_origin.clone()).unwrap(); + + let footprint = Footprint::from_mel::<(PoolId, PoolInfoFor<T>)>(); + T::Consideration::ensure_successful(&caller, footprint); + + let staked_asset = T::BenchmarkHelper::staked_asset(); + let reward_asset = T::BenchmarkHelper::reward_asset(); + + let min_staked_balance = + T::Assets::minimum_balance(staked_asset.clone()).max(T::Balance::one()); + if !T::Assets::asset_exists(staked_asset.clone()) { + assert_ok!(T::Assets::create( + staked_asset.clone(), + caller.clone(), + true, + min_staked_balance + )); + } + let min_reward_balance = + T::Assets::minimum_balance(reward_asset.clone()).max(T::Balance::one()); + if !T::Assets::asset_exists(reward_asset.clone()) { + assert_ok!(T::Assets::create( + reward_asset.clone(), + caller.clone(), + true, + min_reward_balance + )); + } + + assert_ok!(AssetRewards::<T>::create_pool( + caller_origin.clone(), + Box::new(staked_asset), + Box::new(reward_asset), + // reward rate per block + min_reward_balance, + pool_expire::<T>(), + Some(caller), + )); + + Ok(caller_origin) +} + +fn mint_into<T: Config>(caller: &T::AccountId, asset: &T::AssetId) -> T::Balance +where + T::Assets: Mutate<T::AccountId>, +{ + let min_balance = T::Assets::minimum_balance(asset.clone()); + assert_ok!(T::Assets::mint_into( + asset.clone(), + &caller, + min_balance.saturating_mul(10u32.into()) + )); + min_balance +} + +fn assert_last_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) { + System::<T>::assert_last_event(generic_event.into()); +} + +#[benchmarks(where T::Assets: Create<T::AccountId> + Mutate<T::AccountId>)] +mod benchmarks { + use super::*; + + #[benchmark] + fn create_pool() -> Result<(), BenchmarkError> { + let caller_origin = + T::CreatePoolOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + let caller = T::CreatePoolOrigin::ensure_origin(caller_origin.clone()).unwrap(); + + let footprint = Footprint::from_mel::<(PoolId, PoolInfoFor<T>)>(); + T::Consideration::ensure_successful(&caller, footprint); + + let staked_asset = T::BenchmarkHelper::staked_asset(); + let reward_asset = T::BenchmarkHelper::reward_asset(); + + let min_balance = T::Assets::minimum_balance(staked_asset.clone()).max(T::Balance::one()); + if !T::Assets::asset_exists(staked_asset.clone()) { + assert_ok!(T::Assets::create(staked_asset.clone(), caller.clone(), true, min_balance)); + } + let min_balance = T::Assets::minimum_balance(reward_asset.clone()).max(T::Balance::one()); + if !T::Assets::asset_exists(reward_asset.clone()) { + assert_ok!(T::Assets::create(reward_asset.clone(), caller.clone(), true, min_balance)); + } + + #[extrinsic_call] + _( + caller_origin as T::RuntimeOrigin, + Box::new(staked_asset.clone()), + Box::new(reward_asset.clone()), + min_balance, + pool_expire::<T>(), + Some(caller.clone()), + ); + + assert_last_event::<T>( + Event::PoolCreated { + creator: caller.clone(), + admin: caller, + staked_asset_id: staked_asset, + reward_asset_id: reward_asset, + reward_rate_per_block: min_balance, + expiry_block: pool_expire::<T>().evaluate(System::<T>::block_number()), + pool_id: 0, + } + .into(), + ); + + Ok(()) + } + + #[benchmark] + fn stake() -> Result<(), BenchmarkError> { + create_reward_pool::<T>()?; + + let staker: T::AccountId = whitelisted_caller(); + let min_balance = mint_into::<T>(&staker, &T::BenchmarkHelper::staked_asset()); + + // stake first to get worth case benchmark. + assert_ok!(AssetRewards::<T>::stake( + RawOrigin::Signed(staker.clone()).into(), + 0, + min_balance + )); + + #[extrinsic_call] + _(RawOrigin::Signed(staker.clone()), 0, min_balance); + + assert_last_event::<T>(Event::Staked { staker, pool_id: 0, amount: min_balance }.into()); + + Ok(()) + } + + #[benchmark] + fn unstake() -> Result<(), BenchmarkError> { + create_reward_pool::<T>()?; + + let staker: T::AccountId = whitelisted_caller(); + let min_balance = mint_into::<T>(&staker, &T::BenchmarkHelper::staked_asset()); + + assert_ok!(AssetRewards::<T>::stake( + RawOrigin::Signed(staker.clone()).into(), + 0, + min_balance, + )); + + #[extrinsic_call] + _(RawOrigin::Signed(staker.clone()), 0, min_balance, None); + + assert_last_event::<T>( + Event::Unstaked { caller: staker.clone(), staker, pool_id: 0, amount: min_balance } + .into(), + ); + + Ok(()) + } + + #[benchmark] + fn harvest_rewards() -> Result<(), BenchmarkError> { + create_reward_pool::<T>()?; + + let pool_acc = AssetRewards::<T>::pool_account_id(&0u32); + let min_reward_balance = mint_into::<T>(&pool_acc, &T::BenchmarkHelper::reward_asset()); + + let staker = whitelisted_caller(); + let _ = mint_into::<T>(&staker, &T::BenchmarkHelper::staked_asset()); + assert_ok!(AssetRewards::<T>::stake( + RawOrigin::Signed(staker.clone()).into(), + 0, + T::Balance::one(), + )); + + System::<T>::set_block_number(System::<T>::block_number() + BlockNumberFor::<T>::one()); + + #[extrinsic_call] + _(RawOrigin::Signed(staker.clone()), 0, None); + + assert_last_event::<T>( + Event::RewardsHarvested { + caller: staker.clone(), + staker, + pool_id: 0, + amount: min_reward_balance, + } + .into(), + ); + + Ok(()) + } + + #[benchmark] + fn set_pool_reward_rate_per_block() -> Result<(), BenchmarkError> { + let caller_origin = create_reward_pool::<T>()?; + + // stake first to get worth case benchmark. + { + let staker: T::AccountId = whitelisted_caller(); + let min_balance = mint_into::<T>(&staker, &T::BenchmarkHelper::staked_asset()); + + assert_ok!(AssetRewards::<T>::stake(RawOrigin::Signed(staker).into(), 0, min_balance)); + } + + let new_reward_rate_per_block = + T::Assets::minimum_balance(T::BenchmarkHelper::reward_asset()).max(T::Balance::one()) + + T::Balance::one(); + + #[extrinsic_call] + _(caller_origin as T::RuntimeOrigin, 0, new_reward_rate_per_block); + + assert_last_event::<T>( + Event::PoolRewardRateModified { pool_id: 0, new_reward_rate_per_block }.into(), + ); + Ok(()) + } + + #[benchmark] + fn set_pool_admin() -> Result<(), BenchmarkError> { + let caller_origin = create_reward_pool::<T>()?; + let new_admin: T::AccountId = whitelisted_caller(); + + #[extrinsic_call] + _(caller_origin as T::RuntimeOrigin, 0, new_admin.clone()); + + assert_last_event::<T>(Event::PoolAdminModified { pool_id: 0, new_admin }.into()); + + Ok(()) + } + + #[benchmark] + fn set_pool_expiry_block() -> Result<(), BenchmarkError> { + let create_origin = create_reward_pool::<T>()?; + + // stake first to get worth case benchmark. + { + let staker: T::AccountId = whitelisted_caller(); + let min_balance = mint_into::<T>(&staker, &T::BenchmarkHelper::staked_asset()); + + assert_ok!(AssetRewards::<T>::stake(RawOrigin::Signed(staker).into(), 0, min_balance)); + } + + let new_expiry_block = + pool_expire::<T>().evaluate(System::<T>::block_number()) + BlockNumberFor::<T>::one(); + + #[extrinsic_call] + _(create_origin as T::RuntimeOrigin, 0, DispatchTime::At(new_expiry_block)); + + assert_last_event::<T>( + Event::PoolExpiryBlockModified { pool_id: 0, new_expiry_block }.into(), + ); + + Ok(()) + } + + #[benchmark] + fn deposit_reward_tokens() -> Result<(), BenchmarkError> { + create_reward_pool::<T>()?; + let caller = whitelisted_caller(); + + let reward_asset = T::BenchmarkHelper::reward_asset(); + let pool_acc = AssetRewards::<T>::pool_account_id(&0u32); + let min_balance = mint_into::<T>(&caller, &reward_asset); + + let balance_before = T::Assets::balance(reward_asset.clone(), &pool_acc); + + #[extrinsic_call] + _(RawOrigin::Signed(caller), 0, min_balance); + + let balance_after = T::Assets::balance(reward_asset, &pool_acc); + + assert_eq!(balance_after, balance_before + min_balance); + + Ok(()) + } + + #[benchmark] + fn cleanup_pool() -> Result<(), BenchmarkError> { + let create_origin = create_reward_pool::<T>()?; + let caller = T::CreatePoolOrigin::ensure_origin(create_origin.clone()).unwrap(); + + // deposit rewards tokens to get worth case benchmark. + { + let caller = whitelisted_caller(); + let reward_asset = T::BenchmarkHelper::reward_asset(); + let min_balance = mint_into::<T>(&caller, &reward_asset); + assert_ok!(AssetRewards::<T>::deposit_reward_tokens( + RawOrigin::Signed(caller).into(), + 0, + min_balance + )); + } + + #[extrinsic_call] + _(RawOrigin::Signed(caller), 0); + + assert_last_event::<T>(Event::PoolCleanedUp { pool_id: 0 }.into()); + + Ok(()) + } + + impl_benchmark_test_suite!(AssetRewards, crate::mock::new_test_ext(), crate::mock::MockRuntime); +} diff --git a/substrate/frame/asset-rewards/src/lib.rs b/substrate/frame/asset-rewards/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..4ce73e9febf96f8c363ffa9203d0209f3b599d88 --- /dev/null +++ b/substrate/frame/asset-rewards/src/lib.rs @@ -0,0 +1,905 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! # FRAME Staking Rewards Pallet +//! +//! Allows accounts to be rewarded for holding `fungible` asset/s, for example LP tokens. +//! +//! ## Overview +//! +//! Initiate an incentive program for a fungible asset by creating a new pool. +//! +//! During pool creation, a 'staking asset', 'reward asset', 'reward rate per block', 'expiry +//! block', and 'admin' are specified. +//! +//! Once created, holders of the 'staking asset' can 'stake' them in a corresponding pool, which +//! creates a Freeze on the asset. +//! +//! Once staked, rewards denominated in 'reward asset' begin accumulating to the staker, +//! proportional to their share of the total staked tokens in the pool. +//! +//! Reward assets pending distribution are held in an account unique to each pool. +//! +//! Care should be taken by the pool operator to keep pool accounts adequately funded with the +//! reward asset. +//! +//! The pool admin may increase reward rate per block, increase expiry block, and change admin. +//! +//! ## Disambiguation +//! +//! While this pallet shares some terminology with the `staking-pool` and similar native staking +//! related pallets, it is distinct and is entirely unrelated to native staking. +//! +//! ## Permissioning +//! +//! Currently, pool creation and management restricted to a configured Origin. +//! +//! Future iterations of this pallet may allow permissionless creation and management of pools. +//! +//! Note: The permissioned origin must return an AccountId. This can be achieved for any Origin by +//! wrapping it with `EnsureSuccess`. +//! +//! ## Implementation Notes +//! +//! Internal logic functions such as `update_pool_and_staker_rewards` were deliberately written +//! without side-effects. +//! +//! Storage interaction such as reads and writes are instead all performed in the top level +//! pallet Call method, which while slightly more verbose, makes it easier to understand the +//! code and reason about how storage reads and writes occur in the pallet. +//! +//! ## Rewards Algorithm +//! +//! The rewards algorithm is based on the Synthetix [StakingRewards.sol](https://github.com/Synthetixio/synthetix/blob/develop/contracts/StakingRewards.sol) +//! smart contract. +//! +//! Rewards are calculated JIT (just-in-time), and all operations are O(1) making the approach +//! scalable to many pools and stakers. +//! +//! ### Resources +//! +//! - [This video series](https://www.youtube.com/watch?v=6ZO5aYg1GI8), which walks through the math +//! of the algorithm. +//! - [This dev.to article](https://dev.to/heymarkkop/understanding-sushiswaps-masterchef-staking-rewards-1m6f), +//! which explains the algorithm of the SushiSwap MasterChef staking. While not identical to the +//! Synthetix approach, they are quite similar. +#![deny(missing_docs)] +#![cfg_attr(not(feature = "std"), no_std)] + +pub use pallet::*; + +use codec::{Codec, Decode, Encode, MaxEncodedLen}; +use frame_support::{ + traits::{ + fungibles::{Inspect, Mutate}, + schedule::DispatchTime, + tokens::Balance, + }, + PalletId, +}; +use frame_system::pallet_prelude::BlockNumberFor; +use scale_info::TypeInfo; +use sp_core::Get; +use sp_runtime::{ + traits::{MaybeDisplay, Zero}, + DispatchError, +}; +use sp_std::boxed::Box; + +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmarking; +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; +mod weights; + +pub use weights::WeightInfo; + +/// Unique id type for each pool. +pub type PoolId = u32; + +/// Multiplier to maintain precision when calculating rewards. +pub(crate) const PRECISION_SCALING_FACTOR: u16 = 4096; + +/// Convenience alias for `PoolInfo`. +pub type PoolInfoFor<T> = PoolInfo< + <T as frame_system::Config>::AccountId, + <T as Config>::AssetId, + <T as Config>::Balance, + BlockNumberFor<T>, +>; + +/// The state of a staker in a pool. +#[derive(Debug, Default, Clone, Decode, Encode, MaxEncodedLen, TypeInfo)] +pub struct PoolStakerInfo<Balance> { + /// Amount of tokens staked. + amount: Balance, + /// Accumulated, unpaid rewards. + rewards: Balance, + /// Reward per token value at the time of the staker's last interaction with the contract. + reward_per_token_paid: Balance, +} + +/// The state and configuration of an incentive pool. +#[derive(Debug, Clone, Decode, Encode, Default, PartialEq, Eq, MaxEncodedLen, TypeInfo)] +pub struct PoolInfo<AccountId, AssetId, Balance, BlockNumber> { + /// The asset staked in this pool. + staked_asset_id: AssetId, + /// The asset distributed as rewards by this pool. + reward_asset_id: AssetId, + /// The amount of tokens rewarded per block. + reward_rate_per_block: Balance, + /// The block the pool will cease distributing rewards. + expiry_block: BlockNumber, + /// The account authorized to manage this pool. + admin: AccountId, + /// The total amount of tokens staked in this pool. + total_tokens_staked: Balance, + /// Total rewards accumulated per token, up to the `last_update_block`. + reward_per_token_stored: Balance, + /// Last block number the pool was updated. + last_update_block: BlockNumber, + /// The account that holds the pool's rewards. + account: AccountId, +} + +sp_api::decl_runtime_apis! { + /// The runtime API for the asset rewards pallet. + pub trait AssetRewards<Cost: MaybeDisplay + Codec> { + /// Get the cost of creating a pool. + /// + /// This is especially useful when the cost is dynamic. + fn pool_creation_cost() -> Cost; + } +} + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::{ + pallet_prelude::*, + traits::{ + fungibles::MutateFreeze, + tokens::{AssetId, Fortitude, Preservation}, + Consideration, Footprint, + }, + }; + use frame_system::pallet_prelude::*; + use sp_runtime::{ + traits::{ + AccountIdConversion, BadOrigin, EnsureAdd, EnsureAddAssign, EnsureDiv, EnsureMul, + EnsureSub, EnsureSubAssign, + }, + DispatchResult, + }; + + #[pallet::pallet] + pub struct Pallet<T>(_); + + /// A reason for the pallet placing a hold on funds. + #[pallet::composite_enum] + pub enum FreezeReason { + /// Funds are staked in the pallet. + #[codec(index = 0)] + Staked, + } + + /// A reason for the pallet placing a hold on funds. + #[pallet::composite_enum] + pub enum HoldReason { + /// Cost associated with storing pool information on-chain. + #[codec(index = 0)] + PoolCreation, + } + + #[pallet::config] + pub trait Config: frame_system::Config { + /// Overarching event type. + type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>; + + /// The pallet's unique identifier, used to derive the pool's account ID. + /// + /// The account ID is derived once during pool creation and stored in the storage. + #[pallet::constant] + type PalletId: Get<PalletId>; + + /// Identifier for each type of asset. + type AssetId: AssetId + Member + Parameter; + + /// The type in which the assets are measured. + type Balance: Balance + TypeInfo; + + /// The origin with permission to create pools. + /// + /// The Origin must return an AccountId. + type CreatePoolOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = Self::AccountId>; + + /// Registry of assets that can be configured to either stake for rewards, or be offered as + /// rewards for staking. + type Assets: Inspect<Self::AccountId, AssetId = Self::AssetId, Balance = Self::Balance> + + Mutate<Self::AccountId>; + + /// Freezer for the Assets. + type AssetsFreezer: MutateFreeze< + Self::AccountId, + Id = Self::RuntimeFreezeReason, + AssetId = Self::AssetId, + Balance = Self::Balance, + >; + + /// The overarching freeze reason. + type RuntimeFreezeReason: From<FreezeReason>; + + /// Means for associating a cost with the on-chain storage of pool information, which + /// is incurred by the pool creator. + /// + /// The passed `Footprint` specifically accounts for the storage footprint of the pool's + /// information itself, excluding any potential storage footprint related to the stakers. + type Consideration: Consideration<Self::AccountId, Footprint>; + + /// Weight information for extrinsics in this pallet. + type WeightInfo: WeightInfo; + + /// Helper for benchmarking. + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper: benchmarking::BenchmarkHelper<Self::AssetId>; + } + + /// State of pool stakers. + #[pallet::storage] + pub type PoolStakers<T: Config> = StorageDoubleMap< + _, + Blake2_128Concat, + PoolId, + Blake2_128Concat, + T::AccountId, + PoolStakerInfo<T::Balance>, + >; + + /// State and configuration of each staking pool. + #[pallet::storage] + pub type Pools<T: Config> = StorageMap<_, Blake2_128Concat, PoolId, PoolInfoFor<T>>; + + /// The cost associated with storing pool information on-chain which was incurred by the pool + /// creator. + /// + /// This cost may be [`None`], as determined by [`Config::Consideration`]. + #[pallet::storage] + pub type PoolCost<T: Config> = + StorageMap<_, Blake2_128Concat, PoolId, (T::AccountId, T::Consideration)>; + + /// Stores the [`PoolId`] to use for the next pool. + /// + /// Incremented when a new pool is created. + #[pallet::storage] + pub type NextPoolId<T: Config> = StorageValue<_, PoolId, ValueQuery>; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event<T: Config> { + /// An account staked some tokens in a pool. + Staked { + /// The account that staked assets. + staker: T::AccountId, + /// The pool. + pool_id: PoolId, + /// The staked asset amount. + amount: T::Balance, + }, + /// An account unstaked some tokens from a pool. + Unstaked { + /// The account that signed transaction. + caller: T::AccountId, + /// The account that unstaked assets. + staker: T::AccountId, + /// The pool. + pool_id: PoolId, + /// The unstaked asset amount. + amount: T::Balance, + }, + /// An account harvested some rewards. + RewardsHarvested { + /// The account that signed transaction. + caller: T::AccountId, + /// The staker whos rewards were harvested. + staker: T::AccountId, + /// The pool. + pool_id: PoolId, + /// The amount of harvested tokens. + amount: T::Balance, + }, + /// A new reward pool was created. + PoolCreated { + /// The account that created the pool. + creator: T::AccountId, + /// The unique ID for the new pool. + pool_id: PoolId, + /// The staking asset. + staked_asset_id: T::AssetId, + /// The reward asset. + reward_asset_id: T::AssetId, + /// The initial reward rate per block. + reward_rate_per_block: T::Balance, + /// The block the pool will cease to accumulate rewards. + expiry_block: BlockNumberFor<T>, + /// The account allowed to modify the pool. + admin: T::AccountId, + }, + /// A pool reward rate was modified by the admin. + PoolRewardRateModified { + /// The modified pool. + pool_id: PoolId, + /// The new reward rate per block. + new_reward_rate_per_block: T::Balance, + }, + /// A pool admin was modified. + PoolAdminModified { + /// The modified pool. + pool_id: PoolId, + /// The new admin. + new_admin: T::AccountId, + }, + /// A pool expiry block was modified by the admin. + PoolExpiryBlockModified { + /// The modified pool. + pool_id: PoolId, + /// The new expiry block. + new_expiry_block: BlockNumberFor<T>, + }, + /// A pool information was cleared after it's completion. + PoolCleanedUp { + /// The cleared pool. + pool_id: PoolId, + }, + } + + #[pallet::error] + pub enum Error<T> { + /// The staker does not have enough tokens to perform the operation. + NotEnoughTokens, + /// An operation was attempted on a non-existent pool. + NonExistentPool, + /// An operation was attempted for a non-existent staker. + NonExistentStaker, + /// An operation was attempted with a non-existent asset. + NonExistentAsset, + /// There was an error converting a block number. + BlockNumberConversionError, + /// The expiry block must be in the future. + ExpiryBlockMustBeInTheFuture, + /// Insufficient funds to create the freeze. + InsufficientFunds, + /// The expiry block can be only extended. + ExpiryCut, + /// The reward rate per block can be only increased. + RewardRateCut, + /// The pool still has staked tokens or rewards. + NonEmptyPool, + } + + #[pallet::hooks] + impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> { + fn integrity_test() { + // The AccountId is at least 16 bytes to contain the unique PalletId. + let pool_id: PoolId = 1; + assert!( + <frame_support::PalletId as AccountIdConversion<T::AccountId>>::try_into_sub_account( + &T::PalletId::get(), pool_id, + ) + .is_some() + ); + } + } + + /// Pallet's callable functions. + #[pallet::call(weight(<T as Config>::WeightInfo))] + impl<T: Config> Pallet<T> { + /// Create a new reward pool. + /// + /// Parameters: + /// - `origin`: must be `Config::CreatePoolOrigin`; + /// - `staked_asset_id`: the asset to be staked in the pool; + /// - `reward_asset_id`: the asset to be distributed as rewards; + /// - `reward_rate_per_block`: the amount of reward tokens distributed per block; + /// - `expiry`: the block number at which the pool will cease to accumulate rewards. The + /// [`DispatchTime::After`] variant evaluated at the execution time. + /// - `admin`: the account allowed to extend the pool expiration, increase the rewards rate + /// and receive the unutilized reward tokens back after the pool completion. If `None`, + /// the caller is set as an admin. + #[pallet::call_index(0)] + pub fn create_pool( + origin: OriginFor<T>, + staked_asset_id: Box<T::AssetId>, + reward_asset_id: Box<T::AssetId>, + reward_rate_per_block: T::Balance, + expiry: DispatchTime<BlockNumberFor<T>>, + admin: Option<T::AccountId>, + ) -> DispatchResult { + // Check the origin. + let creator = T::CreatePoolOrigin::ensure_origin(origin)?; + + // Ensure the assets exist. + ensure!( + T::Assets::asset_exists(*staked_asset_id.clone()), + Error::<T>::NonExistentAsset + ); + ensure!( + T::Assets::asset_exists(*reward_asset_id.clone()), + Error::<T>::NonExistentAsset + ); + + // Check the expiry block. + let expiry_block = expiry.evaluate(frame_system::Pallet::<T>::block_number()); + ensure!( + expiry_block > frame_system::Pallet::<T>::block_number(), + Error::<T>::ExpiryBlockMustBeInTheFuture + ); + + let pool_id = NextPoolId::<T>::get(); + + let footprint = Self::pool_creation_footprint(); + let cost = T::Consideration::new(&creator, footprint)?; + PoolCost::<T>::insert(pool_id, (creator.clone(), cost)); + + let admin = admin.unwrap_or(creator.clone()); + + // Create the pool. + let pool = PoolInfoFor::<T> { + staked_asset_id: *staked_asset_id.clone(), + reward_asset_id: *reward_asset_id.clone(), + reward_rate_per_block, + total_tokens_staked: 0u32.into(), + reward_per_token_stored: 0u32.into(), + last_update_block: 0u32.into(), + expiry_block, + admin: admin.clone(), + account: Self::pool_account_id(&pool_id), + }; + + // Insert it into storage. + Pools::<T>::insert(pool_id, pool); + + NextPoolId::<T>::put(pool_id.ensure_add(1)?); + + // Emit created event. + Self::deposit_event(Event::PoolCreated { + creator, + pool_id, + staked_asset_id: *staked_asset_id, + reward_asset_id: *reward_asset_id, + reward_rate_per_block, + expiry_block, + admin, + }); + + Ok(()) + } + + /// Stake additional tokens in a pool. + /// + /// A freeze is placed on the staked tokens. + #[pallet::call_index(1)] + pub fn stake(origin: OriginFor<T>, pool_id: PoolId, amount: T::Balance) -> DispatchResult { + let staker = ensure_signed(origin)?; + + // Always start by updating staker and pool rewards. + let pool_info = Pools::<T>::get(pool_id).ok_or(Error::<T>::NonExistentPool)?; + let staker_info = PoolStakers::<T>::get(pool_id, &staker).unwrap_or_default(); + let (mut pool_info, mut staker_info) = + Self::update_pool_and_staker_rewards(&pool_info, &staker_info)?; + + T::AssetsFreezer::increase_frozen( + pool_info.staked_asset_id.clone(), + &FreezeReason::Staked.into(), + &staker, + amount, + )?; + + // Update Pools. + pool_info.total_tokens_staked.ensure_add_assign(amount)?; + + Pools::<T>::insert(pool_id, pool_info); + + // Update PoolStakers. + staker_info.amount.ensure_add_assign(amount)?; + PoolStakers::<T>::insert(pool_id, &staker, staker_info); + + // Emit event. + Self::deposit_event(Event::Staked { staker, pool_id, amount }); + + Ok(()) + } + + /// Unstake tokens from a pool. + /// + /// Removes the freeze on the staked tokens. + /// + /// Parameters: + /// - origin: must be the `staker` if the pool is still active. Otherwise, any account. + /// - pool_id: the pool to unstake from. + /// - amount: the amount of tokens to unstake. + /// - staker: the account to unstake from. If `None`, the caller is used. + #[pallet::call_index(2)] + pub fn unstake( + origin: OriginFor<T>, + pool_id: PoolId, + amount: T::Balance, + staker: Option<T::AccountId>, + ) -> DispatchResult { + let caller = ensure_signed(origin)?; + let staker = staker.unwrap_or(caller.clone()); + + // Always start by updating the pool rewards. + let pool_info = Pools::<T>::get(pool_id).ok_or(Error::<T>::NonExistentPool)?; + let now = frame_system::Pallet::<T>::block_number(); + ensure!(now > pool_info.expiry_block || caller == staker, BadOrigin); + + let staker_info = PoolStakers::<T>::get(pool_id, &staker).unwrap_or_default(); + let (mut pool_info, mut staker_info) = + Self::update_pool_and_staker_rewards(&pool_info, &staker_info)?; + + // Check the staker has enough staked tokens. + ensure!(staker_info.amount >= amount, Error::<T>::NotEnoughTokens); + + // Unfreeze staker assets. + T::AssetsFreezer::decrease_frozen( + pool_info.staked_asset_id.clone(), + &FreezeReason::Staked.into(), + &staker, + amount, + )?; + + // Update Pools. + pool_info.total_tokens_staked.ensure_sub_assign(amount)?; + Pools::<T>::insert(pool_id, pool_info); + + // Update PoolStakers. + staker_info.amount.ensure_sub_assign(amount)?; + + if staker_info.amount.is_zero() && staker_info.rewards.is_zero() { + PoolStakers::<T>::remove(&pool_id, &staker); + } else { + PoolStakers::<T>::insert(&pool_id, &staker, staker_info); + } + + // Emit event. + Self::deposit_event(Event::Unstaked { caller, staker, pool_id, amount }); + + Ok(()) + } + + /// Harvest unclaimed pool rewards. + /// + /// Parameters: + /// - origin: must be the `staker` if the pool is still active. Otherwise, any account. + /// - pool_id: the pool to harvest from. + /// - staker: the account for which to harvest rewards. If `None`, the caller is used. + #[pallet::call_index(3)] + pub fn harvest_rewards( + origin: OriginFor<T>, + pool_id: PoolId, + staker: Option<T::AccountId>, + ) -> DispatchResult { + let caller = ensure_signed(origin)?; + let staker = staker.unwrap_or(caller.clone()); + + // Always start by updating the pool and staker rewards. + let pool_info = Pools::<T>::get(pool_id).ok_or(Error::<T>::NonExistentPool)?; + let now = frame_system::Pallet::<T>::block_number(); + ensure!(now > pool_info.expiry_block || caller == staker, BadOrigin); + + let staker_info = + PoolStakers::<T>::get(pool_id, &staker).ok_or(Error::<T>::NonExistentStaker)?; + let (pool_info, mut staker_info) = + Self::update_pool_and_staker_rewards(&pool_info, &staker_info)?; + + // Transfer unclaimed rewards from the pool to the staker. + T::Assets::transfer( + pool_info.reward_asset_id, + &pool_info.account, + &staker, + staker_info.rewards, + // Could kill the account, but only if the pool was already almost empty. + Preservation::Expendable, + )?; + + // Emit event. + Self::deposit_event(Event::RewardsHarvested { + caller, + staker: staker.clone(), + pool_id, + amount: staker_info.rewards, + }); + + // Reset staker rewards. + staker_info.rewards = 0u32.into(); + + if staker_info.amount.is_zero() { + PoolStakers::<T>::remove(&pool_id, &staker); + } else { + PoolStakers::<T>::insert(&pool_id, &staker, staker_info); + } + + Ok(()) + } + + /// Modify a pool reward rate. + /// + /// Currently the reward rate can only be increased. + /// + /// Only the pool admin may perform this operation. + #[pallet::call_index(4)] + pub fn set_pool_reward_rate_per_block( + origin: OriginFor<T>, + pool_id: PoolId, + new_reward_rate_per_block: T::Balance, + ) -> DispatchResult { + let caller = T::CreatePoolOrigin::ensure_origin(origin.clone()) + .or_else(|_| ensure_signed(origin))?; + + let pool_info = Pools::<T>::get(pool_id).ok_or(Error::<T>::NonExistentPool)?; + ensure!(pool_info.admin == caller, BadOrigin); + ensure!( + new_reward_rate_per_block > pool_info.reward_rate_per_block, + Error::<T>::RewardRateCut + ); + + // Always start by updating the pool rewards. + let rewards_per_token = Self::reward_per_token(&pool_info)?; + let mut pool_info = Self::update_pool_rewards(&pool_info, rewards_per_token)?; + + pool_info.reward_rate_per_block = new_reward_rate_per_block; + Pools::<T>::insert(pool_id, pool_info); + + Self::deposit_event(Event::PoolRewardRateModified { + pool_id, + new_reward_rate_per_block, + }); + + Ok(()) + } + + /// Modify a pool admin. + /// + /// Only the pool admin may perform this operation. + #[pallet::call_index(5)] + pub fn set_pool_admin( + origin: OriginFor<T>, + pool_id: PoolId, + new_admin: T::AccountId, + ) -> DispatchResult { + let caller = T::CreatePoolOrigin::ensure_origin(origin.clone()) + .or_else(|_| ensure_signed(origin))?; + + let mut pool_info = Pools::<T>::get(pool_id).ok_or(Error::<T>::NonExistentPool)?; + ensure!(pool_info.admin == caller, BadOrigin); + + pool_info.admin = new_admin.clone(); + Pools::<T>::insert(pool_id, pool_info); + + Self::deposit_event(Event::PoolAdminModified { pool_id, new_admin }); + + Ok(()) + } + + /// Set when the pool should expire. + /// + /// Currently the expiry block can only be extended. + /// + /// Only the pool admin may perform this operation. + #[pallet::call_index(6)] + pub fn set_pool_expiry_block( + origin: OriginFor<T>, + pool_id: PoolId, + new_expiry: DispatchTime<BlockNumberFor<T>>, + ) -> DispatchResult { + let caller = T::CreatePoolOrigin::ensure_origin(origin.clone()) + .or_else(|_| ensure_signed(origin))?; + + let new_expiry = new_expiry.evaluate(frame_system::Pallet::<T>::block_number()); + ensure!( + new_expiry > frame_system::Pallet::<T>::block_number(), + Error::<T>::ExpiryBlockMustBeInTheFuture + ); + + let pool_info = Pools::<T>::get(pool_id).ok_or(Error::<T>::NonExistentPool)?; + ensure!(pool_info.admin == caller, BadOrigin); + ensure!(new_expiry > pool_info.expiry_block, Error::<T>::ExpiryCut); + + // Always start by updating the pool rewards. + let reward_per_token = Self::reward_per_token(&pool_info)?; + let mut pool_info = Self::update_pool_rewards(&pool_info, reward_per_token)?; + + pool_info.expiry_block = new_expiry; + Pools::<T>::insert(pool_id, pool_info); + + Self::deposit_event(Event::PoolExpiryBlockModified { + pool_id, + new_expiry_block: new_expiry, + }); + + Ok(()) + } + + /// Convenience method to deposit reward tokens into a pool. + /// + /// This method is not strictly necessary (tokens could be transferred directly to the + /// pool pot address), but is provided for convenience so manual derivation of the + /// account id is not required. + #[pallet::call_index(7)] + pub fn deposit_reward_tokens( + origin: OriginFor<T>, + pool_id: PoolId, + amount: T::Balance, + ) -> DispatchResult { + let caller = ensure_signed(origin)?; + let pool_info = Pools::<T>::get(pool_id).ok_or(Error::<T>::NonExistentPool)?; + T::Assets::transfer( + pool_info.reward_asset_id, + &caller, + &pool_info.account, + amount, + Preservation::Preserve, + )?; + Ok(()) + } + + /// Cleanup a pool. + /// + /// Origin must be the pool admin. + /// + /// Cleanup storage, release any associated storage cost and return the remaining reward + /// tokens to the admin. + #[pallet::call_index(8)] + pub fn cleanup_pool(origin: OriginFor<T>, pool_id: PoolId) -> DispatchResult { + let who = ensure_signed(origin)?; + + let pool_info = Pools::<T>::get(pool_id).ok_or(Error::<T>::NonExistentPool)?; + ensure!(pool_info.admin == who, BadOrigin); + + let stakers = PoolStakers::<T>::iter_key_prefix(pool_id).next(); + ensure!(stakers.is_none(), Error::<T>::NonEmptyPool); + + let pool_balance = T::Assets::reducible_balance( + pool_info.reward_asset_id.clone(), + &pool_info.account, + Preservation::Expendable, + Fortitude::Polite, + ); + T::Assets::transfer( + pool_info.reward_asset_id, + &pool_info.account, + &pool_info.admin, + pool_balance, + Preservation::Expendable, + )?; + + if let Some((who, cost)) = PoolCost::<T>::take(pool_id) { + T::Consideration::drop(cost, &who)?; + } + + Pools::<T>::remove(pool_id); + + Self::deposit_event(Event::PoolCleanedUp { pool_id }); + + Ok(()) + } + } + + impl<T: Config> Pallet<T> { + /// The pool creation footprint. + /// + /// The footprint specifically accounts for the storage footprint of the pool's information + /// itself, excluding any potential storage footprint related to the stakers. + pub fn pool_creation_footprint() -> Footprint { + Footprint::from_mel::<(PoolId, PoolInfoFor<T>)>() + } + + /// Derive a pool account ID from the pool's ID. + pub fn pool_account_id(id: &PoolId) -> T::AccountId { + T::PalletId::get().into_sub_account_truncating(id) + } + + /// Computes update pool and staker reward state. + /// + /// Should be called prior to any operation involving a staker. + /// + /// Returns the updated pool and staker info. + /// + /// NOTE: this function has no side-effects. Side-effects such as storage modifications are + /// the responsibility of the caller. + pub fn update_pool_and_staker_rewards( + pool_info: &PoolInfoFor<T>, + staker_info: &PoolStakerInfo<T::Balance>, + ) -> Result<(PoolInfoFor<T>, PoolStakerInfo<T::Balance>), DispatchError> { + let reward_per_token = Self::reward_per_token(&pool_info)?; + let pool_info = Self::update_pool_rewards(pool_info, reward_per_token)?; + + let mut new_staker_info = staker_info.clone(); + new_staker_info.rewards = Self::derive_rewards(&staker_info, &reward_per_token)?; + new_staker_info.reward_per_token_paid = pool_info.reward_per_token_stored; + return Ok((pool_info, new_staker_info)); + } + + /// Computes update pool reward state. + /// + /// Should be called every time the pool is adjusted, and a staker is not involved. + /// + /// Returns the updated pool and staker info. + /// + /// NOTE: this function has no side-effects. Side-effects such as storage modifications are + /// the responsibility of the caller. + pub fn update_pool_rewards( + pool_info: &PoolInfoFor<T>, + reward_per_token: T::Balance, + ) -> Result<PoolInfoFor<T>, DispatchError> { + let mut new_pool_info = pool_info.clone(); + new_pool_info.last_update_block = frame_system::Pallet::<T>::block_number(); + new_pool_info.reward_per_token_stored = reward_per_token; + + Ok(new_pool_info) + } + + /// Derives the current reward per token for this pool. + fn reward_per_token(pool_info: &PoolInfoFor<T>) -> Result<T::Balance, DispatchError> { + if pool_info.total_tokens_staked.is_zero() { + return Ok(pool_info.reward_per_token_stored) + } + + let rewardable_blocks_elapsed: u32 = + match Self::last_block_reward_applicable(pool_info.expiry_block) + .ensure_sub(pool_info.last_update_block)? + .try_into() + { + Ok(b) => b, + Err(_) => return Err(Error::<T>::BlockNumberConversionError.into()), + }; + + Ok(pool_info.reward_per_token_stored.ensure_add( + pool_info + .reward_rate_per_block + .ensure_mul(rewardable_blocks_elapsed.into())? + .ensure_mul(PRECISION_SCALING_FACTOR.into())? + .ensure_div(pool_info.total_tokens_staked)?, + )?) + } + + /// Derives the amount of rewards earned by a staker. + /// + /// This is a helper function for `update_pool_rewards` and should not be called directly. + fn derive_rewards( + staker_info: &PoolStakerInfo<T::Balance>, + reward_per_token: &T::Balance, + ) -> Result<T::Balance, DispatchError> { + Ok(staker_info + .amount + .ensure_mul(reward_per_token.ensure_sub(staker_info.reward_per_token_paid)?)? + .ensure_div(PRECISION_SCALING_FACTOR.into())? + .ensure_add(staker_info.rewards)?) + } + + fn last_block_reward_applicable(pool_expiry_block: BlockNumberFor<T>) -> BlockNumberFor<T> { + let now = frame_system::Pallet::<T>::block_number(); + if now < pool_expiry_block { + now + } else { + pool_expiry_block + } + } + } +} diff --git a/substrate/frame/asset-rewards/src/mock.rs b/substrate/frame/asset-rewards/src/mock.rs new file mode 100644 index 0000000000000000000000000000000000000000..87c8a8a0dea0b44c23baa1ed2fdfabd9b85eb999 --- /dev/null +++ b/substrate/frame/asset-rewards/src/mock.rs @@ -0,0 +1,221 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Test environment for Asset Rewards pallet. + +use super::*; +use crate as pallet_asset_rewards; +use core::default::Default; +use frame_support::{ + construct_runtime, derive_impl, + instances::Instance1, + parameter_types, + traits::{ + tokens::fungible::{HoldConsideration, NativeFromLeft, NativeOrWithId, UnionOf}, + AsEnsureOriginWithArg, ConstU128, ConstU32, EnsureOrigin, LinearStoragePrice, + }, + PalletId, +}; +use frame_system::EnsureSigned; +use sp_runtime::{traits::IdentityLookup, BuildStorage}; + +#[cfg(feature = "runtime-benchmarks")] +use self::benchmarking::BenchmarkHelper; + +type Block = frame_system::mocking::MockBlock<MockRuntime>; + +construct_runtime!( + pub enum MockRuntime + { + System: frame_system, + Balances: pallet_balances, + Assets: pallet_assets::<Instance1>, + AssetsFreezer: pallet_assets_freezer::<Instance1>, + StakingRewards: pallet_asset_rewards, + } +); + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for MockRuntime { + type AccountId = u128; + type Lookup = IdentityLookup<Self::AccountId>; + type Block = Block; + type AccountData = pallet_balances::AccountData<u128>; +} + +impl pallet_balances::Config for MockRuntime { + type Balance = u128; + type DustRemoval = (); + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposit = ConstU128<100>; + type AccountStore = System; + type WeightInfo = (); + type MaxLocks = (); + type MaxReserves = ConstU32<50>; + type ReserveIdentifier = [u8; 8]; + type FreezeIdentifier = RuntimeFreezeReason; + type MaxFreezes = ConstU32<50>; + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = RuntimeFreezeReason; + type DoneSlashHandler = (); +} + +impl pallet_assets::Config<Instance1> for MockRuntime { + type RuntimeEvent = RuntimeEvent; + type Balance = u128; + type RemoveItemsLimit = ConstU32<1000>; + type AssetId = u32; + type AssetIdParameter = u32; + type Currency = Balances; + type CreateOrigin = AsEnsureOriginWithArg<EnsureSigned<Self::AccountId>>; + type ForceOrigin = frame_system::EnsureRoot<Self::AccountId>; + type AssetDeposit = ConstU128<1>; + type AssetAccountDeposit = ConstU128<10>; + type MetadataDepositBase = ConstU128<1>; + type MetadataDepositPerByte = ConstU128<1>; + type ApprovalDeposit = ConstU128<1>; + type StringLimit = ConstU32<50>; + type Freezer = AssetsFreezer; + type Extra = (); + type WeightInfo = (); + type CallbackHandle = (); + pallet_assets::runtime_benchmarks_enabled! { + type BenchmarkHelper = (); + } +} + +parameter_types! { + pub const StakingRewardsPalletId: PalletId = PalletId(*b"py/stkrd"); + pub const Native: NativeOrWithId<u32> = NativeOrWithId::Native; + pub const PermissionedAccountId: u128 = 0; +} + +/// Give Root Origin permission to create pools. +pub struct MockPermissionedOrigin; +impl EnsureOrigin<RuntimeOrigin> for MockPermissionedOrigin { + type Success = <MockRuntime as frame_system::Config>::AccountId; + + fn try_origin(origin: RuntimeOrigin) -> Result<Self::Success, RuntimeOrigin> { + match origin.clone().into() { + Ok(frame_system::RawOrigin::Root) => Ok(PermissionedAccountId::get()), + _ => Err(origin), + } + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin() -> Result<RuntimeOrigin, ()> { + Ok(RuntimeOrigin::root()) + } +} + +/// Allow Freezes for the `Assets` pallet +impl pallet_assets_freezer::Config<pallet_assets_freezer::Instance1> for MockRuntime { + type RuntimeFreezeReason = RuntimeFreezeReason; + type RuntimeEvent = RuntimeEvent; +} + +pub type NativeAndAssets = UnionOf<Balances, Assets, NativeFromLeft, NativeOrWithId<u32>, u128>; + +pub type NativeAndAssetsFreezer = + UnionOf<Balances, AssetsFreezer, NativeFromLeft, NativeOrWithId<u32>, u128>; + +#[cfg(feature = "runtime-benchmarks")] +pub struct AssetRewardsBenchmarkHelper; +#[cfg(feature = "runtime-benchmarks")] +impl BenchmarkHelper<NativeOrWithId<u32>> for AssetRewardsBenchmarkHelper { + fn staked_asset() -> NativeOrWithId<u32> { + NativeOrWithId::<u32>::WithId(101) + } + fn reward_asset() -> NativeOrWithId<u32> { + NativeOrWithId::<u32>::WithId(102) + } +} + +parameter_types! { + pub const CreationHoldReason: RuntimeHoldReason = + RuntimeHoldReason::StakingRewards(pallet_asset_rewards::HoldReason::PoolCreation); +} + +impl Config for MockRuntime { + type RuntimeEvent = RuntimeEvent; + type AssetId = NativeOrWithId<u32>; + type Balance = <Self as pallet_balances::Config>::Balance; + type Assets = NativeAndAssets; + type AssetsFreezer = NativeAndAssetsFreezer; + type PalletId = StakingRewardsPalletId; + type CreatePoolOrigin = MockPermissionedOrigin; + type WeightInfo = (); + type RuntimeFreezeReason = RuntimeFreezeReason; + type Consideration = HoldConsideration< + u128, + Balances, + CreationHoldReason, + LinearStoragePrice<ConstU128<100>, ConstU128<0>, u128>, + >; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = AssetRewardsBenchmarkHelper; +} + +pub(crate) fn new_test_ext() -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::<MockRuntime>::default().build_storage().unwrap(); + + pallet_assets::GenesisConfig::<MockRuntime, Instance1> { + // Genesis assets: id, owner, is_sufficient, min_balance + // pub assets: Vec<(T::AssetId, T::AccountId, bool, T::Balance)>, + assets: vec![(1, 1, true, 1), (10, 1, true, 1), (20, 1, true, 1)], + // Genesis metadata: id, name, symbol, decimals + // pub metadata: Vec<(T::AssetId, Vec<u8>, Vec<u8>, u8)>, + metadata: vec![ + (1, b"test".to_vec(), b"TST".to_vec(), 18), + (10, b"test10".to_vec(), b"T10".to_vec(), 18), + (20, b"test20".to_vec(), b"T20".to_vec(), 18), + ], + // Genesis accounts: id, account_id, balance + // pub accounts: Vec<(T::AssetId, T::AccountId, T::Balance)>, + accounts: vec![ + (1, 1, 10000), + (1, 2, 20000), + (1, 3, 30000), + (1, 4, 40000), + (1, 10, 40000), + (1, 20, 40000), + ], + next_asset_id: None, + } + .assimilate_storage(&mut t) + .unwrap(); + + let pool_zero_account_id = 31086825966906540362769395565; + pallet_balances::GenesisConfig::<MockRuntime> { + balances: vec![ + (0, 10000), + (1, 10000), + (2, 20000), + (3, 30000), + (4, 40000), + (10, 40000), + (20, 40000), + (pool_zero_account_id, 100_000), // Top up the default pool account id + ], + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext +} diff --git a/substrate/frame/asset-rewards/src/tests.rs b/substrate/frame/asset-rewards/src/tests.rs new file mode 100644 index 0000000000000000000000000000000000000000..399d6a54c939293e4fdc2c87118d85596e701374 --- /dev/null +++ b/substrate/frame/asset-rewards/src/tests.rs @@ -0,0 +1,1457 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{mock::*, *}; +use frame_support::{ + assert_err, assert_noop, assert_ok, hypothetically, + traits::{ + fungible, + fungible::NativeOrWithId, + fungibles, + tokens::{Fortitude, Preservation}, + }, +}; +use sp_runtime::{traits::BadOrigin, ArithmeticError, TokenError}; + +const DEFAULT_STAKED_ASSET_ID: NativeOrWithId<u32> = NativeOrWithId::<u32>::WithId(1); +const DEFAULT_REWARD_ASSET_ID: NativeOrWithId<u32> = NativeOrWithId::<u32>::Native; +const DEFAULT_REWARD_RATE_PER_BLOCK: u128 = 100; +const DEFAULT_EXPIRE_AFTER: u64 = 200; +const DEFAULT_ADMIN: u128 = 1; + +/// Creates a basic pool with values: +/// - Staking asset: 1 +/// - Reward asset: Native +/// - Reward rate per block: 100 +/// - Lifetime: 100 +/// - Admin: 1 +/// +/// Useful to reduce boilerplate in tests when it's not important to customise or reuse pool +/// params. +pub fn create_default_pool() { + assert_ok!(StakingRewards::create_pool( + RuntimeOrigin::root(), + Box::new(DEFAULT_STAKED_ASSET_ID.clone()), + Box::new(DEFAULT_REWARD_ASSET_ID.clone()), + DEFAULT_REWARD_RATE_PER_BLOCK, + DispatchTime::After(DEFAULT_EXPIRE_AFTER), + Some(DEFAULT_ADMIN) + )); +} + +/// The same as [`create_default_pool`], but with the admin parameter set to the creator. +pub fn create_default_pool_permissioned_admin() { + assert_ok!(StakingRewards::create_pool( + RuntimeOrigin::root(), + Box::new(DEFAULT_STAKED_ASSET_ID.clone()), + Box::new(DEFAULT_REWARD_ASSET_ID.clone()), + DEFAULT_REWARD_RATE_PER_BLOCK, + DispatchTime::After(DEFAULT_EXPIRE_AFTER), + Some(PermissionedAccountId::get()), + )); +} + +fn assert_hypothetically_earned( + staker: u128, + expected_earned: u128, + pool_id: u32, + reward_asset_id: NativeOrWithId<u32>, +) { + hypothetically!({ + // Get the pre-harvest balance. + let balance_before: <MockRuntime as Config>::Balance = + <<MockRuntime as Config>::Assets>::balance(reward_asset_id.clone(), &staker); + + // Harvest the rewards. + assert_ok!(StakingRewards::harvest_rewards(RuntimeOrigin::signed(staker), pool_id, None),); + + // Sanity check: staker rewards are reset to 0 if some `amount` is still staked, otherwise + // the storage item removed. + if let Some(staker_pool) = PoolStakers::<MockRuntime>::get(pool_id, staker) { + assert!(staker_pool.rewards == 0); + assert!(staker_pool.amount > 0); + } + + // Check that the staker has earned the expected amount. + let balance_after = + <<MockRuntime as Config>::Assets>::balance(reward_asset_id.clone(), &staker); + assert_eq!(balance_after - balance_before, expected_earned); + }); +} + +fn events() -> Vec<Event<MockRuntime>> { + let result = System::events() + .into_iter() + .map(|r| r.event) + .filter_map(|e| { + if let mock::RuntimeEvent::StakingRewards(inner) = e { + Some(inner) + } else { + None + } + }) + .collect(); + + System::reset_events(); + + result +} + +fn pools() -> Vec<(u32, PoolInfo<u128, NativeOrWithId<u32>, u128, u64>)> { + Pools::<MockRuntime>::iter().collect() +} + +mod create_pool { + use super::*; + + #[test] + fn success() { + new_test_ext().execute_with(|| { + assert_eq!(NextPoolId::<MockRuntime>::get(), 0); + + System::set_block_number(10); + let expected_expiry_block = DEFAULT_EXPIRE_AFTER + 10; + + // Create a pool with default values, and no admin override so [`PermissionedAccountId`] + // is admin. + assert_ok!(StakingRewards::create_pool( + RuntimeOrigin::root(), + Box::new(DEFAULT_STAKED_ASSET_ID), + Box::new(DEFAULT_REWARD_ASSET_ID), + DEFAULT_REWARD_RATE_PER_BLOCK, + DispatchTime::After(DEFAULT_EXPIRE_AFTER), + Some(PermissionedAccountId::get()) + )); + + // Event is emitted. + assert_eq!( + events(), + [Event::<MockRuntime>::PoolCreated { + creator: PermissionedAccountId::get(), + pool_id: 0, + staked_asset_id: DEFAULT_STAKED_ASSET_ID, + reward_asset_id: DEFAULT_REWARD_ASSET_ID, + reward_rate_per_block: DEFAULT_REWARD_RATE_PER_BLOCK, + expiry_block: expected_expiry_block, + admin: PermissionedAccountId::get(), + }] + ); + + // State is updated correctly. + assert_eq!(NextPoolId::<MockRuntime>::get(), 1); + assert_eq!( + pools(), + vec![( + 0, + PoolInfo { + staked_asset_id: DEFAULT_STAKED_ASSET_ID, + reward_asset_id: DEFAULT_REWARD_ASSET_ID, + reward_rate_per_block: DEFAULT_REWARD_RATE_PER_BLOCK, + expiry_block: expected_expiry_block, + admin: PermissionedAccountId::get(), + total_tokens_staked: 0, + reward_per_token_stored: 0, + last_update_block: 0, + account: StakingRewards::pool_account_id(&0), + } + )] + ); + + // Create another pool with explicit admin and other overrides. + let admin = 2; + let staked_asset_id = NativeOrWithId::<u32>::WithId(10); + let reward_asset_id = NativeOrWithId::<u32>::WithId(20); + let reward_rate_per_block = 250; + let expiry_block = 500; + let expected_expiry_block = expiry_block + 10; + assert_ok!(StakingRewards::create_pool( + RuntimeOrigin::root(), + Box::new(staked_asset_id.clone()), + Box::new(reward_asset_id.clone()), + reward_rate_per_block, + DispatchTime::After(expiry_block), + Some(admin) + )); + + // Event is emitted. + assert_eq!( + events(), + [Event::<MockRuntime>::PoolCreated { + creator: PermissionedAccountId::get(), + pool_id: 1, + staked_asset_id: staked_asset_id.clone(), + reward_asset_id: reward_asset_id.clone(), + reward_rate_per_block, + admin, + expiry_block: expected_expiry_block, + }] + ); + + // State is updated correctly. + assert_eq!(NextPoolId::<MockRuntime>::get(), 2); + assert_eq!( + pools(), + vec![ + ( + 0, + PoolInfo { + staked_asset_id: DEFAULT_STAKED_ASSET_ID, + reward_asset_id: DEFAULT_REWARD_ASSET_ID, + reward_rate_per_block: DEFAULT_REWARD_RATE_PER_BLOCK, + admin: PermissionedAccountId::get(), + expiry_block: DEFAULT_EXPIRE_AFTER + 10, + total_tokens_staked: 0, + reward_per_token_stored: 0, + last_update_block: 0, + account: StakingRewards::pool_account_id(&0), + } + ), + ( + 1, + PoolInfo { + staked_asset_id, + reward_asset_id, + reward_rate_per_block, + admin, + total_tokens_staked: 0, + expiry_block: expected_expiry_block, + reward_per_token_stored: 0, + last_update_block: 0, + account: StakingRewards::pool_account_id(&1), + } + ) + ] + ); + }); + } + + #[test] + fn success_same_assets() { + new_test_ext().execute_with(|| { + assert_eq!(NextPoolId::<MockRuntime>::get(), 0); + + System::set_block_number(10); + let expected_expiry_block = DEFAULT_EXPIRE_AFTER + 10; + + // Create a pool with the same staking and reward asset. + let asset = NativeOrWithId::<u32>::Native; + assert_ok!(StakingRewards::create_pool( + RuntimeOrigin::root(), + Box::new(asset.clone()), + Box::new(asset.clone()), + DEFAULT_REWARD_RATE_PER_BLOCK, + DispatchTime::After(DEFAULT_EXPIRE_AFTER), + Some(PermissionedAccountId::get()) + )); + + // Event is emitted. + assert_eq!( + events(), + [Event::<MockRuntime>::PoolCreated { + creator: PermissionedAccountId::get(), + pool_id: 0, + staked_asset_id: asset.clone(), + reward_asset_id: asset.clone(), + reward_rate_per_block: DEFAULT_REWARD_RATE_PER_BLOCK, + expiry_block: expected_expiry_block, + admin: PermissionedAccountId::get(), + }] + ); + + // State is updated correctly. + assert_eq!(NextPoolId::<MockRuntime>::get(), 1); + assert_eq!( + pools(), + vec![( + 0, + PoolInfo { + staked_asset_id: asset.clone(), + reward_asset_id: asset, + reward_rate_per_block: DEFAULT_REWARD_RATE_PER_BLOCK, + expiry_block: expected_expiry_block, + admin: PermissionedAccountId::get(), + total_tokens_staked: 0, + reward_per_token_stored: 0, + last_update_block: 0, + account: StakingRewards::pool_account_id(&0), + } + )] + ); + }) + } + + #[test] + fn fails_for_non_existent_asset() { + new_test_ext().execute_with(|| { + let valid_asset = NativeOrWithId::<u32>::WithId(1); + let invalid_asset = NativeOrWithId::<u32>::WithId(200); + + assert_err!( + StakingRewards::create_pool( + RuntimeOrigin::root(), + Box::new(valid_asset.clone()), + Box::new(invalid_asset.clone()), + 10, + DispatchTime::After(10u64), + None + ), + Error::<MockRuntime>::NonExistentAsset + ); + + assert_err!( + StakingRewards::create_pool( + RuntimeOrigin::root(), + Box::new(invalid_asset.clone()), + Box::new(valid_asset.clone()), + 10, + DispatchTime::After(10u64), + None + ), + Error::<MockRuntime>::NonExistentAsset + ); + + assert_err!( + StakingRewards::create_pool( + RuntimeOrigin::root(), + Box::new(invalid_asset.clone()), + Box::new(invalid_asset.clone()), + 10, + DispatchTime::After(10u64), + None + ), + Error::<MockRuntime>::NonExistentAsset + ); + }) + } + + #[test] + fn fails_for_not_permissioned() { + new_test_ext().execute_with(|| { + let user = 100; + let staked_asset_id = NativeOrWithId::<u32>::Native; + let reward_asset_id = NativeOrWithId::<u32>::WithId(1); + let reward_rate_per_block = 100; + let expiry_block = 100u64; + assert_err!( + StakingRewards::create_pool( + RuntimeOrigin::signed(user), + Box::new(staked_asset_id.clone()), + Box::new(reward_asset_id.clone()), + reward_rate_per_block, + DispatchTime::After(expiry_block), + None + ), + BadOrigin + ); + }); + } + + #[test] + fn create_pool_with_caller_admin() { + new_test_ext().execute_with(|| { + assert_eq!(NextPoolId::<MockRuntime>::get(), 0); + + System::set_block_number(10); + let expected_expiry_block = DEFAULT_EXPIRE_AFTER + 10; + + assert_ok!(StakingRewards::create_pool( + RuntimeOrigin::root(), + Box::new(DEFAULT_STAKED_ASSET_ID), + Box::new(DEFAULT_REWARD_ASSET_ID), + DEFAULT_REWARD_RATE_PER_BLOCK, + DispatchTime::After(DEFAULT_EXPIRE_AFTER), + None, + )); + + assert_eq!( + events(), + [Event::<MockRuntime>::PoolCreated { + creator: PermissionedAccountId::get(), + pool_id: 0, + staked_asset_id: DEFAULT_STAKED_ASSET_ID, + reward_asset_id: DEFAULT_REWARD_ASSET_ID, + reward_rate_per_block: DEFAULT_REWARD_RATE_PER_BLOCK, + expiry_block: expected_expiry_block, + admin: PermissionedAccountId::get(), + }] + ); + + assert_eq!(Pools::<MockRuntime>::get(0).unwrap().admin, PermissionedAccountId::get()); + }); + } +} + +mod stake { + use super::*; + + #[test] + fn success() { + new_test_ext().execute_with(|| { + let user = 1; + create_default_pool(); + let pool_id = 0; + let initial_balance = <Assets as fungibles::Inspect<u128>>::reducible_balance( + 1, + &user, + Preservation::Expendable, + Fortitude::Force, + ); + + // User stakes tokens + assert_ok!(StakingRewards::stake(RuntimeOrigin::signed(user), pool_id, 1000)); + + // Check that the user's staked amount is updated + assert_eq!(PoolStakers::<MockRuntime>::get(pool_id, user).unwrap().amount, 1000); + + // Event is emitted. + assert_eq!( + *events().last().unwrap(), + Event::<MockRuntime>::Staked { staker: user, amount: 1000, pool_id: 0 } + ); + + // Check that the pool's total tokens staked is updated + assert_eq!(Pools::<MockRuntime>::get(pool_id).unwrap().total_tokens_staked, 1000); + + // Check user's frozen balance is updated + assert_eq!( + <Assets as fungibles::Inspect<u128>>::reducible_balance( + 1, + &user, + Preservation::Expendable, + Fortitude::Force, + ), + // - extra 1 for ed + initial_balance - 1000 - 1 + ); + + // User stakes more tokens + assert_ok!(StakingRewards::stake(RuntimeOrigin::signed(user), pool_id, 500)); + + // Event is emitted. + assert_eq!( + *events().last().unwrap(), + Event::<MockRuntime>::Staked { staker: user, amount: 500, pool_id: 0 } + ); + + // Check that the user's staked amount is updated + assert_eq!(PoolStakers::<MockRuntime>::get(pool_id, user).unwrap().amount, 1000 + 500); + + // Check that the pool's total tokens staked is updated + assert_eq!(Pools::<MockRuntime>::get(pool_id).unwrap().total_tokens_staked, 1000 + 500); + + assert_eq!( + <Assets as fungibles::Inspect<u128>>::reducible_balance( + 1, + &user, + Preservation::Expendable, + Fortitude::Force, + ), + // - extra 1 for ed + initial_balance - 1500 - 1 + ); + + // Event is emitted. + assert_eq!(events(), []); + }); + } + + #[test] + fn fails_for_non_existent_pool() { + new_test_ext().execute_with(|| { + let user = 1; + assert_err!( + StakingRewards::stake(RuntimeOrigin::signed(user), 999, 1000), + Error::<MockRuntime>::NonExistentPool + ); + }); + } + + #[test] + fn fails_for_insufficient_balance() { + new_test_ext().execute_with(|| { + let user = 1; + create_default_pool(); + let pool_id = 0; + let initial_balance = <Assets as fungibles::Inspect<u128>>::reducible_balance( + 1, + &user, + Preservation::Expendable, + Fortitude::Force, + ); + assert_err!( + StakingRewards::stake(RuntimeOrigin::signed(user), pool_id, initial_balance + 1), + TokenError::FundsUnavailable, + ); + }) + } +} + +mod unstake { + use super::*; + + #[test] + fn success() { + new_test_ext().execute_with(|| { + let user = 1; + create_default_pool(); + let pool_id = 0; + + // User stakes tokens + assert_ok!(StakingRewards::stake(RuntimeOrigin::signed(user), pool_id, 1000)); + + // User unstakes tokens + assert_ok!(StakingRewards::unstake(RuntimeOrigin::signed(user), pool_id, 500, None)); + + // Event is emitted. + assert_eq!( + *events().last().unwrap(), + Event::<MockRuntime>::Unstaked { + caller: user, + staker: user, + amount: 500, + pool_id: 0 + } + ); + + // Check that the user's staked amount is updated + assert_eq!(PoolStakers::<MockRuntime>::get(pool_id, user).unwrap().amount, 500); + + // Check that the pool's total tokens staked is updated + assert_eq!(Pools::<MockRuntime>::get(pool_id).unwrap().total_tokens_staked, 500); + + // User unstakes remaining tokens + assert_ok!(StakingRewards::unstake(RuntimeOrigin::signed(user), pool_id, 500, None)); + + // Check that the storage items is removed since stake amount and rewards are zero. + assert!(PoolStakers::<MockRuntime>::get(pool_id, user).is_none()); + + // Check that the pool's total tokens staked is zero + assert_eq!(Pools::<MockRuntime>::get(pool_id).unwrap().total_tokens_staked, 0); + }); + } + + #[test] + fn unstake_for_other() { + new_test_ext().execute_with(|| { + let staker = 1; + let caller = 2; + let pool_id = 0; + let init_block = System::block_number(); + + create_default_pool(); + + // User stakes tokens + assert_ok!(StakingRewards::stake(RuntimeOrigin::signed(staker), pool_id, 1000)); + + // Fails to unstake for other since pool is still active + assert_noop!( + StakingRewards::unstake(RuntimeOrigin::signed(caller), pool_id, 500, Some(staker)), + BadOrigin, + ); + + System::set_block_number(init_block + DEFAULT_EXPIRE_AFTER + 1); + + assert_ok!(StakingRewards::unstake( + RuntimeOrigin::signed(caller), + pool_id, + 500, + Some(staker) + )); + + // Event is emitted. + assert_eq!( + *events().last().unwrap(), + Event::<MockRuntime>::Unstaked { caller, staker, amount: 500, pool_id: 0 } + ); + }); + } + + #[test] + fn fails_for_non_existent_pool() { + new_test_ext().execute_with(|| { + let user = 1; + let non_existent_pool_id = 999; + + // User tries to unstake tokens from a non-existent pool + assert_err!( + StakingRewards::unstake( + RuntimeOrigin::signed(user), + non_existent_pool_id, + 500, + None + ), + Error::<MockRuntime>::NonExistentPool + ); + }); + } + + #[test] + fn fails_for_insufficient_staked_amount() { + new_test_ext().execute_with(|| { + let user = 1; + create_default_pool(); + let pool_id = 0; + + // User stakes tokens + assert_ok!(StakingRewards::stake(RuntimeOrigin::signed(user), pool_id, 1000)); + + // User tries to unstake more tokens than they have staked + assert_err!( + StakingRewards::unstake(RuntimeOrigin::signed(user), pool_id, 1500, None), + Error::<MockRuntime>::NotEnoughTokens + ); + }); + } +} + +mod harvest_rewards { + use super::*; + + #[test] + fn success() { + new_test_ext().execute_with(|| { + let staker = 1; + let pool_id = 0; + let reward_asset_id = NativeOrWithId::<u32>::Native; + create_default_pool(); + + // Stake + System::set_block_number(10); + assert_ok!(StakingRewards::stake(RuntimeOrigin::signed(staker), pool_id, 1000)); + + // Harvest + System::set_block_number(20); + let balance_before: <MockRuntime as Config>::Balance = + <<MockRuntime as Config>::Assets>::balance(reward_asset_id.clone(), &staker); + assert_ok!(StakingRewards::harvest_rewards( + RuntimeOrigin::signed(staker), + pool_id, + None + )); + let balance_after = + <<MockRuntime as Config>::Assets>::balance(reward_asset_id.clone(), &staker); + + // Assert + assert_eq!( + balance_after - balance_before, + 10 * Pools::<MockRuntime>::get(pool_id).unwrap().reward_rate_per_block + ); + assert_eq!( + *events().last().unwrap(), + Event::<MockRuntime>::RewardsHarvested { + caller: staker, + staker, + pool_id, + amount: 10 * Pools::<MockRuntime>::get(pool_id).unwrap().reward_rate_per_block + } + ); + }); + } + + #[test] + fn harvest_for_other() { + new_test_ext().execute_with(|| { + let caller = 2; + let staker = 1; + let pool_id = 0; + let init_block = System::block_number(); + + create_default_pool(); + + // Stake + System::set_block_number(10); + assert_ok!(StakingRewards::stake(RuntimeOrigin::signed(staker), pool_id, 1000)); + + System::set_block_number(20); + + // Fails to harvest for staker since pool is still active + assert_noop!( + StakingRewards::harvest_rewards( + RuntimeOrigin::signed(caller), + pool_id, + Some(staker) + ), + BadOrigin + ); + + System::set_block_number(init_block + DEFAULT_EXPIRE_AFTER + 1); + + // Harvest for staker + assert_ok!(StakingRewards::harvest_rewards( + RuntimeOrigin::signed(caller), + pool_id, + Some(staker), + )); + + assert!(matches!( + events().last().unwrap(), + Event::<MockRuntime>::RewardsHarvested { + caller, + staker, + pool_id, + .. + } if caller == caller && staker == staker && pool_id == pool_id + )); + }); + } + + #[test] + fn fails_for_non_existent_staker() { + new_test_ext().execute_with(|| { + let non_existent_staker = 999; + + create_default_pool(); + assert_err!( + StakingRewards::harvest_rewards( + RuntimeOrigin::signed(non_existent_staker), + 0, + None + ), + Error::<MockRuntime>::NonExistentStaker + ); + }); + } + + #[test] + fn fails_for_non_existent_pool() { + new_test_ext().execute_with(|| { + let staker = 1; + let non_existent_pool_id = 999; + + assert_err!( + StakingRewards::harvest_rewards( + RuntimeOrigin::signed(staker), + non_existent_pool_id, + None, + ), + Error::<MockRuntime>::NonExistentPool + ); + }); + } +} + +mod set_pool_admin { + use super::*; + + #[test] + fn success_signed_admin() { + new_test_ext().execute_with(|| { + let admin = 1; + let new_admin = 2; + let pool_id = 0; + create_default_pool(); + + // Modify the pool admin + assert_ok!(StakingRewards::set_pool_admin( + RuntimeOrigin::signed(admin), + pool_id, + new_admin, + )); + + // Check state + assert_eq!( + *events().last().unwrap(), + Event::<MockRuntime>::PoolAdminModified { pool_id, new_admin } + ); + assert_eq!(Pools::<MockRuntime>::get(pool_id).unwrap().admin, new_admin); + }); + } + + #[test] + fn success_permissioned_admin() { + new_test_ext().execute_with(|| { + let new_admin = 2; + let pool_id = 0; + create_default_pool_permissioned_admin(); + + // Modify the pool admin + assert_ok!(StakingRewards::set_pool_admin(RuntimeOrigin::root(), pool_id, new_admin)); + + // Check state + assert_eq!( + *events().last().unwrap(), + Event::<MockRuntime>::PoolAdminModified { pool_id, new_admin } + ); + assert_eq!(Pools::<MockRuntime>::get(pool_id).unwrap().admin, new_admin); + }); + } + + #[test] + fn fails_for_non_existent_pool() { + new_test_ext().execute_with(|| { + let admin = 1; + let new_admin = 2; + let non_existent_pool_id = 999; + + assert_err!( + StakingRewards::set_pool_admin( + RuntimeOrigin::signed(admin), + non_existent_pool_id, + new_admin + ), + Error::<MockRuntime>::NonExistentPool + ); + }); + } + + #[test] + fn fails_for_non_admin() { + new_test_ext().execute_with(|| { + let new_admin = 2; + let non_admin = 3; + let pool_id = 0; + create_default_pool(); + + assert_err!( + StakingRewards::set_pool_admin( + RuntimeOrigin::signed(non_admin), + pool_id, + new_admin + ), + BadOrigin + ); + }); + } +} + +mod set_pool_expiry_block { + use super::*; + + #[test] + fn success_permissioned_admin() { + new_test_ext().execute_with(|| { + let pool_id = 0; + let new_expiry_block = System::block_number() + DEFAULT_EXPIRE_AFTER + 1u64; + create_default_pool_permissioned_admin(); + + assert_ok!(StakingRewards::set_pool_expiry_block( + RuntimeOrigin::root(), + pool_id, + DispatchTime::At(new_expiry_block), + )); + + // Check state + assert_eq!(Pools::<MockRuntime>::get(pool_id).unwrap().expiry_block, new_expiry_block); + assert_eq!( + *events().last().unwrap(), + Event::<MockRuntime>::PoolExpiryBlockModified { pool_id, new_expiry_block } + ); + }); + } + + #[test] + fn success_signed_admin() { + new_test_ext().execute_with(|| { + let admin = 1; + let pool_id = 0; + let new_expiry_block = System::block_number() + DEFAULT_EXPIRE_AFTER + 1u64; + create_default_pool(); + + assert_ok!(StakingRewards::set_pool_expiry_block( + RuntimeOrigin::signed(admin), + pool_id, + DispatchTime::At(new_expiry_block) + )); + + // Check state + assert_eq!(Pools::<MockRuntime>::get(pool_id).unwrap().expiry_block, new_expiry_block); + assert_eq!( + *events().last().unwrap(), + Event::<MockRuntime>::PoolExpiryBlockModified { pool_id, new_expiry_block } + ); + }); + } + + #[test] + fn extends_reward_accumulation() { + new_test_ext().execute_with(|| { + let admin = 1; + let staker = 2; + let pool_id = 0; + let new_expiry_block = 300u64; + System::set_block_number(10); + create_default_pool(); + + // Regular reward accumulation + assert_ok!(StakingRewards::stake(RuntimeOrigin::signed(staker), pool_id, 1000)); + System::set_block_number(20); + assert_hypothetically_earned( + staker, + DEFAULT_REWARD_RATE_PER_BLOCK * 10, + pool_id, + NativeOrWithId::<u32>::Native, + ); + + // Expiry was block 210, so earned 200 at block 250 + System::set_block_number(250); + assert_hypothetically_earned( + staker, + DEFAULT_REWARD_RATE_PER_BLOCK * 200, + pool_id, + NativeOrWithId::<u32>::Native, + ); + + // Extend expiry 50 more blocks + assert_ok!(StakingRewards::set_pool_expiry_block( + RuntimeOrigin::signed(admin), + pool_id, + DispatchTime::At(new_expiry_block) + )); + System::set_block_number(350); + + // Staker has been in pool with rewards active for 250 blocks total + assert_hypothetically_earned( + staker, + DEFAULT_REWARD_RATE_PER_BLOCK * 250, + pool_id, + NativeOrWithId::<u32>::Native, + ); + }); + } + + #[test] + fn fails_to_cutback_expiration() { + new_test_ext().execute_with(|| { + let admin = 1; + let pool_id = 0; + create_default_pool(); + + assert_noop!( + StakingRewards::set_pool_expiry_block( + RuntimeOrigin::signed(admin), + pool_id, + DispatchTime::After(30) + ), + Error::<MockRuntime>::ExpiryCut + ); + }); + } + + #[test] + fn fails_for_non_existent_pool() { + new_test_ext().execute_with(|| { + let admin = 1; + let non_existent_pool_id = 999; + let new_expiry_block = 200u64; + + assert_err!( + StakingRewards::set_pool_expiry_block( + RuntimeOrigin::signed(admin), + non_existent_pool_id, + DispatchTime::After(new_expiry_block) + ), + Error::<MockRuntime>::NonExistentPool + ); + }); + } + + #[test] + fn fails_for_non_admin() { + new_test_ext().execute_with(|| { + let non_admin = 2; + let pool_id = 0; + let new_expiry_block = 200u64; + create_default_pool(); + + assert_err!( + StakingRewards::set_pool_expiry_block( + RuntimeOrigin::signed(non_admin), + pool_id, + DispatchTime::After(new_expiry_block) + ), + BadOrigin + ); + }); + } + + #[test] + fn fails_for_expiry_block_in_the_past() { + new_test_ext().execute_with(|| { + let admin = 1; + let pool_id = 0; + create_default_pool(); + System::set_block_number(50); + assert_err!( + StakingRewards::set_pool_expiry_block( + RuntimeOrigin::signed(admin), + pool_id, + DispatchTime::At(40u64) + ), + Error::<MockRuntime>::ExpiryBlockMustBeInTheFuture + ); + }); + } +} + +mod set_pool_reward_rate_per_block { + use super::*; + + #[test] + fn success_signed_admin() { + new_test_ext().execute_with(|| { + let pool_id = 0; + let new_reward_rate = 200; + create_default_pool(); + + // Pool Admin can modify + assert_ok!(StakingRewards::set_pool_reward_rate_per_block( + RuntimeOrigin::signed(DEFAULT_ADMIN), + pool_id, + new_reward_rate + )); + + // Check state + assert_eq!( + Pools::<MockRuntime>::get(pool_id).unwrap().reward_rate_per_block, + new_reward_rate + ); + + // Check event + assert_eq!( + *events().last().unwrap(), + Event::<MockRuntime>::PoolRewardRateModified { + pool_id, + new_reward_rate_per_block: new_reward_rate + } + ); + }); + } + + #[test] + fn success_permissioned_admin() { + new_test_ext().execute_with(|| { + let pool_id = 0; + let new_reward_rate = 200; + create_default_pool_permissioned_admin(); + + // Root can modify + assert_ok!(StakingRewards::set_pool_reward_rate_per_block( + RuntimeOrigin::root(), + pool_id, + new_reward_rate + )); + + // Check state + assert_eq!( + Pools::<MockRuntime>::get(pool_id).unwrap().reward_rate_per_block, + new_reward_rate + ); + + // Check event + assert_eq!( + *events().last().unwrap(), + Event::<MockRuntime>::PoolRewardRateModified { + pool_id, + new_reward_rate_per_block: new_reward_rate + } + ); + }); + } + + #[test] + fn staker_rewards_are_affected_correctly() { + new_test_ext().execute_with(|| { + let admin = 1; + let staker = 2; + let pool_id = 0; + let new_reward_rate = 150; + create_default_pool(); + + // Stake some tokens, and accumulate 10 blocks of rewards at the default pool rate (100) + System::set_block_number(10); + assert_ok!(StakingRewards::stake(RuntimeOrigin::signed(staker), pool_id, 1000)); + System::set_block_number(20); + + // Increase the reward rate + assert_ok!(StakingRewards::set_pool_reward_rate_per_block( + RuntimeOrigin::signed(admin), + pool_id, + new_reward_rate + )); + + // Accumulate 10 blocks of rewards at the new rate + System::set_block_number(30); + + // Check that rewards are calculated correctly with the updated rate + assert_hypothetically_earned( + staker, + 10 * 100 + 10 * new_reward_rate, + pool_id, + NativeOrWithId::<u32>::Native, + ); + }); + } + + #[test] + fn fails_for_non_existent_pool() { + new_test_ext().execute_with(|| { + let admin = 1; + let non_existent_pool_id = 999; + let new_reward_rate = 200; + + assert_err!( + StakingRewards::set_pool_reward_rate_per_block( + RuntimeOrigin::signed(admin), + non_existent_pool_id, + new_reward_rate + ), + Error::<MockRuntime>::NonExistentPool + ); + }); + } + + #[test] + fn fails_for_non_admin() { + new_test_ext().execute_with(|| { + let non_admin = 2; + let pool_id = 0; + let new_reward_rate = 200; + create_default_pool(); + + assert_err!( + StakingRewards::set_pool_reward_rate_per_block( + RuntimeOrigin::signed(non_admin), + pool_id, + new_reward_rate + ), + BadOrigin + ); + }); + } + + #[test] + fn fails_to_decrease() { + new_test_ext().execute_with(|| { + create_default_pool_permissioned_admin(); + + assert_noop!( + StakingRewards::set_pool_reward_rate_per_block( + RuntimeOrigin::root(), + 0, + DEFAULT_REWARD_RATE_PER_BLOCK - 1 + ), + Error::<MockRuntime>::RewardRateCut + ); + }); + } +} + +mod deposit_reward_tokens { + use super::*; + + #[test] + fn success() { + new_test_ext().execute_with(|| { + let depositor = 1; + let pool_id = 0; + let amount = 1000; + let reward_asset_id = NativeOrWithId::<u32>::Native; + create_default_pool(); + let pool_account_id = StakingRewards::pool_account_id(&pool_id); + + let depositor_balance_before = + <<MockRuntime as Config>::Assets>::balance(reward_asset_id.clone(), &depositor); + let pool_balance_before = <<MockRuntime as Config>::Assets>::balance( + reward_asset_id.clone(), + &pool_account_id, + ); + assert_ok!(StakingRewards::deposit_reward_tokens( + RuntimeOrigin::signed(depositor), + pool_id, + amount + )); + let depositor_balance_after = + <<MockRuntime as Config>::Assets>::balance(reward_asset_id.clone(), &depositor); + let pool_balance_after = + <<MockRuntime as Config>::Assets>::balance(reward_asset_id, &pool_account_id); + + assert_eq!(pool_balance_after - pool_balance_before, amount); + assert_eq!(depositor_balance_before - depositor_balance_after, amount); + }); + } + + #[test] + fn fails_for_non_existent_pool() { + new_test_ext().execute_with(|| { + assert_err!( + StakingRewards::deposit_reward_tokens(RuntimeOrigin::signed(1), 999, 100), + Error::<MockRuntime>::NonExistentPool + ); + }); + } + + #[test] + fn fails_for_insufficient_balance() { + new_test_ext().execute_with(|| { + create_default_pool(); + assert_err!( + StakingRewards::deposit_reward_tokens(RuntimeOrigin::signed(1), 0, 100_000_000), + ArithmeticError::Underflow + ); + }); + } +} + +mod cleanup_pool { + use super::*; + + #[test] + fn success() { + new_test_ext().execute_with(|| { + let pool_id = 0; + let admin = DEFAULT_ADMIN; + let admin_balance_before = <Balances as fungible::Inspect<u128>>::balance(&admin); + + create_default_pool(); + assert!(Pools::<MockRuntime>::get(pool_id).is_some()); + + assert_ok!(StakingRewards::cleanup_pool(RuntimeOrigin::signed(admin), pool_id)); + + assert_eq!( + <Balances as fungible::Inspect<u128>>::balance(&admin), + // `100_000` initial pool account balance from Genesis config + admin_balance_before + 100_000, + ); + assert_eq!(Pools::<MockRuntime>::get(pool_id), None); + assert_eq!(PoolStakers::<MockRuntime>::iter_prefix_values(pool_id).count(), 0); + assert_eq!(PoolCost::<MockRuntime>::get(pool_id), None); + }); + } + + #[test] + fn success_only_when_pool_empty() { + new_test_ext().execute_with(|| { + let pool_id = 0; + let staker = 20; + let admin = DEFAULT_ADMIN; + + create_default_pool(); + + // stake to prevent pool cleanup + assert_ok!(StakingRewards::stake(RuntimeOrigin::signed(staker), pool_id, 100)); + + assert_noop!( + StakingRewards::cleanup_pool(RuntimeOrigin::signed(admin), pool_id), + Error::<MockRuntime>::NonEmptyPool + ); + + // unstake partially + assert_ok!(StakingRewards::unstake(RuntimeOrigin::signed(staker), pool_id, 50, None)); + + assert_noop!( + StakingRewards::cleanup_pool(RuntimeOrigin::signed(admin), pool_id), + Error::<MockRuntime>::NonEmptyPool + ); + + // unstake all + assert_ok!(StakingRewards::unstake(RuntimeOrigin::signed(staker), pool_id, 50, None)); + + assert_ok!(StakingRewards::cleanup_pool(RuntimeOrigin::signed(admin), pool_id),); + + assert_eq!(Pools::<MockRuntime>::get(pool_id), None); + assert_eq!(PoolStakers::<MockRuntime>::iter_prefix_values(pool_id).count(), 0); + assert_eq!(PoolCost::<MockRuntime>::get(pool_id), None); + }); + } + + #[test] + fn fails_on_wrong_origin() { + new_test_ext().execute_with(|| { + let caller = 888; + let pool_id = 0; + create_default_pool(); + + assert_noop!( + StakingRewards::cleanup_pool(RuntimeOrigin::signed(caller), pool_id), + BadOrigin + ); + }); + } +} + +/// This integration test +/// 1. Considers 2 stakers each staking and unstaking at different intervals, asserts their +/// claimable rewards are adjusted as expected, and that harvesting works. +/// 2. Checks that rewards are correctly halted after the pool's expiry block, and resume when the +/// pool is extended. +/// 3. Checks that reward rates adjustment works correctly. +/// +/// Note: There are occasionally off by 1 errors due to rounding. In practice this is +/// insignificant. +#[test] +fn integration() { + new_test_ext().execute_with(|| { + let admin = 1; + let staker1 = 10u128; + let staker2 = 20; + let staked_asset_id = NativeOrWithId::<u32>::WithId(1); + let reward_asset_id = NativeOrWithId::<u32>::Native; + let reward_rate_per_block = 100; + let lifetime = 24u64.into(); + System::set_block_number(1); + assert_ok!(StakingRewards::create_pool( + RuntimeOrigin::root(), + Box::new(staked_asset_id.clone()), + Box::new(reward_asset_id.clone()), + reward_rate_per_block, + DispatchTime::After(lifetime), + Some(admin) + )); + let pool_id = 0; + + // Block 7: Staker 1 stakes 100 tokens. + System::set_block_number(7); + assert_ok!(StakingRewards::stake(RuntimeOrigin::signed(staker1), pool_id, 100)); + // At this point + // - Staker 1 has earned 0 tokens. + // - Staker 1 is earning 100 tokens per block. + + // Check that Staker 1 has earned 0 tokens. + assert_hypothetically_earned(staker1, 0, pool_id, reward_asset_id.clone()); + + // Block 9: Staker 2 stakes 100 tokens. + System::set_block_number(9); + assert_ok!(StakingRewards::stake(RuntimeOrigin::signed(staker2), pool_id, 100)); + // At this point + // - Staker 1 has earned 200 (100*2) tokens. + // - Staker 2 has earned 0 tokens. + // - Staker 1 is earning 50 tokens per block. + // - Staker 2 is earning 50 tokens per block. + + // Check that Staker 1 has earned 200 tokens and Staker 2 has earned 0 tokens. + assert_hypothetically_earned(staker1, 200, pool_id, reward_asset_id.clone()); + assert_hypothetically_earned(staker2, 0, pool_id, reward_asset_id.clone()); + + // Block 12: Staker 1 stakes an additional 100 tokens. + System::set_block_number(12); + assert_ok!(StakingRewards::stake(RuntimeOrigin::signed(staker1), pool_id, 100)); + // At this point + // - Staker 1 has earned 350 (200 + (50 * 3)) tokens. + // - Staker 2 has earned 150 (50 * 3) tokens. + // - Staker 1 is earning 66.66 tokens per block. + // - Staker 2 is earning 33.33 tokens per block. + + // Check that Staker 1 has earned 350 tokens and Staker 2 has earned 150 tokens. + assert_hypothetically_earned(staker1, 350, pool_id, reward_asset_id.clone()); + assert_hypothetically_earned(staker2, 150, pool_id, reward_asset_id.clone()); + + // Block 22: Staker 1 unstakes 100 tokens. + System::set_block_number(22); + assert_ok!(StakingRewards::unstake(RuntimeOrigin::signed(staker1), pool_id, 100, None)); + // - Staker 1 has earned 1016 (350 + 66.66 * 10) tokens. + // - Staker 2 has earned 483 (150 + 33.33 * 10) tokens. + // - Staker 1 is earning 50 tokens per block. + // - Staker 2 is earning 50 tokens per block. + assert_hypothetically_earned(staker1, 1016, pool_id, reward_asset_id.clone()); + assert_hypothetically_earned(staker2, 483, pool_id, reward_asset_id.clone()); + + // Block 23: Staker 1 unstakes 100 tokens. + System::set_block_number(23); + assert_ok!(StakingRewards::unstake(RuntimeOrigin::signed(staker1), pool_id, 100, None)); + // - Staker 1 has earned 1065 (1015 + 50) tokens. + // - Staker 2 has earned 533 (483 + 50) tokens. + // - Staker 1 is earning 0 tokens per block. + // - Staker 2 is earning 100 tokens per block. + assert_hypothetically_earned(staker1, 1066, pool_id, reward_asset_id.clone()); + assert_hypothetically_earned(staker2, 533, pool_id, reward_asset_id.clone()); + + // Block 50: Stakers should only have earned 2 blocks worth of tokens (expiry is 25). + System::set_block_number(50); + // - Staker 1 has earned 1065 tokens. + // - Staker 2 has earned 733 (533 + 2 * 100) tokens. + // - Staker 1 is earning 0 tokens per block. + // - Staker 2 is earning 0 tokens per block. + assert_hypothetically_earned(staker1, 1066, pool_id, reward_asset_id.clone()); + assert_hypothetically_earned(staker2, 733, pool_id, reward_asset_id.clone()); + + // Block 51: Extend the pool expiry block to 60. + System::set_block_number(51); + // - Staker 1 is earning 0 tokens per block. + // - Staker 2 is earning 100 tokens per block. + assert_ok!(StakingRewards::set_pool_expiry_block( + RuntimeOrigin::signed(admin), + pool_id, + DispatchTime::At(60u64), + )); + assert_hypothetically_earned(staker1, 1066, pool_id, reward_asset_id.clone()); + assert_hypothetically_earned(staker2, 733, pool_id, reward_asset_id.clone()); + + // Block 53: Check rewards are resumed. + // - Staker 1 has earned 1065 tokens. + // - Staker 2 has earned 933 (733 + 2 * 100) tokens. + // - Staker 2 is earning 100 tokens per block. + System::set_block_number(53); + assert_hypothetically_earned(staker1, 1066, pool_id, reward_asset_id.clone()); + assert_hypothetically_earned(staker2, 933, pool_id, reward_asset_id.clone()); + + // Block 55: Increase the block reward. + // - Staker 1 has earned 1065 tokens. + // - Staker 2 has earned 1133 (933 + 2 * 100) tokens. + // - Staker 2 is earning 50 tokens per block. + System::set_block_number(55); + assert_ok!(StakingRewards::set_pool_reward_rate_per_block( + RuntimeOrigin::signed(admin), + pool_id, + 150 + )); + assert_hypothetically_earned(staker1, 1066, pool_id, reward_asset_id.clone()); + assert_hypothetically_earned(staker2, 1133, pool_id, reward_asset_id.clone()); + + // Block 57: Staker2 harvests their rewards. + System::set_block_number(57); + // - Staker 2 has earned 1433 (1133 + 2 * 150) tokens. + assert_hypothetically_earned(staker2, 1433, pool_id, reward_asset_id.clone()); + // Get the pre-harvest balance. + let balance_before: <MockRuntime as Config>::Balance = + <<MockRuntime as Config>::Assets>::balance(reward_asset_id.clone(), &staker2); + assert_ok!(StakingRewards::harvest_rewards(RuntimeOrigin::signed(staker2), pool_id, None)); + let balance_after = + <<MockRuntime as Config>::Assets>::balance(reward_asset_id.clone(), &staker2); + assert_eq!(balance_after - balance_before, 1433u128); + + // Block 60: Check rewards were adjusted correctly. + // - Staker 1 has earned 1065 tokens. + // - Staker 2 has earned 450 (3 * 150) tokens. + System::set_block_number(60); + assert_hypothetically_earned(staker1, 1066, pool_id, reward_asset_id.clone()); + assert_hypothetically_earned(staker2, 450, pool_id, reward_asset_id.clone()); + + // Finally, check events. + assert_eq!( + events(), + [ + Event::PoolCreated { + creator: PermissionedAccountId::get(), + pool_id, + staked_asset_id, + reward_asset_id, + reward_rate_per_block: 100, + expiry_block: 25, + admin, + }, + Event::Staked { staker: staker1, pool_id, amount: 100 }, + Event::Staked { staker: staker2, pool_id, amount: 100 }, + Event::Staked { staker: staker1, pool_id, amount: 100 }, + Event::Unstaked { caller: staker1, staker: staker1, pool_id, amount: 100 }, + Event::Unstaked { caller: staker1, staker: staker1, pool_id, amount: 100 }, + Event::PoolExpiryBlockModified { pool_id, new_expiry_block: 60 }, + Event::PoolRewardRateModified { pool_id, new_reward_rate_per_block: 150 }, + Event::RewardsHarvested { caller: staker2, staker: staker2, pool_id, amount: 1433 } + ] + ); + }); +} diff --git a/substrate/frame/asset-rewards/src/weights.rs b/substrate/frame/asset-rewards/src/weights.rs new file mode 100644 index 0000000000000000000000000000000000000000..c9e2d0fd251aa0f09b9d9da54b2307327a752380 --- /dev/null +++ b/substrate/frame/asset-rewards/src/weights.rs @@ -0,0 +1,368 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Autogenerated weights for `pallet_asset_rewards` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2025-01-14, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-ys-ssygq-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` + +// Executed Command: +// target/production/substrate-node +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_asset_rewards +// --chain=dev +// --header=./substrate/HEADER-APACHE2 +// --output=./substrate/frame/asset-rewards/src/weights.rs +// --template=./substrate/.maintain/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weight functions needed for `pallet_asset_rewards`. +pub trait WeightInfo { + fn create_pool() -> Weight; + fn stake() -> Weight; + fn unstake() -> Weight; + fn harvest_rewards() -> Weight; + fn set_pool_reward_rate_per_block() -> Weight; + fn set_pool_admin() -> Weight; + fn set_pool_expiry_block() -> Weight; + fn deposit_reward_tokens() -> Weight; + fn cleanup_pool() -> Weight; +} + +/// Weights for `pallet_asset_rewards` using the Substrate node and recommended hardware. +pub struct SubstrateWeight<T>(PhantomData<T>); +impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { + /// Storage: `Assets::Asset` (r:2 w:0) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `AssetRewards::NextPoolId` (r:1 w:1) + /// Proof: `AssetRewards::NextPoolId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(373), added: 2848, mode: `MaxEncodedLen`) + /// Storage: `AssetRewards::PoolCost` (r:0 w:1) + /// Proof: `AssetRewards::PoolCost` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + /// Storage: `AssetRewards::Pools` (r:0 w:1) + /// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(150), added: 2625, mode: `MaxEncodedLen`) + fn create_pool() -> Weight { + // Proof Size summary in bytes: + // Measured: `495` + // Estimated: `6360` + // Minimum execution time: 62_655_000 picoseconds. + Weight::from_parts(63_723_000, 6360) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + /// Storage: `AssetRewards::Pools` (r:1 w:1) + /// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(150), added: 2625, mode: `MaxEncodedLen`) + /// Storage: `AssetRewards::PoolStakers` (r:1 w:1) + /// Proof: `AssetRewards::PoolStakers` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `AssetsFreezer::Freezes` (r:1 w:1) + /// Proof: `AssetsFreezer::Freezes` (`max_values`: None, `max_size`: Some(105), added: 2580, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1 w:0) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `AssetsFreezer::FrozenBalances` (r:1 w:1) + /// Proof: `AssetsFreezer::FrozenBalances` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + fn stake() -> Weight { + // Proof Size summary in bytes: + // Measured: `935` + // Estimated: `3615` + // Minimum execution time: 54_463_000 picoseconds. + Weight::from_parts(55_974_000, 3615) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: `AssetRewards::Pools` (r:1 w:1) + /// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(150), added: 2625, mode: `MaxEncodedLen`) + /// Storage: `AssetRewards::PoolStakers` (r:1 w:1) + /// Proof: `AssetRewards::PoolStakers` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `AssetsFreezer::Freezes` (r:1 w:1) + /// Proof: `AssetsFreezer::Freezes` (`max_values`: None, `max_size`: Some(105), added: 2580, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1 w:0) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `AssetsFreezer::FrozenBalances` (r:1 w:1) + /// Proof: `AssetsFreezer::FrozenBalances` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + fn unstake() -> Weight { + // Proof Size summary in bytes: + // Measured: `935` + // Estimated: `3615` + // Minimum execution time: 55_749_000 picoseconds. + Weight::from_parts(57_652_000, 3615) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: `AssetRewards::Pools` (r:1 w:0) + /// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(150), added: 2625, mode: `MaxEncodedLen`) + /// Storage: `AssetRewards::PoolStakers` (r:1 w:1) + /// Proof: `AssetRewards::PoolStakers` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:2 w:2) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + fn harvest_rewards() -> Weight { + // Proof Size summary in bytes: + // Measured: `1021` + // Estimated: `6208` + // Minimum execution time: 69_372_000 picoseconds. + Weight::from_parts(70_278_000, 6208) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: `AssetRewards::Pools` (r:1 w:1) + /// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(150), added: 2625, mode: `MaxEncodedLen`) + fn set_pool_reward_rate_per_block() -> Weight { + // Proof Size summary in bytes: + // Measured: `347` + // Estimated: `3615` + // Minimum execution time: 19_284_000 picoseconds. + Weight::from_parts(19_791_000, 3615) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `AssetRewards::Pools` (r:1 w:1) + /// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(150), added: 2625, mode: `MaxEncodedLen`) + fn set_pool_admin() -> Weight { + // Proof Size summary in bytes: + // Measured: `347` + // Estimated: `3615` + // Minimum execution time: 17_388_000 picoseconds. + Weight::from_parts(18_390_000, 3615) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `AssetRewards::Pools` (r:1 w:1) + /// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(150), added: 2625, mode: `MaxEncodedLen`) + fn set_pool_expiry_block() -> Weight { + // Proof Size summary in bytes: + // Measured: `347` + // Estimated: `3615` + // Minimum execution time: 19_780_000 picoseconds. + Weight::from_parts(20_676_000, 3615) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `AssetRewards::Pools` (r:1 w:0) + /// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(150), added: 2625, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:2 w:2) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn deposit_reward_tokens() -> Weight { + // Proof Size summary in bytes: + // Measured: `840` + // Estimated: `6208` + // Minimum execution time: 57_746_000 picoseconds. + Weight::from_parts(59_669_000, 6208) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: `AssetRewards::Pools` (r:1 w:1) + /// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(150), added: 2625, mode: `MaxEncodedLen`) + /// Storage: `AssetRewards::PoolStakers` (r:1 w:0) + /// Proof: `AssetRewards::PoolStakers` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:2 w:2) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `AssetRewards::PoolCost` (r:1 w:1) + /// Proof: `AssetRewards::PoolCost` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(373), added: 2848, mode: `MaxEncodedLen`) + fn cleanup_pool() -> Weight { + // Proof Size summary in bytes: + // Measured: `1236` + // Estimated: `6208` + // Minimum execution time: 110_443_000 picoseconds. + Weight::from_parts(113_149_000, 6208) + .saturating_add(T::DbWeight::get().reads(9_u64)) + .saturating_add(T::DbWeight::get().writes(8_u64)) + } +} + +// For backwards compatibility and tests. +impl WeightInfo for () { + /// Storage: `Assets::Asset` (r:2 w:0) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `AssetRewards::NextPoolId` (r:1 w:1) + /// Proof: `AssetRewards::NextPoolId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(373), added: 2848, mode: `MaxEncodedLen`) + /// Storage: `AssetRewards::PoolCost` (r:0 w:1) + /// Proof: `AssetRewards::PoolCost` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + /// Storage: `AssetRewards::Pools` (r:0 w:1) + /// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(150), added: 2625, mode: `MaxEncodedLen`) + fn create_pool() -> Weight { + // Proof Size summary in bytes: + // Measured: `495` + // Estimated: `6360` + // Minimum execution time: 62_655_000 picoseconds. + Weight::from_parts(63_723_000, 6360) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) + } + /// Storage: `AssetRewards::Pools` (r:1 w:1) + /// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(150), added: 2625, mode: `MaxEncodedLen`) + /// Storage: `AssetRewards::PoolStakers` (r:1 w:1) + /// Proof: `AssetRewards::PoolStakers` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `AssetsFreezer::Freezes` (r:1 w:1) + /// Proof: `AssetsFreezer::Freezes` (`max_values`: None, `max_size`: Some(105), added: 2580, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1 w:0) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `AssetsFreezer::FrozenBalances` (r:1 w:1) + /// Proof: `AssetsFreezer::FrozenBalances` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + fn stake() -> Weight { + // Proof Size summary in bytes: + // Measured: `935` + // Estimated: `3615` + // Minimum execution time: 54_463_000 picoseconds. + Weight::from_parts(55_974_000, 3615) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: `AssetRewards::Pools` (r:1 w:1) + /// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(150), added: 2625, mode: `MaxEncodedLen`) + /// Storage: `AssetRewards::PoolStakers` (r:1 w:1) + /// Proof: `AssetRewards::PoolStakers` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `AssetsFreezer::Freezes` (r:1 w:1) + /// Proof: `AssetsFreezer::Freezes` (`max_values`: None, `max_size`: Some(105), added: 2580, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:1 w:0) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `AssetsFreezer::FrozenBalances` (r:1 w:1) + /// Proof: `AssetsFreezer::FrozenBalances` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`) + fn unstake() -> Weight { + // Proof Size summary in bytes: + // Measured: `935` + // Estimated: `3615` + // Minimum execution time: 55_749_000 picoseconds. + Weight::from_parts(57_652_000, 3615) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: `AssetRewards::Pools` (r:1 w:0) + /// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(150), added: 2625, mode: `MaxEncodedLen`) + /// Storage: `AssetRewards::PoolStakers` (r:1 w:1) + /// Proof: `AssetRewards::PoolStakers` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:2 w:2) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + fn harvest_rewards() -> Weight { + // Proof Size summary in bytes: + // Measured: `1021` + // Estimated: `6208` + // Minimum execution time: 69_372_000 picoseconds. + Weight::from_parts(70_278_000, 6208) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: `AssetRewards::Pools` (r:1 w:1) + /// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(150), added: 2625, mode: `MaxEncodedLen`) + fn set_pool_reward_rate_per_block() -> Weight { + // Proof Size summary in bytes: + // Measured: `347` + // Estimated: `3615` + // Minimum execution time: 19_284_000 picoseconds. + Weight::from_parts(19_791_000, 3615) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `AssetRewards::Pools` (r:1 w:1) + /// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(150), added: 2625, mode: `MaxEncodedLen`) + fn set_pool_admin() -> Weight { + // Proof Size summary in bytes: + // Measured: `347` + // Estimated: `3615` + // Minimum execution time: 17_388_000 picoseconds. + Weight::from_parts(18_390_000, 3615) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `AssetRewards::Pools` (r:1 w:1) + /// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(150), added: 2625, mode: `MaxEncodedLen`) + fn set_pool_expiry_block() -> Weight { + // Proof Size summary in bytes: + // Measured: `347` + // Estimated: `3615` + // Minimum execution time: 19_780_000 picoseconds. + Weight::from_parts(20_676_000, 3615) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `AssetRewards::Pools` (r:1 w:0) + /// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(150), added: 2625, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:2 w:2) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn deposit_reward_tokens() -> Weight { + // Proof Size summary in bytes: + // Measured: `840` + // Estimated: `6208` + // Minimum execution time: 57_746_000 picoseconds. + Weight::from_parts(59_669_000, 6208) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: `AssetRewards::Pools` (r:1 w:1) + /// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(150), added: 2625, mode: `MaxEncodedLen`) + /// Storage: `AssetRewards::PoolStakers` (r:1 w:0) + /// Proof: `AssetRewards::PoolStakers` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `Assets::Account` (r:2 w:2) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:2 w:2) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `AssetRewards::PoolCost` (r:1 w:1) + /// Proof: `AssetRewards::PoolCost` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(373), added: 2848, mode: `MaxEncodedLen`) + fn cleanup_pool() -> Weight { + // Proof Size summary in bytes: + // Measured: `1236` + // Estimated: `6208` + // Minimum execution time: 110_443_000 picoseconds. + Weight::from_parts(113_149_000, 6208) + .saturating_add(RocksDbWeight::get().reads(9_u64)) + .saturating_add(RocksDbWeight::get().writes(8_u64)) + } +} diff --git a/substrate/frame/atomic-swap/Cargo.toml b/substrate/frame/atomic-swap/Cargo.toml index 785bfee71b683653fba9f3f9c8be1e531927e01f..05a38ded91c516ac995d90cc54d92d688619d2f4 100644 --- a/substrate/frame/atomic-swap/Cargo.toml +++ b/substrate/frame/atomic-swap/Cargo.toml @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { workspace = true } -frame = { workspace = true, features = ["experimental", "runtime"] } +frame = { workspace = true, features = ["runtime"] } scale-info = { features = ["derive"], workspace = true } [dev-dependencies] diff --git a/substrate/frame/babe/src/mock.rs b/substrate/frame/babe/src/mock.rs index 23857470adc4a5c751ef276a4c2d72b34f078490..8d00509e800b6368696bdea8ca686ef51f6bc71a 100644 --- a/substrate/frame/babe/src/mock.rs +++ b/substrate/frame/babe/src/mock.rs @@ -157,6 +157,7 @@ impl onchain::Config for OnChainSeqPhragmen { #[derive_impl(pallet_staking::config_preludes::TestDefaultConfig)] impl pallet_staking::Config for Test { + type OldCurrency = Balances; type Currency = Balances; type SessionsPerEra = SessionsPerEra; type BondingDuration = BondingDuration; diff --git a/substrate/frame/beefy/src/mock.rs b/substrate/frame/beefy/src/mock.rs index 7ae41c609180e4741c668372c2c2392bbb140808..38e0cc4cfc26641063af61a33b93cd8e996a2a84 100644 --- a/substrate/frame/beefy/src/mock.rs +++ b/substrate/frame/beefy/src/mock.rs @@ -235,6 +235,7 @@ impl onchain::Config for OnChainSeqPhragmen { #[derive_impl(pallet_staking::config_preludes::TestDefaultConfig)] impl pallet_staking::Config for Test { type RuntimeEvent = RuntimeEvent; + type OldCurrency = Balances; type Currency = Balances; type AdminOrigin = frame_system::EnsureRoot<Self::AccountId>; type SessionInterface = Self; diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 1d181eb29cab7321d94b083149a8e58a17ecd924..0dacfe9c55792f53e07fe708323ed6e0f8d5c784 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -520,7 +520,7 @@ impl<T: Config> Pallet<T> { let stake = T::CoreStaking::stake(who)?; // release funds from core staking. - T::CoreStaking::migrate_to_virtual_staker(who); + T::CoreStaking::migrate_to_virtual_staker(who)?; // transfer just released staked amount plus any free amount. let amount_to_transfer = diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs index 811d5739f4e98f24c30c7514455dac049fbbc421..875279864f7ab3794777b1f8bf1756a4c15b703b 100644 --- a/substrate/frame/delegated-staking/src/mock.rs +++ b/substrate/frame/delegated-staking/src/mock.rs @@ -102,6 +102,7 @@ impl onchain::Config for OnChainSeqPhragmen { #[derive_impl(pallet_staking::config_preludes::TestDefaultConfig)] impl pallet_staking::Config for Runtime { + type OldCurrency = Balances; type Currency = Balances; type UnixTime = pallet_timestamp::Pallet<Self>; type AdminOrigin = frame_system::EnsureRoot<Self::AccountId>; diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index b7b82a43771eb388ee1a5e0e11145e614f56930f..c764e2741a2a4a0149853f2279bd8b556ba4b20b 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -671,12 +671,14 @@ mod staking_integration { )); assert_ok!(Staking::nominate(RuntimeOrigin::signed(agent), vec![GENESIS_VALIDATOR],)); let init_stake = Staking::stake(&agent).unwrap(); + // no extra provider added. + assert_eq!(System::providers(&agent), 1); // scenario: 200 is a pool account, and the stake comes from its 4 delegators (300..304) // in equal parts. lets try to migrate this nominator into delegate based stake. // all balance currently is in 200 - assert_eq!(pallet_staking::asset::stakeable_balance::<T>(&agent), agent_amount); + assert_eq!(pallet_staking::asset::total_balance::<T>(&agent), agent_amount); // to migrate, nominator needs to set an account as a proxy delegator where staked funds // will be moved and delegated back to this old nominator account. This should be funded @@ -685,8 +687,9 @@ mod staking_integration { DelegatedStaking::generate_proxy_delegator(Agent::from(agent)).get(); assert_ok!(DelegatedStaking::migrate_to_agent(RawOrigin::Signed(agent).into(), 201)); - // after migration, funds are moved to proxy delegator, still a provider exists. - assert_eq!(System::providers(&agent), 1); + // after migration, no provider left since free balance is 0 and staking pallet released + // all funds. + assert_eq!(System::providers(&agent), 0); assert_eq!(Balances::free_balance(agent), 0); // proxy delegator has one provider as well with no free balance. assert_eq!(System::providers(&proxy_delegator), 1); @@ -798,8 +801,6 @@ mod staking_integration { RawOrigin::Signed(agent).into(), reward_acc )); - // becoming an agent adds another provider. - assert_eq!(System::providers(&agent), 2); // delegate to this account fund(&delegator, 1000); diff --git a/substrate/frame/delegated-staking/src/types.rs b/substrate/frame/delegated-staking/src/types.rs index a78aa3f559060080344b84002d099ca8cb18caaa..14f49466f0e2867fec4da4a360a53fe816d72d22 100644 --- a/substrate/frame/delegated-staking/src/types.rs +++ b/substrate/frame/delegated-staking/src/types.rs @@ -131,10 +131,6 @@ impl<T: Config> AgentLedger<T> { /// /// Increments provider count if this is a new agent. pub(crate) fn update(self, key: &T::AccountId) { - if !<Agents<T>>::contains_key(key) { - // This is a new agent. Provide for this account. - frame_system::Pallet::<T>::inc_providers(key); - } <Agents<T>>::insert(key, self) } @@ -142,8 +138,6 @@ impl<T: Config> AgentLedger<T> { pub(crate) fn remove(key: &T::AccountId) { debug_assert!(<Agents<T>>::contains_key(key), "Agent should exist in storage"); <Agents<T>>::remove(key); - // Remove provider reference. - let _ = frame_system::Pallet::<T>::dec_providers(key).defensive(); } /// Effective total balance of the `Agent`. diff --git a/substrate/frame/election-provider-multi-phase/test-staking-e2e/Cargo.toml b/substrate/frame/election-provider-multi-phase/test-staking-e2e/Cargo.toml index 7a48ae868a5a2e63d6b0467753cf5ca5f8b56f2c..f11f9c04dbf4a6b5da7a04ebb17a1af89826df36 100644 --- a/substrate/frame/election-provider-multi-phase/test-staking-e2e/Cargo.toml +++ b/substrate/frame/election-provider-multi-phase/test-staking-e2e/Cargo.toml @@ -34,6 +34,7 @@ frame-system = { workspace = true, default-features = true } pallet-bags-list = { workspace = true, default-features = true } pallet-balances = { workspace = true, default-features = true } +pallet-delegated-staking = { workspace = true, default-features = true } pallet-election-provider-multi-phase = { workspace = true, default-features = true } pallet-nomination-pools = { workspace = true, default-features = true } pallet-session = { workspace = true, default-features = true } @@ -47,6 +48,7 @@ try-runtime = [ "frame-system/try-runtime", "pallet-bags-list/try-runtime", "pallet-balances/try-runtime", + "pallet-delegated-staking/try-runtime", "pallet-election-provider-multi-phase/try-runtime", "pallet-nomination-pools/try-runtime", "pallet-session/try-runtime", diff --git a/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs b/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs index 26a6345e145ff1050581e32534d8b6fa09ba450d..b1029e89fe85f65650fb5406314241c220cd2b28 100644 --- a/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs +++ b/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs @@ -327,8 +327,8 @@ fn automatic_unbonding_pools() { assert_eq!(<Runtime as pallet_nomination_pools::Config>::MaxUnbonding::get(), 1); // init state of pool members. - let init_stakeable_balance_2 = pallet_staking::asset::stakeable_balance::<Runtime>(&2); - let init_stakeable_balance_3 = pallet_staking::asset::stakeable_balance::<Runtime>(&3); + let init_free_balance_2 = Balances::free_balance(2); + let init_free_balance_3 = Balances::free_balance(3); let pool_bonded_account = Pools::generate_bonded_account(1); @@ -378,7 +378,7 @@ fn automatic_unbonding_pools() { System::reset_events(); let staked_before_withdraw_pool = staked_amount_for(pool_bonded_account); - assert_eq!(pallet_staking::asset::stakeable_balance::<Runtime>(&pool_bonded_account), 26); + assert_eq!(delegated_balance_for(pool_bonded_account), 5 + 10 + 10); // now unbonding 3 will work, although the pool's ledger still has the unlocking chunks // filled up. @@ -390,13 +390,13 @@ fn automatic_unbonding_pools() { [ // auto-withdraw happened as expected to release 2's unbonding funds, but the funds // were not transferred to 2 and stay in the pool's transferrable balance instead. - pallet_staking::Event::Withdrawn { stash: 7939698191839293293, amount: 10 }, - pallet_staking::Event::Unbonded { stash: 7939698191839293293, amount: 10 } + pallet_staking::Event::Withdrawn { stash: pool_bonded_account, amount: 10 }, + pallet_staking::Event::Unbonded { stash: pool_bonded_account, amount: 10 } ] ); // balance of the pool remains the same, it hasn't withdraw explicitly from the pool yet. - assert_eq!(pallet_staking::asset::stakeable_balance::<Runtime>(&pool_bonded_account), 26); + assert_eq!(delegated_balance_for(pool_bonded_account), 25); // but the locked amount in the pool's account decreases due to the auto-withdraw: assert_eq!(staked_before_withdraw_pool - 10, staked_amount_for(pool_bonded_account)); @@ -405,12 +405,12 @@ fn automatic_unbonding_pools() { // however, note that the withdrawing from the pool still works for 2, the funds are taken // from the pool's non staked balance. - assert_eq!(pallet_staking::asset::stakeable_balance::<Runtime>(&pool_bonded_account), 26); - assert_eq!(pallet_staking::asset::staked::<Runtime>(&pool_bonded_account), 15); + assert_eq!(delegated_balance_for(pool_bonded_account), 25); + assert_eq!(staked_amount_for(pool_bonded_account), 15); assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(2), 2, 10)); - assert_eq!(pallet_staking::asset::stakeable_balance::<Runtime>(&pool_bonded_account), 16); + assert_eq!(delegated_balance_for(pool_bonded_account), 15); - assert_eq!(pallet_staking::asset::stakeable_balance::<Runtime>(&2), 20); + assert_eq!(Balances::free_balance(2), 20); assert_eq!(TotalValueLocked::<Runtime>::get(), 15); // 3 cannot withdraw yet. @@ -429,15 +429,9 @@ fn automatic_unbonding_pools() { assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(3), 3, 10)); // final conditions are the expected. - assert_eq!(pallet_staking::asset::stakeable_balance::<Runtime>(&pool_bonded_account), 6); // 5 init bonded + ED - assert_eq!( - pallet_staking::asset::stakeable_balance::<Runtime>(&2), - init_stakeable_balance_2 - ); - assert_eq!( - pallet_staking::asset::stakeable_balance::<Runtime>(&3), - init_stakeable_balance_3 - ); + assert_eq!(delegated_balance_for(pool_bonded_account), 5); // 5 init bonded + assert_eq!(Balances::free_balance(2), init_free_balance_2); + assert_eq!(Balances::free_balance(3), init_free_balance_3); assert_eq!(TotalValueLocked::<Runtime>::get(), init_tvl); }); diff --git a/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs b/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs index eaab848c1694485c47e4274ea83f223be53cd183..bcb25f8287b35e07dd7a690362df7d0aca75677b 100644 --- a/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs +++ b/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs @@ -21,6 +21,7 @@ use frame_support::{ assert_ok, parameter_types, traits, traits::{Hooks, UnfilteredDispatchable, VariantCountOf}, weights::constants, + PalletId, }; use frame_system::EnsureRoot; use sp_core::{ConstU32, Get}; @@ -36,7 +37,7 @@ use sp_runtime::{ }; use sp_staking::{ offence::{OffenceDetails, OnOffenceHandler}, - EraIndex, SessionIndex, + Agent, DelegationInterface, EraIndex, SessionIndex, StakingInterface, }; use std::collections::BTreeMap; @@ -68,6 +69,7 @@ frame_support::construct_runtime!( System: frame_system, ElectionProviderMultiPhase: pallet_election_provider_multi_phase, Staking: pallet_staking, + DelegatedStaking: pallet_delegated_staking, Pools: pallet_nomination_pools, Balances: pallet_balances, BagsList: pallet_bags_list, @@ -77,7 +79,7 @@ frame_support::construct_runtime!( } ); -pub(crate) type AccountId = u64; +pub(crate) type AccountId = u128; pub(crate) type AccountIndex = u32; pub(crate) type BlockNumber = u32; pub(crate) type Balance = u64; @@ -87,8 +89,10 @@ pub(crate) type Moment = u32; #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { + type AccountId = AccountId; type Block = Block; type AccountData = pallet_balances::AccountData<Balance>; + type Lookup = sp_runtime::traits::IdentityLookup<Self::AccountId>; } const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); @@ -265,7 +269,8 @@ impl pallet_nomination_pools::Config for Runtime { type RewardCounter = sp_runtime::FixedU128; type BalanceToU256 = BalanceToU256; type U256ToBalance = U256ToBalance; - type StakeAdapter = pallet_nomination_pools::adapter::TransferStake<Self, Staking>; + type StakeAdapter = + pallet_nomination_pools::adapter::DelegateStake<Self, Staking, DelegatedStaking>; type PostUnbondingPoolsWindow = ConstU32<2>; type PalletId = PoolsPalletId; type MaxMetadataLen = ConstU32<256>; @@ -274,6 +279,21 @@ impl pallet_nomination_pools::Config for Runtime { type AdminOrigin = frame_system::EnsureRoot<Self::AccountId>; } +parameter_types! { + pub const DelegatedStakingPalletId: PalletId = PalletId(*b"py/dlstk"); + pub const SlashRewardFraction: Perbill = Perbill::from_percent(1); +} + +impl pallet_delegated_staking::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type PalletId = DelegatedStakingPalletId; + type Currency = Balances; + type OnSlash = (); + type SlashRewardFraction = SlashRewardFraction; + type RuntimeHoldReason = RuntimeHoldReason; + type CoreStaking = Staking; +} + parameter_types! { pub static MaxUnlockingChunks: u32 = 32; } @@ -285,6 +305,7 @@ pub(crate) const SLASHING_DISABLING_FACTOR: usize = 3; #[derive_impl(pallet_staking::config_preludes::TestDefaultConfig)] impl pallet_staking::Config for Runtime { + type OldCurrency = Balances; type Currency = Balances; type CurrencyBalance = Balance; type UnixTime = Timestamp; @@ -302,7 +323,7 @@ impl pallet_staking::Config for Runtime { type NominationsQuota = pallet_staking::FixedNominationsQuota<MAX_QUOTA_NOMINATIONS>; type TargetList = pallet_staking::UseValidatorsMap<Self>; type MaxUnlockingChunks = MaxUnlockingChunks; - type EventListeners = Pools; + type EventListeners = (Pools, DelegatedStaking); type WeightInfo = pallet_staking::weights::SubstrateWeight<Runtime>; type DisablingStrategy = pallet_staking::UpToLimitWithReEnablingDisablingStrategy<SLASHING_DISABLING_FACTOR>; @@ -502,7 +523,7 @@ impl Default for BalancesExtBuilder { (100, 100), (200, 100), // stashes - (11, 1000), + (11, 1100), (21, 2000), (31, 3000), (41, 4000), @@ -581,7 +602,7 @@ impl ExtBuilder { // set the keys for the first session. keys: stakers .into_iter() - .map(|(id, ..)| (id, id, SessionKeys { other: (id as u64).into() })) + .map(|(id, ..)| (id, id, SessionKeys { other: (id as AccountId as u64).into() })) .collect(), ..Default::default() } @@ -926,7 +947,11 @@ pub(crate) fn set_minimum_election_score( } pub(crate) fn staked_amount_for(account_id: AccountId) -> Balance { - pallet_staking::asset::staked::<Runtime>(&account_id) + Staking::total_stake(&account_id).expect("account must be staker") +} + +pub(crate) fn delegated_balance_for(account_id: AccountId) -> Balance { + DelegatedStaking::agent_balance(Agent::from(account_id)).unwrap_or_default() } pub(crate) fn staking_events() -> Vec<pallet_staking::Event<Runtime>> { diff --git a/substrate/frame/examples/frame-crate/Cargo.toml b/substrate/frame/examples/frame-crate/Cargo.toml index f174c6b9054b52d1f88d5d9853ffecd2df6f8014..46db1afc34643cef8b90a639e39aa2070a16919e 100644 --- a/substrate/frame/examples/frame-crate/Cargo.toml +++ b/substrate/frame/examples/frame-crate/Cargo.toml @@ -19,7 +19,7 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { workspace = true } scale-info = { features = ["derive"], workspace = true } -frame = { features = ["experimental", "runtime"], workspace = true } +frame = { features = ["runtime"], workspace = true } [features] diff --git a/substrate/frame/fast-unstake/src/mock.rs b/substrate/frame/fast-unstake/src/mock.rs index f044fc610187578afd9276d48fb0ad5f2c6569ad..cf4f5f49240e98eaf42a944687b3fd4df81fe431 100644 --- a/substrate/frame/fast-unstake/src/mock.rs +++ b/substrate/frame/fast-unstake/src/mock.rs @@ -105,6 +105,7 @@ impl frame_election_provider_support::ElectionProvider for MockElection { #[derive_impl(pallet_staking::config_preludes::TestDefaultConfig)] impl pallet_staking::Config for Runtime { + type OldCurrency = Balances; type Currency = Balances; type UnixTime = pallet_timestamp::Pallet<Self>; type AdminOrigin = frame_system::EnsureRoot<Self::AccountId>; @@ -223,8 +224,9 @@ impl ExtBuilder { .clone() .into_iter() .map(|(stash, _, balance)| (stash, balance * 2)) - .chain(validators_range.clone().map(|x| (x, 7 + 100))) - .chain(nominators_range.clone().map(|x| (x, 7 + 100))) + // give stakers enough balance for stake, ed and fast unstake deposit. + .chain(validators_range.clone().map(|x| (x, 7 + 1 + 100))) + .chain(nominators_range.clone().map(|x| (x, 7 + 1 + 100))) .collect::<Vec<_>>(), } .assimilate_storage(&mut storage); diff --git a/substrate/frame/fast-unstake/src/tests.rs b/substrate/frame/fast-unstake/src/tests.rs index 7c11f381ca102927e10a3b8241d87dfa28a07d5b..0fddb88e02b7bef231d5f9bb3961318c8656dc24 100644 --- a/substrate/frame/fast-unstake/src/tests.rs +++ b/substrate/frame/fast-unstake/src/tests.rs @@ -19,7 +19,15 @@ use super::*; use crate::{mock::*, types::*, Event}; -use frame_support::{pallet_prelude::*, testing_prelude::*, traits::Currency}; +use frame_support::{ + pallet_prelude::*, + testing_prelude::*, + traits::{ + fungible::Inspect, + tokens::{Fortitude::Polite, Preservation::Expendable}, + Currency, + }, +}; use pallet_staking::{CurrentEra, RewardDestination}; use sp_runtime::traits::BadOrigin; @@ -146,7 +154,7 @@ fn deregister_works() { // Controller then changes mind and deregisters. assert_ok!(FastUnstake::deregister(RuntimeOrigin::signed(1))); - assert_eq!(<T as Config>::Currency::reserved_balance(&1) - pre_reserved, 0); + assert_eq!(<T as Config>::Currency::reserved_balance(&1), pre_reserved); // Ensure stash no longer exists in the queue. assert_eq!(Queue::<T>::get(1), None); @@ -297,7 +305,7 @@ mod on_idle { ); assert_eq!(Queue::<T>::count(), 3); - assert_eq!(<T as Config>::Currency::reserved_balance(&1) - pre_reserved, 0); + assert_eq!(<T as Config>::Currency::reserved_balance(&1), pre_reserved); assert_eq!( fast_unstake_events_since_last_call(), @@ -793,6 +801,8 @@ mod on_idle { RuntimeOrigin::signed(VALIDATOR_PREFIX), vec![VALIDATOR_PREFIX] )); + + assert_eq!(Balances::reducible_balance(&VALIDATOR_PREFIX, Expendable, Polite), 7); assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(VALIDATOR_PREFIX))); // but they indeed are exposed! diff --git a/substrate/frame/grandpa/src/mock.rs b/substrate/frame/grandpa/src/mock.rs index 87369c23948ca0993fc986f555782c48397549b2..0a85d9ffd2b08c4668b874685d9547a8a20785ee 100644 --- a/substrate/frame/grandpa/src/mock.rs +++ b/substrate/frame/grandpa/src/mock.rs @@ -161,6 +161,7 @@ impl onchain::Config for OnChainSeqPhragmen { #[derive_impl(pallet_staking::config_preludes::TestDefaultConfig)] impl pallet_staking::Config for Test { + type OldCurrency = Balances; type Currency = Balances; type CurrencyBalance = <Self as pallet_balances::Config>::Balance; type SessionsPerEra = SessionsPerEra; diff --git a/substrate/frame/mixnet/Cargo.toml b/substrate/frame/mixnet/Cargo.toml index 0ae3b3938c608b6ad10a0b6a06f256b463c50564..33bf7146980d5e32a46093483494b73279f012e4 100644 --- a/substrate/frame/mixnet/Cargo.toml +++ b/substrate/frame/mixnet/Cargo.toml @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { features = ["derive", "max-encoded-len"], workspace = true } -frame = { workspace = true, features = ["experimental", "runtime"] } +frame = { workspace = true, features = ["runtime"] } log = { workspace = true } scale-info = { features = ["derive"], workspace = true } serde = { features = ["derive"], workspace = true } diff --git a/substrate/frame/multisig/Cargo.toml b/substrate/frame/multisig/Cargo.toml index 0d175617c9c23dedd5f2e72e806f187dc1e5e119..e18e14f2626bfcae1ed509005e1108913e42568e 100644 --- a/substrate/frame/multisig/Cargo.toml +++ b/substrate/frame/multisig/Cargo.toml @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { workspace = true } -frame = { workspace = true, features = ["experimental", "runtime"] } +frame = { workspace = true, features = ["runtime"] } scale-info = { features = ["derive"], workspace = true } # third party diff --git a/substrate/frame/node-authorization/Cargo.toml b/substrate/frame/node-authorization/Cargo.toml index 7e55ad178091ffc42d858c80d4729a6f6d86d362..86a78e6e361535ef4556527e6a84890e50cd3a4f 100644 --- a/substrate/frame/node-authorization/Cargo.toml +++ b/substrate/frame/node-authorization/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { features = ["derive"], workspace = true } -frame = { workspace = true, features = ["experimental", "runtime"] } +frame = { workspace = true, features = ["runtime"] } log = { workspace = true } scale-info = { features = ["derive"], workspace = true } diff --git a/substrate/frame/nomination-pools/benchmarking/src/inner.rs b/substrate/frame/nomination-pools/benchmarking/src/inner.rs index 7ddb78cca3f9b8d78293f37c2e894ed805b84ed3..20c5eafbcfc59dee4d2460224e83a70458804d6d 100644 --- a/substrate/frame/nomination-pools/benchmarking/src/inner.rs +++ b/substrate/frame/nomination-pools/benchmarking/src/inner.rs @@ -132,6 +132,10 @@ fn migrate_to_transfer_stake<T: Config>(pool_id: PoolId) { .expect("member should have enough balance to transfer"); }); + // Pool needs to have ED balance free to stake so give it some. + // Note: we didn't require ED until pallet-staking migrated from locks to holds. + let _ = CurrencyOf::<T>::mint_into(&pool_acc, CurrencyOf::<T>::minimum_balance()); + pallet_staking::Pallet::<T>::migrate_to_direct_staker(&pool_acc); } @@ -141,14 +145,6 @@ fn vote_to_balance<T: pallet_nomination_pools::Config>( vote.try_into().map_err(|_| "could not convert u64 to Balance") } -/// `assertion` should strictly be true if the adapter is using `Delegate` strategy and strictly -/// false if the adapter is not using `Delegate` strategy. -fn assert_if_delegate<T: pallet_nomination_pools::Config>(assertion: bool) { - let legacy_adapter_used = T::StakeAdapter::strategy_type() != StakeStrategyType::Delegate; - // one and only one of the two should be true. - assert!(assertion ^ legacy_adapter_used); -} - #[allow(unused)] struct ListScenario<T: pallet_nomination_pools::Config> { /// Stash/Controller that is expected to be moved. @@ -981,9 +977,6 @@ mod benchmarks { #[benchmark] fn apply_slash() { - // Note: With older `TransferStake` strategy, slashing is greedy and apply_slash should - // always fail. - // We want to fill member's unbonding pools. So let's bond with big enough amount. let deposit_amount = Pools::<T>::depositor_min_bond() * T::MaxUnbonding::get().into() * 4u32.into(); @@ -993,7 +986,7 @@ mod benchmarks { // verify user balance in the pool. assert_eq!(PoolMembers::<T>::get(&depositor).unwrap().total_balance(), deposit_amount); // verify delegated balance. - assert_if_delegate::<T>( + assert!( T::StakeAdapter::member_delegation_balance(Member::from(depositor.clone())) == Some(deposit_amount), ); @@ -1017,7 +1010,7 @@ mod benchmarks { deposit_amount / 2u32.into() ); // verify delegated balance are not yet slashed. - assert_if_delegate::<T>( + assert!( T::StakeAdapter::member_delegation_balance(Member::from(depositor.clone())) == Some(deposit_amount), ); @@ -1041,13 +1034,11 @@ mod benchmarks { #[block] { - assert_if_delegate::<T>( - Pools::<T>::apply_slash( - RuntimeOrigin::Signed(slash_reporter.clone()).into(), - depositor_lookup.clone(), - ) - .is_ok(), - ); + assert!(Pools::<T>::apply_slash( + RuntimeOrigin::Signed(slash_reporter.clone()).into(), + depositor_lookup.clone(), + ) + .is_ok(),); } // verify balances are correct and slash applied. @@ -1055,7 +1046,7 @@ mod benchmarks { PoolMembers::<T>::get(&depositor).unwrap().total_balance(), deposit_amount / 2u32.into() ); - assert_if_delegate::<T>( + assert!( T::StakeAdapter::member_delegation_balance(Member::from(depositor.clone())) == Some(deposit_amount / 2u32.into()), ); @@ -1126,18 +1117,16 @@ mod benchmarks { let _ = migrate_to_transfer_stake::<T>(1); #[block] { - assert_if_delegate::<T>( - Pools::<T>::migrate_pool_to_delegate_stake( - RuntimeOrigin::Signed(depositor.clone()).into(), - 1u32.into(), - ) - .is_ok(), - ); + assert!(Pools::<T>::migrate_pool_to_delegate_stake( + RuntimeOrigin::Signed(depositor.clone()).into(), + 1u32.into(), + ) + .is_ok(),); } - // this queries agent balance if `DelegateStake` strategy. + // this queries agent balance. assert_eq!( T::StakeAdapter::total_balance(Pool::from(pool_account.clone())), - Some(deposit_amount) + Some(deposit_amount + CurrencyOf::<T>::minimum_balance()) ); } @@ -1152,13 +1141,11 @@ mod benchmarks { let _ = migrate_to_transfer_stake::<T>(1); // Now migrate pool to delegate stake keeping delegators unmigrated. - assert_if_delegate::<T>( - Pools::<T>::migrate_pool_to_delegate_stake( - RuntimeOrigin::Signed(depositor.clone()).into(), - 1u32.into(), - ) - .is_ok(), - ); + assert!(Pools::<T>::migrate_pool_to_delegate_stake( + RuntimeOrigin::Signed(depositor.clone()).into(), + 1u32.into(), + ) + .is_ok(),); // delegation does not exist. assert!( @@ -1171,16 +1158,14 @@ mod benchmarks { #[block] { - assert_if_delegate::<T>( - Pools::<T>::migrate_delegation( - RuntimeOrigin::Signed(depositor.clone()).into(), - depositor_lookup.clone(), - ) - .is_ok(), - ); + assert!(Pools::<T>::migrate_delegation( + RuntimeOrigin::Signed(depositor.clone()).into(), + depositor_lookup.clone(), + ) + .is_ok(),); } // verify balances once more. - assert_if_delegate::<T>( + assert!( T::StakeAdapter::member_delegation_balance(Member::from(depositor.clone())) == Some(deposit_amount), ); diff --git a/substrate/frame/nomination-pools/benchmarking/src/mock.rs b/substrate/frame/nomination-pools/benchmarking/src/mock.rs index 15d9e2c56031fe1e47b7b0cef3139ca177ddeb67..7c09cf22ad51e9575780525718d4fdd830bb6ecd 100644 --- a/substrate/frame/nomination-pools/benchmarking/src/mock.rs +++ b/substrate/frame/nomination-pools/benchmarking/src/mock.rs @@ -78,6 +78,7 @@ parameter_types! { } #[derive_impl(pallet_staking::config_preludes::TestDefaultConfig)] impl pallet_staking::Config for Runtime { + type OldCurrency = Balances; type Currency = Balances; type CurrencyBalance = Balance; type UnixTime = pallet_timestamp::Pallet<Self>; diff --git a/substrate/frame/nomination-pools/src/adapter.rs b/substrate/frame/nomination-pools/src/adapter.rs index f125919dabfa6bce68dea67dd4fd60ed26ec0e3c..f1c68af4ea6ad170bbd54af728cdfd68dcef3688 100644 --- a/substrate/frame/nomination-pools/src/adapter.rs +++ b/substrate/frame/nomination-pools/src/adapter.rs @@ -16,6 +16,7 @@ // limitations under the License. use crate::*; +use frame_support::traits::tokens::{Fortitude::Polite, Preservation::Expendable}; use sp_staking::{Agent, DelegationInterface, DelegationMigrator, Delegator}; /// Types of stake strategies. @@ -245,8 +246,10 @@ pub trait StakeStrategy { /// strategy in an existing runtime, storage migration is required. See /// [`migration::unversioned::DelegationStakeMigration`]. For new runtimes, it is highly recommended /// to use the [`DelegateStake`] strategy. +#[deprecated = "consider migrating to DelegateStake"] pub struct TransferStake<T: Config, Staking: StakingInterface>(PhantomData<(T, Staking)>); +#[allow(deprecated)] impl<T: Config, Staking: StakingInterface<Balance = BalanceOf<T>, AccountId = T::AccountId>> StakeStrategy for TransferStake<T, Staking> { @@ -262,7 +265,8 @@ impl<T: Config, Staking: StakingInterface<Balance = BalanceOf<T>, AccountId = T: pool_account: Pool<Self::AccountId>, _: Member<Self::AccountId>, ) -> BalanceOf<T> { - T::Currency::balance(&pool_account.0).saturating_sub(Self::active_stake(pool_account)) + // free/liquid balance of the pool account. + T::Currency::reducible_balance(&pool_account.get(), Expendable, Polite) } fn total_balance(pool_account: Pool<Self::AccountId>) -> Option<BalanceOf<T>> { diff --git a/substrate/frame/nomination-pools/src/mock.rs b/substrate/frame/nomination-pools/src/mock.rs index f544e79ec48192b77718c7ab2ef0b40d6cea3bd0..f4552389a267abaacb459ed3b4fea0a82daa1a80 100644 --- a/substrate/frame/nomination-pools/src/mock.rs +++ b/substrate/frame/nomination-pools/src/mock.rs @@ -23,8 +23,10 @@ use frame_support::{ PalletId, }; use frame_system::{EnsureSignedBy, RawOrigin}; -use sp_runtime::{BuildStorage, FixedU128}; -use sp_staking::{OnStakingUpdate, Stake}; +use sp_runtime::{BuildStorage, DispatchResult, FixedU128}; +use sp_staking::{ + Agent, DelegationInterface, DelegationMigrator, Delegator, OnStakingUpdate, Stake, +}; pub type BlockNumber = u64; pub type AccountId = u128; @@ -76,6 +78,7 @@ impl StakingMock { let bonded = BondedBalanceMap::get(); let pre_total = bonded.get(&acc).unwrap(); Self::set_bonded_balance(acc, pre_total - amount); + DelegateMock::on_slash(acc, amount); Pools::on_slash(&acc, pre_total - amount, &Default::default(), amount); } } @@ -112,8 +115,8 @@ impl sp_staking::StakingInterface for StakingMock { .ok_or(DispatchError::Other("NotStash")) } - fn is_virtual_staker(_who: &Self::AccountId) -> bool { - false + fn is_virtual_staker(who: &Self::AccountId) -> bool { + AgentBalanceMap::get().contains_key(who) } fn bond_extra(who: &Self::AccountId, extra: Self::Balance) -> DispatchResult { @@ -162,7 +165,9 @@ impl sp_staking::StakingInterface for StakingMock { staker_map.retain(|(unlocking_at, _amount)| *unlocking_at > current_era); // if there was a withdrawal, notify the pallet. - Pools::on_withdraw(&who, unlocking_before.saturating_sub(unlocking(&staker_map))); + let withdraw_amount = unlocking_before.saturating_sub(unlocking(&staker_map)); + Pools::on_withdraw(&who, withdraw_amount); + DelegateMock::on_withdraw(who, withdraw_amount); UnbondingBalanceMap::set(&unbonding_map); Ok(UnbondingBalanceMap::get().get(&who).unwrap().is_empty() && @@ -239,6 +244,176 @@ impl sp_staking::StakingInterface for StakingMock { } } +parameter_types! { + // Map of agent to their (delegated balance, unclaimed withdrawal, pending slash). + pub storage AgentBalanceMap: BTreeMap<AccountId, (Balance, Balance, Balance)> = Default::default(); + pub storage DelegatorBalanceMap: BTreeMap<AccountId, Balance> = Default::default(); +} +pub struct DelegateMock; +impl DelegationInterface for DelegateMock { + type Balance = Balance; + type AccountId = AccountId; + fn agent_balance(agent: Agent<Self::AccountId>) -> Option<Self::Balance> { + AgentBalanceMap::get() + .get(&agent.get()) + .copied() + .map(|(delegated, _, pending)| delegated - pending) + } + + fn agent_transferable_balance(agent: Agent<Self::AccountId>) -> Option<Self::Balance> { + AgentBalanceMap::get() + .get(&agent.get()) + .copied() + .map(|(_, unclaimed_withdrawals, _)| unclaimed_withdrawals) + } + + fn delegator_balance(delegator: Delegator<Self::AccountId>) -> Option<Self::Balance> { + DelegatorBalanceMap::get().get(&delegator.get()).copied() + } + + fn register_agent( + agent: Agent<Self::AccountId>, + _reward_account: &Self::AccountId, + ) -> DispatchResult { + let mut agents = AgentBalanceMap::get(); + agents.insert(agent.get(), (0, 0, 0)); + AgentBalanceMap::set(&agents); + Ok(()) + } + + fn remove_agent(agent: Agent<Self::AccountId>) -> DispatchResult { + let mut agents = AgentBalanceMap::get(); + let agent = agent.get(); + assert!(agents.contains_key(&agent)); + agents.remove(&agent); + AgentBalanceMap::set(&agents); + Ok(()) + } + + fn delegate( + delegator: Delegator<Self::AccountId>, + agent: Agent<Self::AccountId>, + amount: Self::Balance, + ) -> DispatchResult { + let delegator = delegator.get(); + let mut delegators = DelegatorBalanceMap::get(); + delegators.entry(delegator).and_modify(|b| *b += amount).or_insert(amount); + DelegatorBalanceMap::set(&delegators); + + let agent = agent.get(); + let mut agents = AgentBalanceMap::get(); + agents + .get_mut(&agent) + .map(|(d, _, _)| *d += amount) + .ok_or(DispatchError::Other("agent not registered"))?; + AgentBalanceMap::set(&agents); + + if BondedBalanceMap::get().contains_key(&agent) { + StakingMock::bond_extra(&agent, amount) + } else { + // reward account does not matter in this context. + StakingMock::bond(&agent, amount, &999) + } + } + + fn withdraw_delegation( + delegator: Delegator<Self::AccountId>, + agent: Agent<Self::AccountId>, + amount: Self::Balance, + _num_slashing_spans: u32, + ) -> DispatchResult { + let mut delegators = DelegatorBalanceMap::get(); + delegators.get_mut(&delegator.get()).map(|b| *b -= amount); + DelegatorBalanceMap::set(&delegators); + + let mut agents = AgentBalanceMap::get(); + agents.get_mut(&agent.get()).map(|(d, u, _)| { + *d -= amount; + *u -= amount; + }); + AgentBalanceMap::set(&agents); + + Ok(()) + } + + fn pending_slash(agent: Agent<Self::AccountId>) -> Option<Self::Balance> { + AgentBalanceMap::get() + .get(&agent.get()) + .copied() + .map(|(_, _, pending_slash)| pending_slash) + } + + fn delegator_slash( + agent: Agent<Self::AccountId>, + delegator: Delegator<Self::AccountId>, + value: Self::Balance, + _maybe_reporter: Option<Self::AccountId>, + ) -> DispatchResult { + let mut delegators = DelegatorBalanceMap::get(); + delegators.get_mut(&delegator.get()).map(|b| *b -= value); + DelegatorBalanceMap::set(&delegators); + + let mut agents = AgentBalanceMap::get(); + agents.get_mut(&agent.get()).map(|(_, _, p)| { + p.saturating_reduce(value); + }); + AgentBalanceMap::set(&agents); + + Ok(()) + } +} + +impl DelegateMock { + pub fn set_agent_balance(who: AccountId, delegated: Balance) { + let mut agents = AgentBalanceMap::get(); + agents.insert(who, (delegated, 0, 0)); + AgentBalanceMap::set(&agents); + } + + pub fn set_delegator_balance(who: AccountId, amount: Balance) { + let mut delegators = DelegatorBalanceMap::get(); + delegators.insert(who, amount); + DelegatorBalanceMap::set(&delegators); + } + + pub fn on_slash(agent: AccountId, amount: Balance) { + let mut agents = AgentBalanceMap::get(); + agents.get_mut(&agent).map(|(_, _, p)| *p += amount); + AgentBalanceMap::set(&agents); + } + + fn on_withdraw(agent: AccountId, amount: Balance) { + let mut agents = AgentBalanceMap::get(); + // if agent exists, add the amount to unclaimed withdrawals. + agents.get_mut(&agent).map(|(_, u, _)| *u += amount); + AgentBalanceMap::set(&agents); + } +} + +impl DelegationMigrator for DelegateMock { + type Balance = Balance; + type AccountId = AccountId; + fn migrate_nominator_to_agent( + _agent: Agent<Self::AccountId>, + _reward_account: &Self::AccountId, + ) -> DispatchResult { + unimplemented!("not used in current unit tests") + } + + fn migrate_delegation( + _agent: Agent<Self::AccountId>, + _delegator: Delegator<Self::AccountId>, + _value: Self::Balance, + ) -> DispatchResult { + unimplemented!("not used in current unit tests") + } + + #[cfg(feature = "runtime-benchmarks")] + fn force_kill_agent(_agent: Agent<Self::AccountId>) { + unimplemented!("not used in current unit tests") + } +} + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { type Nonce = u64; @@ -295,7 +470,7 @@ impl pools::Config for Runtime { type RewardCounter = RewardCounter; type BalanceToU256 = BalanceToU256; type U256ToBalance = U256ToBalance; - type StakeAdapter = adapter::TransferStake<Self, StakingMock>; + type StakeAdapter = adapter::DelegateStake<Self, StakingMock, DelegateMock>; type PostUnbondingPoolsWindow = PostUnbondingPoolsWindow; type PalletId = PoolsPalletId; type MaxMetadataLen = MaxMetadataLen; @@ -522,6 +697,21 @@ pub fn reward_imbalance(pool: PoolId) -> RewardImbalance { } } +pub fn set_pool_balance(who: AccountId, amount: Balance) { + StakingMock::set_bonded_balance(who, amount); + DelegateMock::set_agent_balance(who, amount); +} + +pub fn member_delegation(who: AccountId) -> Balance { + <T as Config>::StakeAdapter::member_delegation_balance(Member::from(who)) + .expect("who must be a pool member") +} + +pub fn pool_balance(id: PoolId) -> Balance { + <T as Config>::StakeAdapter::total_balance(Pool::from(Pools::generate_bonded_account(id))) + .expect("who must be a bonded pool account") +} + #[cfg(test)] mod test { use super::*; diff --git a/substrate/frame/nomination-pools/src/tests.rs b/substrate/frame/nomination-pools/src/tests.rs index 06261699a5b23ff58c227875b143a1eaba08e439..c46638d2f8f7bb474088b967e491c54d12bc3105 100644 --- a/substrate/frame/nomination-pools/src/tests.rs +++ b/substrate/frame/nomination-pools/src/tests.rs @@ -24,6 +24,7 @@ use sp_runtime::{ traits::{BadOrigin, Dispatchable}, FixedU128, }; +use sp_staking::{Agent, DelegationInterface}; macro_rules! unbonding_pools_with_era { ($($k:expr => $v:expr),* $(,)?) => {{ @@ -127,41 +128,41 @@ mod bonded_pool { }; // 1 points : 1 balance ratio - StakingMock::set_bonded_balance(bonded_pool.bonded_account(), 100); + set_pool_balance(bonded_pool.bonded_account(), 100); assert_eq!(bonded_pool.balance_to_point(10), 10); assert_eq!(bonded_pool.balance_to_point(0), 0); // 2 points : 1 balance ratio - StakingMock::set_bonded_balance(bonded_pool.bonded_account(), 50); + set_pool_balance(bonded_pool.bonded_account(), 50); assert_eq!(bonded_pool.balance_to_point(10), 20); // 1 points : 2 balance ratio - StakingMock::set_bonded_balance(bonded_pool.bonded_account(), 100); + set_pool_balance(bonded_pool.bonded_account(), 100); bonded_pool.points = 50; assert_eq!(bonded_pool.balance_to_point(10), 5); // 100 points : 0 balance ratio - StakingMock::set_bonded_balance(bonded_pool.bonded_account(), 0); + set_pool_balance(bonded_pool.bonded_account(), 0); bonded_pool.points = 100; assert_eq!(bonded_pool.balance_to_point(10), 100 * 10); // 0 points : 100 balance - StakingMock::set_bonded_balance(bonded_pool.bonded_account(), 100); + set_pool_balance(bonded_pool.bonded_account(), 100); bonded_pool.points = 0; assert_eq!(bonded_pool.balance_to_point(10), 10); // 10 points : 3 balance ratio - StakingMock::set_bonded_balance(bonded_pool.bonded_account(), 30); + set_pool_balance(bonded_pool.bonded_account(), 30); bonded_pool.points = 100; assert_eq!(bonded_pool.balance_to_point(10), 33); // 2 points : 3 balance ratio - StakingMock::set_bonded_balance(bonded_pool.bonded_account(), 300); + set_pool_balance(bonded_pool.bonded_account(), 300); bonded_pool.points = 200; assert_eq!(bonded_pool.balance_to_point(10), 6); // 4 points : 9 balance ratio - StakingMock::set_bonded_balance(bonded_pool.bonded_account(), 900); + set_pool_balance(bonded_pool.bonded_account(), 900); bonded_pool.points = 400; assert_eq!(bonded_pool.balance_to_point(90), 40); }) @@ -182,7 +183,7 @@ mod bonded_pool { }, }; - StakingMock::set_bonded_balance(bonded_pool.bonded_account(), 100); + set_pool_balance(bonded_pool.bonded_account(), 100); assert_eq!(bonded_pool.points_to_balance(10), 10); assert_eq!(bonded_pool.points_to_balance(0), 0); @@ -191,27 +192,27 @@ mod bonded_pool { assert_eq!(bonded_pool.points_to_balance(10), 20); // 100 balance : 0 points ratio - StakingMock::set_bonded_balance(bonded_pool.bonded_account(), 100); + set_pool_balance(bonded_pool.bonded_account(), 100); bonded_pool.points = 0; assert_eq!(bonded_pool.points_to_balance(10), 0); // 0 balance : 100 points ratio - StakingMock::set_bonded_balance(bonded_pool.bonded_account(), 0); + set_pool_balance(bonded_pool.bonded_account(), 0); bonded_pool.points = 100; assert_eq!(bonded_pool.points_to_balance(10), 0); // 10 balance : 3 points ratio - StakingMock::set_bonded_balance(bonded_pool.bonded_account(), 100); + set_pool_balance(bonded_pool.bonded_account(), 100); bonded_pool.points = 30; assert_eq!(bonded_pool.points_to_balance(10), 33); // 2 balance : 3 points ratio - StakingMock::set_bonded_balance(bonded_pool.bonded_account(), 200); + set_pool_balance(bonded_pool.bonded_account(), 200); bonded_pool.points = 300; assert_eq!(bonded_pool.points_to_balance(10), 6); // 4 balance : 9 points ratio - StakingMock::set_bonded_balance(bonded_pool.bonded_account(), 400); + set_pool_balance(bonded_pool.bonded_account(), 400); bonded_pool.points = 900; assert_eq!(bonded_pool.points_to_balance(90), 40); }) @@ -269,30 +270,21 @@ mod bonded_pool { <<Runtime as Config>::MaxPointsToBalance as Get<u8>>::get().into(); // Simulate a 100% slashed pool - StakingMock::set_bonded_balance(pool.bonded_account(), 0); + set_pool_balance(pool.bonded_account(), 0); assert_noop!(pool.ok_to_join(), Error::<Runtime>::OverflowRisk); // Simulate a slashed pool at `MaxPointsToBalance` + 1 slashed pool - StakingMock::set_bonded_balance( - pool.bonded_account(), - max_points_to_balance.saturating_add(1), - ); + set_pool_balance(pool.bonded_account(), max_points_to_balance.saturating_add(1)); assert_ok!(pool.ok_to_join()); // Simulate a slashed pool at `MaxPointsToBalance` - StakingMock::set_bonded_balance(pool.bonded_account(), max_points_to_balance); + set_pool_balance(pool.bonded_account(), max_points_to_balance); assert_noop!(pool.ok_to_join(), Error::<Runtime>::OverflowRisk); - StakingMock::set_bonded_balance( - pool.bonded_account(), - Balance::MAX / max_points_to_balance, - ); + set_pool_balance(pool.bonded_account(), Balance::MAX / max_points_to_balance); // and a sanity check - StakingMock::set_bonded_balance( - pool.bonded_account(), - Balance::MAX / max_points_to_balance - 1, - ); + set_pool_balance(pool.bonded_account(), Balance::MAX / max_points_to_balance - 1); assert_ok!(pool.ok_to_join()); }); } @@ -310,7 +302,7 @@ mod bonded_pool { state: PoolState::Open, }, }; - StakingMock::set_bonded_balance(bonded_pool.bonded_account(), u128::MAX); + set_pool_balance(bonded_pool.bonded_account(), u128::MAX); // Max out the points and balance of the pool and make sure the conversion works as // expected and does not overflow. @@ -640,8 +632,6 @@ mod sub_pools { } mod join { - use sp_runtime::TokenError; - use super::*; #[test] @@ -728,7 +718,7 @@ mod join { ); // Force the pools bonded balance to 0, simulating a 100% slash - StakingMock::set_bonded_balance(Pools::generate_bonded_account(1), 0); + set_pool_balance(Pools::generate_bonded_account(1), 0); assert_noop!( Pools::join(RuntimeOrigin::signed(11), 420, 1), Error::<Runtime>::OverflowRisk @@ -754,29 +744,13 @@ mod join { let max_points_to_balance: u128 = <<Runtime as Config>::MaxPointsToBalance as Get<u8>>::get().into(); - StakingMock::set_bonded_balance( - Pools::generate_bonded_account(123), - max_points_to_balance, - ); + set_pool_balance(Pools::generate_bonded_account(123), max_points_to_balance); assert_noop!( Pools::join(RuntimeOrigin::signed(11), 420, 123), Error::<Runtime>::OverflowRisk ); - StakingMock::set_bonded_balance( - Pools::generate_bonded_account(123), - Balance::MAX / max_points_to_balance, - ); - // Balance needs to be gt Balance::MAX / `MaxPointsToBalance` - assert_noop!( - Pools::join(RuntimeOrigin::signed(11), 5, 123), - TokenError::FundsUnavailable, - ); - - StakingMock::set_bonded_balance( - Pools::generate_bonded_account(1), - max_points_to_balance, - ); + set_pool_balance(Pools::generate_bonded_account(1), max_points_to_balance); // Cannot join a pool that isn't open unsafe_set_state(123, PoolState::Blocked); @@ -807,7 +781,7 @@ mod join { #[cfg_attr(not(debug_assertions), should_panic)] fn join_panics_when_reward_pool_not_found() { ExtBuilder::default().build_and_execute(|| { - StakingMock::set_bonded_balance(Pools::generate_bonded_account(123), 100); + set_pool_balance(Pools::generate_bonded_account(123), 100); BondedPool::<Runtime> { id: 123, inner: BondedPoolInner { @@ -2321,8 +2295,8 @@ mod claim_payout { fn rewards_are_rounded_down_depositor_collects_them() { ExtBuilder::default().add_members(vec![(20, 20)]).build_and_execute(|| { // initial balance of 10. - - assert_eq!(Currency::free_balance(&10), 35); + let init_balance_10 = Currency::free_balance(&10); + assert_eq!(member_delegation(10), 10); assert_eq!( Currency::free_balance(&default_reward_account()), Currency::minimum_balance() @@ -2373,8 +2347,10 @@ mod claim_payout { ); assert!(!Metadata::<T>::contains_key(1)); - // original ed + ed put into reward account + reward + bond + dust. - assert_eq!(Currency::free_balance(&10), 35 + 5 + 13 + 10 + 1); + // original ed + ed put into reward account + reward + dust. + assert_eq!(Currency::free_balance(&10), init_balance_10 + 5 + 13 + 1); + // delegation reduced from 10 to 0. + assert_eq!(member_delegation(10), 0); }) } @@ -2444,9 +2420,10 @@ mod claim_payout { let claimable_reward = 8 - ExistentialDeposit::get(); // NOTE: easier to read if we use 3, so let's use the number instead of variable. assert_eq!(claimable_reward, 3, "test is correct if rewards are divisible by 3"); + let init_balance = Currency::free_balance(&10); // given - assert_eq!(Currency::free_balance(&10), 35); + assert_eq!(member_delegation(10), 10); // when @@ -2455,7 +2432,10 @@ mod claim_payout { assert_ok!(Pools::claim_payout_other(RuntimeOrigin::signed(80), 10)); // then - assert_eq!(Currency::free_balance(&10), 36); + // delegated balance does not change. + assert_eq!(member_delegation(10), 10); + // reward of 1 is paid out to 10. + assert_eq!(Currency::free_balance(&10), init_balance + 1); assert_eq!(Currency::free_balance(&default_reward_account()), 7); }) } @@ -2818,6 +2798,8 @@ mod unbond { ExtBuilder::default() .add_members(vec![(40, 40), (550, 550)]) .build_and_execute(|| { + let init_balance_40 = Currency::free_balance(&40); + let init_balance_550 = Currency::free_balance(&550); let ed = Currency::minimum_balance(); // Given a slash from 600 -> 500 StakingMock::slash_by(1, 500); @@ -2864,7 +2846,9 @@ mod unbond { PoolMembers::<Runtime>::get(40).unwrap().unbonding_eras, member_unbonding_eras!(3 => 6) ); - assert_eq!(Currency::free_balance(&40), 40 + 40); // We claim rewards when unbonding + assert_eq!(member_delegation(40), 40); + // We claim rewards when unbonding + assert_eq!(Currency::free_balance(&40), init_balance_40 + 40); // When unsafe_set_state(1, PoolState::Destroying); @@ -2893,7 +2877,8 @@ mod unbond { PoolMembers::<Runtime>::get(550).unwrap().unbonding_eras, member_unbonding_eras!(3 => 92) ); - assert_eq!(Currency::free_balance(&550), 550 + 550); + assert_eq!(member_delegation(550), 550); + assert_eq!(Currency::free_balance(&550), init_balance_550 + 550); assert_eq!( pool_events_since_last_call(), vec![ @@ -2934,7 +2919,8 @@ mod unbond { ); assert_eq!(StakingMock::active_stake(&default_bonded_account()).unwrap(), 0); - assert_eq!(Currency::free_balance(&550), 550 + 550 + 92); + // 550 is removed from pool. + assert_eq!(member_delegation(550), 0); assert_eq!( pool_events_since_last_call(), vec![ @@ -3532,7 +3518,7 @@ mod pool_withdraw_unbonded { assert_eq!(StakingMock::active_stake(&default_bonded_account()), Ok(15)); assert_eq!(StakingMock::total_stake(&default_bonded_account()), Ok(20)); - assert_eq!(Balances::free_balance(&default_bonded_account()), 20); + assert_eq!(pool_balance(1), 20); // When CurrentEra::set(StakingMock::current_era() + StakingMock::bonding_duration() + 1); @@ -3541,7 +3527,7 @@ mod pool_withdraw_unbonded { // Then their unbonding balance is no longer locked assert_eq!(StakingMock::active_stake(&default_bonded_account()), Ok(15)); assert_eq!(StakingMock::total_stake(&default_bonded_account()), Ok(15)); - assert_eq!(Balances::free_balance(&default_bonded_account()), 20); + assert_eq!(pool_balance(1), 20); }); } #[test] @@ -3552,7 +3538,7 @@ mod pool_withdraw_unbonded { assert_eq!(StakingMock::active_stake(&default_bonded_account()), Ok(15)); assert_eq!(StakingMock::total_stake(&default_bonded_account()), Ok(20)); - assert_eq!(Balances::free_balance(&default_bonded_account()), 20); + assert_eq!(pool_balance(1), 20); assert_eq!(TotalValueLocked::<T>::get(), 20); // When @@ -3568,14 +3554,14 @@ mod pool_withdraw_unbonded { // Then their unbonding balance is no longer locked assert_eq!(StakingMock::active_stake(&default_bonded_account()), Ok(15)); assert_eq!(StakingMock::total_stake(&default_bonded_account()), Ok(15)); - assert_eq!(Currency::free_balance(&default_bonded_account()), 20); + assert_eq!(pool_balance(1), 20); // The difference between TVL and member_balance is exactly the difference between - // `total_stake` and the `free_balance`. - // This relation is not guaranteed in the wild as arbitrary transfers towards - // `free_balance` can be made to the pool that are not accounted for. - let non_locked_balance = Balances::free_balance(&default_bonded_account()) - - StakingMock::total_stake(&default_bonded_account()).unwrap(); + // `pool balance` (sum of all balance delegated to pool) and the `staked balance`. + // This is the withdrawn funds from the pool stake that has not yet been claimed by the + // respective members. + let non_locked_balance = + pool_balance(1) - StakingMock::total_stake(&default_bonded_account()).unwrap(); assert_eq!(member_balance, TotalValueLocked::<T>::get() + non_locked_balance); }); } @@ -3597,7 +3583,7 @@ mod withdraw_unbonded { assert_eq!(StakingMock::bonding_duration(), 3); assert_ok!(Pools::fully_unbond(RuntimeOrigin::signed(550), 550)); assert_ok!(Pools::fully_unbond(RuntimeOrigin::signed(40), 40)); - assert_eq!(Currency::free_balance(&default_bonded_account()), 600); + assert_eq!(pool_balance(1), 600); let mut current_era = 1; CurrentEra::set(current_era); @@ -3626,10 +3612,7 @@ mod withdraw_unbonded { .1 /= 2; UnbondingBalanceMap::set(&x); - Currency::set_balance( - &default_bonded_account(), - Currency::free_balance(&default_bonded_account()) / 2, // 300 - ); + set_pool_balance(1, pool_balance(1) / 2); assert_eq!(StakingMock::active_stake(&default_bonded_account()).unwrap(), 10); StakingMock::slash_by(1, 5); assert_eq!(StakingMock::active_stake(&default_bonded_account()).unwrap(), 5); @@ -3671,11 +3654,6 @@ mod withdraw_unbonded { Event::PoolSlashed { pool_id: 1, balance: 5 } ] ); - assert_eq!( - balances_events_since_last_call(), - vec![BEvent::Burned { who: default_bonded_account(), amount: 300 }] - ); - // When assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(550), 550, 0)); @@ -3691,10 +3669,9 @@ mod withdraw_unbonded { Event::MemberRemoved { pool_id: 1, member: 550, released_balance: 0 } ] ); - assert_eq!( - balances_events_since_last_call(), - vec![BEvent::Transfer { from: default_bonded_account(), to: 550, amount: 275 }] - ); + + // member has 40 tokens in delegation, but only 20 can be withdrawan. + assert_eq!(member_delegation(40), 40); // When assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(40), 40, 0)); @@ -3708,18 +3685,18 @@ mod withdraw_unbonded { assert_eq!( pool_events_since_last_call(), vec![ + // out of 40, 20 is withdrawn. Event::Withdrawn { member: 40, pool_id: 1, balance: 20, points: 40 }, - Event::MemberRemoved { pool_id: 1, member: 40, released_balance: 0 } + // member is removed and the dangling delegation of 20 tokens left in their + // account is released. + Event::MemberRemoved { pool_id: 1, member: 40, released_balance: 20 } ] ); - assert_eq!( - balances_events_since_last_call(), - vec![BEvent::Transfer { from: default_bonded_account(), to: 40, amount: 20 }] - ); // now, finally, the depositor can take out its share. unsafe_set_state(1, PoolState::Destroying); assert_ok!(fully_unbond_permissioned(10)); + assert_eq!(member_delegation(10), 10); current_era += 3; CurrentEra::set(current_era); @@ -3731,7 +3708,9 @@ mod withdraw_unbonded { vec![ Event::Unbonded { member: 10, pool_id: 1, balance: 5, points: 5, era: 9 }, Event::Withdrawn { member: 10, pool_id: 1, balance: 5, points: 5 }, - Event::MemberRemoved { pool_id: 1, member: 10, released_balance: 0 }, + // when member is removed, any leftover delegation is released. + Event::MemberRemoved { pool_id: 1, member: 10, released_balance: 5 }, + // when the last member leaves, the pool is destroyed. Event::Destroyed { pool_id: 1 } ] ); @@ -3739,7 +3718,6 @@ mod withdraw_unbonded { assert_eq!( balances_events_since_last_call(), vec![ - BEvent::Transfer { from: default_bonded_account(), to: 10, amount: 5 }, BEvent::Thawed { who: default_reward_account(), amount: 5 }, BEvent::Transfer { from: default_reward_account(), to: 10, amount: 5 } ] @@ -3753,11 +3731,9 @@ mod withdraw_unbonded { .add_members(vec![(40, 40), (550, 550)]) .build_and_execute(|| { let _ = balances_events_since_last_call(); - // Given // current bond is 600, we slash it all to 300. StakingMock::slash_by(1, 300); - Currency::set_balance(&default_bonded_account(), 300); assert_eq!(StakingMock::total_stake(&default_bonded_account()), Ok(300)); assert_ok!(fully_unbond_permissioned(40)); @@ -3787,10 +3763,6 @@ mod withdraw_unbonded { } ] ); - assert_eq!( - balances_events_since_last_call(), - vec![BEvent::Burned { who: default_bonded_account(), amount: 300 },] - ); CurrentEra::set(StakingMock::bonding_duration()); @@ -3798,10 +3770,6 @@ mod withdraw_unbonded { assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(40), 40, 0)); // Then - assert_eq!( - balances_events_since_last_call(), - vec![BEvent::Transfer { from: default_bonded_account(), to: 40, amount: 20 },] - ); assert_eq!( pool_events_since_last_call(), vec![ @@ -3819,10 +3787,6 @@ mod withdraw_unbonded { assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(550), 550, 0)); // Then - assert_eq!( - balances_events_since_last_call(), - vec![BEvent::Transfer { from: default_bonded_account(), to: 550, amount: 275 },] - ); assert_eq!( pool_events_since_last_call(), vec![ @@ -3852,9 +3816,11 @@ mod withdraw_unbonded { assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 0)); // then - assert_eq!(Currency::free_balance(&10), 10 + 35); - assert_eq!(Currency::free_balance(&default_bonded_account()), 0); - + assert_eq!( + DelegateMock::agent_balance(Agent::from(default_bonded_account())), + None + ); + assert_eq!(StakingMock::stake(&default_bonded_account()).unwrap().total, 0); // in this test 10 also gets a fair share of the slash, because the slash was // applied to the bonded account. assert_eq!( @@ -3870,7 +3836,6 @@ mod withdraw_unbonded { assert_eq!( balances_events_since_last_call(), vec![ - BEvent::Transfer { from: default_bonded_account(), to: 10, amount: 5 }, BEvent::Thawed { who: default_reward_account(), amount: 5 }, BEvent::Transfer { from: default_reward_account(), to: 10, amount: 5 } ] @@ -3878,35 +3843,6 @@ mod withdraw_unbonded { }); } - #[test] - fn withdraw_unbonded_handles_faulty_sub_pool_accounting() { - ExtBuilder::default().build_and_execute(|| { - // Given - assert_eq!(Currency::minimum_balance(), 5); - assert_eq!(Currency::free_balance(&10), 35); - assert_eq!(Currency::free_balance(&default_bonded_account()), 10); - unsafe_set_state(1, PoolState::Destroying); - assert_ok!(Pools::fully_unbond(RuntimeOrigin::signed(10), 10)); - - // Simulate a slash that is not accounted for in the sub pools. - Currency::set_balance(&default_bonded_account(), 5); - assert_eq!( - SubPoolsStorage::<Runtime>::get(1).unwrap().with_era, - //------------------------------balance decrease is not account for - unbonding_pools_with_era! { 3 => UnbondPool { points: 10, balance: 10 } } - ); - - CurrentEra::set(3); - - // When - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 0)); - - // Then - assert_eq!(Currency::free_balance(&10), 10 + 35); - assert_eq!(Currency::free_balance(&default_bonded_account()), 0); - }); - } - #[test] fn withdraw_unbonded_errors_correctly() { ExtBuilder::default().with_check(0).build_and_execute(|| { @@ -3925,6 +3861,10 @@ mod withdraw_unbonded { let mut member = PoolMember { pool_id: 1, points: 10, ..Default::default() }; PoolMembers::<Runtime>::insert(11, member.clone()); + // set agent and delegator balance + DelegateMock::set_agent_balance(Pools::generate_bonded_account(1), 10); + DelegateMock::set_delegator_balance(11, 10); + // Simulate calling `unbond` member.unbonding_eras = member_unbonding_eras!(3 => 10); PoolMembers::<Runtime>::insert(11, member.clone()); @@ -4045,7 +3985,7 @@ mod withdraw_unbonded { } ); CurrentEra::set(StakingMock::bonding_duration()); - assert_eq!(Currency::free_balance(&100), 100); + assert_eq!(member_delegation(100), 100); // Cannot permissionlessly withdraw assert_noop!( @@ -4061,6 +4001,7 @@ mod withdraw_unbonded { assert_eq!(SubPoolsStorage::<Runtime>::get(1).unwrap(), Default::default(),); assert_eq!(Currency::free_balance(&100), 100 + 100); + assert_eq!(member_delegation(100), 0); assert!(!PoolMembers::<Runtime>::contains_key(100)); assert_eq!( pool_events_since_last_call(), @@ -4662,10 +4603,6 @@ mod withdraw_unbonded { // move to era when unbonded funds can be withdrawn. CurrentEra::set(4); - - // increment consumer by 1 reproducing the erroneous consumer bug. - // refer https://github.com/paritytech/polkadot-sdk/issues/4440. - assert_ok!(frame_system::Pallet::<T>::inc_consumers(&pool_one)); assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 0)); assert_eq!( @@ -4712,7 +4649,7 @@ mod create { )); assert_eq!(TotalValueLocked::<T>::get(), 10 + StakingMock::minimum_nominator_bond()); - assert_eq!(Currency::free_balance(&11), 0); + assert_eq!(member_delegation(11), StakingMock::minimum_nominator_bond()); assert_eq!( PoolMembers::<Runtime>::get(11).unwrap(), PoolMember { @@ -4851,7 +4788,7 @@ mod create { 789 )); - assert_eq!(Currency::free_balance(&11), 0); + assert_eq!(member_delegation(11), StakingMock::minimum_nominator_bond()); // delete the initial pool created, then pool_Id `1` will be free assert_noop!( @@ -5014,16 +4951,9 @@ mod set_state { // surpassed. Making this pool destroyable by anyone. StakingMock::slash_by(1, 10); - // in mock we are using transfer stake which implies slash is greedy. Extrinsic to - // apply pending slash should fail. - assert_noop!( - Pools::apply_slash(RuntimeOrigin::signed(11), 10), - Error::<Runtime>::NotSupported - ); - - // pending slash api should return zero as well. - assert_eq!(Pools::api_pool_pending_slash(1), 0); - assert_eq!(Pools::api_member_pending_slash(10), 0); + // pending slash is correct. + assert_eq!(Pools::api_pool_pending_slash(1), 10); + assert_eq!(Pools::api_member_pending_slash(10), 10); // When assert_ok!(Pools::set_state(RuntimeOrigin::signed(11), 1, PoolState::Destroying)); @@ -5175,13 +5105,13 @@ mod bond_extra { // given assert_eq!(PoolMembers::<Runtime>::get(10).unwrap().points, 10); assert_eq!(BondedPools::<Runtime>::get(1).unwrap().points, 10); - assert_eq!(Currency::free_balance(&10), 100); + assert_eq!(member_delegation(10), 10); // when assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(10), BondExtra::FreeBalance(10))); // then - assert_eq!(Currency::free_balance(&10), 90); + assert_eq!(member_delegation(10), 10 + 10); assert_eq!(PoolMembers::<Runtime>::get(10).unwrap().points, 20); assert_eq!(BondedPools::<Runtime>::get(1).unwrap().points, 20); @@ -5198,7 +5128,7 @@ mod bond_extra { assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(10), BondExtra::FreeBalance(20))); // then - assert_eq!(Currency::free_balance(&10), 70); + assert_eq!(member_delegation(10), 20 + 20); assert_eq!(PoolMembers::<Runtime>::get(10).unwrap().points, 40); assert_eq!(BondedPools::<Runtime>::get(1).unwrap().points, 40); @@ -5221,13 +5151,15 @@ mod bond_extra { // given assert_eq!(PoolMembers::<Runtime>::get(10).unwrap().points, 10); assert_eq!(BondedPools::<Runtime>::get(1).unwrap().points, 10); - assert_eq!(Currency::free_balance(&10), 35); + // 10 has delegated 10 tokens to the pool. + assert_eq!(member_delegation(10), 10); // when assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(10), BondExtra::Rewards)); // then - assert_eq!(Currency::free_balance(&10), 35); + // delegator balance is increased by the claimable reward. + assert_eq!(member_delegation(10), 10 + claimable_reward); assert_eq!(PoolMembers::<Runtime>::get(10).unwrap().points, 10 + claimable_reward); assert_eq!(BondedPools::<Runtime>::get(1).unwrap().points, 10 + claimable_reward); @@ -5264,8 +5196,8 @@ mod bond_extra { assert_eq!(PoolMembers::<Runtime>::get(20).unwrap().points, 20); assert_eq!(BondedPools::<Runtime>::get(1).unwrap().points, 30); - assert_eq!(Currency::free_balance(&10), 35); - assert_eq!(Currency::free_balance(&20), 20); + assert_eq!(member_delegation(10), 10); + assert_eq!(member_delegation(20), 20); assert_eq!(TotalValueLocked::<T>::get(), 30); // when @@ -5273,7 +5205,7 @@ mod bond_extra { assert_eq!(Currency::free_balance(&default_reward_account()), 7); // then - assert_eq!(Currency::free_balance(&10), 35); + assert_eq!(member_delegation(10), 10 + 1); assert_eq!(TotalValueLocked::<T>::get(), 31); // 10's share of the reward is 1/3, since they gave 10/30 of the total shares. @@ -5284,11 +5216,11 @@ mod bond_extra { assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(20), BondExtra::Rewards)); // then - assert_eq!(Currency::free_balance(&20), 20); assert_eq!(TotalValueLocked::<T>::get(), 33); // 20's share of the rewards is the other 2/3 of the rewards, since they have 20/30 of // the shares + assert_eq!(member_delegation(20), 20 + 2); assert_eq!(PoolMembers::<Runtime>::get(20).unwrap().points, 20 + 2); assert_eq!(BondedPools::<Runtime>::get(1).unwrap().points, 30 + 3); @@ -5320,8 +5252,8 @@ mod bond_extra { assert_eq!(PoolMembers::<Runtime>::get(10).unwrap().points, 10); assert_eq!(PoolMembers::<Runtime>::get(20).unwrap().points, 20); assert_eq!(BondedPools::<Runtime>::get(1).unwrap().points, 30); - assert_eq!(Currency::free_balance(&10), 35); - assert_eq!(Currency::free_balance(&20), 20); + assert_eq!(member_delegation(10), 10); + assert_eq!(member_delegation(20), 20); // Permissioned by default assert_noop!( @@ -5337,7 +5269,7 @@ mod bond_extra { assert_eq!(Currency::free_balance(&default_reward_account()), 7); // then - assert_eq!(Currency::free_balance(&10), 35); + assert_eq!(member_delegation(10), 10 + 1); assert_eq!(PoolMembers::<Runtime>::get(10).unwrap().points, 10 + 1); assert_eq!(BondedPools::<Runtime>::get(1).unwrap().points, 30 + 1); @@ -5355,7 +5287,7 @@ mod bond_extra { )); // then - assert_eq!(Currency::free_balance(&20), 12); + assert_eq!(member_delegation(20), 20 + 10); assert_eq!(Currency::free_balance(&default_reward_account()), 5); assert_eq!(PoolMembers::<Runtime>::get(20).unwrap().points, 30); assert_eq!(BondedPools::<Runtime>::get(1).unwrap().points, 41); @@ -7487,63 +7419,3 @@ mod chill { }) } } - -// the test mock is using `TransferStake` and so `DelegateStake` is not tested here. Extrinsics -// meant for `DelegateStake` should be gated. -// -// `DelegateStake` tests are in `pallet-nomination-pools-test-delegate-stake`. Since we support both -// strategies currently, we keep these tests as it is but in future we may remove `TransferStake` -// completely. -mod delegate_stake { - use super::*; - #[test] - fn delegation_specific_calls_are_gated() { - ExtBuilder::default().with_check(0).build_and_execute(|| { - // Given - Currency::set_balance(&11, ExistentialDeposit::get() + 2); - assert!(!PoolMembers::<Runtime>::contains_key(11)); - - // When - assert_ok!(Pools::join(RuntimeOrigin::signed(11), 2, 1)); - - // Then - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::Created { depositor: 10, pool_id: 1 }, - Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::Bonded { member: 11, pool_id: 1, bonded: 2, joined: true }, - ] - ); - - assert_eq!( - PoolMembers::<Runtime>::get(11).unwrap(), - PoolMember::<Runtime> { pool_id: 1, points: 2, ..Default::default() } - ); - - // ensure pool 1 cannot be migrated. - assert!(!Pools::api_pool_needs_delegate_migration(1)); - assert_noop!( - Pools::migrate_pool_to_delegate_stake(RuntimeOrigin::signed(10), 1), - Error::<Runtime>::NotSupported - ); - - // members cannot be migrated either. - assert!(!Pools::api_member_needs_delegate_migration(10)); - assert_noop!( - Pools::migrate_delegation(RuntimeOrigin::signed(10), 11), - Error::<Runtime>::NotSupported - ); - - // Given - // The bonded balance is slashed in half - StakingMock::slash_by(1, 6); - - // since slash is greedy with `TransferStake`, `apply_slash` should not work either. - assert_noop!( - Pools::apply_slash(RuntimeOrigin::signed(10), 11), - Error::<Runtime>::NotSupported - ); - }); - } -} diff --git a/substrate/frame/nomination-pools/test-delegate-stake/src/lib.rs b/substrate/frame/nomination-pools/test-delegate-stake/src/lib.rs index cc6335959ab738115c88c4878bc3dc2d8bb6db81..54783332aa3ef245bced9e737ed5c8a89265e7d8 100644 --- a/substrate/frame/nomination-pools/test-delegate-stake/src/lib.rs +++ b/substrate/frame/nomination-pools/test-delegate-stake/src/lib.rs @@ -21,7 +21,10 @@ mod mock; use frame_support::{ assert_noop, assert_ok, hypothetically, - traits::{fungible::InspectHold, Currency}, + traits::{ + fungible::{InspectHold, Mutate}, + Currency, + }, }; use mock::*; use pallet_nomination_pools::{ @@ -942,9 +945,13 @@ fn pool_slash_non_proportional_bonded_pool_and_chunks() { fn pool_migration_e2e() { new_test_ext().execute_with(|| { LegacyAdapter::set(true); - assert_eq!(Balances::minimum_balance(), 5); assert_eq!(CurrentEra::<T>::get(), None); + // hack: mint ED to pool so that the deprecated `TransferStake` works correctly with + // staking. + assert_eq!(Balances::minimum_balance(), 5); + assert_ok!(Balances::mint_into(&POOL1_BONDED, 5)); + // create the pool with TransferStake strategy. assert_ok!(Pools::create(RuntimeOrigin::signed(10), 50, 10, 10, 10)); assert_eq!(LastPoolId::<Runtime>::get(), 1); @@ -1050,10 +1057,11 @@ fn pool_migration_e2e() { assert_eq!( delegated_staking_events_since_last_call(), + // delegated also contains the extra ED that we minted when pool was `TransferStake` . vec![DelegatedStakingEvent::Delegated { agent: POOL1_BONDED, delegator: proxy_delegator_1, - amount: 50 + 10 * 3 + amount: 50 + 10 * 3 + 5 }] ); @@ -1223,6 +1231,11 @@ fn disable_pool_operations_on_non_migrated() { assert_eq!(Balances::minimum_balance(), 5); assert_eq!(CurrentEra::<T>::get(), None); + // hack: mint ED to pool so that the deprecated `TransferStake` works correctly with + // staking. + assert_eq!(Balances::minimum_balance(), 5); + assert_ok!(Balances::mint_into(&POOL1_BONDED, 5)); + // create the pool with TransferStake strategy. assert_ok!(Pools::create(RuntimeOrigin::signed(10), 50, 10, 10, 10)); assert_eq!(LastPoolId::<Runtime>::get(), 1); @@ -1331,11 +1344,12 @@ fn disable_pool_operations_on_non_migrated() { assert_ok!(Pools::migrate_pool_to_delegate_stake(RuntimeOrigin::signed(10), 1)); assert_eq!( delegated_staking_events_since_last_call(), + // delegated also contains the extra ED that we minted when pool was `TransferStake` . vec![DelegatedStakingEvent::Delegated { agent: POOL1_BONDED, delegator: DelegatedStaking::generate_proxy_delegator(Agent::from(POOL1_BONDED)) .get(), - amount: 50 + 10 + amount: 50 + 10 + 5 },] ); diff --git a/substrate/frame/nomination-pools/test-delegate-stake/src/mock.rs b/substrate/frame/nomination-pools/test-delegate-stake/src/mock.rs index d1bc4ef8ff281986532d069292b2fed7785e6ca4..d943ba6f533330453b8961886005b4f20be0804b 100644 --- a/substrate/frame/nomination-pools/test-delegate-stake/src/mock.rs +++ b/substrate/frame/nomination-pools/test-delegate-stake/src/mock.rs @@ -15,6 +15,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +// Disable warnings for `TransferStake` being deprecated. +#![allow(deprecated)] + use frame_election_provider_support::VoteWeight; use frame_support::{ assert_ok, derive_impl, @@ -92,6 +95,7 @@ parameter_types! { #[derive_impl(pallet_staking::config_preludes::TestDefaultConfig)] impl pallet_staking::Config for Runtime { + type OldCurrency = Balances; type Currency = Balances; type UnixTime = pallet_timestamp::Pallet<Self>; type AdminOrigin = frame_system::EnsureRoot<Self::AccountId>; diff --git a/substrate/frame/nomination-pools/test-transfer-stake/Cargo.toml b/substrate/frame/nomination-pools/test-transfer-stake/Cargo.toml deleted file mode 100644 index 0b21d5f4e8cf1e3a98e3220a17bfe7eeb0b44cc0..0000000000000000000000000000000000000000 --- a/substrate/frame/nomination-pools/test-transfer-stake/Cargo.toml +++ /dev/null @@ -1,39 +0,0 @@ -[package] -name = "pallet-nomination-pools-test-transfer-stake" -version = "1.0.0" -authors.workspace = true -edition.workspace = true -license = "Apache-2.0" -homepage.workspace = true -repository.workspace = true -description = "FRAME nomination pools pallet tests with the staking pallet" -publish = false - -[lints] -workspace = true - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dev-dependencies] -codec = { features = ["derive"], workspace = true, default-features = true } -scale-info = { features = ["derive"], workspace = true, default-features = true } - -sp-core = { workspace = true, default-features = true } -sp-io = { workspace = true, default-features = true } -sp-runtime = { workspace = true, default-features = true } -sp-staking = { workspace = true, default-features = true } - -frame-election-provider-support = { workspace = true, default-features = true } -frame-support = { workspace = true, default-features = true } -frame-system = { workspace = true, default-features = true } - -pallet-bags-list = { workspace = true, default-features = true } -pallet-balances = { workspace = true, default-features = true } -pallet-nomination-pools = { workspace = true, default-features = true } -pallet-staking = { workspace = true, default-features = true } -pallet-staking-reward-curve = { workspace = true, default-features = true } -pallet-timestamp = { workspace = true, default-features = true } - -log = { workspace = true, default-features = true } -sp-tracing = { workspace = true, default-features = true } diff --git a/substrate/frame/nomination-pools/test-transfer-stake/src/lib.rs b/substrate/frame/nomination-pools/test-transfer-stake/src/lib.rs deleted file mode 100644 index cc39cfee91c80d95db2b069023991f7647eb3b79..0000000000000000000000000000000000000000 --- a/substrate/frame/nomination-pools/test-transfer-stake/src/lib.rs +++ /dev/null @@ -1,912 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#![cfg(test)] - -mod mock; - -use frame_support::{assert_noop, assert_ok, traits::Currency}; -use mock::*; -use pallet_nomination_pools::{ - BondExtra, BondedPools, Error as PoolsError, Event as PoolsEvent, LastPoolId, PoolMember, - PoolMembers, PoolState, -}; -use pallet_staking::{ - CurrentEra, Error as StakingError, Event as StakingEvent, Payee, RewardDestination, -}; -use sp_runtime::{bounded_btree_map, traits::Zero}; - -#[test] -fn pool_lifecycle_e2e() { - new_test_ext().execute_with(|| { - assert_eq!(Balances::minimum_balance(), 5); - assert_eq!(CurrentEra::<T>::get(), None); - - // create the pool, we know this has id 1. - assert_ok!(Pools::create(RuntimeOrigin::signed(10), 50, 10, 10, 10)); - assert_eq!(LastPoolId::<Runtime>::get(), 1); - - // have the pool nominate. - assert_ok!(Pools::nominate(RuntimeOrigin::signed(10), 1, vec![1, 2, 3])); - - assert_eq!( - staking_events_since_last_call(), - vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: 50 }] - ); - assert_eq!( - pool_events_since_last_call(), - vec![ - PoolsEvent::Created { depositor: 10, pool_id: 1 }, - PoolsEvent::Bonded { member: 10, pool_id: 1, bonded: 50, joined: true }, - ] - ); - - // have two members join - assert_ok!(Pools::join(RuntimeOrigin::signed(20), 10, 1)); - assert_ok!(Pools::join(RuntimeOrigin::signed(21), 10, 1)); - - assert_eq!( - staking_events_since_last_call(), - vec![ - StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 }, - StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 }, - ] - ); - assert_eq!( - pool_events_since_last_call(), - vec![ - PoolsEvent::Bonded { member: 20, pool_id: 1, bonded: 10, joined: true }, - PoolsEvent::Bonded { member: 21, pool_id: 1, bonded: 10, joined: true }, - ] - ); - - // pool goes into destroying - assert_ok!(Pools::set_state(RuntimeOrigin::signed(10), 1, PoolState::Destroying)); - - // depositor cannot unbond yet. - assert_noop!( - Pools::unbond(RuntimeOrigin::signed(10), 10, 50), - PoolsError::<Runtime>::MinimumBondNotMet, - ); - - // now the members want to unbond. - assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 10)); - assert_ok!(Pools::unbond(RuntimeOrigin::signed(21), 21, 10)); - - assert_eq!(PoolMembers::<Runtime>::get(20).unwrap().unbonding_eras.len(), 1); - assert_eq!(PoolMembers::<Runtime>::get(20).unwrap().points, 0); - assert_eq!(PoolMembers::<Runtime>::get(21).unwrap().unbonding_eras.len(), 1); - assert_eq!(PoolMembers::<Runtime>::get(21).unwrap().points, 0); - - assert_eq!( - staking_events_since_last_call(), - vec![ - StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 }, - StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 }, - ] - ); - assert_eq!( - pool_events_since_last_call(), - vec![ - PoolsEvent::StateChanged { pool_id: 1, new_state: PoolState::Destroying }, - PoolsEvent::Unbonded { member: 20, pool_id: 1, points: 10, balance: 10, era: 3 }, - PoolsEvent::Unbonded { member: 21, pool_id: 1, points: 10, balance: 10, era: 3 }, - ] - ); - - // depositor cannot still unbond - assert_noop!( - Pools::unbond(RuntimeOrigin::signed(10), 10, 50), - PoolsError::<Runtime>::MinimumBondNotMet, - ); - - for e in 1..BondingDuration::get() { - CurrentEra::<Runtime>::set(Some(e)); - assert_noop!( - Pools::withdraw_unbonded(RuntimeOrigin::signed(20), 20, 0), - PoolsError::<Runtime>::CannotWithdrawAny - ); - } - - // members are now unlocked. - CurrentEra::<Runtime>::set(Some(BondingDuration::get())); - - // depositor cannot still unbond - assert_noop!( - Pools::unbond(RuntimeOrigin::signed(10), 10, 50), - PoolsError::<Runtime>::MinimumBondNotMet, - ); - - // but members can now withdraw. - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(20), 20, 0)); - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(21), 21, 0)); - assert!(PoolMembers::<Runtime>::get(20).is_none()); - assert!(PoolMembers::<Runtime>::get(21).is_none()); - - assert_eq!( - staking_events_since_last_call(), - vec![StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 20 },] - ); - assert_eq!( - pool_events_since_last_call(), - vec![ - PoolsEvent::Withdrawn { member: 20, pool_id: 1, points: 10, balance: 10 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 20, released_balance: 0 }, - PoolsEvent::Withdrawn { member: 21, pool_id: 1, points: 10, balance: 10 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 21, released_balance: 0 }, - ] - ); - - // as soon as all members have left, the depositor can try to unbond, but since the - // min-nominator intention is set, they must chill first. - assert_noop!( - Pools::unbond(RuntimeOrigin::signed(10), 10, 50), - pallet_staking::Error::<Runtime>::InsufficientBond - ); - - assert_ok!(Pools::chill(RuntimeOrigin::signed(10), 1)); - assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 50)); - - assert_eq!( - staking_events_since_last_call(), - vec![ - StakingEvent::Chilled { stash: POOL1_BONDED }, - StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 50 }, - ] - ); - assert_eq!( - pool_events_since_last_call(), - vec![PoolsEvent::Unbonded { member: 10, pool_id: 1, points: 50, balance: 50, era: 6 }] - ); - - // waiting another bonding duration: - CurrentEra::<Runtime>::set(Some(BondingDuration::get() * 2)); - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 1)); - - // pools is fully destroyed now. - assert_eq!( - staking_events_since_last_call(), - vec![StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 50 },] - ); - assert_eq!( - pool_events_since_last_call(), - vec![ - PoolsEvent::Withdrawn { member: 10, pool_id: 1, points: 50, balance: 50 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 10, released_balance: 0 }, - PoolsEvent::Destroyed { pool_id: 1 } - ] - ); - }) -} - -#[test] -fn destroy_pool_with_erroneous_consumer() { - new_test_ext().execute_with(|| { - // create the pool, we know this has id 1. - assert_ok!(Pools::create(RuntimeOrigin::signed(10), 50, 10, 10, 10)); - assert_eq!(LastPoolId::<Runtime>::get(), 1); - - // expect consumers on pool account to be 2 (staking lock and an explicit inc by staking). - assert_eq!(frame_system::Pallet::<T>::consumers(&POOL1_BONDED), 2); - - // increment consumer by 1 reproducing the erroneous consumer bug. - // refer https://github.com/paritytech/polkadot-sdk/issues/4440. - assert_ok!(frame_system::Pallet::<T>::inc_consumers(&POOL1_BONDED)); - assert_eq!(frame_system::Pallet::<T>::consumers(&POOL1_BONDED), 3); - - // have the pool nominate. - assert_ok!(Pools::nominate(RuntimeOrigin::signed(10), 1, vec![1, 2, 3])); - - assert_eq!( - staking_events_since_last_call(), - vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: 50 }] - ); - assert_eq!( - pool_events_since_last_call(), - vec![ - PoolsEvent::Created { depositor: 10, pool_id: 1 }, - PoolsEvent::Bonded { member: 10, pool_id: 1, bonded: 50, joined: true }, - ] - ); - - // pool goes into destroying - assert_ok!(Pools::set_state(RuntimeOrigin::signed(10), 1, PoolState::Destroying)); - - assert_eq!( - pool_events_since_last_call(), - vec![PoolsEvent::StateChanged { pool_id: 1, new_state: PoolState::Destroying },] - ); - - // move to era 1 - CurrentEra::<Runtime>::set(Some(1)); - - // depositor need to chill before unbonding - assert_noop!( - Pools::unbond(RuntimeOrigin::signed(10), 10, 50), - pallet_staking::Error::<Runtime>::InsufficientBond - ); - - assert_ok!(Pools::chill(RuntimeOrigin::signed(10), 1)); - assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 50)); - - assert_eq!( - staking_events_since_last_call(), - vec![ - StakingEvent::Chilled { stash: POOL1_BONDED }, - StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 50 }, - ] - ); - assert_eq!( - pool_events_since_last_call(), - vec![PoolsEvent::Unbonded { - member: 10, - pool_id: 1, - points: 50, - balance: 50, - era: 1 + 3 - }] - ); - - // waiting bonding duration: - CurrentEra::<Runtime>::set(Some(1 + 3)); - // this should work even with an extra consumer count on pool account. - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 1)); - - // pools is fully destroyed now. - assert_eq!( - staking_events_since_last_call(), - vec![StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 50 },] - ); - assert_eq!( - pool_events_since_last_call(), - vec![ - PoolsEvent::Withdrawn { member: 10, pool_id: 1, points: 50, balance: 50 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 10, released_balance: 0 }, - PoolsEvent::Destroyed { pool_id: 1 } - ] - ); - }) -} - -#[test] -fn pool_chill_e2e() { - new_test_ext().execute_with(|| { - assert_eq!(Balances::minimum_balance(), 5); - assert_eq!(CurrentEra::<T>::get(), None); - - // create the pool, we know this has id 1. - assert_ok!(Pools::create(RuntimeOrigin::signed(10), 50, 10, 10, 10)); - assert_eq!(LastPoolId::<Runtime>::get(), 1); - - // have the pool nominate. - assert_ok!(Pools::nominate(RuntimeOrigin::signed(10), 1, vec![1, 2, 3])); - - assert_eq!( - staking_events_since_last_call(), - vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: 50 }] - ); - assert_eq!( - pool_events_since_last_call(), - vec![ - PoolsEvent::Created { depositor: 10, pool_id: 1 }, - PoolsEvent::Bonded { member: 10, pool_id: 1, bonded: 50, joined: true }, - ] - ); - - // have two members join - assert_ok!(Pools::join(RuntimeOrigin::signed(20), 10, 1)); - assert_ok!(Pools::join(RuntimeOrigin::signed(21), 10, 1)); - - assert_eq!( - staking_events_since_last_call(), - vec![ - StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 }, - StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 }, - ] - ); - assert_eq!( - pool_events_since_last_call(), - vec![ - PoolsEvent::Bonded { member: 20, pool_id: 1, bonded: 10, joined: true }, - PoolsEvent::Bonded { member: 21, pool_id: 1, bonded: 10, joined: true }, - ] - ); - - // in case depositor does not have more than `MinNominatorBond` staked, we can end up in - // situation where a member unbonding would cause pool balance to drop below - // `MinNominatorBond` and hence not allowed. This can happen if the `MinNominatorBond` is - // increased after the pool is created. - assert_ok!(Staking::set_staking_configs( - RuntimeOrigin::root(), - pallet_staking::ConfigOp::Set(55), // minimum nominator bond - pallet_staking::ConfigOp::Noop, - pallet_staking::ConfigOp::Noop, - pallet_staking::ConfigOp::Noop, - pallet_staking::ConfigOp::Noop, - pallet_staking::ConfigOp::Noop, - pallet_staking::ConfigOp::Noop, - )); - - // members can unbond as long as total stake of the pool is above min nominator bond - assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 10),); - assert_eq!(PoolMembers::<Runtime>::get(20).unwrap().unbonding_eras.len(), 1); - assert_eq!(PoolMembers::<Runtime>::get(20).unwrap().points, 0); - - // this member cannot unbond since it will cause `pool stake < MinNominatorBond` - assert_noop!( - Pools::unbond(RuntimeOrigin::signed(21), 21, 10), - StakingError::<Runtime>::InsufficientBond, - ); - - // members can call `chill` permissionlessly now - assert_ok!(Pools::chill(RuntimeOrigin::signed(20), 1)); - - // now another member can unbond. - assert_ok!(Pools::unbond(RuntimeOrigin::signed(21), 21, 10)); - assert_eq!(PoolMembers::<Runtime>::get(21).unwrap().unbonding_eras.len(), 1); - assert_eq!(PoolMembers::<Runtime>::get(21).unwrap().points, 0); - - // nominator can not resume nomination until depositor have enough stake - assert_noop!( - Pools::nominate(RuntimeOrigin::signed(10), 1, vec![1, 2, 3]), - PoolsError::<Runtime>::MinimumBondNotMet, - ); - - // other members joining pool does not affect the depositor's ability to resume nomination - assert_ok!(Pools::join(RuntimeOrigin::signed(22), 10, 1)); - - assert_noop!( - Pools::nominate(RuntimeOrigin::signed(10), 1, vec![1, 2, 3]), - PoolsError::<Runtime>::MinimumBondNotMet, - ); - - // depositor can bond extra stake - assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(10), BondExtra::FreeBalance(10))); - - // `chill` can not be called permissionlessly anymore - assert_noop!( - Pools::chill(RuntimeOrigin::signed(20), 1), - PoolsError::<Runtime>::NotNominator, - ); - - // now nominator can resume nomination - assert_ok!(Pools::nominate(RuntimeOrigin::signed(10), 1, vec![1, 2, 3])); - - // skip to make the unbonding period end. - CurrentEra::<Runtime>::set(Some(BondingDuration::get())); - - // members can now withdraw. - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(20), 20, 0)); - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(21), 21, 0)); - - assert_eq!( - staking_events_since_last_call(), - vec![ - StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 }, - StakingEvent::Chilled { stash: POOL1_BONDED }, - StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 }, - StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 }, // other member bonding - StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 }, // depositor bond extra - StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 20 }, - ] - ); - }) -} - -#[test] -fn pool_slash_e2e() { - new_test_ext().execute_with(|| { - ExistentialDeposit::set(1); - assert_eq!(Balances::minimum_balance(), 1); - assert_eq!(CurrentEra::<T>::get(), None); - - // create the pool, we know this has id 1. - assert_ok!(Pools::create(RuntimeOrigin::signed(10), 40, 10, 10, 10)); - assert_eq!(LastPoolId::<Runtime>::get(), 1); - - assert_eq!( - staking_events_since_last_call(), - vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: 40 }] - ); - assert_eq!( - pool_events_since_last_call(), - vec![ - PoolsEvent::Created { depositor: 10, pool_id: 1 }, - PoolsEvent::Bonded { member: 10, pool_id: 1, bonded: 40, joined: true }, - ] - ); - - assert_eq!( - Payee::<Runtime>::get(POOL1_BONDED), - Some(RewardDestination::Account(POOL1_REWARD)) - ); - - // have two members join - assert_ok!(Pools::join(RuntimeOrigin::signed(20), 20, 1)); - assert_ok!(Pools::join(RuntimeOrigin::signed(21), 20, 1)); - - assert_eq!( - staking_events_since_last_call(), - vec![ - StakingEvent::Bonded { stash: POOL1_BONDED, amount: 20 }, - StakingEvent::Bonded { stash: POOL1_BONDED, amount: 20 } - ] - ); - assert_eq!( - pool_events_since_last_call(), - vec![ - PoolsEvent::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, - PoolsEvent::Bonded { member: 21, pool_id: 1, bonded: 20, joined: true }, - ] - ); - - // now let's progress a bit. - CurrentEra::<Runtime>::set(Some(1)); - - // 20 / 80 of the total funds are unlocked, and safe from any further slash. - assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 10)); - assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 10)); - - assert_eq!( - staking_events_since_last_call(), - vec![ - StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 }, - StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 } - ] - ); - assert_eq!( - pool_events_since_last_call(), - vec![ - PoolsEvent::Unbonded { member: 10, pool_id: 1, balance: 10, points: 10, era: 4 }, - PoolsEvent::Unbonded { member: 20, pool_id: 1, balance: 10, points: 10, era: 4 } - ] - ); - - CurrentEra::<Runtime>::set(Some(2)); - - // note: depositor cannot fully unbond at this point. - // these funds will still get slashed. - assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 10)); - assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 10)); - assert_ok!(Pools::unbond(RuntimeOrigin::signed(21), 21, 10)); - - assert_eq!( - staking_events_since_last_call(), - vec![ - StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 }, - StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 }, - StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 }, - ] - ); - - assert_eq!( - pool_events_since_last_call(), - vec![ - PoolsEvent::Unbonded { member: 10, pool_id: 1, balance: 10, points: 10, era: 5 }, - PoolsEvent::Unbonded { member: 20, pool_id: 1, balance: 10, points: 10, era: 5 }, - PoolsEvent::Unbonded { member: 21, pool_id: 1, balance: 10, points: 10, era: 5 }, - ] - ); - - // At this point, 20 are safe from slash, 30 are unlocking but vulnerable to slash, and - // another 30 are active and vulnerable to slash. Let's slash half of them. - pallet_staking::slashing::do_slash::<Runtime>( - &POOL1_BONDED, - 30, - &mut Default::default(), - &mut Default::default(), - 2, // slash era 2, affects chunks at era 5 onwards. - ); - - assert_eq!( - staking_events_since_last_call(), - vec![StakingEvent::Slashed { staker: POOL1_BONDED, amount: 30 }] - ); - assert_eq!( - pool_events_since_last_call(), - vec![ - // 30 has been slashed to 15 (15 slash) - PoolsEvent::UnbondingPoolSlashed { pool_id: 1, era: 5, balance: 15 }, - // 30 has been slashed to 15 (15 slash) - PoolsEvent::PoolSlashed { pool_id: 1, balance: 15 } - ] - ); - - CurrentEra::<Runtime>::set(Some(3)); - assert_ok!(Pools::unbond(RuntimeOrigin::signed(21), 21, 10)); - - assert_eq!( - PoolMembers::<Runtime>::get(21).unwrap(), - PoolMember { - pool_id: 1, - points: 0, - last_recorded_reward_counter: Zero::zero(), - // the 10 points unlocked just now correspond to 5 points in the unbond pool. - unbonding_eras: bounded_btree_map!(5 => 10, 6 => 5) - } - ); - assert_eq!( - staking_events_since_last_call(), - vec![StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 5 }] - ); - assert_eq!( - pool_events_since_last_call(), - vec![PoolsEvent::Unbonded { member: 21, pool_id: 1, balance: 5, points: 5, era: 6 }] - ); - - // now we start withdrawing. we do it all at once, at era 6 where 20 and 21 are fully free. - CurrentEra::<Runtime>::set(Some(6)); - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(20), 20, 0)); - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(21), 21, 0)); - - assert_eq!( - pool_events_since_last_call(), - vec![ - // 20 had unbonded 10 safely, and 10 got slashed by half. - PoolsEvent::Withdrawn { member: 20, pool_id: 1, balance: 10 + 5, points: 20 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 20, released_balance: 0 }, - // 21 unbonded all of it after the slash - PoolsEvent::Withdrawn { member: 21, pool_id: 1, balance: 5 + 5, points: 15 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 21, released_balance: 0 } - ] - ); - assert_eq!( - staking_events_since_last_call(), - // a 10 (un-slashed) + 10/2 (slashed) balance from 10 has also been unlocked - vec![StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 15 + 10 + 15 }] - ); - - // now, finally, we can unbond the depositor further than their current limit. - assert_ok!(Pools::set_state(RuntimeOrigin::signed(10), 1, PoolState::Destroying)); - assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 20)); - - assert_eq!( - staking_events_since_last_call(), - vec![StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 }] - ); - assert_eq!( - pool_events_since_last_call(), - vec![ - PoolsEvent::StateChanged { pool_id: 1, new_state: PoolState::Destroying }, - PoolsEvent::Unbonded { member: 10, pool_id: 1, points: 10, balance: 10, era: 9 } - ] - ); - - CurrentEra::<Runtime>::set(Some(9)); - assert_eq!( - PoolMembers::<Runtime>::get(10).unwrap(), - PoolMember { - pool_id: 1, - points: 0, - last_recorded_reward_counter: Zero::zero(), - unbonding_eras: bounded_btree_map!(4 => 10, 5 => 10, 9 => 10) - } - ); - // withdraw the depositor, they should lose 12 balance in total due to slash. - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 0)); - - assert_eq!( - staking_events_since_last_call(), - vec![StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 10 }] - ); - assert_eq!( - pool_events_since_last_call(), - vec![ - PoolsEvent::Withdrawn { member: 10, pool_id: 1, balance: 10 + 15, points: 30 }, - PoolsEvent::MemberRemoved { pool_id: 1, member: 10, released_balance: 0 }, - PoolsEvent::Destroyed { pool_id: 1 } - ] - ); - }); -} - -#[test] -fn pool_slash_proportional() { - // a typical example where 3 pool members unbond in era 99, 100, and 101, and a slash that - // happened in era 100 should only affect the latter two. - new_test_ext().execute_with(|| { - ExistentialDeposit::set(1); - BondingDuration::set(28); - assert_eq!(Balances::minimum_balance(), 1); - assert_eq!(CurrentEra::<T>::get(), None); - - // create the pool, we know this has id 1. - assert_ok!(Pools::create(RuntimeOrigin::signed(10), 40, 10, 10, 10)); - assert_eq!(LastPoolId::<T>::get(), 1); - - assert_eq!( - staking_events_since_last_call(), - vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: 40 }] - ); - assert_eq!( - pool_events_since_last_call(), - vec![ - PoolsEvent::Created { depositor: 10, pool_id: 1 }, - PoolsEvent::Bonded { member: 10, pool_id: 1, bonded: 40, joined: true }, - ] - ); - - // have two members join - let bond = 20; - assert_ok!(Pools::join(RuntimeOrigin::signed(20), bond, 1)); - assert_ok!(Pools::join(RuntimeOrigin::signed(21), bond, 1)); - assert_ok!(Pools::join(RuntimeOrigin::signed(22), bond, 1)); - - assert_eq!( - staking_events_since_last_call(), - vec![ - StakingEvent::Bonded { stash: POOL1_BONDED, amount: bond }, - StakingEvent::Bonded { stash: POOL1_BONDED, amount: bond }, - StakingEvent::Bonded { stash: POOL1_BONDED, amount: bond }, - ] - ); - assert_eq!( - pool_events_since_last_call(), - vec![ - PoolsEvent::Bonded { member: 20, pool_id: 1, bonded: bond, joined: true }, - PoolsEvent::Bonded { member: 21, pool_id: 1, bonded: bond, joined: true }, - PoolsEvent::Bonded { member: 22, pool_id: 1, bonded: bond, joined: true }, - ] - ); - - // now let's progress a lot. - CurrentEra::<T>::set(Some(99)); - - // and unbond - assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, bond)); - - assert_eq!( - staking_events_since_last_call(), - vec![StakingEvent::Unbonded { stash: POOL1_BONDED, amount: bond },] - ); - assert_eq!( - pool_events_since_last_call(), - vec![PoolsEvent::Unbonded { - member: 20, - pool_id: 1, - balance: bond, - points: bond, - era: 127 - }] - ); - - CurrentEra::<T>::set(Some(100)); - assert_ok!(Pools::unbond(RuntimeOrigin::signed(21), 21, bond)); - assert_eq!( - staking_events_since_last_call(), - vec![StakingEvent::Unbonded { stash: POOL1_BONDED, amount: bond },] - ); - assert_eq!( - pool_events_since_last_call(), - vec![PoolsEvent::Unbonded { - member: 21, - pool_id: 1, - balance: bond, - points: bond, - era: 128 - }] - ); - - CurrentEra::<T>::set(Some(101)); - assert_ok!(Pools::unbond(RuntimeOrigin::signed(22), 22, bond)); - assert_eq!( - staking_events_since_last_call(), - vec![StakingEvent::Unbonded { stash: POOL1_BONDED, amount: bond },] - ); - assert_eq!( - pool_events_since_last_call(), - vec![PoolsEvent::Unbonded { - member: 22, - pool_id: 1, - balance: bond, - points: bond, - era: 129 - }] - ); - - // Apply a slash that happened in era 100. This is typically applied with a delay. - // Of the total 100, 50 is slashed. - assert_eq!(BondedPools::<T>::get(1).unwrap().points, 40); - pallet_staking::slashing::do_slash::<Runtime>( - &POOL1_BONDED, - 50, - &mut Default::default(), - &mut Default::default(), - 100, - ); - - assert_eq!( - staking_events_since_last_call(), - vec![StakingEvent::Slashed { staker: POOL1_BONDED, amount: 50 }] - ); - assert_eq!( - pool_events_since_last_call(), - vec![ - // This era got slashed 12.5, which rounded up to 13. - PoolsEvent::UnbondingPoolSlashed { pool_id: 1, era: 128, balance: 7 }, - // This era got slashed 12 instead of 12.5 because an earlier chunk got 0.5 more - // slashed, and 12 is all the remaining slash - PoolsEvent::UnbondingPoolSlashed { pool_id: 1, era: 129, balance: 8 }, - // Bonded pool got slashed for 25, remaining 15 in it. - PoolsEvent::PoolSlashed { pool_id: 1, balance: 15 } - ] - ); - }); -} - -#[test] -fn pool_slash_non_proportional_only_bonded_pool() { - // A typical example where a pool member unbonds in era 99, and they can get away with a slash - // that happened in era 100, as long as the pool has enough active bond to cover the slash. If - // everything else in the slashing/staking system works, this should always be the case. - // Nonetheless, `ledger.slash` has been written such that it will slash greedily from any chunk - // if it runs out of chunks that it thinks should be affected by the slash. - new_test_ext().execute_with(|| { - ExistentialDeposit::set(1); - BondingDuration::set(28); - assert_eq!(Balances::minimum_balance(), 1); - assert_eq!(CurrentEra::<T>::get(), None); - - // create the pool, we know this has id 1. - assert_ok!(Pools::create(RuntimeOrigin::signed(10), 40, 10, 10, 10)); - assert_eq!( - staking_events_since_last_call(), - vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: 40 }] - ); - assert_eq!( - pool_events_since_last_call(), - vec![ - PoolsEvent::Created { depositor: 10, pool_id: 1 }, - PoolsEvent::Bonded { member: 10, pool_id: 1, bonded: 40, joined: true }, - ] - ); - - // have two members join - let bond = 20; - assert_ok!(Pools::join(RuntimeOrigin::signed(20), bond, 1)); - assert_eq!( - staking_events_since_last_call(), - vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: bond }] - ); - assert_eq!( - pool_events_since_last_call(), - vec![PoolsEvent::Bonded { member: 20, pool_id: 1, bonded: bond, joined: true }] - ); - - // progress and unbond. - CurrentEra::<T>::set(Some(99)); - assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, bond)); - assert_eq!( - staking_events_since_last_call(), - vec![StakingEvent::Unbonded { stash: POOL1_BONDED, amount: bond }] - ); - assert_eq!( - pool_events_since_last_call(), - vec![PoolsEvent::Unbonded { - member: 20, - pool_id: 1, - balance: bond, - points: bond, - era: 127 - }] - ); - - // slash for 30. This will be deducted only from the bonded pool. - CurrentEra::<T>::set(Some(100)); - assert_eq!(BondedPools::<T>::get(1).unwrap().points, 40); - pallet_staking::slashing::do_slash::<Runtime>( - &POOL1_BONDED, - 30, - &mut Default::default(), - &mut Default::default(), - 100, - ); - - assert_eq!( - staking_events_since_last_call(), - vec![StakingEvent::Slashed { staker: POOL1_BONDED, amount: 30 }] - ); - assert_eq!( - pool_events_since_last_call(), - vec![PoolsEvent::PoolSlashed { pool_id: 1, balance: 10 }] - ); - }); -} - -#[test] -fn pool_slash_non_proportional_bonded_pool_and_chunks() { - // An uncommon example where even though some funds are unlocked such that they should not be - // affected by a slash, we still slash out of them. This should not happen at all. If a - // nomination has unbonded, from the next era onwards, their exposure will drop, so if an era - // happens in that era, then their share of that slash should naturally be less, such that only - // their active ledger stake is enough to compensate it. - new_test_ext().execute_with(|| { - ExistentialDeposit::set(1); - BondingDuration::set(28); - assert_eq!(Balances::minimum_balance(), 1); - assert_eq!(CurrentEra::<T>::get(), None); - - // create the pool, we know this has id 1. - assert_ok!(Pools::create(RuntimeOrigin::signed(10), 40, 10, 10, 10)); - assert_eq!( - staking_events_since_last_call(), - vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: 40 }] - ); - assert_eq!( - pool_events_since_last_call(), - vec![ - PoolsEvent::Created { depositor: 10, pool_id: 1 }, - PoolsEvent::Bonded { member: 10, pool_id: 1, bonded: 40, joined: true }, - ] - ); - - // have two members join - let bond = 20; - assert_ok!(Pools::join(RuntimeOrigin::signed(20), bond, 1)); - assert_eq!( - staking_events_since_last_call(), - vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: bond }] - ); - assert_eq!( - pool_events_since_last_call(), - vec![PoolsEvent::Bonded { member: 20, pool_id: 1, bonded: bond, joined: true }] - ); - - // progress and unbond. - CurrentEra::<T>::set(Some(99)); - assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, bond)); - assert_eq!( - staking_events_since_last_call(), - vec![StakingEvent::Unbonded { stash: POOL1_BONDED, amount: bond }] - ); - assert_eq!( - pool_events_since_last_call(), - vec![PoolsEvent::Unbonded { - member: 20, - pool_id: 1, - balance: bond, - points: bond, - era: 127 - }] - ); - - // slash 50. This will be deducted only from the bonded pool and one of the unbonding pools. - CurrentEra::<T>::set(Some(100)); - assert_eq!(BondedPools::<T>::get(1).unwrap().points, 40); - pallet_staking::slashing::do_slash::<Runtime>( - &POOL1_BONDED, - 50, - &mut Default::default(), - &mut Default::default(), - 100, - ); - - assert_eq!( - staking_events_since_last_call(), - vec![StakingEvent::Slashed { staker: POOL1_BONDED, amount: 50 }] - ); - assert_eq!( - pool_events_since_last_call(), - vec![ - // out of 20, 10 was taken. - PoolsEvent::UnbondingPoolSlashed { pool_id: 1, era: 127, balance: 10 }, - // out of 40, all was taken. - PoolsEvent::PoolSlashed { pool_id: 1, balance: 0 } - ] - ); - }); -} diff --git a/substrate/frame/nomination-pools/test-transfer-stake/src/mock.rs b/substrate/frame/nomination-pools/test-transfer-stake/src/mock.rs deleted file mode 100644 index d913c5fe6948cb5a0fae1d5ccf8e533e5ddb8cc7..0000000000000000000000000000000000000000 --- a/substrate/frame/nomination-pools/test-transfer-stake/src/mock.rs +++ /dev/null @@ -1,231 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use frame_election_provider_support::VoteWeight; -use frame_support::{ - assert_ok, derive_impl, - pallet_prelude::*, - parameter_types, - traits::{ConstU64, ConstU8, VariantCountOf}, - PalletId, -}; -use sp_runtime::{ - traits::{Convert, IdentityLookup}, - BuildStorage, FixedU128, Perbill, -}; - -type AccountId = u128; -type BlockNumber = u64; -type Balance = u128; - -pub(crate) type T = Runtime; - -pub(crate) const POOL1_BONDED: AccountId = 20318131474730217858575332831085u128; -pub(crate) const POOL1_REWARD: AccountId = 20397359637244482196168876781421u128; - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] -impl frame_system::Config for Runtime { - type AccountId = AccountId; - type Lookup = IdentityLookup<Self::AccountId>; - type Block = Block; - type AccountData = pallet_balances::AccountData<Balance>; -} - -impl pallet_timestamp::Config for Runtime { - type Moment = u64; - type OnTimestampSet = (); - type MinimumPeriod = ConstU64<5>; - type WeightInfo = (); -} - -parameter_types! { - pub static ExistentialDeposit: Balance = 5; -} - -#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] -impl pallet_balances::Config for Runtime { - type Balance = Balance; - type ExistentialDeposit = ExistentialDeposit; - type AccountStore = System; - type FreezeIdentifier = RuntimeFreezeReason; - type MaxFreezes = VariantCountOf<RuntimeFreezeReason>; - type RuntimeFreezeReason = RuntimeFreezeReason; -} - -pallet_staking_reward_curve::build! { - const I_NPOS: sp_runtime::curve::PiecewiseLinear<'static> = curve!( - min_inflation: 0_025_000, - max_inflation: 0_100_000, - ideal_stake: 0_500_000, - falloff: 0_050_000, - max_piece_count: 40, - test_precision: 0_005_000, - ); -} - -parameter_types! { - pub const RewardCurve: &'static sp_runtime::curve::PiecewiseLinear<'static> = &I_NPOS; - pub static BondingDuration: u32 = 3; -} - -#[derive_impl(pallet_staking::config_preludes::TestDefaultConfig)] -impl pallet_staking::Config for Runtime { - type Currency = Balances; - type UnixTime = pallet_timestamp::Pallet<Self>; - type AdminOrigin = frame_system::EnsureRoot<Self::AccountId>; - type BondingDuration = BondingDuration; - type EraPayout = pallet_staking::ConvertCurve<RewardCurve>; - type ElectionProvider = - frame_election_provider_support::NoElection<(AccountId, BlockNumber, Staking, ())>; - type GenesisElectionProvider = Self::ElectionProvider; - type VoterList = VoterList; - type TargetList = pallet_staking::UseValidatorsMap<Self>; - type EventListeners = Pools; - type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig; -} - -parameter_types! { - pub static BagThresholds: &'static [VoteWeight] = &[10, 20, 30, 40, 50, 60, 1_000, 2_000, 10_000]; -} - -type VoterBagsListInstance = pallet_bags_list::Instance1; -impl pallet_bags_list::Config<VoterBagsListInstance> for Runtime { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); - type BagThresholds = BagThresholds; - type ScoreProvider = Staking; - type Score = VoteWeight; -} - -pub struct BalanceToU256; -impl Convert<Balance, sp_core::U256> for BalanceToU256 { - fn convert(n: Balance) -> sp_core::U256 { - n.into() - } -} - -pub struct U256ToBalance; -impl Convert<sp_core::U256, Balance> for U256ToBalance { - fn convert(n: sp_core::U256) -> Balance { - n.try_into().unwrap() - } -} - -parameter_types! { - pub const PostUnbondingPoolsWindow: u32 = 10; - pub const PoolsPalletId: PalletId = PalletId(*b"py/nopls"); -} - -impl pallet_nomination_pools::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); - type Currency = Balances; - type RuntimeFreezeReason = RuntimeFreezeReason; - type RewardCounter = FixedU128; - type BalanceToU256 = BalanceToU256; - type U256ToBalance = U256ToBalance; - type StakeAdapter = pallet_nomination_pools::adapter::TransferStake<Self, Staking>; - type PostUnbondingPoolsWindow = PostUnbondingPoolsWindow; - type MaxMetadataLen = ConstU32<256>; - type MaxUnbonding = ConstU32<8>; - type MaxPointsToBalance = ConstU8<10>; - type PalletId = PoolsPalletId; - type AdminOrigin = frame_system::EnsureRoot<Self::AccountId>; -} - -type Block = frame_system::mocking::MockBlock<Runtime>; - -frame_support::construct_runtime!( - pub enum Runtime { - System: frame_system, - Timestamp: pallet_timestamp, - Balances: pallet_balances, - Staking: pallet_staking, - VoterList: pallet_bags_list::<Instance1>, - Pools: pallet_nomination_pools, - } -); - -pub fn new_test_ext() -> sp_io::TestExternalities { - sp_tracing::try_init_simple(); - let mut storage = frame_system::GenesisConfig::<Runtime>::default().build_storage().unwrap(); - let _ = pallet_nomination_pools::GenesisConfig::<Runtime> { - min_join_bond: 2, - min_create_bond: 2, - max_pools: Some(3), - max_members_per_pool: Some(5), - max_members: Some(3 * 5), - global_max_commission: Some(Perbill::from_percent(90)), - } - .assimilate_storage(&mut storage) - .unwrap(); - - let _ = pallet_balances::GenesisConfig::<Runtime> { - balances: vec![(10, 100), (20, 100), (21, 100), (22, 100)], - } - .assimilate_storage(&mut storage) - .unwrap(); - - let mut ext = sp_io::TestExternalities::from(storage); - - ext.execute_with(|| { - // for events to be deposited. - frame_system::Pallet::<Runtime>::set_block_number(1); - - // set some limit for nominations. - assert_ok!(Staking::set_staking_configs( - RuntimeOrigin::root(), - pallet_staking::ConfigOp::Set(10), // minimum nominator bond - pallet_staking::ConfigOp::Noop, - pallet_staking::ConfigOp::Noop, - pallet_staking::ConfigOp::Noop, - pallet_staking::ConfigOp::Noop, - pallet_staking::ConfigOp::Noop, - pallet_staking::ConfigOp::Noop, - )); - }); - - ext -} - -parameter_types! { - static ObservedEventsPools: usize = 0; - static ObservedEventsStaking: usize = 0; - static ObservedEventsBalances: usize = 0; -} - -pub(crate) fn pool_events_since_last_call() -> Vec<pallet_nomination_pools::Event<Runtime>> { - let events = System::events() - .into_iter() - .map(|r| r.event) - .filter_map(|e| if let RuntimeEvent::Pools(inner) = e { Some(inner) } else { None }) - .collect::<Vec<_>>(); - let already_seen = ObservedEventsPools::get(); - ObservedEventsPools::set(events.len()); - events.into_iter().skip(already_seen).collect() -} - -pub(crate) fn staking_events_since_last_call() -> Vec<pallet_staking::Event<Runtime>> { - let events = System::events() - .into_iter() - .map(|r| r.event) - .filter_map(|e| if let RuntimeEvent::Staking(inner) = e { Some(inner) } else { None }) - .collect::<Vec<_>>(); - let already_seen = ObservedEventsStaking::get(); - ObservedEventsStaking::set(events.len()); - events.into_iter().skip(already_seen).collect() -} diff --git a/substrate/frame/offences/benchmarking/src/inner.rs b/substrate/frame/offences/benchmarking/src/inner.rs index 75f3e9931e34c3d8827049d76d0e5510086ccdba..3d3cd470bc24cc3fd63eaf09a52b99ee5eb7b33e 100644 --- a/substrate/frame/offences/benchmarking/src/inner.rs +++ b/substrate/frame/offences/benchmarking/src/inner.rs @@ -180,16 +180,12 @@ where <T as frame_system::Config>::RuntimeEvent: TryInto<frame_system::Event<T>>, { // make sure that all slashes have been applied - // (n nominators + one validator) * (slashed + unlocked) + deposit to reporter + - // reporter account endowed + some funds rescinded from issuance. - assert_eq!( - System::<T>::read_events_for_pallet::<pallet_balances::Event<T>>().len(), - 2 * (offender_count + 1) + 3 - ); + // deposit to reporter + reporter account endowed. + assert_eq!(System::<T>::read_events_for_pallet::<pallet_balances::Event<T>>().len(), 2); // (n nominators + one validator) * slashed + Slash Reported assert_eq!( System::<T>::read_events_for_pallet::<pallet_staking::Event<T>>().len(), - 1 * (offender_count + 1) + 1 + 1 * (offender_count + 1) as usize + 1 ); // offence assert_eq!(System::<T>::read_events_for_pallet::<pallet_offences::Event>().len(), 1); diff --git a/substrate/frame/offences/benchmarking/src/mock.rs b/substrate/frame/offences/benchmarking/src/mock.rs index c5c178aa4443dbd44c38b56e212fcfa1599cbfb6..3c81f2a664e32138f5fee5f43aab30e1f7ce6819 100644 --- a/substrate/frame/offences/benchmarking/src/mock.rs +++ b/substrate/frame/offences/benchmarking/src/mock.rs @@ -125,6 +125,7 @@ impl onchain::Config for OnChainSeqPhragmen { #[derive_impl(pallet_staking::config_preludes::TestDefaultConfig)] impl pallet_staking::Config for Test { + type OldCurrency = Balances; type Currency = Balances; type CurrencyBalance = <Self as pallet_balances::Config>::Balance; type UnixTime = pallet_timestamp::Pallet<Self>; diff --git a/substrate/frame/proxy/Cargo.toml b/substrate/frame/proxy/Cargo.toml index a36b2c1cb9c3af5dc545f35d4788d8a043f1a77e..3f2565abac88d2653781046d111c1f50fb757393 100644 --- a/substrate/frame/proxy/Cargo.toml +++ b/substrate/frame/proxy/Cargo.toml @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { features = ["max-encoded-len"], workspace = true } -frame = { workspace = true, features = ["experimental", "runtime"] } +frame = { workspace = true, features = ["runtime"] } scale-info = { features = ["derive"], workspace = true } [dev-dependencies] diff --git a/substrate/frame/revive/Cargo.toml b/substrate/frame/revive/Cargo.toml index 1284f5ee8947b6ff8d0d6990ce45ba8710ab2711..0959cc50638ba51a671c3ad8311d7e5745ef086f 100644 --- a/substrate/frame/revive/Cargo.toml +++ b/substrate/frame/revive/Cargo.toml @@ -20,12 +20,13 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { features = ["derive", "max-encoded-len"], workspace = true } derive_more = { workspace = true } environmental = { workspace = true } +ethabi = { workspace = true } ethereum-types = { workspace = true, features = ["codec", "rlp", "serialize"] } hex = { workspace = true } impl-trait-for-tuples = { workspace = true } log = { workspace = true } paste = { workspace = true } -polkavm = { version = "0.18.0", default-features = false } +polkavm = { version = "0.19.0", default-features = false } rlp = { workspace = true } scale-info = { features = ["derive"], workspace = true } serde = { features = [ @@ -75,6 +76,7 @@ default = ["std"] std = [ "codec/std", "environmental/std", + "ethabi/std", "ethereum-types/std", "frame-benchmarking?/std", "frame-support/std", diff --git a/substrate/frame/revive/README.md b/substrate/frame/revive/README.md index 575920dfaac79f6188b0b81c8f747dd364f13a12..7538f77d10bc07cbed79dbc6eff82869f1a73673 100644 --- a/substrate/frame/revive/README.md +++ b/substrate/frame/revive/README.md @@ -49,29 +49,6 @@ This module executes PolkaVM smart contracts. These can potentially be written i RISC-V. For now, the only officially supported languages are Solidity (via [`revive`](https://github.com/xermicus/revive)) and Rust (check the `fixtures` directory for Rust examples). -## Debugging - -Contracts can emit messages to the client when called as RPC through the -[`debug_message`](https://paritytech.github.io/substrate/master/pallet_revive/trait.SyscallDocs.html#tymethod.debug_message) -API. - -Those messages are gathered into an internal buffer and sent to the RPC client. It is up to the individual client if -and how those messages are presented to the user. - -This buffer is also printed as a debug message. In order to see these messages on the node console the log level for the -`runtime::revive` target needs to be raised to at least the `debug` level. However, those messages are easy to -overlook because of the noise generated by block production. A good starting point for observing them on the console is -using this command line in the root directory of the Substrate repository: - -```bash -cargo run --release -- --dev -lerror,runtime::revive=debug -``` - -This raises the log level of `runtime::revive` to `debug` and all other targets to `error` in order to prevent them -from spamming the console. - -`--dev`: Use a dev chain spec `--tmp`: Use temporary storage for chain data (the chain state is deleted on exit) - ## Host function tracing For contract authors, it can be a helpful debugging tool to see which host functions are called, with which arguments, diff --git a/substrate/frame/revive/fixtures/Cargo.toml b/substrate/frame/revive/fixtures/Cargo.toml index e17bc88a3847a660500671d85c234dfb6d5e0b80..a6f25cc26f3c040ed9db6b56b9b402d8319ee3b1 100644 --- a/substrate/frame/revive/fixtures/Cargo.toml +++ b/substrate/frame/revive/fixtures/Cargo.toml @@ -21,7 +21,7 @@ sp-io = { workspace = true, default-features = true, optional = true } [build-dependencies] anyhow = { workspace = true, default-features = true } -polkavm-linker = { version = "0.18.0" } +polkavm-linker = { version = "0.19.0" } toml = { workspace = true } [features] diff --git a/substrate/frame/revive/fixtures/build/_Cargo.toml b/substrate/frame/revive/fixtures/build/_Cargo.toml index bfb9aaedd6f5cfa2535aa023f4ca68ba5f835cee..1a0a635420ad5acf02c55302f26c904cceb0f1b4 100644 --- a/substrate/frame/revive/fixtures/build/_Cargo.toml +++ b/substrate/frame/revive/fixtures/build/_Cargo.toml @@ -14,7 +14,8 @@ edition = "2021" [dependencies] uapi = { package = 'pallet-revive-uapi', path = "", features = ["unstable-hostfn"], default-features = false } common = { package = 'pallet-revive-fixtures-common', path = "" } -polkavm-derive = { version = "0.18.0" } +hex-literal = { version = "0.4.1", default-features = false } +polkavm-derive = { version = "0.19.0" } [profile.release] opt-level = 3 diff --git a/substrate/frame/revive/fixtures/contracts/debug_message_logging_disabled.rs b/substrate/frame/revive/fixtures/contracts/debug_message_logging_disabled.rs deleted file mode 100644 index 0ce2b6b5628da348415e5a39161576161b39146b..0000000000000000000000000000000000000000 --- a/substrate/frame/revive/fixtures/contracts/debug_message_logging_disabled.rs +++ /dev/null @@ -1,33 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Emit a "Hello World!" debug message but assume that logging is disabled. -#![no_std] -#![no_main] - -extern crate common; -use uapi::{HostFn, HostFnImpl as api, ReturnErrorCode}; - -#[no_mangle] -#[polkavm_derive::polkavm_export] -pub extern "C" fn deploy() {} - -#[no_mangle] -#[polkavm_derive::polkavm_export] -pub extern "C" fn call() { - assert_eq!(api::debug_message(b"Hello World!"), Err(ReturnErrorCode::LoggingDisabled)); -} diff --git a/substrate/frame/revive/fixtures/contracts/debug_message_works.rs b/substrate/frame/revive/fixtures/contracts/to_account_id.rs similarity index 78% rename from substrate/frame/revive/fixtures/contracts/debug_message_works.rs rename to substrate/frame/revive/fixtures/contracts/to_account_id.rs index 3a2509509d8f156300a256d1a5b0494ea961f5ef..c2a8fce3ec995c9cd28aa31584f6dd66948d6a25 100644 --- a/substrate/frame/revive/fixtures/contracts/debug_message_works.rs +++ b/substrate/frame/revive/fixtures/contracts/to_account_id.rs @@ -15,11 +15,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Emit a "Hello World!" debug message. #![no_std] #![no_main] -extern crate common; +use common::input; use uapi::{HostFn, HostFnImpl as api}; #[no_mangle] @@ -29,5 +28,13 @@ pub extern "C" fn deploy() {} #[no_mangle] #[polkavm_derive::polkavm_export] pub extern "C" fn call() { - api::debug_message(b"Hello World!").unwrap(); + input!( + address: &[u8; 20], + expected_account_id: &[u8; 32], + ); + + let mut account_id = [0u8; 32]; + api::to_account_id(address, &mut account_id); + + assert!(&account_id == expected_account_id); } diff --git a/substrate/frame/revive/fixtures/contracts/tracing.rs b/substrate/frame/revive/fixtures/contracts/tracing.rs new file mode 100644 index 0000000000000000000000000000000000000000..9cbef3bbc84355dc89968347248d4daed68aedad --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/tracing.rs @@ -0,0 +1,75 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This fixture calls itself as many times as passed as argument. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(calls_left: u32, callee_addr: &[u8; 20],); + if calls_left == 0 { + return + } + + let next_input = (calls_left - 1).to_le_bytes(); + api::deposit_event(&[], b"before"); + + // Call the callee, ignore revert. + let _ = api::call( + uapi::CallFlags::empty(), + callee_addr, + u64::MAX, // How much ref_time to devote for the execution. u64::MAX = use all. + u64::MAX, // How much proof_size to devote for the execution. u64::MAX = use all. + &[u8::MAX; 32], // No deposit limit. + &[0u8; 32], // Value transferred to the contract. + &next_input, + None, + ); + + api::deposit_event(&[], b"after"); + + // own address + let mut addr = [0u8; 20]; + api::address(&mut addr); + let mut input = [0u8; 24]; + + input[..4].copy_from_slice(&next_input); + input[4..24].copy_from_slice(&callee_addr[..20]); + + // recurse + api::call( + uapi::CallFlags::ALLOW_REENTRY, + &addr, + u64::MAX, // How much ref_time to devote for the execution. u64::MAX = use all. + u64::MAX, // How much proof_size to devote for the execution. u64::MAX = use all. + &[u8::MAX; 32], // No deposit limit. + &[0u8; 32], // Value transferred to the contract. + &input, + None, + ) + .unwrap(); +} diff --git a/substrate/frame/revive/fixtures/contracts/debug_message_invalid_utf8.rs b/substrate/frame/revive/fixtures/contracts/tracing_callee.rs similarity index 62% rename from substrate/frame/revive/fixtures/contracts/debug_message_invalid_utf8.rs rename to substrate/frame/revive/fixtures/contracts/tracing_callee.rs index 6c850a9ec66312a65e183ce04e7acdc6166093fb..d44771e417f9df5fd8c13277a2b4f5c0d7afe8f3 100644 --- a/substrate/frame/revive/fixtures/contracts/debug_message_invalid_utf8.rs +++ b/substrate/frame/revive/fixtures/contracts/tracing_callee.rs @@ -14,12 +14,10 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - -//! Emit a debug message with an invalid utf-8 code. #![no_std] #![no_main] -extern crate common; +use common::input; use uapi::{HostFn, HostFnImpl as api}; #[no_mangle] @@ -29,5 +27,19 @@ pub extern "C" fn deploy() {} #[no_mangle] #[polkavm_derive::polkavm_export] pub extern "C" fn call() { - api::debug_message(b"\xFC").unwrap(); + input!(id: u32, ); + + match id { + // Revert with message "This function always fails" + 2 => { + let data = hex_literal::hex!( + "08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001a546869732066756e6374696f6e20616c77617973206661696c73000000000000" + ); + api::return_value(uapi::ReturnFlags::REVERT, &data) + }, + 1 => { + panic!("booum"); + }, + _ => api::return_value(uapi::ReturnFlags::empty(), &id.to_le_bytes()), + }; } diff --git a/substrate/frame/revive/proc-macro/src/lib.rs b/substrate/frame/revive/proc-macro/src/lib.rs index b09bdef14632b937a351cea6b2968e889f2202a0..6e38063d20a674c6c567835ced35def5c0858ac2 100644 --- a/substrate/frame/revive/proc-macro/src/lib.rs +++ b/substrate/frame/revive/proc-macro/src/lib.rs @@ -510,12 +510,7 @@ fn expand_functions(def: &EnvDef) -> TokenStream2 { quote! { // wrap body in closure to make sure the tracing is always executed let result = (|| #body)(); - if ::log::log_enabled!(target: "runtime::revive::strace", ::log::Level::Trace) { - use core::fmt::Write; - let mut msg = alloc::string::String::default(); - let _ = core::write!(&mut msg, #trace_fmt_str, #( #trace_fmt_args, )* result); - self.ext().append_debug_buffer(&msg); - } + ::log::trace!(target: "runtime::revive::strace", #trace_fmt_str, #( #trace_fmt_args, )* result); result } }; diff --git a/substrate/frame/revive/rpc/examples/js/pvm/Errors.polkavm b/substrate/frame/revive/rpc/examples/js/pvm/Errors.polkavm index 77de4ff3b1b3fe1f378ae31bbba24ddb38cc6300..48de6e0aa0c6cc1604008ba4e65c237dd557675b 100644 Binary files a/substrate/frame/revive/rpc/examples/js/pvm/Errors.polkavm and b/substrate/frame/revive/rpc/examples/js/pvm/Errors.polkavm differ diff --git a/substrate/frame/revive/rpc/examples/js/pvm/EventExample.polkavm b/substrate/frame/revive/rpc/examples/js/pvm/EventExample.polkavm index 6dbc5ca8b108c1ad04cc248b735b2d7d4f43f2a4..cea22e46adcad0dc9bf6375e676dd4923c624c89 100644 Binary files a/substrate/frame/revive/rpc/examples/js/pvm/EventExample.polkavm and b/substrate/frame/revive/rpc/examples/js/pvm/EventExample.polkavm differ diff --git a/substrate/frame/revive/rpc/examples/js/pvm/Flipper.polkavm b/substrate/frame/revive/rpc/examples/js/pvm/Flipper.polkavm index 488ee684f0c4aee5d64f8b691d048ebefdd51044..67f11e68f117309169a317d415ba547020c1f568 100644 Binary files a/substrate/frame/revive/rpc/examples/js/pvm/Flipper.polkavm and b/substrate/frame/revive/rpc/examples/js/pvm/Flipper.polkavm differ diff --git a/substrate/frame/revive/rpc/examples/js/pvm/FlipperCaller.polkavm b/substrate/frame/revive/rpc/examples/js/pvm/FlipperCaller.polkavm index 38a1098fe3a767aa0af74764bf7247e59f6110b7..29efafd8722db556b949b04c47c88eeec07535a6 100644 Binary files a/substrate/frame/revive/rpc/examples/js/pvm/FlipperCaller.polkavm and b/substrate/frame/revive/rpc/examples/js/pvm/FlipperCaller.polkavm differ diff --git a/substrate/frame/revive/rpc/examples/js/pvm/PiggyBank.polkavm b/substrate/frame/revive/rpc/examples/js/pvm/PiggyBank.polkavm index d0082db90e5e398832e4a32a9ec86dce83d16dd5..78455fcdd7c64a3a1f5e93b6d62cd03b46eb5953 100644 Binary files a/substrate/frame/revive/rpc/examples/js/pvm/PiggyBank.polkavm and b/substrate/frame/revive/rpc/examples/js/pvm/PiggyBank.polkavm differ diff --git a/substrate/frame/revive/rpc/revive_chain.metadata b/substrate/frame/revive/rpc/revive_chain.metadata index 402e8c2d22b21471929e9c61acd2cc968af614cf..a03c95b4944f663225642b1678ef66aaccec3fb5 100644 Binary files a/substrate/frame/revive/rpc/revive_chain.metadata and b/substrate/frame/revive/rpc/revive_chain.metadata differ diff --git a/substrate/frame/revive/rpc/src/client.rs b/substrate/frame/revive/rpc/src/client.rs index cd0effe7faf2f16084e665edd77eaa8449c8973b..c61c5871f76aeda6ae808d5215b5243f112ebba5 100644 --- a/substrate/frame/revive/rpc/src/client.rs +++ b/substrate/frame/revive/rpc/src/client.rs @@ -27,8 +27,9 @@ use crate::{ use jsonrpsee::types::{error::CALL_EXECUTION_FAILED_CODE, ErrorObjectOwned}; use pallet_revive::{ evm::{ - Block, BlockNumberOrTag, BlockNumberOrTagOrHash, GenericTransaction, ReceiptInfo, - SyncingProgress, SyncingStatus, TransactionSigned, H160, H256, U256, + extract_revert_message, Block, BlockNumberOrTag, BlockNumberOrTagOrHash, + GenericTransaction, ReceiptInfo, SyncingProgress, SyncingStatus, TransactionSigned, H160, + H256, U256, }, EthTransactError, EthTransactInfo, }; @@ -83,47 +84,6 @@ fn unwrap_call_err(err: &subxt::error::RpcError) -> Option<ErrorObjectOwned> { } } -/// Extract the revert message from a revert("msg") solidity statement. -fn extract_revert_message(exec_data: &[u8]) -> Option<String> { - let error_selector = exec_data.get(0..4)?; - - match error_selector { - // assert(false) - [0x4E, 0x48, 0x7B, 0x71] => { - let panic_code: u32 = U256::from_big_endian(exec_data.get(4..36)?).try_into().ok()?; - - // See https://docs.soliditylang.org/en/latest/control-structures.html#panic-via-assert-and-error-via-require - let msg = match panic_code { - 0x00 => "generic panic", - 0x01 => "assert(false)", - 0x11 => "arithmetic underflow or overflow", - 0x12 => "division or modulo by zero", - 0x21 => "enum overflow", - 0x22 => "invalid encoded storage byte array accessed", - 0x31 => "out-of-bounds array access; popping on an empty array", - 0x32 => "out-of-bounds access of an array or bytesN", - 0x41 => "out of memory", - 0x51 => "uninitialized function", - code => return Some(format!("execution reverted: unknown panic code: {code:#x}")), - }; - - Some(format!("execution reverted: {msg}")) - }, - // revert(string) - [0x08, 0xC3, 0x79, 0xA0] => { - let decoded = ethabi::decode(&[ethabi::ParamType::String], &exec_data[4..]).ok()?; - if let Some(ethabi::Token::String(msg)) = decoded.first() { - return Some(format!("execution reverted: {msg}")) - } - Some("execution reverted".to_string()) - }, - _ => { - log::debug!(target: LOG_TARGET, "Unknown revert function selector: {error_selector:?}"); - Some("execution reverted".to_string()) - }, - } -} - /// The error type for the client. #[derive(Error, Debug)] pub enum ClientError { diff --git a/substrate/frame/revive/src/benchmarking/call_builder.rs b/substrate/frame/revive/src/benchmarking/call_builder.rs index 1177d47aadc3f2303533ef1e73258883c8e2fb1e..077e18ff5f0b805654d91c86f040e5269819eb67 100644 --- a/substrate/frame/revive/src/benchmarking/call_builder.rs +++ b/substrate/frame/revive/src/benchmarking/call_builder.rs @@ -22,7 +22,7 @@ use crate::{ storage::meter::Meter, transient_storage::MeterEntry, wasm::{PreparedCall, Runtime}, - BalanceOf, Config, DebugBuffer, Error, GasMeter, MomentOf, Origin, WasmBlob, Weight, + BalanceOf, Config, Error, GasMeter, MomentOf, Origin, WasmBlob, Weight, }; use alloc::{vec, vec::Vec}; use frame_benchmarking::benchmarking; @@ -38,7 +38,6 @@ pub struct CallSetup<T: Config> { gas_meter: GasMeter<T>, storage_meter: Meter<T>, value: BalanceOf<T>, - debug_message: Option<DebugBuffer>, data: Vec<u8>, transient_storage_size: u32, } @@ -91,7 +90,6 @@ where gas_meter: GasMeter::new(Weight::MAX), storage_meter, value: 0u32.into(), - debug_message: None, data: vec![], transient_storage_size: 0, } @@ -122,16 +120,6 @@ where self.transient_storage_size = size; } - /// Set the debug message. - pub fn enable_debug_message(&mut self) { - self.debug_message = Some(Default::default()); - } - - /// Get the debug message. - pub fn debug_message(&self) -> Option<DebugBuffer> { - self.debug_message.clone() - } - /// Get the call's input data. pub fn data(&self) -> Vec<u8> { self.data.clone() @@ -150,7 +138,6 @@ where &mut self.gas_meter, &mut self.storage_meter, self.value, - self.debug_message.as_mut(), ); if self.transient_storage_size > 0 { Self::with_transient_storage(&mut ext.0, self.transient_storage_size).unwrap(); diff --git a/substrate/frame/revive/src/benchmarking/mod.rs b/substrate/frame/revive/src/benchmarking/mod.rs index 1796348ff321209aa8b8c3c531b29d182d6f745c..16bdd6d1a18a034922b5eff47c190d76776f4d23 100644 --- a/substrate/frame/revive/src/benchmarking/mod.rs +++ b/substrate/frame/revive/src/benchmarking/mod.rs @@ -107,8 +107,6 @@ where Code::Upload(module.code), data, salt, - DebugInfo::Skip, - CollectEvents::Skip, ); let address = outcome.result?.addr; @@ -558,6 +556,38 @@ mod benchmarks { assert_eq!(result.unwrap(), 1); } + #[benchmark(pov_mode = Measured)] + fn seal_to_account_id() { + // use a mapped address for the benchmark, to ensure that we bench the worst + // case (and not the fallback case). + let address = { + let caller = account("seal_to_account_id", 0, 0); + T::Currency::set_balance(&caller, caller_funding::<T>()); + T::AddressMapper::map(&caller).unwrap(); + T::AddressMapper::to_address(&caller) + }; + + let len = <T::AccountId as MaxEncodedLen>::max_encoded_len(); + build_runtime!(runtime, memory: [vec![0u8; len], address.0, ]); + + let result; + #[block] + { + result = runtime.bench_to_account_id(memory.as_mut_slice(), len as u32, 0); + } + + assert_ok!(result); + assert_ne!( + memory.as_slice()[20..32], + [0xEE; 12], + "fallback suffix found where none should be" + ); + assert_eq!( + T::AccountId::decode(&mut memory.as_slice()), + Ok(runtime.ext().to_account_id(&address)) + ); + } + #[benchmark(pov_mode = Measured)] fn seal_code_hash() { let contract = Contract::<T>::with_index(1, WasmModule::dummy(), vec![]).unwrap(); @@ -742,7 +772,7 @@ mod benchmarks { let mut setup = CallSetup::<T>::default(); let input = setup.data(); let (mut ext, _) = setup.ext(); - ext.override_export(crate::debug::ExportedFunction::Constructor); + ext.override_export(crate::exec::ExportedFunction::Constructor); let mut runtime = crate::wasm::Runtime::<_, [u8]>::new(&mut ext, input); @@ -1047,32 +1077,6 @@ mod benchmarks { ); } - // Benchmark debug_message call - // Whereas this function is used in RPC mode only, it still should be secured - // against an excessive use. - // - // i: size of input in bytes up to maximum allowed contract memory or maximum allowed debug - // buffer size, whichever is less. - #[benchmark] - fn seal_debug_message( - i: Linear<0, { (limits::code::BLOB_BYTES).min(limits::DEBUG_BUFFER_BYTES) }>, - ) { - let mut setup = CallSetup::<T>::default(); - setup.enable_debug_message(); - let (mut ext, _) = setup.ext(); - let mut runtime = crate::wasm::Runtime::<_, [u8]>::new(&mut ext, vec![]); - // Fill memory with printable ASCII bytes. - let mut memory = (0..i).zip((32..127).cycle()).map(|i| i.1).collect::<Vec<_>>(); - - let result; - #[block] - { - result = runtime.bench_debug_message(memory.as_mut_slice(), 0, i); - } - assert_ok!(result); - assert_eq!(setup.debug_message().unwrap().len() as u32, i); - } - #[benchmark(skip_meta, pov_mode = Measured)] fn get_storage_empty() -> Result<(), BenchmarkError> { let max_key_len = limits::STORAGE_KEY_BYTES; diff --git a/substrate/frame/revive/src/debug.rs b/substrate/frame/revive/src/debug.rs deleted file mode 100644 index d1fc0823e03dff6719018ee60f0c0668aca37d7f..0000000000000000000000000000000000000000 --- a/substrate/frame/revive/src/debug.rs +++ /dev/null @@ -1,109 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -pub use crate::{ - exec::{ExecResult, ExportedFunction}, - primitives::ExecReturnValue, -}; -use crate::{Config, LOG_TARGET}; -use sp_core::H160; - -/// Umbrella trait for all interfaces that serves for debugging. -pub trait Debugger<T: Config>: Tracing<T> + CallInterceptor<T> {} - -impl<T: Config, V> Debugger<T> for V where V: Tracing<T> + CallInterceptor<T> {} - -/// Defines methods to capture contract calls, enabling external observers to -/// measure, trace, and react to contract interactions. -pub trait Tracing<T: Config> { - /// The type of [`CallSpan`] that is created by this trait. - type CallSpan: CallSpan; - - /// Creates a new call span to encompass the upcoming contract execution. - /// - /// This method should be invoked just before the execution of a contract and - /// marks the beginning of a traceable span of execution. - /// - /// # Arguments - /// - /// * `contract_address` - The address of the contract that is about to be executed. - /// * `entry_point` - Describes whether the call is the constructor or a regular call. - /// * `input_data` - The raw input data of the call. - fn new_call_span( - contract_address: &H160, - entry_point: ExportedFunction, - input_data: &[u8], - ) -> Self::CallSpan; -} - -/// Defines a span of execution for a contract call. -pub trait CallSpan { - /// Called just after the execution of a contract. - /// - /// # Arguments - /// - /// * `output` - The raw output of the call. - fn after_call(self, output: &ExecReturnValue); -} - -impl<T: Config> Tracing<T> for () { - type CallSpan = (); - - fn new_call_span(contract_address: &H160, entry_point: ExportedFunction, input_data: &[u8]) { - log::trace!(target: LOG_TARGET, "call {entry_point:?} address: {contract_address:?}, input_data: {input_data:?}") - } -} - -impl CallSpan for () { - fn after_call(self, output: &ExecReturnValue) { - log::trace!(target: LOG_TARGET, "call result {output:?}") - } -} - -/// Provides an interface for intercepting contract calls. -pub trait CallInterceptor<T: Config> { - /// Allows to intercept contract calls and decide whether they should be executed or not. - /// If the call is intercepted, the mocked result of the call is returned. - /// - /// # Arguments - /// - /// * `contract_address` - The address of the contract that is about to be executed. - /// * `entry_point` - Describes whether the call is the constructor or a regular call. - /// * `input_data` - The raw input data of the call. - /// - /// # Expected behavior - /// - /// This method should return: - /// * `Some(ExecResult)` - if the call should be intercepted and the mocked result of the call - /// is returned. - /// * `None` - otherwise, i.e. the call should be executed normally. - fn intercept_call( - contract_address: &H160, - entry_point: ExportedFunction, - input_data: &[u8], - ) -> Option<ExecResult>; -} - -impl<T: Config> CallInterceptor<T> for () { - fn intercept_call( - _contract_address: &H160, - _entry_point: ExportedFunction, - _input_data: &[u8], - ) -> Option<ExecResult> { - None - } -} diff --git a/substrate/frame/revive/src/evm.rs b/substrate/frame/revive/src/evm.rs index c8c967fbe091bb2af5bfaacaefbe854d4f9d0043..33660a36aa6ea57f713ccf57a62f8ba013ac4ef9 100644 --- a/substrate/frame/revive/src/evm.rs +++ b/substrate/frame/revive/src/evm.rs @@ -19,6 +19,51 @@ mod api; pub use api::*; +mod tracing; +pub use tracing::*; mod gas_encoder; pub use gas_encoder::*; pub mod runtime; + +use crate::alloc::{format, string::*}; + +/// Extract the revert message from a revert("msg") solidity statement. +pub fn extract_revert_message(exec_data: &[u8]) -> Option<String> { + let error_selector = exec_data.get(0..4)?; + + match error_selector { + // assert(false) + [0x4E, 0x48, 0x7B, 0x71] => { + let panic_code: u32 = U256::from_big_endian(exec_data.get(4..36)?).try_into().ok()?; + + // See https://docs.soliditylang.org/en/latest/control-structures.html#panic-via-assert-and-error-via-require + let msg = match panic_code { + 0x00 => "generic panic", + 0x01 => "assert(false)", + 0x11 => "arithmetic underflow or overflow", + 0x12 => "division or modulo by zero", + 0x21 => "enum overflow", + 0x22 => "invalid encoded storage byte array accessed", + 0x31 => "out-of-bounds array access; popping on an empty array", + 0x32 => "out-of-bounds access of an array or bytesN", + 0x41 => "out of memory", + 0x51 => "uninitialized function", + code => return Some(format!("execution reverted: unknown panic code: {code:#x}")), + }; + + Some(format!("execution reverted: {msg}")) + }, + // revert(string) + [0x08, 0xC3, 0x79, 0xA0] => { + let decoded = ethabi::decode(&[ethabi::ParamKind::String], &exec_data[4..]).ok()?; + if let Some(ethabi::Token::String(msg)) = decoded.first() { + return Some(format!("execution reverted: {}", String::from_utf8_lossy(msg))) + } + Some("execution reverted".to_string()) + }, + _ => { + log::debug!(target: crate::LOG_TARGET, "Unknown revert function selector: {error_selector:?}"); + Some("execution reverted".to_string()) + }, + } +} diff --git a/substrate/frame/revive/src/evm/api.rs b/substrate/frame/revive/src/evm/api.rs index fe18c8735bed4d8dc435f8517d86f67aa9c70393..7a34fdc83f9a5140cc459f4be0da188bd81b5b7b 100644 --- a/substrate/frame/revive/src/evm/api.rs +++ b/substrate/frame/revive/src/evm/api.rs @@ -16,6 +16,8 @@ // limitations under the License. //! JSON-RPC methods and types, for Ethereum. +mod hex_serde; + mod byte; pub use byte::*; @@ -25,6 +27,9 @@ pub use rlp; mod type_id; pub use type_id::*; +mod debug_rpc_types; +pub use debug_rpc_types::*; + mod rpc_types; mod rpc_types_gen; pub use rpc_types_gen::*; diff --git a/substrate/frame/revive/src/evm/api/byte.rs b/substrate/frame/revive/src/evm/api/byte.rs index c2d64f8e5e424b2086e0247e5ccea0e08d4350b0..f11966d0072cf64d674bd2925862720192c43924 100644 --- a/substrate/frame/revive/src/evm/api/byte.rs +++ b/substrate/frame/revive/src/evm/api/byte.rs @@ -15,79 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. //! Define Byte wrapper types for encoding and decoding hex strings +use super::hex_serde::HexCodec; use alloc::{vec, vec::Vec}; use codec::{Decode, Encode}; use core::{ fmt::{Debug, Display, Formatter, Result as FmtResult}, str::FromStr, }; -use hex_serde::HexCodec; use scale_info::TypeInfo; use serde::{Deserialize, Serialize}; -mod hex_serde { - #[cfg(not(feature = "std"))] - use alloc::{format, string::String, vec::Vec}; - use serde::{Deserialize, Deserializer, Serializer}; - - pub trait HexCodec: Sized { - type Error; - fn to_hex(&self) -> String; - fn from_hex(s: String) -> Result<Self, Self::Error>; - } - - impl HexCodec for u8 { - type Error = core::num::ParseIntError; - fn to_hex(&self) -> String { - format!("0x{:x}", self) - } - fn from_hex(s: String) -> Result<Self, Self::Error> { - u8::from_str_radix(s.trim_start_matches("0x"), 16) - } - } - - impl<const T: usize> HexCodec for [u8; T] { - type Error = hex::FromHexError; - fn to_hex(&self) -> String { - format!("0x{}", hex::encode(self)) - } - fn from_hex(s: String) -> Result<Self, Self::Error> { - let data = hex::decode(s.trim_start_matches("0x"))?; - data.try_into().map_err(|_| hex::FromHexError::InvalidStringLength) - } - } - - impl HexCodec for Vec<u8> { - type Error = hex::FromHexError; - fn to_hex(&self) -> String { - format!("0x{}", hex::encode(self)) - } - fn from_hex(s: String) -> Result<Self, Self::Error> { - hex::decode(s.trim_start_matches("0x")) - } - } - - pub fn serialize<S, T>(value: &T, serializer: S) -> Result<S::Ok, S::Error> - where - S: Serializer, - T: HexCodec, - { - let s = value.to_hex(); - serializer.serialize_str(&s) - } - - pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error> - where - D: Deserializer<'de>, - T: HexCodec, - <T as HexCodec>::Error: core::fmt::Debug, - { - let s = String::deserialize(deserializer)?; - let value = T::from_hex(s).map_err(|e| serde::de::Error::custom(format!("{:?}", e)))?; - Ok(value) - } -} - impl FromStr for Bytes { type Err = hex::FromHexError; fn from_str(s: &str) -> Result<Self, Self::Err> { @@ -100,7 +37,7 @@ macro_rules! impl_hex { ($type:ident, $inner:ty, $default:expr) => { #[derive(Encode, Decode, Eq, PartialEq, TypeInfo, Clone, Serialize, Deserialize)] #[doc = concat!("`", stringify!($inner), "`", " wrapper type for encoding and decoding hex strings")] - pub struct $type(#[serde(with = "hex_serde")] pub $inner); + pub struct $type(#[serde(with = "crate::evm::api::hex_serde")] pub $inner); impl Default for $type { fn default() -> Self { @@ -131,6 +68,13 @@ macro_rules! impl_hex { }; } +impl Bytes { + /// See `Vec::is_empty` + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } +} + impl_hex!(Byte, u8, 0u8); impl_hex!(Bytes, Vec<u8>, vec![]); impl_hex!(Bytes8, [u8; 8], [0u8; 8]); diff --git a/substrate/frame/revive/src/evm/api/debug_rpc_types.rs b/substrate/frame/revive/src/evm/api/debug_rpc_types.rs new file mode 100644 index 0000000000000000000000000000000000000000..0857a59fbf3b650707069e1a0a8387acad49fe99 --- /dev/null +++ b/substrate/frame/revive/src/evm/api/debug_rpc_types.rs @@ -0,0 +1,219 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::evm::{Bytes, CallTracer}; +use alloc::{fmt, string::String, vec::Vec}; +use codec::{Decode, Encode}; +use scale_info::TypeInfo; +use serde::{ + de::{self, MapAccess, Visitor}, + Deserialize, Deserializer, Serialize, +}; +use sp_core::{H160, H256, U256}; + +/// Tracer configuration used to trace calls. +#[derive(TypeInfo, Debug, Clone, Encode, Decode, Serialize, PartialEq)] +#[serde(tag = "tracer", content = "tracerConfig")] +pub enum TracerConfig { + /// A tracer that captures call traces. + #[serde(rename = "callTracer")] + CallTracer { + /// Whether or not to capture logs. + #[serde(rename = "withLog")] + with_logs: bool, + }, +} + +impl TracerConfig { + /// Build the tracer associated to this config. + pub fn build<G>(self, gas_mapper: G) -> CallTracer<U256, G> { + match self { + Self::CallTracer { with_logs } => CallTracer::new(with_logs, gas_mapper), + } + } +} + +/// Custom deserializer to support the following JSON format: +/// +/// ```json +/// { "tracer": "callTracer", "tracerConfig": { "withLogs": false } } +/// ``` +/// +/// ```json +/// { "tracer": "callTracer" } +/// ``` +impl<'de> Deserialize<'de> for TracerConfig { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: Deserializer<'de>, + { + struct TracerConfigVisitor; + + impl<'de> Visitor<'de> for TracerConfigVisitor { + type Value = TracerConfig; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a map with tracer and optional tracerConfig") + } + + fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error> + where + M: MapAccess<'de>, + { + let mut tracer_type: Option<String> = None; + let mut with_logs = None; + + while let Some(key) = map.next_key::<String>()? { + match key.as_str() { + "tracer" => { + tracer_type = map.next_value()?; + }, + "tracerConfig" => { + #[derive(Deserialize)] + struct CallTracerConfig { + #[serde(rename = "withLogs")] + with_logs: Option<bool>, + } + let inner: CallTracerConfig = map.next_value()?; + with_logs = inner.with_logs; + }, + _ => {}, + } + } + + match tracer_type.as_deref() { + Some("callTracer") => + Ok(TracerConfig::CallTracer { with_logs: with_logs.unwrap_or(true) }), + _ => Err(de::Error::custom("Unsupported or missing tracer type")), + } + } + } + + deserializer.deserialize_map(TracerConfigVisitor) + } +} + +#[test] +fn test_tracer_config_serialization() { + let tracers = vec![ + (r#"{"tracer": "callTracer"}"#, TracerConfig::CallTracer { with_logs: true }), + ( + r#"{"tracer": "callTracer", "tracerConfig": { "withLogs": true }}"#, + TracerConfig::CallTracer { with_logs: true }, + ), + ( + r#"{"tracer": "callTracer", "tracerConfig": { "withLogs": false }}"#, + TracerConfig::CallTracer { with_logs: false }, + ), + ]; + + for (json_data, expected) in tracers { + let result: TracerConfig = + serde_json::from_str(json_data).expect("Deserialization should succeed"); + assert_eq!(result, expected); + } +} + +impl Default for TracerConfig { + fn default() -> Self { + TracerConfig::CallTracer { with_logs: false } + } +} + +/// The type of call that was executed. +#[derive( + Default, TypeInfo, Encode, Decode, Serialize, Deserialize, Eq, PartialEq, Clone, Debug, +)] +#[serde(rename_all = "UPPERCASE")] +pub enum CallType { + /// A regular call. + #[default] + Call, + /// A read-only call. + StaticCall, + /// A delegate call. + DelegateCall, +} + +/// A smart contract execution call trace. +#[derive( + TypeInfo, Default, Encode, Decode, Serialize, Deserialize, Clone, Debug, Eq, PartialEq, +)] +pub struct CallTrace<Gas = U256> { + /// Address of the sender. + pub from: H160, + /// Address of the receiver. + pub to: H160, + /// Call input data. + pub input: Vec<u8>, + /// Amount of value transferred. + #[serde(skip_serializing_if = "U256::is_zero")] + pub value: U256, + /// Type of call. + #[serde(rename = "type")] + pub call_type: CallType, + /// Amount of gas provided for the call. + pub gas: Gas, + /// Amount of gas used. + #[serde(rename = "gasUsed")] + pub gas_used: Gas, + /// Return data. + #[serde(flatten, skip_serializing_if = "Bytes::is_empty")] + pub output: Bytes, + /// The error message if the call failed. + #[serde(skip_serializing_if = "Option::is_none")] + pub error: Option<String>, + /// The revert reason, if the call reverted. + #[serde(rename = "revertReason")] + pub revert_reason: Option<String>, + /// List of sub-calls. + #[serde(skip_serializing_if = "Vec::is_empty")] + pub calls: Vec<CallTrace<Gas>>, + /// List of logs emitted during the call. + #[serde(skip_serializing_if = "Vec::is_empty")] + pub logs: Vec<CallLog>, +} + +/// A log emitted during a call. +#[derive( + Debug, Default, Clone, Encode, Decode, TypeInfo, Serialize, Deserialize, Eq, PartialEq, +)] +pub struct CallLog { + /// The address of the contract that emitted the log. + pub address: H160, + /// The log's data. + #[serde(skip_serializing_if = "Bytes::is_empty")] + pub data: Bytes, + /// The topics used to index the log. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub topics: Vec<H256>, + /// Position of the log relative to subcalls within the same trace + /// See <https://github.com/ethereum/go-ethereum/pull/28389> for details + #[serde(with = "super::hex_serde")] + pub position: u32, +} + +/// A transaction trace +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct TransactionTrace { + /// The transaction hash. + #[serde(rename = "txHash")] + pub tx_hash: H256, + /// The trace of the transaction. + #[serde(rename = "result")] + pub trace: CallTrace, +} diff --git a/substrate/frame/revive/src/evm/api/hex_serde.rs b/substrate/frame/revive/src/evm/api/hex_serde.rs new file mode 100644 index 0000000000000000000000000000000000000000..ba07b36fa4be6e5cba82bba719c3893cd32aafc5 --- /dev/null +++ b/substrate/frame/revive/src/evm/api/hex_serde.rs @@ -0,0 +1,84 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use alloc::{format, string::String, vec::Vec}; +use serde::{Deserialize, Deserializer, Serializer}; + +pub trait HexCodec: Sized { + type Error; + fn to_hex(&self) -> String; + fn from_hex(s: String) -> Result<Self, Self::Error>; +} + +macro_rules! impl_hex_codec { + ($($t:ty),*) => { + $( + impl HexCodec for $t { + type Error = core::num::ParseIntError; + fn to_hex(&self) -> String { + format!("0x{:x}", self) + } + fn from_hex(s: String) -> Result<Self, Self::Error> { + <$t>::from_str_radix(s.trim_start_matches("0x"), 16) + } + } + )* + }; +} + +impl_hex_codec!(u8, u32); + +impl<const T: usize> HexCodec for [u8; T] { + type Error = hex::FromHexError; + fn to_hex(&self) -> String { + format!("0x{}", hex::encode(self)) + } + fn from_hex(s: String) -> Result<Self, Self::Error> { + let data = hex::decode(s.trim_start_matches("0x"))?; + data.try_into().map_err(|_| hex::FromHexError::InvalidStringLength) + } +} + +impl HexCodec for Vec<u8> { + type Error = hex::FromHexError; + fn to_hex(&self) -> String { + format!("0x{}", hex::encode(self)) + } + fn from_hex(s: String) -> Result<Self, Self::Error> { + hex::decode(s.trim_start_matches("0x")) + } +} + +pub fn serialize<S, T>(value: &T, serializer: S) -> Result<S::Ok, S::Error> +where + S: Serializer, + T: HexCodec, +{ + let s = value.to_hex(); + serializer.serialize_str(&s) +} + +pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error> +where + D: Deserializer<'de>, + T: HexCodec, + <T as HexCodec>::Error: core::fmt::Debug, +{ + let s = String::deserialize(deserializer)?; + let value = T::from_hex(s).map_err(|e| serde::de::Error::custom(format!("{:?}", e)))?; + Ok(value) +} diff --git a/substrate/frame/revive/src/evm/runtime.rs b/substrate/frame/revive/src/evm/runtime.rs index d4b344e20eb850e6c42d84bdf870204c1382cb67..0e5fc3da545b5eddc4363676877f721473c8c42a 100644 --- a/substrate/frame/revive/src/evm/runtime.rs +++ b/substrate/frame/revive/src/evm/runtime.rs @@ -20,7 +20,7 @@ use crate::{ api::{GenericTransaction, TransactionSigned}, GasEncoder, }, - AccountIdOf, AddressMapper, BalanceOf, Config, MomentOf, LOG_TARGET, + AccountIdOf, AddressMapper, BalanceOf, Config, MomentOf, Weight, LOG_TARGET, }; use alloc::vec::Vec; use codec::{Decode, Encode}; @@ -72,6 +72,18 @@ where } } +/// Convert a `Weight` into a gas value, using the fixed `GAS_PRICE`. +/// and the `Config::WeightPrice` to compute the fee. +/// The gas is calculated as `fee / GAS_PRICE`, rounded up to the nearest integer. +pub fn gas_from_weight<T: Config>(weight: Weight) -> U256 +where + BalanceOf<T>: Into<U256>, +{ + use sp_runtime::traits::Convert; + let fee: BalanceOf<T> = T::WeightPrice::convert(weight); + gas_from_fee(fee) +} + /// Wraps [`generic::UncheckedExtrinsic`] to support checking unsigned /// [`crate::Call::eth_transact`] extrinsic. #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)] diff --git a/substrate/frame/revive/src/evm/tracing.rs b/substrate/frame/revive/src/evm/tracing.rs new file mode 100644 index 0000000000000000000000000000000000000000..7466ec1de487738bcb662e92b9127852098e304d --- /dev/null +++ b/substrate/frame/revive/src/evm/tracing.rs @@ -0,0 +1,134 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +use crate::{ + evm::{extract_revert_message, CallLog, CallTrace, CallType}, + primitives::ExecReturnValue, + tracing::Tracer, + DispatchError, Weight, +}; +use alloc::{format, string::ToString, vec::Vec}; +use sp_core::{H160, H256, U256}; + +/// A Tracer that reports logs and nested call traces transactions. +#[derive(Default, Debug, Clone, PartialEq, Eq)] +pub struct CallTracer<Gas, GasMapper> { + /// Map Weight to Gas equivalent. + gas_mapper: GasMapper, + /// Store all in-progress CallTrace instances. + traces: Vec<CallTrace<Gas>>, + /// Stack of indices to the current active traces. + current_stack: Vec<usize>, + /// whether or not to capture logs. + with_log: bool, +} + +impl<Gas, GasMapper> CallTracer<Gas, GasMapper> { + /// Create a new [`CallTracer`] instance. + pub fn new(with_log: bool, gas_mapper: GasMapper) -> Self { + Self { gas_mapper, traces: Vec::new(), current_stack: Vec::new(), with_log } + } + + /// Collect the traces and return them. + pub fn collect_traces(&mut self) -> Vec<CallTrace<Gas>> { + core::mem::take(&mut self.traces) + } +} + +impl<Gas: Default, GasMapper: Fn(Weight) -> Gas> Tracer for CallTracer<Gas, GasMapper> { + fn enter_child_span( + &mut self, + from: H160, + to: H160, + is_delegate_call: bool, + is_read_only: bool, + value: U256, + input: &[u8], + gas_left: Weight, + ) { + let call_type = if is_read_only { + CallType::StaticCall + } else if is_delegate_call { + CallType::DelegateCall + } else { + CallType::Call + }; + + self.traces.push(CallTrace { + from, + to, + value, + call_type, + input: input.to_vec(), + gas: (self.gas_mapper)(gas_left), + ..Default::default() + }); + + // Push the index onto the stack of the current active trace + self.current_stack.push(self.traces.len() - 1); + } + + fn log_event(&mut self, address: H160, topics: &[H256], data: &[u8]) { + if !self.with_log { + return; + } + + let current_index = self.current_stack.last().unwrap(); + let position = self.traces[*current_index].calls.len() as u32; + let log = + CallLog { address, topics: topics.to_vec(), data: data.to_vec().into(), position }; + + let current_index = *self.current_stack.last().unwrap(); + self.traces[current_index].logs.push(log); + } + + fn exit_child_span(&mut self, output: &ExecReturnValue, gas_used: Weight) { + // Set the output of the current trace + let current_index = self.current_stack.pop().unwrap(); + let trace = &mut self.traces[current_index]; + trace.output = output.data.clone().into(); + trace.gas_used = (self.gas_mapper)(gas_used); + + if output.did_revert() { + trace.revert_reason = extract_revert_message(&output.data); + trace.error = Some("execution reverted".to_string()); + } + + // Move the current trace into its parent + if let Some(parent_index) = self.current_stack.last() { + let child_trace = self.traces.remove(current_index); + self.traces[*parent_index].calls.push(child_trace); + } + } + fn exit_child_span_with_error(&mut self, error: DispatchError, gas_used: Weight) { + // Set the output of the current trace + let current_index = self.current_stack.pop().unwrap(); + let trace = &mut self.traces[current_index]; + trace.gas_used = (self.gas_mapper)(gas_used); + + trace.error = match error { + DispatchError::Module(sp_runtime::ModuleError { message, .. }) => + Some(message.unwrap_or_default().to_string()), + _ => Some(format!("{:?}", error)), + }; + + // Move the current trace into its parent + if let Some(parent_index) = self.current_stack.last() { + let child_trace = self.traces.remove(current_index); + self.traces[*parent_index].calls.push(child_trace); + } + } +} diff --git a/substrate/frame/revive/src/exec.rs b/substrate/frame/revive/src/exec.rs index c069216d6cc71bc96b6d18512198aece824535f7..d2ef6c9c7ba6ce2e79f2b1608702b1340ae8db1b 100644 --- a/substrate/frame/revive/src/exec.rs +++ b/substrate/frame/revive/src/exec.rs @@ -17,15 +17,15 @@ use crate::{ address::{self, AddressMapper}, - debug::{CallInterceptor, CallSpan, Tracing}, gas::GasMeter, limits, primitives::{ExecReturnValue, StorageDeposit}, runtime_decl_for_revive_api::{Decode, Encode, RuntimeDebugNoBound, TypeInfo}, storage::{self, meter::Diff, WriteOutcome}, + tracing::if_tracing, transient_storage::TransientStorage, - BalanceOf, CodeInfo, CodeInfoOf, Config, ContractInfo, ContractInfoOf, DebugBuffer, Error, - Event, ImmutableData, ImmutableDataOf, Pallet as Contracts, LOG_TARGET, + BalanceOf, CodeInfo, CodeInfoOf, Config, ContractInfo, ContractInfoOf, Error, Event, + ImmutableData, ImmutableDataOf, Pallet as Contracts, }; use alloc::vec::Vec; use core::{fmt::Debug, marker::PhantomData, mem}; @@ -293,6 +293,9 @@ pub trait Ext: sealing::Sealed { /// Check if a contract lives at the specified `address`. fn is_contract(&self, address: &H160) -> bool; + /// Returns the account id for the given `address`. + fn to_account_id(&self, address: &H160) -> AccountIdOf<Self::T>; + /// Returns the code hash of the contract for the given `address`. /// If not a contract but account exists then `keccak_256([])` is returned, otherwise `zero`. fn code_hash(&self, address: &H160) -> H256; @@ -378,19 +381,6 @@ pub trait Ext: sealing::Sealed { /// Charges `diff` from the meter. fn charge_storage(&mut self, diff: &Diff); - /// Append a string to the debug buffer. - /// - /// It is added as-is without any additional new line. - /// - /// This is a no-op if debug message recording is disabled which is always the case - /// when the code is executing on-chain. - /// - /// Returns `true` if debug message recording is enabled. Otherwise `false` is returned. - fn append_debug_buffer(&mut self, msg: &str) -> bool; - - /// Returns `true` if debug message recording is enabled. Otherwise `false` is returned. - fn debug_buffer_enabled(&self) -> bool; - /// Call some dispatchable and return the result. fn call_runtime(&self, call: <Self::T as Config>::RuntimeCall) -> DispatchResultWithPostInfo; @@ -555,11 +545,6 @@ pub struct Stack<'a, T: Config, E> { frames: BoundedVec<Frame<T>, ConstU32<{ limits::CALL_STACK_DEPTH }>>, /// Statically guarantee that each call stack has at least one frame. first_frame: Frame<T>, - /// A text buffer used to output human readable information. - /// - /// All the bytes added to this field should be valid UTF-8. The buffer has no defined - /// structure and is intended to be shown to users as-is for debugging purposes. - debug_message: Option<&'a mut DebugBuffer>, /// Transient storage used to store data, which is kept for the duration of a transaction. transient_storage: TransientStorage<T>, /// Whether or not actual transfer of funds should be performed. @@ -765,11 +750,6 @@ where { /// Create and run a new call stack by calling into `dest`. /// - /// # Note - /// - /// `debug_message` should only ever be set to `Some` when executing as an RPC because - /// it adds allocations and could be abused to drive the runtime into an OOM panic. - /// /// # Return Value /// /// Result<(ExecReturnValue, CodeSize), (ExecError, CodeSize)> @@ -781,7 +761,6 @@ where value: U256, input_data: Vec<u8>, skip_transfer: bool, - debug_message: Option<&'a mut DebugBuffer>, ) -> ExecResult { let dest = T::AddressMapper::to_account_id(&dest); if let Some((mut stack, executable)) = Self::new( @@ -791,21 +770,33 @@ where storage_meter, value, skip_transfer, - debug_message, )? { stack.run(executable, input_data).map(|_| stack.first_frame.last_frame_output) } else { - Self::transfer_from_origin(&origin, &origin, &dest, value) + if_tracing(|t| { + let address = + origin.account_id().map(T::AddressMapper::to_address).unwrap_or_default(); + let dest = T::AddressMapper::to_address(&dest); + t.enter_child_span(address, dest, false, false, value, &input_data, Weight::zero()); + }); + + let result = Self::transfer_from_origin(&origin, &origin, &dest, value); + match result { + Ok(ref output) => { + if_tracing(|t| { + t.exit_child_span(&output, Weight::zero()); + }); + }, + Err(e) => { + if_tracing(|t| t.exit_child_span_with_error(e.error.into(), Weight::zero())); + }, + } + result } } /// Create and run a new call stack by instantiating a new contract. /// - /// # Note - /// - /// `debug_message` should only ever be set to `Some` when executing as an RPC because - /// it adds allocations and could be abused to drive the runtime into an OOM panic. - /// /// # Return Value /// /// Result<(NewContractAccountId, ExecReturnValue), ExecError)> @@ -818,7 +809,6 @@ where input_data: Vec<u8>, salt: Option<&[u8; 32]>, skip_transfer: bool, - debug_message: Option<&'a mut DebugBuffer>, ) -> Result<(H160, ExecReturnValue), ExecError> { let (mut stack, executable) = Self::new( FrameArgs::Instantiate { @@ -832,7 +822,6 @@ where storage_meter, value, skip_transfer, - debug_message, )? .expect(FRAME_ALWAYS_EXISTS_ON_INSTANTIATE); let address = T::AddressMapper::to_address(&stack.top_frame().account_id); @@ -848,7 +837,6 @@ where gas_meter: &'a mut GasMeter<T>, storage_meter: &'a mut storage::meter::Meter<T>, value: BalanceOf<T>, - debug_message: Option<&'a mut DebugBuffer>, ) -> (Self, E) { Self::new( FrameArgs::Call { @@ -861,7 +849,6 @@ where storage_meter, value.into(), false, - debug_message, ) .unwrap() .unwrap() @@ -878,7 +865,6 @@ where storage_meter: &'a mut storage::meter::Meter<T>, value: U256, skip_transfer: bool, - debug_message: Option<&'a mut DebugBuffer>, ) -> Result<Option<(Self, E)>, ExecError> { origin.ensure_mapped()?; let Some((first_frame, executable)) = Self::new_frame( @@ -903,7 +889,6 @@ where block_number: <frame_system::Pallet<T>>::block_number(), first_frame, frames: Default::default(), - debug_message, transient_storage: TransientStorage::new(limits::TRANSIENT_STORAGE_BYTES), skip_transfer, _phantom: Default::default(), @@ -1051,6 +1036,7 @@ where fn run(&mut self, executable: E, input_data: Vec<u8>) -> Result<(), ExecError> { let frame = self.top_frame(); let entry_point = frame.entry_point; + let is_delegate_call = frame.delegate.is_some(); let delegated_code_hash = if frame.delegate.is_some() { Some(*executable.code_hash()) } else { None }; @@ -1071,6 +1057,9 @@ where let do_transaction = || -> ExecResult { let caller = self.caller(); let frame = top_frame_mut!(self); + let read_only = frame.read_only; + let value_transferred = frame.value_transferred; + let account_id = &frame.account_id.clone(); // We need to charge the storage deposit before the initial transfer so that // it can create the account in case the initial transfer is < ed. @@ -1078,10 +1067,11 @@ where // Root origin can't be used to instantiate a contract, so it is safe to assume that // if we reached this point the origin has an associated account. let origin = &self.origin.account_id()?; + frame.nested_storage.charge_instantiate( origin, - &frame.account_id, - frame.contract_info.get(&frame.account_id), + &account_id, + frame.contract_info.get(&account_id), executable.code_info(), self.skip_transfer, )?; @@ -1102,15 +1092,34 @@ where )?; } - let contract_address = T::AddressMapper::to_address(&top_frame!(self).account_id); - - let call_span = T::Debug::new_call_span(&contract_address, entry_point, &input_data); + let contract_address = T::AddressMapper::to_address(account_id); + let maybe_caller_address = caller.account_id().map(T::AddressMapper::to_address); + + if_tracing(|tracer| { + tracer.enter_child_span( + maybe_caller_address.unwrap_or_default(), + contract_address, + is_delegate_call, + read_only, + value_transferred, + &input_data, + frame.nested_gas.gas_left(), + ); + }); - let output = T::Debug::intercept_call(&contract_address, entry_point, &input_data) - .unwrap_or_else(|| executable.execute(self, entry_point, input_data)) - .map_err(|e| ExecError { error: e.error, origin: ErrorOrigin::Callee })?; + let output = executable.execute(self, entry_point, input_data).map_err(|e| { + if_tracing(|tracer| { + tracer.exit_child_span_with_error( + e.error, + top_frame_mut!(self).nested_gas.gas_consumed(), + ); + }); + ExecError { error: e.error, origin: ErrorOrigin::Callee } + })?; - call_span.after_call(&output); + if_tracing(|tracer| { + tracer.exit_child_span(&output, top_frame_mut!(self).nested_gas.gas_consumed()); + }); // Avoid useless work that would be reverted anyways. if output.did_revert() { @@ -1128,34 +1137,11 @@ where .enforce_limit(contract) .map_err(|e| ExecError { error: e, origin: ErrorOrigin::Callee })?; - let account_id = T::AddressMapper::to_address(&frame.account_id); - match (entry_point, delegated_code_hash) { - (ExportedFunction::Constructor, _) => { - // It is not allowed to terminate a contract inside its constructor. - if matches!(frame.contract_info, CachedContract::Terminated) { - return Err(Error::<T>::TerminatedInConstructor.into()); - } - - let caller = T::AddressMapper::to_address(self.caller().account_id()?); - // Deposit an instantiation event. - Contracts::<T>::deposit_event(Event::Instantiated { - deployer: caller, - contract: account_id, - }); - }, - (ExportedFunction::Call, Some(code_hash)) => { - Contracts::<T>::deposit_event(Event::DelegateCalled { - contract: account_id, - code_hash, - }); - }, - (ExportedFunction::Call, None) => { - let caller = self.caller(); - Contracts::<T>::deposit_event(Event::Called { - caller: caller.clone(), - contract: account_id, - }); - }, + // It is not allowed to terminate a contract inside its constructor. + if entry_point == ExportedFunction::Constructor && + matches!(frame.contract_info, CachedContract::Terminated) + { + return Err(Error::<T>::TerminatedInConstructor.into()); } Ok(output) @@ -1250,13 +1236,6 @@ where } } } else { - if let Some((msg, false)) = self.debug_message.as_ref().map(|m| (m, m.is_empty())) { - log::debug!( - target: LOG_TARGET, - "Execution finished with debug buffer: {}", - core::str::from_utf8(msg).unwrap_or("<Invalid UTF8>"), - ); - } self.gas_meter.absorb_nested(mem::take(&mut self.first_frame.nested_gas)); if !persist { return; @@ -1416,7 +1395,7 @@ where &mut self, gas_limit: Weight, deposit_limit: U256, - dest: &H160, + dest_addr: &H160, value: U256, input_data: Vec<u8>, allows_reentry: bool, @@ -1432,7 +1411,7 @@ where *self.last_frame_output_mut() = Default::default(); let try_call = || { - let dest = T::AddressMapper::to_account_id(dest); + let dest = T::AddressMapper::to_account_id(dest_addr); if !self.allows_reentry(&dest) { return Err(<Error<T>>::ReentranceDenied.into()); } @@ -1569,10 +1548,6 @@ where .charge_deposit(frame.account_id.clone(), StorageDeposit::Refund(*deposit)); } - Contracts::<T>::deposit_event(Event::Terminated { - contract: account_address, - beneficiary: *beneficiary, - }); Ok(()) } @@ -1642,6 +1617,10 @@ where ContractInfoOf::<T>::contains_key(&address) } + fn to_account_id(&self, address: &H160) -> T::AccountId { + T::AddressMapper::to_account_id(address) + } + fn code_hash(&self, address: &H160) -> H256 { <ContractInfoOf<T>>::get(&address) .map(|contract| contract.code_hash) @@ -1724,11 +1703,11 @@ where } fn deposit_event(&mut self, topics: Vec<H256>, data: Vec<u8>) { - Contracts::<Self::T>::deposit_event(Event::ContractEmitted { - contract: T::AddressMapper::to_address(self.account_id()), - data, - topics, + let contract = T::AddressMapper::to_address(self.account_id()); + if_tracing(|tracer| { + tracer.log_event(contract, &topics, &data); }); + Contracts::<Self::T>::deposit_event(Event::ContractEmitted { contract, data, topics }); } fn block_number(&self) -> U256 { @@ -1759,28 +1738,6 @@ where self.top_frame_mut().nested_storage.charge(diff) } - fn debug_buffer_enabled(&self) -> bool { - self.debug_message.is_some() - } - - fn append_debug_buffer(&mut self, msg: &str) -> bool { - if let Some(buffer) = &mut self.debug_message { - buffer - .try_extend(&mut msg.bytes()) - .map_err(|_| { - log::debug!( - target: LOG_TARGET, - "Debug buffer (of {} bytes) exhausted!", - limits::DEBUG_BUFFER_BYTES, - ) - }) - .ok(); - true - } else { - false - } - } - fn call_runtime(&self, call: <Self::T as Config>::RuntimeCall) -> DispatchResultWithPostInfo { let mut origin: T::RuntimeOrigin = RawOrigin::Signed(self.account_id().clone()).into(); origin.add_filter(T::CallFilter::contains); @@ -1847,11 +1804,6 @@ where Self::increment_refcount(hash)?; Self::decrement_refcount(prev_hash); - Contracts::<Self::T>::deposit_event(Event::ContractCodeUpdated { - contract: T::AddressMapper::to_address(&frame.account_id), - new_code_hash: hash, - old_code_hash: prev_hash, - }); Ok(()) } @@ -2103,7 +2055,6 @@ mod tests { value.into(), vec![], false, - None, ), Ok(_) ); @@ -2196,7 +2147,6 @@ mod tests { value.into(), vec![], false, - None, ) .unwrap(); @@ -2237,7 +2187,6 @@ mod tests { value.into(), vec![], false, - None, )); assert_eq!(get_balance(&ALICE), 100 - value); @@ -2274,7 +2223,6 @@ mod tests { U256::zero(), vec![], false, - None, ), ExecError { error: Error::<Test>::CodeNotFound.into(), @@ -2292,7 +2240,6 @@ mod tests { U256::zero(), vec![], false, - None, )); }); } @@ -2321,7 +2268,6 @@ mod tests { 55u64.into(), vec![], false, - None, ) .unwrap(); @@ -2371,7 +2317,6 @@ mod tests { U256::zero(), vec![], false, - None, ); let output = result.unwrap(); @@ -2401,7 +2346,6 @@ mod tests { U256::zero(), vec![], false, - None, ); let output = result.unwrap(); @@ -2431,7 +2375,6 @@ mod tests { U256::zero(), vec![1, 2, 3, 4], false, - None, ); assert_matches!(result, Ok(_)); }); @@ -2468,7 +2411,6 @@ mod tests { vec![1, 2, 3, 4], Some(&[0; 32]), false, - None, ); assert_matches!(result, Ok(_)); }); @@ -2523,7 +2465,6 @@ mod tests { value.into(), vec![], false, - None, ); assert_matches!(result, Ok(_)); @@ -2588,7 +2529,6 @@ mod tests { U256::zero(), vec![], false, - None, ); assert_matches!(result, Ok(_)); @@ -2654,7 +2594,6 @@ mod tests { U256::zero(), vec![], false, - None, ); assert_matches!(result, Ok(_)); @@ -2687,7 +2626,40 @@ mod tests { U256::zero(), vec![], false, - None, + ); + assert_matches!(result, Ok(_)); + }); + } + + #[test] + fn to_account_id_returns_proper_values() { + let bob_code_hash = MockLoader::insert(Call, |ctx, _| { + let alice_account_id = <Test as Config>::AddressMapper::to_account_id(&ALICE_ADDR); + assert_eq!(ctx.ext.to_account_id(&ALICE_ADDR), alice_account_id); + + const UNMAPPED_ADDR: H160 = H160([99u8; 20]); + let mut unmapped_fallback_account_id = [0xEE; 32]; + unmapped_fallback_account_id[..20].copy_from_slice(UNMAPPED_ADDR.as_bytes()); + assert_eq!( + ctx.ext.to_account_id(&UNMAPPED_ADDR), + AccountId32::new(unmapped_fallback_account_id) + ); + + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, bob_code_hash); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); + let result = MockStack::run_call( + origin, + BOB_ADDR, + &mut GasMeter::<Test>::new(GAS_LIMIT), + &mut storage_meter, + U256::zero(), + vec![0], + false, ); assert_matches!(result, Ok(_)); }); @@ -2725,7 +2697,6 @@ mod tests { U256::zero(), vec![0], false, - None, ); assert_matches!(result, Ok(_)); }); @@ -2752,7 +2723,6 @@ mod tests { U256::zero(), vec![0], false, - None, ); assert_matches!(result, Ok(_)); }); @@ -2797,7 +2767,6 @@ mod tests { U256::zero(), vec![0], false, - None, ); assert_matches!(result, Ok(_)); }); @@ -2824,7 +2793,6 @@ mod tests { U256::zero(), vec![0], false, - None, ); assert_matches!(result, Ok(_)); }); @@ -2851,7 +2819,6 @@ mod tests { 1u64.into(), vec![0], false, - None, ); assert_matches!(result, Err(_)); }); @@ -2896,7 +2863,6 @@ mod tests { U256::zero(), vec![0], false, - None, ); assert_matches!(result, Ok(_)); }); @@ -2942,7 +2908,6 @@ mod tests { U256::zero(), vec![], false, - None, ); assert_matches!(result, Ok(_)); @@ -2969,7 +2934,6 @@ mod tests { vec![], Some(&[0; 32]), false, - None, ), Err(_) ); @@ -3005,7 +2969,6 @@ mod tests { vec![], Some(&[0 ;32]), false, - None, ), Ok((address, ref output)) if output.data == vec![80, 65, 83, 83] => address ); @@ -3021,13 +2984,6 @@ mod tests { ContractInfo::<Test>::load_code_hash(&instantiated_contract_id).unwrap(), dummy_ch ); - assert_eq!( - &events(), - &[Event::Instantiated { - deployer: ALICE_ADDR, - contract: instantiated_contract_address - }] - ); }); } @@ -3060,7 +3016,6 @@ mod tests { vec![], Some(&[0; 32]), false, - None, ), Ok((address, ref output)) if output.data == vec![70, 65, 73, 76] => address ); @@ -3125,7 +3080,6 @@ mod tests { (min_balance * 10).into(), vec![], false, - None, ), Ok(_) ); @@ -3145,19 +3099,6 @@ mod tests { ContractInfo::<Test>::load_code_hash(&instantiated_contract_id).unwrap(), dummy_ch ); - assert_eq!( - &events(), - &[ - Event::Instantiated { - deployer: BOB_ADDR, - contract: instantiated_contract_address - }, - Event::Called { - caller: Origin::from_account_id(ALICE), - contract: BOB_ADDR - }, - ] - ); }); } @@ -3206,17 +3147,9 @@ mod tests { U256::zero(), vec![], false, - None, ), Ok(_) ); - - // The contract wasn't instantiated so we don't expect to see an instantiation - // event here. - assert_eq!( - &events(), - &[Event::Called { caller: Origin::from_account_id(ALICE), contract: BOB_ADDR },] - ); }); } @@ -3250,7 +3183,6 @@ mod tests { vec![], Some(&[0; 32]), false, - None, ), Err(Error::<Test>::TerminatedInConstructor.into()) ); @@ -3315,7 +3247,6 @@ mod tests { U256::zero(), vec![0], false, - None, ); assert_matches!(result, Ok(_)); }); @@ -3378,7 +3309,6 @@ mod tests { vec![], Some(&[0; 32]), false, - None, ); assert_matches!(result, Ok(_)); }); @@ -3425,113 +3355,11 @@ mod tests { U256::zero(), vec![], false, - None, ) .unwrap(); }); } - #[test] - fn printing_works() { - let code_hash = MockLoader::insert(Call, |ctx, _| { - ctx.ext.append_debug_buffer("This is a test"); - ctx.ext.append_debug_buffer("More text"); - exec_success() - }); - - let mut debug_buffer = DebugBuffer::try_from(Vec::new()).unwrap(); - - ExtBuilder::default().build().execute_with(|| { - let min_balance = <Test as Config>::Currency::minimum_balance(); - - let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT); - set_balance(&ALICE, min_balance * 10); - place_contract(&BOB, code_hash); - let origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); - MockStack::run_call( - origin, - BOB_ADDR, - &mut gas_meter, - &mut storage_meter, - U256::zero(), - vec![], - false, - Some(&mut debug_buffer), - ) - .unwrap(); - }); - - assert_eq!(&String::from_utf8(debug_buffer.to_vec()).unwrap(), "This is a testMore text"); - } - - #[test] - fn printing_works_on_fail() { - let code_hash = MockLoader::insert(Call, |ctx, _| { - ctx.ext.append_debug_buffer("This is a test"); - ctx.ext.append_debug_buffer("More text"); - exec_trapped() - }); - - let mut debug_buffer = DebugBuffer::try_from(Vec::new()).unwrap(); - - ExtBuilder::default().build().execute_with(|| { - let min_balance = <Test as Config>::Currency::minimum_balance(); - - let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT); - set_balance(&ALICE, min_balance * 10); - place_contract(&BOB, code_hash); - let origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); - let result = MockStack::run_call( - origin, - BOB_ADDR, - &mut gas_meter, - &mut storage_meter, - U256::zero(), - vec![], - false, - Some(&mut debug_buffer), - ); - assert!(result.is_err()); - }); - - assert_eq!(&String::from_utf8(debug_buffer.to_vec()).unwrap(), "This is a testMore text"); - } - - #[test] - fn debug_buffer_is_limited() { - let code_hash = MockLoader::insert(Call, move |ctx, _| { - ctx.ext.append_debug_buffer("overflowing bytes"); - exec_success() - }); - - // Pre-fill the buffer almost up to its limit, leaving not enough space to the message - let debug_buf_before = DebugBuffer::try_from(vec![0u8; DebugBuffer::bound() - 5]).unwrap(); - let mut debug_buf_after = debug_buf_before.clone(); - - ExtBuilder::default().build().execute_with(|| { - let min_balance = <Test as Config>::Currency::minimum_balance(); - let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT); - set_balance(&ALICE, min_balance * 10); - place_contract(&BOB, code_hash); - let origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); - MockStack::run_call( - origin, - BOB_ADDR, - &mut gas_meter, - &mut storage_meter, - U256::zero(), - vec![], - false, - Some(&mut debug_buf_after), - ) - .unwrap(); - assert_eq!(debug_buf_before, debug_buf_after); - }); - } - #[test] fn call_reentry_direct_recursion() { // call the contract passed as input with disabled reentry @@ -3559,7 +3387,6 @@ mod tests { U256::zero(), CHARLIE_ADDR.as_bytes().to_vec(), false, - None, )); // Calling into oneself fails @@ -3572,7 +3399,6 @@ mod tests { U256::zero(), BOB_ADDR.as_bytes().to_vec(), false, - None, ) .map_err(|e| e.error), <Error<Test>>::ReentranceDenied, @@ -3623,7 +3449,6 @@ mod tests { U256::zero(), vec![0], false, - None, ) .map_err(|e| e.error), <Error<Test>>::ReentranceDenied, @@ -3658,31 +3483,20 @@ mod tests { U256::zero(), vec![], false, - None, ) .unwrap(); let remark_hash = <Test as frame_system::Config>::Hashing::hash(b"Hello World"); assert_eq!( System::events(), - vec![ - EventRecord { - phase: Phase::Initialization, - event: MetaEvent::System(frame_system::Event::Remarked { - sender: BOB_FALLBACK, - hash: remark_hash - }), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: MetaEvent::Contracts(crate::Event::Called { - caller: Origin::from_account_id(ALICE), - contract: BOB_ADDR, - }), - topics: vec![], - }, - ] + vec![EventRecord { + phase: Phase::Initialization, + event: MetaEvent::System(frame_system::Event::Remarked { + sender: BOB_FALLBACK, + hash: remark_hash + }), + topics: vec![], + },] ); }); } @@ -3743,7 +3557,6 @@ mod tests { U256::zero(), vec![], false, - None, ) .unwrap(); @@ -3772,14 +3585,6 @@ mod tests { },), topics: vec![], }, - EventRecord { - phase: Phase::Initialization, - event: MetaEvent::Contracts(crate::Event::Called { - caller: Origin::from_account_id(ALICE), - contract: BOB_ADDR, - }), - topics: vec![], - }, ] ); }); @@ -3870,7 +3675,6 @@ mod tests { vec![], Some(&[0; 32]), false, - None, ) .ok(); assert_eq!(System::account_nonce(&ALICE), 0); @@ -3884,7 +3688,6 @@ mod tests { vec![], Some(&[0; 32]), false, - None, )); assert_eq!(System::account_nonce(&ALICE), 1); @@ -3897,7 +3700,6 @@ mod tests { vec![], Some(&[0; 32]), false, - None, )); assert_eq!(System::account_nonce(&ALICE), 2); @@ -3910,7 +3712,6 @@ mod tests { vec![], Some(&[0; 32]), false, - None, )); assert_eq!(System::account_nonce(&ALICE), 3); }); @@ -3979,7 +3780,6 @@ mod tests { U256::zero(), vec![], false, - None, )); }); } @@ -4091,7 +3891,6 @@ mod tests { U256::zero(), vec![], false, - None, )); }); } @@ -4131,7 +3930,6 @@ mod tests { U256::zero(), vec![], false, - None, )); }); } @@ -4171,7 +3969,6 @@ mod tests { U256::zero(), vec![], false, - None, )); }); } @@ -4225,7 +4022,6 @@ mod tests { U256::zero(), vec![], false, - None, )); }); } @@ -4282,7 +4078,6 @@ mod tests { U256::zero(), vec![], false, - None, )); }); } @@ -4358,7 +4153,6 @@ mod tests { U256::zero(), vec![], false, - None, )); }); } @@ -4429,7 +4223,6 @@ mod tests { U256::zero(), vec![0], false, - None, ); assert_matches!(result, Ok(_)); }); @@ -4468,7 +4261,6 @@ mod tests { U256::zero(), vec![], false, - None, )); }); } @@ -4531,7 +4323,6 @@ mod tests { U256::zero(), vec![0], false, - None, ); assert_matches!(result, Ok(_)); }); @@ -4565,7 +4356,6 @@ mod tests { U256::zero(), vec![], false, - None, ); assert_matches!(result, Ok(_)); }); @@ -4641,7 +4431,6 @@ mod tests { U256::zero(), vec![], false, - None, ) .unwrap() }); @@ -4710,7 +4499,6 @@ mod tests { U256::zero(), vec![0], false, - None, ); assert_matches!(result, Ok(_)); }); @@ -4782,7 +4570,6 @@ mod tests { U256::zero(), vec![], false, - None, ); assert_matches!(result, Ok(_)); }); @@ -4834,7 +4621,6 @@ mod tests { U256::zero(), vec![], false, - None, ) .unwrap() }); @@ -4904,7 +4690,6 @@ mod tests { U256::zero(), vec![], false, - None, ) .unwrap() }); @@ -4951,7 +4736,6 @@ mod tests { U256::zero(), vec![], false, - None, ) .unwrap() }); @@ -4996,7 +4780,6 @@ mod tests { U256::zero(), vec![], false, - None, ) .unwrap() }); @@ -5052,7 +4835,6 @@ mod tests { U256::zero(), vec![0], false, - None, ), Ok(_) ); diff --git a/substrate/frame/revive/src/lib.rs b/substrate/frame/revive/src/lib.rs index bdb4b92edd9e62c5d13cfb154ce42887b5b103e5..c36cb3f47caed988ddefbd20a1f1caef680f2c75 100644 --- a/substrate/frame/revive/src/lib.rs +++ b/substrate/frame/revive/src/lib.rs @@ -35,9 +35,9 @@ mod wasm; mod tests; pub mod chain_extension; -pub mod debug; pub mod evm; pub mod test_utils; +pub mod tracing; pub mod weights; use crate::{ @@ -71,7 +71,7 @@ use frame_support::{ use frame_system::{ ensure_signed, pallet_prelude::{BlockNumberFor, OriginFor}, - EventRecord, Pallet as System, + Pallet as System, }; use pallet_transaction_payment::OnChargeTransaction; use scale_info::TypeInfo; @@ -83,7 +83,6 @@ use sp_runtime::{ pub use crate::{ address::{create1, create2, AccountId32Mapper, AddressMapper}, - debug::Tracing, exec::{MomentOf, Origin}, pallet::*, }; @@ -98,9 +97,6 @@ type BalanceOf<T> = <<T as Config>::Currency as Inspect<<T as frame_system::Config>::AccountId>>::Balance; type OnChargeTransactionBalanceOf<T> = <<T as pallet_transaction_payment::Config>::OnChargeTransaction as OnChargeTransaction<T>>::Balance; type CodeVec = BoundedVec<u8, ConstU32<{ limits::code::BLOB_BYTES }>>; -type EventRecordOf<T> = - EventRecord<<T as frame_system::Config>::RuntimeEvent, <T as frame_system::Config>::Hash>; -type DebugBuffer = BoundedVec<u8, ConstU32<{ limits::DEBUG_BUFFER_BYTES }>>; type ImmutableData = BoundedVec<u8, ConstU32<{ limits::IMMUTABLE_BYTES }>>; /// Used as a sentinel value when reading and writing contract memory. @@ -121,7 +117,6 @@ const LOG_TARGET: &str = "runtime::revive"; #[frame_support::pallet] pub mod pallet { use super::*; - use crate::debug::Debugger; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use sp_core::U256; @@ -258,12 +253,6 @@ pub mod pallet { #[pallet::no_default_bounds] type InstantiateOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = Self::AccountId>; - /// For most production chains, it's recommended to use the `()` implementation of this - /// trait. This implementation offers additional logging when the log target - /// "runtime::revive" is set to trace. - #[pallet::no_default_bounds] - type Debug: Debugger<Self>; - /// A type that exposes XCM APIs, allowing contracts to interact with other parachains, and /// execute XCM programs. #[pallet::no_default_bounds] @@ -370,7 +359,6 @@ pub mod pallet { type InstantiateOrigin = EnsureSigned<AccountId>; type WeightInfo = (); type WeightPrice = Self; - type Debug = (); type Xcm = (); type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>; type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>; @@ -382,25 +370,6 @@ pub mod pallet { #[pallet::event] pub enum Event<T: Config> { - /// Contract deployed by address at the specified address. - Instantiated { deployer: H160, contract: H160 }, - - /// Contract has been removed. - /// - /// # Note - /// - /// The only way for a contract to be removed and emitting this event is by calling - /// `seal_terminate`. - Terminated { - /// The contract that was terminated. - contract: H160, - /// The account that received the contracts remaining balance - beneficiary: H160, - }, - - /// Code with the specified hash has been stored. - CodeStored { code_hash: H256, deposit_held: BalanceOf<T>, uploader: H160 }, - /// A custom event emitted by the contract. ContractEmitted { /// The contract that emitted the event. @@ -412,54 +381,6 @@ pub mod pallet { /// Number of topics is capped by [`limits::NUM_EVENT_TOPICS`]. topics: Vec<H256>, }, - - /// A code with the specified hash was removed. - CodeRemoved { code_hash: H256, deposit_released: BalanceOf<T>, remover: H160 }, - - /// A contract's code was updated. - ContractCodeUpdated { - /// The contract that has been updated. - contract: H160, - /// New code hash that was set for the contract. - new_code_hash: H256, - /// Previous code hash of the contract. - old_code_hash: H256, - }, - - /// A contract was called either by a plain account or another contract. - /// - /// # Note - /// - /// Please keep in mind that like all events this is only emitted for successful - /// calls. This is because on failure all storage changes including events are - /// rolled back. - Called { - /// The caller of the `contract`. - caller: Origin<T>, - /// The contract that was called. - contract: H160, - }, - - /// A contract delegate called a code hash. - /// - /// # Note - /// - /// Please keep in mind that like all events this is only emitted for successful - /// calls. This is because on failure all storage changes including events are - /// rolled back. - DelegateCalled { - /// The contract that performed the delegate call and hence in whose context - /// the `code_hash` is executed. - contract: H160, - /// The code hash that was delegate called. - code_hash: H256, - }, - - /// Some funds have been transferred and held as storage deposit. - StorageDepositTransferredAndHeld { from: H160, to: H160, amount: BalanceOf<T> }, - - /// Some storage deposit funds have been transferred and released. - StorageDepositTransferredAndReleased { from: H160, to: H160, amount: BalanceOf<T> }, } #[pallet::error] @@ -810,9 +731,8 @@ pub mod pallet { gas_limit, DepositLimit::Balance(storage_deposit_limit), data, - DebugInfo::Skip, - CollectEvents::Skip, ); + if let Ok(return_value) = &output.result { if return_value.did_revert() { output.result = Err(<Error<T>>::ContractReverted.into()); @@ -848,8 +768,6 @@ pub mod pallet { Code::Existing(code_hash), data, salt, - DebugInfo::Skip, - CollectEvents::Skip, ); if let Ok(retval) = &output.result { if retval.result.did_revert() { @@ -914,8 +832,6 @@ pub mod pallet { Code::Upload(code), data, salt, - DebugInfo::Skip, - CollectEvents::Skip, ); if let Ok(retval) = &output.result { if retval.result.did_revert() { @@ -993,11 +909,6 @@ pub mod pallet { }; <ExecStack<T, WasmBlob<T>>>::increment_refcount(code_hash)?; <ExecStack<T, WasmBlob<T>>>::decrement_refcount(contract.code_hash); - Self::deposit_event(Event::ContractCodeUpdated { - contract: dest, - new_code_hash: code_hash, - old_code_hash: contract.code_hash, - }); contract.code_hash = code_hash; Ok(()) }) @@ -1085,16 +996,10 @@ where gas_limit: Weight, storage_deposit_limit: DepositLimit<BalanceOf<T>>, data: Vec<u8>, - debug: DebugInfo, - collect_events: CollectEvents, - ) -> ContractResult<ExecReturnValue, BalanceOf<T>, EventRecordOf<T>> { + ) -> ContractResult<ExecReturnValue, BalanceOf<T>> { let mut gas_meter = GasMeter::new(gas_limit); let mut storage_deposit = Default::default(); - let mut debug_message = if matches!(debug, DebugInfo::UnsafeDebug) { - Some(DebugBuffer::default()) - } else { - None - }; + let try_call = || { let origin = Origin::from_runtime_origin(origin)?; let mut storage_meter = match storage_deposit_limit { @@ -1109,7 +1014,6 @@ where Self::convert_native_to_evm(value), data, storage_deposit_limit.is_unchecked(), - debug_message.as_mut(), )?; storage_deposit = storage_meter .try_into_deposit(&origin, storage_deposit_limit.is_unchecked()) @@ -1119,18 +1023,11 @@ where Ok(result) }; let result = Self::run_guarded(try_call); - let events = if matches!(collect_events, CollectEvents::UnsafeCollect) { - Some(System::<T>::read_events_no_consensus().map(|e| *e).collect()) - } else { - None - }; ContractResult { result: result.map_err(|r| r.error), gas_consumed: gas_meter.gas_consumed(), gas_required: gas_meter.gas_required(), storage_deposit, - debug_message: debug_message.unwrap_or_default().to_vec(), - events, } } @@ -1138,8 +1035,7 @@ where /// /// Identical to [`Self::instantiate`] or [`Self::instantiate_with_code`] but tailored towards /// being called by other code within the runtime as opposed to from an extrinsic. It returns - /// more information and allows the enablement of features that are not suitable for an - /// extrinsic (debugging, event collection). + /// more information to the caller useful to estimate the cost of the operation. pub fn bare_instantiate( origin: OriginFor<T>, value: BalanceOf<T>, @@ -1148,14 +1044,9 @@ where code: Code, data: Vec<u8>, salt: Option<[u8; 32]>, - debug: DebugInfo, - collect_events: CollectEvents, - ) -> ContractResult<InstantiateReturnValue, BalanceOf<T>, EventRecordOf<T>> { + ) -> ContractResult<InstantiateReturnValue, BalanceOf<T>> { let mut gas_meter = GasMeter::new(gas_limit); let mut storage_deposit = Default::default(); - let mut debug_message = - if debug == DebugInfo::UnsafeDebug { Some(DebugBuffer::default()) } else { None }; - let unchecked_deposit_limit = storage_deposit_limit.is_unchecked(); let mut storage_deposit_limit = match storage_deposit_limit { DepositLimit::Balance(limit) => limit, @@ -1195,7 +1086,6 @@ where data, salt.as_ref(), unchecked_deposit_limit, - debug_message.as_mut(), ); storage_deposit = storage_meter .try_into_deposit(&instantiate_origin, unchecked_deposit_limit)? @@ -1203,11 +1093,6 @@ where result }; let output = Self::run_guarded(try_instantiate); - let events = if matches!(collect_events, CollectEvents::UnsafeCollect) { - Some(System::<T>::read_events_no_consensus().map(|e| *e).collect()) - } else { - None - }; ContractResult { result: output .map(|(addr, result)| InstantiateReturnValue { result, addr }) @@ -1215,8 +1100,6 @@ where gas_consumed: gas_meter.gas_consumed(), gas_required: gas_meter.gas_required(), storage_deposit, - debug_message: debug_message.unwrap_or_default().to_vec(), - events, } } @@ -1254,7 +1137,6 @@ where DepositLimit::Unchecked }; - // TODO remove once we have revisited how we encode the gas limit. if tx.nonce.is_none() { tx.nonce = Some(<System<T>>::account_nonce(&origin).into()); } @@ -1273,8 +1155,6 @@ where }; let input = tx.input.clone().unwrap_or_default().0; - let debug = DebugInfo::Skip; - let collect_events = CollectEvents::Skip; let extract_error = |err| { if err == Error::<T>::TransferFailed.into() || @@ -1305,8 +1185,6 @@ where gas_limit, storage_deposit_limit, input.clone(), - debug, - collect_events, ); let data = match result.result { @@ -1363,8 +1241,6 @@ where Code::Upload(code.to_vec()), data.to_vec(), None, - debug, - collect_events, ); let returned_data = match result.result { @@ -1535,12 +1411,11 @@ environmental!(executing_contract: bool); sp_api::decl_runtime_apis! { /// The API used to dry-run contract interactions. #[api_version(1)] - pub trait ReviveApi<AccountId, Balance, Nonce, BlockNumber, EventRecord> where + pub trait ReviveApi<AccountId, Balance, Nonce, BlockNumber> where AccountId: Codec, Balance: Codec, Nonce: Codec, BlockNumber: Codec, - EventRecord: Codec, { /// Returns the free balance of the given `[H160]` address, using EVM decimals. fn balance(address: H160) -> U256; @@ -1558,7 +1433,7 @@ sp_api::decl_runtime_apis! { gas_limit: Option<Weight>, storage_deposit_limit: Option<Balance>, input_data: Vec<u8>, - ) -> ContractResult<ExecReturnValue, Balance, EventRecord>; + ) -> ContractResult<ExecReturnValue, Balance>; /// Instantiate a new contract. /// @@ -1571,7 +1446,7 @@ sp_api::decl_runtime_apis! { code: Code, data: Vec<u8>, salt: Option<[u8; 32]>, - ) -> ContractResult<InstantiateReturnValue, Balance, EventRecord>; + ) -> ContractResult<InstantiateReturnValue, Balance>; /// Perform an Ethereum call. diff --git a/substrate/frame/revive/src/limits.rs b/substrate/frame/revive/src/limits.rs index 3b55106c67d8ca45ebe6dcf6ce6b33c7e4d0fc68..f101abf0ea7e9478be157c693b4f251d80af4df7 100644 --- a/substrate/frame/revive/src/limits.rs +++ b/substrate/frame/revive/src/limits.rs @@ -57,11 +57,6 @@ pub const TRANSIENT_STORAGE_BYTES: u32 = 4 * 1024; /// The maximum allowable length in bytes for (transient) storage keys. pub const STORAGE_KEY_BYTES: u32 = 128; -/// The maximum size of the debug buffer contracts can write messages to. -/// -/// The buffer will always be disabled for on-chain execution. -pub const DEBUG_BUFFER_BYTES: u32 = 2 * 1024 * 1024; - /// The page size in which PolkaVM should allocate memory chunks. pub const PAGE_SIZE: u32 = 4 * 1024; diff --git a/substrate/frame/revive/src/primitives.rs b/substrate/frame/revive/src/primitives.rs index 452d2c8a3067cf1e7540abfd5bc44b615be69226..9c149c7cc38905bc75d57ce9d3af1cc6bf2e86a4 100644 --- a/substrate/frame/revive/src/primitives.rs +++ b/substrate/frame/revive/src/primitives.rs @@ -63,7 +63,7 @@ impl<T> From<T> for DepositLimit<T> { /// `ContractsApi` version. Therefore when SCALE decoding a `ContractResult` its trailing data /// should be ignored to avoid any potential compatibility issues. #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] -pub struct ContractResult<R, Balance, EventRecord> { +pub struct ContractResult<R, Balance> { /// How much weight was consumed during execution. pub gas_consumed: Weight, /// How much weight is required as gas limit in order to execute this call. @@ -84,26 +84,8 @@ pub struct ContractResult<R, Balance, EventRecord> { /// is `Err`. This is because on error all storage changes are rolled back including the /// payment of the deposit. pub storage_deposit: StorageDeposit<Balance>, - /// An optional debug message. This message is only filled when explicitly requested - /// by the code that calls into the contract. Otherwise it is empty. - /// - /// The contained bytes are valid UTF-8. This is not declared as `String` because - /// this type is not allowed within the runtime. - /// - /// Clients should not make any assumptions about the format of the buffer. - /// They should just display it as-is. It is **not** only a collection of log lines - /// provided by a contract but a formatted buffer with different sections. - /// - /// # Note - /// - /// The debug message is never generated during on-chain execution. It is reserved for - /// RPC calls. - pub debug_message: Vec<u8>, /// The execution result of the wasm code. pub result: Result<R, DispatchError>, - /// The events that were emitted during execution. It is an option as event collection is - /// optional. - pub events: Option<Vec<EventRecord>>, } /// The result of the execution of a `eth_transact` call. @@ -284,36 +266,3 @@ where } } } - -/// Determines whether events should be collected during execution. -#[derive( - Copy, Clone, PartialEq, Eq, RuntimeDebug, Decode, Encode, MaxEncodedLen, scale_info::TypeInfo, -)] -pub enum CollectEvents { - /// Collect events. - /// - /// # Note - /// - /// Events should only be collected when called off-chain, as this would otherwise - /// collect all the Events emitted in the block so far and put them into the PoV. - /// - /// **Never** use this mode for on-chain execution. - UnsafeCollect, - /// Skip event collection. - Skip, -} - -/// Determines whether debug messages will be collected. -#[derive( - Copy, Clone, PartialEq, Eq, RuntimeDebug, Decode, Encode, MaxEncodedLen, scale_info::TypeInfo, -)] -pub enum DebugInfo { - /// Collect debug messages. - /// # Note - /// - /// This should only ever be set to `UnsafeDebug` when executing as an RPC because - /// it adds allocations and could be abused to drive the runtime into an OOM panic. - UnsafeDebug, - /// Skip collection of debug messages. - Skip, -} diff --git a/substrate/frame/revive/src/storage/meter.rs b/substrate/frame/revive/src/storage/meter.rs index 4febcb0c4066c418b1b7189c12a4cc8403601f72..cd390c86f63ae94a30d065def82e628a0091b653 100644 --- a/substrate/frame/revive/src/storage/meter.rs +++ b/substrate/frame/revive/src/storage/meter.rs @@ -18,8 +18,8 @@ //! This module contains functions to meter the storage deposit. use crate::{ - address::AddressMapper, storage::ContractInfo, AccountIdOf, BalanceOf, CodeInfo, Config, Error, - Event, HoldReason, Inspect, Origin, Pallet, StorageDeposit as Deposit, System, LOG_TARGET, + storage::ContractInfo, AccountIdOf, BalanceOf, CodeInfo, Config, Error, HoldReason, Inspect, + Origin, Pallet, StorageDeposit as Deposit, System, LOG_TARGET, }; use alloc::vec::Vec; use core::{fmt::Debug, marker::PhantomData}; @@ -516,12 +516,6 @@ impl<T: Config> Ext<T> for ReservingExt { Preservation::Preserve, Fortitude::Polite, )?; - - Pallet::<T>::deposit_event(Event::StorageDepositTransferredAndHeld { - from: T::AddressMapper::to_address(origin), - to: T::AddressMapper::to_address(contract), - amount: *amount, - }); }, Deposit::Refund(amount) => { let transferred = T::Currency::transfer_on_hold( @@ -534,12 +528,6 @@ impl<T: Config> Ext<T> for ReservingExt { Fortitude::Polite, )?; - Pallet::<T>::deposit_event(Event::StorageDepositTransferredAndReleased { - from: T::AddressMapper::to_address(contract), - to: T::AddressMapper::to_address(origin), - amount: transferred, - }); - if transferred < *amount { // This should never happen, if it does it means that there is a bug in the // runtime logic. In the rare case this happens we try to refund as much as we diff --git a/substrate/frame/revive/src/test_utils/builder.rs b/substrate/frame/revive/src/test_utils/builder.rs index 8ba5e7384070289617c667c8e95b0608a2846a6d..7fbb5b676439e6c378e3b51683bac449513faca5 100644 --- a/substrate/frame/revive/src/test_utils/builder.rs +++ b/substrate/frame/revive/src/test_utils/builder.rs @@ -17,9 +17,8 @@ use super::{deposit_limit, GAS_LIMIT}; use crate::{ - address::AddressMapper, AccountIdOf, BalanceOf, Code, CollectEvents, Config, ContractResult, - DebugInfo, DepositLimit, EventRecordOf, ExecReturnValue, InstantiateReturnValue, OriginFor, - Pallet, Weight, + address::AddressMapper, AccountIdOf, BalanceOf, Code, Config, ContractResult, DepositLimit, + ExecReturnValue, InstantiateReturnValue, OriginFor, Pallet, Weight, }; use frame_support::pallet_prelude::DispatchResultWithPostInfo; use paste::paste; @@ -138,9 +137,7 @@ builder!( code: Code, data: Vec<u8>, salt: Option<[u8; 32]>, - debug: DebugInfo, - collect_events: CollectEvents, - ) -> ContractResult<InstantiateReturnValue, BalanceOf<T>, EventRecordOf<T>>; + ) -> ContractResult<InstantiateReturnValue, BalanceOf<T>>; /// Build the instantiate call and unwrap the result. pub fn build_and_unwrap_result(self) -> InstantiateReturnValue { @@ -164,8 +161,6 @@ builder!( code, data: vec![], salt: Some([0; 32]), - debug: DebugInfo::UnsafeDebug, - collect_events: CollectEvents::Skip, } } ); @@ -201,9 +196,7 @@ builder!( gas_limit: Weight, storage_deposit_limit: DepositLimit<BalanceOf<T>>, data: Vec<u8>, - debug: DebugInfo, - collect_events: CollectEvents, - ) -> ContractResult<ExecReturnValue, BalanceOf<T>, EventRecordOf<T>>; + ) -> ContractResult<ExecReturnValue, BalanceOf<T>>; /// Build the call and unwrap the result. pub fn build_and_unwrap_result(self) -> ExecReturnValue { @@ -219,8 +212,6 @@ builder!( gas_limit: GAS_LIMIT, storage_deposit_limit: DepositLimit::Balance(deposit_limit::<T>()), data: vec![], - debug: DebugInfo::UnsafeDebug, - collect_events: CollectEvents::Skip, } } ); diff --git a/substrate/frame/revive/src/tests.rs b/substrate/frame/revive/src/tests.rs index cf02d17a4d03ddaf5ed2df81bf9e01e98ca1e1bb..90b9f053a03fbf05fd180be81c23da63955134ef 100644 --- a/substrate/frame/revive/src/tests.rs +++ b/substrate/frame/revive/src/tests.rs @@ -16,12 +16,8 @@ // limitations under the License. mod pallet_dummy; -mod test_debug; -use self::{ - test_debug::TestDebug, - test_utils::{ensure_stored, expected_deposit}, -}; +use self::test_utils::{ensure_stored, expected_deposit}; use crate::{ self as pallet_revive, address::{create1, create2, AddressMapper}, @@ -29,18 +25,19 @@ use crate::{ ChainExtension, Environment, Ext, RegisteredChainExtension, Result as ExtensionResult, RetVal, ReturnFlags, }, - evm::{runtime::GAS_PRICE, GenericTransaction}, + evm::{runtime::GAS_PRICE, CallTrace, CallTracer, CallType, GenericTransaction}, exec::Key, limits, primitives::CodeUploadReturnValue, storage::DeletionQueueManager, test_utils::*, tests::test_utils::{get_contract, get_contract_checked}, + tracing::trace, wasm::Memory, weights::WeightInfo, - AccountId32Mapper, BalanceOf, Code, CodeInfoOf, CollectEvents, Config, ContractInfo, - ContractInfoOf, DebugInfo, DeletionQueueCounter, DepositLimit, Error, EthTransactError, - HoldReason, Origin, Pallet, PristineCode, H160, + AccountId32Mapper, BalanceOf, Code, CodeInfoOf, Config, ContractInfo, ContractInfoOf, + DeletionQueueCounter, DepositLimit, Error, EthTransactError, HoldReason, Origin, Pallet, + PristineCode, H160, }; use crate::test_utils::builder::Contract; @@ -523,7 +520,6 @@ impl Config for Test { type UploadOrigin = EnsureAccount<Self, UploadAccount>; type InstantiateOrigin = EnsureAccount<Self, InstantiateAccount>; type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; - type Debug = TestDebug; type ChainId = ChainId; } @@ -713,25 +709,6 @@ fn instantiate_and_call_and_deposit_event() { }), topics: vec![], }, - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts(crate::Event::Instantiated { - deployer: ALICE_ADDR, - contract: addr - }), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts( - pallet_revive::Event::StorageDepositTransferredAndHeld { - from: ALICE_ADDR, - to: addr, - amount: test_utils::contract_info_storage_deposit(&addr), - } - ), - topics: vec![], - }, ] ); }); @@ -1078,14 +1055,6 @@ fn deploy_and_call_other_contract() { }), topics: vec![], }, - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts(crate::Event::Instantiated { - deployer: caller_addr, - contract: callee_addr, - }), - topics: vec![], - }, EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { @@ -1095,33 +1064,6 @@ fn deploy_and_call_other_contract() { }), topics: vec![], }, - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts(crate::Event::Called { - caller: Origin::from_account_id(caller_account.clone()), - contract: callee_addr, - }), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts(crate::Event::Called { - caller: Origin::from_account_id(ALICE), - contract: caller_addr, - }), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts( - pallet_revive::Event::StorageDepositTransferredAndHeld { - from: ALICE_ADDR, - to: callee_addr, - amount: test_utils::contract_info_storage_deposit(&callee_addr), - } - ), - topics: vec![], - }, ] ); }); @@ -1373,8 +1315,6 @@ fn self_destruct_works() { // Check that the BOB contract has been instantiated. let _ = get_contract(&contract.addr); - let info_deposit = test_utils::contract_info_storage_deposit(&contract.addr); - // Drop all previous events initialize_block(2); @@ -1404,33 +1344,6 @@ fn self_destruct_works() { pretty_assertions::assert_eq!( System::events(), vec![ - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts(crate::Event::Terminated { - contract: contract.addr, - beneficiary: DJANGO_ADDR, - }), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts(crate::Event::Called { - caller: Origin::from_account_id(ALICE), - contract: contract.addr, - }), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts( - pallet_revive::Event::StorageDepositTransferredAndReleased { - from: contract.addr, - to: ALICE_ADDR, - amount: info_deposit, - } - ), - topics: vec![], - }, EventRecord { phase: Phase::Initialization, event: RuntimeEvent::System(frame_system::Event::KilledAccount { @@ -2184,58 +2097,6 @@ fn refcounter() { }); } -#[test] -fn debug_message_works() { - let (wasm, _code_hash) = compile_module("debug_message_works").unwrap(); - - ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000); - let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) - .value(30_000) - .build_and_unwrap_contract(); - let result = builder::bare_call(addr).debug(DebugInfo::UnsafeDebug).build(); - - assert_matches!(result.result, Ok(_)); - assert_eq!(std::str::from_utf8(&result.debug_message).unwrap(), "Hello World!"); - }); -} - -#[test] -fn debug_message_logging_disabled() { - let (wasm, _code_hash) = compile_module("debug_message_logging_disabled").unwrap(); - - ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000); - let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) - .value(30_000) - .build_and_unwrap_contract(); - // the dispatchables always run without debugging - assert_ok!(Contracts::call( - RuntimeOrigin::signed(ALICE), - addr, - 0, - GAS_LIMIT, - deposit_limit::<Test>(), - vec![] - )); - }); -} - -#[test] -fn debug_message_invalid_utf8() { - let (wasm, _code_hash) = compile_module("debug_message_invalid_utf8").unwrap(); - - ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000); - let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) - .value(30_000) - .build_and_unwrap_contract(); - let result = builder::bare_call(addr).debug(DebugInfo::UnsafeDebug).build(); - assert_ok!(result.result); - assert!(result.debug_message.is_empty()); - }); -} - #[test] fn gas_estimation_for_subcalls() { let (caller_code, _caller_hash) = compile_module("call_with_limit").unwrap(); @@ -2451,79 +2312,6 @@ fn ecdsa_recover() { }) } -#[test] -fn bare_instantiate_returns_events() { - let (wasm, _code_hash) = compile_module("transfer_return_code").unwrap(); - ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - let min_balance = Contracts::min_balance(); - let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance); - - let result = builder::bare_instantiate(Code::Upload(wasm)) - .value(min_balance * 100) - .collect_events(CollectEvents::UnsafeCollect) - .build(); - - let events = result.events.unwrap(); - assert!(!events.is_empty()); - assert_eq!(events, System::events()); - }); -} - -#[test] -fn bare_instantiate_does_not_return_events() { - let (wasm, _code_hash) = compile_module("transfer_return_code").unwrap(); - ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - let min_balance = Contracts::min_balance(); - let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance); - - let result = builder::bare_instantiate(Code::Upload(wasm)).value(min_balance * 100).build(); - - let events = result.events; - assert!(!System::events().is_empty()); - assert!(events.is_none()); - }); -} - -#[test] -fn bare_call_returns_events() { - let (wasm, _code_hash) = compile_module("transfer_return_code").unwrap(); - ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - let min_balance = Contracts::min_balance(); - let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance); - - let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) - .value(min_balance * 100) - .build_and_unwrap_contract(); - - let result = builder::bare_call(addr).collect_events(CollectEvents::UnsafeCollect).build(); - - let events = result.events.unwrap(); - assert_return_code!(&result.result.unwrap(), RuntimeReturnCode::Success); - assert!(!events.is_empty()); - assert_eq!(events, System::events()); - }); -} - -#[test] -fn bare_call_does_not_return_events() { - let (wasm, _code_hash) = compile_module("transfer_return_code").unwrap(); - ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - let min_balance = Contracts::min_balance(); - let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance); - - let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) - .value(min_balance * 100) - .build_and_unwrap_contract(); - - let result = builder::bare_call(addr).build(); - - let events = result.events; - assert_return_code!(&result.result.unwrap(), RuntimeReturnCode::Success); - assert!(!System::events().is_empty()); - assert!(events.is_none()); - }); -} - #[test] fn sr25519_verify() { let (wasm, _code_hash) = compile_module("sr25519_verify").unwrap(); @@ -2637,23 +2425,9 @@ fn upload_code_works() { initialize_block(2); assert!(!PristineCode::<Test>::contains_key(&code_hash)); - assert_ok!(Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm, 1_000,)); // Ensure the contract was stored and get expected deposit amount to be reserved. - let deposit_expected = expected_deposit(ensure_stored(code_hash)); - - assert_eq!( - System::events(), - vec![EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts(crate::Event::CodeStored { - code_hash, - deposit_held: deposit_expected, - uploader: ALICE_ADDR - }), - topics: vec![], - },] - ); + expected_deposit(ensure_stored(code_hash)); }); } @@ -2711,32 +2485,8 @@ fn remove_code_works() { assert_ok!(Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm, 1_000,)); // Ensure the contract was stored and get expected deposit amount to be reserved. - let deposit_expected = expected_deposit(ensure_stored(code_hash)); - + expected_deposit(ensure_stored(code_hash)); assert_ok!(Contracts::remove_code(RuntimeOrigin::signed(ALICE), code_hash)); - assert_eq!( - System::events(), - vec![ - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts(crate::Event::CodeStored { - code_hash, - deposit_held: deposit_expected, - uploader: ALICE_ADDR - }), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts(crate::Event::CodeRemoved { - code_hash, - deposit_released: deposit_expected, - remover: ALICE_ADDR - }), - topics: vec![], - }, - ] - ); }); } @@ -2752,25 +2502,12 @@ fn remove_code_wrong_origin() { assert_ok!(Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm, 1_000,)); // Ensure the contract was stored and get expected deposit amount to be reserved. - let deposit_expected = expected_deposit(ensure_stored(code_hash)); + expected_deposit(ensure_stored(code_hash)); assert_noop!( Contracts::remove_code(RuntimeOrigin::signed(BOB), code_hash), sp_runtime::traits::BadOrigin, ); - - assert_eq!( - System::events(), - vec![EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts(crate::Event::CodeStored { - code_hash, - deposit_held: deposit_expected, - uploader: ALICE_ADDR - }), - topics: vec![], - },] - ); }); } @@ -2829,7 +2566,7 @@ fn instantiate_with_zero_balance_works() { builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract(); // Ensure the contract was stored and get expected deposit amount to be reserved. - let deposit_expected = expected_deposit(ensure_stored(code_hash)); + expected_deposit(ensure_stored(code_hash)); // Make sure the account exists even though no free balance was send assert_eq!(<Test as Config>::Currency::free_balance(&account_id), min_balance); @@ -2841,15 +2578,6 @@ fn instantiate_with_zero_balance_works() { assert_eq!( System::events(), vec![ - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts(crate::Event::CodeStored { - code_hash, - deposit_held: deposit_expected, - uploader: ALICE_ADDR - }), - topics: vec![], - }, EventRecord { phase: Phase::Initialization, event: RuntimeEvent::System(frame_system::Event::NewAccount { @@ -2874,25 +2602,6 @@ fn instantiate_with_zero_balance_works() { }), topics: vec![], }, - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts(crate::Event::Instantiated { - deployer: ALICE_ADDR, - contract: addr, - }), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts( - pallet_revive::Event::StorageDepositTransferredAndHeld { - from: ALICE_ADDR, - to: addr, - amount: test_utils::contract_info_storage_deposit(&addr), - } - ), - topics: vec![], - }, ] ); }); @@ -2915,7 +2624,7 @@ fn instantiate_with_below_existential_deposit_works() { .build_and_unwrap_contract(); // Ensure the contract was stored and get expected deposit amount to be reserved. - let deposit_expected = expected_deposit(ensure_stored(code_hash)); + expected_deposit(ensure_stored(code_hash)); // Make sure the account exists even though not enough free balance was send assert_eq!(<Test as Config>::Currency::free_balance(&account_id), min_balance + value); assert_eq!( @@ -2926,15 +2635,6 @@ fn instantiate_with_below_existential_deposit_works() { assert_eq!( System::events(), vec![ - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts(crate::Event::CodeStored { - code_hash, - deposit_held: deposit_expected, - uploader: ALICE_ADDR - }), - topics: vec![], - }, EventRecord { phase: Phase::Initialization, event: RuntimeEvent::System(frame_system::Event::NewAccount { @@ -2968,25 +2668,6 @@ fn instantiate_with_below_existential_deposit_works() { }), topics: vec![], }, - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts(crate::Event::Instantiated { - deployer: ALICE_ADDR, - contract: addr, - }), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts( - pallet_revive::Event::StorageDepositTransferredAndHeld { - from: ALICE_ADDR, - to: addr, - amount: test_utils::contract_info_storage_deposit(&addr), - } - ), - topics: vec![], - }, ] ); }); @@ -3028,74 +2709,15 @@ fn storage_deposit_works() { assert_eq!( System::events(), - vec![ - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { - from: ALICE, - to: account_id.clone(), - amount: 42, - }), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts(crate::Event::Called { - caller: Origin::from_account_id(ALICE), - contract: addr, - }), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts( - pallet_revive::Event::StorageDepositTransferredAndHeld { - from: ALICE_ADDR, - to: addr, - amount: charged0, - } - ), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts(crate::Event::Called { - caller: Origin::from_account_id(ALICE), - contract: addr, - }), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts( - pallet_revive::Event::StorageDepositTransferredAndHeld { - from: ALICE_ADDR, - to: addr, - amount: charged1, - } - ), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts(crate::Event::Called { - caller: Origin::from_account_id(ALICE), - contract: addr, - }), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts( - pallet_revive::Event::StorageDepositTransferredAndReleased { - from: addr, - to: ALICE_ADDR, - amount: refunded0, - } - ), - topics: vec![], - }, - ] + vec![EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { + from: ALICE, + to: account_id.clone(), + amount: 42, + }), + topics: vec![], + },] ); }); } @@ -3188,18 +2810,6 @@ fn set_code_extrinsic() { assert_eq!(get_contract(&addr).code_hash, new_code_hash); assert_refcount!(&code_hash, 0); assert_refcount!(&new_code_hash, 1); - assert_eq!( - System::events(), - vec![EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts(pallet_revive::Event::ContractCodeUpdated { - contract: addr, - new_code_hash, - old_code_hash: code_hash, - }), - topics: vec![], - },] - ); }); } @@ -3305,7 +2915,7 @@ fn contract_reverted() { #[test] fn set_code_hash() { - let (wasm, code_hash) = compile_module("set_code_hash").unwrap(); + let (wasm, _) = compile_module("set_code_hash").unwrap(); let (new_wasm, new_code_hash) = compile_module("new_set_code_hash_contract").unwrap(); ExtBuilder::default().existential_deposit(100).build().execute_with(|| { @@ -3327,47 +2937,12 @@ fn set_code_hash() { // First call sets new code_hash and returns 1 let result = builder::bare_call(contract_addr) .data(new_code_hash.as_ref().to_vec()) - .debug(DebugInfo::UnsafeDebug) .build_and_unwrap_result(); assert_return_code!(result, 1); // Second calls new contract code that returns 2 - let result = builder::bare_call(contract_addr) - .debug(DebugInfo::UnsafeDebug) - .build_and_unwrap_result(); + let result = builder::bare_call(contract_addr).build_and_unwrap_result(); assert_return_code!(result, 2); - - // Checking for the last event only - assert_eq!( - &System::events(), - &[ - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts(crate::Event::ContractCodeUpdated { - contract: contract_addr, - new_code_hash, - old_code_hash: code_hash, - }), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts(crate::Event::Called { - caller: Origin::from_account_id(ALICE), - contract: contract_addr, - }), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts(crate::Event::Called { - caller: Origin::from_account_id(ALICE), - contract: contract_addr, - }), - topics: vec![], - }, - ], - ); }); } @@ -4660,6 +4235,43 @@ fn origin_api_works() { }); } +#[test] +fn to_account_id_works() { + let (code_hash_code, _) = compile_module("to_account_id").unwrap(); + + ExtBuilder::default().existential_deposit(1).build().execute_with(|| { + let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000); + let _ = <Test as Config>::Currency::set_balance(&EVE, 1_000_000); + + let Contract { addr, .. } = + builder::bare_instantiate(Code::Upload(code_hash_code)).build_and_unwrap_contract(); + + // mapped account + <Pallet<Test>>::map_account(RuntimeOrigin::signed(EVE)).unwrap(); + let expected_mapped_account_id = &<Test as Config>::AddressMapper::to_account_id(&EVE_ADDR); + assert_ne!( + expected_mapped_account_id.encode()[20..32], + [0xEE; 12], + "fallback suffix found where none should be" + ); + assert_ok!(builder::call(addr) + .data((EVE_ADDR, expected_mapped_account_id).encode()) + .build()); + + // fallback for unmapped accounts + let expected_fallback_account_id = + &<Test as Config>::AddressMapper::to_account_id(&BOB_ADDR); + assert_eq!( + expected_fallback_account_id.encode()[20..32], + [0xEE; 12], + "no fallback suffix found where one should be" + ); + assert_ok!(builder::call(addr) + .data((BOB_ADDR, expected_fallback_account_id).encode()) + .build()); + }); +} + #[test] fn code_hash_works() { let (code_hash_code, self_code_hash) = compile_module("code_hash").unwrap(); @@ -4810,7 +4422,7 @@ fn skip_transfer_works() { ..Default::default() }, Weight::MAX, - |_| 0u32 + |_| 0u32, ), EthTransactError::Message(format!( "insufficient funds for gas * price + value: address {BOB_ADDR:?} have 0 (supplied gas 1)" @@ -4825,7 +4437,7 @@ fn skip_transfer_works() { ..Default::default() }, Weight::MAX, - |_| 0u32 + |_| 0u32, )); let Contract { addr, .. } = @@ -4844,7 +4456,7 @@ fn skip_transfer_works() { ..Default::default() }, Weight::MAX, - |_| 0u32 + |_| 0u32, ), EthTransactError::Message(format!( "insufficient funds for gas * price + value: address {BOB_ADDR:?} have 0 (supplied gas 1)" @@ -4869,7 +4481,7 @@ fn skip_transfer_works() { assert_ok!(Pallet::<Test>::bare_eth_transact( GenericTransaction { from: Some(BOB_ADDR), to: Some(addr), ..Default::default() }, Weight::MAX, - |_| 0u32 + |_| 0u32, )); // works when calling from a contract when no gas is specified. @@ -4881,7 +4493,7 @@ fn skip_transfer_works() { ..Default::default() }, Weight::MAX, - |_| 0u32 + |_| 0u32, )); }); } @@ -4938,3 +4550,151 @@ fn unstable_interface_rejected() { assert_ok!(builder::bare_instantiate(Code::Upload(code)).build().result); }); } + +#[test] +fn tracing_works_for_transfers() { + ExtBuilder::default().build().execute_with(|| { + let _ = <Test as Config>::Currency::set_balance(&ALICE, 100_000_000); + let mut tracer = CallTracer::new(false, |_| U256::zero()); + trace(&mut tracer, || { + builder::bare_call(BOB_ADDR).value(10_000_000).build_and_unwrap_result(); + }); + assert_eq!( + tracer.collect_traces(), + vec![CallTrace { + from: ALICE_ADDR, + to: BOB_ADDR, + value: U256::from(10_000_000), + call_type: CallType::Call, + ..Default::default() + },] + ) + }); +} + +#[test] +fn tracing_works() { + use crate::evm::*; + use CallType::*; + let (code, _code_hash) = compile_module("tracing").unwrap(); + let (wasm_callee, _) = compile_module("tracing_callee").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000); + + let Contract { addr: addr_callee, .. } = + builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_contract(); + + let Contract { addr, .. } = + builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract(); + + let tracer_options = vec![ + ( false , vec![]), + ( + true , + vec![ + CallLog { + address: addr, + topics: Default::default(), + data: b"before".to_vec().into(), + position: 0, + }, + CallLog { + address: addr, + topics: Default::default(), + data: b"after".to_vec().into(), + position: 1, + }, + ], + ), + ]; + + // Verify that the first trace report the same weight reported by bare_call + let mut tracer = CallTracer::new(false, |w| w); + let gas_used = trace(&mut tracer, || { + builder::bare_call(addr).data((3u32, addr_callee).encode()).build().gas_consumed + }); + let traces = tracer.collect_traces(); + assert_eq!(&traces[0].gas_used, &gas_used); + + // Discarding gas usage, check that traces reported are correct + for (with_logs, logs) in tracer_options { + let mut tracer = CallTracer::new(with_logs, |_| U256::zero()); + trace(&mut tracer, || { + builder::bare_call(addr).data((3u32, addr_callee).encode()).build() + }); + + + assert_eq!( + tracer.collect_traces(), + vec![CallTrace { + from: ALICE_ADDR, + to: addr, + input: (3u32, addr_callee).encode(), + call_type: Call, + logs: logs.clone(), + calls: vec![ + CallTrace { + from: addr, + to: addr_callee, + input: 2u32.encode(), + output: hex_literal::hex!( + "08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001a546869732066756e6374696f6e20616c77617973206661696c73000000000000" + ).to_vec().into(), + revert_reason: Some( + "execution reverted: This function always fails".to_string() + ), + error: Some("execution reverted".to_string()), + call_type: Call, + ..Default::default() + }, + CallTrace { + from: addr, + to: addr, + input: (2u32, addr_callee).encode(), + call_type: Call, + logs: logs.clone(), + calls: vec![ + CallTrace { + from: addr, + to: addr_callee, + input: 1u32.encode(), + output: Default::default(), + error: Some("ContractTrapped".to_string()), + call_type: Call, + ..Default::default() + }, + CallTrace { + from: addr, + to: addr, + input: (1u32, addr_callee).encode(), + call_type: Call, + logs: logs.clone(), + calls: vec![ + CallTrace { + from: addr, + to: addr_callee, + input: 0u32.encode(), + output: 0u32.to_le_bytes().to_vec().into(), + call_type: Call, + ..Default::default() + }, + CallTrace { + from: addr, + to: addr, + input: (0u32, addr_callee).encode(), + call_type: Call, + ..Default::default() + }, + ], + ..Default::default() + }, + ], + ..Default::default() + }, + ], + ..Default::default() + },] + ); + } + }); +} diff --git a/substrate/frame/revive/src/tests/test_debug.rs b/substrate/frame/revive/src/tests/test_debug.rs deleted file mode 100644 index c9e19e52ace1372aecf6129c22f03b11f845f66c..0000000000000000000000000000000000000000 --- a/substrate/frame/revive/src/tests/test_debug.rs +++ /dev/null @@ -1,239 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use super::*; - -use crate::{ - debug::{CallInterceptor, CallSpan, ExecResult, ExportedFunction, Tracing}, - primitives::ExecReturnValue, - test_utils::*, - DepositLimit, -}; -use frame_support::traits::Currency; -use pretty_assertions::assert_eq; -use sp_core::H160; -use std::cell::RefCell; - -#[derive(Clone, PartialEq, Eq, Debug)] -struct DebugFrame { - contract_address: sp_core::H160, - call: ExportedFunction, - input: Vec<u8>, - result: Option<Vec<u8>>, -} - -thread_local! { - static DEBUG_EXECUTION_TRACE: RefCell<Vec<DebugFrame>> = RefCell::new(Vec::new()); - static INTERCEPTED_ADDRESS: RefCell<Option<sp_core::H160>> = RefCell::new(None); -} - -pub struct TestDebug; -pub struct TestCallSpan { - contract_address: sp_core::H160, - call: ExportedFunction, - input: Vec<u8>, -} - -impl Tracing<Test> for TestDebug { - type CallSpan = TestCallSpan; - - fn new_call_span( - contract_address: &crate::H160, - entry_point: ExportedFunction, - input_data: &[u8], - ) -> TestCallSpan { - DEBUG_EXECUTION_TRACE.with(|d| { - d.borrow_mut().push(DebugFrame { - contract_address: *contract_address, - call: entry_point, - input: input_data.to_vec(), - result: None, - }) - }); - TestCallSpan { - contract_address: *contract_address, - call: entry_point, - input: input_data.to_vec(), - } - } -} - -impl CallInterceptor<Test> for TestDebug { - fn intercept_call( - contract_address: &sp_core::H160, - _entry_point: ExportedFunction, - _input_data: &[u8], - ) -> Option<ExecResult> { - INTERCEPTED_ADDRESS.with(|i| { - if i.borrow().as_ref() == Some(contract_address) { - Some(Ok(ExecReturnValue { flags: ReturnFlags::REVERT, data: vec![] })) - } else { - None - } - }) - } -} - -impl CallSpan for TestCallSpan { - fn after_call(self, output: &ExecReturnValue) { - DEBUG_EXECUTION_TRACE.with(|d| { - d.borrow_mut().push(DebugFrame { - contract_address: self.contract_address, - call: self.call, - input: self.input, - result: Some(output.data.clone()), - }) - }); - } -} - -#[test] -fn debugging_works() { - let (wasm_caller, _) = compile_module("call").unwrap(); - let (wasm_callee, _) = compile_module("store_call").unwrap(); - - fn current_stack() -> Vec<DebugFrame> { - DEBUG_EXECUTION_TRACE.with(|stack| stack.borrow().clone()) - } - - fn deploy(wasm: Vec<u8>) -> H160 { - Contracts::bare_instantiate( - RuntimeOrigin::signed(ALICE), - 0, - GAS_LIMIT, - DepositLimit::Balance(deposit_limit::<Test>()), - Code::Upload(wasm), - vec![], - Some([0u8; 32]), - DebugInfo::Skip, - CollectEvents::Skip, - ) - .result - .unwrap() - .addr - } - - fn constructor_frame(contract_address: &H160, after: bool) -> DebugFrame { - DebugFrame { - contract_address: *contract_address, - call: ExportedFunction::Constructor, - input: vec![], - result: if after { Some(vec![]) } else { None }, - } - } - - fn call_frame(contract_address: &H160, args: Vec<u8>, after: bool) -> DebugFrame { - DebugFrame { - contract_address: *contract_address, - call: ExportedFunction::Call, - input: args, - result: if after { Some(vec![]) } else { None }, - } - } - - ExtBuilder::default().existential_deposit(200).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); - - assert_eq!(current_stack(), vec![]); - - let addr_caller = deploy(wasm_caller); - let addr_callee = deploy(wasm_callee); - - assert_eq!( - current_stack(), - vec![ - constructor_frame(&addr_caller, false), - constructor_frame(&addr_caller, true), - constructor_frame(&addr_callee, false), - constructor_frame(&addr_callee, true), - ] - ); - - let main_args = (100u32, &addr_callee.clone()).encode(); - let inner_args = (100u32).encode(); - - assert_ok!(Contracts::call( - RuntimeOrigin::signed(ALICE), - addr_caller, - 0, - GAS_LIMIT, - deposit_limit::<Test>(), - main_args.clone() - )); - - let stack_top = current_stack()[4..].to_vec(); - assert_eq!( - stack_top, - vec![ - call_frame(&addr_caller, main_args.clone(), false), - call_frame(&addr_callee, inner_args.clone(), false), - call_frame(&addr_callee, inner_args, true), - call_frame(&addr_caller, main_args, true), - ] - ); - }); -} - -#[test] -fn call_interception_works() { - let (wasm, _) = compile_module("dummy").unwrap(); - - ExtBuilder::default().existential_deposit(200).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); - - let account_id = Contracts::bare_instantiate( - RuntimeOrigin::signed(ALICE), - 0, - GAS_LIMIT, - deposit_limit::<Test>().into(), - Code::Upload(wasm), - vec![], - // some salt to ensure that the address of this contract is unique among all tests - Some([0x41; 32]), - DebugInfo::Skip, - CollectEvents::Skip, - ) - .result - .unwrap() - .addr; - - // no interception yet - assert_ok!(Contracts::call( - RuntimeOrigin::signed(ALICE), - account_id, - 0, - GAS_LIMIT, - deposit_limit::<Test>(), - vec![], - )); - - // intercept calls to this contract - INTERCEPTED_ADDRESS.with(|i| *i.borrow_mut() = Some(account_id)); - - assert_err_ignore_postinfo!( - Contracts::call( - RuntimeOrigin::signed(ALICE), - account_id, - 0, - GAS_LIMIT, - deposit_limit::<Test>(), - vec![], - ), - <Error<Test>>::ContractReverted, - ); - }); -} diff --git a/substrate/frame/revive/src/tracing.rs b/substrate/frame/revive/src/tracing.rs new file mode 100644 index 0000000000000000000000000000000000000000..e9c05f8cb5058ee74b4fbb29cb9fe5aea30faac2 --- /dev/null +++ b/substrate/frame/revive/src/tracing.rs @@ -0,0 +1,64 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{primitives::ExecReturnValue, DispatchError, Weight}; +use environmental::environmental; +use sp_core::{H160, H256, U256}; + +environmental!(tracer: dyn Tracer + 'static); + +/// Trace the execution of the given closure. +/// +/// # Warning +/// +/// Only meant to be called from off-chain code as its additional resource usage is +/// not accounted for in the weights or memory envelope. +pub fn trace<R, F: FnOnce() -> R>(tracer: &mut (dyn Tracer + 'static), f: F) -> R { + tracer::using_once(tracer, f) +} + +/// Run the closure when tracing is enabled. +/// +/// This is safe to be called from on-chain code as tracing will never be activated +/// there. Hence the closure is not executed in this case. +pub(crate) fn if_tracing<F: FnOnce(&mut (dyn Tracer + 'static))>(f: F) { + tracer::with(f); +} + +/// Defines methods to trace contract interactions. +pub trait Tracer { + /// Called before a contract call is executed + fn enter_child_span( + &mut self, + from: H160, + to: H160, + is_delegate_call: bool, + is_read_only: bool, + value: U256, + input: &[u8], + gas: Weight, + ); + + /// Record a log event + fn log_event(&mut self, event: H160, topics: &[H256], data: &[u8]); + + /// Called after a contract call is executed + fn exit_child_span(&mut self, output: &ExecReturnValue, gas_left: Weight); + + /// Called when a contract call terminates with an error + fn exit_child_span_with_error(&mut self, error: DispatchError, gas_left: Weight); +} diff --git a/substrate/frame/revive/src/wasm/mod.rs b/substrate/frame/revive/src/wasm/mod.rs index 3bd4bde5679f24ba89db49032f44909b851071c2..527cf16309540c3edacf626bb7e222f82677f3d9 100644 --- a/substrate/frame/revive/src/wasm/mod.rs +++ b/substrate/frame/revive/src/wasm/mod.rs @@ -29,14 +29,13 @@ pub use crate::wasm::runtime::{ReturnData, TrapReason}; pub use crate::wasm::runtime::{Memory, Runtime, RuntimeCosts}; use crate::{ - address::AddressMapper, exec::{ExecResult, Executable, ExportedFunction, Ext}, gas::{GasMeter, Token}, limits, storage::meter::Diff, weights::WeightInfo, - AccountIdOf, BadOrigin, BalanceOf, CodeInfoOf, CodeVec, Config, Error, Event, ExecError, - HoldReason, Pallet, PristineCode, Weight, LOG_TARGET, + AccountIdOf, BadOrigin, BalanceOf, CodeInfoOf, CodeVec, Config, Error, ExecError, HoldReason, + PristineCode, Weight, LOG_TARGET, }; use alloc::vec::Vec; use codec::{Decode, Encode, MaxEncodedLen}; @@ -157,16 +156,9 @@ where code_info.deposit, BestEffort, ); - let deposit_released = code_info.deposit; - let remover = T::AddressMapper::to_address(&code_info.owner); *existing = None; <PristineCode<T>>::remove(&code_hash); - <Pallet<T>>::deposit_event(Event::CodeRemoved { - code_hash, - deposit_released, - remover, - }); Ok(()) } else { Err(<Error<T>>::CodeNotFound.into()) @@ -202,12 +194,6 @@ where self.code_info.refcount = 0; <PristineCode<T>>::insert(code_hash, &self.code); *stored_code_info = Some(self.code_info.clone()); - let uploader = T::AddressMapper::to_address(&self.code_info.owner); - <Pallet<T>>::deposit_event(Event::CodeStored { - code_hash, - deposit_held: deposit, - uploader, - }); Ok(deposit) }, } @@ -288,7 +274,7 @@ impl<T: Config> WasmBlob<T> { } let engine = polkavm::Engine::new(&config).expect( "on-chain (no_std) use of interpreter is hard coded. - interpreter is available on all plattforms; qed", + interpreter is available on all platforms; qed", ); let mut module_config = polkavm::ModuleConfig::new(); diff --git a/substrate/frame/revive/src/wasm/runtime.rs b/substrate/frame/revive/src/wasm/runtime.rs index 8529c7d9e73bbd878d91ba4fc4e483adda135504..4fbcfe1b47f5b1866e745ab11e599de6e0ce5f99 100644 --- a/substrate/frame/revive/src/wasm/runtime.rs +++ b/substrate/frame/revive/src/wasm/runtime.rs @@ -293,6 +293,8 @@ pub enum RuntimeCosts { CallDataSize, /// Weight of calling `seal_return_data_size`. ReturnDataSize, + /// Weight of calling `seal_to_account_id`. + ToAccountId, /// Weight of calling `seal_origin`. Origin, /// Weight of calling `seal_is_contract`. @@ -339,8 +341,6 @@ pub enum RuntimeCosts { Terminate(u32), /// Weight of calling `seal_deposit_event` with the given number of topics and event size. DepositEvent { num_topic: u32, len: u32 }, - /// Weight of calling `seal_debug_message` per byte of passed message. - DebugMessage(u32), /// Weight of calling `seal_set_storage` for the given storage item sizes. SetStorage { old_bytes: u32, new_bytes: u32 }, /// Weight of calling `seal_clear_storage` per cleared byte. @@ -468,6 +468,7 @@ impl<T: Config> Token<T> for RuntimeCosts { Caller => T::WeightInfo::seal_caller(), Origin => T::WeightInfo::seal_origin(), IsContract => T::WeightInfo::seal_is_contract(), + ToAccountId => T::WeightInfo::seal_to_account_id(), CodeHash => T::WeightInfo::seal_code_hash(), CodeSize => T::WeightInfo::seal_code_size(), OwnCodeHash => T::WeightInfo::seal_own_code_hash(), @@ -489,7 +490,6 @@ impl<T: Config> Token<T> for RuntimeCosts { WeightToFee => T::WeightInfo::seal_weight_to_fee(), Terminate(locked_dependencies) => T::WeightInfo::seal_terminate(locked_dependencies), DepositEvent { num_topic, len } => T::WeightInfo::seal_deposit_event(num_topic, len), - DebugMessage(len) => T::WeightInfo::seal_debug_message(len), SetStorage { new_bytes, old_bytes } => { cost_storage!(write, seal_set_storage, new_bytes, old_bytes) }, @@ -669,10 +669,7 @@ impl<'a, E: Ext, M: ?Sized + Memory<E::T>> Runtime<'a, E, M> { match result { Ok(_) => Ok(ReturnErrorCode::Success), Err(e) => { - if self.ext.debug_buffer_enabled() { - self.ext.append_debug_buffer("call failed with: "); - self.ext.append_debug_buffer(e.into()); - }; + log::debug!(target: LOG_TARGET, "call failed with: {e:?}"); Ok(ErrorReturnCode::get()) }, } @@ -1832,27 +1829,6 @@ pub mod env { self.contains_storage(memory, flags, key_ptr, key_len) } - /// Emit a custom debug message. - /// See [`pallet_revive_uapi::HostFn::debug_message`]. - fn debug_message( - &mut self, - memory: &mut M, - str_ptr: u32, - str_len: u32, - ) -> Result<ReturnErrorCode, TrapReason> { - let str_len = str_len.min(limits::DEBUG_BUFFER_BYTES); - self.charge_gas(RuntimeCosts::DebugMessage(str_len))?; - if self.ext.append_debug_buffer("") { - let data = memory.read(str_ptr, str_len)?; - if let Some(msg) = core::str::from_utf8(&data).ok() { - self.ext.append_debug_buffer(msg); - } - Ok(ReturnErrorCode::Success) - } else { - Ok(ReturnErrorCode::LoggingDisabled) - } - } - /// Recovers the ECDSA public key from the given message hash and signature. /// See [`pallet_revive_uapi::HostFn::ecdsa_recover`]. fn ecdsa_recover( @@ -2162,12 +2138,30 @@ pub mod env { Ok(ReturnErrorCode::Success) }, Err(e) => { - if self.ext.append_debug_buffer("") { - self.ext.append_debug_buffer("seal0::xcm_send failed with: "); - self.ext.append_debug_buffer(e.into()); - }; + log::debug!(target: LOG_TARGET, "seal0::xcm_send failed with: {e:?}"); Ok(ReturnErrorCode::XcmSendFailed) }, } } + + /// Retrieves the account id for a specified contract address. + /// + /// See [`pallet_revive_uapi::HostFn::to_account_id`]. + fn to_account_id( + &mut self, + memory: &mut M, + addr_ptr: u32, + out_ptr: u32, + ) -> Result<(), TrapReason> { + self.charge_gas(RuntimeCosts::ToAccountId)?; + let address = memory.read_h160(addr_ptr)?; + let account_id = self.ext.to_account_id(&address); + Ok(self.write_fixed_sandbox_output( + memory, + out_ptr, + &account_id.encode(), + false, + already_charged, + )?) + } } diff --git a/substrate/frame/revive/src/weights.rs b/substrate/frame/revive/src/weights.rs index e35ba5ca0766458427ed0f73927eea6bb4f1cc66..52153d74ca758c429a4982e393b7d211d79ab5b4 100644 --- a/substrate/frame/revive/src/weights.rs +++ b/substrate/frame/revive/src/weights.rs @@ -67,6 +67,7 @@ pub trait WeightInfo { fn seal_caller() -> Weight; fn seal_origin() -> Weight; fn seal_is_contract() -> Weight; + fn seal_to_account_id() -> Weight; fn seal_code_hash() -> Weight; fn seal_own_code_hash() -> Weight; fn seal_code_size() -> Weight; @@ -96,7 +97,6 @@ pub trait WeightInfo { fn seal_return(n: u32, ) -> Weight; fn seal_terminate(n: u32, ) -> Weight; fn seal_deposit_event(t: u32, n: u32, ) -> Weight; - fn seal_debug_message(i: u32, ) -> Weight; fn get_storage_empty() -> Weight; fn get_storage_full() -> Weight; fn set_storage_empty() -> Weight; @@ -378,6 +378,16 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { Weight::from_parts(10_336_000, 3771) .saturating_add(T::DbWeight::get().reads(1_u64)) } + /// Storage: `Revive::AddressSuffix` (r:1 w:0) + /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) + fn seal_to_account_id() -> Weight { + // Proof Size summary in bytes: + // Measured: `212` + // Estimated: `3677` + // Minimum execution time: 4_000_000 picoseconds. + Weight::from_parts(4_000_000, 3677) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } /// Storage: `Revive::ContractInfoOf` (r:1 w:0) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) fn seal_code_hash() -> Weight { @@ -643,16 +653,6 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { // Standard Error: 34 .saturating_add(Weight::from_parts(774, 0).saturating_mul(n.into())) } - /// The range of component `i` is `[0, 262144]`. - fn seal_debug_message(i: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 340_000 picoseconds. - Weight::from_parts(306_527, 0) - // Standard Error: 1 - .saturating_add(Weight::from_parts(728, 0).saturating_mul(i.into())) - } /// Storage: `Skipped::Metadata` (r:0 w:0) /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) fn get_storage_empty() -> Weight { @@ -1274,6 +1274,16 @@ impl WeightInfo for () { Weight::from_parts(10_336_000, 3771) .saturating_add(RocksDbWeight::get().reads(1_u64)) } + /// Storage: `Revive::AddressSuffix` (r:1 w:0) + /// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`) + fn seal_to_account_id() -> Weight { + // Proof Size summary in bytes: + // Measured: `212` + // Estimated: `3677` + // Minimum execution time: 4_000_000 picoseconds. + Weight::from_parts(4_000_000, 3677) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } /// Storage: `Revive::ContractInfoOf` (r:1 w:0) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) fn seal_code_hash() -> Weight { @@ -1539,16 +1549,6 @@ impl WeightInfo for () { // Standard Error: 34 .saturating_add(Weight::from_parts(774, 0).saturating_mul(n.into())) } - /// The range of component `i` is `[0, 262144]`. - fn seal_debug_message(i: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 340_000 picoseconds. - Weight::from_parts(306_527, 0) - // Standard Error: 1 - .saturating_add(Weight::from_parts(728, 0).saturating_mul(i.into())) - } /// Storage: `Skipped::Metadata` (r:0 w:0) /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) fn get_storage_empty() -> Weight { diff --git a/substrate/frame/revive/uapi/Cargo.toml b/substrate/frame/revive/uapi/Cargo.toml index 7241d667fcdc75c4dbd6e2c5f3a6a02efd1835aa..cf006941cfd0a6477d35b9b0d992060105f6f3c1 100644 --- a/substrate/frame/revive/uapi/Cargo.toml +++ b/substrate/frame/revive/uapi/Cargo.toml @@ -22,7 +22,7 @@ paste = { workspace = true } scale-info = { features = ["derive"], optional = true, workspace = true } [target.'cfg(target_arch = "riscv64")'.dependencies] -polkavm-derive = { version = "0.18.0" } +polkavm-derive = { version = "0.19.0" } [package.metadata.docs.rs] features = ["unstable-hostfn"] diff --git a/substrate/frame/revive/uapi/src/host.rs b/substrate/frame/revive/uapi/src/host.rs index ba0a63b15c379d45a4add50cfc6e0ec6beb14e2b..3e5cf0eb0c243d44c19368bd9cf7305fb418b829 100644 --- a/substrate/frame/revive/uapi/src/host.rs +++ b/substrate/frame/revive/uapi/src/host.rs @@ -144,6 +144,18 @@ pub trait HostFn: private::Sealed { /// - `output`: A reference to the output data buffer to write the origin's address. fn origin(output: &mut [u8; 20]); + /// Retrieve the account id for a specified address. + /// + /// # Parameters + /// + /// - `addr`: A `H160` address. + /// - `output`: A reference to the output data buffer to write the account id. + /// + /// # Note + /// + /// If no mapping exists for `addr`, the fallback account id will be returned. + fn to_account_id(addr: &[u8; 20], output: &mut [u8]); + /// Retrieve the code hash for a specified contract address. /// /// # Parameters @@ -515,26 +527,6 @@ pub trait HostFn: private::Sealed { #[unstable_hostfn] fn contains_storage(flags: StorageFlags, key: &[u8]) -> Option<u32>; - /// Emit a custom debug message. - /// - /// No newlines are added to the supplied message. - /// Specifying invalid UTF-8 just drops the message with no trap. - /// - /// This is a no-op if debug message recording is disabled which is always the case - /// when the code is executing on-chain. The message is interpreted as UTF-8 and - /// appended to the debug buffer which is then supplied to the calling RPC client. - /// - /// # Note - /// - /// Even though no action is taken when debug message recording is disabled there is still - /// a non trivial overhead (and weight cost) associated with calling this function. Contract - /// languages should remove calls to this function (either at runtime or compile time) when - /// not being executed as an RPC. For example, they could allow users to disable logging - /// through compile time flags (cargo features) for on-chain deployment. Additionally, the - /// return value of this function can be cached in order to prevent further calls at runtime. - #[unstable_hostfn] - fn debug_message(str: &[u8]) -> Result; - /// Recovers the ECDSA public key from the given message hash and signature. /// /// Writes the public key into the given output buffer. diff --git a/substrate/frame/revive/uapi/src/host/riscv64.rs b/substrate/frame/revive/uapi/src/host/riscv64.rs index 8c40bc9f48ea802b6d46bc0d1aaa858a991ec136..3726564e26ebaea2982912e63ad08924774790ed 100644 --- a/substrate/frame/revive/uapi/src/host/riscv64.rs +++ b/substrate/frame/revive/uapi/src/host/riscv64.rs @@ -69,6 +69,7 @@ mod sys { pub fn caller(out_ptr: *mut u8); pub fn origin(out_ptr: *mut u8); pub fn is_contract(account_ptr: *const u8) -> ReturnCode; + pub fn to_account_id(address_ptr: *const u8, out_ptr: *mut u8); pub fn code_hash(address_ptr: *const u8, out_ptr: *mut u8); pub fn code_size(address_ptr: *const u8) -> u64; pub fn own_code_hash(out_ptr: *mut u8); @@ -109,7 +110,6 @@ mod sys { out_ptr: *mut u8, out_len_ptr: *mut u32, ) -> ReturnCode; - pub fn debug_message(str_ptr: *const u8, str_len: u32) -> ReturnCode; pub fn call_runtime(call_ptr: *const u8, call_len: u32) -> ReturnCode; pub fn ecdsa_recover( signature_ptr: *const u8, @@ -457,6 +457,11 @@ impl HostFn for HostFnImpl { unsafe { sys::ref_time_left() } } + #[unstable_hostfn] + fn to_account_id(address: &[u8; 20], output: &mut [u8]) { + unsafe { sys::to_account_id(address.as_ptr(), output.as_mut_ptr()) } + } + #[unstable_hostfn] fn block_hash(block_number_ptr: &[u8; 32], output: &mut [u8; 32]) { unsafe { sys::block_hash(block_number_ptr.as_ptr(), output.as_mut_ptr()) }; @@ -519,12 +524,6 @@ impl HostFn for HostFnImpl { ret_code.into() } - #[unstable_hostfn] - fn debug_message(str: &[u8]) -> Result { - let ret_code = unsafe { sys::debug_message(str.as_ptr(), str.len() as u32) }; - ret_code.into() - } - #[unstable_hostfn] fn ecdsa_recover( signature: &[u8; 65], diff --git a/substrate/frame/revive/uapi/src/lib.rs b/substrate/frame/revive/uapi/src/lib.rs index ef1798b4bf615a7e50054954546c963094746042..867f356339876a433f01248780c9cdab6c8ebb7e 100644 --- a/substrate/frame/revive/uapi/src/lib.rs +++ b/substrate/frame/revive/uapi/src/lib.rs @@ -86,9 +86,8 @@ define_error_codes! { /// Transfer failed for other not further specified reason. Most probably /// reserved or locked balance of the sender that was preventing the transfer. TransferFailed = 4, - /// The call to `debug_message` had no effect because debug message - /// recording was disabled. - LoggingDisabled = 5, + /// The subcall ran out of weight or storage deposit. + OutOfResources = 5, /// The call dispatched by `call_runtime` was executed but returned an error. CallRuntimeFailed = 6, /// ECDSA public key recovery failed. Most probably wrong recovery id or signature. @@ -99,8 +98,6 @@ define_error_codes! { XcmExecutionFailed = 9, /// The `xcm_send` call failed. XcmSendFailed = 10, - /// The subcall ran out of weight or storage deposit. - OutOfResources = 11, } /// The raw return code returned by the host side. diff --git a/substrate/frame/root-offences/src/mock.rs b/substrate/frame/root-offences/src/mock.rs index 7a96b8eade4e13f290020ffc0f0a12fa5f72600d..3f14dc00b56068f8be244d24e4cb011748c0aa7f 100644 --- a/substrate/frame/root-offences/src/mock.rs +++ b/substrate/frame/root-offences/src/mock.rs @@ -126,6 +126,7 @@ parameter_types! { #[derive_impl(pallet_staking::config_preludes::TestDefaultConfig)] impl pallet_staking::Config for Test { + type OldCurrency = Balances; type Currency = Balances; type CurrencyBalance = <Self as pallet_balances::Config>::Balance; type UnixTime = Timestamp; @@ -206,10 +207,10 @@ impl ExtBuilder { (30, self.balance_factor * 50), (40, self.balance_factor * 50), // stashes - (11, self.balance_factor * 1000), - (21, self.balance_factor * 1000), - (31, self.balance_factor * 500), - (41, self.balance_factor * 1000), + (11, self.balance_factor * 1500), + (21, self.balance_factor * 1500), + (31, self.balance_factor * 1000), + (41, self.balance_factor * 2000), ], } .assimilate_storage(&mut storage) diff --git a/substrate/frame/salary/Cargo.toml b/substrate/frame/salary/Cargo.toml index 626993a0547b58bee5a125edcec1ffce9ed5b779..84c55b110c8c2b3ebc1b948791311e551094dadf 100644 --- a/substrate/frame/salary/Cargo.toml +++ b/substrate/frame/salary/Cargo.toml @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { features = ["derive"], workspace = true } -frame = { workspace = true, features = ["experimental", "runtime"] } +frame = { workspace = true, features = ["runtime"] } log = { workspace = true } pallet-ranked-collective = { optional = true, workspace = true } scale-info = { features = ["derive"], workspace = true } diff --git a/substrate/frame/session/benchmarking/src/mock.rs b/substrate/frame/session/benchmarking/src/mock.rs index 346cd04c0fa9eb5f917557aa050e759762f9a341..74201da3d2f310f58593d8566eb650086245c76f 100644 --- a/substrate/frame/session/benchmarking/src/mock.rs +++ b/substrate/frame/session/benchmarking/src/mock.rs @@ -133,6 +133,7 @@ impl onchain::Config for OnChainSeqPhragmen { #[derive_impl(pallet_staking::config_preludes::TestDefaultConfig)] impl pallet_staking::Config for Test { + type OldCurrency = Balances; type Currency = Balances; type CurrencyBalance = <Self as pallet_balances::Config>::Balance; type UnixTime = pallet_timestamp::Pallet<Self>; diff --git a/substrate/frame/src/lib.rs b/substrate/frame/src/lib.rs index f23d9cbffa9170b7a4ab3475f28c0a709740e8e2..00605cfe565c244d041d703bf9f5bb80272a5443 100644 --- a/substrate/frame/src/lib.rs +++ b/substrate/frame/src/lib.rs @@ -106,7 +106,7 @@ //! [dependencies] //! codec = { features = ["max-encoded-len"], workspace = true } //! scale-info = { features = ["derive"], workspace = true } -//! frame = { workspace = true, features = ["experimental", "runtime"] } +//! frame = { workspace = true, features = ["runtime"] } //! //! [features] //! default = ["std"] @@ -150,7 +150,6 @@ //! * `runtime::apis` should expose all common runtime APIs that all FRAME-based runtimes need. #![cfg_attr(not(feature = "std"), no_std)] -#![cfg(feature = "experimental")] #[doc(no_inline)] pub use frame_support::pallet; diff --git a/substrate/frame/staking/Cargo.toml b/substrate/frame/staking/Cargo.toml index 22176b6d720b201583905f59780c19aa7c33d2f4..74b1c78e9cbee9661978ee2111ec9d3410a7f56c 100644 --- a/substrate/frame/staking/Cargo.toml +++ b/substrate/frame/staking/Cargo.toml @@ -41,6 +41,7 @@ rand_chacha = { optional = true, workspace = true } [dev-dependencies] frame-benchmarking = { workspace = true, default-features = true } frame-election-provider-support = { workspace = true, default-features = true } +frame-support = { features = ["experimental"], workspace = true, default-features = true } pallet-bags-list = { workspace = true, default-features = true } pallet-balances = { workspace = true, default-features = true } pallet-staking-reward-curve = { workspace = true, default-features = true } diff --git a/substrate/frame/staking/src/asset.rs b/substrate/frame/staking/src/asset.rs index 23368b1f8fca713f78405165c00ec09d54a8d257..a1140d317c204957e5f7cc9178f704cffe5965c1 100644 --- a/substrate/frame/staking/src/asset.rs +++ b/substrate/frame/staking/src/asset.rs @@ -18,9 +18,15 @@ //! Contains all the interactions with [`Config::Currency`] to manipulate the underlying staking //! asset. -use frame_support::traits::{Currency, InspectLockableCurrency, LockableCurrency}; - -use crate::{BalanceOf, Config, NegativeImbalanceOf, PositiveImbalanceOf}; +use crate::{BalanceOf, Config, HoldReason, NegativeImbalanceOf, PositiveImbalanceOf}; +use frame_support::traits::{ + fungible::{ + hold::{Balanced as FunHoldBalanced, Inspect as FunHoldInspect, Mutate as FunHoldMutate}, + Balanced, Inspect as FunInspect, + }, + tokens::{Fortitude, Precision, Preservation}, +}; +use sp_runtime::{DispatchResult, Saturating}; /// Existential deposit for the chain. pub fn existential_deposit<T: Config>() -> BalanceOf<T> { @@ -32,7 +38,7 @@ pub fn total_issuance<T: Config>() -> BalanceOf<T> { T::Currency::total_issuance() } -/// Total balance of `who`. Includes both, free and reserved. +/// Total balance of `who`. Includes both free and staked. pub fn total_balance<T: Config>(who: &T::AccountId) -> BalanceOf<T> { T::Currency::total_balance(who) } @@ -41,42 +47,65 @@ pub fn total_balance<T: Config>(who: &T::AccountId) -> BalanceOf<T> { /// /// This includes balance free to stake along with any balance that is already staked. pub fn stakeable_balance<T: Config>(who: &T::AccountId) -> BalanceOf<T> { - T::Currency::free_balance(who) + free_to_stake::<T>(who).saturating_add(staked::<T>(who)) } /// Balance of `who` that is currently at stake. /// -/// The staked amount is locked and cannot be transferred out of `who`s account. +/// The staked amount is on hold and cannot be transferred out of `who`s account. pub fn staked<T: Config>(who: &T::AccountId) -> BalanceOf<T> { - T::Currency::balance_locked(crate::STAKING_ID, who) + T::Currency::balance_on_hold(&HoldReason::Staking.into(), who) +} + +/// Balance of who that can be staked additionally. +/// +/// Does not include the current stake. +pub fn free_to_stake<T: Config>(who: &T::AccountId) -> BalanceOf<T> { + // since we want to be able to use frozen funds for staking, we force the reduction. + T::Currency::reducible_balance(who, Preservation::Preserve, Fortitude::Force) } /// Set balance that can be staked for `who`. /// -/// This includes any balance that is already staked. +/// If `Value` is lower than the current staked balance, the difference is unlocked. +/// +/// Should only be used with test. #[cfg(any(test, feature = "runtime-benchmarks"))] pub fn set_stakeable_balance<T: Config>(who: &T::AccountId, value: BalanceOf<T>) { - T::Currency::make_free_balance_be(who, value); + use frame_support::traits::fungible::Mutate; + + // minimum free balance (non-staked) required to keep the account alive. + let ed = existential_deposit::<T>(); + // currently on stake + let staked_balance = staked::<T>(who); + + // if new value is greater than staked balance, mint some free balance. + if value > staked_balance { + let _ = T::Currency::set_balance(who, value - staked_balance + ed); + } else { + // else reduce the staked balance. + update_stake::<T>(who, value).expect("can remove from what is staked"); + // burn all free, only leaving ED. + let _ = T::Currency::set_balance(who, ed); + } + + // ensure new stakeable balance same as desired `value`. + assert_eq!(stakeable_balance::<T>(who), value); } /// Update `amount` at stake for `who`. /// /// Overwrites the existing stake amount. If passed amount is lower than the existing stake, the /// difference is unlocked. -pub fn update_stake<T: Config>(who: &T::AccountId, amount: BalanceOf<T>) { - T::Currency::set_lock( - crate::STAKING_ID, - who, - amount, - frame_support::traits::WithdrawReasons::all(), - ); +pub fn update_stake<T: Config>(who: &T::AccountId, amount: BalanceOf<T>) -> DispatchResult { + T::Currency::set_on_hold(&HoldReason::Staking.into(), who, amount) } -/// Kill the stake of `who`. +/// Release all staked amount to `who`. /// -/// All locked amount is unlocked. -pub fn kill_stake<T: Config>(who: &T::AccountId) { - T::Currency::remove_lock(crate::STAKING_ID, who); +/// Fails if there are consumers left on `who` that restricts it from being reaped. +pub fn kill_stake<T: Config>(who: &T::AccountId) -> DispatchResult { + T::Currency::release_all(&HoldReason::Staking.into(), who, Precision::BestEffort).map(|_| ()) } /// Slash the value from `who`. @@ -86,29 +115,32 @@ pub fn slash<T: Config>( who: &T::AccountId, value: BalanceOf<T>, ) -> (NegativeImbalanceOf<T>, BalanceOf<T>) { - T::Currency::slash(who, value) + T::Currency::slash(&HoldReason::Staking.into(), who, value) } /// Mint `value` into an existing account `who`. /// /// This does not increase the total issuance. -pub fn mint_existing<T: Config>( +pub fn mint_into_existing<T: Config>( who: &T::AccountId, value: BalanceOf<T>, ) -> Option<PositiveImbalanceOf<T>> { - T::Currency::deposit_into_existing(who, value).ok() + // since the account already exists, we mint exact value even if value is below ED. + T::Currency::deposit(who, value, Precision::Exact).ok() } -/// Mint reward and create account for `who` if it does not exist. +/// Mint `value` and create account for `who` if it does not exist. /// -/// This does not increase the total issuance. +/// If value is below existential deposit, the account is not created. +/// +/// Note: This does not increase the total issuance. pub fn mint_creating<T: Config>(who: &T::AccountId, value: BalanceOf<T>) -> PositiveImbalanceOf<T> { - T::Currency::deposit_creating(who, value) + T::Currency::deposit(who, value, Precision::BestEffort).unwrap_or_default() } /// Deposit newly issued or slashed `value` into `who`. pub fn deposit_slashed<T: Config>(who: &T::AccountId, value: NegativeImbalanceOf<T>) { - T::Currency::resolve_creating(who, value) + let _ = T::Currency::resolve(who, value); } /// Issue `value` increasing total issuance. @@ -121,5 +153,5 @@ pub fn issue<T: Config>(value: BalanceOf<T>) -> NegativeImbalanceOf<T> { /// Burn the amount from the total issuance. #[cfg(feature = "runtime-benchmarks")] pub fn burn<T: Config>(amount: BalanceOf<T>) -> PositiveImbalanceOf<T> { - T::Currency::burn(amount) + T::Currency::rescind(amount) } diff --git a/substrate/frame/staking/src/benchmarking.rs b/substrate/frame/staking/src/benchmarking.rs index 79d8dd3fbc30365e4a81a2f708383754f67a49cc..59d272168d68bc06f73e4e85ce7fc5a407ca3daf 100644 --- a/substrate/frame/staking/src/benchmarking.rs +++ b/substrate/frame/staking/src/benchmarking.rs @@ -257,7 +257,11 @@ mod benchmarks { .map(|l| l.active) .ok_or("ledger not created after")?; - let _ = asset::mint_existing::<T>(&stash, max_additional).unwrap(); + let _ = asset::mint_into_existing::<T>( + &stash, + max_additional + asset::existential_deposit::<T>(), + ) + .unwrap(); whitelist_account!(stash); @@ -1133,6 +1137,23 @@ mod benchmarks { Ok(()) } + #[benchmark] + fn migrate_currency() -> Result<(), BenchmarkError> { + let (stash, _ctrl) = + create_stash_controller::<T>(USER_SEED, 100, RewardDestination::Staked)?; + let stake = asset::staked::<T>(&stash); + migrate_to_old_currency::<T>(stash.clone()); + // no holds + assert!(asset::staked::<T>(&stash).is_zero()); + whitelist_account!(stash); + + #[extrinsic_call] + _(RawOrigin::Signed(stash.clone()), stash.clone()); + + assert_eq!(asset::staked::<T>(&stash), stake); + Ok(()) + } + impl_benchmark_test_suite!( Staking, crate::mock::ExtBuilder::default().has_stakers(true), diff --git a/substrate/frame/staking/src/ledger.rs b/substrate/frame/staking/src/ledger.rs index ac3be04cf607176d6aee437a7b49bc3f4afffd55..1d66ebd27e9f729e8a42a93960435ccf78b79769 100644 --- a/substrate/frame/staking/src/ledger.rs +++ b/substrate/frame/staking/src/ledger.rs @@ -32,6 +32,7 @@ //! state consistency. use frame_support::{defensive, ensure, traits::Defensive}; +use sp_runtime::DispatchResult; use sp_staking::{StakingAccount, StakingInterface}; use crate::{ @@ -187,7 +188,8 @@ impl<T: Config> StakingLedger<T> { // We skip locking virtual stakers. if !Pallet::<T>::is_virtual_staker(&self.stash) { // for direct stakers, update lock on stash based on ledger. - asset::update_stake::<T>(&self.stash, self.total); + asset::update_stake::<T>(&self.stash, self.total) + .map_err(|_| Error::<T>::NotEnoughFunds)?; } Ledger::<T>::insert( @@ -250,7 +252,7 @@ impl<T: Config> StakingLedger<T> { /// Clears all data related to a staking ledger and its bond in both [`Ledger`] and [`Bonded`] /// storage items and updates the stash staking lock. - pub(crate) fn kill(stash: &T::AccountId) -> Result<(), Error<T>> { + pub(crate) fn kill(stash: &T::AccountId) -> DispatchResult { let controller = <Bonded<T>>::get(stash).ok_or(Error::<T>::NotStash)?; <Ledger<T>>::get(&controller).ok_or(Error::<T>::NotController).map(|ledger| { @@ -259,9 +261,9 @@ impl<T: Config> StakingLedger<T> { <Payee<T>>::remove(&stash); // kill virtual staker if it exists. - if <VirtualStakers<T>>::take(&stash).is_none() { + if <VirtualStakers<T>>::take(&ledger.stash).is_none() { // if not virtual staker, clear locks. - asset::kill_stake::<T>(&ledger.stash); + asset::kill_stake::<T>(&ledger.stash)?; } Ok(()) diff --git a/substrate/frame/staking/src/lib.rs b/substrate/frame/staking/src/lib.rs index 6361663b2b1c05939ba24f572c171c00af2f77e9..42230cb27b756306446cf23cea536a054b11e119 100644 --- a/substrate/frame/staking/src/lib.rs +++ b/substrate/frame/staking/src/lib.rs @@ -312,7 +312,8 @@ use codec::{Decode, Encode, HasCompact, MaxEncodedLen}; use frame_support::{ defensive, defensive_assert, traits::{ - ConstU32, Currency, Defensive, DefensiveMax, DefensiveSaturating, Get, LockIdentifier, + tokens::fungible::{Credit, Debt}, + ConstU32, Defensive, DefensiveMax, DefensiveSaturating, Get, LockIdentifier, }, weights::Weight, BoundedVec, CloneNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound, @@ -361,12 +362,9 @@ pub type RewardPoint = u32; /// The balance type of this pallet. pub type BalanceOf<T> = <T as Config>::CurrencyBalance; -type PositiveImbalanceOf<T> = <<T as Config>::Currency as Currency< - <T as frame_system::Config>::AccountId, ->>::PositiveImbalance; -pub type NegativeImbalanceOf<T> = <<T as Config>::Currency as Currency< - <T as frame_system::Config>::AccountId, ->>::NegativeImbalance; +type PositiveImbalanceOf<T> = Debt<<T as frame_system::Config>::AccountId, <T as Config>::Currency>; +pub type NegativeImbalanceOf<T> = + Credit<<T as frame_system::Config>::AccountId, <T as Config>::Currency>; type AccountIdLookupOf<T> = <<T as frame_system::Config>::Lookup as StaticLookup>::Source; diff --git a/substrate/frame/staking/src/mock.rs b/substrate/frame/staking/src/mock.rs index 769b84826b41613f27873fed650f47999806441f..6346949576fa796726b136822737d6bd5c9fb3e4 100644 --- a/substrate/frame/staking/src/mock.rs +++ b/substrate/frame/staking/src/mock.rs @@ -25,8 +25,7 @@ use frame_election_provider_support::{ use frame_support::{ assert_ok, derive_impl, ord_parameter_types, parameter_types, traits::{ - ConstU64, Currency, EitherOfDiverse, FindAuthor, Get, Imbalance, LockableCurrency, - OnUnbalanced, OneSessionHandler, WithdrawReasons, + ConstU64, EitherOfDiverse, FindAuthor, Get, Imbalance, OnUnbalanced, OneSessionHandler, }, weights::constants::RocksDbWeight, }; @@ -264,6 +263,7 @@ pub(crate) const DISABLING_LIMIT_FACTOR: usize = 3; #[derive_impl(crate::config_preludes::TestDefaultConfig)] impl crate::pallet::pallet::Config for Test { + type OldCurrency = Balances; type Currency = Balances; type UnixTime = Timestamp; type RewardRemainder = RewardRemainderMock; @@ -432,6 +432,7 @@ impl ExtBuilder { fn build(self) -> sp_io::TestExternalities { sp_tracing::try_init_simple(); let mut storage = frame_system::GenesisConfig::<Test>::default().build_storage().unwrap(); + let ed = ExistentialDeposit::get(); let _ = pallet_balances::GenesisConfig::<Test> { balances: vec![ @@ -446,19 +447,23 @@ impl ExtBuilder { (40, self.balance_factor), (50, self.balance_factor), // stashes - (11, self.balance_factor * 1000), - (21, self.balance_factor * 2000), - (31, self.balance_factor * 2000), - (41, self.balance_factor * 2000), - (51, self.balance_factor * 2000), - (201, self.balance_factor * 2000), - (202, self.balance_factor * 2000), + // Note: Previously this pallet used locks and stakers could stake all their + // balance including ED. Now with holds, stakers are required to maintain + // (non-staked) ED in their accounts. Therefore, we drop an additional existential + // deposit to genesis stakers. + (11, self.balance_factor * 1000 + ed), + (21, self.balance_factor * 2000 + ed), + (31, self.balance_factor * 2000 + ed), + (41, self.balance_factor * 2000 + ed), + (51, self.balance_factor * 2000 + ed), + (201, self.balance_factor * 2000 + ed), + (202, self.balance_factor * 2000 + ed), // optional nominator - (100, self.balance_factor * 2000), - (101, self.balance_factor * 2000), + (100, self.balance_factor * 2000 + ed), + (101, self.balance_factor * 2000 + ed), // aux accounts (60, self.balance_factor), - (61, self.balance_factor * 2000), + (61, self.balance_factor * 2000 + ed), (70, self.balance_factor), (71, self.balance_factor * 2000), (80, self.balance_factor), @@ -575,7 +580,7 @@ pub(crate) fn current_era() -> EraIndex { } pub(crate) fn bond(who: AccountId, val: Balance) { - let _ = Balances::make_free_balance_be(&who, val); + let _ = asset::set_stakeable_balance::<Test>(&who, val); assert_ok!(Staking::bond(RuntimeOrigin::signed(who), val, RewardDestination::Stash)); } @@ -600,10 +605,6 @@ pub(crate) fn bond_virtual_nominator( val: Balance, target: Vec<AccountId>, ) { - // In a real scenario, `who` is a keyless account managed by another pallet which provides for - // it. - System::inc_providers(&who); - // Bond who virtually. assert_ok!(<Staking as sp_staking::StakingUnchecked>::virtual_bond(&who, val, &payee)); assert_ok!(Staking::nominate(RuntimeOrigin::signed(who), target)); @@ -809,7 +810,7 @@ pub(crate) fn bond_extra_no_checks(stash: &AccountId, amount: Balance) { let mut ledger = Ledger::<Test>::get(&controller).expect("ledger must exist to bond_extra"); let new_total = ledger.total + amount; - Balances::set_lock(crate::STAKING_ID, stash, new_total, WithdrawReasons::all()); + let _ = asset::update_stake::<Test>(stash, new_total); ledger.total = new_total; ledger.active = new_total; Ledger::<Test>::insert(controller, ledger); @@ -818,10 +819,10 @@ pub(crate) fn bond_extra_no_checks(stash: &AccountId, amount: Balance) { pub(crate) fn setup_double_bonded_ledgers() { let init_ledgers = Ledger::<Test>::iter().count(); - let _ = Balances::make_free_balance_be(&333, 2000); - let _ = Balances::make_free_balance_be(&444, 2000); - let _ = Balances::make_free_balance_be(&555, 2000); - let _ = Balances::make_free_balance_be(&777, 2000); + let _ = asset::set_stakeable_balance::<Test>(&333, 2000); + let _ = asset::set_stakeable_balance::<Test>(&444, 2000); + let _ = asset::set_stakeable_balance::<Test>(&555, 2000); + let _ = asset::set_stakeable_balance::<Test>(&777, 2000); assert_ok!(Staking::bond(RuntimeOrigin::signed(333), 10, RewardDestination::Staked)); assert_ok!(Staking::bond(RuntimeOrigin::signed(444), 20, RewardDestination::Staked)); @@ -923,5 +924,5 @@ pub(crate) fn staking_events_since_last_call() -> Vec<crate::Event<Test>> { } pub(crate) fn balances(who: &AccountId) -> (Balance, Balance) { - (Balances::free_balance(who), Balances::reserved_balance(who)) + (asset::stakeable_balance::<Test>(who), Balances::reserved_balance(who)) } diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 2ae925d036435976c2f58d49e27f81dad1bfc1d1..8c3ff23315a42d94803f2324cebf4c4930951fe0 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -27,8 +27,8 @@ use frame_support::{ dispatch::WithPostDispatchInfo, pallet_prelude::*, traits::{ - Defensive, DefensiveSaturating, EstimateNextNewSession, Get, Imbalance, Len, OnUnbalanced, - TryCollect, UnixTime, + Defensive, DefensiveSaturating, EstimateNextNewSession, Get, Imbalance, + InspectLockableCurrency, Len, LockableCurrency, OnUnbalanced, TryCollect, UnixTime, }, weights::Weight, }; @@ -36,10 +36,9 @@ use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin}; use pallet_session::historical; use sp_runtime::{ traits::{ - Bounded, CheckedAdd, CheckedSub, Convert, One, SaturatedConversion, Saturating, - StaticLookup, Zero, + Bounded, CheckedAdd, Convert, One, SaturatedConversion, Saturating, StaticLookup, Zero, }, - ArithmeticError, Perbill, Percent, + ArithmeticError, DispatchResult, Perbill, Percent, }; use sp_staking::{ currency_to_vote::CurrencyToVote, @@ -54,6 +53,7 @@ use crate::{ BalanceOf, EraInfo, EraPayout, Exposure, ExposureOf, Forcing, IndividualExposure, LedgerIntegrityState, MaxNominationsOf, MaxWinnersOf, Nominations, NominationsQuota, PositiveImbalanceOf, RewardDestination, SessionInterface, StakingLedger, ValidatorPrefs, + STAKING_ID, }; use alloc::{boxed::Box, vec, vec::Vec}; @@ -96,10 +96,12 @@ impl<T: Config> Pallet<T> { pub(crate) fn inspect_bond_state( stash: &T::AccountId, ) -> Result<LedgerIntegrityState, Error<T>> { - let lock = asset::staked::<T>(&stash); + // look at any old unmigrated lock as well. + let hold_or_lock = asset::staked::<T>(&stash) + .max(T::OldCurrency::balance_locked(STAKING_ID, &stash).into()); let controller = <Bonded<T>>::get(stash).ok_or_else(|| { - if lock == Zero::zero() { + if hold_or_lock == Zero::zero() { Error::<T>::NotStash } else { Error::<T>::BadState @@ -111,7 +113,7 @@ impl<T: Config> Pallet<T> { if ledger.stash != *stash { Ok(LedgerIntegrityState::Corrupted) } else { - if lock != ledger.total { + if hold_or_lock != ledger.total { Ok(LedgerIntegrityState::LockCorrupted) } else { Ok(LedgerIntegrityState::Ok) @@ -163,11 +165,7 @@ impl<T: Config> Pallet<T> { additional } else { // additional amount or actual balance of stash whichever is lower. - additional.min( - asset::stakeable_balance::<T>(stash) - .checked_sub(&ledger.total) - .ok_or(ArithmeticError::Overflow)?, - ) + additional.min(asset::free_to_stake::<T>(stash)) }; ledger.total = ledger.total.checked_add(&extra).ok_or(ArithmeticError::Overflow)?; @@ -416,12 +414,12 @@ impl<T: Config> Pallet<T> { let dest = Self::payee(StakingAccount::Stash(stash.clone()))?; let maybe_imbalance = match dest { - RewardDestination::Stash => asset::mint_existing::<T>(stash, amount), + RewardDestination::Stash => asset::mint_into_existing::<T>(stash, amount), RewardDestination::Staked => Self::ledger(Stash(stash.clone())) .and_then(|mut ledger| { ledger.active += amount; ledger.total += amount; - let r = asset::mint_existing::<T>(stash, amount); + let r = asset::mint_into_existing::<T>(stash, amount); let _ = ledger .update() @@ -799,8 +797,6 @@ impl<T: Config> Pallet<T> { Self::do_remove_validator(&stash); Self::do_remove_nominator(&stash); - frame_system::Pallet::<T>::dec_consumers(&stash); - Ok(()) } @@ -1163,6 +1159,81 @@ impl<T: Config> Pallet<T> { ) -> Exposure<T::AccountId, BalanceOf<T>> { EraInfo::<T>::get_full_exposure(era, account) } + + pub(super) fn do_migrate_currency(stash: &T::AccountId) -> DispatchResult { + if Self::is_virtual_staker(stash) { + return Self::do_migrate_virtual_staker(stash); + } + + let ledger = Self::ledger(Stash(stash.clone()))?; + let staked: BalanceOf<T> = T::OldCurrency::balance_locked(STAKING_ID, stash).into(); + ensure!(!staked.is_zero(), Error::<T>::AlreadyMigrated); + ensure!(ledger.total == staked, Error::<T>::BadState); + + // remove old staking lock + T::OldCurrency::remove_lock(STAKING_ID, &stash); + + // check if we can hold all stake. + let max_hold = asset::free_to_stake::<T>(&stash); + let force_withdraw = if max_hold >= staked { + // this means we can hold all stake. yay! + asset::update_stake::<T>(&stash, staked)?; + Zero::zero() + } else { + // if we are here, it means we cannot hold all user stake. We will do a force withdraw + // from ledger, but that's okay since anyways user do not have funds for it. + let force_withdraw = staked.saturating_sub(max_hold); + + // we ignore if active is 0. It implies the locked amount is not actively staked. The + // account can still get away from potential slash but we can't do much better here. + StakingLedger { + total: max_hold, + active: ledger.active.saturating_sub(force_withdraw), + // we are not changing the stash, so we can keep the stash. + ..ledger + } + .update()?; + force_withdraw + }; + + // Get rid of the extra consumer we used to have with OldCurrency. + frame_system::Pallet::<T>::dec_consumers(&stash); + + Self::deposit_event(Event::<T>::CurrencyMigrated { stash: stash.clone(), force_withdraw }); + Ok(()) + } + + fn do_migrate_virtual_staker(stash: &T::AccountId) -> DispatchResult { + // Funds for virtual stakers not managed/held by this pallet. We only need to clear + // the extra consumer we used to have with OldCurrency. + frame_system::Pallet::<T>::dec_consumers(&stash); + + // The delegation system that manages the virtual staker needed to increment provider + // previously because of the consumer needed by this pallet. In reality, this stash + // is just a key for managing the ledger and the account does not need to hold any + // balance or exist. We decrement this provider. + let actual_providers = frame_system::Pallet::<T>::providers(stash); + + let expected_providers = + // provider is expected to be 1 but someone can always transfer some free funds to + // these accounts, increasing the provider. + if asset::free_to_stake::<T>(&stash) >= asset::existential_deposit::<T>() { + 2 + } else { + 1 + }; + + // We should never have more than expected providers. + ensure!(actual_providers <= expected_providers, Error::<T>::BadState); + + // if actual provider is less than expected, it is already migrated. + ensure!(actual_providers == expected_providers, Error::<T>::AlreadyMigrated); + + // dec provider + let _ = frame_system::Pallet::<T>::dec_providers(&stash)?; + + return Ok(()) + } } impl<T: Config> Pallet<T> { @@ -1925,9 +1996,10 @@ impl<T: Config> StakingInterface for Pallet<T> { } impl<T: Config> sp_staking::StakingUnchecked for Pallet<T> { - fn migrate_to_virtual_staker(who: &Self::AccountId) { - asset::kill_stake::<T>(who); + fn migrate_to_virtual_staker(who: &Self::AccountId) -> DispatchResult { + asset::kill_stake::<T>(who)?; VirtualStakers::<T>::insert(who, ()); + Ok(()) } /// Virtually bonds `keyless_who` to `payee` with `value`. @@ -1945,9 +2017,6 @@ impl<T: Config> sp_staking::StakingUnchecked for Pallet<T> { // check if payee not same as who. ensure!(keyless_who != payee, Error::<T>::RewardDestinationRestricted); - // mark this pallet as consumer of `who`. - frame_system::Pallet::<T>::inc_consumers(&keyless_who).map_err(|_| Error::<T>::BadState)?; - // mark who as a virtual staker. VirtualStakers::<T>::insert(keyless_who, ()); @@ -1959,11 +2028,13 @@ impl<T: Config> sp_staking::StakingUnchecked for Pallet<T> { Ok(()) } + /// Only meant to be used in tests. #[cfg(feature = "runtime-benchmarks")] fn migrate_to_direct_staker(who: &Self::AccountId) { assert!(VirtualStakers::<T>::contains_key(who)); let ledger = StakingLedger::<T>::get(Stash(who.clone())).unwrap(); - asset::update_stake::<T>(who, ledger.total); + let _ = asset::update_stake::<T>(who, ledger.total) + .expect("funds must be transferred to stash"); VirtualStakers::<T>::remove(who); } } @@ -2100,7 +2171,7 @@ impl<T: Config> Pallet<T> { if VirtualStakers::<T>::contains_key(stash.clone()) { ensure!( asset::staked::<T>(&stash) == Zero::zero(), - "virtual stakers should not have any locked balance" + "virtual stakers should not have any staked balance" ); ensure!( <Bonded<T>>::get(stash.clone()).unwrap() == stash.clone(), @@ -2128,7 +2199,7 @@ impl<T: Config> Pallet<T> { } else { ensure!( Self::inspect_bond_state(&stash) == Ok(LedgerIntegrityState::Ok), - "bond, ledger and/or staking lock inconsistent for a bonded stash." + "bond, ledger and/or staking hold inconsistent for a bonded stash." ); } diff --git a/substrate/frame/staking/src/pallet/mod.rs b/substrate/frame/staking/src/pallet/mod.rs index b3f8c18f704cd7e5158c89f0e432f50d7bcb8c04..7d5da9ea0c49753e0db14092b978d0c88b6dcd63 100644 --- a/substrate/frame/staking/src/pallet/mod.rs +++ b/substrate/frame/staking/src/pallet/mod.rs @@ -25,8 +25,12 @@ use frame_election_provider_support::{ use frame_support::{ pallet_prelude::*, traits::{ + fungible::{ + hold::{Balanced as FunHoldBalanced, Mutate as FunHoldMutate}, + Mutate as FunMutate, + }, Defensive, DefensiveSaturating, EnsureOrigin, EstimateNextNewSession, Get, - InspectLockableCurrency, LockableCurrency, OnUnbalanced, UnixTime, + InspectLockableCurrency, OnUnbalanced, UnixTime, }, weights::Weight, BoundedVec, @@ -89,13 +93,27 @@ pub mod pallet { #[pallet::config(with_default)] pub trait Config: frame_system::Config { + /// The old trait for staking balance. Deprecated and only used for migrating old ledgers. + #[pallet::no_default] + type OldCurrency: InspectLockableCurrency< + Self::AccountId, + Moment = BlockNumberFor<Self>, + Balance = Self::CurrencyBalance, + >; + /// The staking balance. #[pallet::no_default] - type Currency: LockableCurrency< + type Currency: FunHoldMutate< Self::AccountId, - Moment = BlockNumberFor<Self>, + Reason = Self::RuntimeHoldReason, Balance = Self::CurrencyBalance, - > + InspectLockableCurrency<Self::AccountId>; + > + FunMutate<Self::AccountId, Balance = Self::CurrencyBalance> + + FunHoldBalanced<Self::AccountId, Balance = Self::CurrencyBalance>; + + /// Overarching hold reason. + #[pallet::no_default_bounds] + type RuntimeHoldReason: From<HoldReason>; + /// Just the `Currency::Balance` type; we have this item to allow us to constrain it to /// `From<u64>`. type CurrencyBalance: sp_runtime::traits::AtLeast32BitUnsigned @@ -106,6 +124,8 @@ pub mod pallet { + Default + From<u64> + TypeInfo + + Send + + Sync + MaxEncodedLen; /// Time used for computing era duration. /// @@ -309,6 +329,14 @@ pub mod pallet { type WeightInfo: WeightInfo; } + /// A reason for placing a hold on funds. + #[pallet::composite_enum] + pub enum HoldReason { + /// Funds on stake by a nominator or a validator. + #[codec(index = 0)] + Staking, + } + /// Default implementations of [`DefaultConfig`], which can be used to implement [`Config`]. pub mod config_preludes { use super::*; @@ -327,6 +355,8 @@ pub mod pallet { impl DefaultConfig for TestDefaultConfig { #[inject_runtime_type] type RuntimeEvent = (); + #[inject_runtime_type] + type RuntimeHoldReason = (); type CurrencyBalance = u128; type CurrencyToVote = (); type NominationsQuota = crate::FixedNominationsQuota<16>; @@ -765,7 +795,7 @@ pub mod pallet { status ); assert!( - asset::stakeable_balance::<T>(stash) >= balance, + asset::free_to_stake::<T>(stash) >= balance, "Stash does not have enough balance to bond." ); frame_support::assert_ok!(<Pallet<T>>::bond( @@ -858,6 +888,9 @@ pub mod pallet { ValidatorDisabled { stash: T::AccountId }, /// Validator has been re-enabled. ValidatorReenabled { stash: T::AccountId }, + /// Staking balance migrated from locks to holds, with any balance that could not be held + /// is force withdrawn. + CurrencyMigrated { stash: T::AccountId, force_withdraw: BalanceOf<T> }, } #[pallet::error] @@ -929,6 +962,10 @@ pub mod pallet { NotEnoughFunds, /// Operation not allowed for virtual stakers. VirtualStakerNotAllowed, + /// Stash could not be reaped as other pallet might depend on it. + CannotReapStash, + /// The stake of this account is already migrated to `Fungible` holds. + AlreadyMigrated, } #[pallet::hooks] @@ -1172,10 +1209,7 @@ pub mod pallet { return Err(Error::<T>::InsufficientBond.into()) } - // Would fail if account has no provider. - frame_system::Pallet::<T>::inc_consumers(&stash)?; - - let stash_balance = asset::stakeable_balance::<T>(&stash); + let stash_balance = asset::free_to_stake::<T>(&stash); let value = value.min(stash_balance); Self::deposit_event(Event::<T>::Bonded { stash: stash.clone(), amount: value }); let ledger = StakingLedger::<T>::new(stash.clone(), value); @@ -2231,8 +2265,8 @@ pub mod pallet { let new_total = if let Some(total) = maybe_total { let new_total = total.min(stash_balance); - // enforce lock == ledger.amount. - asset::update_stake::<T>(&stash, new_total); + // enforce hold == ledger.amount. + asset::update_stake::<T>(&stash, new_total)?; new_total } else { current_lock @@ -2259,13 +2293,13 @@ pub mod pallet { // to enforce a new ledger.total and staking lock for this stash. let new_total = maybe_total.ok_or(Error::<T>::CannotRestoreLedger)?.min(stash_balance); - asset::update_stake::<T>(&stash, new_total); + asset::update_stake::<T>(&stash, new_total)?; Ok((stash.clone(), new_total)) }, Err(Error::<T>::BadState) => { // the stash and ledger do not exist but lock is lingering. - asset::kill_stake::<T>(&stash); + asset::kill_stake::<T>(&stash)?; ensure!( Self::inspect_bond_state(&stash) == Err(Error::<T>::NotStash), Error::<T>::BadState @@ -2291,6 +2325,26 @@ pub mod pallet { ); Ok(()) } + + /// Migrates permissionlessly a stash from locks to holds. + /// + /// This removes the old lock on the stake and creates a hold on it atomically. If all + /// stake cannot be held, the best effort is made to hold as much as possible. The remaining + /// stake is removed from the ledger. + /// + /// The fee is waived if the migration is successful. + #[pallet::call_index(30)] + #[pallet::weight(T::WeightInfo::migrate_currency())] + pub fn migrate_currency( + origin: OriginFor<T>, + stash: T::AccountId, + ) -> DispatchResultWithPostInfo { + let _ = ensure_signed(origin)?; + Self::do_migrate_currency(&stash)?; + + // Refund the transaction fee if successful. + Ok(Pays::No.into()) + } } } diff --git a/substrate/frame/staking/src/testing_utils.rs b/substrate/frame/staking/src/testing_utils.rs index 81337710aa9043bf975a95c3d5377b0dea0738f8..dfd5422106c081784c95985fa64477b596e93064 100644 --- a/substrate/frame/staking/src/testing_utils.rs +++ b/substrate/frame/staking/src/testing_utils.rs @@ -238,3 +238,21 @@ pub fn create_validators_with_nominators_for_era<T: Config>( pub fn current_era<T: Config>() -> EraIndex { CurrentEra::<T>::get().unwrap_or(0) } + +pub fn migrate_to_old_currency<T: Config>(who: T::AccountId) { + use frame_support::traits::LockableCurrency; + let staked = asset::staked::<T>(&who); + + // apply locks (this also adds a consumer). + T::OldCurrency::set_lock( + STAKING_ID, + &who, + staked, + frame_support::traits::WithdrawReasons::all(), + ); + // remove holds. + asset::kill_stake::<T>(&who).expect("remove hold failed"); + + // replicate old behaviour of explicit increment of consumer. + frame_system::Pallet::<T>::inc_consumers(&who).expect("increment consumer failed"); +} diff --git a/substrate/frame/staking/src/tests.rs b/substrate/frame/staking/src/tests.rs index 6c2335e1aac8a0f32beba70ac6974729e70af441..90841514399403301be4eb3a0d2ba87da543ddc2 100644 --- a/substrate/frame/staking/src/tests.rs +++ b/substrate/frame/staking/src/tests.rs @@ -26,8 +26,12 @@ use frame_election_provider_support::{ use frame_support::{ assert_noop, assert_ok, assert_storage_noop, dispatch::{extract_actual_weight, GetDispatchInfo, WithPostDispatchInfo}, + hypothetically, pallet_prelude::*, - traits::{Currency, Get, ReservableCurrency}, + traits::{ + fungible::Inspect, Currency, Get, InspectLockableCurrency, LockableCurrency, + ReservableCurrency, WithdrawReasons, + }, }; use mock::*; @@ -108,7 +112,7 @@ fn force_unstake_works() { // Cant transfer assert_noop!( Balances::transfer_allow_death(RuntimeOrigin::signed(11), 1, 10), - TokenError::Frozen, + TokenError::FundsUnavailable, ); // Force unstake requires root. assert_noop!(Staking::force_unstake(RuntimeOrigin::signed(11), 11, 2), BadOrigin); @@ -229,8 +233,7 @@ fn basic_setup_works() { assert_eq!(active_era(), 0); // Account 10 has `balance_factor` free balance - assert_eq!(asset::stakeable_balance::<Test>(&10), 1); - assert_eq!(asset::stakeable_balance::<Test>(&10), 1); + assert_eq!(Balances::balance(&10), 1); // New era is not being forced assert_eq!(ForceEra::<Test>::get(), Forcing::NotForcing); @@ -360,8 +363,16 @@ fn rewards_should_work() { remainder: maximum_payout - total_payout_0 } ); + + // make note of total issuance before rewards. + let total_issuance_0 = asset::total_issuance::<Test>(); + mock::make_all_reward_payment(0); + // total issuance should have increased + let total_issuance_1 = asset::total_issuance::<Test>(); + assert_eq!(total_issuance_1, total_issuance_0 + total_payout_0); + assert_eq_error_rate!( asset::total_balance::<Test>(&11), init_balance_11 + part_for_11 * total_payout_0 * 2 / 3, @@ -401,6 +412,7 @@ fn rewards_should_work() { ); mock::make_all_reward_payment(1); + assert_eq!(asset::total_issuance::<Test>(), total_issuance_1 + total_payout_1); assert_eq_error_rate!( asset::total_balance::<Test>(&11), init_balance_11 + part_for_11 * (total_payout_0 * 2 / 3 + total_payout_1), @@ -490,7 +502,7 @@ fn staking_should_work() { } ); // e.g. it cannot reserve more than 500 that it has free from the total 2000 - assert_noop!(Balances::reserve(&3, 501), BalancesError::<Test, _>::LiquidityRestrictions); + assert_noop!(Balances::reserve(&3, 501), DispatchError::ConsumerRemaining); assert_ok!(Balances::reserve(&3, 409)); }); } @@ -689,7 +701,7 @@ fn nominating_and_rewards_should_work() { ); // Nominator 3: has [400/1800 ~ 2/9 from 10] + [600/2200 ~ 3/11 from 21]'s reward. ==> // 2/9 + 3/11 - assert_eq!(asset::total_balance::<Test>(&3), initial_balance); + assert_eq!(asset::stakeable_balance::<Test>(&3), initial_balance); // 333 is the reward destination for 3. assert_eq_error_rate!( asset::total_balance::<Test>(&333), @@ -992,9 +1004,9 @@ fn cannot_transfer_staked_balance() { ExtBuilder::default().nominate(false).build_and_execute(|| { // Confirm account 11 is stashed assert_eq!(Staking::bonded(&11), Some(11)); - // Confirm account 11 has some free balance + // Confirm account 11 has some stakeable balance assert_eq!(asset::stakeable_balance::<Test>(&11), 1000); - // Confirm account 11 (via controller) is totally staked + // Confirm account 11 is totally staked assert_eq!(Staking::eras_stakers(active_era(), &11).total, 1000); // Confirm account 11 cannot transfer as a result assert_noop!( @@ -1021,11 +1033,12 @@ fn cannot_transfer_staked_balance_2() { assert_eq!(asset::stakeable_balance::<Test>(&21), 2000); // Confirm account 21 (via controller) is totally staked assert_eq!(Staking::eras_stakers(active_era(), &21).total, 1000); - // Confirm account 21 can transfer at most 1000 + // Confirm account 21 cannot transfer more than 1000 assert_noop!( Balances::transfer_allow_death(RuntimeOrigin::signed(21), 21, 1001), TokenError::Frozen, ); + // Confirm account 21 needs to leave at least ED in free balance to be able to transfer assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(21), 21, 1000)); }); } @@ -1036,17 +1049,61 @@ fn cannot_reserve_staked_balance() { ExtBuilder::default().build_and_execute(|| { // Confirm account 11 is stashed assert_eq!(Staking::bonded(&11), Some(11)); - // Confirm account 11 has some free balance - assert_eq!(asset::stakeable_balance::<Test>(&11), 1000); - // Confirm account 11 (via controller 10) is totally staked - assert_eq!(Staking::eras_stakers(active_era(), &11).own, 1000); + // Confirm account 11 is totally staked + assert_eq!(asset::staked::<Test>(&11), 1000); + // Confirm account 11 cannot reserve as a result - assert_noop!(Balances::reserve(&11, 1), BalancesError::<Test, _>::LiquidityRestrictions); + assert_noop!(Balances::reserve(&11, 2), BalancesError::<Test, _>::InsufficientBalance); + assert_noop!(Balances::reserve(&11, 1), DispatchError::ConsumerRemaining); // Give account 11 extra free balance - let _ = asset::set_stakeable_balance::<Test>(&11, 10000); + let _ = asset::set_stakeable_balance::<Test>(&11, 1000 + 1000); + assert_eq!(asset::free_to_stake::<Test>(&11), 1000); + // Confirm account 11 can now reserve balance - assert_ok!(Balances::reserve(&11, 1)); + assert_ok!(Balances::reserve(&11, 500)); + + // free to stake balance has reduced + assert_eq!(asset::free_to_stake::<Test>(&11), 500); + }); +} + +#[test] +fn locked_balance_can_be_staked() { + // Checks that a bonded account cannot reserve balance from free balance + ExtBuilder::default().build_and_execute(|| { + // Confirm account 11 is stashed + assert_eq!(Staking::bonded(&11), Some(11)); + assert_eq!(asset::staked::<Test>(&11), 1000); + assert_eq!(asset::free_to_stake::<Test>(&11), 0); + + // add some staking balance to 11 + let _ = asset::set_stakeable_balance::<Test>(&11, 1000 + 1000); + // free to stake is 1000 + assert_eq!(asset::free_to_stake::<Test>(&11), 1000); + + // lock some balance + Balances::set_lock(*b"somelock", &11, 500, WithdrawReasons::all()); + + // locked balance still available for staking + assert_eq!(asset::free_to_stake::<Test>(&11), 1000); + + // can stake free balance + assert_ok!(Staking::bond_extra(RuntimeOrigin::signed(11), 500)); + assert_eq!(asset::staked::<Test>(&11), 1500); + + // Can stake the locked balance + assert_ok!(Staking::bond_extra(RuntimeOrigin::signed(11), 500)); + assert_eq!(asset::staked::<Test>(&11), 2000); + // no balance left to stake + assert_eq!(asset::free_to_stake::<Test>(&11), 0); + + // this does not fail if someone tries to stake more than free balance but just stakes + // whatever is available. (not sure if that is the best way, but we keep it backward + // compatible) + assert_ok!(Staking::bond_extra(RuntimeOrigin::signed(11), 10)); + // no extra balance staked. + assert_eq!(asset::staked::<Test>(&11), 2000); }); } @@ -1057,9 +1114,9 @@ fn reward_destination_works() { // Check that account 11 is a validator assert!(Session::validators().contains(&11)); // Check the balance of the validator account - assert_eq!(asset::stakeable_balance::<Test>(&10), 1); + assert_eq!(asset::total_balance::<Test>(&10), 1); // Check the balance of the stash account - assert_eq!(asset::stakeable_balance::<Test>(&11), 1000); + assert_eq!(asset::total_balance::<Test>(&11), 1001); // Check how much is at stake assert_eq!( Staking::ledger(11.into()).unwrap(), @@ -1294,12 +1351,12 @@ fn bond_extra_and_withdraw_unbonded_works() { // Give account 11 some large free balance greater than total let _ = asset::set_stakeable_balance::<Test>(&11, 1000000); + // ensure it has the correct balance. + assert_eq!(asset::stakeable_balance::<Test>(&11), 1000000); + // Initial config should be correct assert_eq!(active_era(), 0); - // check the balance of a validator accounts. - assert_eq!(asset::total_balance::<Test>(&11), 1000000); - // confirm that 10 is a normal validator and gets paid at the end of the era. mock::start_active_era(1); @@ -2077,7 +2134,7 @@ fn bond_with_no_staked_value() { ); // bonded with absolute minimum value possible. assert_ok!(Staking::bond(RuntimeOrigin::signed(1), 5, RewardDestination::Account(1))); - assert_eq!(pallet_balances::Locks::<Test>::get(&1)[0].amount, 5); + assert_eq!(pallet_balances::Holds::<Test>::get(&1)[0].amount, 5); // unbonding even 1 will cause all to be unbonded. assert_ok!(Staking::unbond(RuntimeOrigin::signed(1), 1)); @@ -2098,14 +2155,14 @@ fn bond_with_no_staked_value() { // not yet removed. assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(1), 0)); assert!(Staking::ledger(1.into()).is_ok()); - assert_eq!(pallet_balances::Locks::<Test>::get(&1)[0].amount, 5); + assert_eq!(pallet_balances::Holds::<Test>::get(&1)[0].amount, 5); mock::start_active_era(3); // poof. Account 1 is removed from the staking system. assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(1), 0)); assert!(Staking::ledger(1.into()).is_err()); - assert_eq!(pallet_balances::Locks::<Test>::get(&1).len(), 0); + assert_eq!(pallet_balances::Holds::<Test>::get(&1).len(), 0); }); } @@ -2338,9 +2395,20 @@ fn reward_validator_slashing_validator_does_not_overflow() { EraInfo::<Test>::set_exposure(0, &11, exposure); ErasValidatorReward::<Test>::insert(0, stake); assert_ok!(Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 0, 0)); - assert_eq!(asset::total_balance::<Test>(&11), stake * 2); + assert_eq!(asset::stakeable_balance::<Test>(&11), stake * 2); - // Set staker + // ensure ledger has `stake` and no more. + Ledger::<Test>::insert( + 11, + StakingLedgerInspect { + stash: 11, + total: stake, + active: stake, + unlocking: Default::default(), + legacy_claimed_rewards: bounded_vec![1], + }, + ); + // Set staker (unsafe, can reduce balance below actual stake) let _ = asset::set_stakeable_balance::<Test>(&11, stake); let _ = asset::set_stakeable_balance::<Test>(&2, stake); @@ -2366,8 +2434,8 @@ fn reward_validator_slashing_validator_does_not_overflow() { &[Perbill::from_percent(100)], ); - assert_eq!(asset::total_balance::<Test>(&11), stake - 1); - assert_eq!(asset::total_balance::<Test>(&2), 1); + assert_eq!(asset::stakeable_balance::<Test>(&11), stake - 1); + assert_eq!(asset::stakeable_balance::<Test>(&2), 1); }) } @@ -2627,8 +2695,8 @@ fn reporters_receive_their_slice() { // 50% * (10% * initial_balance / 2) let reward = (initial_balance / 20) / 2; let reward_each = reward / 2; // split into two pieces. - assert_eq!(asset::stakeable_balance::<Test>(&1), 10 + reward_each); - assert_eq!(asset::stakeable_balance::<Test>(&2), 20 + reward_each); + assert_eq!(asset::total_balance::<Test>(&1), 10 + reward_each); + assert_eq!(asset::total_balance::<Test>(&2), 20 + reward_each); }); } @@ -2653,7 +2721,7 @@ fn subsequent_reports_in_same_span_pay_out_less() { // F1 * (reward_proportion * slash - 0) // 50% * (10% * initial_balance * 20%) let reward = (initial_balance / 5) / 20; - assert_eq!(asset::stakeable_balance::<Test>(&1), 10 + reward); + assert_eq!(asset::total_balance::<Test>(&1), 10 + reward); on_offence_now( &[OffenceDetails { @@ -2668,7 +2736,7 @@ fn subsequent_reports_in_same_span_pay_out_less() { // F1 * (reward_proportion * slash - prior_payout) // 50% * (10% * (initial_balance / 2) - prior_payout) let reward = ((initial_balance / 20) - prior_payout) / 2; - assert_eq!(asset::stakeable_balance::<Test>(&1), 10 + prior_payout + reward); + assert_eq!(asset::total_balance::<Test>(&1), 10 + prior_payout + reward); }); } @@ -2812,8 +2880,9 @@ fn garbage_collection_after_slashing() { // validator and nominator slash in era are garbage-collected by era change, // so we don't test those here. - assert_eq!(asset::stakeable_balance::<Test>(&11), 2); - assert_eq!(asset::total_balance::<Test>(&11), 2); + assert_eq!(asset::stakeable_balance::<Test>(&11), 0); + // Non staked balance is not touched. + assert_eq!(asset::total_balance::<Test>(&11), ExistentialDeposit::get()); let slashing_spans = SlashingSpans::<Test>::get(&11).unwrap(); assert_eq!(slashing_spans.iter().count(), 2); @@ -6092,7 +6161,7 @@ fn nomination_quota_max_changes_decoding() { .add_staker(70, 71, 10, StakerStatus::Nominator(vec![1, 2, 3])) .add_staker(30, 330, 10, StakerStatus::Nominator(vec![1, 2, 3, 4])) .add_staker(50, 550, 10, StakerStatus::Nominator(vec![1, 2, 3, 4])) - .balance_factor(10) + .balance_factor(11) .build_and_execute(|| { // pre-condition. assert_eq!(MaxNominationsOf::<Test>::get(), 16); @@ -6208,240 +6277,248 @@ fn force_apply_min_commission_works() { #[test] fn proportional_slash_stop_slashing_if_remaining_zero() { - let c = |era, value| UnlockChunk::<Balance> { era, value }; + ExtBuilder::default().nominate(true).build_and_execute(|| { + let c = |era, value| UnlockChunk::<Balance> { era, value }; - // we have some chunks, but they are not affected. - let unlocking = bounded_vec![c(1, 10), c(2, 10)]; + // we have some chunks, but they are not affected. + let unlocking = bounded_vec![c(1, 10), c(2, 10)]; - // Given - let mut ledger = StakingLedger::<Test>::new(123, 20); - ledger.total = 40; - ledger.unlocking = unlocking; + // Given + let mut ledger = StakingLedger::<Test>::new(123, 20); + ledger.total = 40; + ledger.unlocking = unlocking; - assert_eq!(BondingDuration::get(), 3); + assert_eq!(BondingDuration::get(), 3); - // should not slash more than the amount requested, by accidentally slashing the first chunk. - assert_eq!(ledger.slash(18, 1, 0), 18); + // should not slash more than the amount requested, by accidentally slashing the first + // chunk. + assert_eq!(ledger.slash(18, 1, 0), 18); + }); } #[test] fn proportional_ledger_slash_works() { - let c = |era, value| UnlockChunk::<Balance> { era, value }; - // Given - let mut ledger = StakingLedger::<Test>::new(123, 10); - assert_eq!(BondingDuration::get(), 3); - - // When we slash a ledger with no unlocking chunks - assert_eq!(ledger.slash(5, 1, 0), 5); - // Then - assert_eq!(ledger.total, 5); - assert_eq!(ledger.active, 5); - assert_eq!(LedgerSlashPerEra::get().0, 5); - assert_eq!(LedgerSlashPerEra::get().1, Default::default()); - - // When we slash a ledger with no unlocking chunks and the slash amount is greater then the - // total - assert_eq!(ledger.slash(11, 1, 0), 5); - // Then - assert_eq!(ledger.total, 0); - assert_eq!(ledger.active, 0); - assert_eq!(LedgerSlashPerEra::get().0, 0); - assert_eq!(LedgerSlashPerEra::get().1, Default::default()); - - // Given - ledger.unlocking = bounded_vec![c(4, 10), c(5, 10)]; - ledger.total = 2 * 10; - ledger.active = 0; - // When all the chunks overlap with the slash eras - assert_eq!(ledger.slash(20, 0, 0), 20); - // Then - assert_eq!(ledger.unlocking, vec![]); - assert_eq!(ledger.total, 0); - assert_eq!(LedgerSlashPerEra::get().0, 0); - assert_eq!(LedgerSlashPerEra::get().1, BTreeMap::from([(4, 0), (5, 0)])); - - // Given - ledger.unlocking = bounded_vec![c(4, 100), c(5, 100), c(6, 100), c(7, 100)]; - ledger.total = 4 * 100; - ledger.active = 0; - // When the first 2 chunks don't overlap with the affected range of unlock eras. - assert_eq!(ledger.slash(140, 0, 3), 140); - // Then - assert_eq!(ledger.unlocking, vec![c(4, 100), c(5, 100), c(6, 30), c(7, 30)]); - assert_eq!(ledger.total, 4 * 100 - 140); - assert_eq!(LedgerSlashPerEra::get().0, 0); - assert_eq!(LedgerSlashPerEra::get().1, BTreeMap::from([(6, 30), (7, 30)])); - - // Given - ledger.unlocking = bounded_vec![c(4, 100), c(5, 100), c(6, 100), c(7, 100)]; - ledger.total = 4 * 100; - ledger.active = 0; - // When the first 2 chunks don't overlap with the affected range of unlock eras. - assert_eq!(ledger.slash(15, 0, 3), 15); - // Then - assert_eq!(ledger.unlocking, vec![c(4, 100), c(5, 100), c(6, 100 - 8), c(7, 100 - 7)]); - assert_eq!(ledger.total, 4 * 100 - 15); - assert_eq!(LedgerSlashPerEra::get().0, 0); - assert_eq!(LedgerSlashPerEra::get().1, BTreeMap::from([(6, 92), (7, 93)])); - - // Given - ledger.unlocking = bounded_vec![c(4, 40), c(5, 100), c(6, 10), c(7, 250)]; - ledger.active = 500; - // 900 - ledger.total = 40 + 10 + 100 + 250 + 500; - // When we have a partial slash that touches all chunks - assert_eq!(ledger.slash(900 / 2, 0, 0), 450); - // Then - assert_eq!(ledger.active, 500 / 2); - assert_eq!(ledger.unlocking, vec![c(4, 40 / 2), c(5, 100 / 2), c(6, 10 / 2), c(7, 250 / 2)]); - assert_eq!(ledger.total, 900 / 2); - assert_eq!(LedgerSlashPerEra::get().0, 500 / 2); - assert_eq!( - LedgerSlashPerEra::get().1, - BTreeMap::from([(4, 40 / 2), (5, 100 / 2), (6, 10 / 2), (7, 250 / 2)]) - ); + ExtBuilder::default().nominate(true).build_and_execute(|| { + let c = |era, value| UnlockChunk::<Balance> { era, value }; + // Given + let mut ledger = StakingLedger::<Test>::new(123, 10); + assert_eq!(BondingDuration::get(), 3); - // slash 1/4th with not chunk. - ledger.unlocking = bounded_vec![]; - ledger.active = 500; - ledger.total = 500; - // When we have a partial slash that touches all chunks - assert_eq!(ledger.slash(500 / 4, 0, 0), 500 / 4); - // Then - assert_eq!(ledger.active, 3 * 500 / 4); - assert_eq!(ledger.unlocking, vec![]); - assert_eq!(ledger.total, ledger.active); - assert_eq!(LedgerSlashPerEra::get().0, 3 * 500 / 4); - assert_eq!(LedgerSlashPerEra::get().1, Default::default()); - - // Given we have the same as above, - ledger.unlocking = bounded_vec![c(4, 40), c(5, 100), c(6, 10), c(7, 250)]; - ledger.active = 500; - ledger.total = 40 + 10 + 100 + 250 + 500; // 900 - assert_eq!(ledger.total, 900); - // When we have a higher min balance - assert_eq!( - ledger.slash( - 900 / 2, - 25, /* min balance - chunks with era 0 & 2 will be slashed to <=25, causing it to - * get swept */ - 0 - ), - 450 - ); - assert_eq!(ledger.active, 500 / 2); - // the last chunk was not slashed 50% like all the rest, because some other earlier chunks got - // dusted. - assert_eq!(ledger.unlocking, vec![c(5, 100 / 2), c(7, 150)]); - assert_eq!(ledger.total, 900 / 2); - assert_eq!(LedgerSlashPerEra::get().0, 500 / 2); - assert_eq!( - LedgerSlashPerEra::get().1, - BTreeMap::from([(4, 0), (5, 100 / 2), (6, 0), (7, 150)]) - ); + // When we slash a ledger with no unlocking chunks + assert_eq!(ledger.slash(5, 1, 0), 5); + // Then + assert_eq!(ledger.total, 5); + assert_eq!(ledger.active, 5); + assert_eq!(LedgerSlashPerEra::get().0, 5); + assert_eq!(LedgerSlashPerEra::get().1, Default::default()); + + // When we slash a ledger with no unlocking chunks and the slash amount is greater then the + // total + assert_eq!(ledger.slash(11, 1, 0), 5); + // Then + assert_eq!(ledger.total, 0); + assert_eq!(ledger.active, 0); + assert_eq!(LedgerSlashPerEra::get().0, 0); + assert_eq!(LedgerSlashPerEra::get().1, Default::default()); - // Given - // slash order --------------------NA--------2----------0----------1---- - ledger.unlocking = bounded_vec![c(4, 40), c(5, 100), c(6, 10), c(7, 250)]; - ledger.active = 500; - ledger.total = 40 + 10 + 100 + 250 + 500; // 900 - assert_eq!( - ledger.slash( - 500 + 10 + 250 + 100 / 2, // active + era 6 + era 7 + era 5 / 2 - 0, - 3 /* slash era 6 first, so the affected parts are era 6, era 7 and - * ledge.active. This will cause the affected to go to zero, and then we will - * start slashing older chunks */ - ), - 500 + 250 + 10 + 100 / 2 - ); - // Then - assert_eq!(ledger.active, 0); - assert_eq!(ledger.unlocking, vec![c(4, 40), c(5, 100 / 2)]); - assert_eq!(ledger.total, 90); - assert_eq!(LedgerSlashPerEra::get().0, 0); - assert_eq!(LedgerSlashPerEra::get().1, BTreeMap::from([(5, 100 / 2), (6, 0), (7, 0)])); - - // Given - // iteration order------------------NA---------2----------0----------1---- - ledger.unlocking = bounded_vec![c(4, 100), c(5, 100), c(6, 100), c(7, 100)]; - ledger.active = 100; - ledger.total = 5 * 100; - // When - assert_eq!( - ledger.slash( - 351, // active + era 6 + era 7 + era 5 / 2 + 1 - 50, // min balance - everything slashed below 50 will get dusted - 3 /* slash era 3+3 first, so the affected parts are era 6, era 7 and - * ledge.active. This will cause the affected to go to zero, and then we will - * start slashing older chunks */ - ), - 400 - ); - // Then - assert_eq!(ledger.active, 0); - assert_eq!(ledger.unlocking, vec![c(4, 100)]); - assert_eq!(ledger.total, 100); - assert_eq!(LedgerSlashPerEra::get().0, 0); - assert_eq!(LedgerSlashPerEra::get().1, BTreeMap::from([(5, 0), (6, 0), (7, 0)])); - - // Tests for saturating arithmetic - - // Given - let slash = u64::MAX as Balance * 2; - // The value of the other parts of ledger that will get slashed - let value = slash - (10 * 4); - - ledger.active = 10; - ledger.unlocking = bounded_vec![c(4, 10), c(5, 10), c(6, 10), c(7, value)]; - ledger.total = value + 40; - // When - let slash_amount = ledger.slash(slash, 0, 0); - assert_eq_error_rate!(slash_amount, slash, 5); - // Then - assert_eq!(ledger.active, 0); // slash of 9 - assert_eq!(ledger.unlocking, vec![]); - assert_eq!(ledger.total, 0); - assert_eq!(LedgerSlashPerEra::get().0, 0); - assert_eq!(LedgerSlashPerEra::get().1, BTreeMap::from([(4, 0), (5, 0), (6, 0), (7, 0)])); - - // Given - use sp_runtime::PerThing as _; - let slash = u64::MAX as Balance * 2; - let value = u64::MAX as Balance * 2; - let unit = 100; - // slash * value that will saturate - assert!(slash.checked_mul(value).is_none()); - // but slash * unit won't. - assert!(slash.checked_mul(unit).is_some()); - ledger.unlocking = bounded_vec![c(4, unit), c(5, value), c(6, unit), c(7, unit)]; - //--------------------------------------note value^^^ - ledger.active = unit; - ledger.total = unit * 4 + value; - // When - assert_eq!(ledger.slash(slash, 0, 0), slash); - // Then - // The amount slashed out of `unit` - let affected_balance = value + unit * 4; - let ratio = - Perquintill::from_rational_with_rounding(slash, affected_balance, Rounding::Up).unwrap(); - // `unit` after the slash is applied - let unit_slashed = { - let unit_slash = ratio.mul_ceil(unit); - unit - unit_slash - }; - let value_slashed = { - let value_slash = ratio.mul_ceil(value); - value - value_slash - }; - assert_eq!(ledger.active, unit_slashed); - assert_eq!(ledger.unlocking, vec![c(5, value_slashed), c(7, 32)]); - assert_eq!(ledger.total, value_slashed + 32); - assert_eq!(LedgerSlashPerEra::get().0, 0); - assert_eq!( - LedgerSlashPerEra::get().1, - BTreeMap::from([(4, 0), (5, value_slashed), (6, 0), (7, 32)]) - ); + // Given + ledger.unlocking = bounded_vec![c(4, 10), c(5, 10)]; + ledger.total = 2 * 10; + ledger.active = 0; + // When all the chunks overlap with the slash eras + assert_eq!(ledger.slash(20, 0, 0), 20); + // Then + assert_eq!(ledger.unlocking, vec![]); + assert_eq!(ledger.total, 0); + assert_eq!(LedgerSlashPerEra::get().0, 0); + assert_eq!(LedgerSlashPerEra::get().1, BTreeMap::from([(4, 0), (5, 0)])); + + // Given + ledger.unlocking = bounded_vec![c(4, 100), c(5, 100), c(6, 100), c(7, 100)]; + ledger.total = 4 * 100; + ledger.active = 0; + // When the first 2 chunks don't overlap with the affected range of unlock eras. + assert_eq!(ledger.slash(140, 0, 3), 140); + // Then + assert_eq!(ledger.unlocking, vec![c(4, 100), c(5, 100), c(6, 30), c(7, 30)]); + assert_eq!(ledger.total, 4 * 100 - 140); + assert_eq!(LedgerSlashPerEra::get().0, 0); + assert_eq!(LedgerSlashPerEra::get().1, BTreeMap::from([(6, 30), (7, 30)])); + + // Given + ledger.unlocking = bounded_vec![c(4, 100), c(5, 100), c(6, 100), c(7, 100)]; + ledger.total = 4 * 100; + ledger.active = 0; + // When the first 2 chunks don't overlap with the affected range of unlock eras. + assert_eq!(ledger.slash(15, 0, 3), 15); + // Then + assert_eq!(ledger.unlocking, vec![c(4, 100), c(5, 100), c(6, 100 - 8), c(7, 100 - 7)]); + assert_eq!(ledger.total, 4 * 100 - 15); + assert_eq!(LedgerSlashPerEra::get().0, 0); + assert_eq!(LedgerSlashPerEra::get().1, BTreeMap::from([(6, 92), (7, 93)])); + + // Given + ledger.unlocking = bounded_vec![c(4, 40), c(5, 100), c(6, 10), c(7, 250)]; + ledger.active = 500; + // 900 + ledger.total = 40 + 10 + 100 + 250 + 500; + // When we have a partial slash that touches all chunks + assert_eq!(ledger.slash(900 / 2, 0, 0), 450); + // Then + assert_eq!(ledger.active, 500 / 2); + assert_eq!( + ledger.unlocking, + vec![c(4, 40 / 2), c(5, 100 / 2), c(6, 10 / 2), c(7, 250 / 2)] + ); + assert_eq!(ledger.total, 900 / 2); + assert_eq!(LedgerSlashPerEra::get().0, 500 / 2); + assert_eq!( + LedgerSlashPerEra::get().1, + BTreeMap::from([(4, 40 / 2), (5, 100 / 2), (6, 10 / 2), (7, 250 / 2)]) + ); + + // slash 1/4th with not chunk. + ledger.unlocking = bounded_vec![]; + ledger.active = 500; + ledger.total = 500; + // When we have a partial slash that touches all chunks + assert_eq!(ledger.slash(500 / 4, 0, 0), 500 / 4); + // Then + assert_eq!(ledger.active, 3 * 500 / 4); + assert_eq!(ledger.unlocking, vec![]); + assert_eq!(ledger.total, ledger.active); + assert_eq!(LedgerSlashPerEra::get().0, 3 * 500 / 4); + assert_eq!(LedgerSlashPerEra::get().1, Default::default()); + + // Given we have the same as above, + ledger.unlocking = bounded_vec![c(4, 40), c(5, 100), c(6, 10), c(7, 250)]; + ledger.active = 500; + ledger.total = 40 + 10 + 100 + 250 + 500; // 900 + assert_eq!(ledger.total, 900); + // When we have a higher min balance + assert_eq!( + ledger.slash( + 900 / 2, + 25, /* min balance - chunks with era 0 & 2 will be slashed to <=25, causing it + * to get swept */ + 0 + ), + 450 + ); + assert_eq!(ledger.active, 500 / 2); + // the last chunk was not slashed 50% like all the rest, because some other earlier chunks + // got dusted. + assert_eq!(ledger.unlocking, vec![c(5, 100 / 2), c(7, 150)]); + assert_eq!(ledger.total, 900 / 2); + assert_eq!(LedgerSlashPerEra::get().0, 500 / 2); + assert_eq!( + LedgerSlashPerEra::get().1, + BTreeMap::from([(4, 0), (5, 100 / 2), (6, 0), (7, 150)]) + ); + + // Given + // slash order --------------------NA--------2----------0----------1---- + ledger.unlocking = bounded_vec![c(4, 40), c(5, 100), c(6, 10), c(7, 250)]; + ledger.active = 500; + ledger.total = 40 + 10 + 100 + 250 + 500; // 900 + assert_eq!( + ledger.slash( + 500 + 10 + 250 + 100 / 2, // active + era 6 + era 7 + era 5 / 2 + 0, + 3 /* slash era 6 first, so the affected parts are era 6, era 7 and + * ledge.active. This will cause the affected to go to zero, and then we will + * start slashing older chunks */ + ), + 500 + 250 + 10 + 100 / 2 + ); + // Then + assert_eq!(ledger.active, 0); + assert_eq!(ledger.unlocking, vec![c(4, 40), c(5, 100 / 2)]); + assert_eq!(ledger.total, 90); + assert_eq!(LedgerSlashPerEra::get().0, 0); + assert_eq!(LedgerSlashPerEra::get().1, BTreeMap::from([(5, 100 / 2), (6, 0), (7, 0)])); + + // Given + // iteration order------------------NA---------2----------0----------1---- + ledger.unlocking = bounded_vec![c(4, 100), c(5, 100), c(6, 100), c(7, 100)]; + ledger.active = 100; + ledger.total = 5 * 100; + // When + assert_eq!( + ledger.slash( + 351, // active + era 6 + era 7 + era 5 / 2 + 1 + 50, // min balance - everything slashed below 50 will get dusted + 3 /* slash era 3+3 first, so the affected parts are era 6, era 7 and + * ledge.active. This will cause the affected to go to zero, and then we + * will start slashing older chunks */ + ), + 400 + ); + // Then + assert_eq!(ledger.active, 0); + assert_eq!(ledger.unlocking, vec![c(4, 100)]); + assert_eq!(ledger.total, 100); + assert_eq!(LedgerSlashPerEra::get().0, 0); + assert_eq!(LedgerSlashPerEra::get().1, BTreeMap::from([(5, 0), (6, 0), (7, 0)])); + + // Tests for saturating arithmetic + + // Given + let slash = u64::MAX as Balance * 2; + // The value of the other parts of ledger that will get slashed + let value = slash - (10 * 4); + + ledger.active = 10; + ledger.unlocking = bounded_vec![c(4, 10), c(5, 10), c(6, 10), c(7, value)]; + ledger.total = value + 40; + // When + let slash_amount = ledger.slash(slash, 0, 0); + assert_eq_error_rate!(slash_amount, slash, 5); + // Then + assert_eq!(ledger.active, 0); // slash of 9 + assert_eq!(ledger.unlocking, vec![]); + assert_eq!(ledger.total, 0); + assert_eq!(LedgerSlashPerEra::get().0, 0); + assert_eq!(LedgerSlashPerEra::get().1, BTreeMap::from([(4, 0), (5, 0), (6, 0), (7, 0)])); + + // Given + use sp_runtime::PerThing as _; + let slash = u64::MAX as Balance * 2; + let value = u64::MAX as Balance * 2; + let unit = 100; + // slash * value that will saturate + assert!(slash.checked_mul(value).is_none()); + // but slash * unit won't. + assert!(slash.checked_mul(unit).is_some()); + ledger.unlocking = bounded_vec![c(4, unit), c(5, value), c(6, unit), c(7, unit)]; + //--------------------------------------note value^^^ + ledger.active = unit; + ledger.total = unit * 4 + value; + // When + assert_eq!(ledger.slash(slash, 0, 0), slash); + // Then + // The amount slashed out of `unit` + let affected_balance = value + unit * 4; + let ratio = Perquintill::from_rational_with_rounding(slash, affected_balance, Rounding::Up) + .unwrap(); + // `unit` after the slash is applied + let unit_slashed = { + let unit_slash = ratio.mul_ceil(unit); + unit - unit_slash + }; + let value_slashed = { + let value_slash = ratio.mul_ceil(value); + value - value_slash + }; + assert_eq!(ledger.active, unit_slashed); + assert_eq!(ledger.unlocking, vec![c(5, value_slashed), c(7, 32)]); + assert_eq!(ledger.total, value_slashed + 32); + assert_eq!(LedgerSlashPerEra::get().0, 0); + assert_eq!( + LedgerSlashPerEra::get().1, + BTreeMap::from([(4, 0), (5, value_slashed), (6, 0), (7, 32)]) + ); + }); } #[test] @@ -7126,7 +7203,7 @@ mod staking_unchecked { fn virtual_bond_does_not_lock() { ExtBuilder::default().build_and_execute(|| { mock::start_active_era(1); - assert_eq!(asset::stakeable_balance::<Test>(&10), 1); + assert_eq!(asset::total_balance::<Test>(&10), 1); // 10 can bond more than its balance amount since we do not require lock for virtual // bonding. assert_ok!(<Staking as StakingUnchecked>::virtual_bond(&10, 100, &15)); @@ -7265,7 +7342,7 @@ mod staking_unchecked { assert_eq!(asset::staked::<Test>(&200), 1000); // migrate them to virtual staker - <Staking as StakingUnchecked>::migrate_to_virtual_staker(&200); + assert_ok!(<Staking as StakingUnchecked>::migrate_to_virtual_staker(&200)); // payee needs to be updated to a non-stash account. assert_ok!(<Staking as StakingInterface>::set_payee(&200, &201)); @@ -7292,7 +7369,7 @@ mod staking_unchecked { // 101 is a nominator for 11 assert_eq!(initial_exposure.others.first().unwrap().who, 101); // make 101 a virtual nominator - <Staking as StakingUnchecked>::migrate_to_virtual_staker(&101); + assert_ok!(<Staking as StakingUnchecked>::migrate_to_virtual_staker(&101)); // set payee different to self. assert_ok!(<Staking as StakingInterface>::set_payee(&101, &102)); @@ -7367,7 +7444,7 @@ mod staking_unchecked { // 101 is a nominator for 11 assert_eq!(initial_exposure.others.first().unwrap().who, 101); // make 101 a virtual nominator - <Staking as StakingUnchecked>::migrate_to_virtual_staker(&101); + assert_ok!(<Staking as StakingUnchecked>::migrate_to_virtual_staker(&101)); // set payee different to self. assert_ok!(<Staking as StakingInterface>::set_payee(&101, &102)); @@ -7423,7 +7500,7 @@ mod staking_unchecked { // 333 is corrupted assert_eq!(Staking::inspect_bond_state(&333).unwrap(), LedgerIntegrityState::Corrupted); // migrate to virtual staker. - <Staking as StakingUnchecked>::migrate_to_virtual_staker(&333); + assert_ok!(<Staking as StakingUnchecked>::migrate_to_virtual_staker(&333)); // recover the ledger won't work for virtual staker assert_noop!( @@ -8034,8 +8111,7 @@ mod ledger_recovery { // side effects on 333 - ledger, bonded, payee, lock should be intact. assert_eq!(asset::staked::<Test>(&333), lock_333_before); // OK assert_eq!(Bonded::<Test>::get(&333), Some(444)); // OK - assert!(Payee::<Test>::get(&333).is_some()); // OK - + assert!(Payee::<Test>::get(&333).is_some()); // however, ledger associated with its controller was killed. assert!(Ledger::<Test>::get(&444).is_none()); // NOK @@ -9081,3 +9157,249 @@ mod getters { }); } } + +mod hold_migration { + use super::*; + use sp_staking::{Stake, StakingInterface}; + + #[test] + fn ledger_update_creates_hold() { + ExtBuilder::default().has_stakers(true).build_and_execute(|| { + // GIVEN alice who is a nominator with old currency + let alice = 300; + bond_nominator(alice, 1000, vec![11]); + assert_eq!(asset::staked::<Test>(&alice), 1000); + assert_eq!(Balances::balance_locked(STAKING_ID, &alice), 0); + // migrate alice currency to legacy locks + testing_utils::migrate_to_old_currency::<Test>(alice); + // no more holds + assert_eq!(asset::staked::<Test>(&alice), 0); + assert_eq!(Balances::balance_locked(STAKING_ID, &alice), 1000); + assert_eq!( + <Staking as StakingInterface>::stake(&alice), + Ok(Stake { total: 1000, active: 1000 }) + ); + + // any ledger mutation should create a hold + hypothetically!({ + // give some extra balance to alice. + let _ = asset::mint_into_existing::<Test>(&alice, 100); + + // WHEN new fund is bonded to ledger. + assert_ok!(Staking::bond_extra(RuntimeOrigin::signed(alice), 100)); + + // THEN new hold is created + assert_eq!(asset::staked::<Test>(&alice), 1000 + 100); + assert_eq!( + <Staking as StakingInterface>::stake(&alice), + Ok(Stake { total: 1100, active: 1100 }) + ); + + // old locked balance is untouched + assert_eq!(Balances::balance_locked(STAKING_ID, &alice), 1000); + }); + + hypothetically!({ + // WHEN new fund is unbonded from ledger. + assert_ok!(Staking::unbond(RuntimeOrigin::signed(alice), 100)); + + // THEN hold is updated. + assert_eq!(asset::staked::<Test>(&alice), 1000); + assert_eq!( + <Staking as StakingInterface>::stake(&alice), + Ok(Stake { total: 1000, active: 900 }) + ); + + // old locked balance is untouched + assert_eq!(Balances::balance_locked(STAKING_ID, &alice), 1000); + }); + + // WHEN alice currency is migrated. + assert_ok!(Staking::migrate_currency(RuntimeOrigin::signed(1), alice)); + + // THEN hold is updated. + assert_eq!(asset::staked::<Test>(&alice), 1000); + assert_eq!( + <Staking as StakingInterface>::stake(&alice), + Ok(Stake { total: 1000, active: 1000 }) + ); + + // ensure cannot migrate again. + assert_noop!( + Staking::migrate_currency(RuntimeOrigin::signed(1), alice), + Error::<Test>::AlreadyMigrated + ); + + // locked balance is removed + assert_eq!(Balances::balance_locked(STAKING_ID, &alice), 0); + }); + } + + #[test] + fn migrate_removes_old_lock() { + ExtBuilder::default().has_stakers(true).build_and_execute(|| { + // GIVEN alice who is a nominator with old currency + let alice = 300; + bond_nominator(alice, 1000, vec![11]); + testing_utils::migrate_to_old_currency::<Test>(alice); + assert_eq!(asset::staked::<Test>(&alice), 0); + assert_eq!(Balances::balance_locked(STAKING_ID, &alice), 1000); + let pre_migrate_consumer = System::consumers(&alice); + System::reset_events(); + + // WHEN alice currency is migrated. + assert_ok!(Staking::migrate_currency(RuntimeOrigin::signed(1), alice)); + + // THEN + // the extra consumer from old code is removed. + assert_eq!(System::consumers(&alice), pre_migrate_consumer - 1); + // ensure no lock + assert_eq!(Balances::balance_locked(STAKING_ID, &alice), 0); + // ensure stake and hold are same. + assert_eq!( + <Staking as StakingInterface>::stake(&alice), + Ok(Stake { total: 1000, active: 1000 }) + ); + assert_eq!(asset::staked::<Test>(&alice), 1000); + // ensure events are emitted. + assert_eq!( + staking_events_since_last_call(), + vec![Event::CurrencyMigrated { stash: alice, force_withdraw: 0 }] + ); + + // ensure cannot migrate again. + assert_noop!( + Staking::migrate_currency(RuntimeOrigin::signed(1), alice), + Error::<Test>::AlreadyMigrated + ); + }); + } + #[test] + fn cannot_hold_all_stake() { + // When there is not enough funds to hold all stake, part of the stake if force withdrawn. + // At end of the migration, the stake and hold should be same. + ExtBuilder::default().has_stakers(true).build_and_execute(|| { + // GIVEN alice who is a nominator with old currency. + let alice = 300; + let stake = 1000; + bond_nominator(alice, stake, vec![11]); + testing_utils::migrate_to_old_currency::<Test>(alice); + assert_eq!(asset::staked::<Test>(&alice), 0); + assert_eq!(Balances::balance_locked(STAKING_ID, &alice), stake); + // ledger has 1000 staked. + assert_eq!( + <Staking as StakingInterface>::stake(&alice), + Ok(Stake { total: stake, active: stake }) + ); + + // Get rid of the extra ED to emulate all their balance including ED is staked. + assert_ok!(Balances::transfer_allow_death( + RuntimeOrigin::signed(alice), + 10, + ExistentialDeposit::get() + )); + + let expected_force_withdraw = ExistentialDeposit::get(); + + // ledger mutation would fail in this case before migration because of failing hold. + assert_noop!( + Staking::unbond(RuntimeOrigin::signed(alice), 100), + Error::<Test>::NotEnoughFunds + ); + + // clear events + System::reset_events(); + + // WHEN alice currency is migrated. + assert_ok!(Staking::migrate_currency(RuntimeOrigin::signed(1), alice)); + + // THEN + let expected_hold = stake - expected_force_withdraw; + // ensure no lock + assert_eq!(Balances::balance_locked(STAKING_ID, &alice), 0); + // ensure stake and hold are same. + assert_eq!( + <Staking as StakingInterface>::stake(&alice), + Ok(Stake { total: expected_hold, active: expected_hold }) + ); + assert_eq!(asset::staked::<Test>(&alice), expected_hold); + // ensure events are emitted. + assert_eq!( + staking_events_since_last_call(), + vec![Event::CurrencyMigrated { + stash: alice, + force_withdraw: expected_force_withdraw + }] + ); + + // ensure cannot migrate again. + assert_noop!( + Staking::migrate_currency(RuntimeOrigin::signed(1), alice), + Error::<Test>::AlreadyMigrated + ); + + // unbond works after migration. + assert_ok!(Staking::unbond(RuntimeOrigin::signed(alice), 100)); + }); + } + + #[test] + fn virtual_staker_consumer_provider_dec() { + // Ensure virtual stakers consumer and provider count is decremented. + ExtBuilder::default().has_stakers(true).build_and_execute(|| { + // 200 virtual bonds + bond_virtual_nominator(200, 201, 500, vec![11, 21]); + + // previously the virtual nominator had a provider inc by the delegation system as + // well as a consumer by this pallet. + System::inc_providers(&200); + System::inc_consumers(&200).expect("has provider, can consume"); + + hypothetically!({ + // migrate 200 + assert_ok!(Staking::migrate_currency(RuntimeOrigin::signed(1), 200)); + + // ensure account does not exist in system anymore. + assert_eq!(System::consumers(&200), 0); + assert_eq!(System::providers(&200), 0); + assert!(!System::account_exists(&200)); + + // ensure cannot migrate again. + assert_noop!( + Staking::migrate_currency(RuntimeOrigin::signed(1), 200), + Error::<Test>::AlreadyMigrated + ); + }); + + hypothetically!({ + // 200 has an erroneously extra provider + System::inc_providers(&200); + + // causes migration to fail. + assert_noop!( + Staking::migrate_currency(RuntimeOrigin::signed(1), 200), + Error::<Test>::BadState + ); + }); + + // 200 is funded for more than ED by a random account. + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(999), 200, 10)); + + // it has an extra provider now. + assert_eq!(System::providers(&200), 2); + + // migrate 200 + assert_ok!(Staking::migrate_currency(RuntimeOrigin::signed(1), 200)); + + // 1 provider is left, consumers is 0. + assert_eq!(System::providers(&200), 1); + assert_eq!(System::consumers(&200), 0); + + // ensure cannot migrate again. + assert_noop!( + Staking::migrate_currency(RuntimeOrigin::signed(1), 200), + Error::<Test>::AlreadyMigrated + ); + }); + } +} diff --git a/substrate/frame/staking/src/weights.rs b/substrate/frame/staking/src/weights.rs index 56f561679cfc78aee1cca7f1a5b759e17b2f93d9..02ccdacb01c4295f29e4735cda32704e2438f828 100644 --- a/substrate/frame/staking/src/weights.rs +++ b/substrate/frame/staking/src/weights.rs @@ -18,27 +18,25 @@ //! Autogenerated weights for `pallet_staking` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-04-09, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-09-17, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-anb7yjbi-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-obbyq9g6-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` // Executed Command: -// ./target/production/substrate-node +// target/production/substrate-node // benchmark // pallet -// --chain=dev // --steps=50 // --repeat=20 -// --pallet=pallet_staking -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --wasm-execution=compiled // --heap-pages=4096 -// --output=./substrate/frame/staking/src/weights.rs +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_staking +// --chain=dev // --header=./substrate/HEADER-APACHE2 +// --output=./substrate/frame/staking/src/weights.rs // --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -83,6 +81,7 @@ pub trait WeightInfo { fn force_apply_min_commission() -> Weight; fn set_min_commission() -> Weight; fn restore_ledger() -> Weight; + fn migrate_currency() -> Weight; } /// Weights for `pallet_staking` using the Substrate node and recommended hardware. @@ -92,18 +91,18 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::Ledger` (r:1 w:1) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`) + /// Storage: `Staking::VirtualStakers` (r:1 w:0) + /// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) /// Storage: `Staking::Payee` (r:0 w:1) /// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) fn bond() -> Weight { // Proof Size summary in bytes: - // Measured: `1042` - // Estimated: `4764` - // Minimum execution time: 46_504_000 picoseconds. - Weight::from_parts(48_459_000, 4764) + // Measured: `1068` + // Estimated: `4556` + // Minimum execution time: 71_854_000 picoseconds. + Weight::from_parts(73_408_000, 4556) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -111,20 +110,20 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::Ledger` (r:1 w:1) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `Staking::VirtualStakers` (r:1 w:0) + /// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`) /// Storage: `VoterList::ListNodes` (r:3 w:3) /// Proof: `VoterList::ListNodes` (`max_values`: None, `max_size`: Some(154), added: 2629, mode: `MaxEncodedLen`) /// Storage: `VoterList::ListBags` (r:2 w:2) /// Proof: `VoterList::ListBags` (`max_values`: None, `max_size`: Some(82), added: 2557, mode: `MaxEncodedLen`) fn bond_extra() -> Weight { // Proof Size summary in bytes: - // Measured: `1990` + // Measured: `2049` // Estimated: `8877` - // Minimum execution time: 90_475_000 picoseconds. - Weight::from_parts(93_619_000, 8877) + // Minimum execution time: 127_442_000 picoseconds. + Weight::from_parts(130_845_000, 8877) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } @@ -138,22 +137,22 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { /// Proof: `Staking::MinNominatorBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Staking::CurrentEra` (r:1 w:0) /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `Staking::VirtualStakers` (r:1 w:0) + /// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:0) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`) /// Storage: `VoterList::ListNodes` (r:3 w:3) /// Proof: `VoterList::ListNodes` (`max_values`: None, `max_size`: Some(154), added: 2629, mode: `MaxEncodedLen`) /// Storage: `VoterList::ListBags` (r:2 w:2) /// Proof: `VoterList::ListBags` (`max_values`: None, `max_size`: Some(82), added: 2557, mode: `MaxEncodedLen`) fn unbond() -> Weight { // Proof Size summary in bytes: - // Measured: `2195` + // Measured: `2151` // Estimated: `8877` - // Minimum execution time: 99_335_000 picoseconds. - Weight::from_parts(101_440_000, 8877) + // Minimum execution time: 105_259_000 picoseconds. + Weight::from_parts(107_112_000, 8877) .saturating_add(T::DbWeight::get().reads(12_u64)) - .saturating_add(T::DbWeight::get().writes(7_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) } /// Storage: `Staking::Ledger` (r:1 w:1) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) @@ -161,21 +160,21 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::CurrentEra` (r:1 w:0) /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `Staking::VirtualStakers` (r:1 w:0) + /// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`) /// Storage: `NominationPools::ReversePoolIdLookup` (r:1 w:0) /// Proof: `NominationPools::ReversePoolIdLookup` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1297` - // Estimated: `4764` - // Minimum execution time: 50_067_000 picoseconds. - Weight::from_parts(52_396_327, 4764) - // Standard Error: 1_419 - .saturating_add(Weight::from_parts(51_406, 0).saturating_mul(s.into())) + // Measured: `1393` + // Estimated: `4556` + // Minimum execution time: 77_158_000 picoseconds. + Weight::from_parts(79_140_122, 4556) + // Standard Error: 1_688 + .saturating_add(Weight::from_parts(62_663, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -187,10 +186,10 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `Staking::SlashingSpans` (r:1 w:1) /// Proof: `Staking::SlashingSpans` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `Staking::VirtualStakers` (r:1 w:1) + /// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`) /// Storage: `Staking::Validators` (r:1 w:0) /// Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) /// Storage: `Staking::Nominators` (r:1 w:1) @@ -210,14 +209,14 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_kill(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2196 + s * (4 ±0)` + // Measured: `2255 + s * (4 ±0)` // Estimated: `6248 + s * (4 ±0)` - // Minimum execution time: 92_931_000 picoseconds. - Weight::from_parts(101_398_156, 6248) - // Standard Error: 4_180 - .saturating_add(Weight::from_parts(1_377_850, 0).saturating_mul(s.into())) + // Minimum execution time: 125_396_000 picoseconds. + Weight::from_parts(134_915_543, 6248) + // Standard Error: 3_660 + .saturating_add(Weight::from_parts(1_324_736, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(13_u64)) - .saturating_add(T::DbWeight::get().writes(11_u64)) + .saturating_add(T::DbWeight::get().writes(12_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) .saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into())) } @@ -245,10 +244,10 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { /// Proof: `Staking::CounterForValidators` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn validate() -> Weight { // Proof Size summary in bytes: - // Measured: `1372` + // Measured: `1438` // Estimated: `4556` - // Minimum execution time: 56_291_000 picoseconds. - Weight::from_parts(58_372_000, 4556) + // Minimum execution time: 68_826_000 picoseconds. + Weight::from_parts(71_261_000, 4556) .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -261,12 +260,12 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { /// The range of component `k` is `[1, 128]`. fn kick(k: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1815 + k * (572 ±0)` + // Measured: `1848 + k * (572 ±0)` // Estimated: `4556 + k * (3033 ±0)` - // Minimum execution time: 36_218_000 picoseconds. - Weight::from_parts(38_811_308, 4556) - // Standard Error: 8_352 - .saturating_add(Weight::from_parts(6_527_398, 0).saturating_mul(k.into())) + // Minimum execution time: 46_082_000 picoseconds. + Weight::from_parts(49_541_374, 4556) + // Standard Error: 7_218 + .saturating_add(Weight::from_parts(7_281_079, 0).saturating_mul(k.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(k.into()))) @@ -297,12 +296,12 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1866 + n * (102 ±0)` + // Measured: `1932 + n * (102 ±0)` // Estimated: `6248 + n * (2520 ±0)` - // Minimum execution time: 68_607_000 picoseconds. - Weight::from_parts(66_831_185, 6248) - // Standard Error: 14_014 - .saturating_add(Weight::from_parts(4_031_635, 0).saturating_mul(n.into())) + // Minimum execution time: 83_854_000 picoseconds. + Weight::from_parts(81_387_241, 6248) + // Standard Error: 16_811 + .saturating_add(Weight::from_parts(4_900_554, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(6_u64)) @@ -326,10 +325,10 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { /// Proof: `VoterList::CounterForListNodes` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn chill() -> Weight { // Proof Size summary in bytes: - // Measured: `1816` + // Measured: `1882` // Estimated: `6248` - // Minimum execution time: 60_088_000 picoseconds. - Weight::from_parts(62_471_000, 6248) + // Minimum execution time: 73_939_000 picoseconds. + Weight::from_parts(75_639_000, 6248) .saturating_add(T::DbWeight::get().reads(9_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -341,10 +340,10 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { /// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) fn set_payee() -> Weight { // Proof Size summary in bytes: - // Measured: `902` + // Measured: `935` // Estimated: `4556` - // Minimum execution time: 19_777_000 picoseconds. - Weight::from_parts(20_690_000, 4556) + // Minimum execution time: 24_592_000 picoseconds. + Weight::from_parts(25_092_000, 4556) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -356,10 +355,10 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { /// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) fn update_payee() -> Weight { // Proof Size summary in bytes: - // Measured: `969` + // Measured: `1002` // Estimated: `4556` - // Minimum execution time: 23_705_000 picoseconds. - Weight::from_parts(24_409_000, 4556) + // Minimum execution time: 29_735_000 picoseconds. + Weight::from_parts(30_546_000, 4556) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -369,10 +368,10 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) fn set_controller() -> Weight { // Proof Size summary in bytes: - // Measured: `902` + // Measured: `935` // Estimated: `8122` - // Minimum execution time: 23_479_000 picoseconds. - Weight::from_parts(24_502_000, 8122) + // Minimum execution time: 28_728_000 picoseconds. + Weight::from_parts(29_709_000, 8122) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -382,8 +381,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_675_000 picoseconds. - Weight::from_parts(2_802_000, 0) + // Minimum execution time: 2_519_000 picoseconds. + Weight::from_parts(2_673_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Staking::ForceEra` (r:0 w:1) @@ -392,8 +391,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_067_000 picoseconds. - Weight::from_parts(7_413_000, 0) + // Minimum execution time: 8_050_000 picoseconds. + Weight::from_parts(8_268_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Staking::ForceEra` (r:0 w:1) @@ -402,8 +401,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_977_000 picoseconds. - Weight::from_parts(7_353_000, 0) + // Minimum execution time: 8_131_000 picoseconds. + Weight::from_parts(8_349_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Staking::ForceEra` (r:0 w:1) @@ -412,8 +411,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_071_000 picoseconds. - Weight::from_parts(7_463_000, 0) + // Minimum execution time: 8_104_000 picoseconds. + Weight::from_parts(8_317_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Staking::Invulnerables` (r:0 w:1) @@ -423,10 +422,10 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_833_000 picoseconds. - Weight::from_parts(3_328_130, 0) - // Standard Error: 30 - .saturating_add(Weight::from_parts(10_058, 0).saturating_mul(v.into())) + // Minimum execution time: 2_669_000 picoseconds. + Weight::from_parts(3_013_436, 0) + // Standard Error: 31 + .saturating_add(Weight::from_parts(10_704, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Staking::Ledger` (r:11800 w:11800) @@ -438,12 +437,12 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { /// The range of component `i` is `[0, 5900]`. fn deprecate_controller_batch(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1746 + i * (229 ±0)` + // Measured: `1779 + i * (229 ±0)` // Estimated: `990 + i * (7132 ±0)` - // Minimum execution time: 5_300_000 picoseconds. - Weight::from_parts(5_437_000, 990) - // Standard Error: 66_261 - .saturating_add(Weight::from_parts(30_172_457, 0).saturating_mul(i.into())) + // Minimum execution time: 5_101_000 picoseconds. + Weight::from_parts(5_368_000, 990) + // Standard Error: 75_180 + .saturating_add(Weight::from_parts(33_781_643, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(i.into()))) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(i.into()))) .saturating_add(Weight::from_parts(0, 7132).saturating_mul(i.into())) @@ -454,10 +453,10 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::Ledger` (r:1 w:1) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `Staking::VirtualStakers` (r:1 w:1) + /// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `Staking::Validators` (r:1 w:0) @@ -479,14 +478,14 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { /// The range of component `s` is `[0, 100]`. fn force_unstake(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2196 + s * (4 ±0)` + // Measured: `2255 + s * (4 ±0)` // Estimated: `6248 + s * (4 ±0)` - // Minimum execution time: 87_677_000 picoseconds. - Weight::from_parts(96_386_462, 6248) - // Standard Error: 3_717 - .saturating_add(Weight::from_parts(1_370_585, 0).saturating_mul(s.into())) + // Minimum execution time: 119_955_000 picoseconds. + Weight::from_parts(128_392_032, 6248) + // Standard Error: 3_773 + .saturating_add(Weight::from_parts(1_302_488, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(13_u64)) - .saturating_add(T::DbWeight::get().writes(12_u64)) + .saturating_add(T::DbWeight::get().writes(13_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) .saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into())) } @@ -495,12 +494,12 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { /// The range of component `s` is `[1, 1000]`. fn cancel_deferred_slash(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `66672` - // Estimated: `70137` - // Minimum execution time: 105_086_000 picoseconds. - Weight::from_parts(1_167_895_222, 70137) - // Standard Error: 77_022 - .saturating_add(Weight::from_parts(6_487_305, 0).saturating_mul(s.into())) + // Measured: `66705` + // Estimated: `70170` + // Minimum execution time: 139_290_000 picoseconds. + Weight::from_parts(959_667_494, 70170) + // Standard Error: 56_271 + .saturating_add(Weight::from_parts(4_798_293, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -518,12 +517,10 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `Staking::ErasValidatorReward` (r:1 w:0) /// Proof: `Staking::ErasValidatorReward` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) - /// Storage: `Balances::Locks` (r:257 w:257) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:257 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:257 w:257) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Staking::VirtualStakers` (r:257 w:0) + /// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:257 w:257) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`) /// Storage: `Staking::ErasStakersPaged` (r:1 w:0) /// Proof: `Staking::ErasStakersPaged` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Staking::ErasRewardPoints` (r:1 w:0) @@ -532,29 +529,31 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { /// Proof: `Staking::ErasValidatorPrefs` (`max_values`: None, `max_size`: Some(57), added: 2532, mode: `MaxEncodedLen`) /// Storage: `Staking::Payee` (r:257 w:0) /// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:257 w:257) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 256]`. fn payout_stakers_alive_staked(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `33297 + n * (377 ±0)` - // Estimated: `30944 + n * (3774 ±3)` - // Minimum execution time: 154_210_000 picoseconds. - Weight::from_parts(192_836_012, 30944) - // Standard Error: 40_441 - .saturating_add(Weight::from_parts(47_646_642, 0).saturating_mul(n.into())) + // Measured: `33283 + n * (370 ±0)` + // Estimated: `30958 + n * (3566 ±0)` + // Minimum execution time: 193_068_000 picoseconds. + Weight::from_parts(252_762_568, 30958) + // Standard Error: 22_743 + .saturating_add(Weight::from_parts(81_185_306, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(14_u64)) .saturating_add(T::DbWeight::get().reads((6_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 3774).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 3566).saturating_mul(n.into())) } /// Storage: `Staking::Ledger` (r:1 w:1) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) /// Storage: `Staking::Bonded` (r:1 w:0) /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `Staking::VirtualStakers` (r:1 w:0) + /// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:0) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`) /// Storage: `VoterList::ListNodes` (r:3 w:3) /// Proof: `VoterList::ListNodes` (`max_values`: None, `max_size`: Some(154), added: 2629, mode: `MaxEncodedLen`) /// Storage: `VoterList::ListBags` (r:2 w:2) @@ -562,25 +561,25 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { /// The range of component `l` is `[1, 32]`. fn rebond(l: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1991 + l * (7 ±0)` + // Measured: `1947 + l * (7 ±0)` // Estimated: `8877` - // Minimum execution time: 88_337_000 picoseconds. - Weight::from_parts(91_391_254, 8877) - // Standard Error: 4_485 - .saturating_add(Weight::from_parts(103_443, 0).saturating_mul(l.into())) + // Minimum execution time: 91_151_000 picoseconds. + Weight::from_parts(93_596_096, 8877) + // Standard Error: 5_313 + .saturating_add(Weight::from_parts(124_684, 0).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(9_u64)) - .saturating_add(T::DbWeight::get().writes(7_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) } + /// Storage: `Staking::VirtualStakers` (r:1 w:1) + /// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) /// Storage: `Staking::Bonded` (r:1 w:1) /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::Ledger` (r:1 w:1) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) /// Storage: `Staking::SlashingSpans` (r:1 w:1) /// Proof: `Staking::SlashingSpans` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`) /// Storage: `Staking::Validators` (r:1 w:0) /// Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) /// Storage: `Staking::Nominators` (r:1 w:1) @@ -600,14 +599,14 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { /// The range of component `s` is `[1, 100]`. fn reap_stash(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2196 + s * (4 ±0)` + // Measured: `2255 + s * (4 ±0)` // Estimated: `6248 + s * (4 ±0)` - // Minimum execution time: 98_014_000 picoseconds. - Weight::from_parts(102_537_670, 6248) - // Standard Error: 3_324 - .saturating_add(Weight::from_parts(1_353_142, 0).saturating_mul(s.into())) + // Minimum execution time: 133_214_000 picoseconds. + Weight::from_parts(137_290_527, 6248) + // Standard Error: 4_153 + .saturating_add(Weight::from_parts(1_291_007, 0).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(12_u64)) - .saturating_add(T::DbWeight::get().writes(11_u64)) + .saturating_add(T::DbWeight::get().writes(12_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) .saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into())) } @@ -651,12 +650,12 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { // Proof Size summary in bytes: // Measured: `0 + n * (720 ±0) + v * (3598 ±0)` // Estimated: `512390 + n * (3566 ±0) + v * (3566 ±0)` - // Minimum execution time: 608_575_000 picoseconds. - Weight::from_parts(613_663_000, 512390) - // Standard Error: 2_286_521 - .saturating_add(Weight::from_parts(72_108_001, 0).saturating_mul(v.into())) - // Standard Error: 227_839 - .saturating_add(Weight::from_parts(20_314_085, 0).saturating_mul(n.into())) + // Minimum execution time: 692_301_000 picoseconds. + Weight::from_parts(708_732_000, 512390) + // Standard Error: 2_117_299 + .saturating_add(Weight::from_parts(70_087_600, 0).saturating_mul(v.into())) + // Standard Error: 210_977 + .saturating_add(Weight::from_parts(22_953_405, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(206_u64)) .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(n.into()))) @@ -685,14 +684,14 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { /// The range of component `n` is `[500, 1000]`. fn get_npos_voters(v: u32, n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `3175 + n * (911 ±0) + v * (395 ±0)` + // Measured: `3241 + n * (911 ±0) + v * (395 ±0)` // Estimated: `512390 + n * (3566 ±0) + v * (3566 ±0)` - // Minimum execution time: 37_173_756_000 picoseconds. - Weight::from_parts(37_488_937_000, 512390) - // Standard Error: 467_413 - .saturating_add(Weight::from_parts(8_086_367, 0).saturating_mul(v.into())) - // Standard Error: 467_413 - .saturating_add(Weight::from_parts(3_108_193, 0).saturating_mul(n.into())) + // Minimum execution time: 43_708_472_000 picoseconds. + Weight::from_parts(44_048_436_000, 512390) + // Standard Error: 493_244 + .saturating_add(Weight::from_parts(6_697_278, 0).saturating_mul(v.into())) + // Standard Error: 493_244 + .saturating_add(Weight::from_parts(4_559_779, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(201_u64)) .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(n.into()))) @@ -707,12 +706,12 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { /// The range of component `v` is `[500, 1000]`. fn get_npos_targets(v: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `979 + v * (50 ±0)` + // Measured: `1012 + v * (50 ±0)` // Estimated: `3510 + v * (2520 ±0)` - // Minimum execution time: 2_641_258_000 picoseconds. - Weight::from_parts(382_882_595, 3510) - // Standard Error: 11_991 - .saturating_add(Weight::from_parts(4_695_820, 0).saturating_mul(v.into())) + // Minimum execution time: 2_917_165_000 picoseconds. + Weight::from_parts(2_948_999_000, 3510) + // Standard Error: 33_372 + .saturating_add(Weight::from_parts(2_126_909, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(v.into()))) .saturating_add(Weight::from_parts(0, 2520).saturating_mul(v.into())) @@ -735,8 +734,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_753_000 picoseconds. - Weight::from_parts(6_529_000, 0) + // Minimum execution time: 4_748_000 picoseconds. + Weight::from_parts(5_052_000, 0) .saturating_add(T::DbWeight::get().writes(7_u64)) } /// Storage: `Staking::MinCommission` (r:0 w:1) @@ -757,8 +756,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_212_000 picoseconds. - Weight::from_parts(5_451_000, 0) + // Minimum execution time: 4_316_000 picoseconds. + Weight::from_parts(4_526_000, 0) .saturating_add(T::DbWeight::get().writes(7_u64)) } /// Storage: `Staking::Bonded` (r:1 w:0) @@ -785,10 +784,10 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { /// Proof: `VoterList::CounterForListNodes` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn chill_other() -> Weight { // Proof Size summary in bytes: - // Measured: `1939` + // Measured: `2005` // Estimated: `6248` - // Minimum execution time: 73_000_000 picoseconds. - Weight::from_parts(75_184_000, 6248) + // Minimum execution time: 87_374_000 picoseconds. + Weight::from_parts(89_848_000, 6248) .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -798,10 +797,10 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { /// Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) fn force_apply_min_commission() -> Weight { // Proof Size summary in bytes: - // Measured: `691` + // Measured: `724` // Estimated: `3510` - // Minimum execution time: 13_056_000 picoseconds. - Weight::from_parts(13_517_000, 3510) + // Minimum execution time: 15_529_000 picoseconds. + Weight::from_parts(16_094_000, 3510) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -811,28 +810,51 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_201_000 picoseconds. - Weight::from_parts(3_442_000, 0) + // Minimum execution time: 2_533_000 picoseconds. + Weight::from_parts(2_817_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) + /// Storage: `Staking::VirtualStakers` (r:1 w:0) + /// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:0) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:0) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) /// Storage: `Staking::Bonded` (r:1 w:1) /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::Ledger` (r:1 w:1) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + fn restore_ledger() -> Weight { + // Proof Size summary in bytes: + // Measured: `1110` + // Estimated: `4764` + // Minimum execution time: 50_105_000 picoseconds. + Weight::from_parts(50_966_000, 4764) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Staking::VirtualStakers` (r:1 w:0) + /// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Staking::Bonded` (r:1 w:0) + /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `Staking::Ledger` (r:1 w:0) + /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`) /// Storage: `Balances::Freezes` (r:1 w:0) /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) - fn restore_ledger() -> Weight { + fn migrate_currency() -> Weight { // Proof Size summary in bytes: - // Measured: `1047` + // Measured: `1246` // Estimated: `4764` - // Minimum execution time: 44_671_000 picoseconds. - Weight::from_parts(45_611_000, 4764) - .saturating_add(T::DbWeight::get().reads(5_u64)) - .saturating_add(T::DbWeight::get().writes(4_u64)) + // Minimum execution time: 94_054_000 picoseconds. + Weight::from_parts(96_272_000, 4764) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } } @@ -842,18 +864,18 @@ impl WeightInfo for () { /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::Ledger` (r:1 w:1) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`) + /// Storage: `Staking::VirtualStakers` (r:1 w:0) + /// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) /// Storage: `Staking::Payee` (r:0 w:1) /// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) fn bond() -> Weight { // Proof Size summary in bytes: - // Measured: `1042` - // Estimated: `4764` - // Minimum execution time: 46_504_000 picoseconds. - Weight::from_parts(48_459_000, 4764) + // Measured: `1068` + // Estimated: `4556` + // Minimum execution time: 71_854_000 picoseconds. + Weight::from_parts(73_408_000, 4556) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -861,20 +883,20 @@ impl WeightInfo for () { /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::Ledger` (r:1 w:1) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `Staking::VirtualStakers` (r:1 w:0) + /// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`) /// Storage: `VoterList::ListNodes` (r:3 w:3) /// Proof: `VoterList::ListNodes` (`max_values`: None, `max_size`: Some(154), added: 2629, mode: `MaxEncodedLen`) /// Storage: `VoterList::ListBags` (r:2 w:2) /// Proof: `VoterList::ListBags` (`max_values`: None, `max_size`: Some(82), added: 2557, mode: `MaxEncodedLen`) fn bond_extra() -> Weight { // Proof Size summary in bytes: - // Measured: `1990` + // Measured: `2049` // Estimated: `8877` - // Minimum execution time: 90_475_000 picoseconds. - Weight::from_parts(93_619_000, 8877) + // Minimum execution time: 127_442_000 picoseconds. + Weight::from_parts(130_845_000, 8877) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } @@ -888,22 +910,22 @@ impl WeightInfo for () { /// Proof: `Staking::MinNominatorBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Staking::CurrentEra` (r:1 w:0) /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `Staking::VirtualStakers` (r:1 w:0) + /// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:0) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`) /// Storage: `VoterList::ListNodes` (r:3 w:3) /// Proof: `VoterList::ListNodes` (`max_values`: None, `max_size`: Some(154), added: 2629, mode: `MaxEncodedLen`) /// Storage: `VoterList::ListBags` (r:2 w:2) /// Proof: `VoterList::ListBags` (`max_values`: None, `max_size`: Some(82), added: 2557, mode: `MaxEncodedLen`) fn unbond() -> Weight { // Proof Size summary in bytes: - // Measured: `2195` + // Measured: `2151` // Estimated: `8877` - // Minimum execution time: 99_335_000 picoseconds. - Weight::from_parts(101_440_000, 8877) + // Minimum execution time: 105_259_000 picoseconds. + Weight::from_parts(107_112_000, 8877) .saturating_add(RocksDbWeight::get().reads(12_u64)) - .saturating_add(RocksDbWeight::get().writes(7_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) } /// Storage: `Staking::Ledger` (r:1 w:1) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) @@ -911,21 +933,21 @@ impl WeightInfo for () { /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::CurrentEra` (r:1 w:0) /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `Staking::VirtualStakers` (r:1 w:0) + /// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`) /// Storage: `NominationPools::ReversePoolIdLookup` (r:1 w:0) /// Proof: `NominationPools::ReversePoolIdLookup` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1297` - // Estimated: `4764` - // Minimum execution time: 50_067_000 picoseconds. - Weight::from_parts(52_396_327, 4764) - // Standard Error: 1_419 - .saturating_add(Weight::from_parts(51_406, 0).saturating_mul(s.into())) + // Measured: `1393` + // Estimated: `4556` + // Minimum execution time: 77_158_000 picoseconds. + Weight::from_parts(79_140_122, 4556) + // Standard Error: 1_688 + .saturating_add(Weight::from_parts(62_663, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -937,10 +959,10 @@ impl WeightInfo for () { /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `Staking::SlashingSpans` (r:1 w:1) /// Proof: `Staking::SlashingSpans` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `Staking::VirtualStakers` (r:1 w:1) + /// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`) /// Storage: `Staking::Validators` (r:1 w:0) /// Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) /// Storage: `Staking::Nominators` (r:1 w:1) @@ -960,14 +982,14 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_kill(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2196 + s * (4 ±0)` + // Measured: `2255 + s * (4 ±0)` // Estimated: `6248 + s * (4 ±0)` - // Minimum execution time: 92_931_000 picoseconds. - Weight::from_parts(101_398_156, 6248) - // Standard Error: 4_180 - .saturating_add(Weight::from_parts(1_377_850, 0).saturating_mul(s.into())) + // Minimum execution time: 125_396_000 picoseconds. + Weight::from_parts(134_915_543, 6248) + // Standard Error: 3_660 + .saturating_add(Weight::from_parts(1_324_736, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(13_u64)) - .saturating_add(RocksDbWeight::get().writes(11_u64)) + .saturating_add(RocksDbWeight::get().writes(12_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) .saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into())) } @@ -995,10 +1017,10 @@ impl WeightInfo for () { /// Proof: `Staking::CounterForValidators` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn validate() -> Weight { // Proof Size summary in bytes: - // Measured: `1372` + // Measured: `1438` // Estimated: `4556` - // Minimum execution time: 56_291_000 picoseconds. - Weight::from_parts(58_372_000, 4556) + // Minimum execution time: 68_826_000 picoseconds. + Weight::from_parts(71_261_000, 4556) .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -1011,12 +1033,12 @@ impl WeightInfo for () { /// The range of component `k` is `[1, 128]`. fn kick(k: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1815 + k * (572 ±0)` + // Measured: `1848 + k * (572 ±0)` // Estimated: `4556 + k * (3033 ±0)` - // Minimum execution time: 36_218_000 picoseconds. - Weight::from_parts(38_811_308, 4556) - // Standard Error: 8_352 - .saturating_add(Weight::from_parts(6_527_398, 0).saturating_mul(k.into())) + // Minimum execution time: 46_082_000 picoseconds. + Weight::from_parts(49_541_374, 4556) + // Standard Error: 7_218 + .saturating_add(Weight::from_parts(7_281_079, 0).saturating_mul(k.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(k.into()))) @@ -1047,12 +1069,12 @@ impl WeightInfo for () { /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1866 + n * (102 ±0)` + // Measured: `1932 + n * (102 ±0)` // Estimated: `6248 + n * (2520 ±0)` - // Minimum execution time: 68_607_000 picoseconds. - Weight::from_parts(66_831_185, 6248) - // Standard Error: 14_014 - .saturating_add(Weight::from_parts(4_031_635, 0).saturating_mul(n.into())) + // Minimum execution time: 83_854_000 picoseconds. + Weight::from_parts(81_387_241, 6248) + // Standard Error: 16_811 + .saturating_add(Weight::from_parts(4_900_554, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(6_u64)) @@ -1076,10 +1098,10 @@ impl WeightInfo for () { /// Proof: `VoterList::CounterForListNodes` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn chill() -> Weight { // Proof Size summary in bytes: - // Measured: `1816` + // Measured: `1882` // Estimated: `6248` - // Minimum execution time: 60_088_000 picoseconds. - Weight::from_parts(62_471_000, 6248) + // Minimum execution time: 73_939_000 picoseconds. + Weight::from_parts(75_639_000, 6248) .saturating_add(RocksDbWeight::get().reads(9_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -1091,10 +1113,10 @@ impl WeightInfo for () { /// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) fn set_payee() -> Weight { // Proof Size summary in bytes: - // Measured: `902` + // Measured: `935` // Estimated: `4556` - // Minimum execution time: 19_777_000 picoseconds. - Weight::from_parts(20_690_000, 4556) + // Minimum execution time: 24_592_000 picoseconds. + Weight::from_parts(25_092_000, 4556) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1106,10 +1128,10 @@ impl WeightInfo for () { /// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) fn update_payee() -> Weight { // Proof Size summary in bytes: - // Measured: `969` + // Measured: `1002` // Estimated: `4556` - // Minimum execution time: 23_705_000 picoseconds. - Weight::from_parts(24_409_000, 4556) + // Minimum execution time: 29_735_000 picoseconds. + Weight::from_parts(30_546_000, 4556) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1119,10 +1141,10 @@ impl WeightInfo for () { /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) fn set_controller() -> Weight { // Proof Size summary in bytes: - // Measured: `902` + // Measured: `935` // Estimated: `8122` - // Minimum execution time: 23_479_000 picoseconds. - Weight::from_parts(24_502_000, 8122) + // Minimum execution time: 28_728_000 picoseconds. + Weight::from_parts(29_709_000, 8122) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -1132,8 +1154,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_675_000 picoseconds. - Weight::from_parts(2_802_000, 0) + // Minimum execution time: 2_519_000 picoseconds. + Weight::from_parts(2_673_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Staking::ForceEra` (r:0 w:1) @@ -1142,8 +1164,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_067_000 picoseconds. - Weight::from_parts(7_413_000, 0) + // Minimum execution time: 8_050_000 picoseconds. + Weight::from_parts(8_268_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Staking::ForceEra` (r:0 w:1) @@ -1152,8 +1174,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_977_000 picoseconds. - Weight::from_parts(7_353_000, 0) + // Minimum execution time: 8_131_000 picoseconds. + Weight::from_parts(8_349_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Staking::ForceEra` (r:0 w:1) @@ -1162,8 +1184,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_071_000 picoseconds. - Weight::from_parts(7_463_000, 0) + // Minimum execution time: 8_104_000 picoseconds. + Weight::from_parts(8_317_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Staking::Invulnerables` (r:0 w:1) @@ -1173,10 +1195,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_833_000 picoseconds. - Weight::from_parts(3_328_130, 0) - // Standard Error: 30 - .saturating_add(Weight::from_parts(10_058, 0).saturating_mul(v.into())) + // Minimum execution time: 2_669_000 picoseconds. + Weight::from_parts(3_013_436, 0) + // Standard Error: 31 + .saturating_add(Weight::from_parts(10_704, 0).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Staking::Ledger` (r:11800 w:11800) @@ -1188,12 +1210,12 @@ impl WeightInfo for () { /// The range of component `i` is `[0, 5900]`. fn deprecate_controller_batch(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1746 + i * (229 ±0)` + // Measured: `1779 + i * (229 ±0)` // Estimated: `990 + i * (7132 ±0)` - // Minimum execution time: 5_300_000 picoseconds. - Weight::from_parts(5_437_000, 990) - // Standard Error: 66_261 - .saturating_add(Weight::from_parts(30_172_457, 0).saturating_mul(i.into())) + // Minimum execution time: 5_101_000 picoseconds. + Weight::from_parts(5_368_000, 990) + // Standard Error: 75_180 + .saturating_add(Weight::from_parts(33_781_643, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads((4_u64).saturating_mul(i.into()))) .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(i.into()))) .saturating_add(Weight::from_parts(0, 7132).saturating_mul(i.into())) @@ -1204,10 +1226,10 @@ impl WeightInfo for () { /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::Ledger` (r:1 w:1) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `Staking::VirtualStakers` (r:1 w:1) + /// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `Staking::Validators` (r:1 w:0) @@ -1229,14 +1251,14 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 100]`. fn force_unstake(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2196 + s * (4 ±0)` + // Measured: `2255 + s * (4 ±0)` // Estimated: `6248 + s * (4 ±0)` - // Minimum execution time: 87_677_000 picoseconds. - Weight::from_parts(96_386_462, 6248) - // Standard Error: 3_717 - .saturating_add(Weight::from_parts(1_370_585, 0).saturating_mul(s.into())) + // Minimum execution time: 119_955_000 picoseconds. + Weight::from_parts(128_392_032, 6248) + // Standard Error: 3_773 + .saturating_add(Weight::from_parts(1_302_488, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(13_u64)) - .saturating_add(RocksDbWeight::get().writes(12_u64)) + .saturating_add(RocksDbWeight::get().writes(13_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) .saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into())) } @@ -1245,12 +1267,12 @@ impl WeightInfo for () { /// The range of component `s` is `[1, 1000]`. fn cancel_deferred_slash(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `66672` - // Estimated: `70137` - // Minimum execution time: 105_086_000 picoseconds. - Weight::from_parts(1_167_895_222, 70137) - // Standard Error: 77_022 - .saturating_add(Weight::from_parts(6_487_305, 0).saturating_mul(s.into())) + // Measured: `66705` + // Estimated: `70170` + // Minimum execution time: 139_290_000 picoseconds. + Weight::from_parts(959_667_494, 70170) + // Standard Error: 56_271 + .saturating_add(Weight::from_parts(4_798_293, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1268,12 +1290,10 @@ impl WeightInfo for () { /// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `Staking::ErasValidatorReward` (r:1 w:0) /// Proof: `Staking::ErasValidatorReward` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) - /// Storage: `Balances::Locks` (r:257 w:257) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:257 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:257 w:257) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Staking::VirtualStakers` (r:257 w:0) + /// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:257 w:257) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`) /// Storage: `Staking::ErasStakersPaged` (r:1 w:0) /// Proof: `Staking::ErasStakersPaged` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Staking::ErasRewardPoints` (r:1 w:0) @@ -1282,29 +1302,31 @@ impl WeightInfo for () { /// Proof: `Staking::ErasValidatorPrefs` (`max_values`: None, `max_size`: Some(57), added: 2532, mode: `MaxEncodedLen`) /// Storage: `Staking::Payee` (r:257 w:0) /// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:257 w:257) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 256]`. fn payout_stakers_alive_staked(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `33297 + n * (377 ±0)` - // Estimated: `30944 + n * (3774 ±3)` - // Minimum execution time: 154_210_000 picoseconds. - Weight::from_parts(192_836_012, 30944) - // Standard Error: 40_441 - .saturating_add(Weight::from_parts(47_646_642, 0).saturating_mul(n.into())) + // Measured: `33283 + n * (370 ±0)` + // Estimated: `30958 + n * (3566 ±0)` + // Minimum execution time: 193_068_000 picoseconds. + Weight::from_parts(252_762_568, 30958) + // Standard Error: 22_743 + .saturating_add(Weight::from_parts(81_185_306, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(14_u64)) .saturating_add(RocksDbWeight::get().reads((6_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 3774).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 3566).saturating_mul(n.into())) } /// Storage: `Staking::Ledger` (r:1 w:1) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) /// Storage: `Staking::Bonded` (r:1 w:0) /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `Staking::VirtualStakers` (r:1 w:0) + /// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:0) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`) /// Storage: `VoterList::ListNodes` (r:3 w:3) /// Proof: `VoterList::ListNodes` (`max_values`: None, `max_size`: Some(154), added: 2629, mode: `MaxEncodedLen`) /// Storage: `VoterList::ListBags` (r:2 w:2) @@ -1312,25 +1334,25 @@ impl WeightInfo for () { /// The range of component `l` is `[1, 32]`. fn rebond(l: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1991 + l * (7 ±0)` + // Measured: `1947 + l * (7 ±0)` // Estimated: `8877` - // Minimum execution time: 88_337_000 picoseconds. - Weight::from_parts(91_391_254, 8877) - // Standard Error: 4_485 - .saturating_add(Weight::from_parts(103_443, 0).saturating_mul(l.into())) + // Minimum execution time: 91_151_000 picoseconds. + Weight::from_parts(93_596_096, 8877) + // Standard Error: 5_313 + .saturating_add(Weight::from_parts(124_684, 0).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(9_u64)) - .saturating_add(RocksDbWeight::get().writes(7_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) } + /// Storage: `Staking::VirtualStakers` (r:1 w:1) + /// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) /// Storage: `Staking::Bonded` (r:1 w:1) /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::Ledger` (r:1 w:1) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) /// Storage: `Staking::SlashingSpans` (r:1 w:1) /// Proof: `Staking::SlashingSpans` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) - /// Storage: `Balances::Freezes` (r:1 w:0) - /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`) /// Storage: `Staking::Validators` (r:1 w:0) /// Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) /// Storage: `Staking::Nominators` (r:1 w:1) @@ -1350,14 +1372,14 @@ impl WeightInfo for () { /// The range of component `s` is `[1, 100]`. fn reap_stash(s: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `2196 + s * (4 ±0)` + // Measured: `2255 + s * (4 ±0)` // Estimated: `6248 + s * (4 ±0)` - // Minimum execution time: 98_014_000 picoseconds. - Weight::from_parts(102_537_670, 6248) - // Standard Error: 3_324 - .saturating_add(Weight::from_parts(1_353_142, 0).saturating_mul(s.into())) + // Minimum execution time: 133_214_000 picoseconds. + Weight::from_parts(137_290_527, 6248) + // Standard Error: 4_153 + .saturating_add(Weight::from_parts(1_291_007, 0).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(12_u64)) - .saturating_add(RocksDbWeight::get().writes(11_u64)) + .saturating_add(RocksDbWeight::get().writes(12_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) .saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into())) } @@ -1401,12 +1423,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0 + n * (720 ±0) + v * (3598 ±0)` // Estimated: `512390 + n * (3566 ±0) + v * (3566 ±0)` - // Minimum execution time: 608_575_000 picoseconds. - Weight::from_parts(613_663_000, 512390) - // Standard Error: 2_286_521 - .saturating_add(Weight::from_parts(72_108_001, 0).saturating_mul(v.into())) - // Standard Error: 227_839 - .saturating_add(Weight::from_parts(20_314_085, 0).saturating_mul(n.into())) + // Minimum execution time: 692_301_000 picoseconds. + Weight::from_parts(708_732_000, 512390) + // Standard Error: 2_117_299 + .saturating_add(Weight::from_parts(70_087_600, 0).saturating_mul(v.into())) + // Standard Error: 210_977 + .saturating_add(Weight::from_parts(22_953_405, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(206_u64)) .saturating_add(RocksDbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(RocksDbWeight::get().reads((4_u64).saturating_mul(n.into()))) @@ -1435,14 +1457,14 @@ impl WeightInfo for () { /// The range of component `n` is `[500, 1000]`. fn get_npos_voters(v: u32, n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `3175 + n * (911 ±0) + v * (395 ±0)` + // Measured: `3241 + n * (911 ±0) + v * (395 ±0)` // Estimated: `512390 + n * (3566 ±0) + v * (3566 ±0)` - // Minimum execution time: 37_173_756_000 picoseconds. - Weight::from_parts(37_488_937_000, 512390) - // Standard Error: 467_413 - .saturating_add(Weight::from_parts(8_086_367, 0).saturating_mul(v.into())) - // Standard Error: 467_413 - .saturating_add(Weight::from_parts(3_108_193, 0).saturating_mul(n.into())) + // Minimum execution time: 43_708_472_000 picoseconds. + Weight::from_parts(44_048_436_000, 512390) + // Standard Error: 493_244 + .saturating_add(Weight::from_parts(6_697_278, 0).saturating_mul(v.into())) + // Standard Error: 493_244 + .saturating_add(Weight::from_parts(4_559_779, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(201_u64)) .saturating_add(RocksDbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(RocksDbWeight::get().reads((4_u64).saturating_mul(n.into()))) @@ -1457,12 +1479,12 @@ impl WeightInfo for () { /// The range of component `v` is `[500, 1000]`. fn get_npos_targets(v: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `979 + v * (50 ±0)` + // Measured: `1012 + v * (50 ±0)` // Estimated: `3510 + v * (2520 ±0)` - // Minimum execution time: 2_641_258_000 picoseconds. - Weight::from_parts(382_882_595, 3510) - // Standard Error: 11_991 - .saturating_add(Weight::from_parts(4_695_820, 0).saturating_mul(v.into())) + // Minimum execution time: 2_917_165_000 picoseconds. + Weight::from_parts(2_948_999_000, 3510) + // Standard Error: 33_372 + .saturating_add(Weight::from_parts(2_126_909, 0).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(v.into()))) .saturating_add(Weight::from_parts(0, 2520).saturating_mul(v.into())) @@ -1485,8 +1507,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_753_000 picoseconds. - Weight::from_parts(6_529_000, 0) + // Minimum execution time: 4_748_000 picoseconds. + Weight::from_parts(5_052_000, 0) .saturating_add(RocksDbWeight::get().writes(7_u64)) } /// Storage: `Staking::MinCommission` (r:0 w:1) @@ -1507,8 +1529,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_212_000 picoseconds. - Weight::from_parts(5_451_000, 0) + // Minimum execution time: 4_316_000 picoseconds. + Weight::from_parts(4_526_000, 0) .saturating_add(RocksDbWeight::get().writes(7_u64)) } /// Storage: `Staking::Bonded` (r:1 w:0) @@ -1535,10 +1557,10 @@ impl WeightInfo for () { /// Proof: `VoterList::CounterForListNodes` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn chill_other() -> Weight { // Proof Size summary in bytes: - // Measured: `1939` + // Measured: `2005` // Estimated: `6248` - // Minimum execution time: 73_000_000 picoseconds. - Weight::from_parts(75_184_000, 6248) + // Minimum execution time: 87_374_000 picoseconds. + Weight::from_parts(89_848_000, 6248) .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -1548,10 +1570,10 @@ impl WeightInfo for () { /// Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) fn force_apply_min_commission() -> Weight { // Proof Size summary in bytes: - // Measured: `691` + // Measured: `724` // Estimated: `3510` - // Minimum execution time: 13_056_000 picoseconds. - Weight::from_parts(13_517_000, 3510) + // Minimum execution time: 15_529_000 picoseconds. + Weight::from_parts(16_094_000, 3510) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1561,27 +1583,50 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_201_000 picoseconds. - Weight::from_parts(3_442_000, 0) + // Minimum execution time: 2_533_000 picoseconds. + Weight::from_parts(2_817_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: `Balances::Locks` (r:1 w:1) - /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) + /// Storage: `Staking::VirtualStakers` (r:1 w:0) + /// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:0) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:0) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) /// Storage: `Staking::Bonded` (r:1 w:1) /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `Staking::Ledger` (r:1 w:1) /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + fn restore_ledger() -> Weight { + // Proof Size summary in bytes: + // Measured: `1110` + // Estimated: `4764` + // Minimum execution time: 50_105_000 picoseconds. + Weight::from_parts(50_966_000, 4764) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: `Staking::VirtualStakers` (r:1 w:0) + /// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Staking::Bonded` (r:1 w:0) + /// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `Staking::Ledger` (r:1 w:0) + /// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`) /// Storage: `Balances::Freezes` (r:1 w:0) /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`) - fn restore_ledger() -> Weight { + fn migrate_currency() -> Weight { // Proof Size summary in bytes: - // Measured: `1047` + // Measured: `1246` // Estimated: `4764` - // Minimum execution time: 44_671_000 picoseconds. - Weight::from_parts(45_611_000, 4764) - .saturating_add(RocksDbWeight::get().reads(5_u64)) - .saturating_add(RocksDbWeight::get().writes(4_u64)) + // Minimum execution time: 94_054_000 picoseconds. + Weight::from_parts(96_272_000, 4764) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } } \ No newline at end of file diff --git a/substrate/frame/support/src/traits.rs b/substrate/frame/support/src/traits.rs index 728426cc84c71087015cea70dd5a23a50d49c982..4a83c809a6a5ece39a9818f7111c92e6e4b7b100 100644 --- a/substrate/frame/support/src/traits.rs +++ b/substrate/frame/support/src/traits.rs @@ -96,8 +96,9 @@ mod storage; #[cfg(feature = "experimental")] pub use storage::MaybeConsideration; pub use storage::{ - Consideration, Footprint, Incrementable, Instance, LinearStoragePrice, PartialStorageInfoTrait, - StorageInfo, StorageInfoTrait, StorageInstance, TrackedStorageKey, WhitelistedStorageKeys, + Consideration, ConstantStoragePrice, Footprint, Incrementable, Instance, LinearStoragePrice, + PartialStorageInfoTrait, StorageInfo, StorageInfoTrait, StorageInstance, TrackedStorageKey, + WhitelistedStorageKeys, }; mod dispatch; diff --git a/substrate/frame/support/src/traits/storage.rs b/substrate/frame/support/src/traits/storage.rs index 2b8e437073894cc03634bbd950313f541b59fe76..676b73e03d3c4472ebdb1ab024b6c9accb430ac1 100644 --- a/substrate/frame/support/src/traits/storage.rs +++ b/substrate/frame/support/src/traits/storage.rs @@ -200,6 +200,18 @@ where } } +/// Constant `Price` regardless of the given [`Footprint`]. +pub struct ConstantStoragePrice<Price, Balance>(PhantomData<(Price, Balance)>); +impl<Price, Balance> Convert<Footprint, Balance> for ConstantStoragePrice<Price, Balance> +where + Price: Get<Balance>, + Balance: From<u64> + sp_runtime::Saturating, +{ + fn convert(_: Footprint) -> Balance { + Price::get() + } +} + /// Some sort of cost taken from account temporarily in order to offset the cost to the chain of /// holding some data [`Footprint`] in state. /// diff --git a/substrate/frame/support/test/stg_frame_crate/Cargo.toml b/substrate/frame/support/test/stg_frame_crate/Cargo.toml index f627d29cd563067cdad9b48e4dc5a7ac9bdefbd8..157361dbd5d6dc1df062ebd2e4632da830b7362b 100644 --- a/substrate/frame/support/test/stg_frame_crate/Cargo.toml +++ b/substrate/frame/support/test/stg_frame_crate/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { features = ["derive"], workspace = true } -frame = { features = ["experimental", "runtime"], workspace = true } +frame = { features = ["runtime"], workspace = true } scale-info = { features = ["derive"], workspace = true } [features] diff --git a/substrate/frame/transaction-payment/asset-tx-payment/src/lib.rs b/substrate/frame/transaction-payment/asset-tx-payment/src/lib.rs index dd752989c3662d2123c9d7783786bcb3d9de80d2..4a96cbcacb5804541aa6130702869b05cbd32637 100644 --- a/substrate/frame/transaction-payment/asset-tx-payment/src/lib.rs +++ b/substrate/frame/transaction-payment/asset-tx-payment/src/lib.rs @@ -202,7 +202,7 @@ where debug_assert!(self.tip <= fee, "tip should be included in the computed fee"); if fee.is_zero() { Ok((fee, InitialPayment::Nothing)) - } else if let Some(asset_id) = self.asset_id { + } else if let Some(asset_id) = self.asset_id.clone() { T::OnChargeAssetTransaction::withdraw_fee( who, call, @@ -233,7 +233,7 @@ where debug_assert!(self.tip <= fee, "tip should be included in the computed fee"); if fee.is_zero() { Ok(()) - } else if let Some(asset_id) = self.asset_id { + } else if let Some(asset_id) = self.asset_id.clone() { T::OnChargeAssetTransaction::can_withdraw_fee( who, call, @@ -358,7 +358,7 @@ where tip, who, initial_payment, - asset_id: self.asset_id, + asset_id: self.asset_id.clone(), weight: self.weight(call), }) }, diff --git a/substrate/frame/transaction-payment/asset-tx-payment/src/payment.rs b/substrate/frame/transaction-payment/asset-tx-payment/src/payment.rs index 2074b1476f45ae9187778a3c846e0b21a1097176..7b7ae855bf8f34bea492b98a912c2094fdd8343d 100644 --- a/substrate/frame/transaction-payment/asset-tx-payment/src/payment.rs +++ b/substrate/frame/transaction-payment/asset-tx-payment/src/payment.rs @@ -40,7 +40,7 @@ pub trait OnChargeAssetTransaction<T: Config> { /// The underlying integer type in which fees are calculated. type Balance: Balance; /// The type used to identify the assets used for transaction payment. - type AssetId: FullCodec + Copy + MaybeSerializeDeserialize + Debug + Default + Eq + TypeInfo; + type AssetId: FullCodec + Clone + MaybeSerializeDeserialize + Debug + Default + Eq + TypeInfo; /// The type used to store the intermediate values between pre- and post-dispatch. type LiquidityInfo; @@ -112,7 +112,7 @@ where T: Config, CON: ConversionToAssetBalance<BalanceOf<T>, AssetIdOf<T>, AssetBalanceOf<T>>, HC: HandleCredit<T::AccountId, T::Fungibles>, - AssetIdOf<T>: FullCodec + Copy + MaybeSerializeDeserialize + Debug + Default + Eq + TypeInfo, + AssetIdOf<T>: FullCodec + Clone + MaybeSerializeDeserialize + Debug + Default + Eq + TypeInfo, { type Balance = BalanceOf<T>; type AssetId = AssetIdOf<T>; @@ -133,11 +133,14 @@ where // less than one (e.g. 0.5) but gets rounded down by integer division we introduce a minimum // fee. let min_converted_fee = if fee.is_zero() { Zero::zero() } else { One::one() }; - let converted_fee = CON::to_asset_balance(fee, asset_id) + let converted_fee = CON::to_asset_balance(fee, asset_id.clone()) .map_err(|_| TransactionValidityError::from(InvalidTransaction::Payment))? .max(min_converted_fee); - let can_withdraw = - <T::Fungibles as Inspect<T::AccountId>>::can_withdraw(asset_id, who, converted_fee); + let can_withdraw = <T::Fungibles as Inspect<T::AccountId>>::can_withdraw( + asset_id.clone(), + who, + converted_fee, + ); if can_withdraw != WithdrawConsequence::Success { return Err(InvalidTransaction::Payment.into()) } @@ -167,7 +170,7 @@ where // less than one (e.g. 0.5) but gets rounded down by integer division we introduce a minimum // fee. let min_converted_fee = if fee.is_zero() { Zero::zero() } else { One::one() }; - let converted_fee = CON::to_asset_balance(fee, asset_id) + let converted_fee = CON::to_asset_balance(fee, asset_id.clone()) .map_err(|_| TransactionValidityError::from(InvalidTransaction::Payment))? .max(min_converted_fee); let can_withdraw = diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index 17010a8907fc23325d2726e59285313f5fea53a6..8e23c6800a9d53a240261b51a7f87dd20017b9e0 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -325,7 +325,7 @@ pub trait StakingUnchecked: StakingInterface { /// Migrate an existing staker to a virtual staker. /// /// It would release all funds held by the implementation pallet. - fn migrate_to_virtual_staker(who: &Self::AccountId); + fn migrate_to_virtual_staker(who: &Self::AccountId) -> DispatchResult; /// Book-keep a new bond for `keyless_who` without applying any locks (hence virtual). /// diff --git a/umbrella/Cargo.toml b/umbrella/Cargo.toml index 17a7c02e82592c5765f7290183e8ee6711e44aff..fc0b2d5a140ed18419a3cece320f03f6fab8b711 100644 --- a/umbrella/Cargo.toml +++ b/umbrella/Cargo.toml @@ -57,6 +57,7 @@ std = [ "pallet-asset-conversion-tx-payment?/std", "pallet-asset-conversion?/std", "pallet-asset-rate?/std", + "pallet-asset-rewards?/std", "pallet-asset-tx-payment?/std", "pallet-assets-freezer?/std", "pallet-assets?/std", @@ -256,6 +257,7 @@ runtime-benchmarks = [ "pallet-asset-conversion-tx-payment?/runtime-benchmarks", "pallet-asset-conversion?/runtime-benchmarks", "pallet-asset-rate?/runtime-benchmarks", + "pallet-asset-rewards?/runtime-benchmarks", "pallet-asset-tx-payment?/runtime-benchmarks", "pallet-assets-freezer?/runtime-benchmarks", "pallet-assets?/runtime-benchmarks", @@ -386,6 +388,7 @@ try-runtime = [ "pallet-asset-conversion-tx-payment?/try-runtime", "pallet-asset-conversion?/try-runtime", "pallet-asset-rate?/try-runtime", + "pallet-asset-rewards?/try-runtime", "pallet-asset-tx-payment?/try-runtime", "pallet-assets-freezer?/try-runtime", "pallet-assets?/try-runtime", @@ -543,7 +546,7 @@ with-tracing = [ "sp-tracing?/with-tracing", "sp-tracing?/with-tracing", ] -runtime-full = ["assets-common", "binary-merkle-tree", "bp-header-chain", "bp-messages", "bp-parachains", "bp-polkadot", "bp-polkadot-core", "bp-relayers", "bp-runtime", "bp-test-utils", "bp-xcm-bridge-hub", "bp-xcm-bridge-hub-router", "bridge-hub-common", "bridge-runtime-common", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", "cumulus-pallet-parachain-system-proc-macro", "cumulus-pallet-session-benchmarking", "cumulus-pallet-solo-to-para", "cumulus-pallet-weight-reclaim", "cumulus-pallet-xcm", "cumulus-pallet-xcmp-queue", "cumulus-ping", "cumulus-primitives-aura", "cumulus-primitives-core", "cumulus-primitives-parachain-inherent", "cumulus-primitives-proof-size-hostfunction", "cumulus-primitives-storage-weight-reclaim", "cumulus-primitives-timestamp", "cumulus-primitives-utility", "frame-benchmarking", "frame-benchmarking-pallet-pov", "frame-election-provider-solution-type", "frame-election-provider-support", "frame-executive", "frame-metadata-hash-extension", "frame-support", "frame-support-procedural", "frame-support-procedural-tools-derive", "frame-system", "frame-system-benchmarking", "frame-system-rpc-runtime-api", "frame-try-runtime", "pallet-alliance", "pallet-asset-conversion", "pallet-asset-conversion-ops", "pallet-asset-conversion-tx-payment", "pallet-asset-rate", "pallet-asset-tx-payment", "pallet-assets", "pallet-assets-freezer", "pallet-atomic-swap", "pallet-aura", "pallet-authority-discovery", "pallet-authorship", "pallet-babe", "pallet-bags-list", "pallet-balances", "pallet-beefy", "pallet-beefy-mmr", "pallet-bounties", "pallet-bridge-grandpa", "pallet-bridge-messages", "pallet-bridge-parachains", "pallet-bridge-relayers", "pallet-broker", "pallet-child-bounties", "pallet-collator-selection", "pallet-collective", "pallet-collective-content", "pallet-contracts", "pallet-contracts-proc-macro", "pallet-contracts-uapi", "pallet-conviction-voting", "pallet-core-fellowship", "pallet-delegated-staking", "pallet-democracy", "pallet-dev-mode", "pallet-election-provider-multi-phase", "pallet-election-provider-support-benchmarking", "pallet-elections-phragmen", "pallet-fast-unstake", "pallet-glutton", "pallet-grandpa", "pallet-identity", "pallet-im-online", "pallet-indices", "pallet-insecure-randomness-collective-flip", "pallet-lottery", "pallet-membership", "pallet-message-queue", "pallet-migrations", "pallet-mixnet", "pallet-mmr", "pallet-multisig", "pallet-nft-fractionalization", "pallet-nfts", "pallet-nfts-runtime-api", "pallet-nis", "pallet-node-authorization", "pallet-nomination-pools", "pallet-nomination-pools-benchmarking", "pallet-nomination-pools-runtime-api", "pallet-offences", "pallet-offences-benchmarking", "pallet-paged-list", "pallet-parameters", "pallet-preimage", "pallet-proxy", "pallet-ranked-collective", "pallet-recovery", "pallet-referenda", "pallet-remark", "pallet-revive", "pallet-revive-proc-macro", "pallet-revive-uapi", "pallet-root-offences", "pallet-root-testing", "pallet-safe-mode", "pallet-salary", "pallet-scheduler", "pallet-scored-pool", "pallet-session", "pallet-session-benchmarking", "pallet-skip-feeless-payment", "pallet-society", "pallet-staking", "pallet-staking-reward-curve", "pallet-staking-reward-fn", "pallet-staking-runtime-api", "pallet-state-trie-migration", "pallet-statement", "pallet-sudo", "pallet-timestamp", "pallet-tips", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", "pallet-transaction-storage", "pallet-treasury", "pallet-tx-pause", "pallet-uniques", "pallet-utility", "pallet-verify-signature", "pallet-vesting", "pallet-whitelist", "pallet-xcm", "pallet-xcm-benchmarks", "pallet-xcm-bridge-hub", "pallet-xcm-bridge-hub-router", "parachains-common", "polkadot-core-primitives", "polkadot-parachain-primitives", "polkadot-primitives", "polkadot-runtime-common", "polkadot-runtime-metrics", "polkadot-runtime-parachains", "polkadot-sdk-frame", "sc-chain-spec-derive", "sc-tracing-proc-macro", "slot-range-helper", "snowbridge-beacon-primitives", "snowbridge-core", "snowbridge-ethereum", "snowbridge-outbound-queue-merkle-tree", "snowbridge-outbound-queue-runtime-api", "snowbridge-pallet-ethereum-client", "snowbridge-pallet-ethereum-client-fixtures", "snowbridge-pallet-inbound-queue", "snowbridge-pallet-inbound-queue-fixtures", "snowbridge-pallet-outbound-queue", "snowbridge-pallet-system", "snowbridge-router-primitives", "snowbridge-runtime-common", "snowbridge-system-runtime-api", "sp-api", "sp-api-proc-macro", "sp-application-crypto", "sp-arithmetic", "sp-authority-discovery", "sp-block-builder", "sp-consensus-aura", "sp-consensus-babe", "sp-consensus-beefy", "sp-consensus-grandpa", "sp-consensus-pow", "sp-consensus-slots", "sp-core", "sp-crypto-ec-utils", "sp-crypto-hashing", "sp-crypto-hashing-proc-macro", "sp-debug-derive", "sp-externalities", "sp-genesis-builder", "sp-inherents", "sp-io", "sp-keyring", "sp-keystore", "sp-metadata-ir", "sp-mixnet", "sp-mmr-primitives", "sp-npos-elections", "sp-offchain", "sp-runtime", "sp-runtime-interface", "sp-runtime-interface-proc-macro", "sp-session", "sp-staking", "sp-state-machine", "sp-statement-store", "sp-std", "sp-storage", "sp-timestamp", "sp-tracing", "sp-transaction-pool", "sp-transaction-storage-proof", "sp-trie", "sp-version", "sp-version-proc-macro", "sp-wasm-interface", "sp-weights", "staging-parachain-info", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", "substrate-bip39", "testnet-parachains-constants", "tracing-gum-proc-macro", "xcm-procedural", "xcm-runtime-apis"] +runtime-full = ["assets-common", "binary-merkle-tree", "bp-header-chain", "bp-messages", "bp-parachains", "bp-polkadot", "bp-polkadot-core", "bp-relayers", "bp-runtime", "bp-test-utils", "bp-xcm-bridge-hub", "bp-xcm-bridge-hub-router", "bridge-hub-common", "bridge-runtime-common", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", "cumulus-pallet-parachain-system-proc-macro", "cumulus-pallet-session-benchmarking", "cumulus-pallet-solo-to-para", "cumulus-pallet-weight-reclaim", "cumulus-pallet-xcm", "cumulus-pallet-xcmp-queue", "cumulus-ping", "cumulus-primitives-aura", "cumulus-primitives-core", "cumulus-primitives-parachain-inherent", "cumulus-primitives-proof-size-hostfunction", "cumulus-primitives-storage-weight-reclaim", "cumulus-primitives-timestamp", "cumulus-primitives-utility", "frame-benchmarking", "frame-benchmarking-pallet-pov", "frame-election-provider-solution-type", "frame-election-provider-support", "frame-executive", "frame-metadata-hash-extension", "frame-support", "frame-support-procedural", "frame-support-procedural-tools-derive", "frame-system", "frame-system-benchmarking", "frame-system-rpc-runtime-api", "frame-try-runtime", "pallet-alliance", "pallet-asset-conversion", "pallet-asset-conversion-ops", "pallet-asset-conversion-tx-payment", "pallet-asset-rate", "pallet-asset-rewards", "pallet-asset-tx-payment", "pallet-assets", "pallet-assets-freezer", "pallet-atomic-swap", "pallet-aura", "pallet-authority-discovery", "pallet-authorship", "pallet-babe", "pallet-bags-list", "pallet-balances", "pallet-beefy", "pallet-beefy-mmr", "pallet-bounties", "pallet-bridge-grandpa", "pallet-bridge-messages", "pallet-bridge-parachains", "pallet-bridge-relayers", "pallet-broker", "pallet-child-bounties", "pallet-collator-selection", "pallet-collective", "pallet-collective-content", "pallet-contracts", "pallet-contracts-proc-macro", "pallet-contracts-uapi", "pallet-conviction-voting", "pallet-core-fellowship", "pallet-delegated-staking", "pallet-democracy", "pallet-dev-mode", "pallet-election-provider-multi-phase", "pallet-election-provider-support-benchmarking", "pallet-elections-phragmen", "pallet-fast-unstake", "pallet-glutton", "pallet-grandpa", "pallet-identity", "pallet-im-online", "pallet-indices", "pallet-insecure-randomness-collective-flip", "pallet-lottery", "pallet-membership", "pallet-message-queue", "pallet-migrations", "pallet-mixnet", "pallet-mmr", "pallet-multisig", "pallet-nft-fractionalization", "pallet-nfts", "pallet-nfts-runtime-api", "pallet-nis", "pallet-node-authorization", "pallet-nomination-pools", "pallet-nomination-pools-benchmarking", "pallet-nomination-pools-runtime-api", "pallet-offences", "pallet-offences-benchmarking", "pallet-paged-list", "pallet-parameters", "pallet-preimage", "pallet-proxy", "pallet-ranked-collective", "pallet-recovery", "pallet-referenda", "pallet-remark", "pallet-revive", "pallet-revive-proc-macro", "pallet-revive-uapi", "pallet-root-offences", "pallet-root-testing", "pallet-safe-mode", "pallet-salary", "pallet-scheduler", "pallet-scored-pool", "pallet-session", "pallet-session-benchmarking", "pallet-skip-feeless-payment", "pallet-society", "pallet-staking", "pallet-staking-reward-curve", "pallet-staking-reward-fn", "pallet-staking-runtime-api", "pallet-state-trie-migration", "pallet-statement", "pallet-sudo", "pallet-timestamp", "pallet-tips", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", "pallet-transaction-storage", "pallet-treasury", "pallet-tx-pause", "pallet-uniques", "pallet-utility", "pallet-verify-signature", "pallet-vesting", "pallet-whitelist", "pallet-xcm", "pallet-xcm-benchmarks", "pallet-xcm-bridge-hub", "pallet-xcm-bridge-hub-router", "parachains-common", "polkadot-core-primitives", "polkadot-parachain-primitives", "polkadot-primitives", "polkadot-runtime-common", "polkadot-runtime-metrics", "polkadot-runtime-parachains", "polkadot-sdk-frame", "sc-chain-spec-derive", "sc-tracing-proc-macro", "slot-range-helper", "snowbridge-beacon-primitives", "snowbridge-core", "snowbridge-ethereum", "snowbridge-outbound-queue-merkle-tree", "snowbridge-outbound-queue-runtime-api", "snowbridge-pallet-ethereum-client", "snowbridge-pallet-ethereum-client-fixtures", "snowbridge-pallet-inbound-queue", "snowbridge-pallet-inbound-queue-fixtures", "snowbridge-pallet-outbound-queue", "snowbridge-pallet-system", "snowbridge-router-primitives", "snowbridge-runtime-common", "snowbridge-system-runtime-api", "sp-api", "sp-api-proc-macro", "sp-application-crypto", "sp-arithmetic", "sp-authority-discovery", "sp-block-builder", "sp-consensus-aura", "sp-consensus-babe", "sp-consensus-beefy", "sp-consensus-grandpa", "sp-consensus-pow", "sp-consensus-slots", "sp-core", "sp-crypto-ec-utils", "sp-crypto-hashing", "sp-crypto-hashing-proc-macro", "sp-debug-derive", "sp-externalities", "sp-genesis-builder", "sp-inherents", "sp-io", "sp-keyring", "sp-keystore", "sp-metadata-ir", "sp-mixnet", "sp-mmr-primitives", "sp-npos-elections", "sp-offchain", "sp-runtime", "sp-runtime-interface", "sp-runtime-interface-proc-macro", "sp-session", "sp-staking", "sp-state-machine", "sp-statement-store", "sp-std", "sp-storage", "sp-timestamp", "sp-tracing", "sp-transaction-pool", "sp-transaction-storage-proof", "sp-trie", "sp-version", "sp-version-proc-macro", "sp-wasm-interface", "sp-weights", "staging-parachain-info", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", "substrate-bip39", "testnet-parachains-constants", "tracing-gum-proc-macro", "xcm-procedural", "xcm-runtime-apis"] runtime = [ "frame-benchmarking", "frame-benchmarking-pallet-pov", @@ -870,6 +873,11 @@ default-features = false optional = true path = "../substrate/frame/asset-rate" +[dependencies.pallet-asset-rewards] +default-features = false +optional = true +path = "../substrate/frame/asset-rewards" + [dependencies.pallet-asset-tx-payment] default-features = false optional = true diff --git a/umbrella/src/lib.rs b/umbrella/src/lib.rs index 3504f081f2957b5a9a8eccaea15343281ee98292..a132f16a2c33f617ee1a916deb585534d3e6f99c 100644 --- a/umbrella/src/lib.rs +++ b/umbrella/src/lib.rs @@ -312,6 +312,10 @@ pub use pallet_asset_conversion_tx_payment; #[cfg(feature = "pallet-asset-rate")] pub use pallet_asset_rate; +/// FRAME asset rewards pallet. +#[cfg(feature = "pallet-asset-rewards")] +pub use pallet_asset_rewards; + /// pallet to manage transaction payments in assets. #[cfg(feature = "pallet-asset-tx-payment")] pub use pallet_asset_tx_payment;