diff --git a/.github/scripts/generate-prdoc.py b/.github/scripts/generate-prdoc.py index 9154f185e64b9..43e8437a0c960 100644 --- a/.github/scripts/generate-prdoc.py +++ b/.github/scripts/generate-prdoc.py @@ -86,10 +86,10 @@ def create_prdoc(pr, audience, title, description, patch, bump, force): if p == '/': exit(1) p = os.path.dirname(p) - + with open(os.path.join(p, "Cargo.toml")) as f: manifest = toml.load(f) - + if not "package" in manifest: continue diff --git a/.github/workflows/build-publish-images.yml b/.github/workflows/build-publish-images.yml index 874b5d37469cd..deb3b3df5ff2f 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 0da3e54ef60b8..43c70d6abc78b 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 9734639531936..3431eadf70608 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

Please, see the new usage instructions here. Soon the old commands will be disabled.' - }) \ No newline at end of file + body: 'We have migrated the command bot to GHA

Please, see the new usage instructions here or here. Soon the old commands will be disabled.' + }) diff --git a/.github/workflows/misc-sync-templates.yml b/.github/workflows/misc-sync-templates.yml index 8d06d89621d78..ac66e697562b3 100644 --- a/.github/workflows/misc-sync-templates.yml +++ b/.github/workflows/misc-sync-templates.yml @@ -131,6 +131,12 @@ jobs: - name: Copy over the new changes run: | cp -r polkadot-sdk/templates/${{ matrix.template }}/* "${{ env.template-path }}/" + - name: Remove unnecessary files from parachain template + if: ${{ matrix.template == 'parachain' }} + run: | + rm -f "${{ env.template-path }}/README.docify.md" + rm -f "${{ env.template-path }}/Cargo.toml" + rm -f "${{ env.template-path }}/src/lib.rs" - name: Run psvm on monorepo workspace dependencies run: psvm -o -v ${{ github.event.inputs.stable_release_branch }} -p ./Cargo.toml diff --git a/.github/workflows/publish-check-compile.yml b/.github/workflows/publish-check-compile.yml index ce1b2cb231d0b..f20909106a820 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 3150cb9dd4050..c1b13243ba193 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 a6efc8a5599e4..804baf9ff06cf 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 3dd5b1114813d..27c6162a0fc20 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 0000000000000..8e938567d8118 --- /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 0000000000000..823679d67d5c0 --- /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 0000000000000..e6da1a49c4bb3 --- /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 0d71a770d38bf..397d0c7fe823a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -36,6 +36,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + [[package]] name = "adler32" version = "1.2.0" @@ -112,9 +118,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.0.4" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6748e8def348ed4d14996fa801f4122cd763fff530258cdc03f64b25f89d3a5a" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -363,23 +369,24 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.11" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.6" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" @@ -401,12 +408,12 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "3.0.1" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" dependencies = [ "anstyle", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -910,6 +917,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 +936,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 +987,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 +1073,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 +1125,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", @@ -1674,7 +1686,7 @@ dependencies = [ "cc", "cfg-if", "libc", - "miniz_oxide", + "miniz_oxide 0.7.1", "object 0.32.2", "rustc-demangle", ] @@ -3074,12 +3086,12 @@ dependencies = [ [[package]] name = "bstr" -version = "1.6.0" +version = "1.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" +checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" dependencies = [ "memchr", - "regex-automata 0.3.6", + "regex-automata 0.4.8", "serde", ] @@ -3182,9 +3194,9 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.3" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cfa25e60aea747ec7e1124f238816749faa93759c6ff5b31f1ccdda137f4479" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" dependencies = [ "serde", ] @@ -3197,7 +3209,7 @@ checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" dependencies = [ "camino", "cargo-platform", - "semver 1.0.18", + "semver 1.0.24", "serde", "serde_json", "thiserror", @@ -3482,12 +3494,12 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.13" +version = "4.5.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc" +checksum = "a8eb5e908ef3a6efbe1ed62520fb7287959888c88485abe072543190ecc66783" dependencies = [ "clap_builder", - "clap_derive 4.5.13", + "clap_derive 4.5.24", ] [[package]] @@ -3501,24 +3513,24 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.13" +version = "4.5.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64b17d7ea74e9f833c7dbf2cbe4fb12ff26783eda4782a8975b72f895c9b4d99" +checksum = "96b01801b5fc6a0a232407abc821660c9c6d25a1cafc0d4f85f29fb8d9afc121" dependencies = [ "anstream", "anstyle", - "clap_lex 0.7.0", + "clap_lex 0.7.4", "strsim 0.11.1", "terminal_size", ] [[package]] name = "clap_complete" -version = "4.5.13" +version = "4.5.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa3c596da3cf0983427b0df0dba359df9182c13bd5b519b585a482b0c351f4e8" +checksum = "33a7e468e750fa4b6be660e8b5651ad47372e8fb114030b594c2d75d48c5ffd0" dependencies = [ - "clap 4.5.13", + "clap 4.5.26", ] [[package]] @@ -3536,9 +3548,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.13" +version = "4.5.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c" dependencies = [ "heck 0.5.0", "proc-macro2 1.0.86", @@ -3557,9 +3569,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "cmd_lib" @@ -3745,23 +3757,23 @@ dependencies = [ [[package]] name = "color-print" -version = "0.3.4" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2a5e6504ed8648554968650feecea00557a3476bc040d0ffc33080e66b646d0" +checksum = "3aa954171903797d5623e047d9ab69d91b493657917bdfb8c2c80ecaf9cdb6f4" dependencies = [ "color-print-proc-macro", ] [[package]] name = "color-print-proc-macro" -version = "0.3.4" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51beaa537d73d2d1ff34ee70bc095f170420ab2ec5d687ecd3ec2b0d092514b" +checksum = "692186b5ebe54007e45a59aea47ece9eb4108e141326c304cdc91699a7118a22" dependencies = [ "nom", "proc-macro2 1.0.86", "quote 1.0.37", - "syn 1.0.109", + "syn 2.0.87", ] [[package]] @@ -4436,7 +4448,7 @@ dependencies = [ "anes", "cast", "ciborium", - "clap 4.5.13", + "clap 4.5.26", "criterion-plot", "futures", "is-terminal", @@ -4581,7 +4593,7 @@ dependencies = [ name = "cumulus-client-cli" version = "0.7.0" dependencies = [ - "clap 4.5.13", + "clap 4.5.26", "parity-scale-codec", "sc-chain-spec", "sc-cli", @@ -4635,6 +4647,8 @@ dependencies = [ "cumulus-primitives-aura 0.7.0", "cumulus-primitives-core 0.7.0", "cumulus-relay-chain-interface", + "cumulus-test-client", + "cumulus-test-relay-sproof-builder 0.7.0", "futures", "parity-scale-codec", "parking_lot 0.12.3", @@ -4659,6 +4673,7 @@ dependencies = [ "sp-consensus-aura 0.32.0", "sp-core 28.0.0", "sp-inherents 26.0.0", + "sp-keyring 31.0.0", "sp-keystore 0.34.0", "sp-runtime 31.0.1", "sp-state-machine 0.35.0", @@ -5242,7 +5257,7 @@ name = "cumulus-pov-validator" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.5.13", + "clap 4.5.26", "parity-scale-codec", "polkadot-node-primitives", "polkadot-parachain-primitives 6.0.0", @@ -5682,7 +5697,7 @@ name = "cumulus-test-service" version = "0.1.0" dependencies = [ "async-trait", - "clap 4.5.13", + "clap 4.5.26", "criterion", "cumulus-client-cli", "cumulus-client-collator", @@ -5776,9 +5791,9 @@ dependencies = [ [[package]] name = "curl-sys" -version = "0.4.72+curl-8.6.0" +version = "0.4.78+curl-8.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29cbdc8314c447d11e8fd156dcdd031d9e02a7a976163e396b548c03153bc9ea" +checksum = "8eec768341c5c7789611ae51cf6c459099f22e64a5d5d0ce4892434e33821eaf" dependencies = [ "cc", "libc", @@ -6930,14 +6945,14 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.22" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.3.5", - "windows-sys 0.48.0", + "libredox", + "windows-sys 0.59.0", ] [[package]] @@ -7014,12 +7029,12 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.27" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", - "miniz_oxide", + "miniz_oxide 0.8.2", ] [[package]] @@ -7172,7 +7187,7 @@ dependencies = [ "Inflector", "array-bytes", "chrono", - "clap 4.5.13", + "clap 4.5.26", "comfy-table", "cumulus-client-parachain-inherent", "cumulus-primitives-proof-size-hostfunction 0.2.0", @@ -7338,7 +7353,7 @@ dependencies = [ name = "frame-election-solution-type-fuzzer" version = "2.0.0-alpha.5" dependencies = [ - "clap 4.5.13", + "clap 4.5.26", "frame-election-provider-solution-type 13.0.0", "frame-election-provider-support 28.0.0", "frame-support 28.0.0", @@ -7471,7 +7486,7 @@ name = "frame-omni-bencher" version = "0.1.0" dependencies = [ "assert_cmd", - "clap 4.5.13", + "clap 4.5.26", "cumulus-primitives-proof-size-hostfunction 0.2.0", "cumulus-test-runtime", "frame-benchmarking-cli", @@ -9186,6 +9201,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "isahc" version = "1.7.2" @@ -10277,6 +10298,17 @@ dependencies = [ "yamux 0.13.3", ] +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.6.0", + "libc", + "redox_syscall 0.5.8", +] + [[package]] name = "librocksdb-sys" version = "0.11.0+8.1.1" @@ -10353,9 +10385,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.12" +version = "1.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b" +checksum = "df9b68e50e6e0b26f672573834882eb57759f6db9b3be2ea3c35c91188bb4eaa" dependencies = [ "cc", "libc", @@ -10824,7 +10856,7 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" name = "minimal-template-node" version = "0.0.0" dependencies = [ - "clap 4.5.13", + "clap 4.5.26", "docify", "futures", "futures-timer", @@ -10854,6 +10886,15 @@ dependencies = [ "adler", ] +[[package]] +name = "miniz_oxide" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" +dependencies = [ + "adler2", +] + [[package]] name = "mio" version = "1.0.2" @@ -11331,7 +11372,7 @@ version = "0.9.0-dev" dependencies = [ "array-bytes", "async-trait", - "clap 4.5.13", + "clap 4.5.26", "derive_more 0.99.17", "fs_extra", "futures", @@ -11407,7 +11448,7 @@ dependencies = [ name = "node-runtime-generate-bags" version = "3.0.0" dependencies = [ - "clap 4.5.13", + "clap 4.5.26", "generate-bags", "kitchensink-runtime", ] @@ -11416,7 +11457,7 @@ dependencies = [ name = "node-template-release" version = "3.0.0" dependencies = [ - "clap 4.5.13", + "clap 4.5.26", "flate2", "fs_extra", "glob", @@ -12036,6 +12077,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" @@ -12113,17 +12175,12 @@ dependencies = [ name = "pallet-assets-freezer" version = "0.1.0" dependencies = [ - "frame-benchmarking 28.0.0", - "frame-support 28.0.0", - "frame-system 28.0.0", "log", "pallet-assets 29.1.0", "pallet-balances 28.0.0", "parity-scale-codec", + "polkadot-sdk-frame 0.1.0", "scale-info", - "sp-core 28.0.0", - "sp-io 30.0.0", - "sp-runtime 31.0.1", ] [[package]] @@ -13343,6 +13400,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", @@ -14084,18 +14142,12 @@ dependencies = [ name = "pallet-mmr" version = "27.0.0" dependencies = [ - "array-bytes", - "frame-benchmarking 28.0.0", - "frame-support 28.0.0", - "frame-system 28.0.0", "itertools 0.11.0", "log", "parity-scale-codec", + "polkadot-sdk-frame 0.1.0", "scale-info", - "sp-core 28.0.0", - "sp-io 30.0.0", "sp-mmr-primitives 26.0.0", - "sp-runtime 31.0.1", "sp-tracing 16.0.0", ] @@ -14433,29 +14485,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" @@ -14835,6 +14864,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", @@ -14853,7 +14883,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", @@ -14908,7 +14938,7 @@ name = "pallet-revive-eth-rpc" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.5.13", + "clap 4.5.26", "env_logger 0.11.3", "ethabi", "futures", @@ -14942,7 +14972,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", @@ -15057,7 +15087,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", ] @@ -16244,11 +16274,18 @@ dependencies = [ "staging-xcm-builder 17.0.1", ] +[[package]] +name = "parachain-template" +version = "0.0.0" +dependencies = [ + "docify", +] + [[package]] name = "parachain-template-node" version = "0.0.0" dependencies = [ - "clap 4.5.13", + "clap 4.5.26", "color-print", "docify", "futures", @@ -17232,7 +17269,7 @@ name = "polkadot-cli" version = "7.0.0" dependencies = [ "cfg-if", - "clap 4.5.13", + "clap 4.5.26", "frame-benchmarking-cli", "futures", "log", @@ -18103,7 +18140,7 @@ version = "0.1.0" dependencies = [ "assert_cmd", "async-trait", - "clap 4.5.13", + "clap 4.5.26", "color-print", "cumulus-client-cli", "cumulus-client-collator", @@ -18715,6 +18752,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", @@ -19620,7 +19658,7 @@ dependencies = [ "async-trait", "bincode", "bitvec", - "clap 4.5.13", + "clap 4.5.26", "clap-num", "color-eyre", "colored", @@ -19722,7 +19760,7 @@ version = "1.0.0" dependencies = [ "assert_matches", "async-trait", - "clap 4.5.13", + "clap 4.5.26", "color-eyre", "futures", "futures-timer", @@ -19864,7 +19902,7 @@ dependencies = [ name = "polkadot-voter-bags" version = "7.0.0" dependencies = [ - "clap 4.5.13", + "clap 4.5.26", "generate-bags", "sp-io 30.0.0", "westend-runtime", @@ -19928,6 +19966,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" @@ -19955,6 +20006,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" @@ -19984,6 +20044,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" @@ -20011,6 +20081,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" @@ -20047,6 +20126,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" @@ -20077,6 +20168,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" @@ -20123,6 +20224,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" @@ -20141,6 +20258,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" @@ -21103,12 +21226,6 @@ dependencies = [ "regex-syntax 0.6.29", ] -[[package]] -name = "regex-automata" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69" - [[package]] name = "regex-automata" version = "0.4.8" @@ -21208,7 +21325,7 @@ dependencies = [ name = "remote-ext-tests-bags-list" version = "1.0.0" dependencies = [ - "clap 4.5.13", + "clap 4.5.26", "frame-system 28.0.0", "log", "pallet-bags-list-remote-tests", @@ -21821,7 +21938,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.18", + "semver 1.0.24", ] [[package]] @@ -22240,7 +22357,7 @@ name = "sc-chain-spec" version = "28.0.0" dependencies = [ "array-bytes", - "clap 4.5.13", + "clap 4.5.26", "docify", "log", "memmap2 0.9.3", @@ -22283,7 +22400,7 @@ version = "0.36.0" dependencies = [ "array-bytes", "chrono", - "clap 4.5.13", + "clap 4.5.26", "fdlimit", "futures", "futures-timer", @@ -23631,7 +23748,7 @@ dependencies = [ name = "sc-storage-monitor" version = "0.16.0" dependencies = [ - "clap 4.5.13", + "clap 4.5.26", "fs4", "log", "sp-core 28.0.0", @@ -24205,9 +24322,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.18" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" +checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" dependencies = [ "serde", ] @@ -25519,7 +25636,7 @@ dependencies = [ name = "solochain-template-node" version = "0.0.0" dependencies = [ - "clap 4.5.13", + "clap 4.5.26", "frame-benchmarking-cli", "frame-metadata-hash-extension 0.1.0", "frame-system 28.0.0", @@ -26902,7 +27019,7 @@ dependencies = [ name = "sp-npos-elections-fuzzer" version = "2.0.0-alpha.5" dependencies = [ - "clap 4.5.13", + "clap 4.5.26", "honggfuzz", "rand", "sp-npos-elections 26.0.0", @@ -28126,7 +28243,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" name = "staging-chain-spec-builder" version = "1.6.1" dependencies = [ - "clap 4.5.13", + "clap 4.5.26", "cmd_lib", "docify", "log", @@ -28143,7 +28260,7 @@ version = "3.0.0-dev" dependencies = [ "array-bytes", "assert_cmd", - "clap 4.5.13", + "clap 4.5.26", "clap_complete", "criterion", "futures", @@ -28180,7 +28297,7 @@ dependencies = [ name = "staging-node-inspect" version = "0.12.0" dependencies = [ - "clap 4.5.13", + "clap 4.5.26", "parity-scale-codec", "sc-cli", "sc-client-api", @@ -28525,7 +28642,7 @@ dependencies = [ name = "subkey" version = "9.0.0" dependencies = [ - "clap 4.5.13", + "clap 4.5.26", "sc-cli", ] @@ -28951,7 +29068,7 @@ dependencies = [ "rand", "reqwest 0.12.9", "scale-info", - "semver 1.0.18", + "semver 1.0.24", "serde", "serde_json", "sp-version 35.0.0", @@ -29364,9 +29481,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tar" -version = "0.4.40" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" +checksum = "c65998313f8e17d0d553d28f91a0df93e4dbbbf770279c7bc21ca0f09ea1a1f6" dependencies = [ "filetime", "libc", @@ -29414,12 +29531,12 @@ dependencies = [ [[package]] name = "terminal_size" -version = "0.3.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" +checksum = "5352447f921fda68cf61b4101566c0bdb5104eff6804d0678e5227580ab6a4e9" dependencies = [ "rustix 0.38.42", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -29466,7 +29583,7 @@ dependencies = [ name = "test-parachain-adder-collator" version = "1.0.0" dependencies = [ - "clap 4.5.13", + "clap 4.5.26", "futures", "futures-timer", "log", @@ -29513,7 +29630,7 @@ dependencies = [ name = "test-parachain-undying-collator" version = "1.0.0" dependencies = [ - "clap 4.5.13", + "clap 4.5.26", "futures", "futures-timer", "log", @@ -31809,11 +31926,13 @@ dependencies = [ [[package]] name = "xattr" -version = "1.0.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4686009f71ff3e5c4dbcf1a282d0a44db3f021ba69350cd42086b3e5f1c6985" +checksum = "e105d177a3871454f754b33bb0ee637ecaaac997446375fd3e5d43a2ed00c909" dependencies = [ "libc", + "linux-raw-sys 0.4.14", + "rustix 0.38.42", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index eb99b80e16faa..18c1dd2c68d2f 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", @@ -538,6 +538,7 @@ members = [ "templates/minimal/node", "templates/minimal/pallets/template", "templates/minimal/runtime", + "templates/parachain", "templates/parachain/node", "templates/parachain/pallets/template", "templates/parachain/runtime", @@ -893,6 +894,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/bin/runtime-common/src/integrity.rs b/bridges/bin/runtime-common/src/integrity.rs index 535f1a26e5e89..61dbf09109acc 100644 --- a/bridges/bin/runtime-common/src/integrity.rs +++ b/bridges/bin/runtime-common/src/integrity.rs @@ -21,11 +21,11 @@ use bp_header_chain::ChainWithGrandpa; use bp_messages::{ChainWithMessages, InboundLaneData, MessageNonce}; -use bp_runtime::Chain; +use bp_runtime::{AccountIdOf, Chain}; use codec::Encode; use frame_support::{storage::generator::StorageValue, traits::Get, weights::Weight}; use frame_system::limits; -use pallet_bridge_messages::WeightInfoExt as _; +use pallet_bridge_messages::{ThisChainOf, WeightInfoExt as _}; // Re-export to avoid include all dependencies everywhere. #[doc(hidden)] @@ -364,8 +364,11 @@ pub fn check_message_lane_weights< ); // check that weights allow us to receive delivery confirmations - let max_incoming_inbound_lane_data_proof_size = - InboundLaneData::<()>::encoded_size_hint_u32(this_chain_max_unrewarded_relayers as _); + let max_incoming_inbound_lane_data_proof_size = InboundLaneData::< + AccountIdOf>, + >::encoded_size_hint_u32( + this_chain_max_unrewarded_relayers as _ + ); pallet_bridge_messages::ensure_able_to_receive_confirmation::>( C::max_extrinsic_size(), C::max_extrinsic_weight(), diff --git a/bridges/modules/messages/src/lib.rs b/bridges/modules/messages/src/lib.rs index af14257db99c1..61763186cb021 100644 --- a/bridges/modules/messages/src/lib.rs +++ b/bridges/modules/messages/src/lib.rs @@ -230,8 +230,8 @@ pub mod pallet { // why do we need to know the weight of this (`receive_messages_proof`) call? Because // we may want to return some funds for not-dispatching (or partially dispatching) some // messages to the call origin (relayer). And this is done by returning actual weight - // from the call. But we only know dispatch weight of every messages. So to refund - // relayer because we have not dispatched Message, we need to: + // from the call. But we only know dispatch weight of every message. So to refund + // relayer because we have not dispatched message, we need to: // // ActualWeight = DeclaredWeight - Message.DispatchWeight // diff --git a/bridges/relays/lib-substrate-relay/src/error.rs b/bridges/relays/lib-substrate-relay/src/error.rs index 2ebd9130f3912..3a62f30838c75 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 { #[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 36de637f04c43..cdc94b9fae493 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 f49a245c4126f..eb5ac66d46db7 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/client/consensus/aura/Cargo.toml b/cumulus/client/consensus/aura/Cargo.toml index 7022309386455..8637133a5f5cb 100644 --- a/cumulus/client/consensus/aura/Cargo.toml +++ b/cumulus/client/consensus/aura/Cargo.toml @@ -59,6 +59,11 @@ polkadot-node-subsystem-util = { workspace = true, default-features = true } polkadot-overseer = { workspace = true, default-features = true } polkadot-primitives = { workspace = true, default-features = true } +[dev-dependencies] +cumulus-test-client = { workspace = true } +cumulus-test-relay-sproof-builder = { workspace = true } +sp-keyring = { workspace = true } + [features] # Allows collator to use full PoV size for block building full-pov-size = [] diff --git a/cumulus/client/consensus/aura/src/collators/mod.rs b/cumulus/client/consensus/aura/src/collators/mod.rs index 031fa963ba6ae..66c6086eaf9ee 100644 --- a/cumulus/client/consensus/aura/src/collators/mod.rs +++ b/cumulus/client/consensus/aura/src/collators/mod.rs @@ -179,12 +179,19 @@ where let authorities = runtime_api.authorities(parent_hash).ok()?; let author_pub = aura_internal::claim_slot::

(para_slot, &authorities, keystore).await?; - let Ok(Some(api_version)) = - runtime_api.api_version::>(parent_hash) - else { - return (parent_hash == included_block) - .then(|| SlotClaim::unchecked::

(author_pub, para_slot, timestamp)); - }; + // This function is typically called when we want to build block N. At that point, the + // unincluded segment in the runtime is unaware of the hash of block N-1. If the unincluded + // segment in the runtime is full, but block N-1 is the included block, the unincluded segment + // should have length 0 and we can build. Since the hash is not available to the runtime + // however, we need this extra check here. + if parent_hash == included_block { + return Some(SlotClaim::unchecked::

(author_pub, para_slot, timestamp)); + } + + let api_version = runtime_api + .api_version::>(parent_hash) + .ok() + .flatten()?; let slot = if api_version > 1 { relay_slot } else { para_slot }; @@ -243,3 +250,116 @@ where .max_by_key(|a| a.depth) .map(|parent| (included_block, parent)) } + +#[cfg(test)] +mod tests { + use crate::collators::can_build_upon; + use codec::Encode; + use cumulus_primitives_aura::Slot; + use cumulus_primitives_core::BlockT; + use cumulus_relay_chain_interface::PHash; + use cumulus_test_client::{ + runtime::{Block, Hash}, + Client, DefaultTestClientBuilderExt, InitBlockBuilder, TestClientBuilder, + TestClientBuilderExt, + }; + use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder; + use polkadot_primitives::HeadData; + use sc_consensus::{BlockImport, BlockImportParams, ForkChoiceStrategy}; + use sp_consensus::BlockOrigin; + use sp_keystore::{Keystore, KeystorePtr}; + use sp_timestamp::Timestamp; + use std::sync::Arc; + + async fn import_block>( + importer: &I, + block: Block, + origin: BlockOrigin, + import_as_best: bool, + ) { + let (header, body) = block.deconstruct(); + + let mut block_import_params = BlockImportParams::new(origin, header); + block_import_params.fork_choice = Some(ForkChoiceStrategy::Custom(import_as_best)); + block_import_params.body = Some(body); + importer.import_block(block_import_params).await.unwrap(); + } + + fn sproof_with_parent_by_hash(client: &Client, hash: PHash) -> RelayStateSproofBuilder { + let header = client.header(hash).ok().flatten().expect("No header for parent block"); + let included = HeadData(header.encode()); + let mut builder = RelayStateSproofBuilder::default(); + builder.para_id = cumulus_test_client::runtime::PARACHAIN_ID.into(); + builder.included_para_head = Some(included); + + builder + } + async fn build_and_import_block(client: &Client, included: Hash) -> Block { + let sproof = sproof_with_parent_by_hash(client, included); + + let block_builder = client.init_block_builder(None, sproof).block_builder; + + let block = block_builder.build().unwrap().block; + + let origin = BlockOrigin::NetworkInitialSync; + import_block(client, block.clone(), origin, true).await; + block + } + + fn set_up_components() -> (Arc, KeystorePtr) { + let keystore = Arc::new(sp_keystore::testing::MemoryKeystore::new()) as Arc<_>; + for key in sp_keyring::Sr25519Keyring::iter() { + Keystore::sr25519_generate_new( + &*keystore, + sp_application_crypto::key_types::AURA, + Some(&key.to_seed()), + ) + .expect("Can insert key into MemoryKeyStore"); + } + (Arc::new(TestClientBuilder::new().build()), keystore) + } + + /// This tests a special scenario where the unincluded segment in the runtime + /// is full. We are calling `can_build_upon`, passing the last built block as the + /// included one. In the runtime we will not find the hash of the included block in the + /// unincluded segment. The `can_build_upon` runtime API would therefore return `false`, but + /// we are ensuring on the node side that we are are always able to build on the included block. + #[tokio::test] + async fn test_can_build_upon() { + let (client, keystore) = set_up_components(); + + let genesis_hash = client.chain_info().genesis_hash; + let mut last_hash = genesis_hash; + + // Fill up the unincluded segment tracker in the runtime. + while can_build_upon::<_, _, sp_consensus_aura::sr25519::AuthorityPair>( + Slot::from(u64::MAX), + Slot::from(u64::MAX), + Timestamp::default(), + last_hash, + genesis_hash, + &*client, + &keystore, + ) + .await + .is_some() + { + let block = build_and_import_block(&client, genesis_hash).await; + last_hash = block.header().hash(); + } + + // Blocks were built with the genesis hash set as included block. + // We call `can_build_upon` with the last built block as the included block. + let result = can_build_upon::<_, _, sp_consensus_aura::sr25519::AuthorityPair>( + Slot::from(u64::MAX), + Slot::from(u64::MAX), + Timestamp::default(), + last_hash, + last_hash, + &*client, + &keystore, + ) + .await; + assert!(result.is_some()); + } +} 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 a164a8197f72e..c6a8baeff3b37 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 e2757f8b9a35b..f5466a63f1f5b 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 9e8b8f2a52d78..b53edb39c73b5 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 f3a1b3f5bfa28..513ca278a319e 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 88fa379c4072b..75714acb07cd9 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 0000000000000..2f3ee536a7b99 --- /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 = ::RuntimeEvent; + type Balances = ::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( + ::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 = ::RuntimeCall; + type AssetHubRococoRuntime = ::Runtime; + type RococoRuntimeCall = ::RuntimeCall; + type RococoRuntime = ::Runtime; + type RococoRuntimeEvent = ::RuntimeEvent; + type RococoRuntimeOrigin = ::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::::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::::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 = ::Runtime; + type RuntimeEvent = ::RuntimeEvent; + + assert_eq!(1, pallet_asset_rewards::Pools::::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 5cd00c239e60d..ef68a53c3b18b 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 36630e2d22217..68dc87250f76b 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 0dfe7a85f4c2a..576c44fc542fd 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 0000000000000..4df51abcacebf --- /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 = ::RuntimeEvent; + type Balances = ::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( + ::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 = ::RuntimeCall; + type AssetHubWestendRuntime = ::Runtime; + type WestendRuntimeCall = ::RuntimeCall; + type WestendRuntime = ::Runtime; + type WestendRuntimeEvent = ::RuntimeEvent; + type WestendRuntimeOrigin = ::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::::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::::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 = ::Runtime; + type RuntimeEvent = ::RuntimeEvent; + + assert_eq!(1, pallet_asset_rewards::Pools::::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 abe59a8439a8a..d612dd03c247a 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 db9a8201ebbe0..43b7bf0ba1184 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 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, + AssetIdForTrustBackedAssets, + xcm::v5::Location, + >, + xcm::v5::Location, + AccountId, +>; + +/// Union fungibles implementation for [`LocalAndForeignAssets`] and [`Balances`]. +pub type NativeAndNonPoolAssets = fungible::UnionOf< Balances, LocalAndForeignAssets, TargetFromLeft, @@ -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, + 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, + 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, + 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, + ResolveAssetTo, >; type WeightInfo = weights::pallet_asset_conversion_tx_payment::WeightInfo; #[cfg(feature = "runtime-benchmarks")] @@ -953,6 +1008,55 @@ impl pallet_xcm_bridge_hub_router::Config 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 + for PalletAssetRewardsBenchmarkHelper +{ + fn staked_asset() -> Location { + Location::new( + 0, + [PalletInstance(::index() as u8), GeneralIndex(100)], + ) + } + fn reward_asset() -> Location { + Location::new( + 0, + [PalletInstance(::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; + type RuntimeFreezeReason = RuntimeFreezeReason; + type Consideration = HoldConsideration< + AccountId, + Balances, + RewardsPoolCreationHoldReason, + ConstantStoragePrice, + >; + type WeightInfo = weights::pallet_asset_rewards::WeightInfo; + #[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:: = 55, AssetConversion: pallet_asset_conversion = 56, + AssetsFreezer: pallet_assets_freezer:: = 57, ForeignAssetsFreezer: pallet_assets_freezer:: = 58, PoolAssetsFreezer: pallet_assets_freezer:: = 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 for Runtime { + fn pool_creation_cost() -> Balance { + StakePoolCreationDeposit::get() + } + } + impl cumulus_primitives_core::GetCoreSelectorApi 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 ae78a56d8b3c1..6893766ac72d2 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 0000000000000..218c93c510350 --- /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 . + +//! 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(PhantomData); +impl pallet_asset_rewards::WeightInfo for WeightInfo { + /// 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 08b2f520c4b9a..0c6ff5e4bfddc 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 = ::index() as u8; + pub TrustBackedAssetsPalletLocationV3: xcm::v3::Location = + xcm::v3::Junction::PalletInstance(::index() as u8).into(); + pub PoolAssetsPalletLocationV3: xcm::v3::Location = + xcm::v3::Junction::PalletInstance(::index() as u8).into(); pub ForeignAssetsPalletLocation: Location = PalletInstance(::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, 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, + ResolveAssetTo, 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 cb10ae9a48003..65ef63a7fb356 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 5966dd01f18fe..f56c4568f2d1f 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_004, + spec_version: 1_017_005, 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, + 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, @@ -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, + 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, + 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, + 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 + for PalletAssetRewardsBenchmarkHelper +{ + fn staked_asset() -> Location { + Location::new( + 0, + [PalletInstance(::index() as u8), GeneralIndex(100)], + ) + } + fn reward_asset() -> Location { + Location::new( + 0, + [PalletInstance(::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; + type RuntimeFreezeReason = RuntimeFreezeReason; + type Consideration = HoldConsideration< + AccountId, + Balances, + RewardsPoolCreationHoldReason, + ConstantStoragePrice, + >; + type WeightInfo = weights::pallet_asset_rewards::WeightInfo; + #[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, + ResolveAssetTo, >; type WeightInfo = weights::pallet_asset_conversion_tx_payment::WeightInfo; #[cfg(feature = "runtime-benchmarks")] @@ -971,7 +1077,6 @@ impl pallet_revive::Config for Runtime { type InstantiateOrigin = EnsureSigned; type RuntimeHoldReason = RuntimeHoldReason; type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; - type Debug = (); type Xcm = pallet_xcm::Pallet; type ChainId = ConstU64<420_420_421>; type NativeToEthRatio = ConstU32<1_000_000>; // 10^(18 - 12) Eth is 10^18, Native is 10^12. @@ -1035,11 +1140,14 @@ construct_runtime!( NftFractionalization: pallet_nft_fractionalization = 54, PoolAssets: pallet_assets:: = 55, AssetConversion: pallet_asset_conversion = 56, + AssetsFreezer: pallet_assets_freezer:: = 57, ForeignAssetsFreezer: pallet_assets_freezer:: = 58, PoolAssetsFreezer: pallet_assets_freezer:: = 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 @@ -1317,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] @@ -1674,6 +1783,12 @@ impl_runtime_apis! { } } + impl pallet_asset_rewards::AssetRewards for Runtime { + fn pool_creation_cost() -> Balance { + StakePoolCreationDeposit::get() + } + } + impl cumulus_primitives_core::GetCoreSelectorApi for Runtime { fn core_selector() -> (CoreSelector, ClaimQueueOffset) { ParachainSystem::core_selector() 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 442b58635f48a..d653838ad80e6 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 0000000000000..3bbc289fec7b6 --- /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 . + +//! 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(PhantomData); +impl pallet_asset_rewards::WeightInfo for WeightInfo { + /// 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 b4e938f1f8b57..1ea2ce5136abd 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 = 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, 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, + ResolveAssetTo, 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 25c2df6b68d16..50b1b63146bc8 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 = - AsPrefixedGeneralIndex; +pub type AssetIdForPoolAssetsConvert = + AsPrefixedGeneralIndex; /// [`MatchedConvertedConcreteId`] converter dedicated for `PoolAssets` pub type PoolAssetsConvertedConcreteId = MatchedConvertedConcreteId< diff --git a/docs/sdk/packages/guides/first-pallet/Cargo.toml b/docs/sdk/packages/guides/first-pallet/Cargo.toml index a1411580119da..e6325c31781a6 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 303d5c5e7f5fc..8ed17dea1b71e 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/node/core/pvf/tests/it/process.rs b/polkadot/node/core/pvf/tests/it/process.rs index 353367b394f34..29326365b5baa 100644 --- a/polkadot/node/core/pvf/tests/it/process.rs +++ b/polkadot/node/core/pvf/tests/it/process.rs @@ -77,7 +77,9 @@ fn find_process_by_sid_and_name( let mut found = None; for process in all_processes { - let stat = process.stat().expect("/proc existed above. Potential race occurred"); + let Ok(stat) = process.stat() else { + continue; + }; if stat.session != sid || !process.exe().unwrap().to_str().unwrap().contains(exe_name) { continue diff --git a/polkadot/node/metrics/Cargo.toml b/polkadot/node/metrics/Cargo.toml index 454337cb63f87..318deca4f2438 100644 --- a/polkadot/node/metrics/Cargo.toml +++ b/polkadot/node/metrics/Cargo.toml @@ -18,7 +18,7 @@ gum = { workspace = true, default-features = true } metered = { features = ["futures_channel"], workspace = true } # Both `sc-service` and `sc-cli` are required by runtime metrics `logger_hook()`. -sc-cli = { workspace = true, default-features = true } +sc-cli = { workspace = true } sc-service = { workspace = true, default-features = true } bs58 = { features = ["alloc"], workspace = true, default-features = true } diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index b3f2a0033278a..c2c3d35ee5b42 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -1822,7 +1822,7 @@ mod benches { [polkadot_runtime_parachains::initializer, Initializer] [polkadot_runtime_parachains::paras_inherent, ParaInherent] [polkadot_runtime_parachains::paras, Paras] - [polkadot_runtime_parachains::assigner_on_demand, OnDemandAssignmentProvider] + [polkadot_runtime_parachains::on_demand, OnDemandAssignmentProvider] // Substrate [pallet_balances, Balances] [pallet_balances, NisCounterpartBalances] diff --git a/polkadot/runtime/rococo/src/xcm_config.rs b/polkadot/runtime/rococo/src/xcm_config.rs index bb77ec0000e56..10c3f6c0cbfcf 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; +/// Type to convert the Treasury origin to a Plurality `Location` value. +pub type TreasurerToPlurality = OriginToPluralityVoice; + /// 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; + type SendXcmOrigin = xcm_builder::EnsureXcmOrigin< + RuntimeOrigin, + (LocalPalletOriginToLocation, LocalOriginToLocation), + >; type XcmRouter = XcmRouter; // Anyone can execute XCM messages locally. type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; diff --git a/polkadot/runtime/test-runtime/src/lib.rs b/polkadot/runtime/test-runtime/src/lib.rs index 4f9ba8d8508cd..cdf6fa92da2f5 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 58d2bdcb7c7db..a9ba0778fe0ef 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 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 fcdaf7ff2de6d..65b81cc00f069 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 = 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::::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 = 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::::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::::iter().for_each(|(ctrl, ledger)| { + match pallet_staking::Pallet::::migrate_currency( + RuntimeOrigin::signed(alice.clone()).into(), + ledger.stash.clone(), + ) { + Ok(_) => { + let updated_ledger = + pallet_staking::Ledger::::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 393fa0b37176a..f1e7f5ba1576e 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 pallet_staking::WeightInfo for WeightInfo { /// 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 pallet_staking::WeightInfo for WeightInfo { /// 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 pallet_staking::WeightInfo for WeightInfo { /// 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 pallet_staking::WeightInfo for WeightInfo { /// 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 pallet_staking::WeightInfo for WeightInfo { /// 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 pallet_staking::WeightInfo for WeightInfo { /// 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 pallet_staking::WeightInfo for WeightInfo { /// 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 pallet_staking::WeightInfo for WeightInfo { /// 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 pallet_staking::WeightInfo for WeightInfo { /// 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 pallet_staking::WeightInfo for WeightInfo { /// 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 pallet_staking::WeightInfo for WeightInfo { /// 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 pallet_staking::WeightInfo for WeightInfo { /// 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 pallet_staking::WeightInfo for WeightInfo { /// 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 pallet_staking::WeightInfo for WeightInfo { // 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 pallet_staking::WeightInfo for WeightInfo { // 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 pallet_staking::WeightInfo for WeightInfo { // 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 pallet_staking::WeightInfo for WeightInfo { // 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 pallet_staking::WeightInfo for WeightInfo { // 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 pallet_staking::WeightInfo for WeightInfo { /// 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 pallet_staking::WeightInfo for WeightInfo { /// 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 pallet_staking::WeightInfo for WeightInfo { /// 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 pallet_staking::WeightInfo for WeightInfo { /// 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 pallet_staking::WeightInfo for WeightInfo { /// 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 pallet_staking::WeightInfo for WeightInfo { /// 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 pallet_staking::WeightInfo for WeightInfo { /// 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 pallet_staking::WeightInfo for WeightInfo { /// 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 pallet_staking::WeightInfo for WeightInfo { 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 pallet_staking::WeightInfo for WeightInfo { /// 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 pallet_staking::WeightInfo for WeightInfo { /// 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 pallet_staking::WeightInfo for WeightInfo { // 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 pallet_staking::WeightInfo for WeightInfo { // 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 pallet_staking::WeightInfo for WeightInfo { /// 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 pallet_staking::WeightInfo for WeightInfo { /// 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 pallet_staking::WeightInfo for WeightInfo { // 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 3f6a7304c8a94..4235edf82b24d 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; + type SendXcmOrigin = xcm_builder::EnsureXcmOrigin< + RuntimeOrigin, + (LocalPalletOriginToLocation, LocalOriginToLocation), + >; type XcmRouter = XcmRouter; // Anyone can execute XCM messages locally. type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; diff --git a/polkadot/xcm/docs/Cargo.toml b/polkadot/xcm/docs/Cargo.toml index 6fa7ea9a23a92..c3bda50619c15 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 0000000000000..7f352f7a45fb3 --- /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 0000000000000..f2a5aa9a46679 --- /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 0000000000000..ffdb4738a6fd5 --- /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_7081.prdoc b/prdoc/pr_7081.prdoc new file mode 100644 index 0000000000000..be1d8aa6ee013 --- /dev/null +++ b/prdoc/pr_7081.prdoc @@ -0,0 +1,14 @@ +# 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: '[pallet-mmr] Migrate to using frame umbrella crate' + +doc: + - audience: Runtime Dev + description: This PR migrates the pallet-mmr to use the frame umbrella crate. This + is part of the ongoing effort to migrate all pallets to use the frame umbrella crate. + The effort is tracked [here](https://github.com/paritytech/polkadot-sdk/issues/6504). + +crates: + - name: pallet-mmr + bump: minor diff --git a/prdoc/pr_7093.prdoc b/prdoc/pr_7093.prdoc new file mode 100644 index 0000000000000..cad4477e8832f --- /dev/null +++ b/prdoc/pr_7093.prdoc @@ -0,0 +1,8 @@ +title: 'initial docify readme with some content #6333' +doc: +- audience: Runtime Dev + description: | + Docifying the README.MD under templates/parachain by adding a Docify. + Also Adding the Cargo.toml under the same folder, essentially making it a crate as Docify acts + for Readmes only under the same crate. +crates: [ ] diff --git a/prdoc/pr_7177.prdoc b/prdoc/pr_7177.prdoc new file mode 100644 index 0000000000000..9ab0be1f20a93 --- /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 0000000000000..3a9db46ceae96 --- /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 0000000000000..96a3d19472e9f --- /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/prdoc/pr_7205.prdoc b/prdoc/pr_7205.prdoc new file mode 100644 index 0000000000000..758beb0b6313c --- /dev/null +++ b/prdoc/pr_7205.prdoc @@ -0,0 +1,10 @@ +title: 'Collator: Fix `can_build_upon` by always allowing to build on included block' +doc: +- audience: Node Dev + description: |- + Fixes a bug introduced in #6825. + We should always allow building on the included block of parachains. In situations where the unincluded segment + is full, but the included block moved to the most recent block, building was wrongly disallowed. +crates: +- name: cumulus-client-consensus-aura + bump: minor diff --git a/prdoc/pr_7251.prdoc b/prdoc/pr_7251.prdoc new file mode 100644 index 0000000000000..98e371dc940ff --- /dev/null +++ b/prdoc/pr_7251.prdoc @@ -0,0 +1,7 @@ +title: '[pallet-revive] eth-rpc error logging' +doc: +- audience: Runtime Dev + description: Log error instead of failing with an error when block processing fails +crates: +- name: pallet-revive-eth-rpc + bump: minor diff --git a/prdoc/pr_7263.prdoc b/prdoc/pr_7263.prdoc new file mode 100644 index 0000000000000..892e804939559 --- /dev/null +++ b/prdoc/pr_7263.prdoc @@ -0,0 +1,28 @@ +title: Fix `frame-benchmarking-cli` not buildable without rocksdb +doc: +- audience: Runtime Dev + description: |- + ## Description + + The `frame-benchmarking-cli` crate has not been buildable without the `rocksdb` feature since version 1.17.0. + + **Error:** + ```rust + self.database()?.unwrap_or(Database::RocksDb), + ^^^^^^^ variant or associated item not found in `Database` + ``` + + This issue is also related to the `rocksdb` feature bleeding (#3793), where the `rocksdb` feature was always activated even when compiling this crate with `--no-default-features`. + + **Fix:** + - Resolved the error by choosing `paritydb` as the default database when compiled without the `rocksdb` feature. + - Fixed the issue where the `sc-cli` crate's `rocksdb` feature was always active, even compiling `frame-benchmarking-cli` with `--no-default-features`. + + ## Review Notes + + Fix the crate to be built without rocksdb, not intended to solve #3793. +crates: +- name: polkadot-node-metrics + bump: patch +- name: frame-benchmarking-cli + bump: patch diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 97728f12f5f93..26f4dacf9a1e3 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; } +pub type AssetsFreezerInstance = pallet_assets_freezer::Instance1; +impl pallet_assets_freezer::Config for Runtime { + type RuntimeFreezeReason = RuntimeFreezeReason; + type RuntimeEvent = RuntimeEvent; +} + impl pallet_asset_conversion_tx_payment::Config for Runtime { type RuntimeEvent = RuntimeEvent; type AssetId = NativeOrWithId; @@ -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; type RuntimeEvent = RuntimeEvent; - type Slash = Treasury; // send the slashed funds to the treasury. + type RuntimeHoldReason = RuntimeHoldReason; + type Slash = ResolveTo; // 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; type BenchmarkingConfig = StakingBenchmarkingConfig; type DisablingStrategy = pallet_staking::UpToLimitWithReEnablingDisablingStrategy; @@ -925,6 +936,21 @@ impl pallet_bags_list::Config for Runtime { type WeightInfo = pallet_bags_list::weights::SubstrateWeight; } +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; + type StakeAdapter = + pallet_nomination_pools::adapter::DelegateStake; type PostUnbondingPoolsWindow = PostUnbondPoolsWindow; type MaxMetadataLen = ConstU32<256>; type MaxUnbonding = ConstU32<8>; @@ -1464,7 +1491,6 @@ impl pallet_revive::Config for Runtime { type InstantiateOrigin = EnsureSigned; 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, AccountId>; + +/// Benchmark Helper +#[cfg(feature = "runtime-benchmarks")] +pub struct AssetRewardsBenchmarkHelper; + +#[cfg(feature = "runtime-benchmarks")] +impl pallet_asset_rewards::benchmarking::BenchmarkHelper> + for AssetRewardsBenchmarkHelper +{ + fn staked_asset() -> NativeOrWithId { + NativeOrWithId::::WithId(100) + } + fn reward_asset() -> NativeOrWithId { + NativeOrWithId::::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; + type Balance = Balance; + type Assets = NativeAndAssets; + type PalletId = StakingRewardsPalletId; + type CreatePoolOrigin = EnsureSigned; + type WeightInfo = (); + type AssetsFreezer = NativeAndAssetsFreezer; + type Consideration = HoldConsideration< + AccountId, + Balances, + CreationHoldReason, + ConstantStoragePrice, + >; + #[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::pallet_index(82)] + pub type DelegatedStaking = pallet_delegated_staking::Pallet; + + #[runtime::pallet_index(83)] + pub type AssetRewards = pallet_asset_rewards::Pallet; + + #[runtime::pallet_index(84)] + pub type AssetsFreezer = pallet_assets_freezer::Pallet; } impl TryFrom for pallet_revive::Call { @@ -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] @@ -3553,6 +3636,12 @@ impl_runtime_apis! { } } + impl pallet_asset_rewards::AssetRewards for Runtime { + fn pool_creation_cost() -> Balance { + StakePoolCreationDeposit::get() + } + } + #[cfg(feature = "try-runtime")] impl frame_try_runtime::TryRuntime 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 606349e0bd6ba..624b00b4d6c23 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) -> 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/frame/asset-rewards/Cargo.toml b/substrate/frame/asset-rewards/Cargo.toml new file mode 100644 index 0000000000000..a03fa17cf0dc0 --- /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 0000000000000..5605804dd20ec --- /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 { + /// 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() -> DispatchTime> { + DispatchTime::At(BlockNumberFor::::from(100u32)) +} + +fn create_reward_pool() -> Result +where + T::Assets: Create + Mutate, +{ + 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::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::::create_pool( + caller_origin.clone(), + Box::new(staked_asset), + Box::new(reward_asset), + // reward rate per block + min_reward_balance, + pool_expire::(), + Some(caller), + )); + + Ok(caller_origin) +} + +fn mint_into(caller: &T::AccountId, asset: &T::AssetId) -> T::Balance +where + T::Assets: Mutate, +{ + 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(generic_event: ::RuntimeEvent) { + System::::assert_last_event(generic_event.into()); +} + +#[benchmarks(where T::Assets: Create + Mutate)] +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::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::(), + Some(caller.clone()), + ); + + assert_last_event::( + 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::().evaluate(System::::block_number()), + pool_id: 0, + } + .into(), + ); + + Ok(()) + } + + #[benchmark] + fn stake() -> Result<(), BenchmarkError> { + create_reward_pool::()?; + + let staker: T::AccountId = whitelisted_caller(); + let min_balance = mint_into::(&staker, &T::BenchmarkHelper::staked_asset()); + + // stake first to get worth case benchmark. + assert_ok!(AssetRewards::::stake( + RawOrigin::Signed(staker.clone()).into(), + 0, + min_balance + )); + + #[extrinsic_call] + _(RawOrigin::Signed(staker.clone()), 0, min_balance); + + assert_last_event::(Event::Staked { staker, pool_id: 0, amount: min_balance }.into()); + + Ok(()) + } + + #[benchmark] + fn unstake() -> Result<(), BenchmarkError> { + create_reward_pool::()?; + + let staker: T::AccountId = whitelisted_caller(); + let min_balance = mint_into::(&staker, &T::BenchmarkHelper::staked_asset()); + + assert_ok!(AssetRewards::::stake( + RawOrigin::Signed(staker.clone()).into(), + 0, + min_balance, + )); + + #[extrinsic_call] + _(RawOrigin::Signed(staker.clone()), 0, min_balance, None); + + assert_last_event::( + Event::Unstaked { caller: staker.clone(), staker, pool_id: 0, amount: min_balance } + .into(), + ); + + Ok(()) + } + + #[benchmark] + fn harvest_rewards() -> Result<(), BenchmarkError> { + create_reward_pool::()?; + + let pool_acc = AssetRewards::::pool_account_id(&0u32); + let min_reward_balance = mint_into::(&pool_acc, &T::BenchmarkHelper::reward_asset()); + + let staker = whitelisted_caller(); + let _ = mint_into::(&staker, &T::BenchmarkHelper::staked_asset()); + assert_ok!(AssetRewards::::stake( + RawOrigin::Signed(staker.clone()).into(), + 0, + T::Balance::one(), + )); + + System::::set_block_number(System::::block_number() + BlockNumberFor::::one()); + + #[extrinsic_call] + _(RawOrigin::Signed(staker.clone()), 0, None); + + assert_last_event::( + 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::()?; + + // stake first to get worth case benchmark. + { + let staker: T::AccountId = whitelisted_caller(); + let min_balance = mint_into::(&staker, &T::BenchmarkHelper::staked_asset()); + + assert_ok!(AssetRewards::::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::( + 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::()?; + let new_admin: T::AccountId = whitelisted_caller(); + + #[extrinsic_call] + _(caller_origin as T::RuntimeOrigin, 0, new_admin.clone()); + + assert_last_event::(Event::PoolAdminModified { pool_id: 0, new_admin }.into()); + + Ok(()) + } + + #[benchmark] + fn set_pool_expiry_block() -> Result<(), BenchmarkError> { + let create_origin = create_reward_pool::()?; + + // stake first to get worth case benchmark. + { + let staker: T::AccountId = whitelisted_caller(); + let min_balance = mint_into::(&staker, &T::BenchmarkHelper::staked_asset()); + + assert_ok!(AssetRewards::::stake(RawOrigin::Signed(staker).into(), 0, min_balance)); + } + + let new_expiry_block = + pool_expire::().evaluate(System::::block_number()) + BlockNumberFor::::one(); + + #[extrinsic_call] + _(create_origin as T::RuntimeOrigin, 0, DispatchTime::At(new_expiry_block)); + + assert_last_event::( + Event::PoolExpiryBlockModified { pool_id: 0, new_expiry_block }.into(), + ); + + Ok(()) + } + + #[benchmark] + fn deposit_reward_tokens() -> Result<(), BenchmarkError> { + create_reward_pool::()?; + let caller = whitelisted_caller(); + + let reward_asset = T::BenchmarkHelper::reward_asset(); + let pool_acc = AssetRewards::::pool_account_id(&0u32); + let min_balance = mint_into::(&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::()?; + 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::(&caller, &reward_asset); + assert_ok!(AssetRewards::::deposit_reward_tokens( + RawOrigin::Signed(caller).into(), + 0, + min_balance + )); + } + + #[extrinsic_call] + _(RawOrigin::Signed(caller), 0); + + assert_last_event::(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 0000000000000..4ce73e9febf96 --- /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 = PoolInfo< + ::AccountId, + ::AssetId, + ::Balance, + BlockNumberFor, +>; + +/// The state of a staker in a pool. +#[derive(Debug, Default, Clone, Decode, Encode, MaxEncodedLen, TypeInfo)] +pub struct PoolStakerInfo { + /// 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 { + /// 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 { + /// 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(_); + + /// 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> + IsType<::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; + + /// 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; + + /// Registry of assets that can be configured to either stake for rewards, or be offered as + /// rewards for staking. + type Assets: Inspect + + Mutate; + + /// 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; + + /// 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; + + /// Weight information for extrinsics in this pallet. + type WeightInfo: WeightInfo; + + /// Helper for benchmarking. + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper: benchmarking::BenchmarkHelper; + } + + /// State of pool stakers. + #[pallet::storage] + pub type PoolStakers = StorageDoubleMap< + _, + Blake2_128Concat, + PoolId, + Blake2_128Concat, + T::AccountId, + PoolStakerInfo, + >; + + /// State and configuration of each staking pool. + #[pallet::storage] + pub type Pools = StorageMap<_, Blake2_128Concat, PoolId, PoolInfoFor>; + + /// 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 = + 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 = StorageValue<_, PoolId, ValueQuery>; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// 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, + /// 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, + }, + /// A pool information was cleared after it's completion. + PoolCleanedUp { + /// The cleared pool. + pool_id: PoolId, + }, + } + + #[pallet::error] + pub enum Error { + /// 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 Hooks> for Pallet { + fn integrity_test() { + // The AccountId is at least 16 bytes to contain the unique PalletId. + let pool_id: PoolId = 1; + assert!( + >::try_into_sub_account( + &T::PalletId::get(), pool_id, + ) + .is_some() + ); + } + } + + /// Pallet's callable functions. + #[pallet::call(weight(::WeightInfo))] + impl Pallet { + /// 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, + staked_asset_id: Box, + reward_asset_id: Box, + reward_rate_per_block: T::Balance, + expiry: DispatchTime>, + admin: Option, + ) -> 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::::NonExistentAsset + ); + ensure!( + T::Assets::asset_exists(*reward_asset_id.clone()), + Error::::NonExistentAsset + ); + + // Check the expiry block. + let expiry_block = expiry.evaluate(frame_system::Pallet::::block_number()); + ensure!( + expiry_block > frame_system::Pallet::::block_number(), + Error::::ExpiryBlockMustBeInTheFuture + ); + + let pool_id = NextPoolId::::get(); + + let footprint = Self::pool_creation_footprint(); + let cost = T::Consideration::new(&creator, footprint)?; + PoolCost::::insert(pool_id, (creator.clone(), cost)); + + let admin = admin.unwrap_or(creator.clone()); + + // Create the pool. + let pool = PoolInfoFor:: { + 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::::insert(pool_id, pool); + + NextPoolId::::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, pool_id: PoolId, amount: T::Balance) -> DispatchResult { + let staker = ensure_signed(origin)?; + + // Always start by updating staker and pool rewards. + let pool_info = Pools::::get(pool_id).ok_or(Error::::NonExistentPool)?; + let staker_info = PoolStakers::::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::::insert(pool_id, pool_info); + + // Update PoolStakers. + staker_info.amount.ensure_add_assign(amount)?; + PoolStakers::::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, + pool_id: PoolId, + amount: T::Balance, + staker: Option, + ) -> DispatchResult { + let caller = ensure_signed(origin)?; + let staker = staker.unwrap_or(caller.clone()); + + // Always start by updating the pool rewards. + let pool_info = Pools::::get(pool_id).ok_or(Error::::NonExistentPool)?; + let now = frame_system::Pallet::::block_number(); + ensure!(now > pool_info.expiry_block || caller == staker, BadOrigin); + + let staker_info = PoolStakers::::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::::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::::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::::remove(&pool_id, &staker); + } else { + PoolStakers::::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, + pool_id: PoolId, + staker: Option, + ) -> 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::::get(pool_id).ok_or(Error::::NonExistentPool)?; + let now = frame_system::Pallet::::block_number(); + ensure!(now > pool_info.expiry_block || caller == staker, BadOrigin); + + let staker_info = + PoolStakers::::get(pool_id, &staker).ok_or(Error::::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::::remove(&pool_id, &staker); + } else { + PoolStakers::::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, + 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::::get(pool_id).ok_or(Error::::NonExistentPool)?; + ensure!(pool_info.admin == caller, BadOrigin); + ensure!( + new_reward_rate_per_block > pool_info.reward_rate_per_block, + Error::::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::::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, + 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::::get(pool_id).ok_or(Error::::NonExistentPool)?; + ensure!(pool_info.admin == caller, BadOrigin); + + pool_info.admin = new_admin.clone(); + Pools::::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, + pool_id: PoolId, + new_expiry: DispatchTime>, + ) -> DispatchResult { + let caller = T::CreatePoolOrigin::ensure_origin(origin.clone()) + .or_else(|_| ensure_signed(origin))?; + + let new_expiry = new_expiry.evaluate(frame_system::Pallet::::block_number()); + ensure!( + new_expiry > frame_system::Pallet::::block_number(), + Error::::ExpiryBlockMustBeInTheFuture + ); + + let pool_info = Pools::::get(pool_id).ok_or(Error::::NonExistentPool)?; + ensure!(pool_info.admin == caller, BadOrigin); + ensure!(new_expiry > pool_info.expiry_block, Error::::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::::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, + pool_id: PoolId, + amount: T::Balance, + ) -> DispatchResult { + let caller = ensure_signed(origin)?; + let pool_info = Pools::::get(pool_id).ok_or(Error::::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, pool_id: PoolId) -> DispatchResult { + let who = ensure_signed(origin)?; + + let pool_info = Pools::::get(pool_id).ok_or(Error::::NonExistentPool)?; + ensure!(pool_info.admin == who, BadOrigin); + + let stakers = PoolStakers::::iter_key_prefix(pool_id).next(); + ensure!(stakers.is_none(), Error::::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::::take(pool_id) { + T::Consideration::drop(cost, &who)?; + } + + Pools::::remove(pool_id); + + Self::deposit_event(Event::PoolCleanedUp { pool_id }); + + Ok(()) + } + } + + impl Pallet { + /// 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)>() + } + + /// 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, + staker_info: &PoolStakerInfo, + ) -> Result<(PoolInfoFor, PoolStakerInfo), 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, + reward_per_token: T::Balance, + ) -> Result, DispatchError> { + let mut new_pool_info = pool_info.clone(); + new_pool_info.last_update_block = frame_system::Pallet::::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) -> Result { + 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::::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, + reward_per_token: &T::Balance, + ) -> Result { + 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) -> BlockNumberFor { + let now = frame_system::Pallet::::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 0000000000000..87c8a8a0dea0b --- /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; + +construct_runtime!( + pub enum MockRuntime + { + System: frame_system, + Balances: pallet_balances, + Assets: pallet_assets::, + AssetsFreezer: pallet_assets_freezer::, + StakingRewards: pallet_asset_rewards, + } +); + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for MockRuntime { + type AccountId = u128; + type Lookup = IdentityLookup; + type Block = Block; + type AccountData = pallet_balances::AccountData; +} + +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 for MockRuntime { + type RuntimeEvent = RuntimeEvent; + type Balance = u128; + type RemoveItemsLimit = ConstU32<1000>; + type AssetId = u32; + type AssetIdParameter = u32; + type Currency = Balances; + type CreateOrigin = AsEnsureOriginWithArg>; + type ForceOrigin = frame_system::EnsureRoot; + 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 = NativeOrWithId::Native; + pub const PermissionedAccountId: u128 = 0; +} + +/// Give Root Origin permission to create pools. +pub struct MockPermissionedOrigin; +impl EnsureOrigin for MockPermissionedOrigin { + type Success = ::AccountId; + + fn try_origin(origin: RuntimeOrigin) -> Result { + match origin.clone().into() { + Ok(frame_system::RawOrigin::Root) => Ok(PermissionedAccountId::get()), + _ => Err(origin), + } + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin() -> Result { + Ok(RuntimeOrigin::root()) + } +} + +/// Allow Freezes for the `Assets` pallet +impl pallet_assets_freezer::Config for MockRuntime { + type RuntimeFreezeReason = RuntimeFreezeReason; + type RuntimeEvent = RuntimeEvent; +} + +pub type NativeAndAssets = UnionOf, u128>; + +pub type NativeAndAssetsFreezer = + UnionOf, u128>; + +#[cfg(feature = "runtime-benchmarks")] +pub struct AssetRewardsBenchmarkHelper; +#[cfg(feature = "runtime-benchmarks")] +impl BenchmarkHelper> for AssetRewardsBenchmarkHelper { + fn staked_asset() -> NativeOrWithId { + NativeOrWithId::::WithId(101) + } + fn reward_asset() -> NativeOrWithId { + NativeOrWithId::::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; + type Balance = ::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<0>, u128>, + >; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = AssetRewardsBenchmarkHelper; +} + +pub(crate) fn new_test_ext() -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + + pallet_assets::GenesisConfig:: { + // 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, Vec, 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:: { + 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 0000000000000..399d6a54c9392 --- /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 = NativeOrWithId::::WithId(1); +const DEFAULT_REWARD_ASSET_ID: NativeOrWithId = NativeOrWithId::::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, +) { + hypothetically!({ + // Get the pre-harvest balance. + let balance_before: ::Balance = + <::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::::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 = + <::Assets>::balance(reward_asset_id.clone(), &staker); + assert_eq!(balance_after - balance_before, expected_earned); + }); +} + +fn events() -> Vec> { + 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, u64>)> { + Pools::::iter().collect() +} + +mod create_pool { + use super::*; + + #[test] + fn success() { + new_test_ext().execute_with(|| { + assert_eq!(NextPoolId::::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::::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::::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::::WithId(10); + let reward_asset_id = NativeOrWithId::::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::::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::::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::::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::::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::::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::::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::::WithId(1); + let invalid_asset = NativeOrWithId::::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::::NonExistentAsset + ); + + assert_err!( + StakingRewards::create_pool( + RuntimeOrigin::root(), + Box::new(invalid_asset.clone()), + Box::new(valid_asset.clone()), + 10, + DispatchTime::After(10u64), + None + ), + Error::::NonExistentAsset + ); + + assert_err!( + StakingRewards::create_pool( + RuntimeOrigin::root(), + Box::new(invalid_asset.clone()), + Box::new(invalid_asset.clone()), + 10, + DispatchTime::After(10u64), + None + ), + Error::::NonExistentAsset + ); + }) + } + + #[test] + fn fails_for_not_permissioned() { + new_test_ext().execute_with(|| { + let user = 100; + let staked_asset_id = NativeOrWithId::::Native; + let reward_asset_id = NativeOrWithId::::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::::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::::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::::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 = >::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::::get(pool_id, user).unwrap().amount, 1000); + + // Event is emitted. + assert_eq!( + *events().last().unwrap(), + Event::::Staked { staker: user, amount: 1000, pool_id: 0 } + ); + + // Check that the pool's total tokens staked is updated + assert_eq!(Pools::::get(pool_id).unwrap().total_tokens_staked, 1000); + + // Check user's frozen balance is updated + assert_eq!( + >::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::::Staked { staker: user, amount: 500, pool_id: 0 } + ); + + // Check that the user's staked amount is updated + assert_eq!(PoolStakers::::get(pool_id, user).unwrap().amount, 1000 + 500); + + // Check that the pool's total tokens staked is updated + assert_eq!(Pools::::get(pool_id).unwrap().total_tokens_staked, 1000 + 500); + + assert_eq!( + >::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::::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 = >::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::::Unstaked { + caller: user, + staker: user, + amount: 500, + pool_id: 0 + } + ); + + // Check that the user's staked amount is updated + assert_eq!(PoolStakers::::get(pool_id, user).unwrap().amount, 500); + + // Check that the pool's total tokens staked is updated + assert_eq!(Pools::::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::::get(pool_id, user).is_none()); + + // Check that the pool's total tokens staked is zero + assert_eq!(Pools::::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::::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::::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::::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::::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: ::Balance = + <::Assets>::balance(reward_asset_id.clone(), &staker); + assert_ok!(StakingRewards::harvest_rewards( + RuntimeOrigin::signed(staker), + pool_id, + None + )); + let balance_after = + <::Assets>::balance(reward_asset_id.clone(), &staker); + + // Assert + assert_eq!( + balance_after - balance_before, + 10 * Pools::::get(pool_id).unwrap().reward_rate_per_block + ); + assert_eq!( + *events().last().unwrap(), + Event::::RewardsHarvested { + caller: staker, + staker, + pool_id, + amount: 10 * Pools::::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::::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::::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::::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::::PoolAdminModified { pool_id, new_admin } + ); + assert_eq!(Pools::::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::::PoolAdminModified { pool_id, new_admin } + ); + assert_eq!(Pools::::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::::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::::get(pool_id).unwrap().expiry_block, new_expiry_block); + assert_eq!( + *events().last().unwrap(), + Event::::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::::get(pool_id).unwrap().expiry_block, new_expiry_block); + assert_eq!( + *events().last().unwrap(), + Event::::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::::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::::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::::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::::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::::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::::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::::get(pool_id).unwrap().reward_rate_per_block, + new_reward_rate + ); + + // Check event + assert_eq!( + *events().last().unwrap(), + Event::::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::::get(pool_id).unwrap().reward_rate_per_block, + new_reward_rate + ); + + // Check event + assert_eq!( + *events().last().unwrap(), + Event::::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::::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::::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::::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::::Native; + create_default_pool(); + let pool_account_id = StakingRewards::pool_account_id(&pool_id); + + let depositor_balance_before = + <::Assets>::balance(reward_asset_id.clone(), &depositor); + let pool_balance_before = <::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 = + <::Assets>::balance(reward_asset_id.clone(), &depositor); + let pool_balance_after = + <::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::::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 = >::balance(&admin); + + create_default_pool(); + assert!(Pools::::get(pool_id).is_some()); + + assert_ok!(StakingRewards::cleanup_pool(RuntimeOrigin::signed(admin), pool_id)); + + assert_eq!( + >::balance(&admin), + // `100_000` initial pool account balance from Genesis config + admin_balance_before + 100_000, + ); + assert_eq!(Pools::::get(pool_id), None); + assert_eq!(PoolStakers::::iter_prefix_values(pool_id).count(), 0); + assert_eq!(PoolCost::::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::::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::::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::::get(pool_id), None); + assert_eq!(PoolStakers::::iter_prefix_values(pool_id).count(), 0); + assert_eq!(PoolCost::::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::::WithId(1); + let reward_asset_id = NativeOrWithId::::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: ::Balance = + <::Assets>::balance(reward_asset_id.clone(), &staker2); + assert_ok!(StakingRewards::harvest_rewards(RuntimeOrigin::signed(staker2), pool_id, None)); + let balance_after = + <::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 0000000000000..c9e2d0fd251aa --- /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(PhantomData); +impl WeightInfo for SubstrateWeight { + /// 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/assets-freezer/Cargo.toml b/substrate/frame/assets-freezer/Cargo.toml index 3fffa4d0627fa..d8c0ee6e442b2 100644 --- a/substrate/frame/assets-freezer/Cargo.toml +++ b/substrate/frame/assets-freezer/Cargo.toml @@ -16,46 +16,31 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { workspace = true } -frame-benchmarking = { optional = true, workspace = true } -frame-support = { workspace = true } -frame-system = { workspace = true } +frame = { workspace = true, features = ["runtime"] } log = { workspace = true } pallet-assets = { workspace = true } scale-info = { features = ["derive"], workspace = true } -sp-runtime = { workspace = true } [dev-dependencies] pallet-balances = { workspace = true } -sp-core = { workspace = true } -sp-io = { workspace = true } [features] default = ["std"] std = [ "codec/std", - "frame-benchmarking?/std", - "frame-support/std", - "frame-system/std", + "frame/std", "log/std", "pallet-assets/std", "pallet-balances/std", "scale-info/std", - "sp-core/std", - "sp-io/std", - "sp-runtime/std", ] runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", + "frame/runtime-benchmarks", "pallet-assets/runtime-benchmarks", "pallet-balances/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", ] try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", + "frame/try-runtime", "pallet-assets/try-runtime", "pallet-balances/try-runtime", - "sp-runtime/try-runtime", ] diff --git a/substrate/frame/assets-freezer/src/impls.rs b/substrate/frame/assets-freezer/src/impls.rs index cd383f1c3cd1e..8c9f148e1e9a4 100644 --- a/substrate/frame/assets-freezer/src/impls.rs +++ b/substrate/frame/assets-freezer/src/impls.rs @@ -16,13 +16,7 @@ // limitations under the License. use super::*; - -use frame_support::traits::{ - fungibles::{Inspect, InspectFreeze, MutateFreeze}, - tokens::{DepositConsequence, Fortitude, Preservation, Provenance, WithdrawConsequence}, -}; use pallet_assets::FrozenBalance; -use sp_runtime::traits::Zero; // Implements [`FrozenBalance`] from [`pallet-assets`], so it can understand how much of an // account balance is frozen, and is able to signal to this pallet when to clear the state of an @@ -115,7 +109,7 @@ impl, I: 'static> MutateFreeze for Pallet { id: &Self::Id, who: &T::AccountId, amount: Self::Balance, - ) -> sp_runtime::DispatchResult { + ) -> DispatchResult { if amount.is_zero() { return Self::thaw(asset, id, who); } @@ -135,7 +129,7 @@ impl, I: 'static> MutateFreeze for Pallet { id: &Self::Id, who: &T::AccountId, amount: Self::Balance, - ) -> sp_runtime::DispatchResult { + ) -> DispatchResult { if amount.is_zero() { return Ok(()); } @@ -150,7 +144,7 @@ impl, I: 'static> MutateFreeze for Pallet { Self::update_freezes(asset, who, freezes.as_bounded_slice()) } - fn thaw(asset: Self::AssetId, id: &Self::Id, who: &T::AccountId) -> sp_runtime::DispatchResult { + fn thaw(asset: Self::AssetId, id: &Self::Id, who: &T::AccountId) -> DispatchResult { let mut freezes = Freezes::::get(asset.clone(), who); freezes.retain(|f| &f.id != id); Self::update_freezes(asset, who, freezes.as_bounded_slice()) diff --git a/substrate/frame/assets-freezer/src/lib.rs b/substrate/frame/assets-freezer/src/lib.rs index b42d41ac1d925..5f718ed84820f 100644 --- a/substrate/frame/assets-freezer/src/lib.rs +++ b/substrate/frame/assets-freezer/src/lib.rs @@ -18,10 +18,10 @@ //! # Assets Freezer Pallet //! //! A pallet capable of freezing fungibles from `pallet-assets`. This is an extension of -//! `pallet-assets`, wrapping [`fungibles::Inspect`](`frame_support::traits::fungibles::Inspect`). +//! `pallet-assets`, wrapping [`fungibles::Inspect`](`Inspect`). //! It implements both -//! [`fungibles::freeze::Inspect`](frame_support::traits::fungibles::freeze::Inspect) and -//! [`fungibles::freeze::Mutate`](frame_support::traits::fungibles::freeze::Mutate). The complexity +//! [`fungibles::freeze::Inspect`](InspectFreeze) and +//! [`fungibles::freeze::Mutate`](MutateFreeze). The complexity //! of the operations is `O(n)`. where `n` is the variant count of `RuntimeFreezeReason`. //! //! ## Pallet API @@ -35,26 +35,27 @@ //! //! - Pallet hooks allowing [`pallet-assets`] to know the frozen balance for an account on a given //! asset (see [`pallet_assets::FrozenBalance`]). -//! - An implementation of -//! [`fungibles::freeze::Inspect`](frame_support::traits::fungibles::freeze::Inspect) and -//! [`fungibles::freeze::Mutate`](frame_support::traits::fungibles::freeze::Mutate), allowing -//! other pallets to manage freezes for the `pallet-assets` assets. +//! - An implementation of [`fungibles::freeze::Inspect`](InspectFreeze) and +//! [`fungibles::freeze::Mutate`](MutateFreeze), allowing other pallets to manage freezes for the +//! `pallet-assets` assets. #![cfg_attr(not(feature = "std"), no_std)] -use frame_support::{ - pallet_prelude::*, - traits::{tokens::IdAmount, VariantCount, VariantCountOf}, - BoundedVec, -}; -use frame_system::pallet_prelude::BlockNumberFor; -use sp_runtime::{ - traits::{Saturating, Zero}, - BoundedSlice, +use frame::{ + prelude::*, + traits::{ + fungibles::{Inspect, InspectFreeze, MutateFreeze}, + tokens::{ + DepositConsequence, Fortitude, IdAmount, Preservation, Provenance, WithdrawConsequence, + }, + }, }; pub use pallet::*; +#[cfg(feature = "try-runtime")] +use frame::try_runtime::TryRuntimeError; + #[cfg(test)] mod mock; #[cfg(test)] @@ -62,7 +63,7 @@ mod tests; mod impls; -#[frame_support::pallet] +#[frame::pallet] pub mod pallet { use super::*; @@ -125,7 +126,7 @@ pub mod pallet { #[pallet::hooks] impl, I: 'static> Hooks> for Pallet { #[cfg(feature = "try-runtime")] - fn try_state(_: BlockNumberFor) -> Result<(), sp_runtime::TryRuntimeError> { + fn try_state(_: BlockNumberFor) -> Result<(), TryRuntimeError> { Self::do_try_state() } } @@ -159,13 +160,13 @@ impl, I: 'static> Pallet { Ok(()) } - #[cfg(any(test, feature = "try-runtime"))] - fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> { + #[cfg(feature = "try-runtime")] + fn do_try_state() -> Result<(), TryRuntimeError> { for (asset, who, _) in FrozenBalances::::iter() { let max_frozen_amount = Freezes::::get(asset.clone(), who.clone()).iter().map(|l| l.amount).max(); - frame_support::ensure!( + ensure!( FrozenBalances::::get(asset, who) == max_frozen_amount, "The `FrozenAmount` is not equal to the maximum amount in `Freezes` for (`asset`, `who`)" ); diff --git a/substrate/frame/assets-freezer/src/mock.rs b/substrate/frame/assets-freezer/src/mock.rs index bc903a018f7b8..ad08787aba27d 100644 --- a/substrate/frame/assets-freezer/src/mock.rs +++ b/substrate/frame/assets-freezer/src/mock.rs @@ -20,23 +20,15 @@ use crate as pallet_assets_freezer; pub use crate::*; use codec::{Compact, Decode, Encode, MaxEncodedLen}; -use frame_support::{ - derive_impl, - traits::{AsEnsureOriginWithArg, ConstU64}, -}; +use frame::testing_prelude::*; use scale_info::TypeInfo; -use sp_core::{ConstU32, H256}; -use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, -}; pub type AccountId = u64; pub type Balance = u64; pub type AssetId = u32; type Block = frame_system::mocking::MockBlock; -frame_support::construct_runtime!( +construct_runtime!( pub enum Test { System: frame_system, @@ -48,7 +40,7 @@ frame_support::construct_runtime!( #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; + type BaseCallFilter = Everything; type BlockWeights = (); type BlockLength = (); type DbWeight = (); @@ -70,7 +62,7 @@ impl frame_system::Config for Test { type SystemWeightInfo = (); type SS58Prefix = (); type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; + type MaxConsumers = ConstU32<16>; } impl pallet_balances::Config for Test { @@ -132,7 +124,7 @@ impl Config for Test { type RuntimeEvent = RuntimeEvent; } -pub fn new_test_ext(execute: impl FnOnce()) -> sp_io::TestExternalities { +pub fn new_test_ext(execute: impl FnOnce()) -> TestExternalities { let t = RuntimeGenesisConfig { assets: pallet_assets::GenesisConfig { assets: vec![(1, 0, true, 1)], @@ -145,11 +137,12 @@ pub fn new_test_ext(execute: impl FnOnce()) -> sp_io::TestExternalities { } .build_storage() .unwrap(); - let mut ext: sp_io::TestExternalities = t.into(); + let mut ext: TestExternalities = t.into(); ext.execute_with(|| { System::set_block_number(1); execute(); - frame_support::assert_ok!(AssetsFreezer::do_try_state()); + #[cfg(feature = "try-runtime")] + assert_ok!(AssetsFreezer::do_try_state()); }); ext diff --git a/substrate/frame/assets-freezer/src/tests.rs b/substrate/frame/assets-freezer/src/tests.rs index 4f2dea79c705a..b890dc98b5741 100644 --- a/substrate/frame/assets-freezer/src/tests.rs +++ b/substrate/frame/assets-freezer/src/tests.rs @@ -17,22 +17,16 @@ //! Tests for pallet-assets-freezer. -use crate::mock::*; +use crate::mock::{self, *}; use codec::Compact; -use frame_support::{ - assert_ok, assert_storage_noop, - traits::{ - fungibles::{Inspect, InspectFreeze, MutateFreeze}, - tokens::{Fortitude, Preservation}, - }, -}; +use frame::testing_prelude::*; use pallet_assets::FrozenBalance; const WHO: AccountId = 1; -const ASSET_ID: AssetId = 1; +const ASSET_ID: mock::AssetId = 1; -fn test_set_freeze(id: DummyFreezeReason, amount: Balance) { +fn test_set_freeze(id: DummyFreezeReason, amount: mock::Balance) { let mut freezes = Freezes::::get(ASSET_ID, WHO); if let Some(i) = freezes.iter_mut().find(|l| l.id == id) { @@ -281,8 +275,6 @@ mod impl_mutate_freeze { } mod with_pallet_assets { - use frame_support::assert_noop; - use super::*; #[test] diff --git a/substrate/frame/atomic-swap/Cargo.toml b/substrate/frame/atomic-swap/Cargo.toml index 785bfee71b683..05a38ded91c51 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 6021aa0150742..6f9f54cc7efcb 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 f92f77ebac3da..4f091ce76ce64 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; type SessionInterface = Self; diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 1d181eb29cab7..0dacfe9c55792 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -520,7 +520,7 @@ impl Pallet { 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 7550d0c463f3d..a4546e57dab5e 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; type AdminOrigin = frame_system::EnsureRoot; diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index b7b82a43771eb..c764e2741a2a4 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::(&agent), agent_amount); + assert_eq!(pallet_staking::asset::total_balance::(&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 a78aa3f559060..14f49466f0e28 100644 --- a/substrate/frame/delegated-staking/src/types.rs +++ b/substrate/frame/delegated-staking/src/types.rs @@ -131,10 +131,6 @@ impl AgentLedger { /// /// Increments provider count if this is a new agent. pub(crate) fn update(self, key: &T::AccountId) { - if !>::contains_key(key) { - // This is a new agent. Provide for this account. - frame_system::Pallet::::inc_providers(key); - } >::insert(key, self) } @@ -142,8 +138,6 @@ impl AgentLedger { pub(crate) fn remove(key: &T::AccountId) { debug_assert!(>::contains_key(key), "Agent should exist in storage"); >::remove(key); - // Remove provider reference. - let _ = frame_system::Pallet::::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 7a48ae868a5a2..f11f9c04dbf4a 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 26a6345e145ff..b1029e89fe85f 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!(::MaxUnbonding::get(), 1); // init state of pool members. - let init_stakeable_balance_2 = pallet_staking::asset::stakeable_balance::(&2); - let init_stakeable_balance_3 = pallet_staking::asset::stakeable_balance::(&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::(&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::(&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::(&pool_bonded_account), 26); - assert_eq!(pallet_staking::asset::staked::(&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::(&pool_bonded_account), 16); + assert_eq!(delegated_balance_for(pool_bonded_account), 15); - assert_eq!(pallet_staking::asset::stakeable_balance::(&2), 20); + assert_eq!(Balances::free_balance(2), 20); assert_eq!(TotalValueLocked::::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::(&pool_bonded_account), 6); // 5 init bonded + ED - assert_eq!( - pallet_staking::asset::stakeable_balance::(&2), - init_stakeable_balance_2 - ); - assert_eq!( - pallet_staking::asset::stakeable_balance::(&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::::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 2b975d8ed9ccb..3a64964361870 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; + type Lookup = sp_runtime::traits::IdentityLookup; } 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; + type StakeAdapter = + pallet_nomination_pools::adapter::DelegateStake; 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; } +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; type TargetList = pallet_staking::UseValidatorsMap; type MaxUnlockingChunks = MaxUnlockingChunks; - type EventListeners = Pools; + type EventListeners = (Pools, DelegatedStaking); type WeightInfo = pallet_staking::weights::SubstrateWeight; type DisablingStrategy = pallet_staking::UpToLimitWithReEnablingDisablingStrategy; @@ -502,7 +523,7 @@ impl Default for BalancesExtBuilder { (100, 100), (200, 100), // stashes - (11, 1000), + (11, 1100), (21, 2000), (31, 3000), (41, 4000), @@ -582,7 +603,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() } @@ -927,7 +948,11 @@ pub(crate) fn set_minimum_election_score( } pub(crate) fn staked_amount_for(account_id: AccountId) -> Balance { - pallet_staking::asset::staked::(&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> { diff --git a/substrate/frame/examples/frame-crate/Cargo.toml b/substrate/frame/examples/frame-crate/Cargo.toml index f174c6b9054b5..46db1afc34643 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 4c361fa58660b..67f7ee21e6175 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; type AdminOrigin = frame_system::EnsureRoot; @@ -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::>(), ..Default::default() } diff --git a/substrate/frame/fast-unstake/src/tests.rs b/substrate/frame/fast-unstake/src/tests.rs index 7c11f381ca102..0fddb88e02b7b 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!(::Currency::reserved_balance(&1) - pre_reserved, 0); + assert_eq!(::Currency::reserved_balance(&1), pre_reserved); // Ensure stash no longer exists in the queue. assert_eq!(Queue::::get(1), None); @@ -297,7 +305,7 @@ mod on_idle { ); assert_eq!(Queue::::count(), 3); - assert_eq!(::Currency::reserved_balance(&1) - pre_reserved, 0); + assert_eq!(::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 de686c29015f4..cb754fb7955b5 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 = ::Balance; type SessionsPerEra = SessionsPerEra; diff --git a/substrate/frame/merkle-mountain-range/Cargo.toml b/substrate/frame/merkle-mountain-range/Cargo.toml index 04f5ab64100d3..ecbef01a9205c 100644 --- a/substrate/frame/merkle-mountain-range/Cargo.toml +++ b/substrate/frame/merkle-mountain-range/Cargo.toml @@ -16,18 +16,12 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { workspace = true } -frame-benchmarking = { optional = true, workspace = true } -frame-support = { workspace = true } -frame-system = { workspace = true } +frame = { workspace = true, features = ["runtime"] } log = { workspace = true } scale-info = { features = ["derive"], workspace = true } -sp-core = { workspace = true } -sp-io = { workspace = true } sp-mmr-primitives = { workspace = true } -sp-runtime = { workspace = true } [dev-dependencies] -array-bytes = { workspace = true, default-features = true } itertools = { workspace = true } sp-tracing = { workspace = true, default-features = true } @@ -35,24 +29,14 @@ sp-tracing = { workspace = true, default-features = true } default = ["std"] std = [ "codec/std", - "frame-benchmarking?/std", - "frame-support/std", - "frame-system/std", + "frame/std", "log/std", "scale-info/std", - "sp-core/std", - "sp-io/std", "sp-mmr-primitives/std", - "sp-runtime/std", ] runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", + "frame/runtime-benchmarks", ] try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "sp-runtime/try-runtime", + "frame/try-runtime", ] diff --git a/substrate/frame/merkle-mountain-range/src/benchmarking.rs b/substrate/frame/merkle-mountain-range/src/benchmarking.rs index 07afd9529eb26..407f1f7ead60a 100644 --- a/substrate/frame/merkle-mountain-range/src/benchmarking.rs +++ b/substrate/frame/merkle-mountain-range/src/benchmarking.rs @@ -20,8 +20,10 @@ #![cfg(feature = "runtime-benchmarks")] use crate::*; -use frame_benchmarking::v1::benchmarks_instance_pallet; -use frame_support::traits::OnInitialize; +use frame::{ + benchmarking::prelude::v1::benchmarks_instance_pallet, + deps::frame_support::traits::OnInitialize, +}; benchmarks_instance_pallet! { on_initialize { @@ -31,10 +33,10 @@ benchmarks_instance_pallet! { <>::BenchmarkHelper as BenchmarkHelper>::setup(); for leaf in 0..(leaves - 1) { - Pallet::::on_initialize((leaf as u32).into()); + as OnInitialize>>::on_initialize((leaf as u32).into()); } }: { - Pallet::::on_initialize((leaves as u32 - 1).into()); + as OnInitialize>>::on_initialize((leaves as u32 - 1).into()); } verify { assert_eq!(crate::NumberOfLeaves::::get(), leaves); } diff --git a/substrate/frame/merkle-mountain-range/src/default_weights.rs b/substrate/frame/merkle-mountain-range/src/default_weights.rs index b0ef0539018cd..d1ed12edd0628 100644 --- a/substrate/frame/merkle-mountain-range/src/default_weights.rs +++ b/substrate/frame/merkle-mountain-range/src/default_weights.rs @@ -18,16 +18,13 @@ //! Default weights for the MMR Pallet //! This file was not auto-generated. -use frame_support::weights::{ - constants::{RocksDbWeight as DbWeight, WEIGHT_REF_TIME_PER_NANOS}, - Weight, -}; +use frame::{deps::frame_support::weights::constants::*, weights_prelude::*}; impl crate::WeightInfo for () { fn on_initialize(peaks: u32) -> Weight { let peaks = u64::from(peaks); // Reading the parent hash. - let leaf_weight = DbWeight::get().reads(1); + let leaf_weight = RocksDbWeight::get().reads(1); // Blake2 hash cost. let hash_weight = Weight::from_parts(2u64 * WEIGHT_REF_TIME_PER_NANOS, 0); // No-op hook. @@ -36,6 +33,6 @@ impl crate::WeightInfo for () { leaf_weight .saturating_add(hash_weight) .saturating_add(hook_weight) - .saturating_add(DbWeight::get().reads_writes(2 + peaks, 2 + peaks)) + .saturating_add(RocksDbWeight::get().reads_writes(2 + peaks, 2 + peaks)) } } diff --git a/substrate/frame/merkle-mountain-range/src/lib.rs b/substrate/frame/merkle-mountain-range/src/lib.rs index 7dfe95c83361c..76d6c2a1ac76f 100644 --- a/substrate/frame/merkle-mountain-range/src/lib.rs +++ b/substrate/frame/merkle-mountain-range/src/lib.rs @@ -59,20 +59,17 @@ extern crate alloc; use alloc::vec::Vec; -use frame_support::weights::Weight; -use frame_system::pallet_prelude::{BlockNumberFor, HeaderFor}; use log; -use sp_mmr_primitives::utils; -use sp_runtime::{ - traits::{self, One, Saturating}, - SaturatedConversion, -}; -pub use pallet::*; +use frame::prelude::*; + pub use sp_mmr_primitives::{ - self as primitives, utils::NodesUtils, Error, LeafDataProvider, LeafIndex, NodeIndex, + self as primitives, utils, utils::NodesUtils, AncestryProof, Error, FullLeaf, LeafDataProvider, + LeafIndex, LeafProof, NodeIndex, OnNewRoot, }; +pub use pallet::*; + #[cfg(feature = "runtime-benchmarks")] mod benchmarking; mod default_weights; @@ -90,11 +87,11 @@ mod tests; /// crate-local wrapper over [frame_system::Pallet]. Since the current block hash /// is not available (since the block is not finished yet), /// we use the `parent_hash` here along with parent block number. -pub struct ParentNumberAndHash { - _phantom: core::marker::PhantomData, +pub struct ParentNumberAndHash { + _phantom: PhantomData, } -impl LeafDataProvider for ParentNumberAndHash { +impl LeafDataProvider for ParentNumberAndHash { type LeafData = (BlockNumberFor, ::Hash); fn leaf_data() -> Self::LeafData { @@ -111,13 +108,11 @@ pub trait BlockHashProvider { } /// Default implementation of BlockHashProvider using frame_system. -pub struct DefaultBlockHashProvider { +pub struct DefaultBlockHashProvider { _phantom: core::marker::PhantomData, } -impl BlockHashProvider, T::Hash> - for DefaultBlockHashProvider -{ +impl BlockHashProvider, T::Hash> for DefaultBlockHashProvider { fn block_hash(block_number: BlockNumberFor) -> T::Hash { frame_system::Pallet::::block_hash(block_number) } @@ -142,17 +137,16 @@ impl BenchmarkHelper for () { type ModuleMmr = mmr::Mmr>; /// Leaf data. -type LeafOf = <>::LeafData as primitives::LeafDataProvider>::LeafData; +type LeafOf = <>::LeafData as LeafDataProvider>::LeafData; /// Hashing used for the pallet. pub(crate) type HashingOf = >::Hashing; /// Hash type used for the pallet. -pub(crate) type HashOf = <>::Hashing as traits::Hash>::Output; +pub(crate) type HashOf = <>::Hashing as Hash>::Output; -#[frame_support::pallet] +#[frame::pallet] pub mod pallet { use super::*; - use frame_support::pallet_prelude::*; #[pallet::pallet] pub struct Pallet(PhantomData<(T, I)>); @@ -180,7 +174,7 @@ pub mod pallet { /// /// Then we create a tuple of these two hashes, SCALE-encode it (concatenate) and /// hash, to obtain a new MMR inner node - the new peak. - type Hashing: traits::Hash; + type Hashing: Hash; /// Data stored in the leaf nodes. /// @@ -198,7 +192,7 @@ pub mod pallet { /// two forks with identical line of ancestors compete to write the same offchain key, but /// that's fine as long as leaves only contain data coming from ancestors - conflicting /// writes are identical). - type LeafData: primitives::LeafDataProvider; + type LeafData: LeafDataProvider; /// A hook to act on the new MMR root. /// @@ -206,7 +200,7 @@ pub mod pallet { /// apart from having it in the storage. For instance you might output it in the header /// digest (see [`frame_system::Pallet::deposit_log`]) to make it available for Light /// Clients. Hook complexity should be `O(1)`. - type OnNewRoot: primitives::OnNewRoot>; + type OnNewRoot: OnNewRoot>; /// Block hash provider for a given block number. type BlockHashProvider: BlockHashProvider< @@ -248,9 +242,8 @@ pub mod pallet { #[pallet::hooks] impl, I: 'static> Hooks> for Pallet { fn on_initialize(_n: BlockNumberFor) -> Weight { - use primitives::LeafDataProvider; let leaves = NumberOfLeaves::::get(); - let peaks_before = sp_mmr_primitives::utils::NodesUtils::new(leaves).number_of_peaks(); + let peaks_before = NodesUtils::new(leaves).number_of_peaks(); let data = T::LeafData::leaf_data(); // append new leaf to MMR @@ -268,12 +261,12 @@ pub mod pallet { return T::WeightInfo::on_initialize(peaks_before as u32) }, }; - >::on_new_root(&root); + >::on_new_root(&root); NumberOfLeaves::::put(leaves); RootHash::::put(root); - let peaks_after = sp_mmr_primitives::utils::NodesUtils::new(leaves).number_of_peaks(); + let peaks_after = NodesUtils::new(leaves).number_of_peaks(); T::WeightInfo::on_initialize(peaks_before.max(peaks_after) as u32) } @@ -290,28 +283,28 @@ pub mod pallet { pub fn verify_leaves_proof( root: H::Output, leaves: Vec>, - proof: primitives::LeafProof, -) -> Result<(), primitives::Error> + proof: LeafProof, +) -> Result<(), Error> where - H: traits::Hash, - L: primitives::FullLeaf, + H: Hash, + L: FullLeaf, { let is_valid = mmr::verify_leaves_proof::(root, leaves, proof)?; if is_valid { Ok(()) } else { - Err(primitives::Error::Verify.log_debug(("The proof is incorrect.", root))) + Err(Error::Verify.log_debug(("The proof is incorrect.", root))) } } /// Stateless ancestry proof verification. pub fn verify_ancestry_proof( root: H::Output, - ancestry_proof: primitives::AncestryProof, + ancestry_proof: AncestryProof, ) -> Result where - H: traits::Hash, - L: primitives::FullLeaf, + H: Hash, + L: FullLeaf, { mmr::verify_ancestry_proof::(root, ancestry_proof) .map_err(|_| Error::Verify.log_debug(("The ancestry proof is incorrect.", root))) @@ -383,7 +376,7 @@ impl, I: 'static> Pallet { pub fn generate_proof( block_numbers: Vec>, best_known_block_number: Option>, - ) -> Result<(Vec>, primitives::LeafProof>), primitives::Error> { + ) -> Result<(Vec>, LeafProof>), Error> { // check whether best_known_block_number provided, else use current best block let best_known_block_number = best_known_block_number.unwrap_or_else(|| >::block_number()); @@ -393,7 +386,7 @@ impl, I: 'static> Pallet { // we need to translate the block_numbers into leaf indices. let leaf_indices = block_numbers .iter() - .map(|block_num| -> Result { + .map(|block_num| -> Result { Self::block_num_to_leaf_index(*block_num) }) .collect::, _>>()?; @@ -410,14 +403,15 @@ impl, I: 'static> Pallet { /// or the proof is invalid. pub fn verify_leaves( leaves: Vec>, - proof: primitives::LeafProof>, - ) -> Result<(), primitives::Error> { + proof: LeafProof>, + ) -> Result<(), Error> { if proof.leaf_count > NumberOfLeaves::::get() || proof.leaf_count == 0 || proof.items.len().saturating_add(leaves.len()) as u64 > proof.leaf_count { - return Err(primitives::Error::Verify - .log_debug("The proof has incorrect number of leaves or proof items.")) + return Err( + Error::Verify.log_debug("The proof has incorrect number of leaves or proof items.") + ) } let mmr: ModuleMmr = mmr::Mmr::new(proof.leaf_count); @@ -425,14 +419,14 @@ impl, I: 'static> Pallet { if is_valid { Ok(()) } else { - Err(primitives::Error::Verify.log_debug("The proof is incorrect.")) + Err(Error::Verify.log_debug("The proof is incorrect.")) } } pub fn generate_ancestry_proof( prev_block_number: BlockNumberFor, best_known_block_number: Option>, - ) -> Result>, Error> { + ) -> Result>, Error> { // check whether best_known_block_number provided, else use current best block let best_known_block_number = best_known_block_number.unwrap_or_else(|| >::block_number()); @@ -445,8 +439,7 @@ impl, I: 'static> Pallet { } #[cfg(feature = "runtime-benchmarks")] - pub fn generate_mock_ancestry_proof() -> Result>, Error> - { + pub fn generate_mock_ancestry_proof() -> Result>, Error> { let leaf_count = Self::block_num_to_leaf_count(>::block_number())?; let mmr: ModuleMmr = mmr::Mmr::new(leaf_count); mmr.generate_mock_ancestry_proof() @@ -454,7 +447,7 @@ impl, I: 'static> Pallet { pub fn verify_ancestry_proof( root: HashOf, - ancestry_proof: primitives::AncestryProof>, + ancestry_proof: AncestryProof>, ) -> Result, Error> { verify_ancestry_proof::, LeafOf>(root, ancestry_proof) } diff --git a/substrate/frame/merkle-mountain-range/src/mmr/mmr.rs b/substrate/frame/merkle-mountain-range/src/mmr/mmr.rs index f9a4580b9bb30..a9818ba471019 100644 --- a/substrate/frame/merkle-mountain-range/src/mmr/mmr.rs +++ b/substrate/frame/merkle-mountain-range/src/mmr/mmr.rs @@ -20,11 +20,14 @@ use crate::{ storage::{OffchainStorage, RuntimeStorage, Storage}, Hasher, Node, NodeOf, }, - primitives::{self, Error, NodeIndex}, + primitives::{ + mmr_lib, mmr_lib::MMRStoreReadOps, utils::NodesUtils, AncestryProof, Error, FullLeaf, + LeafIndex, LeafProof, NodeIndex, + }, Config, HashOf, HashingOf, }; use alloc::vec::Vec; -use sp_mmr_primitives::{mmr_lib, mmr_lib::MMRStoreReadOps, utils::NodesUtils, LeafIndex}; +use frame::prelude::*; /// Stateless verification of the proof for a batch of leaves. /// Note, the leaves should be sorted such that corresponding leaves and leaf indices have the @@ -33,11 +36,11 @@ use sp_mmr_primitives::{mmr_lib, mmr_lib::MMRStoreReadOps, utils::NodesUtils, Le pub fn verify_leaves_proof( root: H::Output, leaves: Vec>, - proof: primitives::LeafProof, + proof: LeafProof, ) -> Result where - H: sp_runtime::traits::Hash, - L: primitives::FullLeaf, + H: Hash, + L: FullLeaf, { let size = NodesUtils::new(proof.leaf_count).size(); @@ -62,11 +65,11 @@ where pub fn verify_ancestry_proof( root: H::Output, - ancestry_proof: primitives::AncestryProof, + ancestry_proof: AncestryProof, ) -> Result where - H: sp_runtime::traits::Hash, - L: primitives::FullLeaf, + H: Hash, + L: FullLeaf, { let mmr_size = NodesUtils::new(ancestry_proof.leaf_count).size(); @@ -104,7 +107,7 @@ pub struct Mmr where T: Config, I: 'static, - L: primitives::FullLeaf, + L: FullLeaf, Storage: MMRStoreReadOps> + mmr_lib::MMRStoreWriteOps>, { @@ -116,7 +119,7 @@ impl Mmr where T: Config, I: 'static, - L: primitives::FullLeaf, + L: FullLeaf, Storage: MMRStoreReadOps> + mmr_lib::MMRStoreWriteOps>, { @@ -133,7 +136,7 @@ where pub fn verify_leaves_proof( &self, leaves: Vec, - proof: primitives::LeafProof>, + proof: LeafProof>, ) -> Result { let p = mmr_lib::MerkleProof::, Hasher, L>>::new( self.mmr.mmr_size(), @@ -167,7 +170,7 @@ impl Mmr where T: Config, I: 'static, - L: primitives::FullLeaf, + L: FullLeaf, { /// Push another item to the MMR. /// @@ -195,7 +198,7 @@ impl Mmr where T: Config, I: 'static, - L: primitives::FullLeaf + codec::Decode, + L: FullLeaf + codec::Decode, { /// Generate a proof for given leaf indices. /// @@ -204,7 +207,7 @@ where pub fn generate_proof( &self, leaf_indices: Vec, - ) -> Result<(Vec, primitives::LeafProof>), Error> { + ) -> Result<(Vec, LeafProof>), Error> { let positions = leaf_indices .iter() .map(|index| mmr_lib::leaf_index_to_pos(*index)) @@ -222,7 +225,7 @@ where self.mmr .gen_proof(positions) .map_err(|e| Error::GenerateProof.log_error(e)) - .map(|p| primitives::LeafProof { + .map(|p| LeafProof { leaf_indices, leaf_count, items: p.proof_items().iter().map(|x| x.hash()).collect(), @@ -233,14 +236,14 @@ where pub fn generate_ancestry_proof( &self, prev_leaf_count: LeafIndex, - ) -> Result>, Error> { + ) -> Result>, Error> { let prev_mmr_size = NodesUtils::new(prev_leaf_count).size(); let raw_ancestry_proof = self .mmr .gen_ancestry_proof(prev_mmr_size) .map_err(|e| Error::GenerateProof.log_error(e))?; - Ok(primitives::AncestryProof { + Ok(AncestryProof { prev_peaks: raw_ancestry_proof.prev_peaks.into_iter().map(|p| p.hash()).collect(), prev_leaf_count, leaf_count: self.leaves, @@ -258,12 +261,10 @@ where /// The generated proof contains all the leafs in the MMR, so this way we can generate a proof /// with exactly `leaf_count` items. #[cfg(feature = "runtime-benchmarks")] - pub fn generate_mock_ancestry_proof( - &self, - ) -> Result>, Error> { + pub fn generate_mock_ancestry_proof(&self) -> Result>, Error> { use crate::ModuleMmr; use alloc::vec; - use sp_mmr_primitives::mmr_lib::helper; + use mmr_lib::helper; let mmr: ModuleMmr = Mmr::new(self.leaves); let store = >::default(); @@ -289,7 +290,7 @@ where proof_items.push((leaf_pos, leaf)); } - Ok(sp_mmr_primitives::AncestryProof { + Ok(AncestryProof { prev_peaks, prev_leaf_count: self.leaves, leaf_count: self.leaves, diff --git a/substrate/frame/merkle-mountain-range/src/mmr/mod.rs b/substrate/frame/merkle-mountain-range/src/mmr/mod.rs index 5b73f53506e92..85d00f8a65dee 100644 --- a/substrate/frame/merkle-mountain-range/src/mmr/mod.rs +++ b/substrate/frame/merkle-mountain-range/src/mmr/mod.rs @@ -18,10 +18,9 @@ mod mmr; pub mod storage; -use sp_mmr_primitives::{mmr_lib, DataOrHash, FullLeaf}; -use sp_runtime::traits; - pub use self::mmr::{verify_ancestry_proof, verify_leaves_proof, Mmr}; +use crate::primitives::{mmr_lib, DataOrHash, FullLeaf}; +use frame::traits; /// Node type for runtime `T`. pub type NodeOf = Node<>::Hashing, L>; diff --git a/substrate/frame/merkle-mountain-range/src/mmr/storage.rs b/substrate/frame/merkle-mountain-range/src/mmr/storage.rs index 02852388b4171..c201c0ea846d3 100644 --- a/substrate/frame/merkle-mountain-range/src/mmr/storage.rs +++ b/substrate/frame/merkle-mountain-range/src/mmr/storage.rs @@ -17,18 +17,22 @@ //! An MMR storage implementation. -use alloc::{vec, vec::Vec}; -use codec::Encode; -use core::iter::Peekable; -use log::{debug, trace}; -use sp_core::offchain::StorageKind; -use sp_mmr_primitives::{mmr_lib, mmr_lib::helper, utils::NodesUtils}; - use crate::{ mmr::{Node, NodeOf}, - primitives::{self, NodeIndex}, + primitives::{mmr_lib, mmr_lib::helper, utils::NodesUtils, FullLeaf, NodeIndex}, BlockHashProvider, Config, Nodes, NumberOfLeaves, Pallet, }; +use alloc::{vec, vec::Vec}; +use codec::Encode; +use core::iter::Peekable; +use frame::{ + deps::{ + sp_core::offchain::StorageKind, + sp_io::{offchain, offchain_index}, + }, + prelude::*, +}; +use log::{debug, trace}; /// A marker type for runtime-specific storage implementation. /// @@ -48,20 +52,20 @@ pub struct OffchainStorage; impl OffchainStorage { fn get(key: &[u8]) -> Option> { - sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &key) + offchain::local_storage_get(StorageKind::PERSISTENT, &key) } #[cfg(not(feature = "runtime-benchmarks"))] fn set, I: 'static>(key: &[u8], value: &[u8]) { - sp_io::offchain_index::set(key, value); + offchain_index::set(key, value); } #[cfg(feature = "runtime-benchmarks")] fn set, I: 'static>(key: &[u8], value: &[u8]) { if crate::pallet::UseLocalStorage::::get() { - sp_io::offchain::local_storage_set(StorageKind::PERSISTENT, key, value); + offchain::local_storage_set(StorageKind::PERSISTENT, key, value); } else { - sp_io::offchain_index::set(key, value); + offchain_index::set(key, value); } } } @@ -82,7 +86,7 @@ impl mmr_lib::MMRStoreReadOps> for Storage, I: 'static, - L: primitives::FullLeaf + codec::Decode, + L: FullLeaf + Decode, { fn get_elem(&self, pos: NodeIndex) -> mmr_lib::Result>> { // Find out which leaf added node `pos` in the MMR. @@ -120,7 +124,7 @@ impl mmr_lib::MMRStoreWriteOps> for Storage, I: 'static, - L: primitives::FullLeaf + codec::Decode, + L: FullLeaf + Decode, { fn append(&mut self, _: NodeIndex, _: Vec>) -> mmr_lib::Result<()> { panic!("MMR must not be altered in the off-chain context.") @@ -131,7 +135,7 @@ impl mmr_lib::MMRStoreReadOps> for Storage, I: 'static, - L: primitives::FullLeaf, + L: FullLeaf, { fn get_elem(&self, pos: NodeIndex) -> mmr_lib::Result>> { Ok(Nodes::::get(pos).map(Node::Hash)) @@ -142,7 +146,7 @@ impl mmr_lib::MMRStoreWriteOps> for Storage, I: 'static, - L: primitives::FullLeaf, + L: FullLeaf, { fn append(&mut self, pos: NodeIndex, elems: Vec>) -> mmr_lib::Result<()> { if elems.is_empty() { @@ -205,7 +209,7 @@ impl Storage where T: Config, I: 'static, - L: primitives::FullLeaf, + L: FullLeaf, { fn store_to_offchain( pos: NodeIndex, diff --git a/substrate/frame/merkle-mountain-range/src/mock.rs b/substrate/frame/merkle-mountain-range/src/mock.rs index 606719c6deba1..4c234e0d94aaf 100644 --- a/substrate/frame/merkle-mountain-range/src/mock.rs +++ b/substrate/frame/merkle-mountain-range/src/mock.rs @@ -18,14 +18,20 @@ use crate as pallet_mmr; use crate::*; +use crate::{ + frame_system::DefaultConfig, + primitives::{Compact, LeafDataProvider}, +}; use codec::{Decode, Encode}; -use frame_support::{derive_impl, parameter_types}; -use sp_mmr_primitives::{Compact, LeafDataProvider}; -use sp_runtime::traits::Keccak256; +use frame::{ + deps::frame_support::derive_impl, + prelude::{frame_system, frame_system::config_preludes::TestDefaultConfig}, + testing_prelude::*, +}; -type Block = frame_system::mocking::MockBlock; +type Block = MockBlock; -frame_support::construct_runtime!( +construct_runtime!( pub enum Test { System: frame_system, @@ -33,7 +39,7 @@ frame_support::construct_runtime!( } ); -#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +#[derive_impl(TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; } diff --git a/substrate/frame/merkle-mountain-range/src/tests.rs b/substrate/frame/merkle-mountain-range/src/tests.rs index 93e3d06eaa0af..ae0c58e91aba4 100644 --- a/substrate/frame/merkle-mountain-range/src/tests.rs +++ b/substrate/frame/merkle-mountain-range/src/tests.rs @@ -17,19 +17,21 @@ use crate::{mock::*, *}; -use frame_support::traits::{Get, OnInitialize}; -use sp_core::{ - offchain::{testing::TestOffchainExt, OffchainDbExt, OffchainWorkerExt}, - H256, +use crate::primitives::{mmr_lib::helper, utils, Compact, LeafProof}; + +use frame::{ + deps::sp_core::{ + offchain::{testing::TestOffchainExt, OffchainDbExt, OffchainWorkerExt}, + H256, + }, + testing_prelude::*, }; -use sp_mmr_primitives::{mmr_lib::helper, utils, Compact, LeafProof}; -use sp_runtime::BuildStorage; -pub(crate) fn new_test_ext() -> sp_io::TestExternalities { +pub(crate) fn new_test_ext() -> TestState { frame_system::GenesisConfig::::default().build_storage().unwrap().into() } -fn register_offchain_ext(ext: &mut sp_io::TestExternalities) { +fn register_offchain_ext(ext: &mut TestState) { let (offchain, _offchain_state) = TestOffchainExt::with_offchain_db(ext.offchain_db()); ext.register_extension(OffchainDbExt::new(offchain.clone())); ext.register_extension(OffchainWorkerExt::new(offchain)); @@ -54,7 +56,7 @@ pub(crate) fn hex(s: &str) -> H256 { s.parse().unwrap() } -type BlockNumber = frame_system::pallet_prelude::BlockNumberFor; +type BlockNumber = BlockNumberFor; fn decode_node( v: Vec, @@ -517,7 +519,7 @@ fn should_verify() { } fn generate_and_verify_batch_proof( - ext: &mut sp_io::TestExternalities, + ext: &mut TestExternalities, block_numbers: &Vec, blocks_to_add: usize, ) { @@ -719,7 +721,6 @@ fn should_verify_on_the_next_block_since_there_is_no_pruning_yet() { #[test] fn should_verify_canonicalized() { - use frame_support::traits::Hooks; sp_tracing::init_for_tests(); // How deep is our fork-aware storage (in terms of blocks/leaves, nodes will be more). diff --git a/substrate/frame/mixnet/Cargo.toml b/substrate/frame/mixnet/Cargo.toml index 0ae3b3938c608..33bf7146980d5 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 0d175617c9c23..e18e14f2626bf 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 7e55ad178091f..86a78e6e36153 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 7ddb78cca3f9b..20c5eafbcfc59 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(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::::mint_into(&pool_acc, CurrencyOf::::minimum_balance()); + pallet_staking::Pallet::::migrate_to_direct_staker(&pool_acc); } @@ -141,14 +145,6 @@ fn vote_to_balance( 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(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 { /// 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::::depositor_min_bond() * T::MaxUnbonding::get().into() * 4u32.into(); @@ -993,7 +986,7 @@ mod benchmarks { // verify user balance in the pool. assert_eq!(PoolMembers::::get(&depositor).unwrap().total_balance(), deposit_amount); // verify delegated balance. - assert_if_delegate::( + 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::( + assert!( T::StakeAdapter::member_delegation_balance(Member::from(depositor.clone())) == Some(deposit_amount), ); @@ -1041,13 +1034,11 @@ mod benchmarks { #[block] { - assert_if_delegate::( - Pools::::apply_slash( - RuntimeOrigin::Signed(slash_reporter.clone()).into(), - depositor_lookup.clone(), - ) - .is_ok(), - ); + assert!(Pools::::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::::get(&depositor).unwrap().total_balance(), deposit_amount / 2u32.into() ); - assert_if_delegate::( + 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::(1); #[block] { - assert_if_delegate::( - Pools::::migrate_pool_to_delegate_stake( - RuntimeOrigin::Signed(depositor.clone()).into(), - 1u32.into(), - ) - .is_ok(), - ); + assert!(Pools::::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::::minimum_balance()) ); } @@ -1152,13 +1141,11 @@ mod benchmarks { let _ = migrate_to_transfer_stake::(1); // Now migrate pool to delegate stake keeping delegators unmigrated. - assert_if_delegate::( - Pools::::migrate_pool_to_delegate_stake( - RuntimeOrigin::Signed(depositor.clone()).into(), - 1u32.into(), - ) - .is_ok(), - ); + assert!(Pools::::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::( - Pools::::migrate_delegation( - RuntimeOrigin::Signed(depositor.clone()).into(), - depositor_lookup.clone(), - ) - .is_ok(), - ); + assert!(Pools::::migrate_delegation( + RuntimeOrigin::Signed(depositor.clone()).into(), + depositor_lookup.clone(), + ) + .is_ok(),); } // verify balances once more. - assert_if_delegate::( + 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 15d9e2c56031f..7c09cf22ad51e 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; diff --git a/substrate/frame/nomination-pools/src/adapter.rs b/substrate/frame/nomination-pools/src/adapter.rs index f125919dabfa6..f1c68af4ea6ad 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(PhantomData<(T, Staking)>); +#[allow(deprecated)] impl, AccountId = T::AccountId>> StakeStrategy for TransferStake { @@ -262,7 +265,8 @@ impl, AccountId = T: pool_account: Pool, _: Member, ) -> BalanceOf { - 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) -> Option> { diff --git a/substrate/frame/nomination-pools/src/mock.rs b/substrate/frame/nomination-pools/src/mock.rs index f544e79ec4819..f4552389a267a 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 = Default::default(); + pub storage DelegatorBalanceMap: BTreeMap = Default::default(); +} +pub struct DelegateMock; +impl DelegationInterface for DelegateMock { + type Balance = Balance; + type AccountId = AccountId; + fn agent_balance(agent: Agent) -> Option { + AgentBalanceMap::get() + .get(&agent.get()) + .copied() + .map(|(delegated, _, pending)| delegated - pending) + } + + fn agent_transferable_balance(agent: Agent) -> Option { + AgentBalanceMap::get() + .get(&agent.get()) + .copied() + .map(|(_, unclaimed_withdrawals, _)| unclaimed_withdrawals) + } + + fn delegator_balance(delegator: Delegator) -> Option { + DelegatorBalanceMap::get().get(&delegator.get()).copied() + } + + fn register_agent( + agent: Agent, + _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) -> 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, + agent: Agent, + 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, + agent: Agent, + 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) -> Option { + AgentBalanceMap::get() + .get(&agent.get()) + .copied() + .map(|(_, _, pending_slash)| pending_slash) + } + + fn delegator_slash( + agent: Agent, + delegator: Delegator, + value: Self::Balance, + _maybe_reporter: Option, + ) -> 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, + _reward_account: &Self::AccountId, + ) -> DispatchResult { + unimplemented!("not used in current unit tests") + } + + fn migrate_delegation( + _agent: Agent, + _delegator: Delegator, + _value: Self::Balance, + ) -> DispatchResult { + unimplemented!("not used in current unit tests") + } + + #[cfg(feature = "runtime-benchmarks")] + fn force_kill_agent(_agent: Agent) { + 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; + type StakeAdapter = adapter::DelegateStake; 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 { + ::StakeAdapter::member_delegation_balance(Member::from(who)) + .expect("who must be a pool member") +} + +pub fn pool_balance(id: PoolId) -> Balance { + ::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 06261699a5b23..c46638d2f8f7b 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 { <::MaxPointsToBalance as Get>::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::::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::::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::::OverflowRisk @@ -754,29 +744,13 @@ mod join { let max_points_to_balance: u128 = <::MaxPointsToBalance as Get>::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::::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:: { 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::::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::::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::::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::::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::::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::::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::::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::::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::::get(1).unwrap(), Default::default(),); assert_eq!(Currency::free_balance(&100), 100 + 100); + assert_eq!(member_delegation(100), 0); assert!(!PoolMembers::::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::::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::::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::::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::::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::::get(10).unwrap().points, 10); assert_eq!(BondedPools::::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::::get(10).unwrap().points, 20); assert_eq!(BondedPools::::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::::get(10).unwrap().points, 40); assert_eq!(BondedPools::::get(1).unwrap().points, 40); @@ -5221,13 +5151,15 @@ mod bond_extra { // given assert_eq!(PoolMembers::::get(10).unwrap().points, 10); assert_eq!(BondedPools::::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::::get(10).unwrap().points, 10 + claimable_reward); assert_eq!(BondedPools::::get(1).unwrap().points, 10 + claimable_reward); @@ -5264,8 +5196,8 @@ mod bond_extra { assert_eq!(PoolMembers::::get(20).unwrap().points, 20); assert_eq!(BondedPools::::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::::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::::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::::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::::get(20).unwrap().points, 20 + 2); assert_eq!(BondedPools::::get(1).unwrap().points, 30 + 3); @@ -5320,8 +5252,8 @@ mod bond_extra { assert_eq!(PoolMembers::::get(10).unwrap().points, 10); assert_eq!(PoolMembers::::get(20).unwrap().points, 20); assert_eq!(BondedPools::::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::::get(10).unwrap().points, 10 + 1); assert_eq!(BondedPools::::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::::get(20).unwrap().points, 30); assert_eq!(BondedPools::::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::::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::::get(11).unwrap(), - PoolMember:: { 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::::NotSupported - ); - - // members cannot be migrated either. - assert!(!Pools::api_member_needs_delegate_migration(10)); - assert_noop!( - Pools::migrate_delegation(RuntimeOrigin::signed(10), 11), - Error::::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::::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 cc6335959ab73..54783332aa3ef 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::::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::::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::::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::::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 50e1c53263131..7aa8019b9c42c 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; type AdminOrigin = frame_system::EnsureRoot; diff --git a/substrate/frame/offences/benchmarking/src/inner.rs b/substrate/frame/offences/benchmarking/src/inner.rs index 75f3e9931e34c..3d3cd470bc24c 100644 --- a/substrate/frame/offences/benchmarking/src/inner.rs +++ b/substrate/frame/offences/benchmarking/src/inner.rs @@ -180,16 +180,12 @@ where ::RuntimeEvent: TryInto>, { // 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::::read_events_for_pallet::>().len(), - 2 * (offender_count + 1) + 3 - ); + // deposit to reporter + reporter account endowed. + assert_eq!(System::::read_events_for_pallet::>().len(), 2); // (n nominators + one validator) * slashed + Slash Reported assert_eq!( System::::read_events_for_pallet::>().len(), - 1 * (offender_count + 1) + 1 + 1 * (offender_count + 1) as usize + 1 ); // offence assert_eq!(System::::read_events_for_pallet::().len(), 1); diff --git a/substrate/frame/offences/benchmarking/src/mock.rs b/substrate/frame/offences/benchmarking/src/mock.rs index c5c178aa4443d..3c81f2a664e32 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 = ::Balance; type UnixTime = pallet_timestamp::Pallet; diff --git a/substrate/frame/proxy/Cargo.toml b/substrate/frame/proxy/Cargo.toml index a36b2c1cb9c3a..3f2565abac88d 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 1284f5ee8947b..0959cc50638ba 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/fixtures/Cargo.toml b/substrate/frame/revive/fixtures/Cargo.toml index e17bc88a3847a..a6f25cc26f3c0 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 bfb9aaedd6f5c..1a0a635420ad5 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/tracing.rs b/substrate/frame/revive/fixtures/contracts/tracing.rs new file mode 100644 index 0000000000000..9cbef3bbc8435 --- /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/tracing_callee.rs b/substrate/frame/revive/fixtures/contracts/tracing_callee.rs new file mode 100644 index 0000000000000..d44771e417f9d --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/tracing_callee.rs @@ -0,0 +1,45 @@ +// 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. +#![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!(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/rpc/examples/js/pvm/Errors.polkavm b/substrate/frame/revive/rpc/examples/js/pvm/Errors.polkavm index 77de4ff3b1b3f..48de6e0aa0c6c 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 6dbc5ca8b108c..cea22e46adcad 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 488ee684f0c4a..67f11e68f1173 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 38a1098fe3a76..29efafd8722db 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 d0082db90e5e3..78455fcdd7c64 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/src/client.rs b/substrate/frame/revive/rpc/src/client.rs index cd0effe7faf2f..a5a022f97228d 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 { } } -/// Extract the revert message from a revert("msg") solidity statement. -fn extract_revert_message(exec_data: &[u8]) -> Option { - 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 { @@ -350,7 +310,9 @@ impl Client { }; log::debug!(target: LOG_TARGET, "Pushing block: {}", block.number()); - callback(block).await?; + if let Err(err) = callback(block).await { + log::error!(target: LOG_TARGET, "Failed to process block: {err:?}"); + } } log::info!(target: LOG_TARGET, "Block subscription ended"); diff --git a/substrate/frame/revive/src/benchmarking/mod.rs b/substrate/frame/revive/src/benchmarking/mod.rs index 18d7bb0afc31a..16bdd6d1a18a0 100644 --- a/substrate/frame/revive/src/benchmarking/mod.rs +++ b/substrate/frame/revive/src/benchmarking/mod.rs @@ -772,7 +772,7 @@ mod benchmarks { let mut setup = CallSetup::::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); diff --git a/substrate/frame/revive/src/debug.rs b/substrate/frame/revive/src/debug.rs deleted file mode 100644 index d1fc0823e03df..0000000000000 --- 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: Tracing + CallInterceptor {} - -impl Debugger for V where V: Tracing + CallInterceptor {} - -/// Defines methods to capture contract calls, enabling external observers to -/// measure, trace, and react to contract interactions. -pub trait Tracing { - /// 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 Tracing 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 { - /// 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; -} - -impl CallInterceptor for () { - fn intercept_call( - _contract_address: &H160, - _entry_point: ExportedFunction, - _input_data: &[u8], - ) -> Option { - None - } -} diff --git a/substrate/frame/revive/src/evm.rs b/substrate/frame/revive/src/evm.rs index c8c967fbe091b..33660a36aa6ea 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 { + 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 fe18c8735bed4..7a34fdc83f9a5 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 c2d64f8e5e424..f11966d0072cf 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; - } - - impl HexCodec for u8 { - type Error = core::num::ParseIntError; - fn to_hex(&self) -> String { - format!("0x{:x}", self) - } - fn from_hex(s: String) -> Result { - u8::from_str_radix(s.trim_start_matches("0x"), 16) - } - } - - impl HexCodec for [u8; T] { - type Error = hex::FromHexError; - fn to_hex(&self) -> String { - format!("0x{}", hex::encode(self)) - } - fn from_hex(s: String) -> Result { - let data = hex::decode(s.trim_start_matches("0x"))?; - data.try_into().map_err(|_| hex::FromHexError::InvalidStringLength) - } - } - - impl HexCodec for Vec { - type Error = hex::FromHexError; - fn to_hex(&self) -> String { - format!("0x{}", hex::encode(self)) - } - fn from_hex(s: String) -> Result { - hex::decode(s.trim_start_matches("0x")) - } - } - - pub fn serialize(value: &T, serializer: S) -> Result - where - S: Serializer, - T: HexCodec, - { - let s = value.to_hex(); - serializer.serialize_str(&s) - } - - pub fn deserialize<'de, D, T>(deserializer: D) -> Result - where - D: Deserializer<'de>, - T: 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 { @@ -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, 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 0000000000000..0857a59fbf3b6 --- /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(self, gas_mapper: G) -> CallTracer { + 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(deserializer: D) -> Result + 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(self, mut map: M) -> Result + where + M: MapAccess<'de>, + { + let mut tracer_type: Option = None; + let mut with_logs = None; + + while let Some(key) = map.next_key::()? { + match key.as_str() { + "tracer" => { + tracer_type = map.next_value()?; + }, + "tracerConfig" => { + #[derive(Deserialize)] + struct CallTracerConfig { + #[serde(rename = "withLogs")] + with_logs: Option, + } + 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 { + /// Address of the sender. + pub from: H160, + /// Address of the receiver. + pub to: H160, + /// Call input data. + pub input: Vec, + /// 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, + /// The revert reason, if the call reverted. + #[serde(rename = "revertReason")] + pub revert_reason: Option, + /// List of sub-calls. + #[serde(skip_serializing_if = "Vec::is_empty")] + pub calls: Vec>, + /// List of logs emitted during the call. + #[serde(skip_serializing_if = "Vec::is_empty")] + pub logs: Vec, +} + +/// 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, + /// Position of the log relative to subcalls within the same trace + /// See 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 0000000000000..ba07b36fa4be6 --- /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; +} + +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 { + <$t>::from_str_radix(s.trim_start_matches("0x"), 16) + } + } + )* + }; +} + +impl_hex_codec!(u8, u32); + +impl HexCodec for [u8; T] { + type Error = hex::FromHexError; + fn to_hex(&self) -> String { + format!("0x{}", hex::encode(self)) + } + fn from_hex(s: String) -> Result { + let data = hex::decode(s.trim_start_matches("0x"))?; + data.try_into().map_err(|_| hex::FromHexError::InvalidStringLength) + } +} + +impl HexCodec for Vec { + type Error = hex::FromHexError; + fn to_hex(&self) -> String { + format!("0x{}", hex::encode(self)) + } + fn from_hex(s: String) -> Result { + hex::decode(s.trim_start_matches("0x")) + } +} + +pub fn serialize(value: &T, serializer: S) -> Result +where + S: Serializer, + T: HexCodec, +{ + let s = value.to_hex(); + serializer.serialize_str(&s) +} + +pub fn deserialize<'de, D, T>(deserializer: D) -> Result +where + D: Deserializer<'de>, + T: 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 d4b344e20eb85..0e5fc3da545b5 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(weight: Weight) -> U256 +where + BalanceOf: Into, +{ + use sp_runtime::traits::Convert; + let fee: BalanceOf = 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 0000000000000..7466ec1de4877 --- /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 { + /// Map Weight to Gas equivalent. + gas_mapper: GasMapper, + /// Store all in-progress CallTrace instances. + traces: Vec>, + /// Stack of indices to the current active traces. + current_stack: Vec, + /// whether or not to capture logs. + with_log: bool, +} + +impl CallTracer { + /// 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> { + core::mem::take(&mut self.traces) + } +} + +impl Gas> Tracer for CallTracer { + 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 f696f75a4a138..d2ef6c9c7ba6c 100644 --- a/substrate/frame/revive/src/exec.rs +++ b/substrate/frame/revive/src/exec.rs @@ -17,12 +17,12 @@ 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, Error, Event, ImmutableData, ImmutableDataOf, Pallet as Contracts, @@ -773,7 +773,25 @@ where )? { 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 } } @@ -1018,6 +1036,7 @@ where fn run(&mut self, executable: E, input_data: Vec) -> 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 }; @@ -1038,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. @@ -1045,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, )?; @@ -1069,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() { @@ -1353,7 +1395,7 @@ where &mut self, gas_limit: Weight, deposit_limit: U256, - dest: &H160, + dest_addr: &H160, value: U256, input_data: Vec, allows_reentry: bool, @@ -1369,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(>::ReentranceDenied.into()); } @@ -1661,11 +1703,11 @@ where } fn deposit_event(&mut self, topics: Vec, data: Vec) { - Contracts::::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::::deposit_event(Event::ContractEmitted { contract, data, topics }); } fn block_number(&self) -> U256 { diff --git a/substrate/frame/revive/src/lib.rs b/substrate/frame/revive/src/lib.rs index a9f2842c35f6a..c36cb3f47caed 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::{ @@ -83,7 +83,6 @@ use sp_runtime::{ pub use crate::{ address::{create1, create2, AccountId32Mapper, AddressMapper}, - debug::Tracing, exec::{MomentOf, Origin}, pallet::*, }; @@ -118,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; @@ -255,12 +253,6 @@ pub mod pallet { #[pallet::no_default_bounds] type InstantiateOrigin: EnsureOrigin; - /// Debugging utilities for contracts. - /// For production chains, it's recommended to use the `()` implementation of this - /// trait. - #[pallet::no_default_bounds] - type Debug: Debugger; - /// A type that exposes XCM APIs, allowing contracts to interact with other parachains, and /// execute XCM programs. #[pallet::no_default_bounds] @@ -367,7 +359,6 @@ pub mod pallet { type InstantiateOrigin = EnsureSigned; type WeightInfo = (); type WeightPrice = Self; - type Debug = (); type Xcm = (); type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>; type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>; @@ -1146,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(>::account_nonce(&origin).into()); } diff --git a/substrate/frame/revive/src/tests.rs b/substrate/frame/revive/src/tests.rs index 15f18b44868ba..d8b60e38da5ef 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,13 +25,14 @@ 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, Config, ContractInfo, ContractInfoOf, @@ -523,7 +520,6 @@ impl Config for Test { type UploadOrigin = EnsureAccount; type InstantiateOrigin = EnsureAccount; type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; - type Debug = TestDebug; type ChainId = ChainId; } @@ -4554,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 _ = ::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 _ = ::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 b1fdb2d47441e..0000000000000 --- a/substrate/frame/revive/src/tests/test_debug.rs +++ /dev/null @@ -1,235 +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, - result: Option>, -} - -thread_local! { - static DEBUG_EXECUTION_TRACE: RefCell> = RefCell::new(Vec::new()); - static INTERCEPTED_ADDRESS: RefCell> = RefCell::new(None); -} - -pub struct TestDebug; -pub struct TestCallSpan { - contract_address: sp_core::H160, - call: ExportedFunction, - input: Vec, -} - -impl Tracing 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 for TestDebug { - fn intercept_call( - contract_address: &sp_core::H160, - _entry_point: ExportedFunction, - _input_data: &[u8], - ) -> Option { - 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 { - DEBUG_EXECUTION_TRACE.with(|stack| stack.borrow().clone()) - } - - fn deploy(wasm: Vec) -> H160 { - Contracts::bare_instantiate( - RuntimeOrigin::signed(ALICE), - 0, - GAS_LIMIT, - DepositLimit::Balance(deposit_limit::()), - Code::Upload(wasm), - vec![], - Some([0u8; 32]), - ) - .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, 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::(), - 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::().into(), - Code::Upload(wasm), - vec![], - // some salt to ensure that the address of this contract is unique among all tests - Some([0x41; 32]), - ) - .result - .unwrap() - .addr; - - // no interception yet - assert_ok!(Contracts::call( - RuntimeOrigin::signed(ALICE), - account_id, - 0, - GAS_LIMIT, - deposit_limit::(), - 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::(), - vec![], - ), - >::ContractReverted, - ); - }); -} diff --git a/substrate/frame/revive/src/tracing.rs b/substrate/frame/revive/src/tracing.rs new file mode 100644 index 0000000000000..e9c05f8cb5058 --- /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>(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: 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/uapi/Cargo.toml b/substrate/frame/revive/uapi/Cargo.toml index 7241d667fcdc7..cf006941cfd0a 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/root-offences/src/mock.rs b/substrate/frame/root-offences/src/mock.rs index d9469195a0457..9b319cabb09ed 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 = ::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), ], ..Default::default() } diff --git a/substrate/frame/salary/Cargo.toml b/substrate/frame/salary/Cargo.toml index 626993a0547b5..84c55b110c8c2 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 346cd04c0fa9e..74201da3d2f31 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 = ::Balance; type UnixTime = pallet_timestamp::Pallet; diff --git a/substrate/frame/src/lib.rs b/substrate/frame/src/lib.rs index e3e58fc01b5fa..1c4b2ed5b821d 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; @@ -203,12 +202,10 @@ pub mod prelude { /// Dispatch types from `frame-support`, other fundamental traits #[doc(no_inline)] pub use frame_support::dispatch::{GetDispatchInfo, PostDispatchInfo}; - pub use frame_support::{ - defensive, defensive_assert, - traits::{ - Contains, EitherOf, EstimateNextSessionRotation, IsSubType, MapSuccess, NoOpPoll, - OnRuntimeUpgrade, OneSessionHandler, RankedMembers, RankedMembersSwapHandler, - }, + pub use frame_support::traits::{ + Contains, EitherOf, EstimateNextSessionRotation, Everything, IsSubType, MapSuccess, + NoOpPoll, OnRuntimeUpgrade, OneSessionHandler, RankedMembers, RankedMembersSwapHandler, + VariantCount, VariantCountOf, }; /// Pallet prelude of `frame-system`. @@ -226,6 +223,9 @@ pub mod prelude { /// All hashing related things pub use super::hashing::*; + /// All account related things. + pub use super::account::*; + /// All arithmetic types and traits used for safe math. pub use super::arithmetic::*; @@ -235,6 +235,10 @@ pub mod prelude { BlockNumberProvider, Bounded, Convert, DispatchInfoOf, Dispatchable, ReduceBy, ReplaceWithDefault, SaturatedConversion, Saturating, StaticLookup, TrailingZeroInput, }; + + /// Bounded storage related types. + pub use sp_runtime::{BoundedSlice, BoundedVec}; + /// Other error/result types for runtime #[doc(no_inline)] pub use sp_runtime::{ @@ -322,7 +326,7 @@ pub mod testing_prelude { /// Other helper macros from `frame_support` that help with asserting in tests. pub use frame_support::{ assert_err, assert_err_ignore_postinfo, assert_error_encoded_size, assert_noop, assert_ok, - assert_storage_noop, hypothetically, storage_alias, + assert_storage_noop, ensure, hypothetically, storage_alias, }; pub use frame_system::{self, mocking::*, RunToBlockHooks}; @@ -552,6 +556,16 @@ pub mod hashing { pub use sp_runtime::traits::{BlakeTwo256, Hash, Keccak256}; } +/// All account management related traits. +/// +/// This is already part of the [`prelude`]. +pub mod account { + pub use frame_support::traits::{ + AsEnsureOriginWithArg, ChangeMembers, EitherOfDiverse, InitializeMembers, + }; + pub use sp_runtime::traits::{IdentifyAccount, IdentityLookup}; +} + /// Access to all of the dependencies of this crate. In case the prelude re-exports are not enough, /// this module can be used. /// diff --git a/substrate/frame/staking/Cargo.toml b/substrate/frame/staking/Cargo.toml index 22176b6d720b2..74b1c78e9cbee 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 23368b1f8fca7..a1140d317c204 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() -> BalanceOf { @@ -32,7 +38,7 @@ pub fn total_issuance() -> BalanceOf { 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(who: &T::AccountId) -> BalanceOf { T::Currency::total_balance(who) } @@ -41,42 +47,65 @@ pub fn total_balance(who: &T::AccountId) -> BalanceOf { /// /// This includes balance free to stake along with any balance that is already staked. pub fn stakeable_balance(who: &T::AccountId) -> BalanceOf { - T::Currency::free_balance(who) + free_to_stake::(who).saturating_add(staked::(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(who: &T::AccountId) -> BalanceOf { - 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(who: &T::AccountId) -> BalanceOf { + // 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(who: &T::AccountId, value: BalanceOf) { - 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::(); + // currently on stake + let staked_balance = staked::(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::(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::(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(who: &T::AccountId, amount: BalanceOf) { - T::Currency::set_lock( - crate::STAKING_ID, - who, - amount, - frame_support::traits::WithdrawReasons::all(), - ); +pub fn update_stake(who: &T::AccountId, amount: BalanceOf) -> 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(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(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( who: &T::AccountId, value: BalanceOf, ) -> (NegativeImbalanceOf, BalanceOf) { - 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( +pub fn mint_into_existing( who: &T::AccountId, value: BalanceOf, ) -> Option> { - 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(who: &T::AccountId, value: BalanceOf) -> PositiveImbalanceOf { - 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(who: &T::AccountId, value: NegativeImbalanceOf) { - T::Currency::resolve_creating(who, value) + let _ = T::Currency::resolve(who, value); } /// Issue `value` increasing total issuance. @@ -121,5 +153,5 @@ pub fn issue(value: BalanceOf) -> NegativeImbalanceOf { /// Burn the amount from the total issuance. #[cfg(feature = "runtime-benchmarks")] pub fn burn(amount: BalanceOf) -> PositiveImbalanceOf { - 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 79d8dd3fbc303..59d272168d68b 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::(&stash, max_additional).unwrap(); + let _ = asset::mint_into_existing::( + &stash, + max_additional + asset::existential_deposit::(), + ) + .unwrap(); whitelist_account!(stash); @@ -1133,6 +1137,23 @@ mod benchmarks { Ok(()) } + #[benchmark] + fn migrate_currency() -> Result<(), BenchmarkError> { + let (stash, _ctrl) = + create_stash_controller::(USER_SEED, 100, RewardDestination::Staked)?; + let stake = asset::staked::(&stash); + migrate_to_old_currency::(stash.clone()); + // no holds + assert!(asset::staked::(&stash).is_zero()); + whitelist_account!(stash); + + #[extrinsic_call] + _(RawOrigin::Signed(stash.clone()), stash.clone()); + + assert_eq!(asset::staked::(&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 ac3be04cf6071..1d66ebd27e9f7 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 StakingLedger { // We skip locking virtual stakers. if !Pallet::::is_virtual_staker(&self.stash) { // for direct stakers, update lock on stash based on ledger. - asset::update_stake::(&self.stash, self.total); + asset::update_stake::(&self.stash, self.total) + .map_err(|_| Error::::NotEnoughFunds)?; } Ledger::::insert( @@ -250,7 +252,7 @@ impl StakingLedger { /// 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> { + pub(crate) fn kill(stash: &T::AccountId) -> DispatchResult { let controller = >::get(stash).ok_or(Error::::NotStash)?; >::get(&controller).ok_or(Error::::NotController).map(|ledger| { @@ -259,9 +261,9 @@ impl StakingLedger { >::remove(&stash); // kill virtual staker if it exists. - if >::take(&stash).is_none() { + if >::take(&ledger.stash).is_none() { // if not virtual staker, clear locks. - asset::kill_stake::(&ledger.stash); + asset::kill_stake::(&ledger.stash)?; } Ok(()) diff --git a/substrate/frame/staking/src/lib.rs b/substrate/frame/staking/src/lib.rs index 6361663b2b1c0..42230cb27b756 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 = ::CurrencyBalance; -type PositiveImbalanceOf = <::Currency as Currency< - ::AccountId, ->>::PositiveImbalance; -pub type NegativeImbalanceOf = <::Currency as Currency< - ::AccountId, ->>::NegativeImbalance; +type PositiveImbalanceOf = Debt<::AccountId, ::Currency>; +pub type NegativeImbalanceOf = + Credit<::AccountId, ::Currency>; type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; diff --git a/substrate/frame/staking/src/mock.rs b/substrate/frame/staking/src/mock.rs index 5a5648600d754..41fb3a31d52ed 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::::default().build_storage().unwrap(); + let ed = ExistentialDeposit::get(); let _ = pallet_balances::GenesisConfig:: { 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), @@ -576,7 +581,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::(&who, val); assert_ok!(Staking::bond(RuntimeOrigin::signed(who), val, RewardDestination::Stash)); } @@ -601,10 +606,6 @@ pub(crate) fn bond_virtual_nominator( val: Balance, target: Vec, ) { - // 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!(::virtual_bond(&who, val, &payee)); assert_ok!(Staking::nominate(RuntimeOrigin::signed(who), target)); @@ -810,7 +811,7 @@ pub(crate) fn bond_extra_no_checks(stash: &AccountId, amount: Balance) { let mut ledger = Ledger::::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::(stash, new_total); ledger.total = new_total; ledger.active = new_total; Ledger::::insert(controller, ledger); @@ -819,10 +820,10 @@ pub(crate) fn bond_extra_no_checks(stash: &AccountId, amount: Balance) { pub(crate) fn setup_double_bonded_ledgers() { let init_ledgers = Ledger::::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::(&333, 2000); + let _ = asset::set_stakeable_balance::(&444, 2000); + let _ = asset::set_stakeable_balance::(&555, 2000); + let _ = asset::set_stakeable_balance::(&777, 2000); assert_ok!(Staking::bond(RuntimeOrigin::signed(333), 10, RewardDestination::Staked)); assert_ok!(Staking::bond(RuntimeOrigin::signed(444), 20, RewardDestination::Staked)); @@ -924,5 +925,5 @@ pub(crate) fn staking_events_since_last_call() -> Vec> { } pub(crate) fn balances(who: &AccountId) -> (Balance, Balance) { - (Balances::free_balance(who), Balances::reserved_balance(who)) + (asset::stakeable_balance::(who), Balances::reserved_balance(who)) } diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 2ae925d036435..8c3ff23315a42 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 Pallet { pub(crate) fn inspect_bond_state( stash: &T::AccountId, ) -> Result> { - let lock = asset::staked::(&stash); + // look at any old unmigrated lock as well. + let hold_or_lock = asset::staked::(&stash) + .max(T::OldCurrency::balance_locked(STAKING_ID, &stash).into()); let controller = >::get(stash).ok_or_else(|| { - if lock == Zero::zero() { + if hold_or_lock == Zero::zero() { Error::::NotStash } else { Error::::BadState @@ -111,7 +113,7 @@ impl Pallet { 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 Pallet { additional } else { // additional amount or actual balance of stash whichever is lower. - additional.min( - asset::stakeable_balance::(stash) - .checked_sub(&ledger.total) - .ok_or(ArithmeticError::Overflow)?, - ) + additional.min(asset::free_to_stake::(stash)) }; ledger.total = ledger.total.checked_add(&extra).ok_or(ArithmeticError::Overflow)?; @@ -416,12 +414,12 @@ impl Pallet { let dest = Self::payee(StakingAccount::Stash(stash.clone()))?; let maybe_imbalance = match dest { - RewardDestination::Stash => asset::mint_existing::(stash, amount), + RewardDestination::Stash => asset::mint_into_existing::(stash, amount), RewardDestination::Staked => Self::ledger(Stash(stash.clone())) .and_then(|mut ledger| { ledger.active += amount; ledger.total += amount; - let r = asset::mint_existing::(stash, amount); + let r = asset::mint_into_existing::(stash, amount); let _ = ledger .update() @@ -799,8 +797,6 @@ impl Pallet { Self::do_remove_validator(&stash); Self::do_remove_nominator(&stash); - frame_system::Pallet::::dec_consumers(&stash); - Ok(()) } @@ -1163,6 +1159,81 @@ impl Pallet { ) -> Exposure> { EraInfo::::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::OldCurrency::balance_locked(STAKING_ID, stash).into(); + ensure!(!staked.is_zero(), Error::::AlreadyMigrated); + ensure!(ledger.total == staked, Error::::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::(&stash); + let force_withdraw = if max_hold >= staked { + // this means we can hold all stake. yay! + asset::update_stake::(&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::::dec_consumers(&stash); + + Self::deposit_event(Event::::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::::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::::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::(&stash) >= asset::existential_deposit::() { + 2 + } else { + 1 + }; + + // We should never have more than expected providers. + ensure!(actual_providers <= expected_providers, Error::::BadState); + + // if actual provider is less than expected, it is already migrated. + ensure!(actual_providers == expected_providers, Error::::AlreadyMigrated); + + // dec provider + let _ = frame_system::Pallet::::dec_providers(&stash)?; + + return Ok(()) + } } impl Pallet { @@ -1925,9 +1996,10 @@ impl StakingInterface for Pallet { } impl sp_staking::StakingUnchecked for Pallet { - fn migrate_to_virtual_staker(who: &Self::AccountId) { - asset::kill_stake::(who); + fn migrate_to_virtual_staker(who: &Self::AccountId) -> DispatchResult { + asset::kill_stake::(who)?; VirtualStakers::::insert(who, ()); + Ok(()) } /// Virtually bonds `keyless_who` to `payee` with `value`. @@ -1945,9 +2017,6 @@ impl sp_staking::StakingUnchecked for Pallet { // check if payee not same as who. ensure!(keyless_who != payee, Error::::RewardDestinationRestricted); - // mark this pallet as consumer of `who`. - frame_system::Pallet::::inc_consumers(&keyless_who).map_err(|_| Error::::BadState)?; - // mark who as a virtual staker. VirtualStakers::::insert(keyless_who, ()); @@ -1959,11 +2028,13 @@ impl sp_staking::StakingUnchecked for Pallet { Ok(()) } + /// Only meant to be used in tests. #[cfg(feature = "runtime-benchmarks")] fn migrate_to_direct_staker(who: &Self::AccountId) { assert!(VirtualStakers::::contains_key(who)); let ledger = StakingLedger::::get(Stash(who.clone())).unwrap(); - asset::update_stake::(who, ledger.total); + let _ = asset::update_stake::(who, ledger.total) + .expect("funds must be transferred to stash"); VirtualStakers::::remove(who); } } @@ -2100,7 +2171,7 @@ impl Pallet { if VirtualStakers::::contains_key(stash.clone()) { ensure!( asset::staked::(&stash) == Zero::zero(), - "virtual stakers should not have any locked balance" + "virtual stakers should not have any staked balance" ); ensure!( >::get(stash.clone()).unwrap() == stash.clone(), @@ -2128,7 +2199,7 @@ impl Pallet { } 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 b3f8c18f704cd..7d5da9ea0c497 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, + Balance = Self::CurrencyBalance, + >; + /// The staking balance. #[pallet::no_default] - type Currency: LockableCurrency< + type Currency: FunHoldMutate< Self::AccountId, - Moment = BlockNumberFor, + Reason = Self::RuntimeHoldReason, Balance = Self::CurrencyBalance, - > + InspectLockableCurrency; + > + FunMutate + + FunHoldBalanced; + + /// Overarching hold reason. + #[pallet::no_default_bounds] + type RuntimeHoldReason: From; + /// Just the `Currency::Balance` type; we have this item to allow us to constrain it to /// `From`. type CurrencyBalance: sp_runtime::traits::AtLeast32BitUnsigned @@ -106,6 +124,8 @@ pub mod pallet { + Default + From + 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::(stash) >= balance, + asset::free_to_stake::(stash) >= balance, "Stash does not have enough balance to bond." ); frame_support::assert_ok!(>::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 }, } #[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::::InsufficientBond.into()) } - // Would fail if account has no provider. - frame_system::Pallet::::inc_consumers(&stash)?; - - let stash_balance = asset::stakeable_balance::(&stash); + let stash_balance = asset::free_to_stake::(&stash); let value = value.min(stash_balance); Self::deposit_event(Event::::Bonded { stash: stash.clone(), amount: value }); let ledger = StakingLedger::::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::(&stash, new_total); + // enforce hold == ledger.amount. + asset::update_stake::(&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::::CannotRestoreLedger)?.min(stash_balance); - asset::update_stake::(&stash, new_total); + asset::update_stake::(&stash, new_total)?; Ok((stash.clone(), new_total)) }, Err(Error::::BadState) => { // the stash and ledger do not exist but lock is lingering. - asset::kill_stake::(&stash); + asset::kill_stake::(&stash)?; ensure!( Self::inspect_bond_state(&stash) == Err(Error::::NotStash), Error::::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, + 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 81337710aa904..dfd5422106c08 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( pub fn current_era() -> EraIndex { CurrentEra::::get().unwrap_or(0) } + +pub fn migrate_to_old_currency(who: T::AccountId) { + use frame_support::traits::LockableCurrency; + let staked = asset::staked::(&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::(&who).expect("remove hold failed"); + + // replicate old behaviour of explicit increment of consumer. + frame_system::Pallet::::inc_consumers(&who).expect("increment consumer failed"); +} diff --git a/substrate/frame/staking/src/tests.rs b/substrate/frame/staking/src/tests.rs index 6c2335e1aac8a..9084151439940 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::(&10), 1); - assert_eq!(asset::stakeable_balance::(&10), 1); + assert_eq!(Balances::balance(&10), 1); // New era is not being forced assert_eq!(ForceEra::::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::(); + mock::make_all_reward_payment(0); + // total issuance should have increased + let total_issuance_1 = asset::total_issuance::(); + assert_eq!(total_issuance_1, total_issuance_0 + total_payout_0); + assert_eq_error_rate!( asset::total_balance::(&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::(), total_issuance_1 + total_payout_1); assert_eq_error_rate!( asset::total_balance::(&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::::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::(&3), initial_balance); + assert_eq!(asset::stakeable_balance::(&3), initial_balance); // 333 is the reward destination for 3. assert_eq_error_rate!( asset::total_balance::(&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::(&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::(&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::(&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::(&11), 1000); + // Confirm account 11 cannot reserve as a result - assert_noop!(Balances::reserve(&11, 1), BalancesError::::LiquidityRestrictions); + assert_noop!(Balances::reserve(&11, 2), BalancesError::::InsufficientBalance); + assert_noop!(Balances::reserve(&11, 1), DispatchError::ConsumerRemaining); // Give account 11 extra free balance - let _ = asset::set_stakeable_balance::(&11, 10000); + let _ = asset::set_stakeable_balance::(&11, 1000 + 1000); + assert_eq!(asset::free_to_stake::(&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::(&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::(&11), 1000); + assert_eq!(asset::free_to_stake::(&11), 0); + + // add some staking balance to 11 + let _ = asset::set_stakeable_balance::(&11, 1000 + 1000); + // free to stake is 1000 + assert_eq!(asset::free_to_stake::(&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::(&11), 1000); + + // can stake free balance + assert_ok!(Staking::bond_extra(RuntimeOrigin::signed(11), 500)); + assert_eq!(asset::staked::(&11), 1500); + + // Can stake the locked balance + assert_ok!(Staking::bond_extra(RuntimeOrigin::signed(11), 500)); + assert_eq!(asset::staked::(&11), 2000); + // no balance left to stake + assert_eq!(asset::free_to_stake::(&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::(&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::(&10), 1); + assert_eq!(asset::total_balance::(&10), 1); // Check the balance of the stash account - assert_eq!(asset::stakeable_balance::(&11), 1000); + assert_eq!(asset::total_balance::(&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::(&11, 1000000); + // ensure it has the correct balance. + assert_eq!(asset::stakeable_balance::(&11), 1000000); + // Initial config should be correct assert_eq!(active_era(), 0); - // check the balance of a validator accounts. - assert_eq!(asset::total_balance::(&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::::get(&1)[0].amount, 5); + assert_eq!(pallet_balances::Holds::::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::::get(&1)[0].amount, 5); + assert_eq!(pallet_balances::Holds::::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::::get(&1).len(), 0); + assert_eq!(pallet_balances::Holds::::get(&1).len(), 0); }); } @@ -2338,9 +2395,20 @@ fn reward_validator_slashing_validator_does_not_overflow() { EraInfo::::set_exposure(0, &11, exposure); ErasValidatorReward::::insert(0, stake); assert_ok!(Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 0, 0)); - assert_eq!(asset::total_balance::(&11), stake * 2); + assert_eq!(asset::stakeable_balance::(&11), stake * 2); - // Set staker + // ensure ledger has `stake` and no more. + Ledger::::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::(&11, stake); let _ = asset::set_stakeable_balance::(&2, stake); @@ -2366,8 +2434,8 @@ fn reward_validator_slashing_validator_does_not_overflow() { &[Perbill::from_percent(100)], ); - assert_eq!(asset::total_balance::(&11), stake - 1); - assert_eq!(asset::total_balance::(&2), 1); + assert_eq!(asset::stakeable_balance::(&11), stake - 1); + assert_eq!(asset::stakeable_balance::(&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::(&1), 10 + reward_each); - assert_eq!(asset::stakeable_balance::(&2), 20 + reward_each); + assert_eq!(asset::total_balance::(&1), 10 + reward_each); + assert_eq!(asset::total_balance::(&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::(&1), 10 + reward); + assert_eq!(asset::total_balance::(&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::(&1), 10 + prior_payout + reward); + assert_eq!(asset::total_balance::(&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::(&11), 2); - assert_eq!(asset::total_balance::(&11), 2); + assert_eq!(asset::stakeable_balance::(&11), 0); + // Non staked balance is not touched. + assert_eq!(asset::total_balance::(&11), ExistentialDeposit::get()); let slashing_spans = SlashingSpans::::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::::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:: { era, value }; + ExtBuilder::default().nominate(true).build_and_execute(|| { + let c = |era, value| UnlockChunk:: { 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::::new(123, 20); - ledger.total = 40; - ledger.unlocking = unlocking; + // Given + let mut ledger = StakingLedger::::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:: { era, value }; - // Given - let mut ledger = StakingLedger::::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:: { era, value }; + // Given + let mut ledger = StakingLedger::::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::(&10), 1); + assert_eq!(asset::total_balance::(&10), 1); // 10 can bond more than its balance amount since we do not require lock for virtual // bonding. assert_ok!(::virtual_bond(&10, 100, &15)); @@ -7265,7 +7342,7 @@ mod staking_unchecked { assert_eq!(asset::staked::(&200), 1000); // migrate them to virtual staker - ::migrate_to_virtual_staker(&200); + assert_ok!(::migrate_to_virtual_staker(&200)); // payee needs to be updated to a non-stash account. assert_ok!(::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 - ::migrate_to_virtual_staker(&101); + assert_ok!(::migrate_to_virtual_staker(&101)); // set payee different to self. assert_ok!(::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 - ::migrate_to_virtual_staker(&101); + assert_ok!(::migrate_to_virtual_staker(&101)); // set payee different to self. assert_ok!(::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. - ::migrate_to_virtual_staker(&333); + assert_ok!(::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::(&333), lock_333_before); // OK assert_eq!(Bonded::::get(&333), Some(444)); // OK - assert!(Payee::::get(&333).is_some()); // OK - + assert!(Payee::::get(&333).is_some()); // however, ledger associated with its controller was killed. assert!(Ledger::::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::(&alice), 1000); + assert_eq!(Balances::balance_locked(STAKING_ID, &alice), 0); + // migrate alice currency to legacy locks + testing_utils::migrate_to_old_currency::(alice); + // no more holds + assert_eq!(asset::staked::(&alice), 0); + assert_eq!(Balances::balance_locked(STAKING_ID, &alice), 1000); + assert_eq!( + ::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::(&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::(&alice), 1000 + 100); + assert_eq!( + ::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::(&alice), 1000); + assert_eq!( + ::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::(&alice), 1000); + assert_eq!( + ::stake(&alice), + Ok(Stake { total: 1000, active: 1000 }) + ); + + // ensure cannot migrate again. + assert_noop!( + Staking::migrate_currency(RuntimeOrigin::signed(1), alice), + Error::::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::(alice); + assert_eq!(asset::staked::(&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!( + ::stake(&alice), + Ok(Stake { total: 1000, active: 1000 }) + ); + assert_eq!(asset::staked::(&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::::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::(alice); + assert_eq!(asset::staked::(&alice), 0); + assert_eq!(Balances::balance_locked(STAKING_ID, &alice), stake); + // ledger has 1000 staked. + assert_eq!( + ::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::::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!( + ::stake(&alice), + Ok(Stake { total: expected_hold, active: expected_hold }) + ); + assert_eq!(asset::staked::(&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::::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::::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::::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::::AlreadyMigrated + ); + }); + } +} diff --git a/substrate/frame/staking/src/weights.rs b/substrate/frame/staking/src/weights.rs index 56f561679cfc7..02ccdacb01c42 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 WeightInfo for SubstrateWeight { /// 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 WeightInfo for SubstrateWeight { /// 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 WeightInfo for SubstrateWeight { /// 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 WeightInfo for SubstrateWeight { /// 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 WeightInfo for SubstrateWeight { /// 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 WeightInfo for SubstrateWeight { /// 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 WeightInfo for SubstrateWeight { /// 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 WeightInfo for SubstrateWeight { /// 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 WeightInfo for SubstrateWeight { /// 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 WeightInfo for SubstrateWeight { /// 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 WeightInfo for SubstrateWeight { /// 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 WeightInfo for SubstrateWeight { /// 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 WeightInfo for SubstrateWeight { /// 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 WeightInfo for SubstrateWeight { // 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 WeightInfo for SubstrateWeight { // 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 WeightInfo for SubstrateWeight { // 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 WeightInfo for SubstrateWeight { // 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 WeightInfo for SubstrateWeight { // 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 WeightInfo for SubstrateWeight { /// 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 WeightInfo for SubstrateWeight { /// 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 WeightInfo for SubstrateWeight { /// 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 WeightInfo for SubstrateWeight { /// 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 WeightInfo for SubstrateWeight { /// 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 WeightInfo for SubstrateWeight { /// 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 WeightInfo for SubstrateWeight { /// 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 WeightInfo for SubstrateWeight { /// 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 WeightInfo for SubstrateWeight { // 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 WeightInfo for SubstrateWeight { /// 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 WeightInfo for SubstrateWeight { /// 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 WeightInfo for SubstrateWeight { // 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 WeightInfo for SubstrateWeight { // 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 WeightInfo for SubstrateWeight { /// 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 WeightInfo for SubstrateWeight { /// 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 WeightInfo for SubstrateWeight { // 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 728426cc84c71..4a83c809a6a5e 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 2b8e437073894..676b73e03d3c4 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(PhantomData<(Price, Balance)>); +impl Convert for ConstantStoragePrice +where + Price: Get, + Balance: From + 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 f627d29cd5630..157361dbd5d6d 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 dd752989c3662..4a96cbcacb580 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 2074b1476f45a..7b7ae855bf8f3 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 { /// 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, AssetIdOf, AssetBalanceOf>, HC: HandleCredit, - AssetIdOf: FullCodec + Copy + MaybeSerializeDeserialize + Debug + Default + Eq + TypeInfo, + AssetIdOf: FullCodec + Clone + MaybeSerializeDeserialize + Debug + Default + Eq + TypeInfo, { type Balance = BalanceOf; type AssetId = AssetIdOf; @@ -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 = - >::can_withdraw(asset_id, who, converted_fee); + let can_withdraw = >::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 17010a8907fc2..8e23c6800a9d5 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/substrate/utils/frame/benchmarking-cli/src/overhead/command.rs b/substrate/utils/frame/benchmarking-cli/src/overhead/command.rs index 8df8ee5464f71..847f8e16c0df0 100644 --- a/substrate/utils/frame/benchmarking-cli/src/overhead/command.rs +++ b/substrate/utils/frame/benchmarking-cli/src/overhead/command.rs @@ -482,7 +482,7 @@ impl OverheadCmd { let database_source = self.database_config( &base_path.path().to_path_buf(), self.database_cache_size()?.unwrap_or(1024), - self.database()?.unwrap_or(Database::RocksDb), + self.database()?.unwrap_or(Database::Auto), )?; let backend = new_db_backend(DatabaseSettings { diff --git a/templates/parachain/Cargo.toml b/templates/parachain/Cargo.toml new file mode 100644 index 0000000000000..84b9d5e29bbe8 --- /dev/null +++ b/templates/parachain/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "parachain-template" +description = "A parachain-template helper crate to keep documentation in sync with the template's components." +version = "0.0.0" +license = "Unlicense" +authors.workspace = true +homepage.workspace = true +repository.workspace = true +edition.workspace = true +publish = false + +[dependencies] +docify = "0.2.9" + +[features] +generate-readme = [] diff --git a/templates/parachain/README.docify.md b/templates/parachain/README.docify.md new file mode 100644 index 0000000000000..47385e0bbf197 --- /dev/null +++ b/templates/parachain/README.docify.md @@ -0,0 +1,254 @@ +

+ +# Polkadot SDK's Parachain Template + +Polkadot SDK Logo +Polkadot SDK Logo + +> This is a template for creating a [parachain](https://wiki.polkadot.network/docs/learn-parachains) based on Polkadot SDK. +> +> This template is automatically updated after releases in the main [Polkadot SDK monorepo](https://github.com/paritytech/polkadot-sdk). + +
+ +## Table of Contents + +- [Intro](#intro) + +- [Template Structure](#template-structure) + +- [Getting Started](#getting-started) + +- [Starting a Development Chain](#starting-a-development-chain) + + - [Omni Node](#omni-node-prerequisites) + - [Zombienet setup with Omni Node](#zombienet-setup-with-omni-node) + - [Parachain Template Node](#parachain-template-node) + - [Connect with the Polkadot-JS Apps Front-End](#connect-with-the-polkadot-js-apps-front-end) + - [Takeaways](#takeaways) + +- [Runtime development](#runtime-development) +- [Contributing](#contributing) +- [Getting Help](#getting-help) + +## Intro + +- ⏫ This template provides a starting point to build a [parachain](https://wiki.polkadot.network/docs/learn-parachains). + +- ☁️ It is based on the + [Cumulus](https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/polkadot_sdk/cumulus/index.html) framework. + +- 🔧 Its runtime is configured with a single custom pallet as a starting point, and a handful of ready-made pallets + such as a [Balances pallet](https://paritytech.github.io/polkadot-sdk/master/pallet_balances/index.html). + +- 👉 Learn more about parachains [here](https://wiki.polkadot.network/docs/learn-parachains) + +## Template Structure + +A Polkadot SDK based project such as this one consists of: + +- 🧮 the [Runtime](./runtime/README.md) - the core logic of the parachain. +- 🎨 the [Pallets](./pallets/README.md) - from which the runtime is constructed. +- 💿 a [Node](./node/README.md) - the binary application, not part of the project default-members list and not compiled unless + building the project with `--workspace` flag, which builds all workspace members, and is an alternative to + [Omni Node](https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/reference_docs/omni_node/index.html). + +## Getting Started + +- 🦀 The template is using the Rust language. + +- 👉 Check the + [Rust installation instructions](https://www.rust-lang.org/tools/install) for your system. + +- 🛠️ Depending on your operating system and Rust version, there might be additional + packages required to compile this template - please take note of the Rust compiler output. + +Fetch parachain template code: + +```sh +git clone https://github.com/paritytech/polkadot-sdk-parachain-template.git parachain-template + +cd parachain-template +``` + +## Starting a Development Chain + +The parachain template relies on a hardcoded parachain id which is defined in the runtime code +and referenced throughout the contents of this file as `{{PARACHAIN_ID}}`. Please replace +any command or file referencing this placeholder with the value of the `PARACHAIN_ID` constant: + + + +### Omni Node Prerequisites + +[Omni Node](https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/reference_docs/omni_node/index.html) can +be used to run the parachain template's runtime. `polkadot-omni-node` binary crate usage is described at a high-level +[on crates.io](https://crates.io/crates/polkadot-omni-node). + +#### Install `polkadot-omni-node` + +Please see the installation section at [`crates.io/omni-node`](https://crates.io/crates/polkadot-omni-node). + +#### Build `parachain-template-runtime` + +```sh +cargo build --release +``` + +#### Install `staging-chain-spec-builder` + +Please see the installation section at [`crates.io/staging-chain-spec-builder`](https://crates.io/crates/staging-chain-spec-builder). + +#### Use `chain-spec-builder` to generate the `chain_spec.json` file + +```sh +chain-spec-builder create --relay-chain "rococo-local" --para-id {{PARACHAIN_ID}} --runtime \ + target/release/wbuild/parachain-template-runtime/parachain_template_runtime.wasm named-preset development +``` + +**Note**: the `relay-chain` and `para-id` flags are mandatory information required by +Omni Node, and for parachain template case the value for `para-id` must be set to `{{PARACHAIN_ID}}`, since this +is also the value injected through [ParachainInfo](https://docs.rs/staging-parachain-info/0.17.0/staging_parachain_info/) +pallet into the `parachain-template-runtime`'s storage. The `relay-chain` value is set in accordance +with the relay chain ID where this instantiation of parachain-template will connect to. + +#### Run Omni Node + +Start Omni Node with the generated chain spec. We'll start it in development mode (without a relay chain config), producing +and finalizing blocks based on manual seal, configured below to seal a block with each second. + +```bash +polkadot-omni-node --chain --dev --dev-block-time 1000 +``` + +However, such a setup is not close to what would run in production, and for that we need to setup a local +relay chain network that will help with the block finalization. In this guide we'll setup a local relay chain +as well. We'll not do it manually, by starting one node at a time, but we'll use [zombienet](https://paritytech.github.io/zombienet/intro.html). + +Follow through the next section for more details on how to do it. + +### Zombienet setup with Omni Node + +Assuming we continue from the last step of the previous section, we have a chain spec and we need to setup a relay chain. +We can install `zombienet` as described [here](https://paritytech.github.io/zombienet/install.html#installation), and +`zombienet-omni-node.toml` contains the network specification we want to start. + +#### Relay chain prerequisites + +Download the `polkadot` (and the accompanying `polkadot-prepare-worker` and `polkadot-execute-worker`) binaries from +[Polkadot SDK releases](https://github.com/paritytech/polkadot-sdk/releases). Then expose them on `PATH` like so: + +```sh +export PATH="$PATH:" +``` + +#### Update `zombienet-omni-node.toml` with a valid chain spec path + +```toml +# ... +[[parachains]] +id = {{PARACHAIN_ID}} +chain_spec_path = "" +# ... +``` + +#### Start the network + +```sh +zombienet --provider native spawn zombienet-omni-node.toml +``` + +### Parachain Template Node + +As mentioned in the `Template Structure` section, the `node` crate is optionally compiled and it is an alternative +to `Omni Node`. Similarly, it requires setting up a relay chain, and we'll use `zombienet` once more. + +#### Install the `parachain-template-node` + +```sh +cargo install --path node +``` + +#### Setup and start the network + +For setup, please consider the instructions for `zombienet` installation [here](https://paritytech.github.io/zombienet/install.html#installation) +and [relay chain prerequisites](#relay-chain-prerequisites). + +We're left just with starting the network: + +```sh +zombienet --provider native spawn zombienet.toml +``` + +### Connect with the Polkadot-JS Apps Front-End + +- 🌐 You can interact with your local node using the + hosted version of the Polkadot/Substrate Portal: + [relay chain](https://polkadot.js.org/apps/#/explorer?rpc=ws://localhost:9944) + and [parachain](https://polkadot.js.org/apps/#/explorer?rpc=ws://localhost:9988). + +- 🪐 A hosted version is also + available on [IPFS](https://dotapps.io/). + +- 🧑‍🔧 You can also find the source code and instructions for hosting your own instance in the + [`polkadot-js/apps`](https://github.com/polkadot-js/apps) repository. + +### Takeaways + +Development parachains: + +- 🔗 Connect to relay chains, and we showcased how to connect to a local one. +- 🧹 Do not persist the state. +- 💰 Are preconfigured with a genesis state that includes several prefunded development accounts. +- 🧑‍⚖️ Development accounts are used as validators, collators, and `sudo` accounts. + +## Runtime development + +We recommend using [`chopsticks`](https://github.com/AcalaNetwork/chopsticks) when the focus is more on the runtime +development and `OmniNode` is enough as is. + +### Install chopsticks + +To use `chopsticks`, please install the latest version according to the installation [guide](https://github.com/AcalaNetwork/chopsticks?tab=readme-ov-file#install). + +### Build a raw chain spec + +Build the `parachain-template-runtime` as mentioned before in this guide and use `chain-spec-builder` +again but this time by passing `--raw-storage` flag: + +```sh +chain-spec-builder create --raw-storage --relay-chain "rococo-local" --para-id {{PARACHAIN_ID}} --runtime \ + target/release/wbuild/parachain-template-runtime/parachain_template_runtime.wasm named-preset development +``` + +### Start `chopsticks` with the chain spec + +```sh +npx @acala-network/chopsticks@latest --chain-spec +``` + +### Alternatives + +`OmniNode` can be still used for runtime development if using the `--dev` flag, while `parachain-template-node` doesn't +support it at this moment. It can still be used to test a runtime in a full setup where it is started alongside a +relay chain network (see [Parachain Template node](#parachain-template-node) setup). + +## Contributing + +- 🔄 This template is automatically updated after releases in the main [Polkadot SDK monorepo](https://github.com/paritytech/polkadot-sdk). + +- ➡️ Any pull requests should be directed to this [source](https://github.com/paritytech/polkadot-sdk/tree/master/templates/parachain). + +- 😇 Please refer to the monorepo's + [contribution guidelines](https://github.com/paritytech/polkadot-sdk/blob/master/docs/contributor/CONTRIBUTING.md) and + [Code of Conduct](https://github.com/paritytech/polkadot-sdk/blob/master/docs/contributor/CODE_OF_CONDUCT.md). + +## Getting Help + +- 🧑‍🏫 To learn about Polkadot in general, [Polkadot.network](https://polkadot.network/) website is a good starting point. + +- 🧑‍🔧 For technical introduction, [here](https://github.com/paritytech/polkadot-sdk#-documentation) are + the Polkadot SDK documentation resources. + +- 👥 Additionally, there are [GitHub issues](https://github.com/paritytech/polkadot-sdk/issues) and + [Substrate StackExchange](https://substrate.stackexchange.com/). diff --git a/templates/parachain/README.md b/templates/parachain/README.md index c1e333df9e9ee..15e9f7fe61cf0 100644 --- a/templates/parachain/README.md +++ b/templates/parachain/README.md @@ -36,10 +36,10 @@ - ⏫ This template provides a starting point to build a [parachain](https://wiki.polkadot.network/docs/learn-parachains). - ☁️ It is based on the -[Cumulus](https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/polkadot_sdk/cumulus/index.html) framework. + [Cumulus](https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/polkadot_sdk/cumulus/index.html) framework. - 🔧 Its runtime is configured with a single custom pallet as a starting point, and a handful of ready-made pallets -such as a [Balances pallet](https://paritytech.github.io/polkadot-sdk/master/pallet_balances/index.html). + such as a [Balances pallet](https://paritytech.github.io/polkadot-sdk/master/pallet_balances/index.html). - 👉 Learn more about parachains [here](https://wiki.polkadot.network/docs/learn-parachains) @@ -50,18 +50,18 @@ A Polkadot SDK based project such as this one consists of: - 🧮 the [Runtime](./runtime/README.md) - the core logic of the parachain. - 🎨 the [Pallets](./pallets/README.md) - from which the runtime is constructed. - 💿 a [Node](./node/README.md) - the binary application, not part of the project default-members list and not compiled unless -building the project with `--workspace` flag, which builds all workspace members, and is an alternative to -[Omni Node](https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/reference_docs/omni_node/index.html). + building the project with `--workspace` flag, which builds all workspace members, and is an alternative to + [Omni Node](https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/reference_docs/omni_node/index.html). ## Getting Started - 🦀 The template is using the Rust language. - 👉 Check the -[Rust installation instructions](https://www.rust-lang.org/tools/install) for your system. + [Rust installation instructions](https://www.rust-lang.org/tools/install) for your system. - 🛠️ Depending on your operating system and Rust version, there might be additional -packages required to compile this template - please take note of the Rust compiler output. + packages required to compile this template - please take note of the Rust compiler output. Fetch parachain template code: @@ -73,6 +73,14 @@ cd parachain-template ## Starting a Development Chain +The parachain template relies on a hardcoded parachain id which is defined in the runtime code +and referenced throughout the contents of this file as `{{PARACHAIN_ID}}`. Please replace +any command or file referencing this placeholder with the value of the `PARACHAIN_ID` constant: + +```rust,ignore +pub const PARACHAIN_ID: u32 = 1000; +``` + ### Omni Node Prerequisites [Omni Node](https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/reference_docs/omni_node/index.html) can @@ -96,12 +104,12 @@ Please see the installation section at [`crates.io/staging-chain-spec-builder`]( #### Use `chain-spec-builder` to generate the `chain_spec.json` file ```sh -chain-spec-builder create --relay-chain "rococo-local" --para-id 1000 --runtime \ +chain-spec-builder create --relay-chain "rococo-local" --para-id {{PARACHAIN_ID}} --runtime \ target/release/wbuild/parachain-template-runtime/parachain_template_runtime.wasm named-preset development ``` **Note**: the `relay-chain` and `para-id` flags are mandatory information required by -Omni Node, and for parachain template case the value for `para-id` must be set to `1000`, since this +Omni Node, and for parachain template case the value for `para-id` must be set to `{{PARACHAIN_ID}}`, since this is also the value injected through [ParachainInfo](https://docs.rs/staging-parachain-info/0.17.0/staging_parachain_info/) pallet into the `parachain-template-runtime`'s storage. The `relay-chain` value is set in accordance with the relay chain ID where this instantiation of parachain-template will connect to. @@ -141,7 +149,7 @@ export PATH="$PATH:" ```toml # ... [[parachains]] -id = 1000 +id = {{PARACHAIN_ID}} chain_spec_path = "" # ... ``` @@ -177,15 +185,15 @@ zombienet --provider native spawn zombienet.toml ### Connect with the Polkadot-JS Apps Front-End - 🌐 You can interact with your local node using the -hosted version of the Polkadot/Substrate Portal: -[relay chain](https://polkadot.js.org/apps/#/explorer?rpc=ws://localhost:9944) -and [parachain](https://polkadot.js.org/apps/#/explorer?rpc=ws://localhost:9988). + hosted version of the Polkadot/Substrate Portal: + [relay chain](https://polkadot.js.org/apps/#/explorer?rpc=ws://localhost:9944) + and [parachain](https://polkadot.js.org/apps/#/explorer?rpc=ws://localhost:9988). - 🪐 A hosted version is also -available on [IPFS](https://dotapps.io/). + available on [IPFS](https://dotapps.io/). - 🧑‍🔧 You can also find the source code and instructions for hosting your own instance in the -[`polkadot-js/apps`](https://github.com/polkadot-js/apps) repository. + [`polkadot-js/apps`](https://github.com/polkadot-js/apps) repository. ### Takeaways @@ -211,7 +219,7 @@ Build the `parachain-template-runtime` as mentioned before in this guide and use again but this time by passing `--raw-storage` flag: ```sh -chain-spec-builder create --raw-storage --relay-chain "rococo-local" --para-id 1000 --runtime \ +chain-spec-builder create --raw-storage --relay-chain "rococo-local" --para-id {{PARACHAIN_ID}} --runtime \ target/release/wbuild/parachain-template-runtime/parachain_template_runtime.wasm named-preset development ``` @@ -234,15 +242,15 @@ relay chain network (see [Parachain Template node](#parachain-template-node) set - ➡️ Any pull requests should be directed to this [source](https://github.com/paritytech/polkadot-sdk/tree/master/templates/parachain). - 😇 Please refer to the monorepo's -[contribution guidelines](https://github.com/paritytech/polkadot-sdk/blob/master/docs/contributor/CONTRIBUTING.md) and -[Code of Conduct](https://github.com/paritytech/polkadot-sdk/blob/master/docs/contributor/CODE_OF_CONDUCT.md). + [contribution guidelines](https://github.com/paritytech/polkadot-sdk/blob/master/docs/contributor/CONTRIBUTING.md) and + [Code of Conduct](https://github.com/paritytech/polkadot-sdk/blob/master/docs/contributor/CODE_OF_CONDUCT.md). ## Getting Help - 🧑‍🏫 To learn about Polkadot in general, [Polkadot.network](https://polkadot.network/) website is a good starting point. - 🧑‍🔧 For technical introduction, [here](https://github.com/paritytech/polkadot-sdk#-documentation) are -the Polkadot SDK documentation resources. + the Polkadot SDK documentation resources. - 👥 Additionally, there are [GitHub issues](https://github.com/paritytech/polkadot-sdk/issues) and -[Substrate StackExchange](https://substrate.stackexchange.com/). + [Substrate StackExchange](https://substrate.stackexchange.com/). diff --git a/templates/parachain/runtime/src/genesis_config_presets.rs b/templates/parachain/runtime/src/genesis_config_presets.rs index f1b24e4372476..8cdadca5060ad 100644 --- a/templates/parachain/runtime/src/genesis_config_presets.rs +++ b/templates/parachain/runtime/src/genesis_config_presets.rs @@ -17,6 +17,7 @@ use sp_keyring::Sr25519Keyring; /// The default XCM version to set in genesis config. const SAFE_XCM_VERSION: u32 = xcm::prelude::XCM_VERSION; /// Parachain id used for genesis config presets of parachain template. +#[docify::export_content] pub const PARACHAIN_ID: u32 = 1000; /// Generate the session keys from individual elements. diff --git a/templates/parachain/src/lib.rs b/templates/parachain/src/lib.rs new file mode 100644 index 0000000000000..d3c5b8ba3101a --- /dev/null +++ b/templates/parachain/src/lib.rs @@ -0,0 +1,22 @@ +// 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 . + +// The parachain-template crate helps with keeping the README.md in sync +// with code sections across the components under the template: node, +// pallets & runtime, by using `docify`. + +#[cfg(feature = "generate-readme")] +docify::compile_markdown!("README.docify.md", "README.md"); diff --git a/umbrella/Cargo.toml b/umbrella/Cargo.toml index 17a7c02e82592..fc0b2d5a140ed 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 3504f081f2957..a132f16a2c33f 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;